Skip to content

feat(core): export Protocol class + ProtocolSpec generic for typed custom vocabularies#1917

Closed
felixweinberger wants to merge 0 commit intofweinberger/v2-bc-3arg-custom-methodsfrom
fweinberger/v2-bc-export-protocol
Closed

feat(core): export Protocol class + ProtocolSpec generic for typed custom vocabularies#1917
felixweinberger wants to merge 0 commit intofweinberger/v2-bc-3arg-custom-methodsfrom
fweinberger/v2-bc-export-protocol

Conversation

@felixweinberger
Copy link
Copy Markdown
Contributor

Part of the v2 BC series — see reviewer guide. Stacks on #1916 (which stacks on #1891).

Exports the abstract Protocol class (was reachable in v1 via deep imports; ext-apps subclasses it) and adds Protocol<ContextT, SpecT extends ProtocolSpec = McpSpec>. Supplying a concrete ProtocolSpec gives method-name autocomplete and params/result correlation on the typed setRequestHandler/setNotificationHandler overloads.

Motivation and Context

v1 exposed Protocol via deep imports (sdk/shared/protocol.js). MCP-dialect protocols (e.g., MCP Apps) subclass it. v2 omitted it from the public surface — this restores it and adds a ProtocolSpec type-level vocabulary.

How Has This Been Tested?

packages/core/test/shared/protocolSpec.test.ts — typed-SpecT inference, SpecRequests<ProtocolSpec> = never, params typed by passed schema (not SpecT).

Breaking Changes

None — additive. Protocol stays abstract; the second generic defaults so existing Protocol<ContextT> references work unchanged.

Types of changes

  • New feature (non-breaking change which adds functionality)

Additional context

Stacks on #1916#1891.

@felixweinberger felixweinberger added this to the v2.0.0-bc milestone Apr 16, 2026
@felixweinberger felixweinberger added the v2-bc v2 backwards-compatibility series label Apr 16, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 16, 2026

⚠️ No Changeset found

Latest commit: 9da988e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 16, 2026

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@1917

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@1917

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@1917

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@1917

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@1917

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@1917

commit: 5fce199

@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-3arg-custom-methods branch from 2f34630 to daab2e2 Compare April 16, 2026 18:44
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-export-protocol branch 2 times, most recently from 33209d0 to ae50e77 Compare April 16, 2026 19:11
@felixweinberger
Copy link
Copy Markdown
Contributor Author

@claude review

Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No correctness issues spotted beyond the doc-drift nit, but this promotes Protocol (plus four new types) into the curated public surface and adds a second class-level generic — that's exactly the kind of API-surface commitment REVIEW.md says needs a human maintainer's sign-off, so deferring.

Extended reasoning...

Overview

This PR (third in the v2-bc stack) does two things: (1) re-exports the abstract Protocol class from core/public along with four new type-level helpers (ProtocolSpec, McpSpec, SpecRequests, SpecNotifications), and (2) extends Protocol with a second generic parameter SpecT extends ProtocolSpec = McpSpec plus a new spec-typed overload at the front of the setRequestHandler / setNotificationHandler overload sets. ContextT also gains a = BaseContext default. A new vitest file covers the type inference, and a changeset is included.

Security risks

None identified — purely type-level additions and an export-barrel change; no new runtime code paths, I/O, auth, or parsing logic.

Level of scrutiny

High, on API-surface grounds rather than correctness. REVIEW.md is explicit that the burden of proof is on addition and that every new public export must be intentional; CLAUDE.md §Public API Exports until now used Protocol as the canonical internal-only example. Promoting it (and committing to ProtocolSpec as a public extension contract) is a deliberate policy reversal that a maintainer should explicitly approve — even if the motivation (v1 deep-import parity, ext-apps subclassing) is sound.

Other factors

  • I verified Client and Server both re-declare their own setRequestHandler overload sets, so the new SpecT-typed overload on the base class is shadowed there and shouldn't perturb existing user-facing inference; the change is effectively scoped to direct Protocol subclasses.
  • The defaults (ContextT = BaseContext, SpecT = McpSpec) keep existing extends Protocol<X> references compiling, and SpecRequests<ProtocolSpec> = never correctly disables the typed overload for the unconstrained default — both are exercised in the new test.
  • One nit (posted inline): the file-header JSDoc in exports/public/index.ts and CLAUDE.md still cite Protocol as internal-only, contradicting the new export.
  • Stacked on #1916#1891; reviewer should confirm this lands in the intended order.

