Skip to content

[DO NOT MERGE] Port to SDK v2 (extension() registrar / composition)#612

Draft
felixweinberger wants to merge 28 commits intomainfrom
fweinberger/v2-extension-registrar
Draft

[DO NOT MERGE] Port to SDK v2 (extension() registrar / composition)#612
felixweinberger wants to merge 28 commits intomainfrom
fweinberger/v2-extension-registrar

Conversation

@felixweinberger
Copy link
Copy Markdown

@felixweinberger felixweinberger commented Apr 13, 2026

DO NOT MERGE — proof-of-concept port to SDK v2. CI is expected to fail (package.json depends on local file:/tmp/*.tgz SDK builds that combine unpublished typescript-sdk changes).

Review guide — file map, reading order, what to skip (105 files but ~90 are mechanical)

105 files changed, but ~90 of them are mechanical. This guide maps what to read vs skim.

If you have 10 minutes

  1. BREAKING.md — the public-surface delta. This is the contract.
  2. src/app.tsclass App declaration (search for class App extends EventDispatcher). The whole composition pattern is visible in the first ~50 lines of the class body: readonly client: Client, readonly ui: ExtensionHandle<...>, then ui.setRequestHandler(...) calls in the constructor. Note: no zod import — embedded spec types validated via specTypeSchema() from the SDK.
  3. src/app-bridge.ts — same, search for class AppBridge extends EventDispatcher. Note readonly server: Server and the server.setRequestHandler('tools/call', ...) proxy wiring alongside ui.* for ui/* methods.
  4. Skim the test diff: src/app-bridge.test.ts — most of the file is unchanged. That's the point.

File map

Read carefully (~6 files, the actual design)

File What changed Why it matters
BREAKING.md new, 94 lines The contract. What consumers must change.
src/app.ts rewrite, ~−1,600 lines net App composes Client + ExtensionHandle. No assert*Capability overrides. No zod import — specTypeSchema('CallToolResult') etc. passed directly.
src/app-bridge.ts rewrite, ~−2,000 lines net AppBridge composes Server + ExtensionHandle. Standard tools/call etc. via setRequestHandler.
src/events.ts −178 lines ProtocolWithEvents (subclass of SDK Protocol) → standalone EventDispatcher<EventMap>. No SDK coupling.
src/message-transport.ts −59 lines PostMessageTransport on the v2 Transport interface.
src/server/index.ts −431 lines registerAppTool/registerAppResource on v2 McpServer.

Skim (mechanical, verify pattern then move on)

Area Files What changed
src/spec.types.ts, src/types.ts 2 Import paths v1→v2; interface McpUi*Capabilitiestype (for JSONObject constraint).
src/generated/{schema.ts,schema.json,schema.test.ts} 3 Auto-generated; uses z.custom<T>(isSpecType('T', v)) for embedded SDK types. Don't review line-by-line.
src/app-bridge.test.ts 1 73/88 v1 tests pass unmodified. Final: 85 pass / 1 skip / 0 fail.
src/react/useApp.tsx, src/server/index.test.ts, src/*.examples.ts 5 Import-path changes only.
examples/** ~80 files across 25 workspaces Per-example: package.json deps, main.ts import path (/stdio subpath), server.ts v2 McpServer shape. Read one (examples/basic-server-react/) to see the pattern.
docs/, scripts/generate-schemas.ts 3 Import paths; generator emits isSpecType() for SDK types.
package.json, build.bun.ts, tsconfig.json, lockfile 4 Dep swap; tsconfig exclusions for unported *.examples.ts.

Recommended reading order (by commit)

25 commits, intentionally staged. Read bottom-up in groups:

1. Setup (skim): 231251a6 deps swap, b50315b1 types/schemas, a9713e3d EventDispatcher + transport.

2. The core rewrite (read): 25da6658 App, a1f1f016 AppBridge, a5378733 react/server.

3. Compile fixes (skim): ea3f4af5..a97206ca.

4. v1 parity fixes (read commit messages): 87fee346, f7490c6b, 47f4cdf3, be67283e — each is a behavior the test suite caught.

5. Tests + examples (skim): 7e8ae100, cce8436b, 570781e0, 9998758b, 4298d34a, b845fd7c.

6. Upstream-fix follow-through (skim): 953a5d81/4e26407d (#1871), 76b3ba7a /stdio import.

7. Validator integration (read): c8bc2649 eliminates sdk-compat.ts via isSpecType(); 60e9d690 passes specTypeSchema() directly to ExtensionHandle after #1846's StandardSchemaV1 widen.

Questions to evaluate

  • Does composition cover what subclassing did? Compare BREAKING.md's "Removed inherited member" table against what consumers actually use.
  • Is the ui/* ↔ standard-method split clean? In app-bridge.ts, find where ui.setRequestHandler(...) and server.setRequestHandler('tools/call', ...) coexist.
  • Is specTypeSchema() the right validator API? app.ts passes it directly to ExtensionHandle — no Zod at the consumer. generated/schema.ts uses z.custom(isSpecType()) because that file is Zod-composed.
  • Wire compat: v2 AppBridge keeps a ui/initialize handler for v1-iframe back-compat. Right choice vs. clean break?

Known gaps (intentional, documented)

  • npm run test:full includes examples/pdf-server/server.test.ts (~33 fails) — heavy example, deeper port out of scope.
  • tsconfig.json excludes src/**/*.examples.ts(x) and docs/ — companion JSDoc snippets use removed surface.
  • 18 non-basic example workspaces had import paths sed-fixed but builds untested.
  • CI is red until SDK deps publish — package.json points at file:/tmp/*.tgz.

Depends on:

Motivation and Context

App/AppBridge move from subclassing Protocol to composing Client/Server + ExtensionHandle per the SEP-1865 role assignment. See BREAKING.md for the full surface delta. 73/88 of the v1 app-bridge.test.ts tests pass unmodified against the rewrite (final: 85 pass / 1 skip / 0 fail).

How Has This Been Tested?

Locally against an SDK build with the dependencies above: npm run build, npm test (197 pass / 1 skip / 0 fail), and basic-server-react/basic-server-vanillajs/quickstart/basic-host example builds all green. npm run test:full retains the unported examples/pdf-server suite.

Breaking Changes

Yes — see BREAKING.md. Peer dep moves from @modelcontextprotocol/sdk@^1 to @modelcontextprotocol/{client,server}@^2.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

This PR is the artifact for reviewing the v2 shape. Once the SDK dependencies publish to @alpha, the file: deps will be swapped to npm versions and CI will go green.

…ssignment)

Preserves all public methods and on* handlers. Removed: 5 assert*Capability
no-op overrides, inherited Protocol surface. Wire renames: notifications/message
-> ui/log; oncalltool/onlisttools now on ui/{call-view-tool,list-view-tools}.
Standard MCP proxying via server.setRequestHandler. ui/initialize handler kept
for v1-iframe wire compat. callTool/listTools wire renamed to ui/{call-view-tool,
list-view-tools}.
…nHandle casts; ctx type params; exclude docs/; gen-schemas path
… tsc clean

npx tsc --noEmit -> 0 errors
npm run build -> see commit body
…pulls child_process)

npm run build -> see body
…express,node}; restore test tsconfig includes
…mulate, onclose shim, onupdatemodelcontext return type, getToolUiResourceUri error message
… unported pdf-server suite

The pdf-server example's server.test.ts (~77 tests) needs deeper v2 porting
beyond the SDK migration scope. SDK tests + pdf-annotations.test.ts (no SDK
coupling) all pass.
@felixweinberger felixweinberger force-pushed the fweinberger/v2-extension-registrar branch from 1cfc3dd to 76b3ba7 Compare April 13, 2026 15:39
- spec.types.ts: import RequestId from @modelcontextprotocol/client (already public)
- app.ts/app-bridge.ts: replace pass-through *Schema shims with z.custom<T>(v => isSpecType('T', v))
- generate-schemas.ts: emit isSpecType-backed z.custom for external SDK types instead of importing from sdk-compat
- delete src/sdk-compat.ts

Friction surfaced: ExtensionHandle.setRequestHandler/sendRequest accept AnySchema (Zod-only),
so specTypeSchema()'s StandardSchemaV1 return cannot be passed directly. Wrapped via
z.custom(isSpecType) instead. #1868 should widen the param type to StandardSchemaV1 | AnySchema.
@felixweinberger felixweinberger force-pushed the fweinberger/v2-extension-registrar branch from aa13066 to 2fe270f Compare April 13, 2026 18:10
…pt-sdk#1846 StandardSchemaV1 widening)

generated/schema.ts keeps z.custom(isSpecType()) since it composes into Zod
chains; specTypeSchema() is used at the ExtensionHandle boundary where the
SDK now accepts StandardSchemaV1. app-bridge event-map params type widened
ZodType -> StandardSchemaV1. app.ts no longer imports zod.
@felixweinberger felixweinberger force-pushed the fweinberger/v2-extension-registrar branch from 2fe270f to 60e9d69 Compare April 13, 2026 18:13
- AppBridge dual-listens on legacy notifications/message → loggingmessage event
- bridge.callTool/listTools throw descriptive error if iframe is v1 (instead of MethodNotFound)
- Unskip ping test (Server.ping() is public; skip reason was wrong)
- Document on*-setter replace-semantics with explicit test
- BREAKING.md: v1↔v2 interop section, host-first upgrade guidance
…mappings

- Remove tsconfig excludes for *.examples.ts(x) and docs/ — all now typecheck
- Port companion files to v2 API (app.ui.sendRequest, client.callTool, z.object wraps)
- typedoc externalSymbolLinkMappings for @modelcontextprotocol/{client,server}
- Fix raw-shape inputSchema and Schema.shape→Schema across 9 example servers
- 25/25 examples now build; typedoc warnings 11→0
….registerTool)

All 33 fails fixed: v2 registerTool requires StandardSchema (raw shapes lack
~standard.validate). Also extra.signal→extra.mcpReq.signal, drop stale v1
import. test:full now 277/2/0 (was 197/1/33). e2e API tests pass; browser
tests need playwright install (env, not v2 break).
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.

1 participant