fastmcp 1.27.6 → 2.0.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 +90 -31
- package/dist/FastMCP.d.ts +16 -11
- package/dist/FastMCP.js +31 -48
- package/dist/FastMCP.js.map +1 -1
- package/jsr.json +1 -1
- package/package.json +9 -9
- package/src/FastMCP.test.ts +149 -26
- package/src/FastMCP.ts +63 -62
- package/src/examples/addition.ts +40 -4
package/README.md
CHANGED
|
@@ -15,10 +15,10 @@ A TypeScript framework for building [MCP](https://glama.ai/mcp) servers capable
|
|
|
15
15
|
- [Audio content](#returning-an-audio)
|
|
16
16
|
- [Logging](#logging)
|
|
17
17
|
- [Error handling](#errors)
|
|
18
|
-
- [
|
|
19
|
-
- [HTTP Streaming](#http-streaming)
|
|
18
|
+
- [HTTP Streaming](#http-streaming) (with SSE compatibility)
|
|
20
19
|
- CORS (enabled by default)
|
|
21
20
|
- [Progress notifications](#progress)
|
|
21
|
+
- [Streaming output](#streaming-output)
|
|
22
22
|
- [Typed server events](#typed-server-events)
|
|
23
23
|
- [Prompt argument auto-completion](#prompt-argument-auto-completion)
|
|
24
24
|
- [Sampling](#requestsampling)
|
|
@@ -87,24 +87,6 @@ If you are looking for a boilerplate repository to build your own MCP server, ch
|
|
|
87
87
|
|
|
88
88
|
FastMCP supports multiple transport options for remote communication, allowing an MCP hosted on a remote machine to be accessed over the network.
|
|
89
89
|
|
|
90
|
-
#### SSE
|
|
91
|
-
|
|
92
|
-
[Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) (SSE) provide a mechanism for servers to send real-time updates to clients over an HTTPS connection.
|
|
93
|
-
|
|
94
|
-
You can run the server with SSE support:
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
server.start({
|
|
98
|
-
transportType: "sse",
|
|
99
|
-
sse: {
|
|
100
|
-
endpoint: "/sse",
|
|
101
|
-
port: 8080,
|
|
102
|
-
},
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
This will start the server and listen for SSE connections on `http://localhost:8080/sse`.
|
|
107
|
-
|
|
108
90
|
#### HTTP Streaming
|
|
109
91
|
|
|
110
92
|
[HTTP streaming](https://www.cloudflare.com/learning/video/what-is-http-live-streaming/) provides a more efficient alternative to SSE in environments that support it, with potentially better performance for larger payloads.
|
|
@@ -115,7 +97,6 @@ You can run the server with HTTP streaming support:
|
|
|
115
97
|
server.start({
|
|
116
98
|
transportType: "httpStream",
|
|
117
99
|
httpStream: {
|
|
118
|
-
endpoint: "/stream",
|
|
119
100
|
port: 8080,
|
|
120
101
|
},
|
|
121
102
|
});
|
|
@@ -125,10 +106,10 @@ This will start the server and listen for HTTP streaming connections on `http://
|
|
|
125
106
|
|
|
126
107
|
You can connect to these servers using the appropriate client transport.
|
|
127
108
|
|
|
128
|
-
For
|
|
109
|
+
For HTTP streaming connections:
|
|
129
110
|
|
|
130
111
|
```ts
|
|
131
|
-
import {
|
|
112
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
132
113
|
|
|
133
114
|
const client = new Client(
|
|
134
115
|
{
|
|
@@ -140,15 +121,17 @@ const client = new Client(
|
|
|
140
121
|
},
|
|
141
122
|
);
|
|
142
123
|
|
|
143
|
-
const transport = new
|
|
124
|
+
const transport = new StreamableHTTPClientTransport(
|
|
125
|
+
new URL(`http://localhost:8080/stream`),
|
|
126
|
+
);
|
|
144
127
|
|
|
145
128
|
await client.connect(transport);
|
|
146
129
|
```
|
|
147
130
|
|
|
148
|
-
For
|
|
131
|
+
For SSE connections:
|
|
149
132
|
|
|
150
133
|
```ts
|
|
151
|
-
import {
|
|
134
|
+
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
|
|
152
135
|
|
|
153
136
|
const client = new Client(
|
|
154
137
|
{
|
|
@@ -160,9 +143,7 @@ const client = new Client(
|
|
|
160
143
|
},
|
|
161
144
|
);
|
|
162
145
|
|
|
163
|
-
const transport = new
|
|
164
|
-
new URL(`http://localhost:8080/stream`),
|
|
165
|
-
);
|
|
146
|
+
const transport = new SSEClientTransport(new URL(`http://localhost:8080/sse`));
|
|
166
147
|
|
|
167
148
|
await client.connect(transport);
|
|
168
149
|
```
|
|
@@ -668,6 +649,86 @@ server.addTool({
|
|
|
668
649
|
});
|
|
669
650
|
```
|
|
670
651
|
|
|
652
|
+
#### Streaming Output
|
|
653
|
+
|
|
654
|
+
FastMCP supports streaming partial results from tools while they're still executing, enabling responsive UIs and real-time feedback. This is particularly useful for:
|
|
655
|
+
|
|
656
|
+
- Long-running operations that generate content incrementally
|
|
657
|
+
- Progressive generation of text, images, or other media
|
|
658
|
+
- Operations where users benefit from seeing immediate partial results
|
|
659
|
+
|
|
660
|
+
To enable streaming for a tool, add the `streamingHint` annotation and use the `streamContent` method:
|
|
661
|
+
|
|
662
|
+
```js
|
|
663
|
+
server.addTool({
|
|
664
|
+
name: "generateText",
|
|
665
|
+
description: "Generate text incrementally",
|
|
666
|
+
parameters: z.object({
|
|
667
|
+
prompt: z.string(),
|
|
668
|
+
}),
|
|
669
|
+
annotations: {
|
|
670
|
+
streamingHint: true, // Signals this tool uses streaming
|
|
671
|
+
readOnlyHint: true,
|
|
672
|
+
},
|
|
673
|
+
execute: async (args, { streamContent }) => {
|
|
674
|
+
// Send initial content immediately
|
|
675
|
+
await streamContent({ type: "text", text: "Starting generation...\n" });
|
|
676
|
+
|
|
677
|
+
// Simulate incremental content generation
|
|
678
|
+
const words = "The quick brown fox jumps over the lazy dog.".split(" ");
|
|
679
|
+
for (const word of words) {
|
|
680
|
+
await streamContent({ type: "text", text: word + " " });
|
|
681
|
+
await new Promise((resolve) => setTimeout(resolve, 300)); // Simulate delay
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// When using streamContent, you can:
|
|
685
|
+
// 1. Return void (if all content was streamed)
|
|
686
|
+
// 2. Return a final result (which will be appended to streamed content)
|
|
687
|
+
|
|
688
|
+
// Option 1: All content was streamed, so return void
|
|
689
|
+
return;
|
|
690
|
+
|
|
691
|
+
// Option 2: Return final content that will be appended
|
|
692
|
+
// return "Generation complete!";
|
|
693
|
+
},
|
|
694
|
+
});
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
Streaming works with all content types (text, image, audio) and can be combined with progress reporting:
|
|
698
|
+
|
|
699
|
+
```js
|
|
700
|
+
server.addTool({
|
|
701
|
+
name: "processData",
|
|
702
|
+
description: "Process data with streaming updates",
|
|
703
|
+
parameters: z.object({
|
|
704
|
+
datasetSize: z.number(),
|
|
705
|
+
}),
|
|
706
|
+
annotations: {
|
|
707
|
+
streamingHint: true,
|
|
708
|
+
},
|
|
709
|
+
execute: async (args, { streamContent, reportProgress }) => {
|
|
710
|
+
const total = args.datasetSize;
|
|
711
|
+
|
|
712
|
+
for (let i = 0; i < total; i++) {
|
|
713
|
+
// Report numeric progress
|
|
714
|
+
await reportProgress({ progress: i, total });
|
|
715
|
+
|
|
716
|
+
// Stream intermediate results
|
|
717
|
+
if (i % 10 === 0) {
|
|
718
|
+
await streamContent({
|
|
719
|
+
type: "text",
|
|
720
|
+
text: `Processed ${i} of ${total} items...\n`,
|
|
721
|
+
});
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
return "Processing complete!";
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
```
|
|
731
|
+
|
|
671
732
|
#### Tool Annotations
|
|
672
733
|
|
|
673
734
|
As of the MCP Specification (2025-03-26), tools can include annotations that provide richer context and control by adding metadata about a tool's behavior:
|
|
@@ -892,8 +953,6 @@ server.addPrompt({
|
|
|
892
953
|
FastMCP allows you to `authenticate` clients using a custom function:
|
|
893
954
|
|
|
894
955
|
```ts
|
|
895
|
-
import { AuthError } from "fastmcp";
|
|
896
|
-
|
|
897
956
|
const server = new FastMCP({
|
|
898
957
|
name: "My Server",
|
|
899
958
|
version: "1.0.0",
|
package/dist/FastMCP.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
2
|
import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
|
3
|
-
import {
|
|
3
|
+
import { Root, ClientCapabilities, CreateMessageRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
4
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
6
6
|
import http from 'http';
|
|
@@ -49,6 +49,7 @@ type Context<T extends FastMCPSessionAuth> = {
|
|
|
49
49
|
};
|
|
50
50
|
reportProgress: (progress: Progress) => Promise<void>;
|
|
51
51
|
session: T | undefined;
|
|
52
|
+
streamContent: (content: Content | Content[]) => Promise<void>;
|
|
52
53
|
};
|
|
53
54
|
type Extra = unknown;
|
|
54
55
|
type Extras = Record<string, Extra>;
|
|
@@ -88,7 +89,12 @@ type ImageContent = {
|
|
|
88
89
|
mimeType: string;
|
|
89
90
|
type: "image";
|
|
90
91
|
};
|
|
91
|
-
type
|
|
92
|
+
type AudioContent = {
|
|
93
|
+
data: string;
|
|
94
|
+
mimeType: string;
|
|
95
|
+
type: "audio";
|
|
96
|
+
};
|
|
97
|
+
type Content = AudioContent | ImageContent | TextContent;
|
|
92
98
|
type ContentResult = {
|
|
93
99
|
content: Content[];
|
|
94
100
|
isError?: boolean;
|
|
@@ -216,9 +222,15 @@ type ServerOptions<T extends FastMCPSessionAuth> = {
|
|
|
216
222
|
version: `${number}.${number}.${number}`;
|
|
217
223
|
};
|
|
218
224
|
type Tool<T extends FastMCPSessionAuth, Params extends ToolParameters = ToolParameters> = {
|
|
219
|
-
annotations?:
|
|
225
|
+
annotations?: {
|
|
226
|
+
/**
|
|
227
|
+
* When true, the tool leverages incremental content streaming
|
|
228
|
+
* Return void for tools that handle all their output via streaming
|
|
229
|
+
*/
|
|
230
|
+
streamingHint?: boolean;
|
|
231
|
+
} & ToolAnnotations;
|
|
220
232
|
description?: string;
|
|
221
|
-
execute: (args: StandardSchemaV1.InferOutput<Params>, context: Context<T>) => Promise<AudioContent | ContentResult | ImageContent | string | TextContent>;
|
|
233
|
+
execute: (args: StandardSchemaV1.InferOutput<Params>, context: Context<T>) => Promise<AudioContent | ContentResult | ImageContent | string | TextContent | void>;
|
|
222
234
|
name: string;
|
|
223
235
|
parameters?: Params;
|
|
224
236
|
timeoutMs?: number;
|
|
@@ -332,16 +344,9 @@ declare class FastMCP<T extends Record<string, unknown> | undefined = undefined>
|
|
|
332
344
|
*/
|
|
333
345
|
start(options?: {
|
|
334
346
|
httpStream: {
|
|
335
|
-
endpoint: `/${string}`;
|
|
336
347
|
port: number;
|
|
337
348
|
};
|
|
338
349
|
transportType: "httpStream";
|
|
339
|
-
} | {
|
|
340
|
-
sse: {
|
|
341
|
-
endpoint: `/${string}`;
|
|
342
|
-
port: number;
|
|
343
|
-
};
|
|
344
|
-
transportType: "sse";
|
|
345
350
|
} | {
|
|
346
351
|
transportType: "stdio";
|
|
347
352
|
}): Promise<void>;
|
package/dist/FastMCP.js
CHANGED
|
@@ -19,7 +19,7 @@ import { EventEmitter } from "events";
|
|
|
19
19
|
import { fileTypeFromBuffer } from "file-type";
|
|
20
20
|
import { readFile } from "fs/promises";
|
|
21
21
|
import Fuse from "fuse.js";
|
|
22
|
-
import {
|
|
22
|
+
import { startHTTPServer } from "mcp-proxy";
|
|
23
23
|
import { setTimeout as delay } from "timers/promises";
|
|
24
24
|
import { fetch } from "undici";
|
|
25
25
|
import parseURITemplate from "uri-templates";
|
|
@@ -163,9 +163,18 @@ var ImageContentZodSchema = z.object({
|
|
|
163
163
|
mimeType: z.string(),
|
|
164
164
|
type: z.literal("image")
|
|
165
165
|
}).strict();
|
|
166
|
+
var AudioContentZodSchema = z.object({
|
|
167
|
+
/**
|
|
168
|
+
* The base64-encoded audio data.
|
|
169
|
+
*/
|
|
170
|
+
data: z.string().base64(),
|
|
171
|
+
mimeType: z.string(),
|
|
172
|
+
type: z.literal("audio")
|
|
173
|
+
}).strict();
|
|
166
174
|
var ContentZodSchema = z.discriminatedUnion("type", [
|
|
167
175
|
TextContentZodSchema,
|
|
168
|
-
ImageContentZodSchema
|
|
176
|
+
ImageContentZodSchema,
|
|
177
|
+
AudioContentZodSchema
|
|
169
178
|
]);
|
|
170
179
|
var ContentResultZodSchema = z.object({
|
|
171
180
|
content: ContentZodSchema.array(),
|
|
@@ -345,7 +354,7 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
345
354
|
const pingConfig = this.#pingConfig || {};
|
|
346
355
|
let defaultEnabled = false;
|
|
347
356
|
if ("type" in transport) {
|
|
348
|
-
if (transport.type === "
|
|
357
|
+
if (transport.type === "httpStream") {
|
|
349
358
|
defaultEnabled = true;
|
|
350
359
|
}
|
|
351
360
|
}
|
|
@@ -764,10 +773,21 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
764
773
|
});
|
|
765
774
|
}
|
|
766
775
|
};
|
|
776
|
+
const streamContent = async (content) => {
|
|
777
|
+
const contentArray = Array.isArray(content) ? content : [content];
|
|
778
|
+
await this.#server.notification({
|
|
779
|
+
method: "notifications/tool/streamContent",
|
|
780
|
+
params: {
|
|
781
|
+
content: contentArray,
|
|
782
|
+
toolName: request.params.name
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
};
|
|
767
786
|
const executeToolPromise = tool.execute(args, {
|
|
768
787
|
log,
|
|
769
788
|
reportProgress,
|
|
770
|
-
session: this.#auth
|
|
789
|
+
session: this.#auth,
|
|
790
|
+
streamContent
|
|
771
791
|
});
|
|
772
792
|
const maybeStringResult = await (tool.timeoutMs ? Promise.race([
|
|
773
793
|
executeToolPromise,
|
|
@@ -781,7 +801,11 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
|
|
|
781
801
|
}, tool.timeoutMs);
|
|
782
802
|
})
|
|
783
803
|
]) : executeToolPromise);
|
|
784
|
-
if (
|
|
804
|
+
if (maybeStringResult === void 0 || maybeStringResult === null) {
|
|
805
|
+
result = ContentResultZodSchema.parse({
|
|
806
|
+
content: []
|
|
807
|
+
});
|
|
808
|
+
} else if (typeof maybeStringResult === "string") {
|
|
785
809
|
result = ContentResultZodSchema.parse({
|
|
786
810
|
content: [{ text: maybeStringResult, type: "text" }]
|
|
787
811
|
});
|
|
@@ -828,7 +852,6 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
828
852
|
#resources = [];
|
|
829
853
|
#resourcesTemplates = [];
|
|
830
854
|
#sessions = [];
|
|
831
|
-
#sseServer = null;
|
|
832
855
|
#tools = [];
|
|
833
856
|
/**
|
|
834
857
|
* Adds a prompt to the server.
|
|
@@ -878,44 +901,8 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
878
901
|
this.emit("connect", {
|
|
879
902
|
session
|
|
880
903
|
});
|
|
881
|
-
} else if (options.transportType === "sse") {
|
|
882
|
-
this.#sseServer = await startSSEServer({
|
|
883
|
-
createServer: async (request) => {
|
|
884
|
-
let auth;
|
|
885
|
-
if (this.#authenticate) {
|
|
886
|
-
auth = await this.#authenticate(request);
|
|
887
|
-
}
|
|
888
|
-
return new FastMCPSession({
|
|
889
|
-
auth,
|
|
890
|
-
name: this.#options.name,
|
|
891
|
-
ping: this.#options.ping,
|
|
892
|
-
prompts: this.#prompts,
|
|
893
|
-
resources: this.#resources,
|
|
894
|
-
resourcesTemplates: this.#resourcesTemplates,
|
|
895
|
-
roots: this.#options.roots,
|
|
896
|
-
tools: this.#tools,
|
|
897
|
-
version: this.#options.version
|
|
898
|
-
});
|
|
899
|
-
},
|
|
900
|
-
endpoint: options.sse.endpoint,
|
|
901
|
-
onClose: (session) => {
|
|
902
|
-
this.emit("disconnect", {
|
|
903
|
-
session
|
|
904
|
-
});
|
|
905
|
-
},
|
|
906
|
-
onConnect: async (session) => {
|
|
907
|
-
this.#sessions.push(session);
|
|
908
|
-
this.emit("connect", {
|
|
909
|
-
session
|
|
910
|
-
});
|
|
911
|
-
},
|
|
912
|
-
port: options.sse.port
|
|
913
|
-
});
|
|
914
|
-
console.info(
|
|
915
|
-
`[FastMCP info] server is running on SSE at http://localhost:${options.sse.port}${options.sse.endpoint}`
|
|
916
|
-
);
|
|
917
904
|
} else if (options.transportType === "httpStream") {
|
|
918
|
-
this.#httpStreamServer = await
|
|
905
|
+
this.#httpStreamServer = await startHTTPServer({
|
|
919
906
|
createServer: async (request) => {
|
|
920
907
|
let auth;
|
|
921
908
|
if (this.#authenticate) {
|
|
@@ -933,7 +920,6 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
933
920
|
version: this.#options.version
|
|
934
921
|
});
|
|
935
922
|
},
|
|
936
|
-
endpoint: options.httpStream.endpoint,
|
|
937
923
|
onClose: (session) => {
|
|
938
924
|
this.emit("disconnect", {
|
|
939
925
|
session
|
|
@@ -948,7 +934,7 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
948
934
|
port: options.httpStream.port
|
|
949
935
|
});
|
|
950
936
|
console.info(
|
|
951
|
-
`[FastMCP info] server is running on HTTP Stream at http://localhost:${options.httpStream.port}
|
|
937
|
+
`[FastMCP info] server is running on HTTP Stream at http://localhost:${options.httpStream.port}/stream`
|
|
952
938
|
);
|
|
953
939
|
} else {
|
|
954
940
|
throw new Error("Invalid transport type");
|
|
@@ -958,9 +944,6 @@ var FastMCP = class extends FastMCPEventEmitter {
|
|
|
958
944
|
* Stops the server.
|
|
959
945
|
*/
|
|
960
946
|
async stop() {
|
|
961
|
-
if (this.#sseServer) {
|
|
962
|
-
await this.#sseServer.close();
|
|
963
|
-
}
|
|
964
947
|
if (this.#httpStreamServer) {
|
|
965
948
|
await this.#httpStreamServer.close();
|
|
966
949
|
}
|