Skip to content

feat: add v2025-11-25 protocol adapter - #7234

Merged
IMax153 merged 4 commits into
Effect-TS:mainfrom
lloydrichards:feat/v2025-11-25-adapter
Aug 14, 2026
Merged

IMax153 merged 4 commits into
Effect-TS:mainfrom
lloydrichards:feat/v2025-11-25-adapter

Conversation

@lloydrichards

@lloydrichards lloydrichards commented Aug 13, 2026 •

Copy link
Copy Markdown
Contributor

Type

  • Feature

Description

Hot on the heels of adding the McpCore, I was ready with the next important step: v2025-11-25. The issue goes over the main criteria but the big changes in this protocol version are:

  • Icons for tools, resources, resource templates, and prompts (SEP-973)
  • Input validation errors as tool execution errors (SEP-1303)
  • Tool calling in sampling via tools and toolChoice (SEP-1577)
  • Elicitation enhancements -> default values (SEP-1034), titled/multi-select enums (SEP-1330), URL mode (SEP-1036)

What's been left out is the experimental Tasks (SEP-1686) as there is a separate issue to address this with the task extension. Clients should still be able negotiate this is a v2025-11-28 server just without the Task capabilities.

What more interesting now if that we're starting to update the McpSchema (what user-land will be using) with some new functionality like Icons and Tools with Sampling which are negotiated as part of the ProtocolAdaptors they wish to support. So they can add it as part of their McpServer, but the client will not need to know about it unless the protocol versions allows.

A toy example of some of these changes:
// New in 2025-11-25: server, resource, prompt, and tool descriptors can expose icons. Clients may choose the best entry for their size and color theme.
const ServerIcon = McpSchema.Icon.make({
  src: "https://example.com/icons/weather-server.svg",
  mimeType: "image/svg+xml",
  sizes: ["48x48", "any"],
  theme: "dark"
})

const AskWeatherModel = Tool.make("ask_weather_model", {
  description: "Ask the connected client's model to plan a weather lookup",
  parameters: Schema.Struct({
    city: Schema.String
  }),
  // CreateMessageResult is both the decoded response type and its Schema, so the tool preserves the structured sampling result without stringifying it.
  success: McpSchema.CreateMessageResult,
  failure: Schema.Never,
  dependencies: [McpSchema.McpServerClient]
})

const WeatherToolkit = Toolkit.make(AskWeatherModel)

const WeatherHandlers = WeatherToolkit.toLayer(WeatherToolkit.of({
  ask_weather_model: Effect.fnUntraced(
    function*({ city }) {
      // Every handler can inspect the protocol and capabilities negotiated with the client, then obtain the version-neutral reverse-operation facade.
      const currentClient = yield* McpSchema.McpServerClient
      const client = yield* currentClient.getClient

      const result = yield* client.createMessage(
        McpSchema.CreateMessage.payloadSchema.make({
          messages: [
            McpSchema.SamplingMessage.make({
              role: "user",
              content: McpSchema.TextContent.make({
                text: `Plan how to find the current weather in ${city}.`
              })
            })
          ],
          // New in 2025-11-25: sampling requests can declare tools and control whether the client's model may or must call one.
          tools: [{
            name: "get_weather",
            description: "Get current weather for a city",
            inputSchema: {
              type: "object",
              properties: {
                city: { type: "string" }
              },
              required: ["city"]
            }
          }],
          toolChoice: new McpSchema.ToolChoice({ mode: "required" }),
          maxTokens: 200
        })
      )

      // A November client may return ToolUseContent. A real server would run the requested tool and continue with matching ToolResultContent.
      return result
    },
    (effect) =>
      effect.pipe(
        Effect.scoped,
        Effect.orDie
      )
  )
}))

const McpLive = McpServer.toolkit(WeatherToolkit).pipe(
  Layer.provide(WeatherHandlers),
  Layer.provide(McpServer.layerStdio({
    name: "weather-demo",
    version: "1.0.0",
    description: "Demonstrates MCP 2025-11-25 metadata and sampling tools",
    // New in 2025-11-25: richer implementation metadata is projected during initialization and omitted automatically for older negotiated versions.
    websiteUrl: "https://example.com/weather-demo",
    icons: [ServerIcon],
    protocols: [
      // June remains a fallback for clients that do not yet offer the newer revision.
      McpProtocol.v2025_06_18,
      // New dated adapter with tool sampling, URL elicitation, icons
      McpProtocol.v2025_11_25
    ]
  })),
  Layer.provide(NodeServices.layer)
)

