feat: MCP v2 (draft) - #2843
Conversation
|
We do use a stateful implementation with Redis currently which we should get rid of with this PR right? |
… to 2.0.0-beta.4 - Bump @modelcontextprotocol/server and /node from 2.0.0-alpha.3 to 2.0.0-beta.4 - HTTP: replace hand-wired per-request transport + Redis session store with createMcpHandler (stateless legacy fallback serves 2025-era clients, modern 2026-07-28 era served natively); responseMode 'sse' preserves the immediate-header-flush behavior for long-running tools - stdio: replace server.connect(transport) with serveStdio(factory) - Tool-arg alias rewriting moves from transport onmessage hook to the parsed HTTP body / a wrapped stdio onmessage - Delete sessionStore.ts, redis.ts and the @upstash/redis dependency
|
Pushed an update that moves the migration to the SDK's stateless serving model (per the 2026-07-28 migration guide):
Net −133 lines. Verified: typecheck, build, lint, 46/46 tests, plus end-to-end smoke tests of both transports (legacy initialize handshake, sessionless |
Replaces the two transport-specific interception sites (HTTP body rewrite, stdio onmessage wrap) with z.preprocess on each tool's input schema. The SDK runs the preprocess during validation regardless of transport or protocol era, so the aliasing no longer depends on express.json() ordering or on serveStdio assigning transport.onmessage synchronously. Emitted JSON schemas are unchanged (verified via tools/list). Also: hoist static tool schemas/annotations out of the per-request createMcpServer(), precompute the WWW-Authenticate header, drop the dead Access-Control-Expose-Headers: MCP-Session-Id, and remove a redundant TRANSPORT_TYPE local alias.
Reverts the module-level hoisting for readability — the schema reads where the tool is registered. The z.preprocess alias rewriting stays in the schema (transport-agnostic), just written inline.
@modelcontextprotocol/server and /node 2.0.0-beta.4 declare engines.node >=20, so align the README requirements and Dockerfile example, the mcpb manifest compatibility block, the troubleshooting docs, and declare engines in package.json so installers see the floor on the top-level package instead of an EBADENGINE warning naming a transitive dep.
- Capture client info for modern-era (2026-07-28) stdio clients from the per-request _meta envelope (CLIENT_INFO_META_KEY); oninitialized only fires for the legacy initialize handshake, so modern clients lost the X-Context7-Client-IDE/Version headers and IDE-specific auth command. - Pass onerror to toNodeHandler: adapter-level failures (request conversion / handler.fetch throws) were answered with a bare 500 and logged nowhere. - Declare prompts/resources capabilities via the McpServer constructor instead of hand-wiring empty list handlers; the SDK installs the same empty-collection handlers plus proper error handling for prompts/get and resources/read.
P1 (functional regression): the auth sign-in nudge is dead on the 2026-07-28 era
Note: on the HTTP path this nudge was already inert in both old and new code.
|
P2 (fragility, not a bug): modern stdio identity relies on an untyped beta surfaceThe stdio factory sets The concern:
|
P4 (functional gap): SEP-2243 standard headers are not allow-listed for CORSEnabling modern-era serving means 2026-07-28 Streamable HTTP clients send the SEP-2243 standard headers
|
… test, nudge docs) - Allow Mcp-Method / Mcp-Name in CORS preflight so browser-based 2026-07-28 clients pass credentialed cross-origin calls (P4) - Move envelopeClientInfo into lib/utils and pin the untyped beta envelope shape with a unit test so an SDK bump fails loudly (P2) - Document the auth sign-in nudge as 2025-era-only by design: the modern inputRequired replacement would gate doc delivery behind the nudge, which is the wrong trade for a soft hint (P1) Claude-Session: https://claude.ai/code/session_01UdKfobSMnSFbdW6Lx78SSp
… http and stdio Spawn the real built binary for both transports and drive it with the v2 SDK client in both protocol eras (2026-07-28 pinned and the 2025 legacy handshake). The Context7 API is stubbed via CONTEXT7_API_URL with a local server that records requests, so era negotiation, tool listing (JSON Schema derivation through the z.preprocess alias wrapper), tool calls, hallucinated-arg aliasing, and client-info propagation (envelope, initialize handshake, and User-Agent fallback) are all asserted at the wire. Claude-Session: https://claude.ai/code/session_01UuNKMhXx5T1pHuj1fwG8UK
The v2 SDK packages went GA on 2026-07-27. Move node/server/client off 2.0.0-beta.4 to 2.0.0, which also picks up the beta.5 wire alignment (serverInfo moves to result _meta, envelope clientInfo optional) plus the GA fixes for SSE keep-alive frames, JSON Schema draft-07/06 dialect handling, and 401/403 on the negotiation probe. No source changes required. Claude-Session: https://claude.ai/code/session_01RRBT2REbb8sT7Sfab6b4xw
Migrate
@upstash/context7-mcpto MCP TypeScript SDK v2Summary
This PR migrates the
packages/mcpserver (@upstash/context7-mcp) from the MCPTypeScript SDK v1 (
@modelcontextprotocol/sdk) to v2(
@modelcontextprotocol/server+@modelcontextprotocol/node).It is a mechanical, behavior-preserving migration — no tool logic, prompts, transport
wiring, or runtime behavior changed. The bulk of it was produced by the official
@modelcontextprotocol/codemodand then reviewed by hand.Why v2
v2 replaces the single monolithic
@modelcontextprotocol/sdkpackage with a set offocused, ESM-only packages:
@modelcontextprotocol/server— the runtime-neutral server API (McpServer,stdio transport, shared types).
@modelcontextprotocol/node— the Node.js HTTP transport(
NodeStreamableHTTPServerTransport).The result is a smaller dependency surface, a cleaner public API, and first-class
support for non-Node runtimes. v1 is being superseded, so new work should target v2.
What changed (high level)
@modelcontextprotocol/sdk@modelcontextprotocol/server+@modelcontextprotocol/node@modelcontextprotocol/sdk/...{ query: z.string() }z.object({ query: z.string() })setRequestHandler(ListPromptsRequestSchema, …)setRequestHandler("prompts/list", …)StreamableHTTPServerTransportNodeStreamableHTTPServerTransportTwo concrete illustrations:
What did NOT change
zoddependency (already^4.4.3; v2's Standard Schema requirement is satisfiedby Zod v4).
How this was produced
Generated with the official MCP v1→v2 codemod and reviewed by hand:
Verification
All package checks pass after the migration:
typecheck(tsc --noEmit)build(tsc)test(vitest run) — 12/12 passinglint(eslint .)Before merging
2.0.0-alpha.3). The dependency pins reflect that.Recommend merging once v2 reaches a stable release and re-pinning to the GA version.
minimumReleaseAgeExclude: ['@modelcontextprotocol/*']entry added topnpm-workspace.yamlexempts the SDK packages from this repo's 7-dayminimumReleaseAgegate so the pre-release versions can be installed. It can beremoved once the depended-on v2 release is older than that window.
Roadmap
alphadraftbeta(legacy era MCP on modern interfaces)stable- backwards compatible with legacy era MCP clients; enabling Modern stateless MCP will need context7 maintainers support