fastmcp 1.23.0 → 1.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/FastMCP.d.ts +165 -165
- package/dist/FastMCP.js +275 -275
- package/dist/FastMCP.js.map +1 -1
- package/dist/bin/fastmcp.js +9 -9
- package/dist/bin/fastmcp.js.map +1 -1
- package/eslint.config.ts +14 -0
- package/jsr.json +1 -1
- package/package.json +6 -1
- package/src/FastMCP.test.ts +398 -396
- package/src/FastMCP.ts +498 -498
- package/src/bin/fastmcp.ts +7 -7
- package/src/examples/addition.ts +25 -24
- package/eslint.config.js +0 -3
package/src/FastMCP.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
3
4
|
import {
|
|
4
5
|
AudioContent,
|
|
5
6
|
CallToolRequestSchema,
|
|
@@ -20,19 +21,18 @@ import {
|
|
|
20
21
|
SetLevelRequestSchema,
|
|
21
22
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
22
23
|
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
23
|
-
import { toJsonSchema } from "xsschema";
|
|
24
|
-
import { z } from "zod";
|
|
25
|
-
import { setTimeout as delay } from "timers/promises";
|
|
26
|
-
import { readFile } from "fs/promises";
|
|
27
|
-
import { fileTypeFromBuffer } from "file-type";
|
|
28
|
-
import { StrictEventEmitter } from "strict-event-emitter-types";
|
|
29
24
|
import { EventEmitter } from "events";
|
|
25
|
+
import { fileTypeFromBuffer } from "file-type";
|
|
26
|
+
import { readFile } from "fs/promises";
|
|
30
27
|
import Fuse from "fuse.js";
|
|
31
|
-
import { startSSEServer } from "mcp-proxy";
|
|
32
|
-
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
33
|
-
import parseURITemplate from "uri-templates";
|
|
34
28
|
import http from "http";
|
|
29
|
+
import { startSSEServer } from "mcp-proxy";
|
|
30
|
+
import { StrictEventEmitter } from "strict-event-emitter-types";
|
|
31
|
+
import { setTimeout as delay } from "timers/promises";
|
|
35
32
|
import { fetch } from "undici";
|
|
33
|
+
import parseURITemplate from "uri-templates";
|
|
34
|
+
import { toJsonSchema } from "xsschema";
|
|
35
|
+
import { z } from "zod";
|
|
36
36
|
|
|
37
37
|
export type SSEServer = {
|
|
38
38
|
close: () => Promise<void>;
|
|
@@ -44,15 +44,15 @@ type FastMCPEvents<T extends FastMCPSessionAuth> = {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
type FastMCPSessionEvents = {
|
|
47
|
-
rootsChanged: (event: { roots: Root[] }) => void;
|
|
48
47
|
error: (event: { error: Error }) => void;
|
|
48
|
+
rootsChanged: (event: { roots: Root[] }) => void;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Generates an image content object from a URL, file path, or buffer.
|
|
53
53
|
*/
|
|
54
54
|
export const imageContent = async (
|
|
55
|
-
input: {
|
|
55
|
+
input: { buffer: Buffer } | { path: string } | { url: string },
|
|
56
56
|
): Promise<ImageContent> => {
|
|
57
57
|
let rawData: Buffer;
|
|
58
58
|
|
|
@@ -79,14 +79,14 @@ export const imageContent = async (
|
|
|
79
79
|
const base64Data = rawData.toString("base64");
|
|
80
80
|
|
|
81
81
|
return {
|
|
82
|
-
type: "image",
|
|
83
82
|
data: base64Data,
|
|
84
83
|
mimeType: mimeType?.mime ?? "image/png",
|
|
84
|
+
type: "image",
|
|
85
85
|
} as const;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
88
|
export const audioContent = async (
|
|
89
|
-
input: {
|
|
89
|
+
input: { buffer: Buffer } | { path: string } | { url: string },
|
|
90
90
|
): Promise<AudioContent> => {
|
|
91
91
|
let rawData: Buffer;
|
|
92
92
|
|
|
@@ -113,47 +113,29 @@ export const audioContent = async (
|
|
|
113
113
|
const base64Data = rawData.toString("base64");
|
|
114
114
|
|
|
115
115
|
return {
|
|
116
|
-
type: "audio",
|
|
117
116
|
data: base64Data,
|
|
118
117
|
mimeType: mimeType?.mime ?? "audio/mpeg",
|
|
118
|
+
type: "audio",
|
|
119
119
|
} as const;
|
|
120
120
|
};
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
122
|
+
type Context<T extends FastMCPSessionAuth> = {
|
|
123
|
+
log: {
|
|
124
|
+
debug: (message: string, data?: SerializableValue) => void;
|
|
125
|
+
error: (message: string, data?: SerializableValue) => void;
|
|
126
|
+
info: (message: string, data?: SerializableValue) => void;
|
|
127
|
+
warn: (message: string, data?: SerializableValue) => void;
|
|
128
|
+
};
|
|
129
|
+
reportProgress: (progress: Progress) => Promise<void>;
|
|
130
|
+
session: T | undefined;
|
|
131
|
+
};
|
|
128
132
|
|
|
129
133
|
type Extra = unknown;
|
|
130
134
|
|
|
131
135
|
type Extras = Record<string, Extra>;
|
|
132
136
|
|
|
133
|
-
export class UnexpectedStateError extends FastMCPError {
|
|
134
|
-
public extras?: Extras;
|
|
135
|
-
|
|
136
|
-
public constructor(message: string, extras?: Extras) {
|
|
137
|
-
super(message);
|
|
138
|
-
this.name = new.target.name;
|
|
139
|
-
this.extras = extras;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* An error that is meant to be surfaced to the user.
|
|
145
|
-
*/
|
|
146
|
-
export class UserError extends UnexpectedStateError {}
|
|
147
|
-
|
|
148
|
-
type ToolParameters = StandardSchemaV1;
|
|
149
|
-
|
|
150
137
|
type Literal = boolean | null | number | string | undefined;
|
|
151
138
|
|
|
152
|
-
type SerializableValue =
|
|
153
|
-
| Literal
|
|
154
|
-
| SerializableValue[]
|
|
155
|
-
| { [key: string]: SerializableValue };
|
|
156
|
-
|
|
157
139
|
type Progress = {
|
|
158
140
|
/**
|
|
159
141
|
* The progress thus far. This should increase every time progress is made, even if the total is unknown.
|
|
@@ -165,41 +147,58 @@ type Progress = {
|
|
|
165
147
|
total?: number;
|
|
166
148
|
};
|
|
167
149
|
|
|
168
|
-
type
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
debug: (message: string, data?: SerializableValue) => void;
|
|
173
|
-
error: (message: string, data?: SerializableValue) => void;
|
|
174
|
-
info: (message: string, data?: SerializableValue) => void;
|
|
175
|
-
warn: (message: string, data?: SerializableValue) => void;
|
|
176
|
-
};
|
|
177
|
-
};
|
|
150
|
+
type SerializableValue =
|
|
151
|
+
| { [key: string]: SerializableValue }
|
|
152
|
+
| Literal
|
|
153
|
+
| SerializableValue[];
|
|
178
154
|
|
|
179
155
|
type TextContent = {
|
|
180
|
-
type: "text";
|
|
181
156
|
text: string;
|
|
157
|
+
type: "text";
|
|
182
158
|
};
|
|
183
159
|
|
|
160
|
+
type ToolParameters = StandardSchemaV1;
|
|
161
|
+
|
|
162
|
+
abstract class FastMCPError extends Error {
|
|
163
|
+
public constructor(message?: string) {
|
|
164
|
+
super(message);
|
|
165
|
+
this.name = new.target.name;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export class UnexpectedStateError extends FastMCPError {
|
|
170
|
+
public extras?: Extras;
|
|
171
|
+
|
|
172
|
+
public constructor(message: string, extras?: Extras) {
|
|
173
|
+
super(message);
|
|
174
|
+
this.name = new.target.name;
|
|
175
|
+
this.extras = extras;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* An error that is meant to be surfaced to the user.
|
|
181
|
+
*/
|
|
182
|
+
export class UserError extends UnexpectedStateError {}
|
|
183
|
+
|
|
184
184
|
const TextContentZodSchema = z
|
|
185
185
|
.object({
|
|
186
|
-
type: z.literal("text"),
|
|
187
186
|
/**
|
|
188
187
|
* The text content of the message.
|
|
189
188
|
*/
|
|
190
189
|
text: z.string(),
|
|
190
|
+
type: z.literal("text"),
|
|
191
191
|
})
|
|
192
192
|
.strict() satisfies z.ZodType<TextContent>;
|
|
193
193
|
|
|
194
194
|
type ImageContent = {
|
|
195
|
-
type: "image";
|
|
196
195
|
data: string;
|
|
197
196
|
mimeType: string;
|
|
197
|
+
type: "image";
|
|
198
198
|
};
|
|
199
199
|
|
|
200
200
|
const ImageContentZodSchema = z
|
|
201
201
|
.object({
|
|
202
|
-
type: z.literal("image"),
|
|
203
202
|
/**
|
|
204
203
|
* The base64-encoded image data.
|
|
205
204
|
*/
|
|
@@ -208,10 +207,11 @@ const ImageContentZodSchema = z
|
|
|
208
207
|
* The MIME type of the image. Different providers may support different image types.
|
|
209
208
|
*/
|
|
210
209
|
mimeType: z.string(),
|
|
210
|
+
type: z.literal("image"),
|
|
211
211
|
})
|
|
212
212
|
.strict() satisfies z.ZodType<ImageContent>;
|
|
213
213
|
|
|
214
|
-
type Content =
|
|
214
|
+
type Content = ImageContent | TextContent;
|
|
215
215
|
|
|
216
216
|
const ContentZodSchema = z.discriminatedUnion("type", [
|
|
217
217
|
TextContentZodSchema,
|
|
@@ -231,9 +231,9 @@ const ContentResultZodSchema = z
|
|
|
231
231
|
.strict() satisfies z.ZodType<ContentResult>;
|
|
232
232
|
|
|
233
233
|
type Completion = {
|
|
234
|
-
values: string[];
|
|
235
|
-
total?: number;
|
|
236
234
|
hasMore?: boolean;
|
|
235
|
+
total?: number;
|
|
236
|
+
values: string[];
|
|
237
237
|
};
|
|
238
238
|
|
|
239
239
|
/**
|
|
@@ -241,140 +241,85 @@ type Completion = {
|
|
|
241
241
|
*/
|
|
242
242
|
const CompletionZodSchema = z.object({
|
|
243
243
|
/**
|
|
244
|
-
*
|
|
244
|
+
* Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
|
|
245
245
|
*/
|
|
246
|
-
|
|
246
|
+
hasMore: z.optional(z.boolean()),
|
|
247
247
|
/**
|
|
248
248
|
* The total number of completion options available. This can exceed the number of values actually sent in the response.
|
|
249
249
|
*/
|
|
250
250
|
total: z.optional(z.number().int()),
|
|
251
251
|
/**
|
|
252
|
-
*
|
|
252
|
+
* An array of completion values. Must not exceed 100 items.
|
|
253
253
|
*/
|
|
254
|
-
|
|
254
|
+
values: z.array(z.string()).max(100),
|
|
255
255
|
}) satisfies z.ZodType<Completion>;
|
|
256
256
|
|
|
257
|
-
|
|
258
|
-
* Tool annotations as defined in MCP Specification (2025-03-26)
|
|
259
|
-
* These provide hints about a tool's behavior.
|
|
260
|
-
*/
|
|
261
|
-
type ToolAnnotations = {
|
|
262
|
-
/**
|
|
263
|
-
* A human-readable title for the tool, useful for UI display
|
|
264
|
-
*/
|
|
265
|
-
title?: string;
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* If true, indicates the tool does not modify its environment
|
|
269
|
-
* @default false
|
|
270
|
-
*/
|
|
271
|
-
readOnlyHint?: boolean;
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* If true, the tool may perform destructive updates
|
|
275
|
-
* Only meaningful when readOnlyHint is false
|
|
276
|
-
* @default true
|
|
277
|
-
*/
|
|
278
|
-
destructiveHint?: boolean;
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* If true, calling the tool repeatedly with the same arguments has no additional effect
|
|
282
|
-
* Only meaningful when readOnlyHint is false
|
|
283
|
-
* @default false
|
|
284
|
-
*/
|
|
285
|
-
idempotentHint?: boolean;
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* If true, the tool may interact with an "open world" of external entities
|
|
289
|
-
* @default true
|
|
290
|
-
*/
|
|
291
|
-
openWorldHint?: boolean;
|
|
292
|
-
};
|
|
257
|
+
type ArgumentValueCompleter = (value: string) => Promise<Completion>;
|
|
293
258
|
|
|
294
|
-
type
|
|
295
|
-
|
|
296
|
-
|
|
259
|
+
type InputPrompt<
|
|
260
|
+
Arguments extends InputPromptArgument[] = InputPromptArgument[],
|
|
261
|
+
Args = PromptArgumentsToObject<Arguments>,
|
|
297
262
|
> = {
|
|
298
|
-
|
|
263
|
+
arguments?: InputPromptArgument[];
|
|
299
264
|
description?: string;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
execute: (
|
|
303
|
-
args: StandardSchemaV1.InferOutput<Params>,
|
|
304
|
-
context: Context<T>,
|
|
305
|
-
) => Promise<
|
|
306
|
-
string | ContentResult | TextContent | ImageContent | AudioContent
|
|
307
|
-
>;
|
|
265
|
+
load: (args: Args) => Promise<string>;
|
|
266
|
+
name: string;
|
|
308
267
|
};
|
|
309
268
|
|
|
310
|
-
type
|
|
311
|
-
| {
|
|
312
|
-
text: string;
|
|
313
|
-
}
|
|
314
|
-
| {
|
|
315
|
-
blob: string;
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
type InputResourceTemplateArgument = Readonly<{
|
|
319
|
-
name: string;
|
|
320
|
-
description?: string;
|
|
269
|
+
type InputPromptArgument = Readonly<{
|
|
321
270
|
complete?: ArgumentValueCompleter;
|
|
322
|
-
}>;
|
|
323
|
-
|
|
324
|
-
type ResourceTemplateArgument = Readonly<{
|
|
325
|
-
name: string;
|
|
326
271
|
description?: string;
|
|
327
|
-
|
|
272
|
+
enum?: string[];
|
|
273
|
+
name: string;
|
|
274
|
+
required?: boolean;
|
|
328
275
|
}>;
|
|
329
276
|
|
|
330
|
-
type
|
|
277
|
+
type InputResourceTemplate<
|
|
331
278
|
Arguments extends ResourceTemplateArgument[] = ResourceTemplateArgument[],
|
|
332
279
|
> = {
|
|
333
|
-
uriTemplate: string;
|
|
334
|
-
name: string;
|
|
335
|
-
description?: string;
|
|
336
|
-
mimeType?: string;
|
|
337
280
|
arguments: Arguments;
|
|
338
|
-
|
|
281
|
+
description?: string;
|
|
339
282
|
load: (
|
|
340
283
|
args: ResourceTemplateArgumentsToObject<Arguments>,
|
|
341
284
|
) => Promise<ResourceResult>;
|
|
285
|
+
mimeType?: string;
|
|
286
|
+
name: string;
|
|
287
|
+
uriTemplate: string;
|
|
342
288
|
};
|
|
343
289
|
|
|
344
|
-
type
|
|
345
|
-
|
|
346
|
-
|
|
290
|
+
type InputResourceTemplateArgument = Readonly<{
|
|
291
|
+
complete?: ArgumentValueCompleter;
|
|
292
|
+
description?: string;
|
|
293
|
+
name: string;
|
|
294
|
+
}>;
|
|
347
295
|
|
|
348
|
-
type
|
|
349
|
-
|
|
296
|
+
type LoggingLevel =
|
|
297
|
+
| "alert"
|
|
298
|
+
| "critical"
|
|
299
|
+
| "debug"
|
|
300
|
+
| "emergency"
|
|
301
|
+
| "error"
|
|
302
|
+
| "info"
|
|
303
|
+
| "notice"
|
|
304
|
+
| "warning";
|
|
305
|
+
|
|
306
|
+
type Prompt<
|
|
307
|
+
Arguments extends PromptArgument[] = PromptArgument[],
|
|
308
|
+
Args = PromptArgumentsToObject<Arguments>,
|
|
350
309
|
> = {
|
|
351
|
-
|
|
352
|
-
name: string
|
|
310
|
+
arguments?: PromptArgument[];
|
|
311
|
+
complete?: (name: string, value: string) => Promise<Completion>;
|
|
353
312
|
description?: string;
|
|
354
|
-
|
|
355
|
-
arguments: Arguments;
|
|
356
|
-
load: (
|
|
357
|
-
args: ResourceTemplateArgumentsToObject<Arguments>,
|
|
358
|
-
) => Promise<ResourceResult>;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
type Resource = {
|
|
362
|
-
uri: string;
|
|
313
|
+
load: (args: Args) => Promise<string>;
|
|
363
314
|
name: string;
|
|
364
|
-
description?: string;
|
|
365
|
-
mimeType?: string;
|
|
366
|
-
load: () => Promise<ResourceResult | ResourceResult[]>;
|
|
367
|
-
complete?: (name: string, value: string) => Promise<Completion>;
|
|
368
315
|
};
|
|
369
316
|
|
|
370
|
-
type
|
|
371
|
-
|
|
372
|
-
type InputPromptArgument = Readonly<{
|
|
373
|
-
name: string;
|
|
374
|
-
description?: string;
|
|
375
|
-
required?: boolean;
|
|
317
|
+
type PromptArgument = Readonly<{
|
|
376
318
|
complete?: ArgumentValueCompleter;
|
|
319
|
+
description?: string;
|
|
377
320
|
enum?: string[];
|
|
321
|
+
name: string;
|
|
322
|
+
required?: boolean;
|
|
378
323
|
}>;
|
|
379
324
|
|
|
380
325
|
type PromptArgumentsToObject<T extends { name: string; required?: boolean }[]> =
|
|
@@ -387,98 +332,171 @@ type PromptArgumentsToObject<T extends { name: string; required?: boolean }[]> =
|
|
|
387
332
|
: string | undefined;
|
|
388
333
|
};
|
|
389
334
|
|
|
390
|
-
type
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
load: (args: Args) => Promise<string>;
|
|
335
|
+
type Resource = {
|
|
336
|
+
complete?: (name: string, value: string) => Promise<Completion>;
|
|
337
|
+
description?: string;
|
|
338
|
+
load: () => Promise<ResourceResult | ResourceResult[]>;
|
|
339
|
+
mimeType?: string;
|
|
340
|
+
name: string;
|
|
341
|
+
uri: string;
|
|
398
342
|
};
|
|
399
343
|
|
|
400
|
-
type
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
344
|
+
type ResourceResult =
|
|
345
|
+
| {
|
|
346
|
+
blob: string;
|
|
347
|
+
}
|
|
348
|
+
| {
|
|
349
|
+
text: string;
|
|
350
|
+
};
|
|
407
351
|
|
|
408
|
-
type
|
|
409
|
-
Arguments extends
|
|
410
|
-
Args = PromptArgumentsToObject<Arguments>,
|
|
352
|
+
type ResourceTemplate<
|
|
353
|
+
Arguments extends ResourceTemplateArgument[] = ResourceTemplateArgument[],
|
|
411
354
|
> = {
|
|
412
|
-
arguments
|
|
355
|
+
arguments: Arguments;
|
|
413
356
|
complete?: (name: string, value: string) => Promise<Completion>;
|
|
414
357
|
description?: string;
|
|
415
|
-
load: (
|
|
358
|
+
load: (
|
|
359
|
+
args: ResourceTemplateArgumentsToObject<Arguments>,
|
|
360
|
+
) => Promise<ResourceResult>;
|
|
361
|
+
mimeType?: string;
|
|
416
362
|
name: string;
|
|
363
|
+
uriTemplate: string;
|
|
417
364
|
};
|
|
418
365
|
|
|
419
|
-
type
|
|
366
|
+
type ResourceTemplateArgument = Readonly<{
|
|
367
|
+
complete?: ArgumentValueCompleter;
|
|
368
|
+
description?: string;
|
|
420
369
|
name: string;
|
|
421
|
-
|
|
370
|
+
}>;
|
|
371
|
+
|
|
372
|
+
type ResourceTemplateArgumentsToObject<T extends { name: string }[]> = {
|
|
373
|
+
[K in T[number]["name"]]: string;
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
type ServerOptions<T extends FastMCPSessionAuth> = {
|
|
422
377
|
authenticate?: Authenticate<T>;
|
|
423
378
|
instructions?: string;
|
|
379
|
+
name: string;
|
|
380
|
+
version: `${number}.${number}.${number}`;
|
|
424
381
|
};
|
|
425
382
|
|
|
426
|
-
type
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
383
|
+
type Tool<
|
|
384
|
+
T extends FastMCPSessionAuth,
|
|
385
|
+
Params extends ToolParameters = ToolParameters,
|
|
386
|
+
> = {
|
|
387
|
+
annotations?: ToolAnnotations;
|
|
388
|
+
description?: string;
|
|
389
|
+
execute: (
|
|
390
|
+
args: StandardSchemaV1.InferOutput<Params>,
|
|
391
|
+
context: Context<T>,
|
|
392
|
+
) => Promise<
|
|
393
|
+
AudioContent | ContentResult | ImageContent | string | TextContent
|
|
394
|
+
>;
|
|
395
|
+
name: string;
|
|
396
|
+
parameters?: Params;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Tool annotations as defined in MCP Specification (2025-03-26)
|
|
401
|
+
* These provide hints about a tool's behavior.
|
|
402
|
+
*/
|
|
403
|
+
type ToolAnnotations = {
|
|
404
|
+
/**
|
|
405
|
+
* If true, the tool may perform destructive updates
|
|
406
|
+
* Only meaningful when readOnlyHint is false
|
|
407
|
+
* @default true
|
|
408
|
+
*/
|
|
409
|
+
destructiveHint?: boolean;
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* If true, calling the tool repeatedly with the same arguments has no additional effect
|
|
413
|
+
* Only meaningful when readOnlyHint is false
|
|
414
|
+
* @default false
|
|
415
|
+
*/
|
|
416
|
+
idempotentHint?: boolean;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* If true, the tool may interact with an "open world" of external entities
|
|
420
|
+
* @default true
|
|
421
|
+
*/
|
|
422
|
+
openWorldHint?: boolean;
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* If true, indicates the tool does not modify its environment
|
|
426
|
+
* @default false
|
|
427
|
+
*/
|
|
428
|
+
readOnlyHint?: boolean;
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* A human-readable title for the tool, useful for UI display
|
|
432
|
+
*/
|
|
433
|
+
title?: string;
|
|
434
|
+
};
|
|
435
435
|
|
|
436
436
|
const FastMCPSessionEventEmitterBase: {
|
|
437
437
|
new (): StrictEventEmitter<EventEmitter, FastMCPSessionEvents>;
|
|
438
438
|
} = EventEmitter;
|
|
439
439
|
|
|
440
|
-
|
|
440
|
+
type FastMCPSessionAuth = Record<string, unknown> | undefined;
|
|
441
441
|
|
|
442
442
|
type SamplingResponse = {
|
|
443
|
+
content: AudioContent | ImageContent | TextContent;
|
|
443
444
|
model: string;
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
content: TextContent | ImageContent | AudioContent;
|
|
445
|
+
role: "assistant" | "user";
|
|
446
|
+
stopReason?: "endTurn" | "maxTokens" | "stopSequence" | string;
|
|
447
447
|
};
|
|
448
448
|
|
|
449
|
-
|
|
449
|
+
class FastMCPSessionEventEmitter extends FastMCPSessionEventEmitterBase {}
|
|
450
450
|
|
|
451
451
|
export class FastMCPSession<
|
|
452
452
|
T extends FastMCPSessionAuth = FastMCPSessionAuth,
|
|
453
453
|
> extends FastMCPSessionEventEmitter {
|
|
454
|
+
public get clientCapabilities(): ClientCapabilities | null {
|
|
455
|
+
return this.#clientCapabilities ?? null;
|
|
456
|
+
}
|
|
457
|
+
public get loggingLevel(): LoggingLevel {
|
|
458
|
+
return this.#loggingLevel;
|
|
459
|
+
}
|
|
460
|
+
public get roots(): Root[] {
|
|
461
|
+
return this.#roots;
|
|
462
|
+
}
|
|
463
|
+
public get server(): Server {
|
|
464
|
+
return this.#server;
|
|
465
|
+
}
|
|
466
|
+
#auth: T | undefined;
|
|
454
467
|
#capabilities: ServerCapabilities = {};
|
|
455
468
|
#clientCapabilities?: ClientCapabilities;
|
|
456
469
|
#loggingLevel: LoggingLevel = "info";
|
|
470
|
+
#pingInterval: null | ReturnType<typeof setInterval> = null;
|
|
471
|
+
|
|
457
472
|
#prompts: Prompt[] = [];
|
|
473
|
+
|
|
458
474
|
#resources: Resource[] = [];
|
|
475
|
+
|
|
459
476
|
#resourceTemplates: ResourceTemplate[] = [];
|
|
477
|
+
|
|
460
478
|
#roots: Root[] = [];
|
|
479
|
+
|
|
461
480
|
#server: Server;
|
|
462
|
-
#auth: T | undefined;
|
|
463
481
|
|
|
464
482
|
constructor({
|
|
465
483
|
auth,
|
|
466
|
-
name,
|
|
467
|
-
version,
|
|
468
484
|
instructions,
|
|
469
|
-
|
|
485
|
+
name,
|
|
486
|
+
prompts,
|
|
470
487
|
resources,
|
|
471
488
|
resourcesTemplates,
|
|
472
|
-
|
|
489
|
+
tools,
|
|
490
|
+
version,
|
|
473
491
|
}: {
|
|
474
492
|
auth?: T;
|
|
475
|
-
name: string;
|
|
476
|
-
version: string;
|
|
477
493
|
instructions?: string;
|
|
478
|
-
|
|
494
|
+
name: string;
|
|
495
|
+
prompts: Prompt[];
|
|
479
496
|
resources: Resource[];
|
|
480
497
|
resourcesTemplates: InputResourceTemplate[];
|
|
481
|
-
|
|
498
|
+
tools: Tool<T>[];
|
|
499
|
+
version: string;
|
|
482
500
|
}) {
|
|
483
501
|
super();
|
|
484
502
|
|
|
@@ -537,92 +555,16 @@ export class FastMCPSession<
|
|
|
537
555
|
}
|
|
538
556
|
}
|
|
539
557
|
|
|
540
|
-
|
|
541
|
-
this.#
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
private addResourceTemplate(inputResourceTemplate: InputResourceTemplate) {
|
|
545
|
-
const completers: Record<string, ArgumentValueCompleter> = {};
|
|
546
|
-
|
|
547
|
-
for (const argument of inputResourceTemplate.arguments ?? []) {
|
|
548
|
-
if (argument.complete) {
|
|
549
|
-
completers[argument.name] = argument.complete;
|
|
550
|
-
}
|
|
558
|
+
public async close() {
|
|
559
|
+
if (this.#pingInterval) {
|
|
560
|
+
clearInterval(this.#pingInterval);
|
|
551
561
|
}
|
|
552
562
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
return await completers[name](value);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
return {
|
|
561
|
-
values: [],
|
|
562
|
-
};
|
|
563
|
-
},
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
this.#resourceTemplates.push(resourceTemplate);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
private addPrompt(inputPrompt: InputPrompt) {
|
|
570
|
-
const completers: Record<string, ArgumentValueCompleter> = {};
|
|
571
|
-
const enums: Record<string, string[]> = {};
|
|
572
|
-
|
|
573
|
-
for (const argument of inputPrompt.arguments ?? []) {
|
|
574
|
-
if (argument.complete) {
|
|
575
|
-
completers[argument.name] = argument.complete;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if (argument.enum) {
|
|
579
|
-
enums[argument.name] = argument.enum;
|
|
580
|
-
}
|
|
563
|
+
try {
|
|
564
|
+
await this.#server.close();
|
|
565
|
+
} catch (error) {
|
|
566
|
+
console.error("[FastMCP error]", "could not close server", error);
|
|
581
567
|
}
|
|
582
|
-
|
|
583
|
-
const prompt = {
|
|
584
|
-
...inputPrompt,
|
|
585
|
-
complete: async (name: string, value: string) => {
|
|
586
|
-
if (completers[name]) {
|
|
587
|
-
return await completers[name](value);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
if (enums[name]) {
|
|
591
|
-
const fuse = new Fuse(enums[name], {
|
|
592
|
-
keys: ["value"],
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
const result = fuse.search(value);
|
|
596
|
-
|
|
597
|
-
return {
|
|
598
|
-
values: result.map((item) => item.item),
|
|
599
|
-
total: result.length,
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
return {
|
|
604
|
-
values: [],
|
|
605
|
-
};
|
|
606
|
-
},
|
|
607
|
-
};
|
|
608
|
-
|
|
609
|
-
this.#prompts.push(prompt);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
public get clientCapabilities(): ClientCapabilities | null {
|
|
613
|
-
return this.#clientCapabilities ?? null;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
public get server(): Server {
|
|
617
|
-
return this.#server;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
#pingInterval: ReturnType<typeof setInterval> | null = null;
|
|
621
|
-
|
|
622
|
-
public async requestSampling(
|
|
623
|
-
message: z.infer<typeof CreateMessageRequestSchema>["params"],
|
|
624
|
-
): Promise<SamplingResponse> {
|
|
625
|
-
return this.#server.createMessage(message);
|
|
626
568
|
}
|
|
627
569
|
|
|
628
570
|
public async connect(transport: Transport) {
|
|
@@ -675,30 +617,82 @@ export class FastMCPSession<
|
|
|
675
617
|
}
|
|
676
618
|
}
|
|
677
619
|
|
|
678
|
-
public
|
|
679
|
-
|
|
620
|
+
public async requestSampling(
|
|
621
|
+
message: z.infer<typeof CreateMessageRequestSchema>["params"],
|
|
622
|
+
): Promise<SamplingResponse> {
|
|
623
|
+
return this.#server.createMessage(message);
|
|
680
624
|
}
|
|
681
625
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
626
|
+
private addPrompt(inputPrompt: InputPrompt) {
|
|
627
|
+
const completers: Record<string, ArgumentValueCompleter> = {};
|
|
628
|
+
const enums: Record<string, string[]> = {};
|
|
686
629
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
630
|
+
for (const argument of inputPrompt.arguments ?? []) {
|
|
631
|
+
if (argument.complete) {
|
|
632
|
+
completers[argument.name] = argument.complete;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (argument.enum) {
|
|
636
|
+
enums[argument.name] = argument.enum;
|
|
637
|
+
}
|
|
691
638
|
}
|
|
692
|
-
}
|
|
693
639
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
640
|
+
const prompt = {
|
|
641
|
+
...inputPrompt,
|
|
642
|
+
complete: async (name: string, value: string) => {
|
|
643
|
+
if (completers[name]) {
|
|
644
|
+
return await completers[name](value);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (enums[name]) {
|
|
648
|
+
const fuse = new Fuse(enums[name], {
|
|
649
|
+
keys: ["value"],
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
const result = fuse.search(value);
|
|
653
|
+
|
|
654
|
+
return {
|
|
655
|
+
total: result.length,
|
|
656
|
+
values: result.map((item) => item.item),
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
return {
|
|
661
|
+
values: [],
|
|
662
|
+
};
|
|
663
|
+
},
|
|
697
664
|
};
|
|
665
|
+
|
|
666
|
+
this.#prompts.push(prompt);
|
|
698
667
|
}
|
|
699
668
|
|
|
700
|
-
|
|
701
|
-
|
|
669
|
+
private addResource(inputResource: Resource) {
|
|
670
|
+
this.#resources.push(inputResource);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
private addResourceTemplate(inputResourceTemplate: InputResourceTemplate) {
|
|
674
|
+
const completers: Record<string, ArgumentValueCompleter> = {};
|
|
675
|
+
|
|
676
|
+
for (const argument of inputResourceTemplate.arguments ?? []) {
|
|
677
|
+
if (argument.complete) {
|
|
678
|
+
completers[argument.name] = argument.complete;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const resourceTemplate = {
|
|
683
|
+
...inputResourceTemplate,
|
|
684
|
+
complete: async (name: string, value: string) => {
|
|
685
|
+
if (completers[name]) {
|
|
686
|
+
return await completers[name](value);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return {
|
|
690
|
+
values: [],
|
|
691
|
+
};
|
|
692
|
+
},
|
|
693
|
+
};
|
|
694
|
+
|
|
695
|
+
this.#resourceTemplates.push(resourceTemplate);
|
|
702
696
|
}
|
|
703
697
|
|
|
704
698
|
private setupCompleteHandlers() {
|
|
@@ -774,19 +768,10 @@ export class FastMCPSession<
|
|
|
774
768
|
});
|
|
775
769
|
}
|
|
776
770
|
|
|
777
|
-
private
|
|
778
|
-
this.#server.
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
this.#server.listRoots().then((roots) => {
|
|
782
|
-
this.#roots = roots.roots;
|
|
783
|
-
|
|
784
|
-
this.emit("rootsChanged", {
|
|
785
|
-
roots: roots.roots,
|
|
786
|
-
});
|
|
787
|
-
});
|
|
788
|
-
},
|
|
789
|
-
);
|
|
771
|
+
private setupErrorHandling() {
|
|
772
|
+
this.#server.onerror = (error) => {
|
|
773
|
+
console.error("[FastMCP error]", error);
|
|
774
|
+
};
|
|
790
775
|
}
|
|
791
776
|
|
|
792
777
|
private setupLoggingHandlers() {
|
|
@@ -797,137 +782,63 @@ export class FastMCPSession<
|
|
|
797
782
|
});
|
|
798
783
|
}
|
|
799
784
|
|
|
800
|
-
private
|
|
801
|
-
this.#server.setRequestHandler(
|
|
785
|
+
private setupPromptHandlers(prompts: Prompt[]) {
|
|
786
|
+
this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
802
787
|
return {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
annotations: tool.annotations,
|
|
812
|
-
};
|
|
813
|
-
}),
|
|
814
|
-
),
|
|
788
|
+
prompts: prompts.map((prompt) => {
|
|
789
|
+
return {
|
|
790
|
+
arguments: prompt.arguments,
|
|
791
|
+
complete: prompt.complete,
|
|
792
|
+
description: prompt.description,
|
|
793
|
+
name: prompt.name,
|
|
794
|
+
};
|
|
795
|
+
}),
|
|
815
796
|
};
|
|
816
797
|
});
|
|
817
798
|
|
|
818
|
-
this.#server.setRequestHandler(
|
|
819
|
-
const
|
|
799
|
+
this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
800
|
+
const prompt = prompts.find(
|
|
801
|
+
(prompt) => prompt.name === request.params.name,
|
|
802
|
+
);
|
|
820
803
|
|
|
821
|
-
if (!
|
|
804
|
+
if (!prompt) {
|
|
822
805
|
throw new McpError(
|
|
823
806
|
ErrorCode.MethodNotFound,
|
|
824
|
-
`Unknown
|
|
807
|
+
`Unknown prompt: ${request.params.name}`,
|
|
825
808
|
);
|
|
826
809
|
}
|
|
827
810
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
if (tool.parameters) {
|
|
831
|
-
const parsed = await tool.parameters["~standard"].validate(
|
|
832
|
-
request.params.arguments,
|
|
833
|
-
);
|
|
811
|
+
const args = request.params.arguments;
|
|
834
812
|
|
|
835
|
-
|
|
813
|
+
for (const arg of prompt.arguments ?? []) {
|
|
814
|
+
if (arg.required && !(args && arg.name in args)) {
|
|
836
815
|
throw new McpError(
|
|
837
|
-
ErrorCode.
|
|
838
|
-
`
|
|
816
|
+
ErrorCode.InvalidRequest,
|
|
817
|
+
`Missing required argument: ${arg.name}`,
|
|
839
818
|
);
|
|
840
819
|
}
|
|
841
|
-
|
|
842
|
-
args = parsed.value;
|
|
843
820
|
}
|
|
844
821
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
let result: ContentResult;
|
|
848
|
-
|
|
849
|
-
try {
|
|
850
|
-
const reportProgress = async (progress: Progress) => {
|
|
851
|
-
await this.#server.notification({
|
|
852
|
-
method: "notifications/progress",
|
|
853
|
-
params: {
|
|
854
|
-
...progress,
|
|
855
|
-
progressToken,
|
|
856
|
-
},
|
|
857
|
-
});
|
|
858
|
-
};
|
|
859
|
-
|
|
860
|
-
const log = {
|
|
861
|
-
debug: (message: string, context?: SerializableValue) => {
|
|
862
|
-
this.#server.sendLoggingMessage({
|
|
863
|
-
level: "debug",
|
|
864
|
-
data: {
|
|
865
|
-
message,
|
|
866
|
-
context,
|
|
867
|
-
},
|
|
868
|
-
});
|
|
869
|
-
},
|
|
870
|
-
error: (message: string, context?: SerializableValue) => {
|
|
871
|
-
this.#server.sendLoggingMessage({
|
|
872
|
-
level: "error",
|
|
873
|
-
data: {
|
|
874
|
-
message,
|
|
875
|
-
context,
|
|
876
|
-
},
|
|
877
|
-
});
|
|
878
|
-
},
|
|
879
|
-
info: (message: string, context?: SerializableValue) => {
|
|
880
|
-
this.#server.sendLoggingMessage({
|
|
881
|
-
level: "info",
|
|
882
|
-
data: {
|
|
883
|
-
message,
|
|
884
|
-
context,
|
|
885
|
-
},
|
|
886
|
-
});
|
|
887
|
-
},
|
|
888
|
-
warn: (message: string, context?: SerializableValue) => {
|
|
889
|
-
this.#server.sendLoggingMessage({
|
|
890
|
-
level: "warning",
|
|
891
|
-
data: {
|
|
892
|
-
message,
|
|
893
|
-
context,
|
|
894
|
-
},
|
|
895
|
-
});
|
|
896
|
-
},
|
|
897
|
-
};
|
|
898
|
-
|
|
899
|
-
const maybeStringResult = await tool.execute(args, {
|
|
900
|
-
reportProgress,
|
|
901
|
-
log,
|
|
902
|
-
session: this.#auth,
|
|
903
|
-
});
|
|
822
|
+
let result: Awaited<ReturnType<Prompt["load"]>>;
|
|
904
823
|
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
content: [{ type: "text", text: maybeStringResult }],
|
|
908
|
-
});
|
|
909
|
-
} else if ("type" in maybeStringResult) {
|
|
910
|
-
result = ContentResultZodSchema.parse({
|
|
911
|
-
content: [maybeStringResult],
|
|
912
|
-
});
|
|
913
|
-
} else {
|
|
914
|
-
result = ContentResultZodSchema.parse(maybeStringResult);
|
|
915
|
-
}
|
|
824
|
+
try {
|
|
825
|
+
result = await prompt.load(args as Record<string, string | undefined>);
|
|
916
826
|
} catch (error) {
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
};
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
return {
|
|
925
|
-
content: [{ type: "text", text: `Error: ${error}` }],
|
|
926
|
-
isError: true,
|
|
927
|
-
};
|
|
827
|
+
throw new McpError(
|
|
828
|
+
ErrorCode.InternalError,
|
|
829
|
+
`Error loading prompt: ${error}`,
|
|
830
|
+
);
|
|
928
831
|
}
|
|
929
832
|
|
|
930
|
-
return
|
|
833
|
+
return {
|
|
834
|
+
description: prompt.description,
|
|
835
|
+
messages: [
|
|
836
|
+
{
|
|
837
|
+
content: { text: result, type: "text" },
|
|
838
|
+
role: "user",
|
|
839
|
+
},
|
|
840
|
+
],
|
|
841
|
+
};
|
|
931
842
|
});
|
|
932
843
|
}
|
|
933
844
|
|
|
@@ -936,9 +847,9 @@ export class FastMCPSession<
|
|
|
936
847
|
return {
|
|
937
848
|
resources: resources.map((resource) => {
|
|
938
849
|
return {
|
|
939
|
-
uri: resource.uri,
|
|
940
|
-
name: resource.name,
|
|
941
850
|
mimeType: resource.mimeType,
|
|
851
|
+
name: resource.name,
|
|
852
|
+
uri: resource.uri,
|
|
942
853
|
};
|
|
943
854
|
}),
|
|
944
855
|
};
|
|
@@ -972,9 +883,9 @@ export class FastMCPSession<
|
|
|
972
883
|
return {
|
|
973
884
|
contents: [
|
|
974
885
|
{
|
|
975
|
-
uri: uri,
|
|
976
886
|
mimeType: resourceTemplate.mimeType,
|
|
977
887
|
name: resourceTemplate.name,
|
|
888
|
+
uri: uri,
|
|
978
889
|
...result,
|
|
979
890
|
},
|
|
980
891
|
],
|
|
@@ -1008,9 +919,9 @@ export class FastMCPSession<
|
|
|
1008
919
|
if (Array.isArray(maybeArrayResult)) {
|
|
1009
920
|
return {
|
|
1010
921
|
contents: maybeArrayResult.map((result) => ({
|
|
1011
|
-
uri: resource.uri,
|
|
1012
922
|
mimeType: resource.mimeType,
|
|
1013
923
|
name: resource.name,
|
|
924
|
+
uri: resource.uri,
|
|
1014
925
|
...result,
|
|
1015
926
|
})),
|
|
1016
927
|
};
|
|
@@ -1018,9 +929,9 @@ export class FastMCPSession<
|
|
|
1018
929
|
return {
|
|
1019
930
|
contents: [
|
|
1020
931
|
{
|
|
1021
|
-
uri: resource.uri,
|
|
1022
932
|
mimeType: resource.mimeType,
|
|
1023
933
|
name: resource.name,
|
|
934
|
+
uri: resource.uri,
|
|
1024
935
|
...maybeArrayResult,
|
|
1025
936
|
},
|
|
1026
937
|
],
|
|
@@ -1051,63 +962,152 @@ export class FastMCPSession<
|
|
|
1051
962
|
);
|
|
1052
963
|
}
|
|
1053
964
|
|
|
1054
|
-
private
|
|
1055
|
-
this.#server.
|
|
965
|
+
private setupRootsHandlers() {
|
|
966
|
+
this.#server.setNotificationHandler(
|
|
967
|
+
RootsListChangedNotificationSchema,
|
|
968
|
+
() => {
|
|
969
|
+
this.#server.listRoots().then((roots) => {
|
|
970
|
+
this.#roots = roots.roots;
|
|
971
|
+
|
|
972
|
+
this.emit("rootsChanged", {
|
|
973
|
+
roots: roots.roots,
|
|
974
|
+
});
|
|
975
|
+
});
|
|
976
|
+
},
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
private setupToolHandlers(tools: Tool<T>[]) {
|
|
981
|
+
this.#server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1056
982
|
return {
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
983
|
+
tools: await Promise.all(
|
|
984
|
+
tools.map(async (tool) => {
|
|
985
|
+
return {
|
|
986
|
+
annotations: tool.annotations,
|
|
987
|
+
description: tool.description,
|
|
988
|
+
inputSchema: tool.parameters
|
|
989
|
+
? await toJsonSchema(tool.parameters)
|
|
990
|
+
: undefined,
|
|
991
|
+
name: tool.name,
|
|
992
|
+
};
|
|
993
|
+
}),
|
|
994
|
+
),
|
|
1065
995
|
};
|
|
1066
996
|
});
|
|
1067
997
|
|
|
1068
|
-
this.#server.setRequestHandler(
|
|
1069
|
-
const
|
|
1070
|
-
(prompt) => prompt.name === request.params.name,
|
|
1071
|
-
);
|
|
998
|
+
this.#server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
999
|
+
const tool = tools.find((tool) => tool.name === request.params.name);
|
|
1072
1000
|
|
|
1073
|
-
if (!
|
|
1001
|
+
if (!tool) {
|
|
1074
1002
|
throw new McpError(
|
|
1075
1003
|
ErrorCode.MethodNotFound,
|
|
1076
|
-
`Unknown
|
|
1004
|
+
`Unknown tool: ${request.params.name}`,
|
|
1077
1005
|
);
|
|
1078
1006
|
}
|
|
1079
1007
|
|
|
1080
|
-
|
|
1008
|
+
let args: unknown = undefined;
|
|
1081
1009
|
|
|
1082
|
-
|
|
1083
|
-
|
|
1010
|
+
if (tool.parameters) {
|
|
1011
|
+
const parsed = await tool.parameters["~standard"].validate(
|
|
1012
|
+
request.params.arguments,
|
|
1013
|
+
);
|
|
1014
|
+
|
|
1015
|
+
if (parsed.issues) {
|
|
1084
1016
|
throw new McpError(
|
|
1085
|
-
ErrorCode.
|
|
1086
|
-
`
|
|
1017
|
+
ErrorCode.InvalidParams,
|
|
1018
|
+
`Invalid ${request.params.name} parameters: ${JSON.stringify(parsed.issues)}`,
|
|
1087
1019
|
);
|
|
1088
1020
|
}
|
|
1021
|
+
|
|
1022
|
+
args = parsed.value;
|
|
1089
1023
|
}
|
|
1090
1024
|
|
|
1091
|
-
|
|
1025
|
+
const progressToken = request.params?._meta?.progressToken;
|
|
1026
|
+
|
|
1027
|
+
let result: ContentResult;
|
|
1092
1028
|
|
|
1093
1029
|
try {
|
|
1094
|
-
|
|
1030
|
+
const reportProgress = async (progress: Progress) => {
|
|
1031
|
+
await this.#server.notification({
|
|
1032
|
+
method: "notifications/progress",
|
|
1033
|
+
params: {
|
|
1034
|
+
...progress,
|
|
1035
|
+
progressToken,
|
|
1036
|
+
},
|
|
1037
|
+
});
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
const log = {
|
|
1041
|
+
debug: (message: string, context?: SerializableValue) => {
|
|
1042
|
+
this.#server.sendLoggingMessage({
|
|
1043
|
+
data: {
|
|
1044
|
+
context,
|
|
1045
|
+
message,
|
|
1046
|
+
},
|
|
1047
|
+
level: "debug",
|
|
1048
|
+
});
|
|
1049
|
+
},
|
|
1050
|
+
error: (message: string, context?: SerializableValue) => {
|
|
1051
|
+
this.#server.sendLoggingMessage({
|
|
1052
|
+
data: {
|
|
1053
|
+
context,
|
|
1054
|
+
message,
|
|
1055
|
+
},
|
|
1056
|
+
level: "error",
|
|
1057
|
+
});
|
|
1058
|
+
},
|
|
1059
|
+
info: (message: string, context?: SerializableValue) => {
|
|
1060
|
+
this.#server.sendLoggingMessage({
|
|
1061
|
+
data: {
|
|
1062
|
+
context,
|
|
1063
|
+
message,
|
|
1064
|
+
},
|
|
1065
|
+
level: "info",
|
|
1066
|
+
});
|
|
1067
|
+
},
|
|
1068
|
+
warn: (message: string, context?: SerializableValue) => {
|
|
1069
|
+
this.#server.sendLoggingMessage({
|
|
1070
|
+
data: {
|
|
1071
|
+
context,
|
|
1072
|
+
message,
|
|
1073
|
+
},
|
|
1074
|
+
level: "warning",
|
|
1075
|
+
});
|
|
1076
|
+
},
|
|
1077
|
+
};
|
|
1078
|
+
|
|
1079
|
+
const maybeStringResult = await tool.execute(args, {
|
|
1080
|
+
log,
|
|
1081
|
+
reportProgress,
|
|
1082
|
+
session: this.#auth,
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
if (typeof maybeStringResult === "string") {
|
|
1086
|
+
result = ContentResultZodSchema.parse({
|
|
1087
|
+
content: [{ text: maybeStringResult, type: "text" }],
|
|
1088
|
+
});
|
|
1089
|
+
} else if ("type" in maybeStringResult) {
|
|
1090
|
+
result = ContentResultZodSchema.parse({
|
|
1091
|
+
content: [maybeStringResult],
|
|
1092
|
+
});
|
|
1093
|
+
} else {
|
|
1094
|
+
result = ContentResultZodSchema.parse(maybeStringResult);
|
|
1095
|
+
}
|
|
1095
1096
|
} catch (error) {
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1097
|
+
if (error instanceof UserError) {
|
|
1098
|
+
return {
|
|
1099
|
+
content: [{ text: error.message, type: "text" }],
|
|
1100
|
+
isError: true,
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
return {
|
|
1105
|
+
content: [{ text: `Error: ${error}`, type: "text" }],
|
|
1106
|
+
isError: true,
|
|
1107
|
+
};
|
|
1100
1108
|
}
|
|
1101
1109
|
|
|
1102
|
-
return
|
|
1103
|
-
description: prompt.description,
|
|
1104
|
-
messages: [
|
|
1105
|
-
{
|
|
1106
|
-
role: "user",
|
|
1107
|
-
content: { type: "text", text: result },
|
|
1108
|
-
},
|
|
1109
|
-
],
|
|
1110
|
-
};
|
|
1110
|
+
return result;
|
|
1111
1111
|
});
|
|
1112
1112
|
}
|
|
1113
1113
|
}
|
|
@@ -1116,21 +1116,25 @@ const FastMCPEventEmitterBase: {
|
|
|
1116
1116
|
new (): StrictEventEmitter<EventEmitter, FastMCPEvents<FastMCPSessionAuth>>;
|
|
1117
1117
|
} = EventEmitter;
|
|
1118
1118
|
|
|
1119
|
-
class FastMCPEventEmitter extends FastMCPEventEmitterBase {}
|
|
1120
|
-
|
|
1121
1119
|
type Authenticate<T> = (request: http.IncomingMessage) => Promise<T>;
|
|
1122
1120
|
|
|
1121
|
+
class FastMCPEventEmitter extends FastMCPEventEmitterBase {}
|
|
1122
|
+
|
|
1123
1123
|
export class FastMCP<
|
|
1124
1124
|
T extends Record<string, unknown> | undefined = undefined,
|
|
1125
1125
|
> extends FastMCPEventEmitter {
|
|
1126
|
+
public get sessions(): FastMCPSession<T>[] {
|
|
1127
|
+
return this.#sessions;
|
|
1128
|
+
}
|
|
1129
|
+
#authenticate: Authenticate<T> | undefined;
|
|
1126
1130
|
#options: ServerOptions<T>;
|
|
1127
1131
|
#prompts: InputPrompt[] = [];
|
|
1128
1132
|
#resources: Resource[] = [];
|
|
1129
1133
|
#resourcesTemplates: InputResourceTemplate[] = [];
|
|
1130
1134
|
#sessions: FastMCPSession<T>[] = [];
|
|
1131
|
-
#sseServer:
|
|
1135
|
+
#sseServer: null | SSEServer = null;
|
|
1136
|
+
|
|
1132
1137
|
#tools: Tool<T>[] = [];
|
|
1133
|
-
#authenticate: Authenticate<T> | undefined;
|
|
1134
1138
|
|
|
1135
1139
|
constructor(public options: ServerOptions<T>) {
|
|
1136
1140
|
super();
|
|
@@ -1139,15 +1143,13 @@ export class FastMCP<
|
|
|
1139
1143
|
this.#authenticate = options.authenticate;
|
|
1140
1144
|
}
|
|
1141
1145
|
|
|
1142
|
-
public get sessions(): FastMCPSession<T>[] {
|
|
1143
|
-
return this.#sessions;
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
1146
|
/**
|
|
1147
|
-
* Adds a
|
|
1147
|
+
* Adds a prompt to the server.
|
|
1148
1148
|
*/
|
|
1149
|
-
public
|
|
1150
|
-
|
|
1149
|
+
public addPrompt<const Args extends InputPromptArgument[]>(
|
|
1150
|
+
prompt: InputPrompt<Args>,
|
|
1151
|
+
) {
|
|
1152
|
+
this.#prompts.push(prompt);
|
|
1151
1153
|
}
|
|
1152
1154
|
|
|
1153
1155
|
/**
|
|
@@ -1167,12 +1169,10 @@ export class FastMCP<
|
|
|
1167
1169
|
}
|
|
1168
1170
|
|
|
1169
1171
|
/**
|
|
1170
|
-
* Adds a
|
|
1172
|
+
* Adds a tool to the server.
|
|
1171
1173
|
*/
|
|
1172
|
-
public
|
|
1173
|
-
|
|
1174
|
-
) {
|
|
1175
|
-
this.#prompts.push(prompt);
|
|
1174
|
+
public addTool<Params extends ToolParameters>(tool: Tool<T, Params>) {
|
|
1175
|
+
this.#tools.push(tool as unknown as Tool<T>);
|
|
1176
1176
|
}
|
|
1177
1177
|
|
|
1178
1178
|
/**
|
|
@@ -1180,11 +1180,11 @@ export class FastMCP<
|
|
|
1180
1180
|
*/
|
|
1181
1181
|
public async start(
|
|
1182
1182
|
options:
|
|
1183
|
-
| { transportType: "stdio" }
|
|
1184
1183
|
| {
|
|
1185
|
-
transportType: "sse";
|
|
1186
1184
|
sse: { endpoint: `/${string}`; port: number };
|
|
1187
|
-
|
|
1185
|
+
transportType: "sse";
|
|
1186
|
+
}
|
|
1187
|
+
| { transportType: "stdio" } = {
|
|
1188
1188
|
transportType: "stdio",
|
|
1189
1189
|
},
|
|
1190
1190
|
) {
|
|
@@ -1192,13 +1192,13 @@ export class FastMCP<
|
|
|
1192
1192
|
const transport = new StdioServerTransport();
|
|
1193
1193
|
|
|
1194
1194
|
const session = new FastMCPSession<T>({
|
|
1195
|
-
name: this.#options.name,
|
|
1196
|
-
version: this.#options.version,
|
|
1197
1195
|
instructions: this.#options.instructions,
|
|
1198
|
-
|
|
1196
|
+
name: this.#options.name,
|
|
1197
|
+
prompts: this.#prompts,
|
|
1199
1198
|
resources: this.#resources,
|
|
1200
1199
|
resourcesTemplates: this.#resourcesTemplates,
|
|
1201
|
-
|
|
1200
|
+
tools: this.#tools,
|
|
1201
|
+
version: this.#options.version,
|
|
1202
1202
|
});
|
|
1203
1203
|
|
|
1204
1204
|
await session.connect(transport);
|
|
@@ -1210,8 +1210,6 @@ export class FastMCP<
|
|
|
1210
1210
|
});
|
|
1211
1211
|
} else if (options.transportType === "sse") {
|
|
1212
1212
|
this.#sseServer = await startSSEServer<FastMCPSession<T>>({
|
|
1213
|
-
endpoint: options.sse.endpoint as `/${string}`,
|
|
1214
|
-
port: options.sse.port,
|
|
1215
1213
|
createServer: async (request) => {
|
|
1216
1214
|
let auth: T | undefined;
|
|
1217
1215
|
|
|
@@ -1222,13 +1220,14 @@ export class FastMCP<
|
|
|
1222
1220
|
return new FastMCPSession<T>({
|
|
1223
1221
|
auth,
|
|
1224
1222
|
name: this.#options.name,
|
|
1225
|
-
|
|
1226
|
-
tools: this.#tools,
|
|
1223
|
+
prompts: this.#prompts,
|
|
1227
1224
|
resources: this.#resources,
|
|
1228
1225
|
resourcesTemplates: this.#resourcesTemplates,
|
|
1229
|
-
|
|
1226
|
+
tools: this.#tools,
|
|
1227
|
+
version: this.#options.version,
|
|
1230
1228
|
});
|
|
1231
1229
|
},
|
|
1230
|
+
endpoint: options.sse.endpoint as `/${string}`,
|
|
1232
1231
|
onClose: (session) => {
|
|
1233
1232
|
this.emit("disconnect", {
|
|
1234
1233
|
session,
|
|
@@ -1241,6 +1240,7 @@ export class FastMCP<
|
|
|
1241
1240
|
session,
|
|
1242
1241
|
});
|
|
1243
1242
|
},
|
|
1243
|
+
port: options.sse.port,
|
|
1244
1244
|
});
|
|
1245
1245
|
|
|
1246
1246
|
console.info(
|
|
@@ -1263,11 +1263,11 @@ export class FastMCP<
|
|
|
1263
1263
|
|
|
1264
1264
|
export type { Context };
|
|
1265
1265
|
export type { Tool, ToolParameters };
|
|
1266
|
-
export type { Content,
|
|
1266
|
+
export type { Content, ContentResult, ImageContent, TextContent };
|
|
1267
1267
|
export type { Progress, SerializableValue };
|
|
1268
1268
|
export type { Resource, ResourceResult };
|
|
1269
1269
|
export type { ResourceTemplate, ResourceTemplateArgument };
|
|
1270
1270
|
export type { Prompt, PromptArgument };
|
|
1271
1271
|
export type { InputPrompt, InputPromptArgument };
|
|
1272
|
-
export type {
|
|
1272
|
+
export type { LoggingLevel, ServerOptions };
|
|
1273
1273
|
export type { FastMCPEvents, FastMCPSessionEvents };
|