Skip to content

feat(mcp): stateless spec modern target apis and integration (28-07-2026) - #2692

Open
Hritik003 wants to merge 13 commits into
theagentrouter:mainfrom
Hritik003:mcp-new-spec-phase1/add-modern-target-apis-and-era-detection
Open

Hritik003 wants to merge 13 commits into
theagentrouter:mainfrom
Hritik003:mcp-new-spec-phase1/add-modern-target-apis-and-era-detection

Conversation

@Hritik003

@Hritik003 Hritik003 commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Description

This PR continues modern MCP (2026-07-28) support by wiring era detection into the shared POST entrypoint and adding the remaining single-target modern handlers, plus dataplane coverage for the end-to-end path.

servePOST now classifies each request with detectClientEra and dispatches to the modern or legacy handler. Modern requests are validated against modern-only requirements (Mcp-Protocol-Version, Mcp-Method, _meta); legacy-only methods like initialize / ping are rejected on the modern path, and modern-only methods like server/discover are rejected on the legacy path.

On the modern path this adds single-target APIs

  • tools/call,
  • resources/read,
  • prompts/get,
  • completion/complete
  • subscriptions/listen

with backend selection from

  • namespaced names/URIs,
  • selector/authz checks,
  • response URI rewriting,
  • and shared forwarding helpers.

Dataplane e2e coverage includes a modern test MCP server, a modern client helper, and tests for

  • discover/list/call/read/prompt/complete,
  • metrics, tracing,
  • and rejection of legacy methods on the modern path.
  • Legacy test backends reject server/discover so go-sdk v1.7+ Connect still falls back to initialize for existing legacy tests.

Related Issues/PRs

Special notes for reviewers (if applicable)

Please focus review on:

  • detectClientEra / servePOST dispatch in internal/mcpproxy/era.go and handlers.go
  • single-target routing and URI/name namespacing in internal/mcpproxy/modern.go (tools/call, resources/read, prompts/get, completion/complete)
  • dataplane modern suite under tests/data-plane-mcp/ and tests/internal/testmcp/modern_server.go

Also note the concurrent ResponseRecorder fix in the subscriptions listen unit test (CI -race) and the legacy discover reject response echoing the JSON-RPC request id (needed for bench/direct Connect fallback).

Testing

Have documented all the testing scenarios in the doc - https://docs.google.com/document/d/1QwA6bvmMYghMBVP-AYvJ2NPWKQNzCHPXfdsrbe9pr3M/edit?usp=sharing

Has 3 suites of testing:

  • modern APIs happy cases
  • modern APIs validation scenarios
  • era routing, and auth

Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
@netlify

netlify Bot commented Sep 15, 2026

Copy link
Copy Markdown

Deploy Preview for theagentrouter canceled.

Name Link
🔨 Latest commit 579b523
🔍 Latest deploy log https://app.netlify.com/projects/theagentrouter/deploys/6aabbc4269947b0007c8796a

Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
@codecov

codecov Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.51613% with 96 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/mcpproxy/modern.go 82.68% 80 Missing ⚠️
internal/mcpproxy/handlers.go 87.87% 16 Missing ⚠️

📢 Thoughts on this report? Let us know!

Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
@Hritik003 Hritik003 changed the title feat(mcp): modern target apis for new mcp spec and integration (28-06-2026) feat(mcp): stateless spec modern target apis and integration (28-06-2026) Sep 15, 2026
@Hritik003 Hritik003 changed the title feat(mcp): stateless spec modern target apis and integration (28-06-2026) feat(mcp): stateless spec modern target apis and integration (28-07-2026) Sep 16, 2026
Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
@missBerg missBerg added enhancement New feature or request area/mcp MCP proxy, MCPRoute, and MCP spec conformance labels Sep 16, 2026
1. protocolError now written as structured JSON-RPC error
2. Era detection: future/unknown versions get -32022
3. Mcp-Method without version header rejected
4. Backend JSON-RPC errors forwarded structurally

Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
1. onErrorResponse now writes JSON-RPC
2. rewriteAcknowledgedSubscriptions no longer drops boolean resourceSubscription

Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
1. Kept the two separate helpers:
	- onErrorResponse(w, status, msg) — no id, code from status
	- onRequestError(w, status, code, msg, id) — echo id + explicit code

Signed-off-by: Hritik003 <hritik.raj@nutanix.com>
subscriptions/acknowledged  to send array of subscribed uris when backend returns
@Hritik003
Hritik003 marked this pull request as ready for review September 17, 2026 10:32
@Hritik003
Hritik003 requested a review from a team as a code owner September 17, 2026 10:32
@Hritik003

Copy link
Copy Markdown
Contributor Author

/retest

@mohitgurnani

Copy link
Copy Markdown
Contributor

Nice work on this — the dual-era framing and the phased rollout in the proposal are the right call for a breaking wire-protocol change on a shared gateway. Two things I'd want resolved before merge, plus a couple of smaller notes.

1. isLegacyVersion (era.go) is looser than the design intends

func isLegacyVersion(v string) bool {
	if len(v) != 10 || v[4] != '-' || v[7] != '-' { return false }
	return v < protocolVersion20260728
}

This accepts any date-shaped string lexicographically before 2026-07-28 as "legacy" — not just the two real legacy versions (2025-06-18, 2025-11-25). A client sending e.g. Mcp-Protocol-Version: 2019-01-01 falls through to validateLegacyRequest instead of getting the -32022 UnsupportedProtocolVersion rejection D1 describes. Should validate against the actual supportedVersions allowlist rather than "looks like a date and sorts earlier."

2. Backend-era verification isn't wired in yet, but this PR activates live dispatch on it

The proposal's own Phase 1 acceptance criteria says activation "Depends on: PR 0.1 required; 0.2–0.4 required for a complete modern Cell 2" and should "require positive homogeneous backend-era evidence... before enabling the modern route." I don't see a discovery.go or any backendEra/homogeneity check anywhere in the tree at this PR's head. Yet servePOST now dispatches to serveModernPOST purely off detectClientEra(r, rawMsg) — i.e. off what the client declares, with nothing verifying the backend actually speaks modern first:

detection := detectClientEra(r, rawMsg)
if detection.era == eraModern {
    m.serveModernPOST(w, r, req, startAt)
}

As written, a client that just adds Mcp-Protocol-Version: 2026-07-28 gets routed through the full modern path (namespacing, _meta injection, etc.) to whatever backend is selected for that route, even if that backend is actually legacy or misconfigured — with no clean rejection, just whatever the mismatch produces downstream. Is backend-era gating intentionally deferred past this PR (e.g. because all currently configured backends are known-modern), or should activation wait on it? If it's intentional, a note in the PR description would help — right now it reads like the gap the proposal was trying to sequence around.

Smaller:

  • DCO check is currently failing on the latest commit — needs a sign-off.
  • authorizeRequest is only invoked in handleModernToolsCall, not in the new resources/read/prompts/get/completion/complete handlers. I checked — this mirrors the existing asymmetry in legacy.go, so it's parity rather than a regression, but since three new handlers are being added it seemed worth flagging in case it wasn't a deliberate choice going forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/mcp MCP proxy, MCPRoute, and MCP spec conformance enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants