Tracks the migration from @modelcontextprotocol/sdk v1 to the split v2
packages, plus follow-up upgrades while v2 is in beta. Both packages are pinned
to the latest beta published on npm, 2.0.0-beta.2 (verified 2026-07-06).
Current upgrade branch: chore/mcp-sdk-beta-2, created from origin/main at
3d46b04.
| # | Change | Repository impact | Status |
|---|---|---|---|
| 1 | Upgrade @modelcontextprotocol/server |
Production dependency pinned to 2.0.0-beta.2 |
Done |
| 2 | Upgrade @modelcontextprotocol/client |
Test-only dev dependency pinned to 2.0.0-beta.2 |
Done |
| 3 | Refresh npm lock data | Tarball URLs and integrity hashes now resolve beta.2 | Done |
| 4 | Review beta.2 compatibility | No application source changes required | Done |
| 5 | Run repository validation | Format, lint, typecheck, and all 265 tests pass | Done |
| 6 | Deploy and smoke-test staging | Deployed; metadata/auth routes pass, Wrangler-token limitation documented | Done |
Beta.2 adds ESM/CommonJS dual-package exports to both packages. The server also
fixes the HTTP mapping for a post-dispatch MissingRequiredClientCapabilityError
so an uncommitted response uses the specification-required 400 Bad Request.
This repository consumes the ESM/workerd exports and does not directly handle
that SDK error, so neither release change requires source adaptation.
| Package | Before | After |
|---|---|---|
@modelcontextprotocol/sdk |
^1.26.0 (dep) |
removed |
@modelcontextprotocol/server |
— | 2.0.0-beta.2 (dep) |
@modelcontextprotocol/client |
— | 2.0.0-beta.2 (devDep, tests only) |
v2 is a package split: the server APIs move to @modelcontextprotocol/server
(root export — there is no /server/* or /types.js subpath anymore) and the
Client used in tests moves to @modelcontextprotocol/client.
@cfworker/json-schema is not required as a direct dependency. In the v1
alpha it had to be installed manually; the v2 beta bundles the Workers JSON
Schema validator inline (validators/cf-worker) and selects it automatically on
the workerd/browser export conditions via the package's _shims entry, so
wrangler's bundle picks it up with no extra wiring.
src/index.ts—WebStandardStreamableHTTPServerTransport. This transport still exists in v2 (root export, same options:sessionIdGenerator,enableJsonResponse,retryInterval; samehandleRequest(request)/close()), so this is a one-line import repoint with no behavior change.src/server.ts,src/metrics.ts—McpServer(value / type).src/tools/{search,execute,docs-search}.ts—McpServertype;Tooltype.
CallToolRequestSchema / ListToolsRequestSchema no longer exist. v2's
Protocol.setRequestHandler takes the method string for spec methods:
-server.server.setRequestHandler(ListToolsRequestSchema, () => ({ ... }))
-server.server.setRequestHandler(CallToolRequestSchema, async (request) => { ... })
+server.server.setRequestHandler('tools/list', () => ({ ... }))
+server.server.setRequestHandler('tools/call', async (request) => { ... })The handler still receives the full parsed request (request.params.name,
request.params.arguments) and returns a CallToolResult, so the body is
unchanged. Tool and CallToolResult are imported as types from the root.
The registerCapabilities({ tools: ... }) call must stay before the handlers —
v2 throws if a handler is registered for an undeclared capability.
v2 registerTool expects a Standard Schema for inputSchema/outputSchema
rather than a raw { field: zodType } shape (the raw shape is a deprecated
auto-wrapped overload). Wrapped in z.object(...):
src/tools/search.ts—inputSchemasrc/tools/execute.ts— bothinputSchemashapessrc/tools/docs-search.ts—inputSchemaandoutputSchema
The non-Code-Mode path serves precomputed tool definitions that must stay
byte-identical to what the Code-Mode registerTool path emits (enforced by
tests/non-codemode.test.ts). Two v2 output changes were mirrored in the
precomputed artifacts:
- JSON Schema dialect — v2 (zod v4) emits
$schema: "https://json-schema.org/draft/2020-12/schema"instead of v1'sdraft-07. Updated insrc/openapi.ts(buildJsonInputSchema) and theDOCS_TOOLconstant insrc/tools/docs-search.ts. execution.taskSupport— v1 emittedexecution: { taskSupport: 'forbidden' }by default; v2 omitsexecutionfor non-task tools. Dropped from theNonCodemodeTooltype,buildNonCodemodeTools,toWireTool, andDOCS_TOOL.
Client now comes from @modelcontextprotocol/client; InMemoryTransport and
McpServer from @modelcontextprotocol/server.
npm run check # format:check, lint, typecheck, test
npm run deploy # wrangler deploy --env staging-
format:check,lint,typecheck: pass. -
npm test: 265 passed (16 files), including the Worker integration suite. One initial parallel run encountered transient five-second cold-start timeouts; the affected test passed in isolation, ESM resolution was confirmed, and an unchanged full rerun passed all tests in 9.85 seconds. -
Staging deployed successfully with a 94 ms Worker startup:
Worker: cloudflare-api-mcp-staging URL: https://staging.mcp.cloudflare.com Version ID: 8dc659f6-c631-4961-9717-e73bd094dbeb (100% traffic)
-
Deployed-worker smoke checks:
GET /.well-known/oauth-protected-resource→200.GET /.well-known/oauth-authorization-server→200.- unauthenticated
POST /mcp→401 invalid_token. - Wrangler OAuth token → production API identity probes return
200. - the same Wrangler token → staging API identity probes return
403. - authenticated
tools/listthrough the staging Worker therefore returns403 insufficient_scopebefore MCP dispatch, as expected for a production token presented toapi.staging.cloudflare.com.
The token was read directly from
wrangler auth token --json, was never printed or persisted, and cannot exercise authenticated MCP dispatch while the staging Worker correctly targets the staging Cloudflare API. The full MCP protocol, tool dispatch, and Worker Loader paths are covered by the passing local Worker integration suite.
The initial v1-to-v2 migration passed 261 tests (16 files), including the e2e
suite that drives the real worker (exports.default.fetch) through the full
Streamable HTTP transport, tool dispatch, and a real Worker Loader isolate call.
It was deployed to staging with a 107 ms startup and no bundle/boot errors,
confirming the bundled workerd validator shim resolved on the edge.
Historical beta.1 staging deploy:
Worker: cloudflare-api-mcp-staging
URL: https://staging.mcp.cloudflare.com
Version ID: a3ff901c-61ca-4e6f-bfc1-008b66b5fef8Deployed-worker smoke checks:
GET /.well-known/oauth-protected-resource→200(correct resource metadata).GET /.well-known/oauth-authorization-server→200.POST /mcpwithout auth →401 invalid_token(routes to the auth guard).
A full authenticated tools/list / tools/call against the staging endpoint
needs a token valid on the staging Cloudflare API (api.staging.cloudflare.com);
a production API token returns 403 there (verified: prod API 200, staging
API 403 for the same token), independent of this migration. The protocol
round-trip against 2.0.0-beta.1 is covered by the e2e test suite.
- The original
2.0.0-beta.1install required overriding the local npmmin-release-age; beta.2 was old enough at upgrade time to install normally. - Tests still emit the existing
vitest-pool-workersglobal-scope logging noise, but the run passes.