Skip to content

Commit 81186df

Browse files
docs: add documentation for 26 undocumented features (Tier 1 gap)
Expand existing docs and add new protocol.md to cover all 48 non-experimental MCP features required for SEP-1730 Tier 1. docs/server.md: - Tool image, audio, embedded resource, and error results - Tool change notifications - Binary resources, resource templates, subscribing/unsubscribing - Prompt image content, embedded resources, change notifications - Completions with completable() wrapper - Logging and log level docs/client.md: - stdio transport with StdioClientTransport - Roots listing and change notifications docs/capabilities.md: - Elicitation schema validation, default values, enum values - Fix experimental tasks API paths docs/protocol.md (new): - Ping, progress notifications, cancellation - Pagination, capability negotiation, protocol version negotiation - JSON Schema 2020-12 support All code snippets verified against SDK source by review agent.
1 parent f2d2145 commit 81186df

5 files changed

Lines changed: 560 additions & 5 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ For more details on how to run these examples (including recommended commands an
154154
- [docs/server.md](docs/server.md) – building and running MCP servers, transports, tools/resources/prompts, CORS, DNS rebinding, and multi-node deployment.
155155
- [docs/client.md](docs/client.md) – using the high-level client, transports, backwards compatibility, and OAuth helpers.
156156
- [docs/capabilities.md](docs/capabilities.md) – sampling, elicitation (form and URL), and experimental task-based execution.
157+
- [docs/protocol.md](docs/protocol.md) – protocol features: ping, progress, cancellation, pagination, capability negotiation, and JSON Schema.
157158
- [docs/faq.md](docs/faq.md) – environment and troubleshooting FAQs (including Node.js Web Crypto support).
158159
- External references:
159160
- [Model Context Protocol documentation](https://modelcontextprotocol.io)

docs/capabilities.md

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,97 @@ Runnable example:
2727

2828
The `simpleStreamableHttp` server also includes a `collect-user-info` tool that demonstrates how to drive elicitation from a tool and handle the response.
2929

30+
#### Schema validation
31+
32+
Elicitation schemas support validation constraints on each field. The server validates responses automatically against the `requestedSchema` using the SDK's JSON Schema validator.
33+
34+
```typescript
35+
const result = await server.server.elicitInput({
36+
mode: 'form',
37+
message: 'Enter your details:',
38+
requestedSchema: {
39+
type: 'object',
40+
properties: {
41+
email: {
42+
type: 'string',
43+
title: 'Email',
44+
format: 'email',
45+
minLength: 5
46+
},
47+
age: {
48+
type: 'integer',
49+
title: 'Age',
50+
minimum: 0,
51+
maximum: 150
52+
}
53+
},
54+
required: ['email']
55+
}
56+
});
57+
```
58+
59+
String fields support `minLength`, `maxLength`, and `format` (`'email'`, `'uri'`, `'date'`, `'date-time'`). Number fields support `minimum` and `maximum`.
60+
61+
#### Default values
62+
63+
Schema properties can include `default` values. When the client declares the `applyDefaults` capability, the SDK automatically fills in defaults for fields the user doesn't provide:
64+
65+
```typescript
66+
// Client declares applyDefaults:
67+
const client = new Client(
68+
{ name: 'my-client', version: '1.0.0' },
69+
{ capabilities: { elicitation: { form: { applyDefaults: true } } } }
70+
);
71+
72+
// Server schema with defaults:
73+
requestedSchema: {
74+
type: 'object',
75+
properties: {
76+
newsletter: { type: 'boolean', title: 'Newsletter', default: false },
77+
theme: { type: 'string', title: 'Theme', default: 'dark' }
78+
}
79+
}
80+
```
81+
82+
#### Enum values
83+
84+
Elicitation schemas support several enum patterns for single-select and multi-select fields:
85+
86+
```typescript
87+
requestedSchema: {
88+
type: 'object',
89+
properties: {
90+
// Simple enum (untitled options)
91+
color: {
92+
type: 'string',
93+
title: 'Favorite Color',
94+
enum: ['red', 'green', 'blue'],
95+
default: 'blue'
96+
},
97+
// Titled enum with display labels
98+
priority: {
99+
type: 'string',
100+
title: 'Priority',
101+
oneOf: [
102+
{ const: 'low', title: 'Low Priority' },
103+
{ const: 'medium', title: 'Medium Priority' },
104+
{ const: 'high', title: 'High Priority' }
105+
]
106+
},
107+
// Multi-select
108+
tags: {
109+
type: 'array',
110+
title: 'Tags',
111+
items: { type: 'string', enum: ['frontend', 'backend', 'docs'] },
112+
minItems: 1,
113+
maxItems: 3
114+
}
115+
}
116+
}
117+
```
118+
119+
For a full example with validation, defaults, and enums, see [`elicitationFormExample.ts`](../src/examples/server/elicitationFormExample.ts).
120+
30121
### URL elicitation
31122

32123
URL elicitation is designed for sensitive data and secure web‑based flows (e.g., collecting an API key, confirming a payment, or doing third‑party OAuth). Instead of returning form data, the server asks the client to open a URL and the rest of the flow happens in the browser.
@@ -70,7 +161,7 @@ For a runnable example that uses the in-memory store shipped with the SDK, see:
70161
On the client, you use:
71162

72163
- `client.experimental.tasks.callToolStream(...)` to start a tool call that may create a task and emit status updates over time.
73-
- `client.getTask(...)` and `client.getTaskResult(...)` to check status and fetch results after reconnecting.
164+
- `client.experimental.tasks.getTask(...)` and `client.experimental.tasks.getTaskResult(...)` to check status and fetch results after reconnecting.
74165

75166
The interactive client in:
76167

docs/client.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,53 @@ These examples show how to:
5858
- Perform dynamic client registration if needed.
5959
- Acquire access tokens.
6060
- Attach OAuth credentials to Streamable HTTP requests.
61+
62+
## stdio transport
63+
64+
Use `StdioClientTransport` to connect to a server that runs as a local child process:
65+
66+
```typescript
67+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
68+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
69+
70+
const transport = new StdioClientTransport({
71+
command: 'node',
72+
args: ['server.js'],
73+
env: { NODE_ENV: 'production' },
74+
cwd: '/path/to/server'
75+
});
76+
77+
const client = new Client({ name: 'my-client', version: '1.0.0' });
78+
await client.connect(transport);
79+
// connect() calls transport.start() automatically, spawning the child process
80+
```
81+
82+
The transport communicates over the child process's stdin/stdout using JSON-RPC. The `stderr` option controls where the child's stderr goes (defaults to `'inherit'`).
83+
84+
## Roots
85+
86+
Roots let a client expose filesystem locations to the server, so the server knows which directories or files are relevant. Declare the `roots` capability and register a handler:
87+
88+
```typescript
89+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
90+
import { ListRootsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
91+
92+
const client = new Client({ name: 'my-client', version: '1.0.0' }, { capabilities: { roots: { listChanged: true } } });
93+
94+
client.setRequestHandler(ListRootsRequestSchema, async () => {
95+
return {
96+
roots: [
97+
{ uri: 'file:///home/user/project', name: 'My Project' },
98+
{ uri: 'file:///home/user/data', name: 'Data Directory' }
99+
]
100+
};
101+
});
102+
```
103+
104+
When the set of roots changes, notify the server so it can re-query:
105+
106+
```typescript
107+
await client.sendRootsListChanged();
108+
```
109+
110+
The `listChanged: true` capability flag is required to send change notifications.

docs/protocol.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
## Protocol features
2+
3+
This page covers cross-cutting protocol mechanics that apply to both clients and servers.
4+
5+
## Ping
6+
7+
Both client and server expose a `ping()` method for health checks. The remote side responds automatically — no handler registration is needed.
8+
9+
```typescript
10+
// Client pinging the server:
11+
await client.ping();
12+
13+
// With a timeout (milliseconds):
14+
await client.ping({ timeout: 5000 });
15+
16+
// Server pinging the client (via the low-level server):
17+
await server.server.ping();
18+
```
19+
20+
## Progress notifications
21+
22+
Long-running requests can report progress to the caller. The SDK handles `progressToken` assignment automatically when you provide an `onprogress` callback.
23+
24+
**Receiving progress** (client side):
25+
26+
```typescript
27+
const result = await client.callTool({ name: 'long-task', arguments: {} }, CallToolResultSchema, {
28+
onprogress: progress => {
29+
// progress has: { progress: number, total?: number, message?: string }
30+
console.log(`${progress.progress}/${progress.total}: ${progress.message}`);
31+
},
32+
timeout: 30000,
33+
resetTimeoutOnProgress: true
34+
});
35+
```
36+
37+
**Sending progress** (server side, from a request handler):
38+
39+
```typescript
40+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
41+
for (let i = 0; i < 100; i++) {
42+
await extra.sendNotification({
43+
method: 'notifications/progress',
44+
params: {
45+
progressToken: request.params._meta?.progressToken,
46+
progress: i,
47+
total: 100,
48+
message: `Processing item ${i}`
49+
}
50+
});
51+
await doWork();
52+
}
53+
return { content: [{ type: 'text', text: 'Done' }] };
54+
});
55+
```
56+
57+
## Cancellation
58+
59+
Requests can be cancelled by the caller using an `AbortSignal`. The SDK sends a `notifications/cancelled` message to the remote side and aborts the handler via its `signal`.
60+
61+
**Client cancelling a request**:
62+
63+
```typescript
64+
const controller = new AbortController();
65+
66+
const resultPromise = client.callTool({ name: 'slow-tool', arguments: {} }, CallToolResultSchema, { signal: controller.signal });
67+
68+
// Cancel after 5 seconds:
69+
setTimeout(() => controller.abort('User cancelled'), 5000);
70+
```
71+
72+
**Server handler responding to cancellation**:
73+
74+
```typescript
75+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
76+
for (let i = 0; i < 100; i++) {
77+
extra.signal.throwIfAborted(); // throws if the client cancelled
78+
await doWork();
79+
}
80+
return { content: [{ type: 'text', text: 'Done' }] };
81+
});
82+
```
83+
84+
## Pagination
85+
86+
All list methods (`listTools`, `listPrompts`, `listResources`, `listResourceTemplates`) support cursor-based pagination. Pass `cursor` from the previous response's `nextCursor` to fetch the next page.
87+
88+
```typescript
89+
let cursor: string | undefined;
90+
const allTools: Tool[] = [];
91+
92+
do {
93+
const result = await client.listTools({ cursor });
94+
allTools.push(...result.tools);
95+
cursor = result.nextCursor;
96+
} while (cursor);
97+
```
98+
99+
The same pattern applies to `listPrompts`, `listResources`, and `listResourceTemplates`.
100+
101+
## Capability negotiation
102+
103+
Both client and server declare their capabilities during the `initialize` handshake. The SDK enforces these — attempting to use an undeclared capability throws an error.
104+
105+
**Client capabilities** are set at construction time:
106+
107+
```typescript
108+
const client = new Client(
109+
{ name: 'my-client', version: '1.0.0' },
110+
{
111+
capabilities: {
112+
roots: { listChanged: true },
113+
sampling: {},
114+
elicitation: { form: {} }
115+
}
116+
}
117+
);
118+
```
119+
120+
After connecting, inspect what the server supports:
121+
122+
```typescript
123+
await client.connect(transport);
124+
125+
const caps = client.getServerCapabilities();
126+
if (caps?.tools) {
127+
const tools = await client.listTools();
128+
}
129+
if (caps?.resources?.subscribe) {
130+
// server supports resource subscriptions
131+
}
132+
```
133+
134+
**Server capabilities** are inferred from registered handlers. When using `McpServer`, capabilities are set automatically based on what you register (tools, resources, prompts). With the low-level `Server`, you declare them in the constructor.
135+
136+
## Protocol version negotiation
137+
138+
The SDK automatically negotiates protocol versions during `initialize`. The client sends `LATEST_PROTOCOL_VERSION` and the server responds with the highest mutually supported version.
139+
140+
Supported versions are defined in `SUPPORTED_PROTOCOL_VERSIONS` (currently `2025-11-25`, `2025-06-18`, `2025-03-26`, `2024-11-05`, `2024-10-07`). If the server responds with an unsupported version, the client throws an error.
141+
142+
No user code is needed — this is handled automatically by `client.connect()`.
143+
144+
## JSON Schema 2020-12
145+
146+
MCP uses JSON Schema 2020-12 for tool input and output schemas. When using `McpServer` with Zod, schemas are converted to JSON Schema automatically:
147+
148+
```typescript
149+
server.registerTool(
150+
'calculate',
151+
{
152+
description: 'Add two numbers',
153+
inputSchema: { a: z.number(), b: z.number() }
154+
},
155+
async ({ a, b }) => ({
156+
content: [{ type: 'text', text: String(a + b) }]
157+
})
158+
);
159+
```
160+
161+
With the low-level `Server`, you provide JSON Schema directly:
162+
163+
```typescript
164+
{
165+
name: 'calculate',
166+
inputSchema: {
167+
type: 'object',
168+
properties: {
169+
a: { type: 'number' },
170+
b: { type: 'number' }
171+
},
172+
required: ['a', 'b']
173+
}
174+
}
175+
```
176+
177+
The SDK validates tool outputs against `outputSchema` (when provided) using a pluggable JSON Schema validator. The default validator uses Ajv; a Cloudflare Workers-compatible alternative is available via `CfWorkerJsonSchemaValidator`.

0 commit comments

Comments
 (0)