feat(everything): serve both protocol eras on SDK v2 (2026-07-28 + legacy) - #4551
Draft
olaservo wants to merge 2 commits into
Draft
feat(everything): serve both protocol eras on SDK v2 (2026-07-28 + legacy)#4551olaservo wants to merge 2 commits into
olaservo wants to merge 2 commits into
Conversation
…gacy)
Migrates the everything server to the v2 scoped SDK packages
(@modelcontextprotocol/server, /node, /core, /server-legacy at 2.0.0-beta.5)
and adds support for protocol revision 2026-07-28 alongside the existing
legacy revisions (2024-10-07 through 2025-11-25).
Transports now hand the server factory to an SDK serving entry rather than
constructing an instance and connecting a transport:
- stdio: serveStdio(factory) serves both eras; the opening exchange selects
one (server/discover probe vs. initialize handshake).
- streamableHttp: createMcpHandler(factory) + toNodeHandler serves both eras
from one endpoint. The session map, mcp-session-id plumbing,
InMemoryEventStore/Last-Event-ID replay, and hand-written GET/DELETE routes
are all removed - 2026-07-28 has no protocol-level sessions and no SSE
resumability, and subscriptions/listen replaces the standalone GET stream.
- sse: unchanged in behaviour, now explicitly legacy-only via server-legacy.
Tools are written once and served to both eras. Those needing client input
return inputRequired(...) instead of pushing a server->client request; the
SDK's legacy shim converts the same return into real server->client requests
for legacy clients, so no tool branches on ctx.era.
Notable behaviour changes:
- registerConditionalTools is gone. 2026-07-28 has no handshake to learn
capabilities from, capabilities are per-request, and tools/list may not vary
per connection. Tools register unconditionally and the SDK refuses a caller
lacking a capability at dispatch with -32021, as the spec requires.
- trigger-{sampling,elicitation}-request-async removed: they polled the
client's task endpoints, and 2026-07-28 removed the server->client channel
with no successor.
- trigger-url-elicitation drops the -32042 error path and its session-keyed
dedupe, both legacy-only by construction.
- simulate-research-query is no longer task-based. SDK v2 removed the
experimental tasks layer and the redesigned extension cannot be served
through the SDK; the tool keeps its staged progress and mid-flight
elicitation as a multi-round-trip flow, threading state through an
HMAC-sealed requestState.
- toggle-simulated-logging is legacy-only and documented as such: it models a
connection-scoped log stream, which 2026-07-28 removed.
Also adds cache hints for the cacheable list surfaces and requestState
verification, and updates all seven docs plus the README.
Requires Node.js >= 20 and is ESM-only, inherited from SDK v2.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…resources
The 2026-07-28 cache fields resolve most-specific-author-first: fields the
handler returns, then a per-registration `cacheHint`, then the server-level
`ServerOptions.cacheHints`, then the conservative default (`ttlMs: 0`,
`cacheScope: "private"`).
Only the server level was in use, so reads of the static `docs/` resources fell
through to that default and were never cacheable. They are the one genuinely
static surface here - the files ship inside the package and change only when it
is rebuilt - so they now carry `{ ttlMs: 3_600_000, cacheScope: "public" }`.
`public` is correct because the content is identical for every caller. The
dynamic and session-scoped resources deliberately keep no hint and continue to
fall through to the conservative default.
Verified on the wire: a modern client reading a static document now sees
ttlMs=3600000 / cacheScope=public (vs the server-level 60s on tools/list), and
a legacy client still sees no cache fields at all.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
knoal
reviewed
Jul 28, 2026
knoal
left a comment
There was a problem hiding this comment.
Reviewing via MCE A/B pilot 8 (sophia@hermes.local).
Summary
Big change: switches everything server to dual-era support (legacy 2024-10-07 to 2025-11-25 + modern 2026-07-28+). Migrates imports from @modelcontextprotocol/sdk to @modelcontextprotocol/server (v2 beta). Requires Node.js >= 20, ESM-only. 1982/-2478 LOC, 53 files, DRAFT.
What's good
- Fits the MCP servers rubric perfectly: "Enhancements that demonstrate MCP protocol features" — this demonstrates the new protocol era.
- Excellent docs: dual-era table in README.md, references the discover spec, documents Node.js >= 20 requirement.
- server-legacy correctly marked deprecated: "frozen copy of v1's SSE transport and OAuth Authorization Server helpers for migration purposes only."
Pre-merge verifications
- zod 4.2.0 doesn't conflict with workspace's existing zod installs (the same issue bit PR #4557).
- Node.js >= 20 is documented for self-hosters.
- Both legacy and modern test fixtures are present — the diff should show tests for both protocol eras.
APPROVE_WITH_CAVEATS — solid demonstration of the new protocol era, but verify the zod 4.2.0 install and Node 20+ requirement.
— sophia
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.
Description
Migrates
everythingto the v2 scoped SDK packages (@modelcontextprotocol/server,/node,/core,/server-legacyat2.0.0-beta.5) and adds protocol revision 2026-07-28 alongside the legacy revisions (2024-10-07…2025-11-25). One codebase, both eras.Targets
v2/mainper the branch discipline in #4475.Note
Scope differs from #4475. That issue scopes re-implementing #4452 — an SDK package migration that stays on the legacy protocol, whose own roadmap defers modern stateless MCP as "needs maintainers support". This PR is a superset: it does that migration and the modern era. Happy to split it if the decomposition wants finer slices.
#4452 was used only as a cross-check, not a source. We converged independently on the same mechanical answers (import mapping,
extra→ctxreshape) and diverge exactly where its roadmap stops.Server Details
everythingMotivation and Context
2026-07-28 is a structural break, not a version bump: no
initializehandshake, no sessions, no server→client request channel, no SSE resumability, andsubscriptions/listenin place of the standalone GET stream.everythingexists to exercise the protocol, so it should exercise both eras.Transports
Each transport now hands the factory to an SDK serving entry instead of constructing an instance and connecting a transport:
stdioserveStdio(factory)streamableHttpcreateMcpHandler(factory)+toNodeHandlersseserver-legacystreamableHttploses the session map,mcp-session-idplumbing,InMemoryEventStore/Last-Event-IDreplay, and hand-written GET/DELETE routes — 238 → ~90 lines.Tools are written once
Tools needing client input return
inputRequired(...)rather than pushing a request. The SDK's legacy shim converts the same return into real server→client requests for legacy clients, so no tool branches onctx.era.Behaviour changes worth review
registerConditionalToolsremoved. 2026-07-28 has no handshake to learn capabilities from, capabilities are per-request, andtools/listmay not vary per connection. Tools register unconditionally; the SDK refuses a caller lacking a capability at dispatch with-32021, which the spec makes a MUST. Net effect: a capability-less client sees 17 tools instead of 13 and gets a clean, fast refusal.trigger-{sampling,elicitation}-request-asyncremoved (~500 lines). They polled the client's task endpoints; 2026-07-28 removed that channel with no successor, and MRTR covers the use case.trigger-url-elicitationdrops the-32042error path and its session-keyed dedupe — both legacy-only by construction.simulate-research-queryis no longer task-based. SDK v2 removed the experimental tasks layer, and the redesigned extension (io.modelcontextprotocol/tasks, SEP-2663) cannot be served through the SDK:tasks/*are spec names absent from the modern registry, answered-32601even with a handler registered, and not registrable as vendor-prefixed custom methods. The tool keeps its staged progress and mid-flight elicitation as a multi-round-trip flow, threading state through an HMAC-sealedrequestState.toggle-simulated-loggingis legacy-only, and says so in its description. It models a connection-scoped log stream; 2026-07-28 removed both halves (logging/setLevel→ per-request_meta.logLevel, and servers MUST NOT emitnotifications/messagefor requests without it). The toggle is accepted but nothing arrives. Verified non-destabilising — the send is filtered, not rejected.Also adds cache hints for the cacheable list surfaces, and
requestStateverification wired intoServerOptions.How Has This Been Tested?
initialize) and once withversionNegotiation: { mode: 'auto' }(negotiating 2026-07-28 viaserver/discover). On both eras: 17 tools listed, and elicitation, sampling, roots, and the multi-roundrequestStateflow all round-trip from one unchanged handler set.isErroron legacy,-32021on modern.filesystem,memory,sequentialthinking) still build against the shared lockfile.Breaking Changes
trigger-url-elicitationloses itserrorPathargument.stdio|sse|streamableHttp), and the prompt/resource surface is unchanged.Types of changes
Checklist
Additional context
Docs. All seven files under
src/everything/docs/were rewritten. This matters beyond tidiness:instructions.mdis served to clients as the server instructions, andresources/files.tsexposes the whole folder as static resources — stale docs were being handed to models.Formatting.
prettier:checkfails on this repo for pre-existing reasons (several test files are already non-conforming onmain, and it isn't enforced in CI). I formatted only what I changed and deliberately avoided reformatting untouched code, to keep the diff reviewable.Draft because: the manual LLM-client test is outstanding, and the scope question above may want maintainer input before this is treated as final.
🤖 Generated with Claude Code