fastmcp 1.10.0 → 1.11.0
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/README.md +55 -0
- package/dist/FastMCP.d.ts +30 -6
- package/dist/FastMCP.js +95 -9
- package/dist/FastMCP.js.map +1 -1
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/FastMCP.test.ts +218 -120
- package/src/FastMCP.ts +151 -15
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ A TypeScript framework for building [MCP](https://modelcontextprotocol.io/) serv
|
|
|
15
15
|
- [SSE](#sse)
|
|
16
16
|
- [Progress notifications](#progress)
|
|
17
17
|
- [Typed server events](#typed-server-events)
|
|
18
|
+
- [Prompt argument auto-completion](#prompt-argument-auto-completion)
|
|
18
19
|
- Automated SSE pings
|
|
19
20
|
- Roots
|
|
20
21
|
- CLI for [testing](#test-with-mcp-cli) and [debugging](#inspect-with-mcp-inspector)
|
|
@@ -393,6 +394,60 @@ server.addPrompt({
|
|
|
393
394
|
});
|
|
394
395
|
```
|
|
395
396
|
|
|
397
|
+
#### Prompt argument auto-completion
|
|
398
|
+
|
|
399
|
+
Prompts can provide auto-completion for their arguments:
|
|
400
|
+
|
|
401
|
+
```js
|
|
402
|
+
server.addPrompt({
|
|
403
|
+
name: "countryPoem",
|
|
404
|
+
description: "Writes a poem about a country",
|
|
405
|
+
load: async ({ name }) => {
|
|
406
|
+
return `Hello, ${name}!`;
|
|
407
|
+
},
|
|
408
|
+
arguments: [
|
|
409
|
+
{
|
|
410
|
+
name: "name",
|
|
411
|
+
description: "Name of the country",
|
|
412
|
+
required: true,
|
|
413
|
+
complete: async (value) => {
|
|
414
|
+
if (value === "Germ") {
|
|
415
|
+
return {
|
|
416
|
+
values: ["Germany"],
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
values: [],
|
|
422
|
+
};
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
],
|
|
426
|
+
});
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### Automatic prompt argument completion
|
|
430
|
+
|
|
431
|
+
If you provide an `enum` array for an argument, the server will automatically provide completions for the argument.
|
|
432
|
+
|
|
433
|
+
```js
|
|
434
|
+
server.addPrompt({
|
|
435
|
+
name: "countryPoem",
|
|
436
|
+
description: "Writes a poem about a country",
|
|
437
|
+
load: async ({ name }) => {
|
|
438
|
+
return `Hello, ${name}!`;
|
|
439
|
+
},
|
|
440
|
+
arguments: [
|
|
441
|
+
{
|
|
442
|
+
name: "name",
|
|
443
|
+
description: "Name of the country",
|
|
444
|
+
required: true,
|
|
445
|
+
enum: ["Germany", "France", "Italy"],
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
});
|
|
449
|
+
```
|
|
450
|
+
|
|
396
451
|
### Sessions
|
|
397
452
|
|
|
398
453
|
The `session` object is an instance of `FastMCPSession` and it describes active client sessions.
|
package/dist/FastMCP.d.ts
CHANGED
|
@@ -86,6 +86,11 @@ type ContentResult = {
|
|
|
86
86
|
content: Content[];
|
|
87
87
|
isError?: boolean;
|
|
88
88
|
};
|
|
89
|
+
type Completion = {
|
|
90
|
+
values: string[];
|
|
91
|
+
total?: number;
|
|
92
|
+
hasMore?: boolean;
|
|
93
|
+
};
|
|
89
94
|
type Tool<Params extends ToolParameters = ToolParameters> = {
|
|
90
95
|
name: string;
|
|
91
96
|
description?: string;
|
|
@@ -103,21 +108,38 @@ type Resource = {
|
|
|
103
108
|
blob: string;
|
|
104
109
|
}>;
|
|
105
110
|
};
|
|
106
|
-
type
|
|
111
|
+
type ArgumentValueCompleter = (value: string) => Promise<Completion>;
|
|
112
|
+
type InputPromptArgument = Readonly<{
|
|
107
113
|
name: string;
|
|
108
114
|
description?: string;
|
|
109
115
|
required?: boolean;
|
|
116
|
+
complete?: ArgumentValueCompleter;
|
|
117
|
+
enum?: string[];
|
|
110
118
|
}>;
|
|
111
|
-
type ArgumentsToObject<T extends
|
|
119
|
+
type ArgumentsToObject<T extends InputPromptArgument[]> = {
|
|
112
120
|
[K in T[number]["name"]]: Extract<T[number], {
|
|
113
121
|
name: K;
|
|
114
122
|
}>["required"] extends true ? string : string | undefined;
|
|
115
123
|
};
|
|
116
|
-
type
|
|
124
|
+
type InputPrompt<Arguments extends InputPromptArgument[] = InputPromptArgument[], Args = ArgumentsToObject<Arguments>> = {
|
|
125
|
+
name: string;
|
|
126
|
+
description?: string;
|
|
127
|
+
arguments?: InputPromptArgument[];
|
|
128
|
+
load: (args: Args) => Promise<string>;
|
|
129
|
+
};
|
|
130
|
+
type PromptArgument = Readonly<{
|
|
117
131
|
name: string;
|
|
118
132
|
description?: string;
|
|
119
|
-
|
|
133
|
+
required?: boolean;
|
|
134
|
+
complete?: ArgumentValueCompleter;
|
|
135
|
+
enum?: string[];
|
|
136
|
+
}>;
|
|
137
|
+
type Prompt<Arguments extends PromptArgument[] = PromptArgument[], Args = ArgumentsToObject<Arguments>> = {
|
|
138
|
+
arguments?: PromptArgument[];
|
|
139
|
+
complete?: (name: string, value: string) => Promise<Completion>;
|
|
140
|
+
description?: string;
|
|
120
141
|
load: (args: Args) => Promise<string>;
|
|
142
|
+
name: string;
|
|
121
143
|
};
|
|
122
144
|
type ServerOptions = {
|
|
123
145
|
name: string;
|
|
@@ -138,6 +160,7 @@ declare class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
138
160
|
resources: Resource[];
|
|
139
161
|
prompts: Prompt[];
|
|
140
162
|
});
|
|
163
|
+
private addPrompt;
|
|
141
164
|
get clientCapabilities(): ClientCapabilities | null;
|
|
142
165
|
get server(): Server;
|
|
143
166
|
connect(transport: Transport): Promise<void>;
|
|
@@ -145,6 +168,7 @@ declare class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
145
168
|
close(): Promise<void>;
|
|
146
169
|
private setupErrorHandling;
|
|
147
170
|
get loggingLevel(): LoggingLevel;
|
|
171
|
+
private setupCompleteHandlers;
|
|
148
172
|
private setupRootsHandlers;
|
|
149
173
|
private setupLoggingHandlers;
|
|
150
174
|
private setupToolHandlers;
|
|
@@ -172,7 +196,7 @@ declare class FastMCP extends FastMCPEventEmitter {
|
|
|
172
196
|
/**
|
|
173
197
|
* Adds a prompt to the server.
|
|
174
198
|
*/
|
|
175
|
-
addPrompt<const Args extends
|
|
199
|
+
addPrompt<const Args extends InputPromptArgument[]>(prompt: InputPrompt<Args>): void;
|
|
176
200
|
/**
|
|
177
201
|
* Starts the server.
|
|
178
202
|
*/
|
|
@@ -191,4 +215,4 @@ declare class FastMCP extends FastMCPEventEmitter {
|
|
|
191
215
|
stop(): Promise<void>;
|
|
192
216
|
}
|
|
193
217
|
|
|
194
|
-
export { FastMCP, FastMCPSession, type SSEServer, UserError, imageContent };
|
|
218
|
+
export { FastMCP, FastMCPSession, type SSEServer, UnexpectedStateError, UserError, imageContent };
|
package/dist/FastMCP.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import {
|
|
5
5
|
CallToolRequestSchema,
|
|
6
|
+
CompleteRequestSchema,
|
|
6
7
|
ErrorCode,
|
|
7
8
|
GetPromptRequestSchema,
|
|
8
9
|
ListPromptsRequestSchema,
|
|
@@ -19,6 +20,7 @@ import { setTimeout as delay } from "timers/promises";
|
|
|
19
20
|
import { readFile } from "fs/promises";
|
|
20
21
|
import { fileTypeFromBuffer } from "file-type";
|
|
21
22
|
import { EventEmitter } from "events";
|
|
23
|
+
import Fuse from "fuse.js";
|
|
22
24
|
import { startSSEServer } from "mcp-proxy";
|
|
23
25
|
var imageContent = async (input) => {
|
|
24
26
|
let rawData;
|
|
@@ -87,6 +89,20 @@ var ContentResultZodSchema = z.object({
|
|
|
87
89
|
content: ContentZodSchema.array(),
|
|
88
90
|
isError: z.boolean().optional()
|
|
89
91
|
}).strict();
|
|
92
|
+
var CompletionZodSchema = z.object({
|
|
93
|
+
/**
|
|
94
|
+
* An array of completion values. Must not exceed 100 items.
|
|
95
|
+
*/
|
|
96
|
+
values: z.array(z.string()).max(100),
|
|
97
|
+
/**
|
|
98
|
+
* The total number of completion options available. This can exceed the number of values actually sent in the response.
|
|
99
|
+
*/
|
|
100
|
+
total: z.optional(z.number().int()),
|
|
101
|
+
/**
|
|
102
|
+
* Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
|
|
103
|
+
*/
|
|
104
|
+
hasMore: z.optional(z.boolean())
|
|
105
|
+
});
|
|
90
106
|
var FastMCPSessionEventEmitterBase = EventEmitter;
|
|
91
107
|
var FastMCPSessionEventEmitter = class extends FastMCPSessionEventEmitterBase {
|
|
92
108
|
};
|
|
@@ -96,6 +112,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
96
112
|
#server;
|
|
97
113
|
#clientCapabilities;
|
|
98
114
|
#roots = [];
|
|
115
|
+
#prompts = [];
|
|
99
116
|
constructor({
|
|
100
117
|
name,
|
|
101
118
|
version,
|
|
@@ -111,6 +128,9 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
111
128
|
this.#capabilities.resources = {};
|
|
112
129
|
}
|
|
113
130
|
if (prompts.length) {
|
|
131
|
+
for (const prompt of prompts) {
|
|
132
|
+
this.addPrompt(prompt);
|
|
133
|
+
}
|
|
114
134
|
this.#capabilities.prompts = {};
|
|
115
135
|
}
|
|
116
136
|
this.#capabilities.logging = {};
|
|
@@ -121,6 +141,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
121
141
|
this.setupErrorHandling();
|
|
122
142
|
this.setupLoggingHandlers();
|
|
123
143
|
this.setupRootsHandlers();
|
|
144
|
+
this.setupCompleteHandlers();
|
|
124
145
|
if (tools.length) {
|
|
125
146
|
this.setupToolHandlers(tools);
|
|
126
147
|
}
|
|
@@ -131,6 +152,40 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
131
152
|
this.setupPromptHandlers(prompts);
|
|
132
153
|
}
|
|
133
154
|
}
|
|
155
|
+
addPrompt(inputPrompt) {
|
|
156
|
+
const completers = {};
|
|
157
|
+
const enums = {};
|
|
158
|
+
for (const argument of inputPrompt.arguments ?? []) {
|
|
159
|
+
if (argument.complete) {
|
|
160
|
+
completers[argument.name] = argument.complete;
|
|
161
|
+
}
|
|
162
|
+
if (argument.enum) {
|
|
163
|
+
enums[argument.name] = argument.enum;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const prompt = {
|
|
167
|
+
...inputPrompt,
|
|
168
|
+
complete: async (name, value) => {
|
|
169
|
+
if (completers[name]) {
|
|
170
|
+
return await completers[name](value);
|
|
171
|
+
}
|
|
172
|
+
if (enums[name]) {
|
|
173
|
+
const fuse = new Fuse(enums[name], {
|
|
174
|
+
keys: ["value"]
|
|
175
|
+
});
|
|
176
|
+
const result = fuse.search(value);
|
|
177
|
+
return {
|
|
178
|
+
values: result.map((item) => item.item),
|
|
179
|
+
total: result.length
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
values: []
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
this.#prompts.push(prompt);
|
|
188
|
+
}
|
|
134
189
|
get clientCapabilities() {
|
|
135
190
|
return this.#clientCapabilities ?? null;
|
|
136
191
|
}
|
|
@@ -186,6 +241,37 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
186
241
|
get loggingLevel() {
|
|
187
242
|
return this.#loggingLevel;
|
|
188
243
|
}
|
|
244
|
+
setupCompleteHandlers() {
|
|
245
|
+
this.#server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
|
246
|
+
if (request.params.ref.type === "ref/prompt") {
|
|
247
|
+
const prompt = this.#prompts.find(
|
|
248
|
+
(prompt2) => prompt2.name === request.params.ref.name
|
|
249
|
+
);
|
|
250
|
+
if (!prompt) {
|
|
251
|
+
throw new UnexpectedStateError("Unknown prompt", {
|
|
252
|
+
request
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (!prompt.complete) {
|
|
256
|
+
throw new UnexpectedStateError("Prompt does not support completion", {
|
|
257
|
+
request
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const completion = CompletionZodSchema.parse(
|
|
261
|
+
await prompt.complete(
|
|
262
|
+
request.params.argument.name,
|
|
263
|
+
request.params.argument.value
|
|
264
|
+
)
|
|
265
|
+
);
|
|
266
|
+
return {
|
|
267
|
+
completion
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
throw new UnexpectedStateError("Unexpected completion request", {
|
|
271
|
+
request
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
}
|
|
189
275
|
setupRootsHandlers() {
|
|
190
276
|
this.#server.setNotificationHandler(
|
|
191
277
|
RootsListChangedNotificationSchema,
|
|
@@ -371,7 +457,8 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
371
457
|
return {
|
|
372
458
|
name: prompt.name,
|
|
373
459
|
description: prompt.description,
|
|
374
|
-
arguments: prompt.arguments
|
|
460
|
+
arguments: prompt.arguments,
|
|
461
|
+
complete: prompt.complete
|
|
375
462
|
};
|
|
376
463
|
})
|
|
377
464
|
};
|
|
@@ -387,14 +474,12 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
387
474
|
);
|
|
388
475
|
}
|
|
389
476
|
const args = request.params.arguments;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
);
|
|
397
|
-
}
|
|
477
|
+
for (const arg of prompt.arguments ?? []) {
|
|
478
|
+
if (arg.required && !(args && arg.name in args)) {
|
|
479
|
+
throw new McpError(
|
|
480
|
+
ErrorCode.InvalidRequest,
|
|
481
|
+
`Missing required argument: ${arg.name}`
|
|
482
|
+
);
|
|
398
483
|
}
|
|
399
484
|
}
|
|
400
485
|
let result;
|
|
@@ -519,6 +604,7 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
519
604
|
export {
|
|
520
605
|
FastMCP,
|
|
521
606
|
FastMCPSession,
|
|
607
|
+
UnexpectedStateError,
|
|
522
608
|
UserError,
|
|
523
609
|
imageContent
|
|
524
610
|
};
|
package/dist/FastMCP.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/FastMCP.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ClientCapabilities,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n Root,\n RootsListChangedNotificationSchema,\n ServerCapabilities,\n SetLevelRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport { z } from \"zod\";\nimport { setTimeout as delay } from \"timers/promises\";\nimport { readFile } from \"fs/promises\";\nimport { fileTypeFromBuffer } from \"file-type\";\nimport { StrictEventEmitter } from \"strict-event-emitter-types\";\nimport { EventEmitter } from \"events\";\nimport { startSSEServer } from \"mcp-proxy\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nexport type SSEServer = {\n close: () => Promise<void>;\n};\n\ntype FastMCPEvents = {\n connect: (event: { session: FastMCPSession }) => void;\n disconnect: (event: { session: FastMCPSession }) => void;\n};\n\ntype FastMCPSessionEvents = {\n rootsChanged: (event: { roots: Root[] }) => void;\n error: (event: { error: Error }) => void;\n};\n\n/**\n * Generates an image content object from a URL, file path, or buffer.\n */\nexport const imageContent = async (\n input: { url: string } | { path: string } | { buffer: Buffer },\n): Promise<ImageContent> => {\n let rawData: Buffer;\n\n if (\"url\" in input) {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch image from URL: ${response.statusText}`);\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } else if (\"path\" in input) {\n rawData = await readFile(input.path);\n } else if (\"buffer\" in input) {\n rawData = input.buffer;\n } else {\n throw new Error(\n \"Invalid input: Provide a valid 'url', 'path', or 'buffer'\",\n );\n }\n\n const mimeType = await fileTypeFromBuffer(rawData);\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n type: \"image\",\n data: base64Data,\n mimeType: mimeType?.mime ?? \"image/png\",\n } as const;\n};\n\nabstract class FastMCPError extends Error {\n public constructor(message?: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\ntype Extra = unknown;\n\ntype Extras = Record<string, Extra>;\n\nclass UnexpectedStateError extends FastMCPError {\n public extras?: Extras;\n\n public constructor(message: string, extras?: Extras) {\n super(message);\n this.name = new.target.name;\n this.extras = extras;\n }\n}\n\n/**\n * An error that is meant to be surfaced to the user.\n */\nexport class UserError extends UnexpectedStateError {}\n\ntype ToolParameters = z.ZodTypeAny;\n\ntype Literal = boolean | null | number | string | undefined;\n\ntype SerializableValue =\n | Literal\n | SerializableValue[]\n | { [key: string]: SerializableValue };\n\ntype Progress = {\n /**\n * The progress thus far. This should increase every time progress is made, even if the total is unknown.\n */\n progress: number;\n /**\n * Total number of items to process (or total progress required), if known.\n */\n total?: number;\n};\n\ntype Context = {\n reportProgress: (progress: Progress) => Promise<void>;\n log: {\n debug: (message: string, data?: SerializableValue) => void;\n error: (message: string, data?: SerializableValue) => void;\n info: (message: string, data?: SerializableValue) => void;\n warn: (message: string, data?: SerializableValue) => void;\n };\n};\n\ntype TextContent = {\n type: \"text\";\n text: string;\n};\n\nconst TextContentZodSchema = z\n .object({\n type: z.literal(\"text\"),\n /**\n * The text content of the message.\n */\n text: z.string(),\n })\n .strict() satisfies z.ZodType<TextContent>;\n\ntype ImageContent = {\n type: \"image\";\n data: string;\n mimeType: string;\n};\n\nconst ImageContentZodSchema = z\n .object({\n type: z.literal(\"image\"),\n /**\n * The base64-encoded image data.\n */\n data: z.string().base64(),\n /**\n * The MIME type of the image. Different providers may support different image types.\n */\n mimeType: z.string(),\n })\n .strict() satisfies z.ZodType<ImageContent>;\n\ntype Content = TextContent | ImageContent;\n\nconst ContentZodSchema = z.discriminatedUnion(\"type\", [\n TextContentZodSchema,\n ImageContentZodSchema,\n]) satisfies z.ZodType<Content>;\n\ntype ContentResult = {\n content: Content[];\n isError?: boolean;\n};\n\nconst ContentResultZodSchema = z\n .object({\n content: ContentZodSchema.array(),\n isError: z.boolean().optional(),\n })\n .strict() satisfies z.ZodType<ContentResult>;\n\ntype Tool<Params extends ToolParameters = ToolParameters> = {\n name: string;\n description?: string;\n parameters?: Params;\n execute: (\n args: z.infer<Params>,\n context: Context,\n ) => Promise<string | ContentResult | TextContent | ImageContent>;\n};\n\ntype Resource = {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n load: () => Promise<{ text: string } | { blob: string }>;\n};\n\ntype PromptArgument = Readonly<{\n name: string;\n description?: string;\n required?: boolean;\n}>;\n\ntype ArgumentsToObject<T extends PromptArgument[]> = {\n [K in T[number][\"name\"]]: Extract<\n T[number],\n { name: K }\n >[\"required\"] extends true\n ? string\n : string | undefined;\n};\n\ntype Prompt<\n Arguments extends PromptArgument[] = PromptArgument[],\n Args = ArgumentsToObject<Arguments>,\n> = {\n name: string;\n description?: string;\n arguments?: Arguments;\n load: (args: Args) => Promise<string>;\n};\n\ntype ServerOptions = {\n name: string;\n version: `${number}.${number}.${number}`;\n};\n\ntype LoggingLevel =\n | \"debug\"\n | \"info\"\n | \"notice\"\n | \"warning\"\n | \"error\"\n | \"critical\"\n | \"alert\"\n | \"emergency\";\n\nconst FastMCPSessionEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPSessionEvents>;\n} = EventEmitter;\n\nclass FastMCPSessionEventEmitter extends FastMCPSessionEventEmitterBase {}\n\nexport class FastMCPSession extends FastMCPSessionEventEmitter {\n #capabilities: ServerCapabilities = {};\n #loggingLevel: LoggingLevel = \"info\";\n #server: Server;\n #clientCapabilities?: ClientCapabilities;\n #roots: Root[] = [];\n\n constructor({\n name,\n version,\n tools,\n resources,\n prompts,\n }: {\n name: string;\n version: string;\n tools: Tool[];\n resources: Resource[];\n prompts: Prompt[];\n }) {\n super();\n\n if (tools.length) {\n this.#capabilities.tools = {};\n }\n\n if (resources.length) {\n this.#capabilities.resources = {};\n }\n\n if (prompts.length) {\n this.#capabilities.prompts = {};\n }\n\n this.#capabilities.logging = {};\n\n this.#server = new Server(\n { name: name, version: version },\n { capabilities: this.#capabilities },\n );\n\n this.setupErrorHandling();\n this.setupLoggingHandlers();\n this.setupRootsHandlers();\n\n if (tools.length) {\n this.setupToolHandlers(tools);\n }\n\n if (resources.length) {\n this.setupResourceHandlers(resources);\n }\n\n if (prompts.length) {\n this.setupPromptHandlers(prompts);\n }\n }\n\n public get clientCapabilities(): ClientCapabilities | null {\n return this.#clientCapabilities ?? null;\n }\n\n public get server(): Server {\n return this.#server;\n }\n\n #pingInterval: ReturnType<typeof setInterval> | null = null;\n\n public async connect(transport: Transport) {\n if (this.#server.transport) {\n throw new UnexpectedStateError(\"Server is already connected\");\n }\n\n await this.#server.connect(transport);\n\n let attempt = 0;\n\n while (attempt++ < 10) {\n const capabilities = await this.#server.getClientCapabilities();\n\n if (capabilities) {\n this.#clientCapabilities = capabilities;\n\n break;\n }\n\n await delay(100);\n }\n\n if (!this.#clientCapabilities) {\n throw new UnexpectedStateError(\"Server did not connect\");\n }\n\n if (this.#clientCapabilities?.roots) {\n const roots = await this.#server.listRoots();\n\n this.#roots = roots.roots;\n }\n\n this.#pingInterval = setInterval(async () => {\n try {\n await this.#server.ping();\n } catch (error) {\n this.emit(\"error\", {\n error: error as Error,\n });\n }\n }, 1000);\n }\n\n public get roots(): Root[] {\n return this.#roots;\n }\n\n public async close() {\n if (this.#pingInterval) {\n clearInterval(this.#pingInterval);\n }\n\n await this.#server.close();\n }\n\n private setupErrorHandling() {\n this.#server.onerror = (error) => {\n console.error(\"[MCP Error]\", error);\n };\n }\n\n public get loggingLevel(): LoggingLevel {\n return this.#loggingLevel;\n }\n\n private setupRootsHandlers() {\n this.#server.setNotificationHandler(\n RootsListChangedNotificationSchema,\n () => {\n this.#server.listRoots().then((roots) => {\n this.#roots = roots.roots;\n\n this.emit(\"rootsChanged\", {\n roots: roots.roots,\n });\n });\n },\n );\n }\n\n private setupLoggingHandlers() {\n this.#server.setRequestHandler(SetLevelRequestSchema, (request) => {\n this.#loggingLevel = request.params.level;\n\n return {};\n });\n }\n\n private setupToolHandlers(tools: Tool[]) {\n this.#server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: tools.map((tool) => {\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters\n ? zodToJsonSchema(tool.parameters)\n : undefined,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const tool = tools.find((tool) => tool.name === request.params.name);\n\n if (!tool) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n let args: any = undefined;\n\n if (tool.parameters) {\n const parsed = tool.parameters.safeParse(request.params.arguments);\n\n if (!parsed.success) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Invalid ${request.params.name} arguments`,\n );\n }\n\n args = parsed.data;\n }\n\n const progressToken = request.params?._meta?.progressToken;\n\n let result: ContentResult;\n\n try {\n const reportProgress = async (progress: Progress) => {\n await this.#server.notification({\n method: \"notifications/progress\",\n params: {\n ...progress,\n progressToken,\n },\n });\n };\n\n const log = {\n debug: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"debug\",\n data: {\n message,\n context,\n },\n });\n },\n error: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"error\",\n data: {\n message,\n context,\n },\n });\n },\n info: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"info\",\n data: {\n message,\n context,\n },\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"warning\",\n data: {\n message,\n context,\n },\n });\n },\n };\n\n const maybeStringResult = await tool.execute(args, {\n reportProgress,\n log,\n });\n\n if (typeof maybeStringResult === \"string\") {\n result = ContentResultZodSchema.parse({\n content: [{ type: \"text\", text: maybeStringResult }],\n });\n } else if (\"type\" in maybeStringResult) {\n result = ContentResultZodSchema.parse({\n content: [maybeStringResult],\n });\n } else {\n result = ContentResultZodSchema.parse(maybeStringResult);\n }\n } catch (error) {\n if (error instanceof UserError) {\n return {\n content: [{ type: \"text\", text: error.message }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: `Error: ${error}` }],\n isError: true,\n };\n }\n\n return result;\n });\n }\n\n private setupResourceHandlers(resources: Resource[]) {\n this.#server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: resources.map((resource) => {\n return {\n uri: resource.uri,\n name: resource.name,\n mimeType: resource.mimeType,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request) => {\n const resource = resources.find(\n (resource) => resource.uri === request.params.uri,\n );\n\n if (!resource) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown resource: ${request.params.uri}`,\n );\n }\n\n let result: Awaited<ReturnType<Resource[\"load\"]>>;\n\n try {\n result = await resource.load();\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error reading resource: ${error}`,\n {\n uri: resource.uri,\n },\n );\n }\n\n return {\n contents: [\n {\n uri: resource.uri,\n mimeType: resource.mimeType,\n ...result,\n },\n ],\n };\n },\n );\n }\n\n private setupPromptHandlers(prompts: Prompt[]) {\n this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: prompts.map((prompt) => {\n return {\n name: prompt.name,\n description: prompt.description,\n arguments: prompt.arguments,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const prompt = prompts.find(\n (prompt) => prompt.name === request.params.name,\n );\n\n if (!prompt) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown prompt: ${request.params.name}`,\n );\n }\n\n const args = request.params.arguments;\n\n if (prompt.arguments) {\n for (const arg of prompt.arguments) {\n if (arg.required && !(args && arg.name in args)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Missing required argument: ${arg.name}`,\n );\n }\n }\n }\n\n let result: Awaited<ReturnType<Prompt[\"load\"]>>;\n\n try {\n result = await prompt.load(args as Record<string, string | undefined>);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error loading prompt: ${error}`,\n );\n }\n\n return {\n description: prompt.description,\n messages: [\n {\n role: \"user\",\n content: { type: \"text\", text: result },\n },\n ],\n };\n });\n }\n}\n\nconst FastMCPEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPEvents>;\n} = EventEmitter;\n\nclass FastMCPEventEmitter extends FastMCPEventEmitterBase {}\n\nexport class FastMCP extends FastMCPEventEmitter {\n #options: ServerOptions;\n #prompts: Prompt[] = [];\n #resources: Resource[] = [];\n #sessions: FastMCPSession[] = [];\n #sseServer: SSEServer | null = null;\n #tools: Tool[] = [];\n\n constructor(public options: ServerOptions) {\n super();\n\n this.#options = options;\n }\n\n public get sessions(): FastMCPSession[] {\n return this.#sessions;\n }\n\n /**\n * Adds a tool to the server.\n */\n public addTool<Params extends ToolParameters>(tool: Tool<Params>) {\n this.#tools.push(tool as unknown as Tool);\n }\n\n /**\n * Adds a resource to the server.\n */\n public addResource(resource: Resource) {\n this.#resources.push(resource);\n }\n\n /**\n * Adds a prompt to the server.\n */\n public addPrompt<const Args extends PromptArgument[]>(prompt: Prompt<Args>) {\n this.#prompts.push(prompt);\n }\n\n /**\n * Starts the server.\n */\n public async start(\n options:\n | { transportType: \"stdio\" }\n | {\n transportType: \"sse\";\n sse: { endpoint: `/${string}`; port: number };\n } = {\n transportType: \"stdio\",\n },\n ) {\n if (options.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n\n const session = new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n\n await session.connect(transport);\n\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session,\n });\n\n console.error(`server is running on stdio`);\n } else if (options.transportType === \"sse\") {\n this.#sseServer = await startSSEServer<FastMCPSession>({\n endpoint: options.sse.endpoint as `/${string}`,\n port: options.sse.port,\n createServer: async () => {\n return new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n },\n onClose: (session) => {\n this.emit(\"disconnect\", {\n session,\n });\n },\n onConnect: async (session) => {\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session,\n });\n },\n });\n\n console.error(\n `server is running on SSE at http://localhost:${options.sse.port}${options.sse.endpoint}`,\n );\n } else {\n throw new Error(\"Invalid transport type\");\n }\n }\n\n /**\n * Stops the server.\n */\n public async stop() {\n if (this.#sseServer) {\n this.#sseServer.close();\n }\n }\n}\n"],"mappings":"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","names":["tool","resource","prompt"]}
|
|
1
|
+
{"version":3,"sources":["../src/FastMCP.ts"],"sourcesContent":["import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n CallToolRequestSchema,\n ClientCapabilities,\n CompleteRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n Root,\n RootsListChangedNotificationSchema,\n ServerCapabilities,\n SetLevelRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport { z } from \"zod\";\nimport { setTimeout as delay } from \"timers/promises\";\nimport { readFile } from \"fs/promises\";\nimport { fileTypeFromBuffer } from \"file-type\";\nimport { StrictEventEmitter } from \"strict-event-emitter-types\";\nimport { EventEmitter } from \"events\";\nimport Fuse from \"fuse.js\";\nimport { startSSEServer } from \"mcp-proxy\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\n\nexport type SSEServer = {\n close: () => Promise<void>;\n};\n\ntype FastMCPEvents = {\n connect: (event: { session: FastMCPSession }) => void;\n disconnect: (event: { session: FastMCPSession }) => void;\n};\n\ntype FastMCPSessionEvents = {\n rootsChanged: (event: { roots: Root[] }) => void;\n error: (event: { error: Error }) => void;\n};\n\n/**\n * Generates an image content object from a URL, file path, or buffer.\n */\nexport const imageContent = async (\n input: { url: string } | { path: string } | { buffer: Buffer },\n): Promise<ImageContent> => {\n let rawData: Buffer;\n\n if (\"url\" in input) {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(`Failed to fetch image from URL: ${response.statusText}`);\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } else if (\"path\" in input) {\n rawData = await readFile(input.path);\n } else if (\"buffer\" in input) {\n rawData = input.buffer;\n } else {\n throw new Error(\n \"Invalid input: Provide a valid 'url', 'path', or 'buffer'\",\n );\n }\n\n const mimeType = await fileTypeFromBuffer(rawData);\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n type: \"image\",\n data: base64Data,\n mimeType: mimeType?.mime ?? \"image/png\",\n } as const;\n};\n\nabstract class FastMCPError extends Error {\n public constructor(message?: string) {\n super(message);\n this.name = new.target.name;\n }\n}\n\ntype Extra = unknown;\n\ntype Extras = Record<string, Extra>;\n\nexport class UnexpectedStateError extends FastMCPError {\n public extras?: Extras;\n\n public constructor(message: string, extras?: Extras) {\n super(message);\n this.name = new.target.name;\n this.extras = extras;\n }\n}\n\n/**\n * An error that is meant to be surfaced to the user.\n */\nexport class UserError extends UnexpectedStateError {}\n\ntype ToolParameters = z.ZodTypeAny;\n\ntype Literal = boolean | null | number | string | undefined;\n\ntype SerializableValue =\n | Literal\n | SerializableValue[]\n | { [key: string]: SerializableValue };\n\ntype Progress = {\n /**\n * The progress thus far. This should increase every time progress is made, even if the total is unknown.\n */\n progress: number;\n /**\n * Total number of items to process (or total progress required), if known.\n */\n total?: number;\n};\n\ntype Context = {\n reportProgress: (progress: Progress) => Promise<void>;\n log: {\n debug: (message: string, data?: SerializableValue) => void;\n error: (message: string, data?: SerializableValue) => void;\n info: (message: string, data?: SerializableValue) => void;\n warn: (message: string, data?: SerializableValue) => void;\n };\n};\n\ntype TextContent = {\n type: \"text\";\n text: string;\n};\n\nconst TextContentZodSchema = z\n .object({\n type: z.literal(\"text\"),\n /**\n * The text content of the message.\n */\n text: z.string(),\n })\n .strict() satisfies z.ZodType<TextContent>;\n\ntype ImageContent = {\n type: \"image\";\n data: string;\n mimeType: string;\n};\n\nconst ImageContentZodSchema = z\n .object({\n type: z.literal(\"image\"),\n /**\n * The base64-encoded image data.\n */\n data: z.string().base64(),\n /**\n * The MIME type of the image. Different providers may support different image types.\n */\n mimeType: z.string(),\n })\n .strict() satisfies z.ZodType<ImageContent>;\n\ntype Content = TextContent | ImageContent;\n\nconst ContentZodSchema = z.discriminatedUnion(\"type\", [\n TextContentZodSchema,\n ImageContentZodSchema,\n]) satisfies z.ZodType<Content>;\n\ntype ContentResult = {\n content: Content[];\n isError?: boolean;\n};\n\nconst ContentResultZodSchema = z\n .object({\n content: ContentZodSchema.array(),\n isError: z.boolean().optional(),\n })\n .strict() satisfies z.ZodType<ContentResult>;\n\ntype Completion = {\n values: string[];\n total?: number;\n hasMore?: boolean;\n};\n\n/**\n * https://github.com/modelcontextprotocol/typescript-sdk/blob/3164da64d085ec4e022ae881329eee7b72f208d4/src/types.ts#L983-L1003\n */\nconst CompletionZodSchema = z.object({\n /**\n * An array of completion values. Must not exceed 100 items.\n */\n values: z.array(z.string()).max(100),\n /**\n * The total number of completion options available. This can exceed the number of values actually sent in the response.\n */\n total: z.optional(z.number().int()),\n /**\n * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.\n */\n hasMore: z.optional(z.boolean()),\n}) satisfies z.ZodType<Completion>;\n\ntype Tool<Params extends ToolParameters = ToolParameters> = {\n name: string;\n description?: string;\n parameters?: Params;\n execute: (\n args: z.infer<Params>,\n context: Context,\n ) => Promise<string | ContentResult | TextContent | ImageContent>;\n};\n\ntype Resource = {\n uri: string;\n name: string;\n description?: string;\n mimeType?: string;\n load: () => Promise<{ text: string } | { blob: string }>;\n};\n\ntype ArgumentValueCompleter = (value: string) => Promise<Completion>;\n\ntype InputPromptArgument = Readonly<{\n name: string;\n description?: string;\n required?: boolean;\n complete?: ArgumentValueCompleter;\n enum?: string[];\n}>;\n\ntype ArgumentsToObject<T extends InputPromptArgument[]> = {\n [K in T[number][\"name\"]]: Extract<\n T[number],\n { name: K }\n >[\"required\"] extends true\n ? string\n : string | undefined;\n};\n\ntype InputPrompt<\n Arguments extends InputPromptArgument[] = InputPromptArgument[],\n Args = ArgumentsToObject<Arguments>,\n> = {\n name: string;\n description?: string;\n arguments?: InputPromptArgument[];\n load: (args: Args) => Promise<string>;\n};\n\ntype PromptArgument = Readonly<{\n name: string;\n description?: string;\n required?: boolean;\n complete?: ArgumentValueCompleter;\n enum?: string[];\n}>;\n\ntype Prompt<\n Arguments extends PromptArgument[] = PromptArgument[],\n Args = ArgumentsToObject<Arguments>,\n> = {\n arguments?: PromptArgument[];\n complete?: (name: string, value: string) => Promise<Completion>;\n description?: string;\n load: (args: Args) => Promise<string>;\n name: string;\n};\n\ntype ServerOptions = {\n name: string;\n version: `${number}.${number}.${number}`;\n};\n\ntype LoggingLevel =\n | \"debug\"\n | \"info\"\n | \"notice\"\n | \"warning\"\n | \"error\"\n | \"critical\"\n | \"alert\"\n | \"emergency\";\n\nconst FastMCPSessionEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPSessionEvents>;\n} = EventEmitter;\n\nclass FastMCPSessionEventEmitter extends FastMCPSessionEventEmitterBase {}\n\nexport class FastMCPSession extends FastMCPSessionEventEmitter {\n #capabilities: ServerCapabilities = {};\n #loggingLevel: LoggingLevel = \"info\";\n #server: Server;\n #clientCapabilities?: ClientCapabilities;\n #roots: Root[] = [];\n #prompts: Prompt[] = [];\n\n constructor({\n name,\n version,\n tools,\n resources,\n prompts,\n }: {\n name: string;\n version: string;\n tools: Tool[];\n resources: Resource[];\n prompts: Prompt[];\n }) {\n super();\n\n if (tools.length) {\n this.#capabilities.tools = {};\n }\n\n if (resources.length) {\n this.#capabilities.resources = {};\n }\n\n if (prompts.length) {\n for (const prompt of prompts) {\n this.addPrompt(prompt);\n }\n\n this.#capabilities.prompts = {};\n }\n\n this.#capabilities.logging = {};\n\n this.#server = new Server(\n { name: name, version: version },\n { capabilities: this.#capabilities },\n );\n\n this.setupErrorHandling();\n this.setupLoggingHandlers();\n this.setupRootsHandlers();\n this.setupCompleteHandlers();\n\n if (tools.length) {\n this.setupToolHandlers(tools);\n }\n\n if (resources.length) {\n this.setupResourceHandlers(resources);\n }\n\n if (prompts.length) {\n this.setupPromptHandlers(prompts);\n }\n }\n\n private addPrompt(inputPrompt: InputPrompt) {\n const completers: Record<string, ArgumentValueCompleter> = {};\n const enums: Record<string, string[]> = {};\n\n for (const argument of inputPrompt.arguments ?? []) {\n if (argument.complete) {\n completers[argument.name] = argument.complete;\n }\n\n if (argument.enum) {\n enums[argument.name] = argument.enum;\n }\n }\n\n const prompt = {\n ...inputPrompt,\n complete: async (name: string, value: string) => {\n if (completers[name]) {\n return await completers[name](value);\n }\n\n if (enums[name]) {\n const fuse = new Fuse(enums[name], {\n keys: [\"value\"],\n });\n\n const result = fuse.search(value);\n\n return {\n values: result.map((item) => item.item),\n total: result.length,\n };\n }\n\n return {\n values: [],\n };\n },\n };\n\n this.#prompts.push(prompt);\n }\n\n public get clientCapabilities(): ClientCapabilities | null {\n return this.#clientCapabilities ?? null;\n }\n\n public get server(): Server {\n return this.#server;\n }\n\n #pingInterval: ReturnType<typeof setInterval> | null = null;\n\n public async connect(transport: Transport) {\n if (this.#server.transport) {\n throw new UnexpectedStateError(\"Server is already connected\");\n }\n\n await this.#server.connect(transport);\n\n let attempt = 0;\n\n while (attempt++ < 10) {\n const capabilities = await this.#server.getClientCapabilities();\n\n if (capabilities) {\n this.#clientCapabilities = capabilities;\n\n break;\n }\n\n await delay(100);\n }\n\n if (!this.#clientCapabilities) {\n throw new UnexpectedStateError(\"Server did not connect\");\n }\n\n if (this.#clientCapabilities?.roots) {\n const roots = await this.#server.listRoots();\n\n this.#roots = roots.roots;\n }\n\n this.#pingInterval = setInterval(async () => {\n try {\n await this.#server.ping();\n } catch (error) {\n this.emit(\"error\", {\n error: error as Error,\n });\n }\n }, 1000);\n }\n\n public get roots(): Root[] {\n return this.#roots;\n }\n\n public async close() {\n if (this.#pingInterval) {\n clearInterval(this.#pingInterval);\n }\n\n await this.#server.close();\n }\n\n private setupErrorHandling() {\n this.#server.onerror = (error) => {\n console.error(\"[MCP Error]\", error);\n };\n }\n\n public get loggingLevel(): LoggingLevel {\n return this.#loggingLevel;\n }\n\n private setupCompleteHandlers() {\n this.#server.setRequestHandler(CompleteRequestSchema, async (request) => {\n if (request.params.ref.type === \"ref/prompt\") {\n const prompt = this.#prompts.find(\n (prompt) => prompt.name === request.params.ref.name,\n );\n\n if (!prompt) {\n throw new UnexpectedStateError(\"Unknown prompt\", {\n request,\n });\n }\n\n if (!prompt.complete) {\n throw new UnexpectedStateError(\"Prompt does not support completion\", {\n request,\n });\n }\n\n const completion = CompletionZodSchema.parse(\n await prompt.complete(\n request.params.argument.name,\n request.params.argument.value,\n ),\n );\n\n return {\n completion,\n };\n }\n\n throw new UnexpectedStateError(\"Unexpected completion request\", {\n request,\n });\n });\n }\n\n private setupRootsHandlers() {\n this.#server.setNotificationHandler(\n RootsListChangedNotificationSchema,\n () => {\n this.#server.listRoots().then((roots) => {\n this.#roots = roots.roots;\n\n this.emit(\"rootsChanged\", {\n roots: roots.roots,\n });\n });\n },\n );\n }\n\n private setupLoggingHandlers() {\n this.#server.setRequestHandler(SetLevelRequestSchema, (request) => {\n this.#loggingLevel = request.params.level;\n\n return {};\n });\n }\n\n private setupToolHandlers(tools: Tool[]) {\n this.#server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: tools.map((tool) => {\n return {\n name: tool.name,\n description: tool.description,\n inputSchema: tool.parameters\n ? zodToJsonSchema(tool.parameters)\n : undefined,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const tool = tools.find((tool) => tool.name === request.params.name);\n\n if (!tool) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${request.params.name}`,\n );\n }\n\n let args: any = undefined;\n\n if (tool.parameters) {\n const parsed = tool.parameters.safeParse(request.params.arguments);\n\n if (!parsed.success) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Invalid ${request.params.name} arguments`,\n );\n }\n\n args = parsed.data;\n }\n\n const progressToken = request.params?._meta?.progressToken;\n\n let result: ContentResult;\n\n try {\n const reportProgress = async (progress: Progress) => {\n await this.#server.notification({\n method: \"notifications/progress\",\n params: {\n ...progress,\n progressToken,\n },\n });\n };\n\n const log = {\n debug: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"debug\",\n data: {\n message,\n context,\n },\n });\n },\n error: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"error\",\n data: {\n message,\n context,\n },\n });\n },\n info: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"info\",\n data: {\n message,\n context,\n },\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n level: \"warning\",\n data: {\n message,\n context,\n },\n });\n },\n };\n\n const maybeStringResult = await tool.execute(args, {\n reportProgress,\n log,\n });\n\n if (typeof maybeStringResult === \"string\") {\n result = ContentResultZodSchema.parse({\n content: [{ type: \"text\", text: maybeStringResult }],\n });\n } else if (\"type\" in maybeStringResult) {\n result = ContentResultZodSchema.parse({\n content: [maybeStringResult],\n });\n } else {\n result = ContentResultZodSchema.parse(maybeStringResult);\n }\n } catch (error) {\n if (error instanceof UserError) {\n return {\n content: [{ type: \"text\", text: error.message }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: `Error: ${error}` }],\n isError: true,\n };\n }\n\n return result;\n });\n }\n\n private setupResourceHandlers(resources: Resource[]) {\n this.#server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: resources.map((resource) => {\n return {\n uri: resource.uri,\n name: resource.name,\n mimeType: resource.mimeType,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request) => {\n const resource = resources.find(\n (resource) => resource.uri === request.params.uri,\n );\n\n if (!resource) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown resource: ${request.params.uri}`,\n );\n }\n\n let result: Awaited<ReturnType<Resource[\"load\"]>>;\n\n try {\n result = await resource.load();\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error reading resource: ${error}`,\n {\n uri: resource.uri,\n },\n );\n }\n\n return {\n contents: [\n {\n uri: resource.uri,\n mimeType: resource.mimeType,\n ...result,\n },\n ],\n };\n },\n );\n }\n\n private setupPromptHandlers(prompts: Prompt[]) {\n this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: prompts.map((prompt) => {\n return {\n name: prompt.name,\n description: prompt.description,\n arguments: prompt.arguments,\n complete: prompt.complete,\n };\n }),\n };\n });\n\n this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => {\n const prompt = prompts.find(\n (prompt) => prompt.name === request.params.name,\n );\n\n if (!prompt) {\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Unknown prompt: ${request.params.name}`,\n );\n }\n\n const args = request.params.arguments;\n\n for (const arg of prompt.arguments ?? []) {\n if (arg.required && !(args && arg.name in args)) {\n throw new McpError(\n ErrorCode.InvalidRequest,\n `Missing required argument: ${arg.name}`,\n );\n }\n }\n\n let result: Awaited<ReturnType<Prompt[\"load\"]>>;\n\n try {\n result = await prompt.load(args as Record<string, string | undefined>);\n } catch (error) {\n throw new McpError(\n ErrorCode.InternalError,\n `Error loading prompt: ${error}`,\n );\n }\n\n return {\n description: prompt.description,\n messages: [\n {\n role: \"user\",\n content: { type: \"text\", text: result },\n },\n ],\n };\n });\n }\n}\n\nconst FastMCPEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPEvents>;\n} = EventEmitter;\n\nclass FastMCPEventEmitter extends FastMCPEventEmitterBase {}\n\nexport class FastMCP extends FastMCPEventEmitter {\n #options: ServerOptions;\n #prompts: InputPrompt[] = [];\n #resources: Resource[] = [];\n #sessions: FastMCPSession[] = [];\n #sseServer: SSEServer | null = null;\n #tools: Tool[] = [];\n\n constructor(public options: ServerOptions) {\n super();\n\n this.#options = options;\n }\n\n public get sessions(): FastMCPSession[] {\n return this.#sessions;\n }\n\n /**\n * Adds a tool to the server.\n */\n public addTool<Params extends ToolParameters>(tool: Tool<Params>) {\n this.#tools.push(tool as unknown as Tool);\n }\n\n /**\n * Adds a resource to the server.\n */\n public addResource(resource: Resource) {\n this.#resources.push(resource);\n }\n\n /**\n * Adds a prompt to the server.\n */\n public addPrompt<const Args extends InputPromptArgument[]>(\n prompt: InputPrompt<Args>,\n ) {\n this.#prompts.push(prompt);\n }\n\n /**\n * Starts the server.\n */\n public async start(\n options:\n | { transportType: \"stdio\" }\n | {\n transportType: \"sse\";\n sse: { endpoint: `/${string}`; port: number };\n } = {\n transportType: \"stdio\",\n },\n ) {\n if (options.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n\n const session = new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n\n await session.connect(transport);\n\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session,\n });\n\n console.error(`server is running on stdio`);\n } else if (options.transportType === \"sse\") {\n this.#sseServer = await startSSEServer<FastMCPSession>({\n endpoint: options.sse.endpoint as `/${string}`,\n port: options.sse.port,\n createServer: async () => {\n return new FastMCPSession({\n name: this.#options.name,\n version: this.#options.version,\n tools: this.#tools,\n resources: this.#resources,\n prompts: this.#prompts,\n });\n },\n onClose: (session) => {\n this.emit(\"disconnect\", {\n session,\n });\n },\n onConnect: async (session) => {\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session,\n });\n },\n });\n\n console.error(\n `server is running on SSE at http://localhost:${options.sse.port}${options.sse.endpoint}`,\n );\n } else {\n throw new Error(\"Invalid transport type\");\n }\n }\n\n /**\n * Stops the server.\n */\n public async stop() {\n if (this.#sseServer) {\n this.#sseServer.close();\n }\n }\n}\n"],"mappings":"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","names":["prompt","tool","resource"]}
|
package/jsr.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastmcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"main": "dist/FastMCP.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
25
25
|
"execa": "^9.5.2",
|
|
26
26
|
"file-type": "^19.6.0",
|
|
27
|
+
"fuse.js": "^7.0.0",
|
|
27
28
|
"mcp-proxy": "^2.4.0",
|
|
28
29
|
"strict-event-emitter-types": "^2.0.0",
|
|
29
30
|
"yargs": "^17.7.2",
|
package/src/FastMCP.test.ts
CHANGED
|
@@ -16,11 +16,11 @@ import {
|
|
|
16
16
|
|
|
17
17
|
const runWithTestServer = async ({
|
|
18
18
|
run,
|
|
19
|
-
client:
|
|
20
|
-
|
|
19
|
+
client: createClient,
|
|
20
|
+
server: createServer,
|
|
21
21
|
}: {
|
|
22
|
-
|
|
23
|
-
client?: Client
|
|
22
|
+
server?: () => Promise<FastMCP>;
|
|
23
|
+
client?: () => Promise<Client>;
|
|
24
24
|
run: ({
|
|
25
25
|
client,
|
|
26
26
|
server,
|
|
@@ -32,7 +32,12 @@ const runWithTestServer = async ({
|
|
|
32
32
|
}) => {
|
|
33
33
|
const port = await getRandomPort();
|
|
34
34
|
|
|
35
|
-
const server =
|
|
35
|
+
const server = createServer
|
|
36
|
+
? await createServer()
|
|
37
|
+
: new FastMCP({
|
|
38
|
+
name: "Test",
|
|
39
|
+
version: "1.0.0",
|
|
40
|
+
});
|
|
36
41
|
|
|
37
42
|
await server.start({
|
|
38
43
|
transportType: "sse",
|
|
@@ -43,17 +48,17 @@ const runWithTestServer = async ({
|
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
try {
|
|
46
|
-
const client =
|
|
47
|
-
|
|
48
|
-
new Client(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
const client = createClient
|
|
52
|
+
? await createClient()
|
|
53
|
+
: new Client(
|
|
54
|
+
{
|
|
55
|
+
name: "example-client",
|
|
56
|
+
version: "1.0.0",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
capabilities: {},
|
|
60
|
+
},
|
|
61
|
+
);
|
|
57
62
|
|
|
58
63
|
const transport = new SSEClientTransport(
|
|
59
64
|
new URL(`http://localhost:${port}/sse`),
|
|
@@ -77,7 +82,7 @@ const runWithTestServer = async ({
|
|
|
77
82
|
|
|
78
83
|
test("adds tools", async () => {
|
|
79
84
|
await runWithTestServer({
|
|
80
|
-
|
|
85
|
+
server: async () => {
|
|
81
86
|
const server = new FastMCP({
|
|
82
87
|
name: "Test",
|
|
83
88
|
version: "1.0.0",
|
|
@@ -122,7 +127,7 @@ test("adds tools", async () => {
|
|
|
122
127
|
|
|
123
128
|
test("calls a tool", async () => {
|
|
124
129
|
await runWithTestServer({
|
|
125
|
-
|
|
130
|
+
server: async () => {
|
|
126
131
|
const server = new FastMCP({
|
|
127
132
|
name: "Test",
|
|
128
133
|
version: "1.0.0",
|
|
@@ -160,7 +165,7 @@ test("calls a tool", async () => {
|
|
|
160
165
|
|
|
161
166
|
test("returns a list", async () => {
|
|
162
167
|
await runWithTestServer({
|
|
163
|
-
|
|
168
|
+
server: async () => {
|
|
164
169
|
const server = new FastMCP({
|
|
165
170
|
name: "Test",
|
|
166
171
|
version: "1.0.0",
|
|
@@ -206,7 +211,7 @@ test("returns a list", async () => {
|
|
|
206
211
|
|
|
207
212
|
test("returns an image", async () => {
|
|
208
213
|
await runWithTestServer({
|
|
209
|
-
|
|
214
|
+
server: async () => {
|
|
210
215
|
const server = new FastMCP({
|
|
211
216
|
name: "Test",
|
|
212
217
|
version: "1.0.0",
|
|
@@ -255,7 +260,7 @@ test("returns an image", async () => {
|
|
|
255
260
|
|
|
256
261
|
test("handles UserError errors", async () => {
|
|
257
262
|
await runWithTestServer({
|
|
258
|
-
|
|
263
|
+
server: async () => {
|
|
259
264
|
const server = new FastMCP({
|
|
260
265
|
name: "Test",
|
|
261
266
|
version: "1.0.0",
|
|
@@ -294,7 +299,7 @@ test("handles UserError errors", async () => {
|
|
|
294
299
|
|
|
295
300
|
test("calling an unknown tool throws McpError with MethodNotFound code", async () => {
|
|
296
301
|
await runWithTestServer({
|
|
297
|
-
|
|
302
|
+
server: async () => {
|
|
298
303
|
const server = new FastMCP({
|
|
299
304
|
name: "Test",
|
|
300
305
|
version: "1.0.0",
|
|
@@ -323,7 +328,7 @@ test("calling an unknown tool throws McpError with MethodNotFound code", async (
|
|
|
323
328
|
|
|
324
329
|
test("tracks tool progress", async () => {
|
|
325
330
|
await runWithTestServer({
|
|
326
|
-
|
|
331
|
+
server: async () => {
|
|
327
332
|
const server = new FastMCP({
|
|
328
333
|
name: "Test",
|
|
329
334
|
version: "1.0.0",
|
|
@@ -378,12 +383,6 @@ test("tracks tool progress", async () => {
|
|
|
378
383
|
|
|
379
384
|
test("sets logging levels", async () => {
|
|
380
385
|
await runWithTestServer({
|
|
381
|
-
start: async () => {
|
|
382
|
-
return new FastMCP({
|
|
383
|
-
name: "Test",
|
|
384
|
-
version: "1.0.0",
|
|
385
|
-
});
|
|
386
|
-
},
|
|
387
386
|
run: async ({ client, session }) => {
|
|
388
387
|
await client.setLoggingLevel("debug");
|
|
389
388
|
|
|
@@ -398,7 +397,7 @@ test("sets logging levels", async () => {
|
|
|
398
397
|
|
|
399
398
|
test("sends logging messages to the client", async () => {
|
|
400
399
|
await runWithTestServer({
|
|
401
|
-
|
|
400
|
+
server: async () => {
|
|
402
401
|
const server = new FastMCP({
|
|
403
402
|
name: "Test",
|
|
404
403
|
version: "1.0.0",
|
|
@@ -474,7 +473,7 @@ test("sends logging messages to the client", async () => {
|
|
|
474
473
|
|
|
475
474
|
test("adds resources", async () => {
|
|
476
475
|
await runWithTestServer({
|
|
477
|
-
|
|
476
|
+
server: async () => {
|
|
478
477
|
const server = new FastMCP({
|
|
479
478
|
name: "Test",
|
|
480
479
|
version: "1.0.0",
|
|
@@ -509,7 +508,7 @@ test("adds resources", async () => {
|
|
|
509
508
|
|
|
510
509
|
test("adds prompts", async () => {
|
|
511
510
|
await runWithTestServer({
|
|
512
|
-
|
|
511
|
+
server: async () => {
|
|
513
512
|
const server = new FastMCP({
|
|
514
513
|
name: "Test",
|
|
515
514
|
version: "1.0.0",
|
|
@@ -533,6 +532,26 @@ test("adds prompts", async () => {
|
|
|
533
532
|
return server;
|
|
534
533
|
},
|
|
535
534
|
run: async ({ client }) => {
|
|
535
|
+
expect(
|
|
536
|
+
await client.getPrompt({
|
|
537
|
+
name: "git-commit",
|
|
538
|
+
arguments: {
|
|
539
|
+
changes: "foo",
|
|
540
|
+
},
|
|
541
|
+
}),
|
|
542
|
+
).toEqual({
|
|
543
|
+
description: "Generate a Git commit message",
|
|
544
|
+
messages: [
|
|
545
|
+
{
|
|
546
|
+
role: "user",
|
|
547
|
+
content: {
|
|
548
|
+
type: "text",
|
|
549
|
+
text: "Generate a concise but descriptive commit message for these changes:\n\nfoo",
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
],
|
|
553
|
+
});
|
|
554
|
+
|
|
536
555
|
expect(await client.listPrompts()).toEqual({
|
|
537
556
|
prompts: [
|
|
538
557
|
{
|
|
@@ -666,40 +685,34 @@ test("handles multiple clients", async () => {
|
|
|
666
685
|
});
|
|
667
686
|
|
|
668
687
|
test("session knows about client capabilities", async () => {
|
|
669
|
-
|
|
670
|
-
{
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
capabilities: {
|
|
676
|
-
roots: {
|
|
677
|
-
listChanged: true,
|
|
688
|
+
await runWithTestServer({
|
|
689
|
+
client: async () => {
|
|
690
|
+
const client = new Client(
|
|
691
|
+
{
|
|
692
|
+
name: "example-client",
|
|
693
|
+
version: "1.0.0",
|
|
678
694
|
},
|
|
679
|
-
},
|
|
680
|
-
},
|
|
681
|
-
);
|
|
682
|
-
|
|
683
|
-
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
684
|
-
return {
|
|
685
|
-
roots: [
|
|
686
695
|
{
|
|
687
|
-
|
|
688
|
-
|
|
696
|
+
capabilities: {
|
|
697
|
+
roots: {
|
|
698
|
+
listChanged: true,
|
|
699
|
+
},
|
|
700
|
+
},
|
|
689
701
|
},
|
|
690
|
-
|
|
691
|
-
};
|
|
692
|
-
});
|
|
702
|
+
);
|
|
693
703
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
704
|
+
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
705
|
+
return {
|
|
706
|
+
roots: [
|
|
707
|
+
{
|
|
708
|
+
uri: "file:///home/user/projects/frontend",
|
|
709
|
+
name: "Frontend Repository",
|
|
710
|
+
},
|
|
711
|
+
],
|
|
712
|
+
};
|
|
700
713
|
});
|
|
701
714
|
|
|
702
|
-
return
|
|
715
|
+
return client;
|
|
703
716
|
},
|
|
704
717
|
run: async ({ session }) => {
|
|
705
718
|
expect(session.clientCapabilities).toEqual({
|
|
@@ -712,40 +725,34 @@ test("session knows about client capabilities", async () => {
|
|
|
712
725
|
});
|
|
713
726
|
|
|
714
727
|
test("session knows about roots", async () => {
|
|
715
|
-
|
|
716
|
-
{
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
capabilities: {
|
|
722
|
-
roots: {
|
|
723
|
-
listChanged: true,
|
|
728
|
+
await runWithTestServer({
|
|
729
|
+
client: async () => {
|
|
730
|
+
const client = new Client(
|
|
731
|
+
{
|
|
732
|
+
name: "example-client",
|
|
733
|
+
version: "1.0.0",
|
|
724
734
|
},
|
|
725
|
-
},
|
|
726
|
-
},
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
730
|
-
return {
|
|
731
|
-
roots: [
|
|
732
735
|
{
|
|
733
|
-
|
|
734
|
-
|
|
736
|
+
capabilities: {
|
|
737
|
+
roots: {
|
|
738
|
+
listChanged: true,
|
|
739
|
+
},
|
|
740
|
+
},
|
|
735
741
|
},
|
|
736
|
-
|
|
737
|
-
};
|
|
738
|
-
});
|
|
742
|
+
);
|
|
739
743
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
744
|
+
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
745
|
+
return {
|
|
746
|
+
roots: [
|
|
747
|
+
{
|
|
748
|
+
uri: "file:///home/user/projects/frontend",
|
|
749
|
+
name: "Frontend Repository",
|
|
750
|
+
},
|
|
751
|
+
],
|
|
752
|
+
};
|
|
746
753
|
});
|
|
747
754
|
|
|
748
|
-
return
|
|
755
|
+
return client;
|
|
749
756
|
},
|
|
750
757
|
run: async ({ session }) => {
|
|
751
758
|
expect(session.roots).toEqual([
|
|
@@ -759,20 +766,6 @@ test("session knows about roots", async () => {
|
|
|
759
766
|
});
|
|
760
767
|
|
|
761
768
|
test("session listens to roots changes", async () => {
|
|
762
|
-
const client = new Client(
|
|
763
|
-
{
|
|
764
|
-
name: "example-client",
|
|
765
|
-
version: "1.0.0",
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
capabilities: {
|
|
769
|
-
roots: {
|
|
770
|
-
listChanged: true,
|
|
771
|
-
},
|
|
772
|
-
},
|
|
773
|
-
},
|
|
774
|
-
);
|
|
775
|
-
|
|
776
769
|
let clientRoots: Root[] = [
|
|
777
770
|
{
|
|
778
771
|
uri: "file:///home/user/projects/frontend",
|
|
@@ -780,23 +773,31 @@ test("session listens to roots changes", async () => {
|
|
|
780
773
|
},
|
|
781
774
|
];
|
|
782
775
|
|
|
783
|
-
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
784
|
-
return {
|
|
785
|
-
roots: clientRoots,
|
|
786
|
-
};
|
|
787
|
-
});
|
|
788
|
-
|
|
789
776
|
await runWithTestServer({
|
|
790
|
-
client
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
777
|
+
client: async () => {
|
|
778
|
+
const client = new Client(
|
|
779
|
+
{
|
|
780
|
+
name: "example-client",
|
|
781
|
+
version: "1.0.0",
|
|
782
|
+
},
|
|
783
|
+
{
|
|
784
|
+
capabilities: {
|
|
785
|
+
roots: {
|
|
786
|
+
listChanged: true,
|
|
787
|
+
},
|
|
788
|
+
},
|
|
789
|
+
},
|
|
790
|
+
);
|
|
791
|
+
|
|
792
|
+
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
793
|
+
return {
|
|
794
|
+
roots: clientRoots,
|
|
795
|
+
};
|
|
795
796
|
});
|
|
796
797
|
|
|
797
|
-
return
|
|
798
|
+
return client;
|
|
798
799
|
},
|
|
799
|
-
run: async ({ session }) => {
|
|
800
|
+
run: async ({ session, client }) => {
|
|
800
801
|
expect(session.roots).toEqual([
|
|
801
802
|
{
|
|
802
803
|
uri: "file:///home/user/projects/frontend",
|
|
@@ -847,22 +848,119 @@ test("session listens to roots changes", async () => {
|
|
|
847
848
|
|
|
848
849
|
test("session sends pings to the client", async () => {
|
|
849
850
|
await runWithTestServer({
|
|
850
|
-
|
|
851
|
+
run: async ({ client }) => {
|
|
852
|
+
const onPing = vi.fn().mockReturnValue({});
|
|
853
|
+
|
|
854
|
+
client.setRequestHandler(PingRequestSchema, onPing);
|
|
855
|
+
|
|
856
|
+
await delay(2000);
|
|
857
|
+
|
|
858
|
+
expect(onPing).toHaveBeenCalledTimes(1);
|
|
859
|
+
},
|
|
860
|
+
});
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
test("prompt argument autocompletion", async () => {
|
|
864
|
+
await runWithTestServer({
|
|
865
|
+
server: async () => {
|
|
851
866
|
const server = new FastMCP({
|
|
852
867
|
name: "Test",
|
|
853
868
|
version: "1.0.0",
|
|
854
869
|
});
|
|
855
870
|
|
|
871
|
+
server.addPrompt({
|
|
872
|
+
name: "countryPoem",
|
|
873
|
+
description: "Writes a poem about a country",
|
|
874
|
+
load: async ({ name }) => {
|
|
875
|
+
return `Hello, ${name}!`;
|
|
876
|
+
},
|
|
877
|
+
arguments: [
|
|
878
|
+
{
|
|
879
|
+
name: "name",
|
|
880
|
+
description: "Name of the country",
|
|
881
|
+
required: true,
|
|
882
|
+
complete: async (value) => {
|
|
883
|
+
if (value === "Germ") {
|
|
884
|
+
return {
|
|
885
|
+
values: ["Germany"],
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
return {
|
|
890
|
+
values: [],
|
|
891
|
+
};
|
|
892
|
+
},
|
|
893
|
+
},
|
|
894
|
+
],
|
|
895
|
+
});
|
|
896
|
+
|
|
856
897
|
return server;
|
|
857
898
|
},
|
|
858
899
|
run: async ({ client }) => {
|
|
859
|
-
const
|
|
900
|
+
const response = await client.complete({
|
|
901
|
+
ref: {
|
|
902
|
+
type: "ref/prompt",
|
|
903
|
+
name: "countryPoem",
|
|
904
|
+
},
|
|
905
|
+
argument: {
|
|
906
|
+
name: "name",
|
|
907
|
+
value: "Germ",
|
|
908
|
+
},
|
|
909
|
+
});
|
|
860
910
|
|
|
861
|
-
|
|
911
|
+
expect(response).toEqual({
|
|
912
|
+
completion: {
|
|
913
|
+
values: ["Germany"],
|
|
914
|
+
},
|
|
915
|
+
});
|
|
916
|
+
},
|
|
917
|
+
});
|
|
918
|
+
});
|
|
862
919
|
|
|
863
|
-
|
|
920
|
+
test("adds automatic prompt argument completion when enum is provided", async () => {
|
|
921
|
+
await runWithTestServer({
|
|
922
|
+
server: async () => {
|
|
923
|
+
const server = new FastMCP({
|
|
924
|
+
name: "Test",
|
|
925
|
+
version: "1.0.0",
|
|
926
|
+
});
|
|
864
927
|
|
|
865
|
-
|
|
928
|
+
server.addPrompt({
|
|
929
|
+
name: "countryPoem",
|
|
930
|
+
description: "Writes a poem about a country",
|
|
931
|
+
load: async ({ name }) => {
|
|
932
|
+
return `Hello, ${name}!`;
|
|
933
|
+
},
|
|
934
|
+
arguments: [
|
|
935
|
+
{
|
|
936
|
+
name: "name",
|
|
937
|
+
description: "Name of the country",
|
|
938
|
+
required: true,
|
|
939
|
+
enum: ["Germany", "France", "Italy"],
|
|
940
|
+
},
|
|
941
|
+
],
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
return server;
|
|
945
|
+
},
|
|
946
|
+
run: async ({ client }) => {
|
|
947
|
+
const response = await client.complete({
|
|
948
|
+
ref: {
|
|
949
|
+
type: "ref/prompt",
|
|
950
|
+
name: "countryPoem",
|
|
951
|
+
},
|
|
952
|
+
argument: {
|
|
953
|
+
name: "name",
|
|
954
|
+
value: "Germ",
|
|
955
|
+
},
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
expect(response).toEqual({
|
|
959
|
+
completion: {
|
|
960
|
+
values: ["Germany"],
|
|
961
|
+
total: 1,
|
|
962
|
+
},
|
|
963
|
+
});
|
|
866
964
|
},
|
|
867
965
|
});
|
|
868
966
|
});
|
package/src/FastMCP.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
3
3
|
import {
|
|
4
4
|
CallToolRequestSchema,
|
|
5
5
|
ClientCapabilities,
|
|
6
|
+
CompleteRequestSchema,
|
|
6
7
|
ErrorCode,
|
|
7
8
|
GetPromptRequestSchema,
|
|
8
9
|
ListPromptsRequestSchema,
|
|
@@ -22,6 +23,7 @@ import { readFile } from "fs/promises";
|
|
|
22
23
|
import { fileTypeFromBuffer } from "file-type";
|
|
23
24
|
import { StrictEventEmitter } from "strict-event-emitter-types";
|
|
24
25
|
import { EventEmitter } from "events";
|
|
26
|
+
import Fuse from "fuse.js";
|
|
25
27
|
import { startSSEServer } from "mcp-proxy";
|
|
26
28
|
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
|
|
27
29
|
|
|
@@ -87,7 +89,7 @@ type Extra = unknown;
|
|
|
87
89
|
|
|
88
90
|
type Extras = Record<string, Extra>;
|
|
89
91
|
|
|
90
|
-
class UnexpectedStateError extends FastMCPError {
|
|
92
|
+
export class UnexpectedStateError extends FastMCPError {
|
|
91
93
|
public extras?: Extras;
|
|
92
94
|
|
|
93
95
|
public constructor(message: string, extras?: Extras) {
|
|
@@ -186,6 +188,30 @@ const ContentResultZodSchema = z
|
|
|
186
188
|
})
|
|
187
189
|
.strict() satisfies z.ZodType<ContentResult>;
|
|
188
190
|
|
|
191
|
+
type Completion = {
|
|
192
|
+
values: string[];
|
|
193
|
+
total?: number;
|
|
194
|
+
hasMore?: boolean;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* https://github.com/modelcontextprotocol/typescript-sdk/blob/3164da64d085ec4e022ae881329eee7b72f208d4/src/types.ts#L983-L1003
|
|
199
|
+
*/
|
|
200
|
+
const CompletionZodSchema = z.object({
|
|
201
|
+
/**
|
|
202
|
+
* An array of completion values. Must not exceed 100 items.
|
|
203
|
+
*/
|
|
204
|
+
values: z.array(z.string()).max(100),
|
|
205
|
+
/**
|
|
206
|
+
* The total number of completion options available. This can exceed the number of values actually sent in the response.
|
|
207
|
+
*/
|
|
208
|
+
total: z.optional(z.number().int()),
|
|
209
|
+
/**
|
|
210
|
+
* Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown.
|
|
211
|
+
*/
|
|
212
|
+
hasMore: z.optional(z.boolean()),
|
|
213
|
+
}) satisfies z.ZodType<Completion>;
|
|
214
|
+
|
|
189
215
|
type Tool<Params extends ToolParameters = ToolParameters> = {
|
|
190
216
|
name: string;
|
|
191
217
|
description?: string;
|
|
@@ -204,13 +230,17 @@ type Resource = {
|
|
|
204
230
|
load: () => Promise<{ text: string } | { blob: string }>;
|
|
205
231
|
};
|
|
206
232
|
|
|
207
|
-
type
|
|
233
|
+
type ArgumentValueCompleter = (value: string) => Promise<Completion>;
|
|
234
|
+
|
|
235
|
+
type InputPromptArgument = Readonly<{
|
|
208
236
|
name: string;
|
|
209
237
|
description?: string;
|
|
210
238
|
required?: boolean;
|
|
239
|
+
complete?: ArgumentValueCompleter;
|
|
240
|
+
enum?: string[];
|
|
211
241
|
}>;
|
|
212
242
|
|
|
213
|
-
type ArgumentsToObject<T extends
|
|
243
|
+
type ArgumentsToObject<T extends InputPromptArgument[]> = {
|
|
214
244
|
[K in T[number]["name"]]: Extract<
|
|
215
245
|
T[number],
|
|
216
246
|
{ name: K }
|
|
@@ -219,14 +249,33 @@ type ArgumentsToObject<T extends PromptArgument[]> = {
|
|
|
219
249
|
: string | undefined;
|
|
220
250
|
};
|
|
221
251
|
|
|
252
|
+
type InputPrompt<
|
|
253
|
+
Arguments extends InputPromptArgument[] = InputPromptArgument[],
|
|
254
|
+
Args = ArgumentsToObject<Arguments>,
|
|
255
|
+
> = {
|
|
256
|
+
name: string;
|
|
257
|
+
description?: string;
|
|
258
|
+
arguments?: InputPromptArgument[];
|
|
259
|
+
load: (args: Args) => Promise<string>;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
type PromptArgument = Readonly<{
|
|
263
|
+
name: string;
|
|
264
|
+
description?: string;
|
|
265
|
+
required?: boolean;
|
|
266
|
+
complete?: ArgumentValueCompleter;
|
|
267
|
+
enum?: string[];
|
|
268
|
+
}>;
|
|
269
|
+
|
|
222
270
|
type Prompt<
|
|
223
271
|
Arguments extends PromptArgument[] = PromptArgument[],
|
|
224
272
|
Args = ArgumentsToObject<Arguments>,
|
|
225
273
|
> = {
|
|
226
|
-
|
|
274
|
+
arguments?: PromptArgument[];
|
|
275
|
+
complete?: (name: string, value: string) => Promise<Completion>;
|
|
227
276
|
description?: string;
|
|
228
|
-
arguments?: Arguments;
|
|
229
277
|
load: (args: Args) => Promise<string>;
|
|
278
|
+
name: string;
|
|
230
279
|
};
|
|
231
280
|
|
|
232
281
|
type ServerOptions = {
|
|
@@ -256,6 +305,7 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
256
305
|
#server: Server;
|
|
257
306
|
#clientCapabilities?: ClientCapabilities;
|
|
258
307
|
#roots: Root[] = [];
|
|
308
|
+
#prompts: Prompt[] = [];
|
|
259
309
|
|
|
260
310
|
constructor({
|
|
261
311
|
name,
|
|
@@ -281,6 +331,10 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
281
331
|
}
|
|
282
332
|
|
|
283
333
|
if (prompts.length) {
|
|
334
|
+
for (const prompt of prompts) {
|
|
335
|
+
this.addPrompt(prompt);
|
|
336
|
+
}
|
|
337
|
+
|
|
284
338
|
this.#capabilities.prompts = {};
|
|
285
339
|
}
|
|
286
340
|
|
|
@@ -294,6 +348,7 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
294
348
|
this.setupErrorHandling();
|
|
295
349
|
this.setupLoggingHandlers();
|
|
296
350
|
this.setupRootsHandlers();
|
|
351
|
+
this.setupCompleteHandlers();
|
|
297
352
|
|
|
298
353
|
if (tools.length) {
|
|
299
354
|
this.setupToolHandlers(tools);
|
|
@@ -308,6 +363,49 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
308
363
|
}
|
|
309
364
|
}
|
|
310
365
|
|
|
366
|
+
private addPrompt(inputPrompt: InputPrompt) {
|
|
367
|
+
const completers: Record<string, ArgumentValueCompleter> = {};
|
|
368
|
+
const enums: Record<string, string[]> = {};
|
|
369
|
+
|
|
370
|
+
for (const argument of inputPrompt.arguments ?? []) {
|
|
371
|
+
if (argument.complete) {
|
|
372
|
+
completers[argument.name] = argument.complete;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (argument.enum) {
|
|
376
|
+
enums[argument.name] = argument.enum;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const prompt = {
|
|
381
|
+
...inputPrompt,
|
|
382
|
+
complete: async (name: string, value: string) => {
|
|
383
|
+
if (completers[name]) {
|
|
384
|
+
return await completers[name](value);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (enums[name]) {
|
|
388
|
+
const fuse = new Fuse(enums[name], {
|
|
389
|
+
keys: ["value"],
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const result = fuse.search(value);
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
values: result.map((item) => item.item),
|
|
396
|
+
total: result.length,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
values: [],
|
|
402
|
+
};
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
this.#prompts.push(prompt);
|
|
407
|
+
}
|
|
408
|
+
|
|
311
409
|
public get clientCapabilities(): ClientCapabilities | null {
|
|
312
410
|
return this.#clientCapabilities ?? null;
|
|
313
411
|
}
|
|
@@ -382,6 +480,43 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
382
480
|
return this.#loggingLevel;
|
|
383
481
|
}
|
|
384
482
|
|
|
483
|
+
private setupCompleteHandlers() {
|
|
484
|
+
this.#server.setRequestHandler(CompleteRequestSchema, async (request) => {
|
|
485
|
+
if (request.params.ref.type === "ref/prompt") {
|
|
486
|
+
const prompt = this.#prompts.find(
|
|
487
|
+
(prompt) => prompt.name === request.params.ref.name,
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
if (!prompt) {
|
|
491
|
+
throw new UnexpectedStateError("Unknown prompt", {
|
|
492
|
+
request,
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
if (!prompt.complete) {
|
|
497
|
+
throw new UnexpectedStateError("Prompt does not support completion", {
|
|
498
|
+
request,
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const completion = CompletionZodSchema.parse(
|
|
503
|
+
await prompt.complete(
|
|
504
|
+
request.params.argument.name,
|
|
505
|
+
request.params.argument.value,
|
|
506
|
+
),
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
return {
|
|
510
|
+
completion,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
throw new UnexpectedStateError("Unexpected completion request", {
|
|
515
|
+
request,
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
385
520
|
private setupRootsHandlers() {
|
|
386
521
|
this.#server.setNotificationHandler(
|
|
387
522
|
RootsListChangedNotificationSchema,
|
|
@@ -595,6 +730,7 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
595
730
|
name: prompt.name,
|
|
596
731
|
description: prompt.description,
|
|
597
732
|
arguments: prompt.arguments,
|
|
733
|
+
complete: prompt.complete,
|
|
598
734
|
};
|
|
599
735
|
}),
|
|
600
736
|
};
|
|
@@ -614,14 +750,12 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
614
750
|
|
|
615
751
|
const args = request.params.arguments;
|
|
616
752
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
);
|
|
624
|
-
}
|
|
753
|
+
for (const arg of prompt.arguments ?? []) {
|
|
754
|
+
if (arg.required && !(args && arg.name in args)) {
|
|
755
|
+
throw new McpError(
|
|
756
|
+
ErrorCode.InvalidRequest,
|
|
757
|
+
`Missing required argument: ${arg.name}`,
|
|
758
|
+
);
|
|
625
759
|
}
|
|
626
760
|
}
|
|
627
761
|
|
|
@@ -657,7 +791,7 @@ class FastMCPEventEmitter extends FastMCPEventEmitterBase {}
|
|
|
657
791
|
|
|
658
792
|
export class FastMCP extends FastMCPEventEmitter {
|
|
659
793
|
#options: ServerOptions;
|
|
660
|
-
#prompts:
|
|
794
|
+
#prompts: InputPrompt[] = [];
|
|
661
795
|
#resources: Resource[] = [];
|
|
662
796
|
#sessions: FastMCPSession[] = [];
|
|
663
797
|
#sseServer: SSEServer | null = null;
|
|
@@ -690,7 +824,9 @@ export class FastMCP extends FastMCPEventEmitter {
|
|
|
690
824
|
/**
|
|
691
825
|
* Adds a prompt to the server.
|
|
692
826
|
*/
|
|
693
|
-
public addPrompt<const Args extends
|
|
827
|
+
public addPrompt<const Args extends InputPromptArgument[]>(
|
|
828
|
+
prompt: InputPrompt<Args>,
|
|
829
|
+
) {
|
|
694
830
|
this.#prompts.push(prompt);
|
|
695
831
|
}
|
|
696
832
|
|