Skip to content

feat(mcp): support protocol revision 2026-07-28 - #4044

Open
Sayt-0 wants to merge 3 commits into
mainfrom
feat/3952-mcp-2026-07-28
Open

feat(mcp): support protocol revision 2026-07-28#4044
Sayt-0 wants to merge 3 commits into
mainfrom
feat/3952-mcp-2026-07-28

Conversation

@Sayt-0

@Sayt-0 Sayt-0 commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

  • upgrade github.com/modelcontextprotocol/go-sdk from v1.6.1 to v1.7.0
  • enable stateless Streamable HTTP for MCP protocol revision 2026-07-28
  • preserve legacy initialize compatibility and update fallback fixtures
  • reject unsupported MCP keep-alive combinations for HTTP and attach modes
  • add regression coverage for protocol negotiation, stateless transport behavior, annotation serialization, and concurrent clients

Issue expectations

Expectation Implementation
Upgrade the Go MCP SDK to v1.7.0 Updated go.mod and go.sum
Expose MCP 2026-07-28 over HTTP Enabled StreamableHTTPOptions.Stateless in the production handler
Support server/discover negotiation Covered with a v1.7 client against the production handler
Preserve legacy clients Legacy initialize, notifications/initialized, and tools/list remain accepted with request-local state
Remove HTTP session semantics Verified that responses omit Mcp-Session-Id and that GET/DELETE return 405
Handle tool annotation defaults Added coverage proving false readOnlyHint and idempotentHint values remain serialized
Preserve sampling and MRTR behavior Existing sampling E2E replay passes with the v1.7 middleware
Update protocol fixtures Advanced the legacy catalog fixture to 2025-11-25 and fixed discover fallback request IDs
Handle keep-alive compatibility Keep-alive remains available for agent stdio and is rejected for stateless HTTP and attach mode

Compatibility

  • Legacy initialize-based requests remain supported over stateless HTTP.
  • Older stateful clients that require a standalone GET stream or session DELETE teardown must use a stateless-compatible client.
  • Product-specific smoke tests with Gordon and Agentic Platform were not available locally.

Validation

  • task build
  • task test
  • task lint
  • go build ./...
  • go vet ./...
  • go test -race -count=1 ./pkg/mcp ./pkg/tools/mcp ./pkg/tools/builtin/mcpcatalog
  • go test -count=1 ./e2e -run 'TestExec_Gemini_SamplingWithTools|TestMCP_'
  • npx --yes markdownlint-cli2@0.22.1 features/cli/index.md features/mcp-mode/index.md
  • go mod tidy -diff
  • git diff --check

Closes #3952

@Sayt-0
Sayt-0 requested a review from a team as a code owner August 24, 2026 08:30
@Sayt-0
Sayt-0 force-pushed the feat/3952-mcp-2026-07-28 branch from 346edb3 to 06d63f1 Compare August 24, 2026 08:48
@Sayt-0
Sayt-0 enabled auto-merge August 24, 2026 08:50
@aheritier aheritier added area/deps Dependency updates and version bumps area/docs Documentation changes area/mcp MCP protocol, MCP tool servers, integration area/testing Test infrastructure, CI/CD, test runners, evaluation area/tools For features/issues/fixes related to the usage of built-in and MCP tools kind/feat PR adds a new feature (maps to feat:). Use on PRs only. labels Aug 24, 2026
@aheritier

Copy link
Copy Markdown
Collaborator

👋 This PR has merge conflicts with the base branch. Please rebase or merge the latest base branch and resolve them. I've moved it to draft and added status/needs-rebase; it'll be picked back up automatically once the conflicts are cleared.

@aheritier aheritier added the status/needs-rebase PR has merge conflicts or is out of date with main label Aug 24, 2026

@aheritier aheritier left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at 06d63f1 with CI green (19/19 checks). I independently rebuilt and re-validated this branch in a clean worktree: go build ./... OK, go mod tidy -diff clean, go test -race ./pkg/mcp/... ./cmd/root/... ./pkg/tools/mcp/... ./pkg/tools/builtin/mcpcatalog/... ./pkg/runtime/... all pass, and go test ./e2e -run 'TestExec_Gemini_SamplingWithTools|TestMCP_' passes (3/3).

The implementation matches issue #3952 point for point, and the new HTTP tests are the real thing — they drive the production newStreamableHTTPHandler over a real httptest server rather than an in-process handler, which was exactly the gap called out in the investigation comment.

