0
# Server Tools API
1
2
Tools enable LLMs to take actions through your MCP server. Tools are model-controlled - AI decides which tools to call and with what arguments.
3
4
## Registration
5
6
### Modern API (Recommended)
7
8
```typescript { .api }
9
server.registerTool<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape>(
10
name: string,
11
config: {
12
title?: string;
13
description?: string;
14
inputSchema?: InputArgs;
15
outputSchema?: OutputArgs;
16
annotations?: ToolAnnotations;
17
_meta?: Record<string, unknown>;
18
},
19
callback: (args: z.infer<ZodObject<InputArgs>>, extra: RequestHandlerExtra) => Promise<CallToolResult> | CallToolResult
20
): RegisteredTool;
21
```
22
23
### Examples
24
25
```typescript
26
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
27
import { z } from 'zod';
28
29
const server = new McpServer({ name: 'tools-server', version: '1.0.0' });
30
31
// Simple tool
32
server.registerTool('add', {
33
title: 'Addition Tool',
34
description: 'Add two numbers together',
35
inputSchema: {
36
a: z.number().describe('First number'),
37
b: z.number().describe('Second number')
38
},
39
outputSchema: {
40
result: z.number().describe('Sum of the two numbers')
41
}
42
}, async ({ a, b }) => {
43
const output = { result: a + b };
44
return {
45
content: [{ type: 'text', text: JSON.stringify(output) }],
46
structuredContent: output
47
};
48
});
49
50
// Tool with annotations
51
server.registerTool('fetch-data', {
52
title: 'Fetch External Data',
53
description: 'Fetch data from an external API',
54
inputSchema: {
55
url: z.string().url(),
56
method: z.enum(['GET', 'POST']).optional()
57
},
58
outputSchema: {
59
data: z.unknown(),
60
status: z.number()
61
},
62
annotations: {
63
audience: ['user']
64
}
65
}, async ({ url, method = 'GET' }) => {
66
const response = await fetch(url, { method });
67
const data = await response.json();
68
const output = { data, status: response.status };
69
return {
70
content: [{ type: 'text', text: JSON.stringify(output) }],
71
structuredContent: output
72
};
73
});
74
```
75
76
## Return Values
77
78
```typescript { .api }
79
interface CallToolResult {
80
content: ContentBlock[]; // Required: Content to return
81
isError?: boolean; // Optional: Indicates error
82
structuredContent?: Record<string, unknown>; // Required if outputSchema defined
83
_meta?: Record<string, unknown>;
84
}
85
```
86
87
### Content Block Types
88
89
```typescript { .api }
90
interface TextContent {
91
type: 'text';
92
text: string;
93
annotations?: { audience?: ('user' | 'assistant')[]; priority?: number; };
94
}
95
96
interface ImageContent { type: 'image'; data: string; mimeType: string; }
97
interface AudioContent { type: 'audio'; data: string; mimeType: string; }
98
interface EmbeddedResource {
99
type: 'resource';
100
resource: { uri: string; mimeType?: string; text?: string; blob?: string; };
101
}
102
interface ResourceLink {
103
type: 'resource_link';
104
uri: string;
105
name?: string;
106
mimeType?: string;
107
description?: string;
108
}
109
110
type ContentBlock = TextContent | ImageContent | AudioContent | EmbeddedResource | ResourceLink;
111
```
112
113
### Return Examples
114
115
```typescript
116
// Simple text
117
return {
118
content: [{ type: 'text', text: 'Operation completed successfully' }]
119
};
120
121
// With structured content (required if outputSchema defined)
122
return {
123
content: [{ type: 'text', text: JSON.stringify({ count: 42 }) }],
124
structuredContent: { count: 42 }
125
};
126
127
// Error result
128
return {
129
content: [{ type: 'text', text: 'Failed to process request' }],
130
isError: true
131
};
132
133
// Multiple content blocks with resource links
134
return {
135
content: [
136
{ type: 'text', text: 'Found 2 files' },
137
{ type: 'resource_link', uri: 'file:///project/README.md', name: 'README.md', mimeType: 'text/markdown' },
138
{ type: 'resource_link', uri: 'file:///project/index.ts', name: 'index.ts', mimeType: 'text/typescript' }
139
],
140
structuredContent: { count: 2, files: ['README.md', 'index.ts'] }
141
};
142
```
143
144
## Callback Signature
145
146
```typescript { .api }
147
type ToolCallback<Args extends ZodRawShape = undefined> = Args extends undefined
148
? (extra: RequestHandlerExtra) => Promise<CallToolResult> | CallToolResult
149
: (args: z.infer<ZodObject<Args>>, extra: RequestHandlerExtra) => Promise<CallToolResult> | CallToolResult;
150
151
interface RequestHandlerExtra {
152
signal: AbortSignal; // Cancellation signal
153
authInfo?: AuthInfo; // Validated access token info
154
sessionId?: string; // Session ID from transport
155
_meta?: RequestMeta; // Request metadata
156
requestId: RequestId; // JSON-RPC request ID
157
requestInfo?: RequestInfo; // HTTP request details
158
sendNotification: (notification: SendNotificationT) => Promise<void>;
159
sendRequest: <U extends ZodType<object>>(request: SendRequestT, resultSchema: U, options?: RequestOptions) => Promise<z.infer<U>>;
160
}
161
```
162
163
## Managing Tools
164
165
```typescript { .api }
166
interface RegisteredTool {
167
enable(): void; // Make visible in listTools
168
disable(): void; // Hide from listTools
169
update(updates: Partial<ToolConfig>): void; // Update configuration
170
remove(): void; // Remove entirely
171
}
172
```
173
174
### Dynamic Control Example
175
176
```typescript
177
const tool = server.registerTool('admin-action', config, callback);
178
179
tool.disable(); // Initially disabled
180
181
// After authentication
182
tool.enable();
183
184
// Update description
185
tool.update({
186
description: 'Admin action (authenticated as user@example.com)'
187
});
188
189
// Remove when no longer needed
190
tool.remove();
191
```
192
193
## Error Handling
194
195
```typescript
196
server.registerTool('risky-operation', {
197
title: 'Risky Operation',
198
description: 'An operation that might fail',
199
inputSchema: { id: z.string() }
200
}, async ({ id }) => {
201
try {
202
const result = await performRiskyOperation(id);
203
return {
204
content: [{ type: 'text', text: `Success: ${result}` }],
205
structuredContent: { success: true, result }
206
};
207
} catch (error) {
208
return {
209
content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
210
isError: true
211
};
212
}
213
});
214
```
215
216
## Validation
217
218
- Input arguments are automatically validated before callback invocation
219
- Invalid inputs result in `ErrorCode.InvalidParams` error
220
- Output `structuredContent` is validated against `outputSchema` if provided
221
- Missing `structuredContent` when `outputSchema` is defined results in error
222
223
## Notifications
224
225
```typescript { .api }
226
server.sendToolListChanged(): void; // Notify clients of tool list changes
227
```
228
229
Tools are automatically notified when added, removed, enabled, or disabled (if client supports capability).
230
231
## Types Reference
232
233
```typescript { .api }
234
interface Tool {
235
name: string;
236
title?: string;
237
description?: string;
238
inputSchema: JSONSchema;
239
outputSchema?: JSONSchema;
240
annotations?: ToolAnnotations;
241
_meta?: Record<string, unknown>;
242
}
243
244
interface ToolAnnotations {
245
audience?: ('user' | 'assistant')[];
246
[key: string]: unknown;
247
}
248
249
interface ListToolsResult {
250
tools: Tool[];
251
nextCursor?: string;
252
}
253
```
254
255
## Legacy API
256
257
```typescript { .api }
258
server.tool(name: string, callback: ToolCallback): RegisteredTool;
259
server.tool(name: string, description: string, callback: ToolCallback): RegisteredTool;
260
server.tool<Args extends ZodRawShape>(name: string, paramsSchema: Args, callback: ToolCallback<Args>): RegisteredTool;
261
server.tool<Args extends ZodRawShape>(name: string, description: string, paramsSchema: Args, callback: ToolCallback<Args>): RegisteredTool;
262
```
263
264
Use `registerTool` for new code.
265