MCP stdio: negotiate initialize protocol version, default to 2025-11-25, return description in serverInfo, and add initialize wire-shape tests#3622
Conversation
Agent-Logs-Url: https://github.com/Azure/data-api-builder/sessions/7e83ced0-915a-414d-8d89-2533684eb764 Co-authored-by: Aniruddh25 <[email protected]>
Agent-Logs-Url: https://github.com/Azure/data-api-builder/sessions/7e83ced0-915a-414d-8d89-2533684eb764 Co-authored-by: Aniruddh25 <[email protected]>
Agent-Logs-Url: https://github.com/Azure/data-api-builder/sessions/86017944-515e-443c-9f16-545350791623 Co-authored-by: Aniruddh25 <[email protected]>
There was a problem hiding this comment.
Pull request overview
Updates DAB’s MCP stdio initialize handling to better interoperate with newer MCP clients by negotiating the response protocol version (never returning a version higher than the client requested) and aligning where server description metadata is emitted for newer protocol versions.
Changes:
- Bump the default MCP protocol version to
2025-11-25. - Negotiate
initializeresponseprotocolVersionusing the client’s requested version and the server’s configured/supported version. - Add unit tests covering default version, negotiation behavior, ordinal fallback, and server description placement logic.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Azure.DataApiBuilder.Mcp/Core/McpProtocolDefaults.cs | Updates default protocol version and adds centralized initialize-version negotiation + version comparison helpers. |
| src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs | Reads client-requested protocol version from initialize payload, returns negotiated version, and conditionally emits description as serverInfo.description vs instructions. |
| src/Service.Tests/UnitTests/McpProtocolDefaultsTests.cs | Adds focused unit tests for default version, negotiation behavior, ordinal fallback comparison, and description threshold logic. |
| public static string ResolveProtocolVersion(IConfiguration? configuration) | ||
| { | ||
| return configuration?.GetValue<string>(PROTOCOL_VERSION_CONFIG_KEY) ?? DEFAULT_PROTOCOL_VERSION; | ||
| } |
souvikghosh04
left a comment
There was a problem hiding this comment.
Approved with some minor comments
| @@ -184,13 +189,33 @@ private void HandleInitialize(JsonElement? id) | |||
| } | |||
There was a problem hiding this comment.
why do we need a try-catch just to rethrow? should we add logging or other error handling or else remove it?
| namespace Azure.DataApiBuilder.Service.Tests.UnitTests | ||
| { | ||
| [TestClass] | ||
| public class McpProtocolDefaultsTests |
There was a problem hiding this comment.
- some of the tests are very similar so can be collapsed using DataTestMethod
- add an additional test where both client and server has same version
|
Before merging I'd like to see an end-to-end test for HandleInitialize. To be clear this is not duplicate coverage, the 7 new unit tests verify the McpProtocolDefaults helpers in isolation, but the bug being fixed is fundamentally a wiring bug inside HandleInitialize, and the wiring is currently untested. Concretely, the existing tests would still all pass if any of the following regressed:
A focused test would construct an McpStdioServer, feed it an initialize line via redirected STDIN, capture STDOUT, parse the JSON, and assert the response. Three cases cover this:
All three additionally pin down jsonrpc, id, and the capabilities object so the response is spec-shaped end-to-end. Given this is a customer-reported (cri) wire-format regression, locking the wire shape down with a real test (not just helper unit tests) feels like it should be included. |
Agent-Logs-Url: https://github.com/Azure/data-api-builder/sessions/901e8c47-40c1-4349-82f3-727b5c85a884 Co-authored-by: aaronburtle <[email protected]>
Implemented in 73540aa. Added focused HandleInitialize wire-shape tests that assert JSON-RPC envelope ( |
Why make this change?
DAB MCP stdio always returned
2025-06-18ininitialize, even when clients requested newer protocol versions, causing modern MCP clients (e.g., Claude Code 2.x) to disconnect.This update aligns initialize negotiation behavior with MCP version selection expectations, updates the default supported version to
2025-11-25, aligns initialize metadata shape for newer protocol versions, and adds focused wiring tests to lock down the initialize response wire format end-to-end.What is this change?
Protocol default
2025-06-18to2025-11-25inMcpProtocolDefaults.Initialize version negotiation
initializenow readsparams.protocolVersionfrom the client request.<=client requested (never higher than the client).Initialize metadata field shape by negotiated protocol
>= 2025-11-25, runtime MCP description is returned asserverInfo.description.instructionsfor backward compatibility.Negotiation utility + parsing behavior
McpProtocolDefaults.yyyy-MM-dd) with ordinal string fallback for non-date values.serverInfo.descriptionvsinstructions.Focused test coverage
serverInfo.description.HandleInitializethat validate:jsonrpc,id),result.protocolVersion,serverInfo.descriptionvsinstructionsvs neither).How was this tested?
Sample Request(s)
Example CLI usage:
dab start --mcp-stdioExample initialize request:
{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"claude-code","version":"2.1.98"}},"id":1}2025-11-25:{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-11-25","capabilities":{"tools":{"listChanged":true},"logging":{}},"serverInfo":{"name":"SQL MCP Server","version":"<dab-version>","description":"<mcp-runtime-description>"}}}{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{"listChanged":true},"logging":{}},"serverInfo":{"name":"SQL MCP Server","version":"<dab-version>"},"instructions":"<mcp-runtime-description>"}}