// The November adapter rejects the tool-enabled sampling request before it is sent when the connected client has not advertised `sampling.tools`.
NodeRuntime.runMain(Layer.launch(McpLive))

How to Review

If you're familiar with the other ProtocolAdaptor version then you will notice a lot of similarities and duplication. This is mostly intentional for now as we went to make sure each version owns as much of tis own schema and behaviours.

Probably the best places to review would be:

effect/unstable/ai/
├── internal
│   ├── mcpProtocol
│   │   ├── .
│   │   ╰── v2025_11_25.ts <- wire implementation for protocol
│   ├── mcpSchema
│   │   ├── .
│   │   ╰── v2025_11_25.ts <- new McpSchema for protocol
│   ╰── .
├── McpSchema.ts <- added Icons
├── McpServer.ts <- changes sampling and clicitation
.

The tests are interesting but mostly I think to see how the conformance suite is being used to cover the most recent version and skip tests that don't apply to older verions:

 ✓  effect  test/unstable/ai/McpServer/v2024_11_05.test.ts (161 tests | 19 skipped) 251ms
 ✓  effect  test/unstable/ai/McpServer/v2025_03_26.test.ts (166 tests | 16 skipped) 261ms
 ✓  effect  test/unstable/ai/McpServer/v2025_06_18.test.ts (171 tests | 9 skipped) 279ms
 ✓  effect  test/unstable/ai/McpServer/v2025_11_25.test.ts (166 tests) 279ms

Any questions or comments, feel free to ask here or reach out on discord!

Related

@changeset-bot

changeset-bot Bot commented Aug 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5fe4ad1

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

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

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

@github-actions

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.91 KB 6.91 KB 0.00 KB (0.00%)
batching.ts 9.72 KB 9.72 KB 0.00 KB (0.00%)
brand.ts 6.50 KB 6.50 KB 0.00 KB (0.00%)
cache.ts 10.63 KB 10.63 KB 0.00 KB (0.00%)
config.ts 21.05 KB 21.05 KB 0.00 KB (0.00%)
differ.ts 19.99 KB 19.99 KB 0.00 KB (0.00%)
http-client.ts 21.56 KB 21.56 KB 0.00 KB (0.00%)
logger.ts 10.88 KB 10.88 KB 0.00 KB (0.00%)
metric.ts 8.86 KB 8.86 KB 0.00 KB (0.00%)
optic.ts 6.67 KB 6.67 KB 0.00 KB (0.00%)
pubsub.ts 14.90 KB 14.90 KB 0.00 KB (0.00%)
queue.ts 11.57 KB 11.57 KB 0.00 KB (0.00%)
schedule.ts 10.74 KB 10.74 KB 0.00 KB (0.00%)
schema-class.ts 19.61 KB 19.61 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 29.57 KB 29.57 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 25.81 KB 25.81 KB 0.00 KB (0.00%)
schema-string-transformation.ts 13.49 KB 13.49 KB 0.00 KB (0.00%)
schema-string.ts 10.99 KB 10.99 KB 0.00 KB (0.00%)
schema-template-literal.ts 15.30 KB 15.30 KB 0.00 KB (0.00%)
schema-toArbitrary.ts 21.73 KB 21.73 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.20 KB 24.20 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 18.96 KB 18.96 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 18.78 KB 18.78 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.65 KB 18.65 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.81 KB 22.81 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.30 KB 19.30 KB 0.00 KB (0.00%)
schema.ts 18.87 KB 18.87 KB 0.00 KB (0.00%)
stm.ts 12.66 KB 12.66 KB 0.00 KB (0.00%)
stream.ts 9.66 KB 9.66 KB 0.00 KB (0.00%)

@lloydrichards
lloydrichards marked this pull request as ready for review August 13, 2026 18:15

@IMax153 IMax153 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM! Thanks @lloydrichards

@IMax153
IMax153 merged commit 6eebd0a into Effect-TS:main Aug 14, 2026
13 of 15 checks passed
@lloydrichards
lloydrichards deleted the feat/v2025-11-25-adapter branch August 14, 2026 12:51

This branch had an error being deployed

1 failed deployment
fork — 5fe4ad19 Deployed Aug 13, 2026 by lloydrichards via approval-gate #11853
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.

Support MCP protocol version 2025-11-25 (v4)

2 participants