diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategy.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategy.cs
index 7a63675226..da07446e5a 100644
--- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategy.cs
+++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategy.cs
@@ -7,6 +7,7 @@
using Azure.Mcp.Core.Areas.Server.Models;
using Azure.Mcp.Core.Areas.Server.Options;
using Azure.Mcp.Core.Commands;
+using Azure.Mcp.Core.Configuration;
using Azure.Mcp.Core.Services.Telemetry;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -21,11 +22,21 @@ namespace Azure.Mcp.Core.Areas.Server.Commands.Discovery;
/// The command factory used to access available command groups.
/// Options for configuring the service behavior.
/// Logger instance for this discovery strategy.
-public sealed class ConsolidatedToolDiscoveryStrategy(CommandFactory commandFactory, IServiceProvider serviceProvider, IOptions options, ILogger logger) : BaseDiscoveryStrategy(logger)
+public sealed class ConsolidatedToolDiscoveryStrategy(
+ CommandFactory commandFactory,
+ IServiceProvider serviceProvider,
+ ITelemetryService telemetryService,
+ IOptions options,
+ IOptions serverConfigurationOptions,
+ ILogger commandFactoryLogger,
+ ILogger logger) : BaseDiscoveryStrategy(logger)
{
private readonly CommandFactory _commandFactory = commandFactory;
private readonly IServiceProvider _serviceProvider = serviceProvider;
+ private readonly ITelemetryService _telemetryService = telemetryService;
private readonly IOptions _options = options;
+ private readonly IOptions _serverConfigurationOptions = serverConfigurationOptions;
+ private readonly ILogger _commandFactoryLogger = commandFactoryLogger;
private CommandFactory? _consolidatedCommandFactory;
///
@@ -146,14 +157,12 @@ public CommandFactory CreateConsolidatedCommandFactory()
#endif
// Create a new CommandFactory with all consolidated areas
- var telemetryService = _serviceProvider.GetRequiredService();
- var factoryLogger = _serviceProvider.GetRequiredService>();
-
_consolidatedCommandFactory = new CommandFactory(
_serviceProvider,
consolidatedAreas,
- telemetryService,
- factoryLogger
+ _telemetryService,
+ _serverConfigurationOptions,
+ _commandFactoryLogger
);
return _consolidatedCommandFactory;
diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs
index 7c5713c364..37e198f816 100644
--- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs
+++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceCollectionExtensions.cs
@@ -8,7 +8,9 @@
using Azure.Mcp.Core.Areas.Server.Commands.ToolLoading;
using Azure.Mcp.Core.Areas.Server.Options;
using Azure.Mcp.Core.Commands;
+using Azure.Mcp.Core.Configuration;
using Azure.Mcp.Core.Helpers;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@@ -25,8 +27,6 @@ namespace Azure.Mcp.Core.Areas.Server.Commands;
///
public static class AzureMcpServiceCollectionExtensions
{
- private const string DefaultServerName = "Azure MCP Server";
-
///
/// Adds the Azure MCP server services to the specified .
///
@@ -42,26 +42,51 @@ public static IServiceCollection AddAzureMcpServer(this IServiceCollection servi
services.AddSingleton(serviceStartOptions);
services.AddSingleton(Options.Create(serviceStartOptions));
- // Register default tool loader options from service start options
- var defaultToolLoaderOptions = new ToolLoaderOptions
- {
- Namespace = serviceStartOptions.Namespace,
- ReadOnly = serviceStartOptions.ReadOnly ?? false,
- InsecureDisableElicitation = serviceStartOptions.InsecureDisableElicitation,
- Tool = serviceStartOptions.Tool,
- };
+ // Register MCP runtimes
+ services.AddSingleton();
- if (serviceStartOptions.Mode == ModeTypes.NamespaceProxy)
+ ConfigureToolLoadersAndDiscoveryStrategies(services, serviceStartOptions);
+
+ var mcpServerOptions = services.AddOptions()
+ .Configure>(
+ (mcpServerOptions, mcpRuntime, serverConfig) =>
+ {
+
+ var entryAssembly = Assembly.GetEntryAssembly();
+
+ mcpServerOptions.ProtocolVersion = "2024-11-05";
+ mcpServerOptions.ServerInfo = new Implementation
+ {
+ Name = serverConfig.Value.Name,
+ Version = serverConfig.Value.Version
+ };
+
+ mcpServerOptions.Handlers = new()
+ {
+ CallToolHandler = mcpRuntime.CallToolHandler,
+ ListToolsHandler = mcpRuntime.ListToolsHandler,
+ };
+
+ // Add instructions for the server
+ mcpServerOptions.ServerInstructions = GetServerInstructions();
+ });
+
+ var mcpServerBuilder = services.AddMcpServer();
+
+ if (serviceStartOptions.Transport == TransportTypes.Http)
{
- if (defaultToolLoaderOptions.Namespace == null || defaultToolLoaderOptions.Namespace.Length == 0)
- {
- defaultToolLoaderOptions = defaultToolLoaderOptions with { Namespace = ["extension"] };
- }
+ mcpServerBuilder.WithHttpTransport();
+ }
+ else
+ {
+ mcpServerBuilder.WithStdioServerTransport();
}
- services.AddSingleton(defaultToolLoaderOptions);
- services.AddSingleton(Options.Create(defaultToolLoaderOptions));
+ return services;
+ }
+ internal static void ConfigureToolLoadersAndDiscoveryStrategies(IServiceCollection services, ServiceStartOptions serviceStartOptions)
+ {
// Register tool loader strategies
services.AddSingleton();
services.AddSingleton();
@@ -77,8 +102,25 @@ public static IServiceCollection AddAzureMcpServer(this IServiceCollection servi
services.AddSingleton();
services.AddSingleton();
- // Register MCP runtimes
- services.AddSingleton();
+ // Register default tool loader options from service start options
+ var defaultToolLoaderOptions = new ToolLoaderOptions
+ {
+ Namespace = serviceStartOptions.Namespace,
+ ReadOnly = serviceStartOptions.ReadOnly ?? false,
+ InsecureDisableElicitation = serviceStartOptions.InsecureDisableElicitation,
+ Tool = serviceStartOptions.Tool,
+ };
+
+ if (serviceStartOptions.Mode == ModeTypes.NamespaceProxy)
+ {
+ if (defaultToolLoaderOptions.Namespace == null || defaultToolLoaderOptions.Namespace.Length == 0)
+ {
+ defaultToolLoaderOptions = defaultToolLoaderOptions with { Namespace = ["extension"] };
+ }
+ }
+
+ services.AddSingleton(defaultToolLoaderOptions);
+ services.AddSingleton(Options.Create(defaultToolLoaderOptions));
// Register MCP discovery strategies based on proxy mode
if (serviceStartOptions.Mode == ModeTypes.SingleToolProxy)
@@ -207,45 +249,74 @@ public static IServiceCollection AddAzureMcpServer(this IServiceCollection servi
return new CompositeToolLoader(toolLoaders, loggerFactory.CreateLogger());
});
}
+ }
- var mcpServerOptions = services
- .AddOptions()
- .Configure((mcpServerOptions, mcpRuntime) =>
+ ///
+ /// Using configures .
+ ///
+ /// Service Collection to add configuration logic to.
+ public static void ConfigureMcpServerOptions(this IServiceCollection services)
+ {
+ services.AddOptions()
+ .BindConfiguration(string.Empty)
+ .Configure>((options, rootConfiguration, serviceStartOptions) =>
{
- var mcpServerOptionsBuilder = services.AddOptions();
- var entryAssembly = Assembly.GetEntryAssembly();
- var assemblyName = entryAssembly?.GetName();
- var serverName = entryAssembly?.GetCustomAttribute()?.Title ?? DefaultServerName;
+ var collectTelemetry = rootConfiguration.GetValue("AZURE_MCP_COLLECT_TELEMETRY");
+ var isOtelExporterEnabled = rootConfiguration.GetValue("AZURE_MCP_ENABLE_OTLP_EXPORTER");
+ var applicationInsightsString = rootConfiguration.GetValue("APPLICATIONINSIGHTS_CONNECTION_STRING");
- mcpServerOptions.ProtocolVersion = "2024-11-05";
- mcpServerOptions.ServerInfo = new Implementation
- {
- Name = serverName,
- Version = assemblyName?.Version?.ToString() ?? "1.0.0-beta"
- };
+ var transport = serviceStartOptions.Value.Transport;
+ var isTelemetryEnabledEnvironment = collectTelemetry.HasValue
+ ? collectTelemetry.Value
+ : true;
+ var isStdioTransport = string.IsNullOrEmpty(transport)
+ || string.Equals(transport, TransportTypes.StdIo, StringComparison.OrdinalIgnoreCase);
- mcpServerOptions.Handlers = new()
+ var entryAssembly = Assembly.GetEntryAssembly();
+ if (entryAssembly == null)
{
- CallToolHandler = mcpRuntime.CallToolHandler,
- ListToolsHandler = mcpRuntime.ListToolsHandler,
- };
+ throw new InvalidOperationException("Should be a managed assembly as entry assembly.");
+ }
+
+ options.Version = GetServerVersion(entryAssembly);
+ options.ApplicationInsightsConnectionString = applicationInsightsString;
- // Add instructions for the server
- mcpServerOptions.ServerInstructions = GetServerInstructions();
+ // if transport is not set (default to stdio) or is set to stdio, enable telemetry
+ // telemetry is disabled for HTTP transport
+ options.IsTelemetryEnabled = isTelemetryEnabledEnvironment && isStdioTransport;
+ options.IsOtelExporterEnabled = isOtelExporterEnabled.HasValue
+ ? isOtelExporterEnabled.Value
+ : false;
});
- var mcpServerBuilder = services.AddMcpServer();
+ services.AddSingleton, AzureMcpServerConfigurationValidator>();
+ }
- if (serviceStartOptions.Transport == TransportTypes.Http)
+ ///
+ /// Gets the version information for the server. Uses logic from Azure SDK for .NET to generate the same version string.
+ /// https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/src/Pipeline/UserAgentPolicy.cs#L91
+ /// For example, an informational version of "6.14.0-rc.116+54d611f7" will return "6.14.0-rc.116"
+ ///
+ /// The caller assembly to extract name and version information from.
+ /// A version string.
+ internal static string GetServerVersion(Assembly entryAssembly)
+ {
+ AssemblyInformationalVersionAttribute? versionAttribute = entryAssembly.GetCustomAttribute();
+ if (versionAttribute == null)
{
- mcpServerBuilder.WithHttpTransport();
+ throw new InvalidOperationException(
+ $"{nameof(AssemblyInformationalVersionAttribute)} is required on client SDK assembly '{entryAssembly.FullName}'.");
}
- else
+
+ string version = versionAttribute.InformationalVersion;
+
+ int hashSeparator = version.IndexOf('+');
+ if (hashSeparator != -1)
{
- mcpServerBuilder.WithStdioServerTransport();
+ version = version.Substring(0, hashSeparator);
}
- return services;
+ return version;
}
///
diff --git a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs
index 84ba7d534d..1ae8927e9d 100644
--- a/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs
+++ b/core/Azure.Mcp.Core/src/Areas/Server/Commands/ServiceStartCommand.cs
@@ -14,9 +14,11 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;
using Microsoft.Identity.Web;
using OpenTelemetry.Logs;
@@ -55,7 +57,7 @@ public sealed class ServiceStartCommand : BaseCommand
///
public override ToolMetadata Metadata => new() { Destructive = false, ReadOnly = true };
- public static Action ConfigureServices { get; set; } = _ => { };
+ public static Action ConfigureServices { get; set; } = (_) => { };
public static Func InitializeServicesAsync { get; set; } = _ => Task.CompletedTask;
@@ -332,41 +334,39 @@ private IHost CreateHost(ServiceStartOptions serverOptions)
/// An IHost instance configured for STDIO transport.
private IHost CreateStdioHost(ServiceStartOptions serverOptions)
{
- return Host.CreateDefaultBuilder()
- .ConfigureLogging(logging =>
- {
- logging.ClearProviders();
- logging.ConfigureOpenTelemetryLogger();
- logging.AddEventSourceLogger();
+ var builder = Host.CreateApplicationBuilder();
- if (serverOptions.Debug)
- {
- // Configure console logger to emit Debug+ to stderr so tests can capture logs from StandardError
- logging.AddConsole(options =>
- {
- options.LogToStandardErrorThreshold = LogLevel.Debug;
- options.FormatterName = Microsoft.Extensions.Logging.Console.ConsoleFormatterNames.Simple;
- });
- logging.AddSimpleConsole(simple =>
- {
- simple.ColorBehavior = Microsoft.Extensions.Logging.Console.LoggerColorBehavior.Disabled;
- simple.IncludeScopes = false;
- simple.SingleLine = true;
- simple.TimestampFormat = "[HH:mm:ss] ";
- });
- logging.AddFilter("Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider", LogLevel.Debug);
- logging.SetMinimumLevel(LogLevel.Debug);
- }
- })
- .ConfigureServices(services =>
+ var logging = builder.Logging;
+
+ logging.ClearProviders();
+ logging.AddEventSourceLogger();
+
+ if (serverOptions.Debug)
+ {
+ // Configure console logger to emit Debug+ to stderr so tests can capture logs from StandardError
+ logging.AddConsole(options =>
+ {
+ options.LogToStandardErrorThreshold = LogLevel.Debug;
+ options.FormatterName = ConsoleFormatterNames.Simple;
+ });
+ logging.AddSimpleConsole(simple =>
{
- // Configure the outgoing authentication strategy.
- services.AddSingleIdentityTokenCredentialProvider();
+ simple.ColorBehavior = LoggerColorBehavior.Disabled;
+ simple.IncludeScopes = false;
+ simple.SingleLine = true;
+ simple.TimestampFormat = "[HH:mm:ss] ";
+ });
+ logging.AddFilter("Microsoft.Extensions.Logging.Console.ConsoleLoggerProvider", LogLevel.Debug);
+ logging.SetMinimumLevel(LogLevel.Debug);
+ }
+
+ // Configure the outgoing authentication strategy.
+ builder.Services.AddSingleIdentityTokenCredentialProvider();
+
+ ConfigureServices(builder);
+ ConfigureMcpServer(builder.Services, serverOptions);
- ConfigureServices(services);
- ConfigureMcpServer(services, serverOptions);
- })
- .Build();
+ return builder.Build();
}
///
@@ -380,7 +380,6 @@ private IHost CreateHttpHost(ServiceStartOptions serverOptions)
// Configure logging
builder.Logging.ClearProviders();
- builder.Logging.ConfigureOpenTelemetryLogger();
builder.Logging.AddEventSourceLogger();
builder.Logging.AddConsole();
@@ -465,7 +464,7 @@ private IHost CreateHttpHost(ServiceStartOptions serverOptions)
});
// Configure services
- ConfigureServices(services); // Our static callback hook
+ ConfigureServices(builder); // Our static callback hook
ConfigureMcpServer(services, serverOptions);
WebApplication app = builder.Build();
@@ -555,7 +554,6 @@ private IHost CreateIncomingAuthDisabledHttpHost(ServiceStartOptions serverOptio
// Configure logging
builder.Logging.ClearProviders();
- builder.Logging.ConfigureOpenTelemetryLogger();
builder.Logging.AddEventSourceLogger();
builder.Logging.AddConsole();
@@ -579,7 +577,7 @@ private IHost CreateIncomingAuthDisabledHttpHost(ServiceStartOptions serverOptio
});
// Configure services
- ConfigureServices(services); // Our static callback hook
+ ConfigureServices(builder); // Our static callback hook
ConfigureMcpServer(services, serverOptions);
// We still use the multi-user, HTTP context-aware caching strategy here
diff --git a/core/Azure.Mcp.Core/src/Azure.Mcp.Core.csproj b/core/Azure.Mcp.Core/src/Azure.Mcp.Core.csproj
index 094e8ce931..53ed202815 100644
--- a/core/Azure.Mcp.Core/src/Azure.Mcp.Core.csproj
+++ b/core/Azure.Mcp.Core/src/Azure.Mcp.Core.csproj
@@ -1,6 +1,8 @@
true
+
+ true
true
diff --git a/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs b/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs
index 6e54a20056..fd2163f9ca 100644
--- a/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs
+++ b/core/Azure.Mcp.Core/src/Commands/CommandFactory.cs
@@ -8,8 +8,10 @@
using System.Text.Encodings.Web;
using System.Text.Json.Serialization;
using Azure.Mcp.Core.Areas;
+using Azure.Mcp.Core.Configuration;
using Azure.Mcp.Core.Services.Telemetry;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using static Azure.Mcp.Core.Services.Telemetry.TelemetryConstants;
namespace Azure.Mcp.Core.Commands;
@@ -22,6 +24,7 @@ public class CommandFactory
private readonly RootCommand _rootCommand;
private readonly CommandGroup _rootGroup;
private readonly ModelsJsonContext _srcGenWithOptions;
+ private readonly string _serverName;
public const char Separator = '_';
@@ -47,14 +50,17 @@ public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOp
}
}
- internal const string RootCommandGroupName = "azmcp";
-
- public CommandFactory(IServiceProvider serviceProvider, IEnumerable serviceAreas, ITelemetryService telemetryService, ILogger logger)
+ public CommandFactory(IServiceProvider serviceProvider,
+ IEnumerable serviceAreas,
+ ITelemetryService telemetryService,
+ IOptions serverConfig,
+ ILogger logger)
{
_serviceAreas = serviceAreas?.ToArray() ?? throw new ArgumentNullException(nameof(serviceAreas));
_serviceProvider = serviceProvider;
_logger = logger;
- _rootGroup = new CommandGroup(RootCommandGroupName, "Azure MCP Server");
+ _serverName = serverConfig.Value.Name;
+ _rootGroup = new CommandGroup(_serverName, serverConfig.Value.DisplayName);
_rootCommand = CreateRootCommand();
_commandMap = CreateCommandDictionary(_rootGroup);
_telemetryService = telemetryService;
@@ -136,7 +142,7 @@ private void RegisterCommandGroup()
// Create a temporary root node to register all the area's subgroups and commands to.
// Use this to create the mapping of all commands to that area.
- var tempRoot = new CommandGroup(RootCommandGroupName, string.Empty);
+ var tempRoot = new CommandGroup(_serverName, string.Empty);
tempRoot.AddSubGroup(commandTree);
var commandDictionary = CreateCommandDictionary(tempRoot);
diff --git a/core/Azure.Mcp.Core/src/Configuration/AzureMcpServerConfiguration.cs b/core/Azure.Mcp.Core/src/Configuration/AzureMcpServerConfiguration.cs
index aa2c922a33..83812e4c0a 100644
--- a/core/Azure.Mcp.Core/src/Configuration/AzureMcpServerConfiguration.cs
+++ b/core/Azure.Mcp.Core/src/Configuration/AzureMcpServerConfiguration.cs
@@ -1,15 +1,53 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.ComponentModel.DataAnnotations;
+
namespace Azure.Mcp.Core.Configuration;
+///
+/// Configuration settings for the MCP server.
+///
public class AzureMcpServerConfiguration
{
- public const string DefaultName = "Azure.Mcp.Server";
+ ///
+ /// The default prefix for the MCP server commands and help menus.
+ ///
+ [Required]
+ public required string Prefix { get; set; }
+
+ ///
+ /// The name of the MCP server. (i.e. Azure.Mcp.Server)
+ ///
+ [Required]
+ public required string Name { get; set; }
- public string Name { get; set; } = DefaultName;
+ ///
+ /// The display name of the MCP server.
+ ///
+ [Required]
+ public required string DisplayName { get; set; }
- public string Version { get; set; } = "1.0.0-beta";
+ ///
+ /// The version of the MCP server.
+ ///
+ [Required]
+ public required string Version { get; set; }
+ ///
+ /// Indicates whether telemetry is enabled.
+ ///
+ [Required]
public bool IsTelemetryEnabled { get; set; } = true;
+
+ ///
+ /// Indicates whether to enable Open Telemetry Exporter when is true.
+ ///
+ /// The application insights connection string to use if is true.
+ ///
+ public string? ApplicationInsightsConnectionString { get; set; }
}
diff --git a/core/Azure.Mcp.Core/src/Configuration/AzureMcpServerConfigurationValidator.cs b/core/Azure.Mcp.Core/src/Configuration/AzureMcpServerConfigurationValidator.cs
new file mode 100644
index 0000000000..adca039315
--- /dev/null
+++ b/core/Azure.Mcp.Core/src/Configuration/AzureMcpServerConfigurationValidator.cs
@@ -0,0 +1,11 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.Extensions.Options;
+
+namespace Azure.Mcp.Core.Configuration;
+
+[OptionsValidator]
+public partial class AzureMcpServerConfigurationValidator : IValidateOptions
+{
+}
diff --git a/core/Azure.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs b/core/Azure.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs
index cbb7fc19fa..e3bc8c8c14 100644
--- a/core/Azure.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs
+++ b/core/Azure.Mcp.Core/src/Extensions/OpenTelemetryExtensions.cs
@@ -1,14 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using System.Reflection;
using System.Runtime.InteropServices;
-using Azure.Mcp.Core.Areas.Server.Options;
using Azure.Mcp.Core.Configuration;
using Azure.Mcp.Core.Services.Telemetry;
using Azure.Monitor.OpenTelemetry.Exporter; // Don't believe this is unused, it is needed for UseAzureMonitorExporter
using Microsoft.Extensions.Azure;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using OpenTelemetry.Logs;
@@ -20,34 +20,9 @@ namespace Azure.Mcp.Core.Extensions;
public static class OpenTelemetryExtensions
{
- private const string DefaultAppInsights = "InstrumentationKey=21e003c0-efee-4d3f-8a98-1868515aa2c9;IngestionEndpoint=https://centralus-2.in.applicationinsights.azure.com/;LiveEndpoint=https://centralus.livediagnostics.monitor.azure.com/;ApplicationId=f14f6a2d-6405-4f88-bd58-056f25fe274f";
-
- public static void ConfigureOpenTelemetry(this IServiceCollection services)
+ public static void ConfigureTelemetryServices(this IServiceCollection services,
+ IHostEnvironment hostEnvironment, IConfiguration configuration)
{
- services.AddOptions()
- .Configure>((options, serviceStartOptions) =>
- {
- // Assembly.GetEntryAssembly is used to retrieve the version of the server application as that is
- // the assembly that will run the tool calls.
- var entryAssembly = Assembly.GetEntryAssembly();
- if (entryAssembly != null)
- {
- options.Version = GetServerVersion(entryAssembly);
- }
-
- var collectTelemetry = Environment.GetEnvironmentVariable("AZURE_MCP_COLLECT_TELEMETRY");
-
- var transport = serviceStartOptions.Value.Transport;
-
- bool isTelemetryEnabledEnvironment = string.IsNullOrEmpty(collectTelemetry) || (bool.TryParse(collectTelemetry, out var shouldCollect) && shouldCollect);
-
- bool isStdioTransport = string.IsNullOrEmpty(transport) || string.Equals(transport, "stdio", StringComparison.OrdinalIgnoreCase);
-
- // if transport is not set (default to stdio) or is set to stdio, enable telemetry
- // telemetry is disabled for HTTP transport
- options.IsTelemetryEnabled = isTelemetryEnabledEnvironment && isStdioTransport;
- });
-
services.AddSingleton();
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@@ -67,95 +42,104 @@ public static void ConfigureOpenTelemetry(this IServiceCollection services)
services.AddSingleton();
}
- EnableAzureMonitor(services);
+ ConfigureOpenTelemetry(services, hostEnvironment, configuration);
}
- public static void ConfigureOpenTelemetryLogger(this ILoggingBuilder builder)
+ private static void ConfigureOpenTelemetry(this IServiceCollection services,
+ IHostEnvironment hostEnvironment,
+ IConfiguration configuration)
{
- builder.AddOpenTelemetry(logger =>
+ if (hostEnvironment.IsDevelopment())
{
- logger.AddProcessor(new TelemetryLogRecordEraser());
- });
- }
+ services.AddSingleton(sp =>
+ {
+ var logger = sp.GetRequiredService();
+ var forwarder = new AzureEventSourceLogForwarder(logger);
+ forwarder.Start();
+ return forwarder;
+ });
+ }
- private static void EnableAzureMonitor(this IServiceCollection services)
- {
-#if DEBUG
- services.AddSingleton(sp =>
- {
- var forwarder = new AzureEventSourceLogForwarder(sp.GetRequiredService());
- forwarder.Start();
- return forwarder;
- });
-#endif
+ services.AddSingleton();
- var appInsightsConnectionString = Environment.GetEnvironmentVariable("APPLICATIONINSIGHTS_CONNECTION_STRING");
+ // Note: Turn on all the signals for metrics, tracing, and logging.
+ // The configuration for each of these components is done further down
+ // because we need to resolve classes using IServiceProvider.
+ //
+ // There is a single resource we gather information from.
+ var otelBuilder = services.AddOpenTelemetry()
+ .ConfigureResource(builder =>
+ {
+ builder.AddDetector(sp => sp.GetRequiredService());
+ })
+ .WithMetrics()
+ .WithTracing()
+ .WithLogging(builder =>
+ {
+ builder.AddProcessor(new TelemetryLogRecordEraser());
+ });
- if (string.IsNullOrEmpty(appInsightsConnectionString))
+ // Metrics configuration
+ services.ConfigureOpenTelemetryMeterProvider((sp, builder) =>
{
- appInsightsConnectionString = DefaultAppInsights;
- }
+ var config = sp.GetRequiredService>().Value;
+ if (!config.IsTelemetryEnabled)
+ {
+ return;
+ }
+
+ builder.AddAzureMonitorMetricExporter(options =>
+ {
+ options.ConnectionString = config.ApplicationInsightsConnectionString;
+ });
+ if (config.IsOtelExporterEnabled)
+ {
+ builder.AddOtlpExporter();
+ }
+ });
+
+ // Tracer configuration
services.ConfigureOpenTelemetryTracerProvider((sp, builder) =>
{
- var serverConfig = sp.GetRequiredService>();
- if (!serverConfig.Value.IsTelemetryEnabled)
+ var config = sp.GetRequiredService>().Value;
+ if (!config.IsTelemetryEnabled)
{
return;
}
- builder.AddSource(serverConfig.Value.Name);
- });
+ // Matches the ActivitySource created in ITelemetryService.
+ builder.AddSource(config.Name);
- var otelBuilder = services.AddOpenTelemetry()
- .ConfigureResource(r =>
+ builder.AddAzureMonitorTraceExporter(options =>
{
- var version = Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString();
-
- r.AddService("azmcp", version)
- .AddTelemetrySdk();
+ options.ConnectionString = config.ApplicationInsightsConnectionString;
});
-#if RELEASE
- otelBuilder.UseAzureMonitorExporter(options =>
- {
- options.ConnectionString = appInsightsConnectionString;
+ if (config.IsOtelExporterEnabled)
+ {
+ builder.AddOtlpExporter();
+ }
});
-#endif
- var enableOtlp = Environment.GetEnvironmentVariable("AZURE_MCP_ENABLE_OTLP_EXPORTER");
- if (!string.IsNullOrEmpty(enableOtlp) && bool.TryParse(enableOtlp, out var shouldEnable) && shouldEnable)
+ // Tracer configuration
+ services.ConfigureOpenTelemetryLoggerProvider((sp, builder) =>
{
- otelBuilder.WithTracing(tracing => tracing.AddOtlpExporter())
- .WithMetrics(metrics => metrics.AddOtlpExporter())
- .WithLogging(logging => logging.AddOtlpExporter());
- }
- }
-
- ///
- /// Gets the version information for the server. Uses logic from Azure SDK for .NET to generate the same version string.
- /// https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/core/System.ClientModel/src/Pipeline/UserAgentPolicy.cs#L91
- /// For example, an informational version of "6.14.0-rc.116+54d611f7" will return "6.14.0-rc.116"
- ///
- /// The entry assembly to extract name and version information from.
- /// A version string.
- internal static string GetServerVersion(Assembly entryAssembly)
- {
- AssemblyInformationalVersionAttribute? versionAttribute = entryAssembly.GetCustomAttribute();
- if (versionAttribute == null)
- {
- throw new InvalidOperationException(
- $"{nameof(AssemblyInformationalVersionAttribute)} is required on client SDK assembly '{entryAssembly.FullName}'.");
- }
-
- string version = versionAttribute.InformationalVersion;
+ var config = sp.GetRequiredService>().Value;
+ if (!config.IsTelemetryEnabled)
+ {
+ return;
+ }
- int hashSeparator = version.IndexOf('+');
- if (hashSeparator != -1)
- {
- version = version.Substring(0, hashSeparator);
- }
+ builder.AddAzureMonitorLogExporter(options =>
+ {
+ options.ConnectionString = config.ApplicationInsightsConnectionString;
+ });
- return version;
+ if (config.IsOtelExporterEnabled)
+ {
+ builder.AddOtlpExporter();
+ }
+ });
}
}
diff --git a/core/Azure.Mcp.Core/src/Services/Telemetry/AzureMcpServerResourceDetector.cs b/core/Azure.Mcp.Core/src/Services/Telemetry/AzureMcpServerResourceDetector.cs
new file mode 100644
index 0000000000..f59c8c25b4
--- /dev/null
+++ b/core/Azure.Mcp.Core/src/Services/Telemetry/AzureMcpServerResourceDetector.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Azure.Mcp.Core.Configuration;
+using Microsoft.Extensions.Options;
+using OpenTelemetry.Resources;
+
+namespace Azure.Mcp.Core.Services.Telemetry;
+
+///
+/// Adds the MCP server as an application to gather telemetry from.
+///
+/// MCP server configuration
+public class AzureMcpServerResourceDetector(IOptions serverConfiguration)
+ : IResourceDetector
+{
+ private readonly AzureMcpServerConfiguration _serverConfiguration = serverConfiguration.Value;
+
+ public Resource Detect()
+ {
+ return ResourceBuilder.CreateDefault()
+ .AddService(_serverConfiguration.Prefix, serviceVersion: _serverConfiguration.Version)
+ .AddTelemetrySdk()
+ .Build();
+ }
+}
diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs
index 311ebc196a..420f8a966b 100644
--- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs
+++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/CommandFactoryHelpers.cs
@@ -5,9 +5,11 @@
using Azure.Mcp.Core.Areas;
using Azure.Mcp.Core.Areas.Group;
using Azure.Mcp.Core.Areas.Server;
+using Azure.Mcp.Core.Areas.Server.Options;
using Azure.Mcp.Core.Areas.Subscription;
using Azure.Mcp.Core.Areas.Tools;
using Azure.Mcp.Core.Commands;
+using Azure.Mcp.Core.Configuration;
using Azure.Mcp.Core.Services.Telemetry;
using Azure.Mcp.Tools.Acr;
using Azure.Mcp.Tools.Aks;
@@ -21,6 +23,7 @@
using Azure.Mcp.Tools.CloudArchitect;
using Azure.Mcp.Tools.Cosmos;
using Azure.Mcp.Tools.Deploy;
+using Azure.Mcp.Tools.Deploy.Services.Util;
using Azure.Mcp.Tools.EventGrid;
using Azure.Mcp.Tools.Extension;
using Azure.Mcp.Tools.Foundry;
@@ -45,6 +48,7 @@
using Azure.Mcp.Tools.Workbooks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using ModelContextProtocol.Protocol;
namespace Azure.Mcp.Core.UnitTests.Areas.Server;
@@ -98,7 +102,8 @@ public static CommandFactory CreateCommandFactory(IServiceProvider? serviceProvi
var services = serviceProvider ?? CreateDefaultServiceProvider();
var logger = services.GetRequiredService>();
var telemetryService = services.GetService() ?? new NoOpTelemetryService();
- var commandFactory = new CommandFactory(services, areaSetups, telemetryService, logger);
+ var serviceOptions = services.GetRequiredService>();
+ var commandFactory = new CommandFactory(services, areaSetups, telemetryService, serviceOptions, logger);
return commandFactory;
}
@@ -110,6 +115,14 @@ public static IServiceProvider CreateDefaultServiceProvider()
public static IServiceCollection SetupCommonServices()
{
+ var mcpServerConfiguration = new AzureMcpServerConfiguration
+ {
+ DisplayName = "Test Display Name",
+ Name = "Test.Mcp.Server",
+ Prefix = "test-azmcp",
+ Version = "0.0.1-beta.1",
+ IsTelemetryEnabled = true,
+ };
IAreaSetup[] areaSetups = [
// Core areas
new SubscriptionSetup(),
@@ -154,6 +167,7 @@ public static IServiceCollection SetupCommonServices()
var builder = new ServiceCollection()
.AddLogging()
+ .AddSingleton(Microsoft.Extensions.Options.Options.Create(mcpServerConfiguration))
.AddSingleton();
foreach (var area in areaSetups)
diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategyTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategyTests.cs
index 3166f295bd..1ae1fafef6 100644
--- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategyTests.cs
+++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Server/Commands/Discovery/ConsolidatedToolDiscoveryStrategyTests.cs
@@ -1,12 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using Azure.Mcp.Core.Areas;
using Azure.Mcp.Core.Areas.Server.Commands.Discovery;
-using Azure.Mcp.Core.Areas.Server.Models;
using Azure.Mcp.Core.Areas.Server.Options;
using Azure.Mcp.Core.Commands;
+using Azure.Mcp.Core.Configuration;
+using Azure.Mcp.Core.Services.Telemetry;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Xunit;
namespace Azure.Mcp.Core.UnitTests.Areas.Server.Commands.Discovery;
@@ -21,8 +23,15 @@ private static ConsolidatedToolDiscoveryStrategy CreateStrategy(
var factory = commandFactory ?? CommandFactoryHelpers.CreateCommandFactory();
var serviceProvider = CommandFactoryHelpers.SetupCommonServices().BuildServiceProvider();
var startOptions = Microsoft.Extensions.Options.Options.Create(options ?? new ServiceStartOptions());
- var logger = NSubstitute.Substitute.For>();
- var strategy = new ConsolidatedToolDiscoveryStrategy(factory, serviceProvider, startOptions, logger);
+
+ var strategy = new ConsolidatedToolDiscoveryStrategy(factory,
+ serviceProvider,
+ serviceProvider.GetRequiredService(),
+ startOptions,
+ serviceProvider.GetRequiredService>(),
+ serviceProvider.GetRequiredService>(),
+ serviceProvider.GetRequiredService>());
+
if (entryPoint != null)
{
strategy.EntryPoint = entryPoint;
diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs
index ccbad378da..db21e9eed5 100644
--- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs
+++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Areas/Tools/UnitTests/ToolsListCommandTests.cs
@@ -9,12 +9,14 @@
using Azure.Mcp.Core.Areas.Tools.Commands;
using Azure.Mcp.Core.Areas.Tools.Options;
using Azure.Mcp.Core.Commands;
+using Azure.Mcp.Core.Configuration;
using Azure.Mcp.Core.Extensions;
using Azure.Mcp.Core.Models.Command;
using Azure.Mcp.Core.Services.Telemetry;
using Azure.Mcp.Core.UnitTests.Areas.Server;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using NSubstitute;
using Xunit;
@@ -396,12 +398,14 @@ public async Task ExecuteAsync_WithEmptyCommandFactory_ReturnsEmptyResults()
var logger = tempServiceProvider.GetRequiredService>();
var telemetryService = Substitute.For();
var emptyAreaSetups = Array.Empty();
+ var serverConfig = Substitute.For>();
// Create a NEW service collection just for the empty command factory
var finalCollection = new ServiceCollection();
finalCollection.AddLogging();
- var emptyCommandFactory = new CommandFactory(tempServiceProvider, emptyAreaSetups, telemetryService, logger);
+ var emptyCommandFactory = new CommandFactory(tempServiceProvider, emptyAreaSetups,
+ telemetryService, serverConfig, logger);
finalCollection.AddSingleton(emptyCommandFactory);
var emptyServiceProvider = finalCollection.BuildServiceProvider();
diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Commands/CommandFactoryTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Commands/CommandFactoryTests.cs
index 3a66b95f27..100d15274f 100644
--- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Commands/CommandFactoryTests.cs
+++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Commands/CommandFactoryTests.cs
@@ -4,9 +4,11 @@
using System.CommandLine;
using Azure.Mcp.Core.Areas;
using Azure.Mcp.Core.Commands;
+using Azure.Mcp.Core.Configuration;
using Azure.Mcp.Core.Services.Telemetry;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using NSubstitute;
using Xunit;
@@ -22,6 +24,8 @@ public class CommandFactoryTests
private readonly IServiceProvider _serviceProvider;
private readonly ILogger _logger;
private readonly ITelemetryService _telemetryService;
+ private readonly IOptions _defaultServerConfigurationOptions;
+ private readonly AzureMcpServerConfiguration _defaultServerConfiguration;
public CommandFactoryTests()
{
@@ -30,6 +34,18 @@ public CommandFactoryTests()
_serviceProvider = services.BuildServiceProvider();
_logger = Substitute.For>();
_telemetryService = Substitute.For();
+
+ _defaultServerConfiguration = new AzureMcpServerConfiguration
+ {
+ DisplayName = "Command Factory Test Display Name",
+ Name = "Test.Mcp.Server.CommandFactory",
+ Prefix = "test-azmcp-command-factory",
+ Version = "0.0.1-beta.1",
+ IsTelemetryEnabled = false
+ };
+
+ _defaultServerConfigurationOptions = Substitute.For>();
+ _defaultServerConfigurationOptions.Value.Returns(_defaultServerConfiguration);
}
[Fact]
@@ -123,7 +139,7 @@ public void Constructor_Throws_AreaSetups_Duplicate()
// Act & Assert
Assert.Throws(() =>
- new CommandFactory(_serviceProvider, serviceAreas, _telemetryService, _logger));
+ new CommandFactory(_serviceProvider, serviceAreas, _telemetryService, _defaultServerConfigurationOptions, _logger));
}
[Fact]
@@ -138,7 +154,7 @@ public void Constructor_Throws_AreaSetups_EmptyName()
// Act & Assert
Assert.Throws(() =>
- new CommandFactory(_serviceProvider, serviceAreas, _telemetryService, _logger));
+ new CommandFactory(_serviceProvider, serviceAreas, _telemetryService, _defaultServerConfigurationOptions, _logger));
}
[Theory]
@@ -153,7 +169,7 @@ public void GetServiceArea_Existing_SetupArea(string commandName, string expecte
var area3 = CreateIAreaSetup("name3");
var serviceAreas = new List { area1, area3, area2 };
- var factory = new CommandFactory(_serviceProvider, serviceAreas, _telemetryService, _logger);
+ var factory = new CommandFactory(_serviceProvider, serviceAreas, _telemetryService, _defaultServerConfigurationOptions, _logger);
// Act
// Try in the case that the root prefix is not used. This is in the case that the tool
@@ -173,7 +189,8 @@ public void GetServiceArea_DoesNotExist()
var area3 = CreateIAreaSetup("name3");
var serviceAreas = new List { area1, area2, area3 };
- var factory = new CommandFactory(_serviceProvider, serviceAreas, _telemetryService, _logger);
+ var factory = new CommandFactory(_serviceProvider, serviceAreas, _telemetryService,
+ _defaultServerConfigurationOptions, _logger);
// All commands created in command factory are prefixed with the root command group, "azmcp".
var commandNameToTry = "azmcp" + CommandFactory.Separator + "name0_subgroup2_directCommand4";
diff --git a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Services/Telemetry/TelemetryServiceTests.cs b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Services/Telemetry/TelemetryServiceTests.cs
index b75dab5126..87c8918ff4 100644
--- a/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Services/Telemetry/TelemetryServiceTests.cs
+++ b/core/Azure.Mcp.Core/tests/Azure.Mcp.Core.UnitTests/Services/Telemetry/TelemetryServiceTests.cs
@@ -18,6 +18,8 @@ public class TelemetryServiceTests
private const string TestMacAddressHash = "test-hash";
private readonly AzureMcpServerConfiguration _testConfiguration = new()
{
+ Prefix = "temp-prefix",
+ DisplayName = "Telemetry service display name",
Name = "TestService",
Version = "1.0.0",
IsTelemetryEnabled = true
@@ -147,6 +149,8 @@ public async Task StartActivity_WithInvalidActivityId_ShouldHandleGracefully(str
// Arrange
var configuration = new AzureMcpServerConfiguration
{
+ Prefix = "temp-prefix-a",
+ DisplayName = "Telemetry service display name A",
Name = "TestService",
Version = "1.0.0",
IsTelemetryEnabled = true
@@ -177,6 +181,8 @@ public void StartActivity_WithoutInitialization_Throws()
// Arrange
var configuration = new AzureMcpServerConfiguration
{
+ Prefix = "temp-prefix-a",
+ DisplayName = "Telemetry service display name A",
Name = "TestService",
Version = "1.0.0",
IsTelemetryEnabled = true
@@ -208,11 +214,12 @@ public async Task StartActivity_WhenInitializationFails_Throws()
var configuration = new AzureMcpServerConfiguration
{
+ Prefix = "temp-prefix-a",
+ DisplayName = "Telemetry service display name A",
Name = "TestService",
Version = "1.0.0",
IsTelemetryEnabled = true
};
-
var mockOptions = Substitute.For>();
mockOptions.Value.Returns(configuration);
@@ -245,6 +252,8 @@ public async Task StartActivity_ReturnsActivityWhenEnabled()
var configuration = new AzureMcpServerConfiguration
{
+ Prefix = "temp-prefix-a",
+ DisplayName = "Telemetry service display name A",
Name = "TestService",
Version = "1.0.0",
IsTelemetryEnabled = true
@@ -277,6 +286,8 @@ public async Task InitializeAsync_InvokedOnce()
// Arrange
var configuration = new AzureMcpServerConfiguration
{
+ Prefix = "temp-prefix-a",
+ DisplayName = "Telemetry service display name A",
Name = "TestService",
Version = "1.0.0",
IsTelemetryEnabled = true
diff --git a/eng/scripts/Analyze-Code.ps1 b/eng/scripts/Analyze-Code.ps1
index b729c5faab..d3f0dea3e3 100644
--- a/eng/scripts/Analyze-Code.ps1
+++ b/eng/scripts/Analyze-Code.ps1
@@ -7,7 +7,10 @@ Push-Location $RepoRoot
try {
Write-Host "Running dotnet format to check for formatting issues..."
$solutionFile = Get-ChildItem -Path . -Filter *.sln | Select-Object -First 1
- dotnet format $solutionFile --verify-no-changes
+
+ # Excluding diagnostics IL2026 and IL3050 due to known issues with source generator
+ # Can be removed when https://github.com/dotnet/sdk/issues/45054 is resolved
+ dotnet format $solutionFile --verify-no-changes --exclude-diagnostics IL2026 IL3050
# Run dotnet format
if ($LASTEXITCODE) {
diff --git a/servers/Azure.Mcp.Server/src/Azure.Mcp.Server.csproj b/servers/Azure.Mcp.Server/src/Azure.Mcp.Server.csproj
index 63cb4138c7..677b498652 100644
--- a/servers/Azure.Mcp.Server/src/Azure.Mcp.Server.csproj
+++ b/servers/Azure.Mcp.Server/src/Azure.Mcp.Server.csproj
@@ -58,6 +58,24 @@
+
+
+ PreserveNewest
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+ PreserveNewest
+
+
+
diff --git a/servers/Azure.Mcp.Server/src/Program.cs b/servers/Azure.Mcp.Server/src/Program.cs
index 7fcfe8250c..e5f89c0359 100644
--- a/servers/Azure.Mcp.Server/src/Program.cs
+++ b/servers/Azure.Mcp.Server/src/Program.cs
@@ -3,6 +3,7 @@
using System.Net;
using Azure.Mcp.Core.Areas;
+using Azure.Mcp.Core.Areas.Server.Commands;
using Azure.Mcp.Core.Commands;
using Azure.Mcp.Core.Services.Azure.ResourceGroup;
using Azure.Mcp.Core.Services.Azure.Subscription;
@@ -11,7 +12,9 @@
using Azure.Mcp.Core.Services.ProcessExecution;
using Azure.Mcp.Core.Services.Telemetry;
using Azure.Mcp.Core.Services.Time;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ServiceStartCommand = Azure.Mcp.Core.Areas.Server.Commands.ServiceStartCommand;
@@ -27,20 +30,26 @@ private static async Task Main(string[] args)
ServiceStartCommand.ConfigureServices = ConfigureServices;
ServiceStartCommand.InitializeServicesAsync = InitializeServicesAsync;
- ServiceCollection services = new();
- ConfigureServices(services);
+ var builder = Host.CreateApplicationBuilder(args);
- services.AddLogging(builder =>
+ builder.Configuration.AddJsonFile("appsettings.Release.json", optional: true);
+
+ builder.Services.AddLogging(builder =>
{
- builder.ConfigureOpenTelemetryLogger();
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
- var serviceProvider = services.BuildServiceProvider();
- await InitializeServicesAsync(serviceProvider);
+ ConfigureServices(builder);
+
+ using var host = builder.Build();
+
+ await InitializeServicesAsync(host.Services);
- var commandFactory = serviceProvider.GetRequiredService();
+ // Starts any IHostedServices
+ await host.StartAsync();
+
+ var commandFactory = host.Services.GetRequiredService();
var rootCommand = commandFactory.RootCommand;
var parseResult = rootCommand.Parse(args);
var status = await parseResult.InvokeAsync();
@@ -58,9 +67,9 @@ private static async Task Main(string[] args)
return 1;
}
}
+
private static IAreaSetup[] RegisterAreas()
{
-
return [
// Register core areas
new Azure.Mcp.Tools.AzureAIBestPractices.AzureAIBestPracticesSetup(),
@@ -177,9 +186,11 @@ private static void WriteResponse(CommandResponse response)
///
///
/// A service collection.
- internal static void ConfigureServices(IServiceCollection services)
+ internal static void ConfigureServices(IHostApplicationBuilder builder)
{
- services.ConfigureOpenTelemetry();
+ var services = builder.Services;
+ services.ConfigureTelemetryServices(builder.Environment, builder.Configuration);
+ services.ConfigureMcpServerOptions();
services.AddMemoryCache();
services.AddSingleton();
diff --git a/servers/Azure.Mcp.Server/src/Properties/launchSettings.json b/servers/Azure.Mcp.Server/src/Properties/launchSettings.json
index 6702182ee8..038a12f744 100644
--- a/servers/Azure.Mcp.Server/src/Properties/launchSettings.json
+++ b/servers/Azure.Mcp.Server/src/Properties/launchSettings.json
@@ -1,17 +1,25 @@
{
- "$schema": "https://json.schemastore.org/launchsettings.json",
- "profiles": {
- "debug-remotemcp": {
- "commandName": "Project",
- "commandLineArgs": "server start --transport http --outgoing-auth-strategy UseHostingEnvironmentIdentity",
- "dotnetRunMessages": true,
- "environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
- "ASPNETCORE_URLS": "http://localhost:1031",
- "AzureAd__TenantId": "70a036f6-8e4d-4615-bad6-149c02e7720d",
- "AzureAd__ClientId": "ca1e0302-d50a-47d7-b5e6-7aff49884bce",
- "AzureAd__Instance": "https://login.microsoftonline.com/"
- }
- }
+ "profiles": {
+ "debug-remotemcp": {
+ "commandName": "Project",
+ "commandLineArgs": "server start --transport http --outgoing-auth-strategy UseHostingEnvironmentIdentity",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development",
+ "ASPNETCORE_URLS": "http://localhost:1031",
+ "AzureAd__TenantId": "70a036f6-8e4d-4615-bad6-149c02e7720d",
+ "AzureAd__ClientId": "ca1e0302-d50a-47d7-b5e6-7aff49884bce",
+ "AzureAd__Instance": "https://login.microsoftonline.com/"
+ },
+ "dotnetRunMessages": true
+ },
+ "local-stdio": {
+ "commandName": "Project",
+ "commandLineArgs": "server start",
+ "environmentVariables": {
+ "DOTNET_ENVIRONMENT": "Development"
+ },
+ "dotnetRunMessages": true
}
-}
+ },
+ "$schema": "https://json.schemastore.org/launchsettings.json"
+}
\ No newline at end of file
diff --git a/servers/Azure.Mcp.Server/src/appsettings.Development.json b/servers/Azure.Mcp.Server/src/appsettings.Development.json
new file mode 100644
index 0000000000..aca8f26f24
--- /dev/null
+++ b/servers/Azure.Mcp.Server/src/appsettings.Development.json
@@ -0,0 +1,22 @@
+{
+ "AZURE_MCP_COLLECT_TELEMETRY": "false",
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "Microsoft.Hosting.Lifetime": "Information"
+ },
+ "Console": {
+ "LogToStandardErrorThreshold": "Debug",
+ "FormatterName": "simple",
+ "FormatterOptions": {
+ "ColorBehavior": "Disabled",
+ "SingleLine": true,
+ "TimestampFormat": "[HH:mm:ss] ",
+ "UseUtcTimestamp": true
+ },
+ "LogLevel": {
+ "Azure.Mcp.Core.Commands.CommandFactory": "Information"
+ }
+ }
+ }
+}
diff --git a/servers/Azure.Mcp.Server/src/appsettings.Release.json b/servers/Azure.Mcp.Server/src/appsettings.Release.json
new file mode 100644
index 0000000000..5b63320791
--- /dev/null
+++ b/servers/Azure.Mcp.Server/src/appsettings.Release.json
@@ -0,0 +1,3 @@
+{
+ "APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=21e003c0-efee-4d3f-8a98-1868515aa2c9;IngestionEndpoint=https://centralus-2.in.applicationinsights.azure.com/;LiveEndpoint=https://centralus.livediagnostics.monitor.azure.com/;ApplicationId=f14f6a2d-6405-4f88-bd58-056f25fe274f"
+}
diff --git a/servers/Azure.Mcp.Server/src/appsettings.json b/servers/Azure.Mcp.Server/src/appsettings.json
new file mode 100644
index 0000000000..418cc40bb5
--- /dev/null
+++ b/servers/Azure.Mcp.Server/src/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Prefix": "azmcp",
+ "Name": "Azure.Mcp.Server",
+ "DisplayName": "Azure MCP Server",
+ "Logging": {
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ }
+}
diff --git a/servers/Fabric.Mcp.Server/src/Program.cs b/servers/Fabric.Mcp.Server/src/Program.cs
index 208cdaa89d..67559604cd 100644
--- a/servers/Fabric.Mcp.Server/src/Program.cs
+++ b/servers/Fabric.Mcp.Server/src/Program.cs
@@ -15,7 +15,9 @@
using Azure.Mcp.Core.Services.ProcessExecution;
using Azure.Mcp.Core.Services.Telemetry;
using Azure.Mcp.Core.Services.Time;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ServiceStartCommand = Azure.Mcp.Core.Areas.Server.Commands.ServiceStartCommand;
@@ -31,17 +33,16 @@ private static async Task Main(string[] args)
ServiceStartCommand.ConfigureServices = ConfigureServices;
ServiceStartCommand.InitializeServicesAsync = InitializeServicesAsync;
- ServiceCollection services = new();
- ConfigureServices(services);
+ var builder = Host.CreateApplicationBuilder();
+ ConfigureServices(builder);
- services.AddLogging(builder =>
+ builder.Services.AddLogging(builder =>
{
- builder.ConfigureOpenTelemetryLogger();
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
- var serviceProvider = services.BuildServiceProvider();
+ var serviceProvider = builder.Services.BuildServiceProvider();
await InitializeServicesAsync(serviceProvider);
var commandFactory = serviceProvider.GetRequiredService();
@@ -132,9 +133,11 @@ private static void WriteResponse(CommandResponse response)
///
///
/// A service collection.
- internal static void ConfigureServices(IServiceCollection services)
+ internal static void ConfigureServices(IHostApplicationBuilder builder)
{
- services.ConfigureOpenTelemetry();
+ var services = builder.Services;
+
+ services.ConfigureTelemetryServices(builder.Environment, builder.Configuration);
services.AddMemoryCache();
services.AddSingleton();
diff --git a/servers/Template.Mcp.Server/src/Program.cs b/servers/Template.Mcp.Server/src/Program.cs
index b58d4adcf0..b456e7c1e7 100644
--- a/servers/Template.Mcp.Server/src/Program.cs
+++ b/servers/Template.Mcp.Server/src/Program.cs
@@ -12,7 +12,9 @@
using Azure.Mcp.Core.Services.ProcessExecution;
using Azure.Mcp.Core.Services.Telemetry;
using Azure.Mcp.Core.Services.Time;
+using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ServiceStartCommand = Azure.Mcp.Core.Areas.Server.Commands.ServiceStartCommand;
@@ -28,17 +30,16 @@ private static async Task Main(string[] args)
ServiceStartCommand.ConfigureServices = ConfigureServices;
ServiceStartCommand.InitializeServicesAsync = InitializeServicesAsync;
- ServiceCollection services = new();
- ConfigureServices(services);
+ var builder = Host.CreateApplicationBuilder();
+ ConfigureServices(builder);
- services.AddLogging(builder =>
+ builder.Services.AddLogging(builder =>
{
- builder.ConfigureOpenTelemetryLogger();
builder.AddConsole();
builder.SetMinimumLevel(LogLevel.Information);
});
- var serviceProvider = services.BuildServiceProvider();
+ var serviceProvider = builder.Services.BuildServiceProvider();
await InitializeServicesAsync(serviceProvider);
var commandFactory = serviceProvider.GetRequiredService();
@@ -126,9 +127,10 @@ private static void WriteResponse(CommandResponse response)
///
///
/// A service collection.
- internal static void ConfigureServices(IServiceCollection services)
+ internal static void ConfigureServices(IHostApplicationBuilder builder)
{
- services.ConfigureOpenTelemetry();
+ var services = builder.Services;
+ services.ConfigureTelemetryServices(builder.Environment, builder.Configuration);
services.AddMemoryCache();
services.AddSingleton();
diff --git a/servers/Template.Mcp.Server/src/Template.Mcp.Server.csproj b/servers/Template.Mcp.Server/src/Template.Mcp.Server.csproj
index ee9c3a2d31..2f13aa2599 100644
--- a/servers/Template.Mcp.Server/src/Template.Mcp.Server.csproj
+++ b/servers/Template.Mcp.Server/src/Template.Mcp.Server.csproj
@@ -65,6 +65,15 @@
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+
diff --git a/servers/Template.Mcp.Server/src/appsettings.Development.json b/servers/Template.Mcp.Server/src/appsettings.Development.json
new file mode 100644
index 0000000000..c001896125
--- /dev/null
+++ b/servers/Template.Mcp.Server/src/appsettings.Development.json
@@ -0,0 +1,18 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "Microsoft.Hosting.Lifetime": "Information"
+ },
+ "Console": {
+ "LogToStandardErrorThreshold": "Debug",
+ "FormatterName": "simple",
+ "FormatterOptions": {
+ "ColorBehavior": "Disabled",
+ "SingleLine": true,
+ "TimestampFormat": "[HH:mm:ss] ",
+ "UseUtcTimestamp": true
+ }
+ }
+ }
+}
diff --git a/servers/Template.Mcp.Server/src/appsettings.json b/servers/Template.Mcp.Server/src/appsettings.json
new file mode 100644
index 0000000000..ebe8df30bf
--- /dev/null
+++ b/servers/Template.Mcp.Server/src/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Prefix": "template-mcp",
+ "Name": "Template.Mcp.Server",
+ "DisplayName": "Template MCP Server",
+ "Logging": {
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ }
+}