Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
<PackageVersion Include="Azure.ResourceManager.StorageSync" Version="1.3.1" />
<PackageVersion Include="Azure.ResourceManager.FileShares" Version="1.0.0-beta.2" />
<PackageVersion Include="Azure.ResourceManager.ResourceGraph" Version="1.1.0" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection" Version="10.0.7" />
<PackageVersion Include="Microsoft.Azure.Kusto.Data" Version="14.0.3" />
<PackageVersion Include="Microsoft.Azure.Kusto.Ingest" Version="14.0.3" />
<PackageVersion Include="Microsoft.CognitiveServices.Speech" Version="1.46.0" />
Expand Down Expand Up @@ -88,14 +89,15 @@
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="Yarp.ReverseProxy" Version="2.3.0" />
<PackageVersion Include="System.ClientModel" Version="1.8.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.1" />
<PackageVersion Include="System.Formats.Asn1" Version="10.0.2" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.15.0" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="10.0.2" />
<PackageVersion Include="System.Numerics.Tensors" Version="10.0.2" />
<PackageVersion Include="System.Text.Json" Version="10.0.2" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageVersion Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.3.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.6" />
<PackageVersion Include="System.Formats.Asn1" Version="10.0.6" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.17.0" />
<PackageVersion Include="System.Net.ServerSentEvents" Version="10.0.6" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="10.0.7" />
<PackageVersion Include="System.Numerics.Tensors" Version="10.0.6" />
<PackageVersion Include="System.Text.Json" Version="10.0.6" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.3" />
<PackageVersion Include="Azure.Monitor.OpenTelemetry.AspNetCore" Version="1.4.0" />
<PackageVersion Include="Microsoft.Extensions.Azure" Version="1.11.0" />
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.1" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.1" />
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ This repository contains core libraries, test frameworks, engineering systems, p
- **INSTALL**: [![Install Microsoft Sentinel Data Exploration MCP in VS Code](https://img.shields.io/badge/VS_Code-0098FF?style=flat-square&logo=visualstudiocode&logoColor=ffffff)](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22microsoft-sentinel-data-exploration%22%2C%22url%22%3A%22https%3A%2F%2Fsentinel.microsoft.com%2Fmcp%2Fdata-exploration%22%7D) [![Install Microsoft Sentinel Data Exploration MCP in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=ffffff)](https://vscode.dev/redirect?url=vscode-insiders:mcp/install?%7B%22name%22%3A%22microsoft-sentinel-data-exploration%22%2C%22url%22%3A%22https%3A%2F%2Fsentinel.microsoft.com%2Fmcp%2Fdata-exploration%22%7D)

### 🛢️ Microsoft SQL
- **REPOSITORY**: [MSSQL MCP Server](https://aka.ms/MssqlMcp)
- **REPOSITORY**: [MSSQL MCP Server](https://aka.ms/sql/mcp)
- **DESCRIPTION**: Chat with your business data the new agentic way using natural language and AI. Connect to any SQL database—from ground (on-premises) to Azure cloud to Microsoft Fabric via a simple connection string. Discover and define table schemas, manage tables, and perform CRUD operations through conversational prompts.
- **CATEGORY**: `DEVELOPER TOOLS`
- **TYPE**: `Local`
- **INSTALL**: [MSSQL MCP Server](https://aka.ms/MssqlMcp)
- **INSTALL**: [MSSQL MCP Server](https://aka.ms/sql/mcp)

### 💬 Microsoft Teams
- **REPOSITORY**: [bap-microsoft/MCP-Platform](https://github.com/bap-microsoft/MCP-Platform/tree/main/src/Services/WebApi/MCPServers/FirstParty/CodeBased/mcp_TeamsServer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Azure.Mcp.Core.csproj" />
<ProjectReference Include="..\Azure.Mcp.Tests\Azure.Mcp.Tests.csproj" />
<ProjectReference Include="..\..\..\..\servers\Azure.Mcp.Server\src\Azure.Mcp.Server.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Mcp.Core.Areas.Server.Commands;
using Microsoft.Mcp.Core.Configuration;
using Microsoft.Mcp.Tests.Client;
using Xunit;

namespace Azure.Mcp.Core.Tests.Areas.Server.Commands;

public class ServiceInfoCommandTests : CommandUnitTestsBase<ServiceInfoCommand, object>
{
private readonly McpServerConfiguration _mcpServerConfiguration;

public ServiceInfoCommandTests()
{
_mcpServerConfiguration = new McpServerConfiguration
{
Name = "Test-Name?",
ShortName = "test",
Version = "Test-Version?",
DisplayName = "Test Display",
Description = "Test Description",
RootCommandGroupName = "azmcp"
};
Services.AddSingleton(Microsoft.Extensions.Options.Options.Create(_mcpServerConfiguration));
}

[Fact]
public async Task ExecuteAsync_ReturnsCorrectProperties()
{
var response = await ExecuteCommandAsync([]);

// Assert
var result = ValidateAndDeserializeResponse(response, ServiceInfoJsonContext.Default.ServiceInfoCommandResult);

Assert.Equal(_mcpServerConfiguration.Name, result.Name);
Assert.Equal(_mcpServerConfiguration.Version, result.Version);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ public static ICommandFactory CreateCommandFactory(IServiceProvider? serviceProv
var configurationOptions = Microsoft.Extensions.Options.Options.Create(new McpServerConfiguration
{
Name = "Test Server",
ShortName = "test",
Version = "Test Version",
DisplayName = "Test Display",
Description = "Test Description",
RootCommandGroupName = "azmcp"
});
var telemetryService = services.GetService<ITelemetryService>() ?? new NoOpTelemetryService();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ private static ConsolidatedToolDiscoveryStrategy CreateStrategy(
var configurationOptions = Microsoft.Extensions.Options.Options.Create(new McpServerConfiguration
{
Name = "Test Server",
ShortName = "test",
Version = "Test Version",
DisplayName = "Test Display",
Description = "Test Description",
RootCommandGroupName = "azmcp"
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Microsoft.Mcp.Core.Areas.Server.Commands.ServerInstructions;
using Microsoft.Mcp.Core.Areas.Server.Commands.ToolLoading;
using Microsoft.Mcp.Core.Areas.Server.Options;
using Microsoft.Mcp.Core.Configuration;
using Microsoft.Mcp.Core.Services.Azure.Authentication;
using ModelContextProtocol.Server;
using NSubstitute;
Expand All @@ -25,6 +26,19 @@ private IServiceCollection SetupBaseServices()
services.AddSingleton(sp => CommandFactoryHelpers.CreateCommandFactory(sp));
services.AddSingleIdentityTokenCredentialProvider();
services.AddRegistryRoot(typeof(Azure.Mcp.Server.Program).Assembly, "Azure.Mcp.Server.Resources.registry.json");

var serverConfiguration = new McpServerConfiguration
{
Name = "Azure.Mcp.Server",
ShortName = "azure",
DisplayName = "Azure MCP Server",
Version = "1.0.0",
RootCommandGroupName = "azmcp",
Description = "Test description"
};
services.AddSingleton(serverConfiguration);
services.AddSingleton(Microsoft.Extensions.Options.Options.Create(serverConfiguration));

return services;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ public ServiceInfoCommandTests()
_mcpServerConfiguration = new McpServerConfiguration
{
Name = "Test-Name?",
ShortName = "test",
Version = "Test-Version?",
DisplayName = "Test Display",
Description = "Test Description",
RootCommandGroupName = "azmcp"
};
_command = new(Microsoft.Extensions.Options.Options.Create(_mcpServerConfiguration), _logger);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ public PluginTelemetryCommandTests()
{
RootCommandGroupName = "azmcp",
Name = "Azure.Mcp.Server.Test",
ShortName = "azure",
DisplayName = "Azure MCP Server (Test)",
Description = "Azure MCP Server (Test) description",
Version = "1.0.0-test"
}));
services.AddSingleton<ITelemetryService>(Substitute.For<ITelemetryService>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Microsoft.Mcp.Core.Areas.Server.Commands.Discovery;
using Microsoft.Mcp.Core.Areas.Server.Commands.ToolLoading;
using Microsoft.Mcp.Core.Areas.Server.Options;
using Microsoft.Mcp.Core.Configuration;
using Microsoft.Mcp.Core.Helpers;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
Expand All @@ -19,6 +20,19 @@ namespace Azure.Mcp.Core.UnitTests.Areas.Server.Commands.ToolLoading;

public class SingleProxyToolLoaderTests
{
private static Microsoft.Extensions.Options.IOptions<McpServerConfiguration> CreateServerConfigurationOptions()
{
return Microsoft.Extensions.Options.Options.Create(new McpServerConfiguration
{
Name = "Azure.Mcp.Server",
ShortName = "azure",
DisplayName = "Azure MCP Server",
Version = "1.0.0",
RootCommandGroupName = "azmcp",
Description = "This server/tool provides real-time, programmatic access to all Azure products, services, and resources."
});
}

private static RegistryDiscoveryStrategy CreateStrategy(ServiceStartOptions options, ILogger<RegistryDiscoveryStrategy> logger)
{
var serviceOptions = Microsoft.Extensions.Options.Options.Create(options ?? new ServiceStartOptions());
Expand Down Expand Up @@ -49,13 +63,13 @@ private static (SingleProxyToolLoader toolLoader, IMcpDiscoveryStrategy discover
commandGroupDiscoveryStrategy,
registryDiscoveryStrategy
], compositeLogger);
var toolLoader = new SingleProxyToolLoader(compositeDiscoveryStrategy, logger, Microsoft.Extensions.Options.Options.Create(toolLoaderOptions ?? new ToolLoaderOptions()));
var toolLoader = new SingleProxyToolLoader(compositeDiscoveryStrategy, logger, Microsoft.Extensions.Options.Options.Create(toolLoaderOptions ?? new ToolLoaderOptions()), CreateServerConfigurationOptions());
return (toolLoader, compositeDiscoveryStrategy);
}
else
{
var mockDiscoveryStrategy = Substitute.For<IMcpDiscoveryStrategy>();
var toolLoader = new SingleProxyToolLoader(mockDiscoveryStrategy, logger, Microsoft.Extensions.Options.Options.Create(toolLoaderOptions ?? new ToolLoaderOptions()));
var toolLoader = new SingleProxyToolLoader(mockDiscoveryStrategy, logger, Microsoft.Extensions.Options.Options.Create(toolLoaderOptions ?? new ToolLoaderOptions()), CreateServerConfigurationOptions());
return (toolLoader, mockDiscoveryStrategy);
}
}
Expand Down Expand Up @@ -285,7 +299,7 @@ public async Task GetChildToolList_WithReadOnlyOption_ReturnsOnlyReadOnlyTools()
var toolLoaderOptions = Microsoft.Extensions.Options.Options.Create(new ToolLoaderOptions() { ReadOnly = true });
var logger = Substitute.For<ILogger<SingleProxyToolLoader>>();

var toolLoader = new SingleProxyToolLoader(discoveryStrategy, logger, toolLoaderOptions);
var toolLoader = new SingleProxyToolLoader(discoveryStrategy, logger, toolLoaderOptions, CreateServerConfigurationOptions());
var request = CreateCallToolRequest("storage");

// Act
Expand Down Expand Up @@ -339,7 +353,7 @@ public async Task GetChildToolList_WithIsHttpOption_DoesNotReturnLocalRequiredTo
var toolLoaderOptions = Microsoft.Extensions.Options.Options.Create(new ToolLoaderOptions() { IsHttpMode = true });
var logger = Substitute.For<ILogger<SingleProxyToolLoader>>();

var toolLoader = new SingleProxyToolLoader(discoveryStrategy, logger, toolLoaderOptions);
var toolLoader = new SingleProxyToolLoader(discoveryStrategy, logger, toolLoaderOptions, CreateServerConfigurationOptions());
var request = CreateCallToolRequest("storage");

// Act
Expand Down Expand Up @@ -394,11 +408,13 @@ public void SingleProxyToolLoader_Constructor_ThrowsOnNullArguments()
var logger = Substitute.For<ILogger<SingleProxyToolLoader>>();
var discoveryStrategy = Substitute.For<IMcpDiscoveryStrategy>();
var toolLoaderOptions = Microsoft.Extensions.Options.Options.Create(new ToolLoaderOptions());
var serverConfigurationOptions = CreateServerConfigurationOptions();

// Act & Assert
Assert.Throws<ArgumentNullException>(() => new SingleProxyToolLoader(null!, logger, toolLoaderOptions));
Assert.Throws<ArgumentNullException>(() => new SingleProxyToolLoader(discoveryStrategy, null!, toolLoaderOptions));
Assert.Throws<ArgumentNullException>(() => new SingleProxyToolLoader(discoveryStrategy, logger, null!));
Assert.Throws<ArgumentNullException>(() => new SingleProxyToolLoader(null!, logger, toolLoaderOptions, serverConfigurationOptions));
Assert.Throws<ArgumentNullException>(() => new SingleProxyToolLoader(discoveryStrategy, null!, toolLoaderOptions, serverConfigurationOptions));
Assert.Throws<ArgumentNullException>(() => new SingleProxyToolLoader(discoveryStrategy, logger, null!, serverConfigurationOptions));
Assert.Throws<ArgumentNullException>(() => new SingleProxyToolLoader(discoveryStrategy, logger, toolLoaderOptions, null!));
}

#region Execution-Time Mode Enforcement Tests
Expand All @@ -413,7 +429,7 @@ private static SingleProxyToolLoader CreateToolLoaderWithMockClient(
var logger = Substitute.For<ILogger<SingleProxyToolLoader>>();
var options = Microsoft.Extensions.Options.Options.Create(toolLoaderOptions);

return new SingleProxyToolLoader(discoveryStrategy, logger, options);
return new SingleProxyToolLoader(discoveryStrategy, logger, options, CreateServerConfigurationOptions());
}

private static ModelContextProtocol.Server.RequestContext<CallToolRequestParams> CreateCallToolRequestWithToolAndCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -399,8 +399,10 @@ public async Task ExecuteAsync_WithEmptyCommandFactory_ReturnsEmptyResults()
var configurationOptions = Microsoft.Extensions.Options.Options.Create(new McpServerConfiguration
{
Name = "Test Server",
ShortName = "test",
Version = "Test Version",
DisplayName = "Test Display",
Description = "Test Description",
RootCommandGroupName = "azmcp"
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ public CommandFactoryTests()
_serverConfiguration = new McpServerConfiguration
{
Name = "Test Server",
ShortName = "test",
Version = "Test Version",
DisplayName = "Test Display",
Description = "Test Description",
RootCommandGroupName = "azmcp"
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
Expand All @@ -26,8 +27,11 @@ namespace Microsoft.Mcp.Core.Areas.Server.Commands;
/// <summary>
/// Extension methods for configuring Azure MCP server services.
/// </summary>
public static class ServiceCollectionExtensions
public static partial class ServiceCollectionExtensions
{
[GeneratedRegex("^[A-Za-z0-9_-]+$")]
private static partial Regex ShortNamePattern();

/// <summary>
/// Adds the Azure MCP server services to the specified <see cref="IServiceCollection"/>.
/// </summary>
Expand Down Expand Up @@ -266,13 +270,31 @@ public static void InitializeConfigurationAndOptions(this IServiceCollection ser
.Configure<IConfiguration, IOptions<ServiceStartOptions>>((options, rootConfiguration, serviceStartOptions) =>
{
// Manually bind configuration values to avoid reflection-based binding for AOT compatibility
options.RootCommandGroupName = rootConfiguration[nameof(McpServerConfiguration.RootCommandGroupName)]
var mcpConfiguration = rootConfiguration.GetRequiredSection("MicrosoftMcp");
options.RootCommandGroupName = mcpConfiguration[nameof(McpServerConfiguration.RootCommandGroupName)]
?? throw new InvalidOperationException($"Configuration value '{nameof(McpServerConfiguration.RootCommandGroupName)}' is required.");
options.Name = rootConfiguration[nameof(McpServerConfiguration.Name)]
options.Name = mcpConfiguration[nameof(McpServerConfiguration.Name)]
?? throw new InvalidOperationException($"Configuration value '{nameof(McpServerConfiguration.Name)}' is required.");
options.DisplayName = rootConfiguration[nameof(McpServerConfiguration.DisplayName)]
options.DisplayName = mcpConfiguration[nameof(McpServerConfiguration.DisplayName)]
?? throw new InvalidOperationException($"Configuration value '{nameof(McpServerConfiguration.DisplayName)}' is required.");

options.ShortName = mcpConfiguration[nameof(McpServerConfiguration.ShortName)]
?? throw new InvalidOperationException($"Configuration value '{nameof(McpServerConfiguration.ShortName)}' is required.");
options.ShortName = options.ShortName.Trim();
if (!ShortNamePattern().IsMatch(options.ShortName))
{
throw new InvalidOperationException(
$"Configuration value '{nameof(McpServerConfiguration.ShortName)}' must contain only letters, digits, '_', or '-'.");
}

options.Description = mcpConfiguration[nameof(McpServerConfiguration.Description)]
?? throw new InvalidOperationException($"Configuration value '{nameof(McpServerConfiguration.Description)}' is required.");
if (string.IsNullOrWhiteSpace(options.Description))
{
throw new InvalidOperationException(
$"Configuration value '{nameof(McpServerConfiguration.Description)}' must not be empty or whitespace.");
}

// 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();
Expand Down
Loading