docs: comprehensive feature documentation for SEP-1730 Tier 1 (#1548)

This commit is contained in:
Felix Weinberger 2026-02-17 16:22:16 +00:00 • committed by GitHub
commit 342ea394ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -45,6 +45,23 @@ Examples:
- Stateless Streamable HTTP: [`simpleStatelessStreamableHttp.ts`](../src/examples/server/simpleStatelessStreamableHttp.ts)
- Stateful with resumability: [`simpleStreamableHttp.ts`](../src/examples/server/simpleStreamableHttp.ts)
### stdio
For local integrations where the client spawns the server as a child process, use `StdioServerTransport`. Communication happens over stdin/stdout using JSON-RPC:
```typescript
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
// ... register tools, resources, prompts ...
const transport = new StdioServerTransport();
await server.connect(transport);
```
This is the simplest transport — no HTTP server setup required. The client uses `StdioClientTransport` to spawn and communicate with the server process (see [docs/client.md](client.md#stdio-transport)).
### Deprecated HTTP + SSE
The older HTTP+SSE transport (protocol version 2024‑11‑05) is supported only for backwards compatibility. New implementations should prefer Streamable HTTP.
@ -128,11 +145,91 @@ This snippet is illustrative only; for runnable servers that expose tools, see:
- [`simpleStreamableHttp.ts`](../src/examples/server/simpleStreamableHttp.ts)
- [`toolWithSampleServer.ts`](../src/examples/server/toolWithSampleServer.ts)
#### Image and audio results
Tools can return image and audio content alongside text. Use base64-encoded data with the appropriate MIME type:
```typescript
// e.g. const chartPngBase64 = fs.readFileSync('chart.png').toString('base64');
server.registerTool('generate-chart', { description: 'Generate a chart image' }, async () => ({
content: [
{
type: 'image',
data: chartPngBase64,
mimeType: 'image/png'
}
]
}));
// e.g. const audioBase64 = fs.readFileSync('speech.wav').toString('base64');
server.registerTool(
'text-to-speech',
{
description: 'Convert text to speech',
inputSchema: { text: z.string() }
},
async ({ text }) => ({
content: [
{
type: 'audio',
data: audioBase64,
mimeType: 'audio/wav'
}
]
})
);
```
#### Embedded resource results
Tools can return embedded resources, allowing the tool to attach full resource objects in its response:
```typescript
server.registerTool('fetch-data', { description: 'Fetch and return data as a resource' }, async () => ({
content: [
{
type: 'resource',
resource: {
uri: 'data://result',
mimeType: 'application/json',
text: JSON.stringify({ key: 'value' })
}
}
]
}));
```
#### Error handling
To indicate that a tool call failed, set `isError: true` in the result. The content describes what went wrong:
```typescript
server.registerTool('risky-operation', { description: 'An operation that might fail' }, async () => {
try {
const result = await doSomething();
return { content: [{ type: 'text', text: result }] };
} catch (err) {
return {
content: [{ type: 'text', text: `Error: ${err.message}` }],
isError: true
};
}
});
```
#### Tool change notifications
When tools are added, removed, or updated at runtime, the server automatically notifies connected clients. This happens when you call `registerTool()`, or use `remove()`, `enable()`, `disable()`, or `update()` on a `RegisteredTool`. You can also trigger it manually:
```typescript
server.sendToolListChanged();
```
#### ResourceLink outputs
Tools can return `resource_link` content items to reference large resources without embedding them directly, allowing clients to fetch only what they need.
The README’s `list-files` example shows the pattern conceptually; for concrete usage, see the Streamable HTTP examples in `src/examples/server`.
The README's `list-files` example shows the pattern conceptually; for concrete usage, see the Streamable HTTP examples in `src/examples/server`.
### Resources
@ -155,7 +252,70 @@ server.registerResource(
);
```
Dynamic resources use `ResourceTemplate` and can support completions on path parameters. For full runnable examples of resources:
#### Binary resources
Resources can return binary data using `blob` (base64-encoded) instead of `text`:
```typescript
server.registerResource('logo', 'images://logo.png', { title: 'Logo', mimeType: 'image/png' }, async uri => ({
contents: [{ uri: uri.href, blob: logoPngBase64 }]
}));
```
#### Resource templates
Dynamic resources use `ResourceTemplate` to match URI patterns. The template parameters are passed to the read callback:
```typescript
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
server.registerResource('user-profile', new ResourceTemplate('users://{userId}/profile', { list: undefined }), { title: 'User Profile', mimeType: 'application/json' }, async (uri, { userId }) => ({
contents: [
{
uri: uri.href,
text: JSON.stringify(await getUser(userId))
}
]
}));
```
#### Subscribing and unsubscribing
Clients can subscribe to resource changes. The server declares subscription support via the `resources.subscribe` capability, which `McpServer` enables automatically when resources are registered.
To handle subscriptions, register handlers on the low-level server for `SubscribeRequestSchema` and `UnsubscribeRequestSchema`:
```typescript
import { SubscribeRequestSchema, UnsubscribeRequestSchema } from '@modelcontextprotocol/sdk/types.js';
const subscriptions = new Set<string>();
server.server.setRequestHandler(SubscribeRequestSchema, async request => {
subscriptions.add(request.params.uri);
return {};
});
server.server.setRequestHandler(UnsubscribeRequestSchema, async request => {
subscriptions.delete(request.params.uri);
return {};
});
```
When a subscribed resource changes, notify the client:
```typescript
if (subscriptions.has(resourceUri)) {
await server.server.sendResourceUpdated({ uri: resourceUri });
}
```
Resource list changes (adding/removing resources) are notified automatically when using `registerResource()`, `remove()`, `enable()`, or `disable()`. You can also trigger it manually:
```typescript
server.sendResourceListChanged();
```
For full runnable examples of resources:
- [`simpleStreamableHttp.ts`](../src/examples/server/simpleStreamableHttp.ts)
@ -187,15 +347,150 @@ server.registerPrompt(
);
```
#### Image content in prompts
Prompts can include image content in their messages:
```typescript
server.registerPrompt(
'analyze-image',
{
title: 'Analyze Image',
description: 'Analyze an image',
argsSchema: { imageBase64: z.string() }
},
({ imageBase64 }) => ({
messages: [
{
role: 'user',
content: {
type: 'image',
data: imageBase64,
mimeType: 'image/png'
}
}
]
})
);
```
#### Embedded resources in prompts
Prompts can embed resource content in their messages:
```typescript
server.registerPrompt(
'summarize-doc',
{
title: 'Summarize Document',
description: 'Summarize a document resource'
},
() => ({
messages: [
{
role: 'user',
content: {
type: 'resource',
resource: {
uri: 'docs://readme',
mimeType: 'text/plain',
text: 'Document content here...'
}
}
}
]
})
);
```
#### Prompt change notifications
Like tools, prompt list changes are notified automatically when using `registerPrompt()`, `remove()`, `enable()`, or `disable()`. You can also trigger it manually:
```typescript
server.sendPromptListChanged();
```
For prompts integrated into a full server, see:
- [`simpleStreamableHttp.ts`](../src/examples/server/simpleStreamableHttp.ts)
### Completions
Both prompts and resources can support argument completions. On the client side, you use `client.complete()` with a reference to the prompt or resource and the partially‑typed argument.
Both prompts and resources can support argument completions using the `completable` wrapper. This lets clients offer autocomplete suggestions as users type.
See the MCP spec sections on prompts and resources for complete details, and [`simpleStreamableHttp.ts`](../src/examples/client/simpleStreamableHttp.ts) for client‑side usage patterns.
```typescript
import { completable } from '@modelcontextprotocol/sdk/server/completable.js';
server.registerPrompt(
'greet',
{
title: 'Greeting',
description: 'Generate a greeting',
argsSchema: {
name: completable(z.string(), value => {
// Return suggestions matching the partial input
const names = ['Alice', 'Bob', 'Charlie'];
return names.filter(n => n.toLowerCase().startsWith(value.toLowerCase()));
})
}
},
({ name }) => ({
messages: [{ role: 'user', content: { type: 'text', text: `Hello, ${name}!` } }]
})
);
```
Resource templates also support completions on their path parameters via `completable`. On the client side, use `client.complete()` with a reference to the prompt or resource and the partially-typed argument:
```typescript
const result = await client.complete({
ref: { type: 'ref/prompt', name: 'greet' },
argument: { name: 'name', value: 'Al' }
});
console.log(result.completion.values); // ['Alice']
```
### Logging
The server can send log messages to the client using `server.sendLoggingMessage()`. Clients can request a minimum log level via the `logging/setLevel` request, which `McpServer` handles automatically — messages below the requested level are suppressed.
```typescript
// Send a log message from a tool handler:
server.registerTool(
'process-data',
{
description: 'Process some data',
inputSchema: { data: z.string() }
},
async ({ data }, extra) => {
await server.sendLoggingMessage({ level: 'info', data: `Processing: ${data}` }, extra.sessionId);
// ... do work ...
return { content: [{ type: 'text', text: 'Done' }] };
}
);
```
For a full example, see [`simpleStreamableHttp.ts`](../src/examples/server/simpleStreamableHttp.ts) which uses `sendLoggingMessage` throughout.
Log levels in order: `debug`, `info`, `notice`, `warning`, `error`, `critical`, `alert`, `emergency`.
#### Log level filtering
Clients can request a minimum log level via `logging/setLevel`. The low-level `Server` handles this automatically when the `logging` capability is enabled — it stores the requested level per session and suppresses messages below it. You can also send log messages directly using
`sendLoggingMessage`:
```typescript
const server = new McpServer({ name: 'my-server', version: '1.0.0' }, { capabilities: { logging: {} } });
// Client requests: only show 'warning' and above
// (handled automatically by the Server)
// These will be sent or suppressed based on the client's requested level:
await server.sendLoggingMessage({ level: 'debug', data: 'verbose detail' }); // suppressed
await server.sendLoggingMessage({ level: 'warning', data: 'something is off' }); // sent
await server.sendLoggingMessage({ level: 'error', data: 'something broke' }); // sent
```
### Display names and metadata