Compatibility evidence I generated (beyond the PR's own tests)

Because the main open question on #3952 was "will this break existing clients", I built a real pre-2026-07-28 client (go-sdk v1.6.1) and pointed it at the new stateless handler:

  • connect → 2025-11-25 negotiated, tools/list → OK, tools/call → OK, Close() → OK.
  • Repeated with a client registering ToolListChangedHandler/LoggingMessageHandler (so the SDK opens the standalone GET SSE stream): the 405 on GET is tolerated, the session survives, and subsequent calls still succeed.

So stateless mode does not break go-sdk-based legacy clients.

I also confirmed enabling Stateless: true is safe for our served surface: createToolHandler (pkg/mcp/server.go:236) runs the agent with WithNonInteractive(true) via rt.Run and never touches req.Session, and there is no Elicit/CreateMessage/notification call anywhere in pkg/mcp. We issue no server→client requests, which is precisely the thing stateless mode forbids.

Finally, the .golangci.yml SA1019 exclusion is necessary and correctly scoped — with the block removed, golangci-lint run --max-issues-per-linter=0 --max-same-issues=0 reports sampling deprecations in exactly the listed paths (pkg/runtime/sampling.go, pkg/runtime/sampling_test.go, pkg/tools/mcp/mcp.go, pkg/tools/mcp/session_client.go, pkg/tools/mcp/session_client_test.go, pkg/tools/sampling.go, pkg/tools/codemode/codemode_test.go). No dead entries.

[should-fix] No coverage for the MRTR elicitation path, which this bump silently rewires

The investigation comment on #3952 asked for "MRTR elicitation/sampling retry coverage". Sampling has it — e2e/sampling_test.go pairs a real gomcp server over StreamableHTTPHandler with our client and passes. Elicitation has none: no *_test.go in the repo pairs an SDK client and server for elicitation (every file referencing ElicitationHandler has zero mcp.NewClient references), so the retry path is entirely unexercised.

This matters because the semantics genuinely changed under 2026-07-28. Server-side inline elicitation is now rejected outright:

"elicitation/create" cannot be sent while serving a request on protocol version 2026-07-28:
return an InputRequests map instead (multi round-trip requests, SEP-2322)

Our client wiring (pkg/tools/mcp/remote.go:169, pkg/tools/mcp/stdio.go:51ClientOptions.ElicitationHandler) does still work against an MRTR-compliant server — I verified it: a stateless v1.7 server returning CallToolResult.InputRequests{"q1": &mcp.ElicitParams{...}} + RequestState caused our exact handler shape to be invoked once and the retried call to complete with action=accept. That's good news, but it's guaranteed only by SDK middleware today, with nothing in our suite to catch a regression. A hermetic test using that recipe (server returns InputRequests, client asserts the elicitation handler fires and the retry resolves) would close the last item from the agreed plan.

[optional] The mcp-mode doc is more alarming than the observed behaviour

docs/features/mcp-mode/index.md:64 says older stateful clients depending on a standalone GET stream or session DELETE teardown "must upgrade to (or switch to) a client compatible with stateless streaming HTTP". Per the v1.6.1 experiment above, such a client keeps working — the 405s are tolerated and only out-of-band notifications are lost (and we send none). Softening this to "loses out-of-band GET-stream notifications" would set more accurate expectations. Being conservative here is not harmful, so purely your call.

[optional] examples/golibrary/renderer/main.go:127 still constructs NewStreamableHTTPHandler(..., nil) (stateful) while production is now stateless. Harmless for an example, but it no longer mirrors product behaviour.

Not approving yet — one item needs your call, not mine

The remaining gate is the one you raised on #3952 on 2026-08-18: verifying this doesn't break Gordon, Agentic Platform, or existing users. The PR body is candid that those smoke tests "were not available locally", so that validation is still outstanding. My v1.6.1 interop results above should de-risk the go-sdk client case substantially, but non-Go clients and the two downstream products remain unverified.

Worth also confirming you're happy to land this as a single commit — the plan on #3952 proposed splitting "upgrade, preserve behaviour" (step 1) from "enable stateless serving" (step 2), which would have made a revert cheaper if a downstream product does turn out to be affected.

Two intentional behaviour changes for the release notes: --mcp-keepalive now hard-errors with --http (previously accepted) and with --attach (previously silently ignored). Both are correct and well tested (cmd/root/mcp_test.go:43-85, pkg/mcp/server_test.go:50), and runAttach (cmd/root/mcp.go:115) does indeed discard runConfig, so the error message is accurate.

Nice work — this is careful, well-evidenced migration work.

@aheritier aheritier removed the status/needs-rebase PR has merge conflicts or is out of date with main label Aug 24, 2026
@Sayt-0
Sayt-0 requested a review from aheritier August 24, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/deps Dependency updates and version bumps area/docs Documentation changes area/mcp MCP protocol, MCP tool servers, integration area/testing Test infrastructure, CI/CD, test runners, evaluation area/tools For features/issues/fixes related to the usage of built-in and MCP tools kind/feat PR adds a new feature (maps to feat:). Use on PRs only.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Support MCP spec revision 2026-07-28

2 participants