0
# Server Advanced Features
1
2
Advanced capabilities: sampling (LLM completions), elicitation (user input), dynamic capability management, and low-level protocol access.
3
4
## Sampling (LLM Completions)
5
6
Servers can request LLM completions from clients, enabling tools to use AI capabilities.
7
8
```typescript { .api }
9
async createMessage(
10
params: {
11
messages: SamplingMessage[]; // Messages for the LLM
12
maxTokens?: number; // Maximum tokens to generate
13
modelPreferences?: ModelPreferences; // Model preferences
14
systemPrompt?: string; // System prompt
15
includeContext?: 'none' | 'thisServer' | 'allServers';
16
temperature?: number; // Temperature (0-1)
17
stopSequences?: string[]; // Stop sequences
18
metadata?: Record<string, unknown>; // Request metadata
19
},
20
options?: RequestOptions
21
): Promise<CreateMessageResult>;
22
23
interface SamplingMessage {
24
role: 'user' | 'assistant';
25
content: TextContent | ImageContent | AudioContent;
26
}
27
28
interface ModelPreferences {
29
hints?: ModelHint[]; // Prioritized model hints
30
costPriority?: number; // 0-1, higher = prefer cheaper
31
speedPriority?: number; // 0-1, higher = prefer faster
32
intelligencePriority?: number; // 0-1, higher = prefer smarter
33
}
34
35
interface CreateMessageResult {
36
role: 'assistant';
37
content: TextContent | ImageContent | AudioContent;
38
model: string;
39
stopReason?: 'endTurn' | 'stopSequence' | 'maxTokens';
40
_meta?: Record<string, unknown>;
41
}
42
```
43
44
### Example
45
46
```typescript
47
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
48
import { z } from 'zod';
49
50
const server = new McpServer({ name: 'sampling-server', version: '1.0.0' });
51
52
server.registerTool('summarize', {
53
title: 'Summarize Text',
54
description: 'Summarize text using an LLM',
55
inputSchema: { text: z.string().describe('Text to summarize') },
56
outputSchema: { summary: z.string() }
57
}, async ({ text }) => {
58
const response = await server.server.createMessage({
59
messages: [{
60
role: 'user',
61
content: { type: 'text', text: `Please summarize the following text concisely:\n\n${text}` }
62
}],
63
maxTokens: 500,
64
temperature: 0.7
65
});
66
67
const summary = response.content.type === 'text' ? response.content.text : 'Unable to generate summary';
68
const output = { summary };
69
70
return {
71
content: [{ type: 'text', text: JSON.stringify(output) }],
72
structuredContent: output
73
};
74
});
75
```
76
77
## Elicitation (User Input)
78
79
Servers can request structured input from users through forms.
80
81
```typescript { .api }
82
async elicitInput(
83
params: {
84
message: string; // Message to display to user
85
requestedSchema: JSONSchema; // JSON Schema describing requested input
86
},
87
options?: RequestOptions
88
): Promise<ElicitResult>;
89
90
interface ElicitResult {
91
action: 'accept' | 'decline' | 'cancel';
92
content?: Record<string, unknown>; // User-provided content if action is 'accept'
93
_meta?: Record<string, unknown>;
94
}
95
```
96
97
### Examples
98
99
```typescript
100
// User confirmation
101
server.registerTool('delete-file', {
102
title: 'Delete File',
103
description: 'Delete a file with user confirmation',
104
inputSchema: { filePath: z.string() },
105
outputSchema: { deleted: z.boolean() }
106
}, async ({ filePath }) => {
107
const result = await server.server.elicitInput({
108
message: `Are you sure you want to delete ${filePath}?`,
109
requestedSchema: {
110
type: 'object',
111
properties: {
112
confirm: { type: 'boolean', title: 'Confirm deletion', description: 'Check to confirm file deletion' }
113
},
114
required: ['confirm']
115
}
116
});
117
118
let output;
119
if (result.action === 'accept' && result.content?.confirm) {
120
await fs.unlink(filePath);
121
output = { deleted: true };
122
} else {
123
output = { deleted: false };
124
}
125
126
return {
127
content: [{ type: 'text', text: JSON.stringify(output) }],
128
structuredContent: output
129
};
130
});
131
132
// Restaurant booking with alternatives
133
server.registerTool('book-restaurant', {
134
title: 'Book Restaurant',
135
description: 'Book a table at a restaurant',
136
inputSchema: { restaurant: z.string(), date: z.string(), partySize: z.number() }
137
}, async ({ restaurant, date, partySize }) => {
138
const available = await checkAvailability(restaurant, date, partySize);
139
140
if (!available) {
141
const result = await server.server.elicitInput({
142
message: `No tables available at ${restaurant} on ${date}. Would you like to check alternative dates?`,
143
requestedSchema: {
144
type: 'object',
145
properties: {
146
checkAlternatives: { type: 'boolean', title: 'Check alternative dates' },
147
flexibleDates: {
148
type: 'string',
149
title: 'Date flexibility',
150
enum: ['next_day', 'same_week', 'next_week'],
151
enumNames: ['Next day', 'Same week', 'Next week']
152
}
153
},
154
required: ['checkAlternatives']
155
}
156
});
157
158
if (result.action === 'accept' && result.content?.checkAlternatives) {
159
const alternatives = await findAlternatives(restaurant, date, partySize, result.content.flexibleDates);
160
return {
161
content: [{ type: 'text', text: `Alternative dates: ${alternatives.join(', ')}` }],
162
structuredContent: { alternatives }
163
};
164
}
165
}
166
167
await makeBooking(restaurant, date, partySize);
168
return {
169
content: [{ type: 'text', text: 'Booking confirmed' }],
170
structuredContent: { success: true }
171
};
172
});
173
```
174
175
## Dynamic Capability Management
176
177
Tools, resources, and prompts can be controlled at runtime:
178
179
```typescript { .api }
180
interface RegisteredTool {
181
enable(): void; // Make visible in listTools
182
disable(): void; // Hide from listTools
183
update(config: Partial<ToolConfig>): void; // Update configuration
184
remove(): void; // Remove entirely
185
}
186
```
187
188
### Example
189
190
```typescript
191
// Register tools with different access levels
192
const readTool = server.registerTool('read-data', readConfig, readCallback);
193
const writeTool = server.registerTool('write-data', writeConfig, writeCallback);
194
const adminTool = server.registerTool('admin-action', adminConfig, adminCallback);
195
196
// Initially, only read access
197
writeTool.disable();
198
adminTool.disable();
199
200
// After authentication, enable based on role
201
function updatePermissions(userRole: string) {
202
if (userRole === 'editor') {
203
writeTool.enable();
204
} else if (userRole === 'admin') {
205
writeTool.enable();
206
adminTool.enable();
207
}
208
server.sendToolListChanged();
209
}
210
211
// Dynamic tool update
212
readTool.update({ description: `Read data (authenticated as ${username})` });
213
```
214
215
## Notification Debouncing
216
217
Reduce network traffic by coalescing rapid notifications:
218
219
```typescript
220
const server = new McpServer(
221
{ name: 'efficient-server', version: '1.0.0' },
222
{
223
debouncedNotificationMethods: [
224
'notifications/tools/list_changed',
225
'notifications/resources/list_changed',
226
'notifications/prompts/list_changed'
227
]
228
}
229
);
230
231
// Bulk updates send only one notification per type
232
for (let i = 0; i < 100; i++) {
233
server.registerTool(`tool${i}`, config, callback).disable();
234
}
235
// Only one notifications/tools/list_changed sent
236
```
237
238
## Low-Level Server Access
239
240
```typescript { .api }
241
const mcpServer = new McpServer({ name: 'my-server', version: '1.0.0' });
242
const lowLevelServer = mcpServer.server; // Access underlying Server instance
243
244
// Set custom request handlers
245
lowLevelServer.setRequestHandler(CustomRequestSchema, async (request, extra) => {
246
return customResult;
247
});
248
249
// Set custom notification handlers
250
lowLevelServer.setNotificationHandler(CustomNotificationSchema, async (notification) => {
251
// Handle notification
252
});
253
254
// Send custom requests to client
255
const result = await lowLevelServer.request({ method: 'custom/method', params: {} }, CustomResultSchema);
256
257
// Send custom notifications to client
258
await lowLevelServer.notification({ method: 'custom/notification', params: {} });
259
```
260
261
## Protocol Base Class
262
263
```typescript { .api }
264
import { Protocol, ProtocolOptions, RequestOptions, NotificationOptions } from '@modelcontextprotocol/sdk/shared/protocol.js';
265
266
abstract class Protocol<RequestT, NotificationT, ResultT> {
267
protected transport?: Transport;
268
269
setRequestHandler<T extends ZodType<object>>(schema: T, handler: (request: z.infer<T>, extra: RequestHandlerExtra) => Promise<Result> | Result): void;
270
setNotificationHandler<T extends ZodType<object>>(schema: T, handler: (notification: z.infer<T>) => Promise<void> | void): void;
271
request<U extends ZodType<object>>(request: RequestT, resultSchema: U, options?: RequestOptions): Promise<z.infer<U>>;
272
notification(notification: NotificationT, options?: NotificationOptions): Promise<void>;
273
close(): Promise<void>;
274
connect(transport: Transport): Promise<void>;
275
}
276
277
interface ProtocolOptions {
278
enforceStrictCapabilities?: boolean; // Default: true
279
debouncedNotificationMethods?: string[];
280
}
281
```
282
283
## Client Information
284
285
```typescript { .api }
286
server.getClientCapabilities(): ClientCapabilities | undefined;
287
server.getClientVersion(): Implementation | undefined;
288
```
289
290
## Logging
291
292
```typescript { .api }
293
async sendLoggingMessage(
294
params: {
295
level: LoggingLevel;
296
logger?: string;
297
data: unknown;
298
},
299
sessionId?: string
300
): Promise<void>;
301
302
type LoggingLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency';
303
```
304
305
### Example
306
307
```typescript
308
await server.sendLoggingMessage({
309
level: 'info',
310
logger: 'my-tool',
311
data: 'Operation completed successfully'
312
});
313
314
await server.sendLoggingMessage({
315
level: 'error',
316
logger: 'database',
317
data: { error: 'Connection failed', code: 'ECONNREFUSED' }
318
});
319
```
320
321
## Roots Management
322
323
```typescript { .api }
324
async listRoots(params?: Record<string, unknown>, options?: RequestOptions): Promise<ListRootsResult>;
325
326
interface ListRootsResult {
327
roots: Root[];
328
_meta?: Record<string, unknown>;
329
}
330
331
interface Root {
332
uri: string;
333
name: string;
334
}
335
```
336
337
## Connection Management
338
339
```typescript { .api }
340
isConnected(): boolean;
341
async close(): Promise<void>;
342
```
343
344
## Lifecycle Callbacks
345
346
```typescript
347
server.server.oninitialized = () => { console.log('Client initialized'); };
348
server.server.onclose = () => { console.log('Connection closed'); };
349
server.server.onerror = (error) => { console.error('Server error:', error); };
350
```
351
352
## Types Reference
353
354
```typescript { .api }
355
interface RequestHandlerExtra {
356
request: <U>(request: Request, resultSchema: U, options?: RequestOptions) => Promise<Result>;
357
notification: (notification: Notification, options?: NotificationOptions) => Promise<void>;
358
sessionId?: string;
359
requestInfo?: RequestInfo;
360
signal: AbortSignal;
361
}
362
363
interface RequestOptions {
364
timeout?: number;
365
onprogress?: (progress: Progress) => void;
366
signal?: AbortSignal;
367
}
368
369
interface NotificationOptions {
370
priority?: number;
371
}
372
```
373