Skip to content

MCP stdio: negotiate initialize protocol version, default to 2025-11-25, return description in serverInfo, and add initialize wire-shape tests#3622

Open
Copilot wants to merge 7 commits into
mainfrom
copilot/fix-mcp-initialize-response
Open

MCP stdio: negotiate initialize protocol version, default to 2025-11-25, return description in serverInfo, and add initialize wire-shape tests#3622
Copilot wants to merge 7 commits into
mainfrom
copilot/fix-mcp-initialize-response

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 20, 2026

Why make this change?

DAB MCP stdio always returned 2025-06-18 in initialize, 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

    • Bumped MCP default protocol from 2025-06-18 to 2025-11-25 in McpProtocolDefaults.
  • Initialize version negotiation

    • initialize now reads params.protocolVersion from the client request.
    • Server responds with the greatest supported version <= client requested (never higher than the client).
  • Initialize metadata field shape by negotiated protocol

    • For negotiated protocol versions >= 2025-11-25, runtime MCP description is returned as serverInfo.description.
    • For older negotiated protocol versions, description remains top-level instructions for backward compatibility.
  • Negotiation utility + parsing behavior

    • Added centralized negotiation helper in McpProtocolDefaults.
    • Compares date-formatted versions (yyyy-MM-dd) with ordinal string fallback for non-date values.
    • Added protocol-threshold helper for choosing serverInfo.description vs instructions.
  • Focused test coverage

    • Added helper unit tests for:
      • new default protocol value,
      • client newer than server (server version returned),
      • client older than server (client version returned),
      • missing client version,
      • non-date fallback comparison path,
      • protocol threshold behavior for serverInfo.description.
    • Added focused initialize wiring tests for HandleInitialize that validate:
      • JSON-RPC envelope shape (jsonrpc, id),
      • negotiated result.protocolVersion,
      • capabilities shape,
      • field placement across protocol/description scenarios (serverInfo.description vs instructions vs neither).
string negotiatedProtocolVersion =
    McpProtocolDefaults.ResolveInitializeResponseProtocolVersion(
        _protocolVersion,
        clientRequestedProtocolVersion);

How was this tested?

  • Integration Tests
  • Unit Tests

Sample Request(s)

  • Example CLI usage:

    • dab start --mcp-stdio
  • Example initialize request:

{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"claude-code","version":"2.1.98"}},"id":1}
  • Example initialize response behavior for negotiated 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>"}}}
  • Example initialize response behavior for negotiated older versions:
{"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>"}}

Copilot AI changed the title [WIP] Fix MCP stdio initialize response protocol version MCP stdio: negotiate initialize protocol version and default to 2025-11-25 May 20, 2026
Copilot AI requested a review from Aniruddh25 May 20, 2026 23:26
Comment thread src/Azure.DataApiBuilder.Mcp/Core/McpStdioServer.cs
@Aniruddh25 Aniruddh25 marked this pull request as ready for review May 21, 2026 00:11
Copilot AI review requested due to automatic review settings May 21, 2026 00:11
Copilot AI changed the title MCP stdio: negotiate initialize protocol version and default to 2025-11-25 MCP stdio: negotiate initialize protocol version, default to 2025-11-25, and return description in serverInfo May 21, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 initialize response protocolVersion using 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.

Comment on lines 39 to 42
public static string ResolveProtocolVersion(IConfiguration? configuration)
{
return configuration?.GetValue<string>(PROTOCOL_VERSION_CONFIG_KEY) ?? DEFAULT_PROTOCOL_VERSION;
}
Copy link
Copy Markdown
Contributor

@souvikghosh04 souvikghosh04 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with some minor comments

Comment on lines 185 to 189
@@ -184,13 +189,33 @@ private void HandleInitialize(JsonElement? id)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. some of the tests are very similar so can be collapsed using DataTestMethod
  2. add an additional test where both client and server has same version

@aaronburtle
Copy link
Copy Markdown
Contributor

@copilot

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:

  1. Any of the three response-builder branches in HandleInitialize reverts protocolVersion = negotiatedProtocolVersion back to protocolVersion = _protocolVersion (i.e., the original bug re-introduced).

  2. The if (...shouldUseServerInfoDescription) and else if branches get swapped, so older clients receive serverInfo.description and newer clients receive top-level instructions.

  3. GetClientProtocolVersion is broken, currently a private helper with no direct test coverage of its missing-params / missing-protocolVersion / wrong-type paths.

  4. JSON-RPC envelope changes (id not preserved, jsonrpc "2.0" dropped, etc).

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:

  1. Client requests 2025-11-25 with runtime.mcp.description set → response has result.protocolVersion == "2025-11-25" and result.serverInfo.description set, no top-level instructions.

  2. Client requests 2025-06-18 with runtime.mcp.description set → response has result.protocolVersion == "2025-06-18" and top-level instructions set, no serverInfo.description.

  3. Client requests 2025-11-25 with no description configured → response has result.protocolVersion == "2025-11-25" and neither field present.

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.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 21, 2026

@copilot

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 isolatio...

Implemented in 73540aa. Added focused HandleInitialize wire-shape tests that assert JSON-RPC envelope (jsonrpc, id), negotiated result.protocolVersion, capabilities shape, and description field placement for all three requested cases (serverInfo.description for 2025-11-25, top-level instructions for 2025-06-18, and neither field when description is unset). No UI changes in this PR (screenshot: N/A).

Copilot AI changed the title MCP stdio: negotiate initialize protocol version, default to 2025-11-25, and return description in serverInfo MCP stdio: negotiate initialize protocol version, default to 2025-11-25, return description in serverInfo, and add initialize wire-shape tests May 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

6 participants