docs: comprehensive feature documentation for SEP-1730 Tier 1 (#1548)
This commit is contained in:
parent
2084a22074
commit
342ea394ca
1 changed files with 742 additions and 5 deletions
303
docs/server.md
303
docs/server.md
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue