Skip to content

Commit 71f7307

Browse files
Merge main into v2-2026-07-28 (#2300)
Co-authored-by: Matt <77928207+mattzcarey@users.noreply.github.com>
1 parent 16b6bbf commit 71f7307

19 files changed

Lines changed: 860 additions & 92 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@modelcontextprotocol/server': patch
3+
'@modelcontextprotocol/client': patch
4+
---
5+
6+
Non-SEP draft spec conformance fixes
7+
8+
- `McpServer` now eagerly installs list/read/call handlers for every primitive capability (`tools`, `resources`, `prompts`) declared in `ServerOptions.capabilities`. Per the draft spec, a server that declares a capability MUST respond to its list method (potentially with an empty result) instead of returning "Method not found". Previously, handlers were only installed lazily on first registration, so a server constructed with e.g. `capabilities: { tools: {} }` and zero registered tools answered `tools/list` with `-32601`. Low-level `Server` users remain responsible for registering handlers for declared capabilities (documented on `ServerOptions.capabilities`).
9+
- Fixed pagination doc examples on `Client.listTools`/`listPrompts`/`listResources` to loop `while (cursor !== undefined)` instead of `while (cursor)` — per the draft spec, clients MUST NOT treat an empty-string cursor as the end of results.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@modelcontextprotocol/core': patch
3+
'@modelcontextprotocol/server': patch
4+
'@modelcontextprotocol/client': patch
5+
---
6+
7+
Mark the roots, sampling, and logging runtime APIs as `@deprecated` per SEP-2577 (deprecated as of protocol version 2026-07-28; functional for at least twelve months). Annotates `Server.createMessage`/`listRoots`/`sendLoggingMessage`, `McpServer.sendLoggingMessage`, `Client.setLoggingLevel`/`sendRootsListChanged`, the `ServerContext.mcpReq.log`/`requestSampling` helpers, and the `roots`/`sampling`/`logging` capability schema fields. JSDoc/docs only — no behavior change.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/core': patch
3+
---
4+
5+
Add reserved trace context `_meta` key constants (`TRACEPARENT_META_KEY`, `TRACESTATE_META_KEY`, `BAGGAGE_META_KEY`) per SEP-414, plus docs and a passthrough regression test. The spec reserves the unprefixed `_meta` keys `traceparent`, `tracestate`, and `baggage` (W3C Trace Context / W3C Baggage formats) for distributed tracing; the SDK passes them through untouched.

docs/client.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ import {
2626
SdkError,
2727
SdkErrorCode,
2828
SSEClientTransport,
29-
StreamableHTTPClientTransport
29+
StreamableHTTPClientTransport,
30+
TRACEPARENT_META_KEY,
31+
TRACESTATE_META_KEY
3032
} from '@modelcontextprotocol/client';
3133
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
3234
```
@@ -404,6 +406,9 @@ client.setNotificationHandler('notifications/resources/list_changed', async () =
404406
});
405407
```
406408

409+
> [!WARNING]
410+
> MCP logging (including `setLoggingLevel()` and `notifications/message`) is deprecated as of protocol version 2026-07-28 (SEP-2577). It remains fully functional during the deprecation window (at least twelve months); see the [deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated). Servers should migrate to stderr logging (STDIO) or OpenTelemetry.
411+
407412
To control the minimum severity of log messages the server sends, use {@linkcode @modelcontextprotocol/client!client/client.Client#setLoggingLevel | setLoggingLevel()}:
408413

409414
```ts source="../examples/client/src/clientGuide.examples.ts#setLoggingLevel_basic"
@@ -431,6 +436,9 @@ const client = new Client(
431436

432437
### Sampling
433438

439+
> [!WARNING]
440+
> Sampling is deprecated as of protocol version 2026-07-28 (SEP-2577). It remains fully functional during the deprecation window (at least twelve months); see the [deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated). Servers should migrate to calling LLM provider APIs directly.
441+
434442
When a server needs an LLM completion during tool execution, it sends a `sampling/createMessage` request to the client (see [Sampling](https://modelcontextprotocol.io/docs/learn/client-concepts#sampling) in the MCP overview). Register a handler to fulfill it:
435443

436444
```ts source="../examples/client/src/clientGuide.examples.ts#sampling_handler"
@@ -472,6 +480,9 @@ For a full form-based elicitation handler with AJV validation, see [`simpleStrea
472480

473481
### Roots
474482

483+
> [!WARNING]
484+
> Roots are deprecated as of protocol version 2026-07-28 (SEP-2577). They remain fully functional during the deprecation window (at least twelve months); see the [deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated). Migrate to passing paths via tool parameters, resource URIs, or configuration.
485+
475486
Roots let the client expose filesystem boundaries to the server (see [Roots](https://modelcontextprotocol.io/docs/learn/client-concepts#roots) in the MCP overview). Declare the `roots` capability and register a `roots/list` handler:
476487

477488
```ts source="../examples/client/src/clientGuide.examples.ts#roots_handler"
@@ -571,6 +582,58 @@ const transport = new StreamableHTTPClientTransport(new URL('http://localhost:30
571582
});
572583
```
573584

585+
## Trace context propagation
586+
587+
The MCP specification ([SEP-414](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414)) reserves the unprefixed `_meta` keys `traceparent`, `tracestate`, and `baggage` for distributed trace context, as an exception to the usual `_meta` key prefix rule. When present, the values must follow the [W3C Trace Context](https://www.w3.org/TR/trace-context/) and [W3C Baggage](https://www.w3.org/TR/baggage/) formats. The SDK does not interpret these keys — `_meta` passes through both directions untouched — so you can propagate OpenTelemetry context across any transport, including stdio where HTTP headers are unavailable. The key names are exported as `TRACEPARENT_META_KEY`, `TRACESTATE_META_KEY`, and `BAGGAGE_META_KEY`.
588+
589+
Attach trace context to a single request via `_meta`:
590+
591+
```ts source="../examples/client/src/clientGuide.examples.ts#traceContext_perRequest"
592+
// Values would normally come from your tracer's active span context.
593+
const result = await client.callTool({
594+
name: 'calculate-bmi',
595+
arguments: { weightKg: 70, heightM: 1.75 },
596+
_meta: {
597+
[TRACEPARENT_META_KEY]: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
598+
[TRACESTATE_META_KEY]: 'vendor1=opaqueValue1'
599+
}
600+
});
601+
console.log(result.content);
602+
```
603+
604+
Or inject it into every outgoing request with fetch middleware (Streamable HTTP transport):
605+
606+
```ts source="../examples/client/src/clientGuide.examples.ts#traceContext_middleware"
607+
const traceContextMiddleware = createMiddleware(async (next, input, init) => {
608+
if (typeof init?.body !== 'string') {
609+
return next(input, init);
610+
}
611+
const message = JSON.parse(init.body) as {
612+
method?: string;
613+
params?: { _meta?: Record<string, unknown>; [key: string]: unknown };
614+
};
615+
// Only requests and notifications carry params._meta; skip responses.
616+
if (message.method === undefined) {
617+
return next(input, init);
618+
}
619+
message.params = {
620+
...message.params,
621+
_meta: {
622+
...message.params?._meta,
623+
// Replace with values from your tracer's active span context.
624+
[TRACEPARENT_META_KEY]: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
625+
}
626+
};
627+
return next(input, { ...init, body: JSON.stringify(message) });
628+
});
629+
630+
const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp'), {
631+
fetch: applyMiddlewares(traceContextMiddleware)(fetch)
632+
});
633+
```
634+
635+
On the server side, handlers can read the incoming trace context from `ctx.mcpReq._meta` — see the [server guide](./server.md#trace-context-propagation).
636+
574637
## Resumption tokens
575638

576639
When using SSE-based streaming, the server can assign event IDs. Pass `onresumptiontoken` to track them, and `resumptionToken` to resume from where you left off after a disconnection:

docs/server.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { randomUUID } from 'node:crypto';
2222
import { createMcpExpressApp } from '@modelcontextprotocol/express';
2323
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
2424
import type { CallToolResult, ResourceLink } from '@modelcontextprotocol/server';
25-
import { completable, McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
25+
import { completable, McpServer, ResourceTemplate, TRACEPARENT_META_KEY } from '@modelcontextprotocol/server';
2626
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
2727
import * as z from 'zod/v4';
2828
```
@@ -252,6 +252,9 @@ server.registerResource(
252252
);
253253
```
254254

255+
> [!IMPORTANT]
256+
> **Security note:** If a resource is backed by the filesystem (for example, a `file://` server or a template whose variables map onto file paths), the spec requires sanitizing any user-influenced path before use. Resolve the requested path and verify it stays within the intended root directory, rejecting traversal sequences such as `..` (including encoded forms) and symlinks that escape the root. Never pass template variables or client-supplied URIs to filesystem APIs unchecked.
257+
255258
## Prompts
256259

257260
Prompts are reusable templates that help structure interactions with models (see [Prompts](https://modelcontextprotocol.io/docs/learn/server-concepts#prompts) in the MCP overview). Use a prompt when you want to offer a canned interaction pattern that users invoke explicitly; use a [tool](#tools) when the LLM should decide when to call it.
@@ -312,6 +315,9 @@ server.registerPrompt(
312315

313316
## Logging
314317

318+
> [!WARNING]
319+
> MCP logging is deprecated as of protocol version 2026-07-28 (SEP-2577). It remains fully functional during the deprecation window (at least twelve months); see the [deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated). Migrate to stderr logging (STDIO servers) or OpenTelemetry.
320+
315321
Logging lets your server send structured diagnostics — debug traces, progress updates, warnings — to the connected client as notifications (see [Logging](https://modelcontextprotocol.io/specification/latest/server/utilities/logging) in the MCP specification).
316322

317323
Declare the `logging` capability, then call `ctx.mcpReq.log(level, data)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside any handler:
@@ -378,12 +384,43 @@ server.registerTool(
378384

379385
`progress` must increase on each call. `total` and `message` are optional. If the client does not provide a `progressToken`, skip the notification.
380386

387+
## Trace context propagation
388+
389+
The MCP specification ([SEP-414](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414)) reserves the unprefixed `_meta` keys `traceparent`, `tracestate`, and `baggage` for distributed trace context, as an exception to the usual `_meta` key prefix rule. When present, the values must follow the [W3C Trace Context](https://www.w3.org/TR/trace-context/) and [W3C Baggage](https://www.w3.org/TR/baggage/) formats. The SDK does not interpret these keys — `_meta` passes through untouched on any transport, including stdio. The key names are exported as `TRACEPARENT_META_KEY`, `TRACESTATE_META_KEY`, and `BAGGAGE_META_KEY`.
390+
391+
Read the caller's trace context from `ctx.mcpReq._meta` in a handler:
392+
393+
```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_traceContext"
394+
server.registerTool(
395+
'traced-operation',
396+
{
397+
description: 'Operation that participates in distributed tracing',
398+
inputSchema: z.object({ query: z.string() })
399+
},
400+
async ({ query }, ctx): Promise<CallToolResult> => {
401+
// e.g. '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
402+
const traceparent = ctx.mcpReq._meta?.[TRACEPARENT_META_KEY];
403+
if (typeof traceparent === 'string') {
404+
// Continue the caller's trace, e.g. start a child span with your
405+
// OpenTelemetry tracer using this trace context.
406+
}
407+
408+
return { content: [{ type: 'text', text: `Results for ${query}` }] };
409+
}
410+
);
411+
```
412+
413+
To propagate context onward (for example on a server-initiated sampling request, or back on a response), set the same keys in the outgoing `_meta`. See the [client guide](./client.md#trace-context-propagation) for injecting trace context on the client side.
414+
381415
## Server-initiated requests
382416

383417
MCP is bidirectional — servers can send requests *to* the client during tool execution, as long as the client declares matching capabilities (see [Architecture](https://modelcontextprotocol.io/docs/learn/architecture) in the MCP overview).
384418

385419
### Sampling
386420

421+
> [!WARNING]
422+
> Sampling is deprecated as of protocol version 2026-07-28 (SEP-2577). It remains fully functional during the deprecation window (at least twelve months); see the [deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated). Migrate to calling LLM provider APIs directly from your server.
423+
387424
Sampling lets a tool handler request an LLM completion from the connected client — the handler describes a prompt and the client returns the model's response (see [Sampling](https://modelcontextprotocol.io/docs/learn/client-concepts#sampling) in the MCP overview). Use sampling when a tool needs the model to generate or transform text mid-execution.
388425

389426
Call `ctx.mcpReq.requestSampling(params)` (from {@linkcode @modelcontextprotocol/server!index.ServerContext | ServerContext}) inside a tool handler:
@@ -478,6 +515,9 @@ For runnable examples, see [`elicitationFormExample.ts`](https://github.com/mode
478515

479516
### Roots
480517

518+
> [!WARNING]
519+
> Roots are deprecated as of protocol version 2026-07-28 (SEP-2577). They remain fully functional during the deprecation window (at least twelve months); see the [deprecated features registry](https://modelcontextprotocol.io/specification/draft/deprecated). Migrate to passing paths via tool parameters, resource URIs, or configuration.
520+
481521
Roots let a tool handler discover the client's workspace directories — for example, to scope a file search or identify project boundaries (see [Roots](https://modelcontextprotocol.io/docs/learn/client-concepts#roots) in the MCP overview). Call {@linkcode @modelcontextprotocol/server!server/server.Server#listRoots | server.server.listRoots()} (requires the client to declare the `roots` capability):
482522

483523
```ts source="../examples/server/src/serverGuide.examples.ts#registerTool_roots"

examples/client/src/clientGuide.examples.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import {
2121
SdkError,
2222
SdkErrorCode,
2323
SSEClientTransport,
24-
StreamableHTTPClientTransport
24+
StreamableHTTPClientTransport,
25+
TRACEPARENT_META_KEY,
26+
TRACESTATE_META_KEY
2527
} from '@modelcontextprotocol/client';
2628
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio';
2729
//#endregion imports
@@ -522,6 +524,55 @@ async function middleware_basic() {
522524
return transport;
523525
}
524526

527+
/** Example: Attach W3C Trace Context to a single request via `_meta`. */
528+
async function traceContext_perRequest(client: Client) {
529+
//#region traceContext_perRequest
530+
// Values would normally come from your tracer's active span context.
531+
const result = await client.callTool({
532+
name: 'calculate-bmi',
533+
arguments: { weightKg: 70, heightM: 1.75 },
534+
_meta: {
535+
[TRACEPARENT_META_KEY]: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
536+
[TRACESTATE_META_KEY]: 'vendor1=opaqueValue1'
537+
}
538+
});
539+
console.log(result.content);
540+
//#endregion traceContext_perRequest
541+
}
542+
543+
/** Example: Client middleware that injects trace context into every outgoing request. */
544+
async function traceContext_middleware() {
545+
//#region traceContext_middleware
546+
const traceContextMiddleware = createMiddleware(async (next, input, init) => {
547+
if (typeof init?.body !== 'string') {
548+
return next(input, init);
549+
}
550+
const message = JSON.parse(init.body) as {
551+
method?: string;
552+
params?: { _meta?: Record<string, unknown>; [key: string]: unknown };
553+
};
554+
// Only requests and notifications carry params._meta; skip responses.
555+
if (message.method === undefined) {
556+
return next(input, init);
557+
}
558+
message.params = {
559+
...message.params,
560+
_meta: {
561+
...message.params?._meta,
562+
// Replace with values from your tracer's active span context.
563+
[TRACEPARENT_META_KEY]: '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
564+
}
565+
};
566+
return next(input, { ...init, body: JSON.stringify(message) });
567+
});
568+
569+
const transport = new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp'), {
570+
fetch: applyMiddlewares(traceContextMiddleware)(fetch)
571+
});
572+
//#endregion traceContext_middleware
573+
return transport;
574+
}
575+
525576
/** Example: Track resumption tokens for SSE reconnection. */
526577
async function resumptionToken_basic(client: Client) {
527578
//#region resumptionToken_basic
@@ -572,4 +623,6 @@ void errorHandling_toolErrors;
572623
void errorHandling_lifecycle;
573624
void errorHandling_timeout;
574625
void middleware_basic;
626+
void traceContext_perRequest;
627+
void traceContext_middleware;
575628
void resumptionToken_basic;

examples/server/src/serverGuide.examples.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { randomUUID } from 'node:crypto';
1313
import { createMcpExpressApp } from '@modelcontextprotocol/express';
1414
import { NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
1515
import type { CallToolResult, ResourceLink } from '@modelcontextprotocol/server';
16-
import { completable, McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
16+
import { completable, McpServer, ResourceTemplate, TRACEPARENT_META_KEY } from '@modelcontextprotocol/server';
1717
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
1818
import * as z from 'zod/v4';
1919
//#endregion imports
@@ -319,6 +319,29 @@ function registerTool_progress(server: McpServer) {
319319
//#endregion registerTool_progress
320320
}
321321

322+
/** Example: Tool that reads W3C Trace Context from request `_meta`. */
323+
function registerTool_traceContext(server: McpServer) {
324+
//#region registerTool_traceContext
325+
server.registerTool(
326+
'traced-operation',
327+
{
328+
description: 'Operation that participates in distributed tracing',
329+
inputSchema: z.object({ query: z.string() })
330+
},
331+
async ({ query }, ctx): Promise<CallToolResult> => {
332+
// e.g. '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
333+
const traceparent = ctx.mcpReq._meta?.[TRACEPARENT_META_KEY];
334+
if (typeof traceparent === 'string') {
335+
// Continue the caller's trace, e.g. start a child span with your
336+
// OpenTelemetry tracer using this trace context.
337+
}
338+
339+
return { content: [{ type: 'text', text: `Results for ${query}` }] };
340+
}
341+
);
342+
//#endregion registerTool_traceContext
343+
}
344+
322345
// ---------------------------------------------------------------------------
323346
// Server-initiated requests
324347
// ---------------------------------------------------------------------------
@@ -543,6 +566,7 @@ void registerTool_errorHandling;
543566
void registerTool_annotations;
544567
void registerTool_logging;
545568
void registerTool_progress;
569+
void registerTool_traceContext;
546570
void registerTool_sampling;
547571
void registerTool_elicitation;
548572
void registerTool_roots;

0 commit comments

Comments
 (0)