feat(mcp): upgrade both MCP servers to the 2026-07-28 revision - #2290
Draft
Dani Akash (DaniAkash) wants to merge 7 commits into
Draft
feat(mcp): upgrade both MCP servers to the 2026-07-28 revision#2290Dani Akash (DaniAkash) wants to merge 7 commits into
Dani Akash (DaniAkash) wants to merge 7 commits into
Conversation
…-07-28 support (#2272) * feat(server): migrate the MCP server to the v2 SDK with dual-era 2026-07-28 support Replace @hono/mcp and the server-side @modelcontextprotocol/sdk 1.x with @modelcontextprotocol/server and @modelcontextprotocol/hono 2.0, serving both the legacy initialize handshake and the modern 2026-07-28 protocol per request via a stateless WebStandardStreamableHTTPServerTransport. v2 derives tools/list JSON schemas only from Zod v4, so the local browser and filesystem tool schemas move to the zod/v4 subpath of the installed zod 3.25; the Klavis connector schemas are bridged through fromJsonSchema until the Strata client migration. Drop the logging capability and the SetLevel handler, both removed in 2026-07-28. The Strata MCP client stays on the 1.x SDK for a follow-up change. Refs #2168 * fix(ci): call the real tabs tool in the MCP integration test The test invoked a non-existent tool name and passed only because the 1.x server returned a lenient error result for unknown tools. The v2 server correctly rejects unknown tools (its sibling test asserts the throw), so call the registered tabs tool with a valid action instead.
…2274) Move the Klavis Strata MCP client and the remaining Klavis type imports from the 1.x @modelcontextprotocol/sdk to @modelcontextprotocol/client 2.0, so apps/server no longer depends on the legacy SDK at runtime. The SDK stays as a devDependency for the integration and end-to-end tests that simulate a legacy client against the v2 server. No behavior change; the connector schema bridge is unchanged and can be cleaned up in a follow-up. Refs #2168
…n pin (#2275) * chore(claw-server): upgrade rmcp to 3.x with a legacy protocol version pin Bump rmcp from 2.1 to 3.x. rmcp 3.0 changes the ServerHandler call_tool return type to the MRTR-aware CallToolResponse, so wrap the existing CallToolResult with .into() in the production and reference handlers and the serving test. rmcp 3.x advertises the modern 2026-07-28 protocol by default, but the claw-server's sessionless state model for that revision is not in place yet, so override supported_protocol_versions() on the production handler to pin to legacy versions (2025-11-25 and earlier) and keep this upgrade behavior-neutral. The pin is lifted together with the handle-based session model. All crates compile, cargo test passes, clippy and fmt clean. Refs #2168 * test(claw-server): prove the legacy protocol pin rejects the modern revision Add a serving-level test that initializes against a legacy-pinned handler with protocolVersion 2026-07-28 and asserts the server negotiates down to a legacy revision rather than agreeing to the modern one. This exercises the same supported_protocol_versions() override the claw-server uses and confirms rmcp consults it during initialize negotiation.
#2278) * feat(claw-server): serve the modern MCP revision with a session handle Advertise the 2026-07-28 revision alongside the legacy revisions. rmcp serves 2026-07-28 clients statelessly, so the per-agent tab-group session, previously keyed on the mcp-session-id header that revision removes, is now carried as a server-minted handle passed as an ordinary `session` tool argument: minted as a UUID when absent, returned in structuredContent, and looked up on later calls so the agent keeps its tab group. Legacy clients and stdio keep the existing session model unchanged, and idle sweeping reaps handle sessions independent of transport close. * test(claw-server): expect the injected session arg in the name_session golden schema * fix(claw-server): only reuse live server-minted session handles Resolve a modern session by reusing a handle only when it maps to a live session; any absent or unrecognized handle now mints a fresh server-generated handle instead of being minted under the caller-supplied value. A caller can no longer choose or seed a session id, so it cannot land on another agent's ownership by presenting a chosen id, and because every mint uses a fresh unique id, two concurrent calls can no longer both mint under the same id and orphan one session. Continuity flows through the returned handle.
…le (#2289) * feat(server): give /mcp clients a server-minted session identity handle Expose an optional session string argument on each /mcp browser tool. The server mints a UUID when the caller omits it and returns it in the result's structuredContent so an agent can thread it back on later calls to identify its own session; a supplied handle is echoed. The handle is stripped before the tool runs, so tool logic (including the one strict-schema tool) and the internal agent's shared tool schemas stay unchanged. It is gated behind an opt-in registration flag so only the /mcp surface carries it, and it is attributed in the per-tool execution metric. Identity only: no isolation, no per-session state, no change to browser behavior. * fix(server): attribute the /mcp session handle on the per-call log The session handle was added to the aggregated `tool_executed` metric event, whose rollup keeps only tool name, source, and success, so the handle was discarded. Move the attribution to the per-call log context instead, which is the right home for a per-session value and avoids the unbounded cardinality of a UUID in an aggregated metric. The returned handle in structuredContent is unchanged.
Contributor
✅ Tests passed: 2423/2426Ran 14 of 15 suites (1 not affected by this change).
|
…point (#2298) * fix(server): deliver the /mcp tool abort signal from the request context The browser tool wrapper read the abort signal from a top-level `extra.signal`, which does not exist on the v2 SDK's tool handler context, so it was always undefined and cancellation never reached the tool over /mcp. Read it from `extra.mcpReq.signal` where the SDK actually places it. The internal AI-SDK agent path threads its own signal and is unaffected. Adds a test that a pre-aborted signal delivered via mcpReq.signal makes a waiting tool abort. * feat(server): reject browser-originated /mcp requests via Sec-Fetch-Site The /mcp endpoint is intentionally reachable across the LAN, so a loopback bind is not an option. Browsers always send a Sec-Fetch-Site header while native MCP clients and the internal ACP client never do, so rejecting requests that carry it filters browser-originated attacks (DNS rebinding, CSRF) without restricting the bind address. Scoped to /mcp so browser-facing routes are unaffected, and mirrors the claw-server's request hygiene. * fix(server): serve the modern 2026-07-28 revision on the TS /mcp endpoint The TS agent server advertised only legacy protocol versions and rejected 2026-07-28: the McpServer was built without supportedProtocolVersions (so it never registered server/discover), and the route dispatched through the bare streamable-HTTP transport, which does not run the modern request machinery. Advertise the modern revision alongside the legacy list on the McpServer, and split the route: legacy (2025-era) requests keep the existing hand-wired transport with enableJsonResponse so the internal ACP client still gets single-JSON responses, while modern requests go through createMcpHandler, which serves server/discover and the stateless 2026-07-28 dispatch. Both eras are exercised end to end by new tests: legacy initialize negotiates 2025-11-25 as JSON, modern server/discover advertises 2026-07-28, and a modern tool call succeeds.
Reconcile the MCP 2026-07-28 epic with main after the AI SDK v7 upgrade (#2373) landed. Conflicts resolved on their merits: - filesystem tools: keep the v7 `Tool` return annotation from main and the `zod/v4` import the v2 MCP registerTool requires from the epic. - apps/server deps: v2 MCP packages from the epic plus the v7-era @openrouter/ai-sdk-provider and @sentry/bun bumps from main. - claw-server MCP service: keep both the session handle (with_session_arg) and the new save_skill/mark_skill_run tools, and wrap those two tools with the session arg so every tool advertises it, matching the runtime session flow. - bun.lock reconciled via bun install to a frozen-lockfile fixed point.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Upgrades both BrowserOS MCP servers to the MCP 2026-07-28 revision while preserving full backward compatibility with legacy clients. Both
/mcpendpoints negotiate per request: modern clients are served the stateless 2026-07-28 revision, and legacy clients keep theinitializehandshake andmcp-session-idsession model exactly as before.TS agent server (
apps/server)@modelcontextprotocol/sdk1.x +@hono/mcpto the scoped v2 packages, serving both eras per request viaWebStandardStreamableHTTPServerTransport. (feat(server): migrate the MCP server to the v2 SDK with dual-era 2026-07-28 support #2272)zod/v4API that v2'sregisterToolrequires. (feat(server): migrate the MCP server to the v2 SDK with dual-era 2026-07-28 support #2272)@modelcontextprotocol/client2.0, dropping the runtime dependency on the 1.x SDK. (refactor(server): migrate the Strata MCP client to the v2 client SDK #2274)sessionidentity handle so an agent can identify its own calls under the stateless transport. (feat(server): give /mcp clients a server-minted session identity handle #2289)Rust claw-server (
apps/claw-server-rust)rmcp2.1 to 3.x, adapting tool handlers to the MRTR-aware response type. (chore(claw-server): upgrade rmcp to 3.x with a legacy protocol version pin #2275)mcp-session-idheader that the revision removes, is now carried as a SEP-2567 server-minted handle passed as an ordinary tool argument. (feat(claw-server): serve the modern MCP revision with a session handle #2278)Backward compatibility
Legacy clients are untouched:
initialize,mcp-session-idsessions, and the existing tab-group behavior all continue to work; stdio serving is unchanged.Status
Draft. Best-practices hardening (Host-header / DNS-rebinding validation, loopback bind review) and dual-era acceptance and probe tests are still to land on this branch before it is ready for review.