Skip to content

feat: add mcp core for protocol versions - #6829

Open
lloydrichards wants to merge 5 commits into
Effect-TS:mainfrom
lloydrichards:feat/mcp-core
Open

feat: add mcp core for protocol versions#6829
lloydrichards wants to merge 5 commits into
Effect-TS:mainfrom
lloydrichards:feat/mcp-core

Conversation

@lloydrichards

@lloydrichards lloydrichards commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Type

  • Feature

Description

As part of being able to define new protocol adapters, I've added first-class support for multiple MCP protocol revisions based on the existing McpSchema (<= v2025-06-18):

  • v2024-11-05
  • v2025-03-26
  • v2025-06-18

So now an MCP server now declares an ordered list of protocol adapters:

McpServer.layerStdio({
  name: "example",
  version: "1.0.0",
  protocols: [
    McpProtocol.v2025_06_18,
    McpProtocol.v2025_03_26,
    McpProtocol.v2024_11_05
  ]
})

The client’s requested protocol is selected before its payload is decoded. Once initialization succeeds, the selected adapter remains associated with that session.

The main separation is between:

  • McpCore, which owns version-neutral tools, resources, prompts, completions, notifications, and semantic errors
  • dated protocol adapters, which own their wire schemas, RPC vocabulary, projections, reverse requests, and transport rules
  • McpServer, which owns transport lifecycle, sessions, dispatch, cancellation, and notification delivery

Each dated adapter is declared through McpProtocol.make:

export const protocol = McpProtocol.make({
  // Identifies the wire revision selected during initialization.
  protocolVersion: "2025-06-18",

  // Transport behavior can vary between MCP revisions.
  transport: {
    acceptsJsonRpcBatches: false,
    requiresVersionHeader: true
  },

  // The dated client → server and server → client RPC vocabularies.
  clientRpcs: ClientRpcs,
  clientNotificationRpcs: ClientNotificationRpcs,
  serverRequestRpcs: ServerRequestRpcs,
  serverNotificationRpcs: ServerNotificationRpcs,

  // Typed wire handlers translate dated requests into canonical core calls,
  // then project canonical results back into this revision's response types.
  handlerRpcs: AdapterRpcs,
  makeHandlers: (core, lifecycle) =>
    AdapterRpcs.of({
      "tools/list": () =>
        McpServerClient.use((client) =>
          core.tools.list(profileFromClient(client))
        ).pipe(
          Effect.map((tools) => ListToolsResult.make({
            tools: tools.map(projectTool)
          }))
        )
    }),

  // Reverse requests expose one stable server-facing client API even though
  // the wire request and response schemas vary by revision.
  toReverseClient: (profile, client) => ({
    listRoots: /* canonical request ↔ dated request */,
    createMessage: /* canonical request ↔ dated request */,
    elicit: /* supported here; rejected by older adapters */
  }),x

  // Server notifications are projected independently for each revision.
  projectNotification,

  // Cancellation remains an adapter concern because its payload must be
  // understood before the transport interrupts an active request.
  normalizeCancellation
})

McpProtocol.make derives the repetitive mechanics from this declaration:

  • payload codecs from the dated RPC schemas
  • reverse RPC client construction
  • handler installation and service requirement inference

This keeps version-specific decisions at the adapter boundary instead of introducing revision checks throughout McpServer.

How to Review

There is a lot of code here, but also a lot of duplicated patterns since each dated protocol reimplements its own schema and adaptor. This should make each protocol more resistent to changes as new versions are added, but still adds a lot to look over:

Area Added Deleted
Dated wire schemas 1,129 0
Core and dated adapters 1,985 48
Server integration and selection 854 699
Tests and harnesses 1,758 548

I would suggest starting with the commits as they break up the different parts, but can mostly focus on how the v2025-06-18 works since this is semantically the same as how the McpSchema currently works. The other two are mostly about reducing the capabilities or changing how some behaviour works, but nothing too crazy is added and all the conformance suites have been updated to exclude spec tests that didn't apply to older versions.

Related

@changeset-bot

changeset-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ddf5e7a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Major
@effect/opentelemetry Major
@effect/platform-browser Major
@effect/platform-bun Major
@effect/platform-deno Major
@effect/platform-node-shared Major
@effect/platform-node Major
@effect/vitest Major
@effect/ai-anthropic Major
@effect/ai-openai-compat Major
@effect/ai-openai Major
@effect/ai-openrouter Major
@effect/atom-react Major
@effect/atom-solid Major
@effect/atom-vue Major
@effect/sql-clickhouse Major
@effect/sql-d1 Major
@effect/sql-libsql Major
@effect/sql-mssql Major
@effect/sql-mysql2 Major
@effect/sql-pg Major
@effect/sql-pglite Major
@effect/sql-sqlite-bun Major
@effect/sql-sqlite-do Major
@effect/sql-sqlite-node Major
@effect/sql-sqlite-react-native Major
@effect/sql-sqlite-wasm Major
@effect/doctest Major
@effect/openapi-generator Major
@effect/docgen Major