SpecRequests
} from '../../shared/protocol.js';
export { DEFAULT_REQUEST_TIMEOUT_MSEC } from '../../shared/protocol.js';
export { DEFAULT_REQUEST_TIMEOUT_MSEC, Protocol } from '../../shared/protocol.js';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Nit: now that Protocol is exported here, two pieces of contributor-facing prose still call it out as internal-only — the file-header JSDoc in this file (lines 7-9: "Internal utilities (Protocol class, stdio parsing, …)") and CLAUDE.md §Public API Exports (lines 69-70, which uses "Protocol class" as the canonical example of an internal-barrel-only symbol). The inline section comment on line 41 was updated, but these two were missed; drop "Protocol class" from both example lists.

Extended reasoning...

What's stale and where. This PR promotes the abstract Protocol class to the public surface (packages/core/src/exports/public/index.ts:55) and correctly updates the inline section comment on line 41 from "NOT the Protocol class itself or mergeCapabilities" to "Protocol class (abstract — subclass for custom vocabularies) + types. NOT mergeCapabilities." However, two other contributor-facing docs still use Protocol as the example of an internal-only symbol:

  1. packages/core/src/exports/public/index.ts:7-9 — the module-level JSDoc reads: "Internal utilities (Protocol class, stdio parsing, schema helpers, etc.) remain available via the internal barrel". This file now exports Protocol 48 lines below that comment.
  2. CLAUDE.md:69-70 — line 69 lists "Protocol class" as an example of what the internal barrel exports, and line 70 describes core/public as "Exports only TypeScript types, error classes, constants, and guards", with the contrast implying Protocol lives only in the internal barrel.

Why it matters. REVIEW.md's "Documentation & Changesets" item asks us to flag prose that now contradicts the implementation, and its API-surface checklist explicitly references "CLAUDE.md § Public API Exports" — so this section is load-bearing for future reviewers deciding what's allowed in core/public. A contributor reading either doc today would conclude that exporting Protocol from core/public is a policy violation, when in fact this PR is the deliberate policy change.

Step-by-step proof.

  1. Open packages/core/src/exports/public/index.ts at HEAD: line 55 reads export { DEFAULT_REQUEST_TIMEOUT_MSEC, Protocol } from '../../shared/protocol.js';.
  2. Scroll to lines 7-9 of the same file: the JSDoc still parenthetically lists "Protocol class" as an internal utility not exported here. Direct contradiction within one file.
  3. Open CLAUDE.md:69: "Internal barrel. Exports everything (including Zod schemas, Protocol class, stdio utils)." Line 70: "Curated public API. Exports only TypeScript types, error classes, constants, and guards." The juxtaposition uses Protocol as the example of something the internal barrel has and core/public does not.
  4. The PR diff updates line 41's section comment to acknowledge the new export but touches neither the file header nor CLAUDE.md.

Scope note. CLAUDE.md:70's "only types/errors/constants/guards" wording was already loose before this PR (UriTemplate, ReadBuffer, AjvJsonSchemaValidator, InMemoryTaskStore are already exported), so that clause is partly pre-existing drift. But the explicit "Protocol class" call-out in line 69 and in the file header are new contradictions introduced specifically by this PR's export change.

Fix. Trivial doc edits: drop "Protocol class, " from the parenthetical at public/index.ts:7, and update CLAUDE.md:69-70 to stop using Protocol as the internal-only example (and optionally relax line 70's "only types/errors/constants/guards" phrasing while you're there).

@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-3arg-custom-methods branch from daab2e2 to 324ed12 Compare April 16, 2026 19:38
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-export-protocol branch from ae50e77 to b25d3a5 Compare April 16, 2026 19:41
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-3arg-custom-methods branch from 324ed12 to 3961553 Compare April 16, 2026 20:04
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-export-protocol branch from b25d3a5 to 5fce199 Compare April 16, 2026 20:06
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-3arg-custom-methods branch 2 times, most recently from e75e0b0 to 9da988e Compare April 16, 2026 20:20
@felixweinberger felixweinberger force-pushed the fweinberger/v2-bc-export-protocol branch from 5fce199 to 9da988e Compare April 16, 2026 20:21
@felixweinberger
Copy link
Copy Markdown
Contributor Author

@claude review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2-bc v2 backwards-compatibility series

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant