Skip to content

Commit 3f8b2ff

Browse files
committed
merge
2 parents e8158d2 + fd416e2 commit 3f8b2ff

22 files changed

Lines changed: 1629 additions & 87 deletions

Nuget.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<configuration>
33
<packageSources>
44
<clear />

src/Azure.DataApiBuilder.Mcp/Azure.DataApiBuilder.Mcp.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>net8.0</TargetFramework>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7+
<!-- Suppress transitive dependency version mismatch warnings from MCP SDK -->
8+
<NoWarn>$(NoWarn);NU1603</NoWarn>
79
</PropertyGroup>
810

911
<ItemGroup>

src/Azure.DataApiBuilder.Mcp/Core/McpServerConfiguration.cs

Lines changed: 63 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using Microsoft.Extensions.DependencyInjection;
88
using ModelContextProtocol;
99
using ModelContextProtocol.Protocol;
10+
using ModelContextProtocol.Server;
1011

1112
namespace Azure.DataApiBuilder.Mcp.Core
1213
{
@@ -16,80 +17,84 @@ namespace Azure.DataApiBuilder.Mcp.Core
1617
internal static class McpServerConfiguration
1718
{
1819
/// <summary>
19-
/// Configures the MCP server with tool capabilities
20+
/// Configures the MCP server with tool capabilities.
2021
/// </summary>
2122
internal static IServiceCollection ConfigureMcpServer(this IServiceCollection services)
2223
{
23-
services.AddMcpServer(options =>
24+
services.AddMcpServer()
25+
.WithListToolsHandler((RequestContext<ListToolsRequestParams> request, CancellationToken ct) =>
2426
{
25-
options.ServerInfo = new() { Name = McpProtocolDefaults.MCP_SERVER_NAME, Version = McpProtocolDefaults.MCP_SERVER_VERSION };
26-
options.Capabilities = new()
27+
McpToolRegistry? toolRegistry = request.Services?.GetRequiredService<McpToolRegistry>();
28+
if (toolRegistry == null)
2729
{
28-
Tools = new()
29-
{
30-
ListToolsHandler = (request, ct) =>
31-
{
32-
McpToolRegistry? toolRegistry = request.Services?.GetRequiredService<McpToolRegistry>();
33-
if (toolRegistry == null)
34-
{
35-
throw new InvalidOperationException("Tool registry is not available.");
36-
}
30+
throw new InvalidOperationException("Tool registry is not available.");
31+
}
3732

38-
List<Tool> tools = toolRegistry.GetAllTools().ToList();
33+
List<Tool> tools = toolRegistry.GetAllTools().ToList();
3934

40-
return ValueTask.FromResult(new ListToolsResult
41-
{
42-
Tools = tools
43-
});
44-
},
45-
CallToolHandler = async (request, ct) =>
46-
{
47-
McpToolRegistry? toolRegistry = request.Services?.GetRequiredService<McpToolRegistry>();
48-
if (toolRegistry == null)
49-
{
50-
throw new InvalidOperationException("Tool registry is not available.");
51-
}
52-
53-
string? toolName = request.Params?.Name;
54-
if (string.IsNullOrEmpty(toolName))
55-
{
56-
throw new McpException("Tool name is required.");
57-
}
35+
return ValueTask.FromResult(new ListToolsResult
36+
{
37+
Tools = tools
38+
});
39+
})
40+
.WithCallToolHandler(async (RequestContext<CallToolRequestParams> request, CancellationToken ct) =>
41+
{
42+
McpToolRegistry? toolRegistry = request.Services?.GetRequiredService<McpToolRegistry>();
43+
if (toolRegistry == null)
44+
{
45+
throw new InvalidOperationException("Tool registry is not available.");
46+
}
5847

59-
if (!toolRegistry.TryGetTool(toolName, out IMcpTool? tool))
60-
{
61-
throw new McpException($"Unknown tool: '{toolName}'");
62-
}
48+
string? toolName = request.Params?.Name;
49+
if (string.IsNullOrEmpty(toolName))
50+
{
51+
throw new McpException("Tool name is required.");
52+
}
6353

64-
JsonDocument? arguments = null;
65-
try
66-
{
67-
if (request.Params?.Arguments != null)
68-
{
69-
// Convert IReadOnlyDictionary<string, JsonElement> to JsonDocument
70-
Dictionary<string, object?> jsonObject = new();
71-
foreach (KeyValuePair<string, JsonElement> kvp in request.Params.Arguments)
72-
{
73-
jsonObject[kvp.Key] = kvp.Value;
74-
}
54+
if (!toolRegistry.TryGetTool(toolName, out IMcpTool? tool))
55+
{
56+
throw new McpException($"Unknown tool: '{toolName}'");
57+
}
7558

76-
string json = JsonSerializer.Serialize(jsonObject);
77-
arguments = JsonDocument.Parse(json);
78-
}
59+
if (tool is null || request.Services is null)
60+
{
61+
throw new InvalidOperationException("Tool or service provider unexpectedly null.");
62+
}
7963

80-
return await McpTelemetryHelper.ExecuteWithTelemetryAsync(
81-
tool!, toolName, arguments, request.Services!, ct);
82-
}
83-
finally
84-
{
85-
arguments?.Dispose();
86-
}
64+
JsonDocument? arguments = null;
65+
try
66+
{
67+
if (request.Params?.Arguments != null)
68+
{
69+
// Convert IReadOnlyDictionary<string, JsonElement> to JsonDocument
70+
Dictionary<string, object?> jsonObject = new();
71+
foreach (KeyValuePair<string, JsonElement> kvp in request.Params.Arguments)
72+
{
73+
jsonObject[kvp.Key] = kvp.Value;
8774
}
75+
76+
string json = JsonSerializer.Serialize(jsonObject);
77+
arguments = JsonDocument.Parse(json);
8878
}
89-
};
79+
80+
return await McpTelemetryHelper.ExecuteWithTelemetryAsync(
81+
tool, toolName, arguments, request.Services, ct);
82+
}
83+
finally
84+
{
85+
arguments?.Dispose();
86+
}
9087
})
9188
.WithHttpTransport();
9289

90+
// Configure underlying MCP server options
91+
services.PostConfigure<McpServerOptions>(options =>
92+
{
93+
options.ServerInfo = new() { Name = McpProtocolDefaults.MCP_SERVER_NAME, Version = McpProtocolDefaults.MCP_SERVER_VERSION };
94+
options.Capabilities ??= new();
95+
options.Capabilities.Tools ??= new();
96+
});
97+
9398
return services;
9499
}
95100
}

src/Azure.DataApiBuilder.Mcp/Core/McpToolRegistry.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ public void RegisterTool(IMcpTool tool)
3737
// Check for duplicate tool names (case-insensitive)
3838
if (_tools.TryGetValue(toolName, out IMcpTool? existingTool))
3939
{
40+
// If the same tool instance is already registered, skip silently.
41+
// This can happen when both McpToolRegistryInitializer (hosted service)
42+
// and McpStdioHelper register tools during stdio mode startup.
43+
if (ReferenceEquals(existingTool, tool))
44+
{
45+
return;
46+
}
47+
4048
string existingToolType = existingTool.ToolType == ToolType.BuiltIn ? "built-in" : "custom";
4149
string newToolType = tool.ToolType == ToolType.BuiltIn ? "built-in" : "custom";
4250

src/Azure.DataApiBuilder.Mcp/Utils/McpResponseBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static CallToolResult BuildSuccessResult(
3434
{
3535
Content = new List<ContentBlock>
3636
{
37-
new TextContentBlock { Type = "text", Text = output }
37+
new TextContentBlock { Text = output }
3838
}
3939
};
4040
}
@@ -67,7 +67,7 @@ public static CallToolResult BuildErrorResult(
6767
{
6868
Content = new List<ContentBlock>
6969
{
70-
new TextContentBlock { Type = "text", Text = output }
70+
new TextContentBlock { Text = output }
7171
},
7272
IsError = true
7373
};

0 commit comments

Comments
 (0)