Repository navigation
[Not ready for merge] Serve both 2025-era and 2026-07-28 MCP protocol revisions over stdio - #85
smartinellibenedetti wants to merge 6 commits into
Conversation
Runs the official v1-to-v2 codemod (Server/Client packages, string-literal request handler methods) plus the manual fixes it can't automate: bumps zod to v4 (required by v2) and fixes the resulting z.record() signature and ZodError.issues changes, types tools/call and prompts/get handlers against the new strict CallToolResult/GetPromptResult schemas, fixes prompts/get error paths that returned a non-spec-compliant isError/content shape, and replaces zod-to-json-schema (incompatible with zod v4 — it was silently emitting empty schemas) with zod's built-in z.toJSONSchema. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ation
zod v4's z.toJSONSchema(schema, { io: "input" }) — needed so optional
fields with a .default() (e.g. api_call's method) aren't misreported as
required — silently drops additionalProperties from plain z.object()
schemas, unlike the zod-to-json-schema output it replaced. That's a real
regression: clients validating tool arguments against the schema would no
longer catch unknown/misspelled params before calling.
Restores it in convertSchema() and adds integration coverage that drives
the real compiled server over stdio (the only way this surfaces, since it
depends on the SDK's own tools/list response validation) to check every
tool's inputSchema declares additionalProperties: false and that
optional/defaulted params never appear in required.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bumps the inspector devDependency from ^1.0.0 to ^2.3.0 per https://github.com/modelcontextprotocol/inspector/blob/main/docs/v1-to-v2-migration.md. Our only invocation (npm run inspect -> mcp-inspector node dist/index.js) has no CLI flags, so it's unaffected by v2's flag-ordering change and needed no script updates — verified it still launches cleanly on the new port layout (6274 web + 6275 sandbox, no 6277 proxy) with the new MCP_INSPECTOR_API_TOKEN env var. Also removes the now-dead overrides.@modelcontextprotocol/sdk.ajv pin: the v1 SDK was inspector v1's last transitive dependent, and with it gone the override was a no-op. Confirmed ajv still resolves safely to 8.20.0 on its own via inspector v2's direct dependency. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The v2 SDK's modern-era (2026-07-28) support is opt-in: a hand-wired server.connect(new StdioServerTransport()) only ever serves the 2025-era protocol regardless of SDK version. Switches to serveStdio(buildServer) from @modelcontextprotocol/server/stdio, which pins one instance per connection and transparently serves both eras from the same handler registrations via the SDK's legacy shim — no handler logic changes. Verified both eras end-to-end: the existing 2025-era integration test (via @modelcontextprotocol/client) still passes, and a hand-built 2026-07-28 server/discover -> tools/list -> tools/call round trip against the built server returns resultType: "complete" throughout. Also adds inspector.config.json (a catalog entry with protocolEra: "modern") and points npm run inspect at it via --config/--server, since MCP Inspector's ad-hoc launch mode (mcp-inspector <command>) has no persisted server entry for a Protocol Era setting to attach to. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-era test
Drops the buildServer() factory wrapper from the previous commit: since a
stdio process serves exactly one connection for its whole lifetime,
serveStdio()'s factory is only ever called once anyway, so there's no need
to move the ~300 lines of handler registrations into a new function scope.
Builds the single Server instance at module scope exactly as before and
hands serveStdio() a trivial `() => server` factory instead — same
runtime behavior, but the diff against the SDK v2 migration base is now a
few dozen lines instead of a wholesale reindentation of the whole file.
Also wires up ServeStdioOptions.onerror: out-of-band errors from the
opening/era-classification exchange (e.g. a malformed 2026-07-28 envelope
claim) happen before any Server instance is pinned, so they never reached
the instance's own onerror — they were answered correctly on the wire but
invisible in our logs until now.
Adds an integration test that pins a real Client to
`versionNegotiation: { mode: { pin: "2026-07-28" } }` and drives a full
discover -> tools/list -> tools/call round trip against the built server.
Verified it's load-bearing by temporarily reverting the serveStdio
bootstrap to the old hand-wired (2025-only) form: the test failed with a
clear SdkError, then passed again once restored.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the server’s stdio bootstrap to opt into MCP’s 2026-07-28 (“modern era”) protocol revision while continuing to serve the existing 2025-era protocol via the SDK’s legacy shim. It also updates the MCP Inspector workflow so local inspection can explicitly negotiate modern era.
Changes:
- Switch stdio startup in
src/index.tsfrom a hand-wired transport connection toserveStdio(...)to support both protocol eras. - Add an integration regression test that pins the client to
2026-07-28and performs a full round trip. - Update Inspector usage: add
inspector.config.json, bump@modelcontextprotocol/inspector, and changenpm run inspectto use the config entry.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/index.ts | Migrates stdio bootstrap to serveStdio and adds logging for opening/negotiation errors. |
| src/tests/integration/integration-server-tool-gating.test.ts | Adds modern-era pinned integration coverage to catch regressions in stdio wiring. |
| package.json | Updates Inspector dependency and changes inspect script to use a persisted config/server entry. |
| inspector.config.json | Adds MCP Inspector catalog entry with protocolEra: "modern" for this server. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const result = await client.callTool({ | ||
| name: "docs_search", | ||
| arguments: { query: "node filters" }, | ||
| }); | ||
| expect(result.content).toBeDefined(); |
6e13b42 to
b8a9cd2
Compare
|
Superseded by #88 — same sdk-update base, but includes the elicitInput→MRTR rework for destructive-action confirmation this PR didn't yet have, plus cacheHints, the resources/templates/list fix, and broader test coverage on both protocol eras. |
About this change
Jira Ticket: N/A
Purpose of the Changes
Opts this server into the MCP
2026-07-28("modern era") protocol revision over stdio, in addition to the2025-eraprotocol it already served. The v2 SDK's modern-era support is opt-in — a hand-wiredserver.connect(new StdioServerTransport())(what this server did before this PR) only ever serves the 2025-era protocol, regardless of SDK version.Switches the bootstrap in
src/index.tstoserveStdio(buildServer)from@modelcontextprotocol/server/stdio. This wraps the existing server construction (allsetRequestHandlerregistrations, unchanged) in abuildServer()factory;serveStdiopins one instance from that factory per connection and transparently serves both protocol eras from the same handler code via the SDK's legacy shim — no handler logic changes were needed.Also adds
inspector.config.json(a catalog entry withprotocolEra: "modern") and pointsnpm run inspectat it via--config/--server, since MCP Inspector's ad-hoc launch mode (mcp-inspector <command>) has no persisted server entry for a Protocol Era setting to attach to — without this, Inspector has nothing to let you flip to Modern era in its UI.Builds on
sdk-update(the v1→v2 MCP SDK migration branch, PR #83) — this PR targetssdk-update, notmain.Kind of Change
Development Checklist
npm run validatepasses locally (build + test + integration validations)@modelcontextprotocol/client's standard connect, which now runs through the legacy shim). Modern-era behavior was verified manually (see Testing below) rather than via an automated test, since it requires hand-constructing a raw2026-07-28envelope claim that the SDK's client library doesn't yet expose a high-level API for.RUNDECK_DOCS_PATHbehavior unaffected, or changes called out below — unaffectedTesting
Testing setup:
Then, to confirm modern-era negotiation specifically (not covered by the automated suite):
Also verified via the actual Inspector tool, not just raw JSON-RPC:
succeeds with
protocolEra: "modern"set in the config entry.Acceptance Criteria:
npm run buildsucceeds with no TypeScript errorsnpm testpasses (320/320) — confirms 2025-era behavior is unchangednpm run validatepasses2026-07-28-envelopeserver/discoverrequest against the built server succeeds and reportssupportedVersions: ["2026-07-28"]server/discover->tools/list->tools/call) completes withresultType: "complete"at every stepnpm run inspectlaunches cleanly via the new catalog-config invocation and Inspector negotiates Modern era against this server