-
Notifications
You must be signed in to change notification settings - Fork 2k
Export Protocol and mergeCapabilities from client/server package roots #2501
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| --- | ||
| '@modelcontextprotocol/core-internal': minor | ||
| '@modelcontextprotocol/client': minor | ||
| '@modelcontextprotocol/server': minor | ||
| '@modelcontextprotocol/codemod': patch | ||
| --- | ||
|
|
||
| Export the `Protocol` base class and `mergeCapabilities` from the `@modelcontextprotocol/client` and `@modelcontextprotocol/server` package roots. | ||
|
|
||
| Some SDK consumers build their own JSON-RPC protocols on the `Protocol` engine rather than speaking MCP itself — ext-apps (MCP Apps) runs its `ui/initialize` handshake over an iframe postMessage channel this way. Subclassing `Client`/`Server` forces the core MCP `initialize` handshake onto such channels, which their deployed peers do not understand. Exporting `Protocol` restores the v1 extension point: `Protocol.connect()` wires the transport without any handshake, and non-spec methods pass through the wire layer without era gating. | ||
|
|
||
| The supporting protocol types (`ProtocolOptions`, `BaseContext`, `RequestOptions`, `Transport`, …) were already public. | ||
|
|
||
| The codemod now rewrites `Protocol` and `mergeCapabilities` imports from `shared/protocol.js` to the client or server package root, like the module's other symbols, instead of dropping them with an action-required marker. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| /** | ||
| * Pins that the `Protocol` base class and `mergeCapabilities` are exported from | ||
| * the package ROOT — not just from the core-internal public barrel. The root's | ||
| * `export * from '@modelcontextprotocol/core-internal/public'` is what carries | ||
| * them; replacing it with named exports (or adding a colliding explicit export) | ||
| * would silently drop them while the barrel-level pin stays green. | ||
| */ | ||
| import { describe, expect, test } from 'vitest'; | ||
|
|
||
| import { mergeCapabilities, Protocol } from '../../src/index'; | ||
|
|
||
| describe('package root exports', () => { | ||
| test('Protocol and mergeCapabilities are exported from the client root', () => { | ||
| expect(typeof Protocol).toBe('function'); | ||
| expect(typeof mergeCapabilities).toBe('function'); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -171,20 +171,10 @@ | |
| ResourceTemplate: 'ResourceTemplateType' | ||
| } | ||
| }, | ||
| '@modelcontextprotocol/sdk/shared/protocol.js': { | ||
| target: 'RESOLVE_BY_CONTEXT', | ||
| status: 'moved', | ||
| removedSymbols: { | ||
| Protocol: | ||
| 'The Protocol base class is not exported by the v2 packages. To observe or handle inbound requests ' + | ||
| 'that have no registered handler, use client.fallbackRequestHandler / server.fallbackRequestHandler; ' + | ||
| 'build custom behavior on Client or Server instead of subclassing Protocol. ' + | ||
| 'See the migration guide: Behavioral changes > Client connection & dispatch.', | ||
| mergeCapabilities: | ||
| 'mergeCapabilities() is not exported by the v2 packages. Pass the complete capabilities object to the ' + | ||
| 'Client/Server constructor, or merge capability objects with a plain object spread.' | ||
| } | ||
| status: 'moved' | ||
| }, | ||
|
Check warning on line 177 in packages/codemod/src/migrations/v1-to-v2/mappings/importMap.ts
|
||
|
Comment on lines
174
to
177
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Routing Extended reasoning...The bug. This PR removes the The code path. Consider a
Why existing code doesn't prevent it. Pre-PR, Impact. Bounded, which is why this is a nit rather than a blocker: (1) the trigger requires a mixed project importing How to fix. Emit an info/action-required diagnostic when the |
||
| '@modelcontextprotocol/sdk/shared/transport.js': { | ||
| target: 'RESOLVE_BY_CONTEXT', | ||
| status: 'moved' | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🟡 The newly-public
Protocolextension point is only documented in prose inside the v1→v2 migration guide, which is addressed to migrating users — a new v2 consumer has no discoverable docs thatProtocolis subclassable, how to implement it, or the dual-bundled-copyinstanceofcaveat (which lives only here). Consider adding a short section or pointer indocs/advanced/custom-methods.md(and/or an examples/ story) covering the custom-JSON-RPC-engine use case.Extended reasoning...
What the finding is. This PR makes
ProtocolandmergeCapabilitiespublic API on the@modelcontextprotocol/clientand@modelcontextprotocol/serverpackage roots, positioned as the supported extension point for consumers that build their own JSON-RPC protocols on the SDK engine (the ext-appsui/initializeuse case). However, the only prose documentation added is the section indocs/migration/upgrade-to-v2.md(~lines 1447–1466) — content framed for v1 migrators ("TheProtocolbase class andmergeCapabilitiesmoved: import them from … instead ofshared/protocol.js"). There is no guide page, section, or example addressed to a new v2 consumer.Verification. Grepping
docs/forextends Protocol,Protocol base class,subclass Protocol, orcustom JSON-RPCmatches nothing outside the migration guide.docs/advanced/custom-methods.md— the natural home, since it already documents custom JSON-RPC methods onClient/ServerviasetRequestHandler— never mentions thatProtocolis public and directly subclassable.mergeCapabilitiesappears in no non-migration doc. Noexamples/story demonstratesextends Protocol. The only other documentation is the (good) JSDoc on theProtocolclass inpackages/core-internal/src/shared/protocol.ts.Why this matters. Concrete walkthrough of the gap: a developer starting fresh on v2 wants to run a custom handshake protocol over an SDK transport (exactly the ext-apps shape this PR exists to support). They browse
docs/advanced/—custom-methods.md,custom-transports.md,low-level-server.md— and find only theClient/Servercustom-method path, which still forces the MCPinitializehandshake onto their channel. Nothing tells them (1) thatProtocolis importable from the package root, (2) what a subclass must implement (buildContext— identity is fine — plus the three capability assertion hooks), or (3) the dual-bundled-copy caveat: the client and server packages each bundle their own compiled copy of the class, soinstanceofreturnsfalseacross the two copies and the nominal types don't mix. That last caveat is a real consumer-facing gotcha, and it is currently discoverable only via the migration guide. The repo review checklist also lists, for new features, "verify prose documentation is added (not just JSDoc), and assess whether examples/ needs a new or updated example" as a distinct item from the migration-guide item.Why nit, not blocking. Prose documentation does exist (migration guide + changeset + JSDoc), the primary audience for this feature today is migrating v1 consumers like ext-apps — who are well served by the migration guide — and there is zero runtime impact. The export itself is well-tested (barrel + both package roots + a no-initialize-on-the-wire behavior pin).
Suggested fix. Add a short section (or pointer) in
docs/advanced/custom-methods.mdcovering the "build your own protocol on the engine" case: importProtocolfrom one package root, implementbuildContext+ the three assertion hooks, note thatconnect()performs no MCPinitialize, and include the single-package-consistently /instanceofcaveat. Optionally add anexamples/story mirroringpackages/core-internal/test/shared/publicProtocol.test.ts. This can equally land as a fast follow-up.