Skip to content

[Not ready for merge] Serve both 2025-era and 2026-07-28 MCP protocol revisions over stdio - #85

Closed
smartinellibenedetti wants to merge 6 commits into
sdk-updatefrom
update-protocol
Closed

smartinellibenedetti wants to merge 6 commits into
sdk-updatefrom
update-protocol

Conversation

@smartinellibenedetti

Copy link
Copy Markdown
Contributor

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 the 2025-era protocol it already served. The v2 SDK's modern-era support is opt-in — a hand-wired server.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.ts to serveStdio(buildServer) from @modelcontextprotocol/server/stdio. This wraps the existing server construction (all setRequestHandler registrations, unchanged) in a buildServer() factory; serveStdio pins 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 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 — 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 targets sdk-update, not main.

Kind of Change

  • Bug Fix
  • Enhancement/New Feature/Behavior
  • Maintenance/Refactor
  • Other... (FILL IN)

Development Checklist

  • npm run validate passes locally (build + test + integration validations)
  • Unit tests added/updated for modified code — no new unit tests added; existing 320 tests pass unchanged (they exercise 2025-era behavior via @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 raw 2026-07-28 envelope claim that the SDK's client library doesn't yet expose a high-level API for.
  • New/changed tools, resources, or prompts documented in CLAUDE.md if applicable — N/A, no tool/resource/prompt surface changed
  • New environment variables documented in CLAUDE.md's Environment Variables table — N/A, none added
  • Docs download / RUNDECK_DOCS_PATH behavior unaffected, or changes called out below — unaffected

Testing

Testing setup:

npm install
npm run build
npm test
npm run validate

Then, to confirm modern-era negotiation specifically (not covered by the automated suite):

# Hand-built 2026-07-28 opening against the built server, over one connection:
# server/discover -> tools/list -> tools/call, each stamped with the modern
# envelope claim (_meta: protocolVersion/clientInfo/clientCapabilities).
# All three came back resultType: "complete"; server/discover reported
# supportedVersions: ["2026-07-28"] and serverInfo in its _meta.

Also verified via the actual Inspector tool, not just raw JSON-RPC:

npx mcp-inspector --cli --config ./inspector.config.json --server rundeck-mcp --method tools/list --format json

succeeds with protocolEra: "modern" set in the config entry.

Acceptance Criteria:

  • npm run build succeeds with no TypeScript errors
  • npm test passes (320/320) — confirms 2025-era behavior is unchanged
  • npm run validate passes
  • A raw 2026-07-28-envelope server/discover request against the built server succeeds and reports supportedVersions: ["2026-07-28"]
  • A full modern-era round trip (server/discover -> tools/list -> tools/call) completes with resultType: "complete" at every step
  • npm run inspect launches cleanly via the new catalog-config invocation and Inspector negotiates Modern era against this server

smartinellibenedetti and others added 6 commits August 19, 2026 00:26
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>
@smartinellibenedetti
smartinellibenedetti changed the base branch from sdk-update to custom-agent-header August 19, 2026 23:31
@smartinellibenedetti
smartinellibenedetti marked this pull request as ready for review August 19, 2026 23:32
Copilot AI lite review requested due to automatic review settings August 19, 2026 23:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.ts from a hand-wired transport connection to serveStdio(...) to support both protocol eras.
  • Add an integration regression test that pins the client to 2026-07-28 and performs a full round trip.
  • Update Inspector usage: add inspector.config.json, bump @modelcontextprotocol/inspector, and change npm run inspect to 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.

Comment on lines +201 to +205
const result = await client.callTool({
name: "docs_search",
arguments: { query: "node filters" },
});
expect(result.content).toBeDefined();
@smartinellibenedetti
smartinellibenedetti changed the base branch from custom-agent-header to sdk-update August 20, 2026 00:51
@smartinellibenedetti smartinellibenedetti changed the title Serve both 2025-era and 2026-07-28 MCP protocol revisions over stdio [Not ready for merge] Serve both 2025-era and 2026-07-28 MCP protocol revisions over stdio Aug 20, 2026
@smartinellibenedetti

Copy link
Copy Markdown
Contributor Author

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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants