MCP Spec RC (2026-07-28) + SDK 2.0.0-beta — Audit (T639)
Audit-only. No code changed, no dependency bumped. Scope: src/mcp.ts (MCP server —
exposes the orchestrator) and src/adapters/McpToolAdapter.ts (MCP client — consumes
arbitrary remote MCP servers), read against the MCP specification release candidate that
ships final on 2026-07-28 and the @modelcontextprotocol/sdk 2.0.0-beta line (beta.3 → beta.5
as of this audit). Installed/live dependency: @modelcontextprotocol/sdk@1.29.0
(package.json pins ^1.29.0) — confirmed via node_modules/@modelcontextprotocol/sdk/package.json
and left untouched.
SUPERSEDED IN PART — read this before acting on anything below (added 2026-09-05, T649 implementation).
This audit was written against the spec RC and the
2.0.0-betaline. The stable2.0.0packages shipped on 2026-07-28 and they do not behave the way the beta led this document to predict. Three of its load-bearing claims were re-checked against the real published tarballs (npm view <pkg>@2.0.0 dist.tarball→ curl → tar → read the emitted.d.mts) and corrected:
- The Tasks extension was NOT removed.
@modelcontextprotocol/core@2.0.0still shipstasks/getandtasks/cancelmethod schemas plusServerTasksCapabilitySchema,ListTasksResultSchemaandCancelTaskRequestSchema(indist/auth-BWdKR39I.d.mts, re-exported throughdist/internal.d.mts). Grepping onlydist/index.d.mtsreturns zero hits and gives the wrong answer. SEP-2663 describes the beta, not the release.getClientVersion()was NOT deleted, socallerId()never broke.@modelcontextprotocol/server@2.0.0still declares it and marks it@deprecatedwith: "The accessor remains functional — instances serving the 2026-07-28 era are backfilled per request from the validated envelope."InitializeRequestSchemaandInitializedNotificationSchemaare likewise still exported. A 2025-era client still negotiates a 2025-era revision against the 2.x server — verified live, seenpm run selftest:mcp-handshake.- Raw Zod-shape
inputSchemastill works. The 2.x docs usez.object(...)and this repo converted to it, but a positive control (revert one tool to the bare shape, drive the server, readtools/list) produced identical properties. The conversion is a forward-compat cleanup, not a break-fix.Net effect: the migration this audit framed as a breaking rewrite was, against the stable packages, a routine dependency swap. It landed on 2026-09-05 —
@modelcontextprotocol/{server,client,core}@^2.0.0withzod@^4. Everything below is retained as the record of what was believed at audit time; treat the source tree and the handshake smoke as authority over it.
1. What the spec RC actually changes (primary sources)
Confirmed via the MCP project's own blog (not third-party summaries) and cross-checked against a second independent write-up:
initialize/notifications/initializedhandshake removed entirely (SEP-2575). Protocol version, client info, and client capabilities move from a one-time handshake to_metaon every request. A newserver/discoverRPC is mandatory for every server going forward (servers "MUST implement" it) and replaces up-front capability exchange.Mcp-Session-Idheader + protocol-level session removed (SEP-2567). No more sticky routing; a server that needs a durable handle must mint one itself and pass it as an ordinary tool argument.- Tasks moves from an experimental part of the core spec to its own official extension,
with a redesigned, stateless lifecycle:
tools/callcan return a task handle instead of blocking; the client drives it viatasks/get/tasks/update/tasks/cancel. The spec blog is explicit that this is a breaking redesign, not a promotion of the old shape: "Anyone who shipped against the2025-11-25experimental Tasks API will need to migrate to the new lifecycle." The old experimentaltasks/resultblocking call is gone, replaced by polling. - Deprecation policy: once GA, a feature must stay
Deprecatedat least 12 months before removal — relevant to how aggressively Cyberine needs to react once 2.0 SDKs land.
Sources: MCP blog — 2026-07-28 RC, MCP blog — SDK betas for the RC, stacktr.ee — what breaks.
2. What the TypeScript SDK 2.0.0-beta line actually changes (primary source: the SDK's own migration guide + release notes)
- Package split. The monolithic
@modelcontextprotocol/sdkbecomes@modelcontextprotocol/client,@modelcontextprotocol/server,@modelcontextprotocol/core(the shared Zod schema constants —CallToolResultSchemaand friends move here), plus framework adapters (@modelcontextprotocol/node,@modelcontextprotocol/express). ESM-only, Node 20+. A codemod exists to automate the mechanical parts. - Zod ≥4.2.0 required at runtime. The migration guide states v3 "typechecks cleanly" but
is "incompatible at runtime" — this is a hard, silent-failure class of bug if missed, not a
soft warning. Cyberine currently pins
zod: "^3.23.0"inpackage.json, and zod is imported in 9 files repo-wide (src/mcp.ts,src/research/synthesizer.ts,src/research/schema.ts,src/core/schema.ts,src/plugins/loader.ts,src/engine/searchFanOut.ts,src/engine/verifyClaims.ts,src/engine/synthesizeClaims.ts,src/engine/swarmPlannerSchema.ts) — a v2 MCP SDK adoption drags a repo-wide zod 3→4 migration behind it, not an MCP-local change. - Lazy schema construction (beta.4, 2026-07-13): wire schemas move from import-time
construction to a "module-level memoized factory" built lazily on first validation; a
preloadSchemas()escape hatch exists for runtimes (e.g. Cloudflare Workers) that need eager construction. Also confirmed directly in the installed 1.29.0 source (server/mcp.js): tool JSON-Schema conversion (toJsonSchemaCompat) already happens inside thetools/listhandler at list-time, not atregisterTool()time — so 1.29.0 already carries a forward-compatible shim toward this behavior; it is not a hard flip from "always-eager" to "always-lazy". - Handler signature change.
(extra) => ...becomes(ctx) => ...with a restructured shape (extra.signal→ctx.mcpReq.signal,extra.authInfo→ctx.http?.authInfo, etc). - Raw Zod-shape registration deprecated.
.tool()/.prompt()/.resource()variadic forms are removed;registerTool/registerPrompt/registerResourcewith an explicit config object are required (Cyberine already uses the config-object form — see inventory below). Raw shape objects passed asinputSchema(e.g.{ prompt: z.string(), ... }) must be wrapped inz.object({...})explicitly; the bare-shape convenience is gone. - Error hierarchy renamed.
McpError→ProtocolError; newSdkError/SdkHttpError(.statusinstead of.code). - Import paths move.
StdioServerTransportnow imports from@modelcontextprotocol/server/stdio;StdioClientTransport/StreamableHTTPClientTransportfrom@modelcontextprotocol/client/stdioand@modelcontextprotocol/clientrespectively. SSE and WebSocket transports are removed (irrelevant to Cyberine — it never used them; see inventory below). - The old experimental Tasks API is REMOVED from the SDK (SEP-2663), not carried forward.
As of
@modelcontextprotocol/server@2.0.0-beta.5(2026-07-21 — six days before spec GA), the migration guide states any code that registeredGetTaskRequestSchema-style handlers hits an "action-required diagnostic" codemod warning rather than an automatic rewrite, because the new stable Tasks Extension lifecycle described in section 1 has not yet landed in the beta line. This matters directly for part 3 below. - OAuth hardening (SEP-9207 issuer validation, SEP-2350 scope step-up via
onInsufficientScopeonStreamableHTTPClientTransport, SEP-2207 TLS-only token endpoints). Not exercised by Cyberine today (McpToolAdapteronly sends static bearer headers, no OAuth client flow) — noted for completeness, not a current break.
Sources: SDK betas blog, typescript-sdk migration guide, docs/migration/upgrade-to-v2.md, @modelcontextprotocol/server@2.0.0-beta.3 release, @modelcontextprotocol/server@2.0.0-beta.4/beta.5 releases. Installed-SDK cross-checks: node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js, .../server/index.js, .../types.js, .../experimental/tasks/interfaces.d.ts (all read directly, this audit).
Note on a claim I could NOT verify: the task brief mentioned a breaking ResponseCacheStore
change ("cache now stores JSON strings, not live object graphs"). I checked the installed
1.29.0 SDK source (no ResponseCacheStore/EventStore symbol beyond the example
inMemoryEventStore.js), the full docs/migration/upgrade-to-v2.md raw text, the
beta.3 changelog, and targeted web searches — none surfaced a ResponseCacheStore symbol or
change. Per the "docs say X, source says Y → trust the source" rule: flagging this as
unconfirmed against current primary sources, not asserting it. If it exists it may be an
internal/undocumented class not yet in the public changelog trail — re-check against the final
GA changelog before treating any caching-shape claim as authoritative.
3. Inventory — 1.x assumptions in src/mcp.ts and src/adapters/McpToolAdapter.ts
src/mcp.ts (server side)
| # | Code location | 1.x assumption | What breaks under the RC / v2 SDK |
|---|---|---|---|
| 1 | callerId() (L86-89), using server.server.getClientVersion() | Client identity is resolved once, at the initialize handshake, and is "stable for the whole process lifetime" (the function's own doc comment) — confirmed in the installed SDK: Server._oninitialize() sets _clientVersion = request.params.clientInfo exactly once. | The RC removes initialize/initialized entirely — client info travels in _meta per request, not via a one-time handshake. getClientVersion() as a connection-lifetime accessor has no spec-level backing once 2.0 lands; the code has no per-request identity plumbing to fall back on. This is the single highest-impact break — callerId() feeds every send_message/send_to_team/send_to_role/approve_run/reject_run call's from/decidedBy field. |
| 2 | Imports: @modelcontextprotocol/sdk/server/mcp.js, @modelcontextprotocol/sdk/server/stdio.js (L1-2) | Monolithic package, deep subpath imports | v2 splits into @modelcontextprotocol/server (+ /stdio subpath); every import line needs rewriting (a codemod exists for the mechanical part). |
| 3 | 9x registerTool(name, { inputSchema: { field: z.string()... } }, handler) — raw Zod-shape object as inputSchema | Bare-shape inputSchema accepted directly | v2 deprecates bare shapes; each of the 9 tool registrations needs z.object({...}) wrapping. Cyberine already uses the config-object form (registerTool(name, config, handler), not the removed variadic .tool()), so that half of the migration is already done. |
| 4 | package.json zod pin ^3.23.0 | zod 3.x runtime | v2 SDK packages require zod ^4.2.0 at runtime (typechecks under 3.x, breaks silently at runtime per the SDK's own migration note). Repo-wide blast radius (9 files, not just mcp.ts). |
| 5 | No server/discover handler | Handshake-era capability negotiation is sufficient | RC mandates every server implement server/discover. A 1.29.0-based McpServer simply won't answer it — harmless today (1.29.0 still speaks every protocol version back to 2024-10-07, so existing/legacy clients keep working), but a spec-RC-generation client probing this server would get an unimplemented-method error, not silent success. |
| 6 | ResourceTemplate + server.registerResource(...) (cyberine://runs/{runId}) | Resource-template list/read callback shape stable across versions | Import path moves with the package split; the callback shape itself is not called out as changed in the migration guide — lower risk than items 1-4, but unverified against the actual v2 resource-template API docs (not read this session; recommend re-checking ts.sdk.modelcontextprotocol.io/v2/ directly before migrating). |
| 7 | console.error(...) / installGracefulDrain('mcp', () => server.close()) | McpServer.close() API stable | Not called out as changed anywhere in the migration guide; lowest-risk item in this table. |
src/adapters/McpToolAdapter.ts (client side)
| # | Code location | 1.x assumption | What breaks under the RC / v2 SDK |
|---|---|---|---|
| 1 | Imports: @modelcontextprotocol/sdk/client/index.js, /client/stdio.js, /client/streamableHttp.js (L1-3) | Monolithic package, deep subpath imports | Same package-split issue as the server side: v2 moves these to @modelcontextprotocol/client (+ /stdio subpath). |
| 2 | new Client({name, version}, {capabilities: {}}) then await client.connect(transport) (L161-179) | connect() internally performs the initialize request + notifications/initialized — confirmed by reading the installed SDK's client/index.js (method: 'initialize', then method: 'notifications/initialized') | Under the RC, this internal handshake is replaced by _meta-per-request (+ an optional server/discover probe). This is entirely inside the SDK's Client.connect() — the adapter's own call site (connect, listTools, callTool, close) is untouched by the wire-protocol change as long as the SDK's public Client API keeps the same method names/shapes across the bump. This is actually a well-isolated boundary — the risk is 100% "does the v2 SDK keep this same four-method surface", not anything McpToolAdapter.ts itself assumes about session/handshake internals. |
| 3 | client.listTools() / client.callTool({ name, arguments }) (L183, L208) | No result-schema argument passed | Migration guide: spec methods on client.request()/callTool() "drop the result-schema argument; the SDK resolves it from the method name" — since this code never passed one, it is already compatible with that specific v2 change. |
| 4 | StreamableHTTPClientTransport(new URL(spec.url), { requestInit: { headers } }) (L119, makeTransport) | Static bearer/header auth only, no OAuth client flow | Unaffected by the RC directly; v2 adds onInsufficientScope OAuth step-up (SEP-2350) as an opt-in constructor option. A remote MCP server that requires OAuth 2.1 would not work through this adapter today, on 1.x or 2.x — a pre-existing gap, not something 2.0 newly breaks. |
| 5 | error instanceof Error in report() / connectMcpServer() / tool execute() catch blocks | Generic Error checks, never instanceof McpError | v2 renames McpError → ProtocolError and adds SdkError/SdkHttpError (.status not .code). Because this code never branched on the SDK's specific error class, it survives the rename unaffected — but any future code that wants to distinguish "protocol-level MCP error" from "connection/transport error" must use the new class names. |
| 6 | renderToolResult() (L80-113) reading result.content[].type/text/resource, result.structuredContent, result.isError | CallToolResult content-block shape | This is spec-level content shape, not SDK-version-level, and is untouched by the handshake/session changes in the RC — lowest-risk item in this table. |
| 7 | SSE / WebSocket transports | N/A — never used | The RC/v2 removal of SSE and WebSocket transports is a non-issue: McpToolAdapter only ever constructs StdioClientTransport or StreamableHTTPClientTransport. Calling this out explicitly so a future migration doesn't over-scope work here. |
4. Go/no-go — remodeling orchestrate around the Tasks extension
No-go for now. Revisit once all three of the GA conditions below are true.
Reasoning:
- There is nothing stable to build against yet. The 1.29.0 SDK ships an
experimental.tasksAPI (ExperimentalMcpServerTasks,TaskStore,createTask/getTask/getTaskResult— confirmed by readingexperimental/tasks/interfaces.d.tsdirectly) tied to the old2025-11-25experimental spec shape. The v2.0.0-beta line has already deleted that exact API (SEP-2663) and, as of beta.5 (2026-07-21, six days before spec GA), has not yet shipped its replacement matching the new stable Tasks Extension lifecycle described in the spec RC blog. Codingorchestrateagainst either shape today means coding against something that is either already removed or not yet implemented. - The actual UX problem
orchestratehas — synchronous block over stdio, no progress, no cancel — doesn't require the Tasks extension specifically. Cyberine already has the building blocks to fix this without depending on SDK task-handle support at all: MCP'snotifications/progress(keyed off aprogressTokenin the request's_meta, a much older and already-stable part of the spec, present in 1.29.0 today) covers progress feedback; and the repo's ownRunStore+GlobalRegistry+ message bus already giveorchestrateeverything a hand-rolled "return a handle now, poll/inspect later" pattern needs —inspect_run/poll_inbox/query_runsare already registered MCP tools reading the same run state a Tasks-based redesign would key off. A synchronousorchestratecall could be changed to kick off the run, return therunIdimmediately, and let the caller poll the existing tools — a repo-local change, decoupled from any MCP SDK version. - Once (a) the spec's Tasks extension ships GA, (b) a non-beta
@modelcontextprotocol/serverrelease implements it, and (c) Cyberine has done the zod 3→4 + package-split migration for other reasons anyway, then wrappingorchestrate's existingrunId/RunStoremachinery behind a real task handle becomes a small, low-risk addition on infrastructure Cyberine already has — worth doing then. Recommend filing it as a backlog task gated explicitly on those three conditions, not scheduling it now.
5. Recommendation
- Do not bump
@modelcontextprotocol/sdknow. v1.x is fed fixes for ~6 months past v2 GA per the project's own deprecation policy; there is no forcing function today, and jumping in during the beta window (the Tasks API deletion above is a concrete example of beta-to-beta churn) risks building against something that moves again before GA. - Track re-audit triggers, in priority order: (1)
@modelcontextprotocol/server/client/coreship a stable (non-beta) 2.0.0 release; (2) the Tasks Extension's replacement API lands in that stable release; (3) confirm/deny theResponseCacheStoreclaim against the GA changelog. - When migration is eventually scheduled, budget for it as a repo-wide change, not an
MCP-local one — the mandatory zod 3→4 bump alone touches 9 files outside
src/mcp.ts. - Add this doc's re-audit triggers to the next relevant
CHANGELOG-WATCH.mdsweep (R02) so the MCP SDK gets tracked alongside the other high-velocity upstreams instead of being revisited ad hoc.
No dependency version was changed as part of this audit. @modelcontextprotocol/sdk remains
pinned at ^1.29.0 in package.json, matching the installed 1.29.0 in node_modules.
Muse Code (Meta) — headless muse exec
Every claim below was checked against the installed binary or a real dispatch. Where a measurement contradicted the expectation, the measurement is what is recorded and the expectation is named so...
Website MASTER — "Run record" (marketing) + "Day Assembly / Night Approach" (docs)
Read this before any website work. Locked 2026-10-01 by the operator. It supersedes the Plasma home + "Six Instruments" routes MASTER, archived as website-master-20260925-plasma.md; the rejected...