fastmcp 1.10.0 → 1.12.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 +96 -9
- package/dist/FastMCP.js.map +1 -1
- package/jsr.json +1 -1
- package/package.json +2 -1
- package/src/FastMCP.test.ts +258 -120
- package/src/FastMCP.ts +152 -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,
|
|
@@ -357,6 +443,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
357
443
|
{
|
|
358
444
|
uri: resource.uri,
|
|
359
445
|
mimeType: resource.mimeType,
|
|
446
|
+
name: resource.name,
|
|
360
447
|
...result
|
|
361
448
|
}
|
|
362
449
|
]
|
|
@@ -371,7 +458,8 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
371
458
|
return {
|
|
372
459
|
name: prompt.name,
|
|
373
460
|
description: prompt.description,
|
|
374
|
-
arguments: prompt.arguments
|
|
461
|
+
arguments: prompt.arguments,
|
|
462
|
+
complete: prompt.complete
|
|
375
463
|
};
|
|
376
464
|
})
|
|
377
465
|
};
|
|
@@ -387,14 +475,12 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
|
|
|
387
475
|
);
|
|
388
476
|
}
|
|
389
477
|
const args = request.params.arguments;
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
);
|
|
397
|
-
}
|
|
478
|
+
for (const arg of prompt.arguments ?? []) {
|
|
479
|
+
if (arg.required && !(args && arg.name in args)) {
|
|
480
|
+
throw new McpError(
|
|
481
|
+
ErrorCode.InvalidRequest,
|
|
482
|
+
`Missing required argument: ${arg.name}`
|
|
483
|
+
);
|
|
398
484
|
}
|
|
399
485
|
}
|
|
400
486
|
let result;
|
|
@@ -519,6 +605,7 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
519
605
|
export {
|
|
520
606
|
FastMCP,
|
|
521
607
|
FastMCPSession,
|
|
608
|
+
UnexpectedStateError,
|
|
522
609
|
UserError,
|
|
523
610
|
imageContent
|
|
524
611
|
};
|
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 name: resource.name,\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.12.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",
|
|
@@ -507,9 +506,49 @@ test("adds resources", async () => {
|
|
|
507
506
|
});
|
|
508
507
|
});
|
|
509
508
|
|
|
509
|
+
test("clients reads a resource", async () => {
|
|
510
|
+
await runWithTestServer({
|
|
511
|
+
server: async () => {
|
|
512
|
+
const server = new FastMCP({
|
|
513
|
+
name: "Test",
|
|
514
|
+
version: "1.0.0",
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
server.addResource({
|
|
518
|
+
uri: "file:///logs/app.log",
|
|
519
|
+
name: "Application Logs",
|
|
520
|
+
mimeType: "text/plain",
|
|
521
|
+
async load() {
|
|
522
|
+
return {
|
|
523
|
+
text: "Example log content",
|
|
524
|
+
};
|
|
525
|
+
},
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
return server;
|
|
529
|
+
},
|
|
530
|
+
run: async ({ client }) => {
|
|
531
|
+
expect(
|
|
532
|
+
await client.readResource({
|
|
533
|
+
uri: "file:///logs/app.log",
|
|
534
|
+
}),
|
|
535
|
+
).toEqual({
|
|
536
|
+
contents: [
|
|
537
|
+
{
|
|
538
|
+
uri: "file:///logs/app.log",
|
|
539
|
+
name: "Application Logs",
|
|
540
|
+
text: "Example log content",
|
|
541
|
+
mimeType: "text/plain",
|
|
542
|
+
},
|
|
543
|
+
],
|
|
544
|
+
});
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
});
|
|
548
|
+
|
|
510
549
|
test("adds prompts", async () => {
|
|
511
550
|
await runWithTestServer({
|
|
512
|
-
|
|
551
|
+
server: async () => {
|
|
513
552
|
const server = new FastMCP({
|
|
514
553
|
name: "Test",
|
|
515
554
|
version: "1.0.0",
|
|
@@ -533,6 +572,26 @@ test("adds prompts", async () => {
|
|
|
533
572
|
return server;
|
|
534
573
|
},
|
|
535
574
|
run: async ({ client }) => {
|
|
575
|
+
expect(
|
|
576
|
+
await client.getPrompt({
|
|
577
|
+
name: "git-commit",
|
|
578
|
+
arguments: {
|
|
579
|
+
changes: "foo",
|
|
580
|
+
},
|
|
581
|
+
}),
|
|
582
|
+
).toEqual({
|
|
583
|
+
description: "Generate a Git commit message",
|
|
584
|
+
messages: [
|
|
585
|
+
{
|
|
586
|
+
role: "user",
|
|
587
|
+
content: {
|
|
588
|
+
type: "text",
|
|
589
|
+
text: "Generate a concise but descriptive commit message for these changes:\n\nfoo",
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
],
|
|
593
|
+
});
|
|
594
|
+
|
|
536
595
|
expect(await client.listPrompts()).toEqual({
|
|
537
596
|
prompts: [
|
|
538
597
|
{
|
|
@@ -666,40 +725,34 @@ test("handles multiple clients", async () => {
|
|
|
666
725
|
});
|
|
667
726
|
|
|
668
727
|
test("session knows about client capabilities", async () => {
|
|
669
|
-
|
|
670
|
-
{
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
capabilities: {
|
|
676
|
-
roots: {
|
|
677
|
-
listChanged: true,
|
|
728
|
+
await runWithTestServer({
|
|
729
|
+
client: async () => {
|
|
730
|
+
const client = new Client(
|
|
731
|
+
{
|
|
732
|
+
name: "example-client",
|
|
733
|
+
version: "1.0.0",
|
|
678
734
|
},
|
|
679
|
-
},
|
|
680
|
-
},
|
|
681
|
-
);
|
|
682
|
-
|
|
683
|
-
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
684
|
-
return {
|
|
685
|
-
roots: [
|
|
686
735
|
{
|
|
687
|
-
|
|
688
|
-
|
|
736
|
+
capabilities: {
|
|
737
|
+
roots: {
|
|
738
|
+
listChanged: true,
|
|
739
|
+
},
|
|
740
|
+
},
|
|
689
741
|
},
|
|
690
|
-
|
|
691
|
-
};
|
|
692
|
-
});
|
|
742
|
+
);
|
|
693
743
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
744
|
+
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
745
|
+
return {
|
|
746
|
+
roots: [
|
|
747
|
+
{
|
|
748
|
+
uri: "file:///home/user/projects/frontend",
|
|
749
|
+
name: "Frontend Repository",
|
|
750
|
+
},
|
|
751
|
+
],
|
|
752
|
+
};
|
|
700
753
|
});
|
|
701
754
|
|
|
702
|
-
return
|
|
755
|
+
return client;
|
|
703
756
|
},
|
|
704
757
|
run: async ({ session }) => {
|
|
705
758
|
expect(session.clientCapabilities).toEqual({
|
|
@@ -712,40 +765,34 @@ test("session knows about client capabilities", async () => {
|
|
|
712
765
|
});
|
|
713
766
|
|
|
714
767
|
test("session knows about roots", async () => {
|
|
715
|
-
|
|
716
|
-
{
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
capabilities: {
|
|
722
|
-
roots: {
|
|
723
|
-
listChanged: true,
|
|
768
|
+
await runWithTestServer({
|
|
769
|
+
client: async () => {
|
|
770
|
+
const client = new Client(
|
|
771
|
+
{
|
|
772
|
+
name: "example-client",
|
|
773
|
+
version: "1.0.0",
|
|
724
774
|
},
|
|
725
|
-
},
|
|
726
|
-
},
|
|
727
|
-
);
|
|
728
|
-
|
|
729
|
-
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
730
|
-
return {
|
|
731
|
-
roots: [
|
|
732
775
|
{
|
|
733
|
-
|
|
734
|
-
|
|
776
|
+
capabilities: {
|
|
777
|
+
roots: {
|
|
778
|
+
listChanged: true,
|
|
779
|
+
},
|
|
780
|
+
},
|
|
735
781
|
},
|
|
736
|
-
|
|
737
|
-
};
|
|
738
|
-
});
|
|
782
|
+
);
|
|
739
783
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
784
|
+
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
785
|
+
return {
|
|
786
|
+
roots: [
|
|
787
|
+
{
|
|
788
|
+
uri: "file:///home/user/projects/frontend",
|
|
789
|
+
name: "Frontend Repository",
|
|
790
|
+
},
|
|
791
|
+
],
|
|
792
|
+
};
|
|
746
793
|
});
|
|
747
794
|
|
|
748
|
-
return
|
|
795
|
+
return client;
|
|
749
796
|
},
|
|
750
797
|
run: async ({ session }) => {
|
|
751
798
|
expect(session.roots).toEqual([
|
|
@@ -759,20 +806,6 @@ test("session knows about roots", async () => {
|
|
|
759
806
|
});
|
|
760
807
|
|
|
761
808
|
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
809
|
let clientRoots: Root[] = [
|
|
777
810
|
{
|
|
778
811
|
uri: "file:///home/user/projects/frontend",
|
|
@@ -780,23 +813,31 @@ test("session listens to roots changes", async () => {
|
|
|
780
813
|
},
|
|
781
814
|
];
|
|
782
815
|
|
|
783
|
-
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
784
|
-
return {
|
|
785
|
-
roots: clientRoots,
|
|
786
|
-
};
|
|
787
|
-
});
|
|
788
|
-
|
|
789
816
|
await runWithTestServer({
|
|
790
|
-
client
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
817
|
+
client: async () => {
|
|
818
|
+
const client = new Client(
|
|
819
|
+
{
|
|
820
|
+
name: "example-client",
|
|
821
|
+
version: "1.0.0",
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
capabilities: {
|
|
825
|
+
roots: {
|
|
826
|
+
listChanged: true,
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
);
|
|
831
|
+
|
|
832
|
+
client.setRequestHandler(ListRootsRequestSchema, () => {
|
|
833
|
+
return {
|
|
834
|
+
roots: clientRoots,
|
|
835
|
+
};
|
|
795
836
|
});
|
|
796
837
|
|
|
797
|
-
return
|
|
838
|
+
return client;
|
|
798
839
|
},
|
|
799
|
-
run: async ({ session }) => {
|
|
840
|
+
run: async ({ session, client }) => {
|
|
800
841
|
expect(session.roots).toEqual([
|
|
801
842
|
{
|
|
802
843
|
uri: "file:///home/user/projects/frontend",
|
|
@@ -847,22 +888,119 @@ test("session listens to roots changes", async () => {
|
|
|
847
888
|
|
|
848
889
|
test("session sends pings to the client", async () => {
|
|
849
890
|
await runWithTestServer({
|
|
850
|
-
|
|
891
|
+
run: async ({ client }) => {
|
|
892
|
+
const onPing = vi.fn().mockReturnValue({});
|
|
893
|
+
|
|
894
|
+
client.setRequestHandler(PingRequestSchema, onPing);
|
|
895
|
+
|
|
896
|
+
await delay(2000);
|
|
897
|
+
|
|
898
|
+
expect(onPing).toHaveBeenCalledTimes(1);
|
|
899
|
+
},
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
test("prompt argument autocompletion", async () => {
|
|
904
|
+
await runWithTestServer({
|
|
905
|
+
server: async () => {
|
|
851
906
|
const server = new FastMCP({
|
|
852
907
|
name: "Test",
|
|
853
908
|
version: "1.0.0",
|
|
854
909
|
});
|
|
855
910
|
|
|
911
|
+
server.addPrompt({
|
|
912
|
+
name: "countryPoem",
|
|
913
|
+
description: "Writes a poem about a country",
|
|
914
|
+
load: async ({ name }) => {
|
|
915
|
+
return `Hello, ${name}!`;
|
|
916
|
+
},
|
|
917
|
+
arguments: [
|
|
918
|
+
{
|
|
919
|
+
name: "name",
|
|
920
|
+
description: "Name of the country",
|
|
921
|
+
required: true,
|
|
922
|
+
complete: async (value) => {
|
|
923
|
+
if (value === "Germ") {
|
|
924
|
+
return {
|
|
925
|
+
values: ["Germany"],
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
return {
|
|
930
|
+
values: [],
|
|
931
|
+
};
|
|
932
|
+
},
|
|
933
|
+
},
|
|
934
|
+
],
|
|
935
|
+
});
|
|
936
|
+
|
|
856
937
|
return server;
|
|
857
938
|
},
|
|
858
939
|
run: async ({ client }) => {
|
|
859
|
-
const
|
|
940
|
+
const response = await client.complete({
|
|
941
|
+
ref: {
|
|
942
|
+
type: "ref/prompt",
|
|
943
|
+
name: "countryPoem",
|
|
944
|
+
},
|
|
945
|
+
argument: {
|
|
946
|
+
name: "name",
|
|
947
|
+
value: "Germ",
|
|
948
|
+
},
|
|
949
|
+
});
|
|
860
950
|
|
|
861
|
-
|
|
951
|
+
expect(response).toEqual({
|
|
952
|
+
completion: {
|
|
953
|
+
values: ["Germany"],
|
|
954
|
+
},
|
|
955
|
+
});
|
|
956
|
+
},
|
|
957
|
+
});
|
|
958
|
+
});
|
|
862
959
|
|
|
863
|
-
|
|
960
|
+
test("adds automatic prompt argument completion when enum is provided", async () => {
|
|
961
|
+
await runWithTestServer({
|
|
962
|
+
server: async () => {
|
|
963
|
+
const server = new FastMCP({
|
|
964
|
+
name: "Test",
|
|
965
|
+
version: "1.0.0",
|
|
966
|
+
});
|
|
864
967
|
|
|
865
|
-
|
|
968
|
+
server.addPrompt({
|
|
969
|
+
name: "countryPoem",
|
|
970
|
+
description: "Writes a poem about a country",
|
|
971
|
+
load: async ({ name }) => {
|
|
972
|
+
return `Hello, ${name}!`;
|
|
973
|
+
},
|
|
974
|
+
arguments: [
|
|
975
|
+
{
|
|
976
|
+
name: "name",
|
|
977
|
+
description: "Name of the country",
|
|
978
|
+
required: true,
|
|
979
|
+
enum: ["Germany", "France", "Italy"],
|
|
980
|
+
},
|
|
981
|
+
],
|
|
982
|
+
});
|
|
983
|
+
|
|
984
|
+
return server;
|
|
985
|
+
},
|
|
986
|
+
run: async ({ client }) => {
|
|
987
|
+
const response = await client.complete({
|
|
988
|
+
ref: {
|
|
989
|
+
type: "ref/prompt",
|
|
990
|
+
name: "countryPoem",
|
|
991
|
+
},
|
|
992
|
+
argument: {
|
|
993
|
+
name: "name",
|
|
994
|
+
value: "Germ",
|
|
995
|
+
},
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
expect(response).toEqual({
|
|
999
|
+
completion: {
|
|
1000
|
+
values: ["Germany"],
|
|
1001
|
+
total: 1,
|
|
1002
|
+
},
|
|
1003
|
+
});
|
|
866
1004
|
},
|
|
867
1005
|
});
|
|
868
1006
|
});
|
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,
|
|
@@ -579,6 +714,7 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
579
714
|
{
|
|
580
715
|
uri: resource.uri,
|
|
581
716
|
mimeType: resource.mimeType,
|
|
717
|
+
name: resource.name,
|
|
582
718
|
...result,
|
|
583
719
|
},
|
|
584
720
|
],
|
|
@@ -595,6 +731,7 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
595
731
|
name: prompt.name,
|
|
596
732
|
description: prompt.description,
|
|
597
733
|
arguments: prompt.arguments,
|
|
734
|
+
complete: prompt.complete,
|
|
598
735
|
};
|
|
599
736
|
}),
|
|
600
737
|
};
|
|
@@ -614,14 +751,12 @@ export class FastMCPSession extends FastMCPSessionEventEmitter {
|
|
|
614
751
|
|
|
615
752
|
const args = request.params.arguments;
|
|
616
753
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
);
|
|
624
|
-
}
|
|
754
|
+
for (const arg of prompt.arguments ?? []) {
|
|
755
|
+
if (arg.required && !(args && arg.name in args)) {
|
|
756
|
+
throw new McpError(
|
|
757
|
+
ErrorCode.InvalidRequest,
|
|
758
|
+
`Missing required argument: ${arg.name}`,
|
|
759
|
+
);
|
|
625
760
|
}
|
|
626
761
|
}
|
|
627
762
|
|
|
@@ -657,7 +792,7 @@ class FastMCPEventEmitter extends FastMCPEventEmitterBase {}
|
|
|
657
792
|
|
|
658
793
|
export class FastMCP extends FastMCPEventEmitter {
|
|
659
794
|
#options: ServerOptions;
|
|
660
|
-
#prompts:
|
|
795
|
+
#prompts: InputPrompt[] = [];
|
|
661
796
|
#resources: Resource[] = [];
|
|
662
797
|
#sessions: FastMCPSession[] = [];
|
|
663
798
|
#sseServer: SSEServer | null = null;
|
|
@@ -690,7 +825,9 @@ export class FastMCP extends FastMCPEventEmitter {
|
|
|
690
825
|
/**
|
|
691
826
|
* Adds a prompt to the server.
|
|
692
827
|
*/
|
|
693
|
-
public addPrompt<const Args extends
|
|
828
|
+
public addPrompt<const Args extends InputPromptArgument[]>(
|
|
829
|
+
prompt: InputPrompt<Args>,
|
|
830
|
+
) {
|
|
694
831
|
this.#prompts.push(prompt);
|
|
695
832
|
}
|
|
696
833
|
|