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 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
- - [SSE](#sse)
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 SSE connections:
109
+ For HTTP streaming connections:
129
110
 
130
111
  ```ts
131
- import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
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 SSEClientTransport(new URL(`http://localhost:8080/sse`));
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 HTTP streaming connections:
131
+ For SSE connections:
149
132
 
150
133
  ```ts
151
- import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
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 StreamableHTTPClientTransport(
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 { AudioContent, Root, ClientCapabilities, CreateMessageRequestSchema } from '@modelcontextprotocol/sdk/types.js';
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 Content = ImageContent | TextContent;
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?: ToolAnnotations;
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 { startHTTPStreamServer, startSSEServer } from "mcp-proxy";
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 === "sse" || transport.type === "httpStream") {
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 (typeof maybeStringResult === "string") {
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 startHTTPStreamServer({
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}${options.httpStream.endpoint}`
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
  }