Not sure what this means? Click here to learn what changesets are.

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

@effect-slopcop effect-slopcop Bot added 4.0 enhancement New feature or request labels Aug 1, 2026
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 6.63 KB 6.63 KB 0.00 KB (0.00%)
batching.ts 9.42 KB 9.42 KB 0.00 KB (0.00%)
brand.ts 6.31 KB 6.31 KB 0.00 KB (0.00%)
cache.ts 10.16 KB 10.16 KB 0.00 KB (0.00%)
config.ts 20.32 KB 20.32 KB 0.00 KB (0.00%)
differ.ts 19.93 KB 19.93 KB 0.00 KB (0.00%)
http-client.ts 21.02 KB 21.02 KB 0.00 KB (0.00%)
logger.ts 10.32 KB 10.32 KB 0.00 KB (0.00%)
metric.ts 8.55 KB 8.55 KB 0.00 KB (0.00%)
optic.ts 7.33 KB 7.33 KB 0.00 KB (0.00%)
pubsub.ts 14.46 KB 14.46 KB 0.00 KB (0.00%)
queue.ts 11.13 KB 11.13 KB 0.00 KB (0.00%)
schedule.ts 10.31 KB 10.31 KB 0.00 KB (0.00%)
schema-class.ts 18.86 KB 18.86 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.67 KB 28.67 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 24.99 KB 24.99 KB 0.00 KB (0.00%)
schema-string-transformation.ts 12.99 KB 12.99 KB 0.00 KB (0.00%)
schema-string.ts 10.65 KB 10.65 KB 0.00 KB (0.00%)
schema-template-literal.ts 14.85 KB 14.85 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 21.66 KB 21.66 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.08 KB 24.08 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 18.91 KB 18.91 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 18.73 KB 18.73 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.59 KB 18.59 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.33 KB 22.33 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.25 KB 19.25 KB 0.00 KB (0.00%)
schema.ts 18.12 KB 18.12 KB 0.00 KB (0.00%)
stm.ts 12.11 KB 12.11 KB 0.00 KB (0.00%)
stream.ts 9.37 KB 9.37 KB 0.00 KB (0.00%)

@lloydrichards
lloydrichards marked this pull request as ready for review August 1, 2026 07:33

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ Minor suggestions inline — two rough edges worth a look, plus a note on a behavioral change.

Reviewed changes — adds multi-version MCP protocol support through three dated adapters (2024-11-05, 2025-03-26, 2025-06-18) layered over a version-neutral McpCore. Each adapter owns its dated wire schemas, RPC vocabulary, handler projections, content-type encoding, reverse client construction, and transport rules. McpServer delegates all registration and dispatch to the core, while protocol selection and session management stays in the transport layer.

  • Three dated wire schemas (internal/mcpSchema/v20*_*_*.ts) delta-inheriting from predecessor revisions
  • Three dated protocol adapters (internal/mcpProtocol/v20*_*_*.ts) declaring through McpProtocol.make — handlers, reverse clients, notification projection, and cancellation normalization for each revision
  • Version-neutral McpCore (internal/mcpCore.ts) with registration state, dispatch, and semantic error types
  • Server integration (McpServer.ts, mcpProtocolRegistry.ts) refactored to install adapter handlers via HandlerInstallationTarget and select protocol at init
  • RpcSerialization fix excluding notifications from JSON-RPC batch size counting
  • Lifecycle.test.ts deleted (fully replaced by McpConformance/LifecycleTest.ts, now composed into all three version entrypoints)
  • ProtocolAdapters.test.ts — 1190-line cross-version integration suite exercising projection, encoding, visibility, session isolation, and error paths across all three revisions

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

Comment thread packages/effect/src/unstable/ai/McpServer.ts Outdated
Comment thread packages/effect/src/unstable/ai/McpServer.ts Outdated
Comment thread packages/effect/src/unstable/ai/McpServer.ts Outdated
Comment thread packages/effect/src/unstable/ai/McpServer.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes — four fixes from the prior review feedback in commit ddf5e7a:

  • addResource/addResourceTemplate/addPrompt dedup — each now uses findIndex-by-key replacement matching the addTool pattern, so re-registering the same URI, template, or prompt name replaces the previous entry instead of pushing a duplicate into the public array getters.
  • findResource error behavior restored — unknown URIs now fail with InvalidParams (error code -32602, message Resource '...' not found) instead of silently returning { contents: [] }.
  • Test updated — the findResource unknown-URI test asserts the InvalidParams error; also added new direct service test suite covering resource handler failure preservation and omitted-argument passthrough for tool handlers.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

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

Labels

4.0 enhancement New feature or request

Projects

Status: Discussion Ongoing

Development

Successfully merging this pull request may close these issues.

Canonical McpSchema with ProtocolAdapter

1 participant