fastmcp 3.13.0 → 3.14.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
@@ -696,6 +696,44 @@ server.addTool({
696
696
  });
697
697
  ```
698
698
 
699
+ #### Custom Logger
700
+
701
+ FastMCP allows you to provide a custom logger implementation to control how the server logs messages. This is useful for integrating with existing logging infrastructure or customizing log formatting.
702
+
703
+ ```ts
704
+ import { FastMCP, Logger } from "fastmcp";
705
+
706
+ class CustomLogger implements Logger {
707
+ debug(...args: unknown[]): void {
708
+ console.log("[DEBUG]", new Date().toISOString(), ...args);
709
+ }
710
+
711
+ error(...args: unknown[]): void {
712
+ console.error("[ERROR]", new Date().toISOString(), ...args);
713
+ }
714
+
715
+ info(...args: unknown[]): void {
716
+ console.info("[INFO]", new Date().toISOString(), ...args);
717
+ }
718
+
719
+ log(...args: unknown[]): void {
720
+ console.log("[LOG]", new Date().toISOString(), ...args);
721
+ }
722
+
723
+ warn(...args: unknown[]): void {
724
+ console.warn("[WARN]", new Date().toISOString(), ...args);
725
+ }
726
+ }
727
+
728
+ const server = new FastMCP({
729
+ name: "My Server",
730
+ version: "1.0.0",
731
+ logger: new CustomLogger(),
732
+ });
733
+ ```
734
+
735
+ See `src/examples/custom-logger.ts` for examples with Winston, Pino, and file-based logging.
736
+
699
737
  #### Logging
700
738
 
701
739
  Tools can log messages to the client using the `log` object in the context object:
@@ -1713,6 +1751,7 @@ Refer to this [issue](https://github.com/punkpeye/fastmcp/issues/25#issuecomment
1713
1751
  - [cswkim/discogs-mcp-server](https://github.com/cswkim/discogs-mcp-server) - connects to the Discogs API for interacting with your music collection
1714
1752
  - [Panzer-Jack/feuse-mcp](https://github.com/Panzer-Jack/feuse-mcp) - Frontend Useful MCP Tools - Essential utilities for web developers to automate API integration and code generation
1715
1753
  - [sunra-ai/sunra-clients](https://github.com/sunra-ai/sunra-clients/tree/main/mcp-server) - Sunra.ai is a generative media platform built for developers, providing high-performance AI model inference capabilities.
1754
+ - [foxtrottwist/shortcuts-mcp](https://github.com/foxtrottwist/shortcuts-mcp) - connects Claude to macOS Shortcuts for system automation, app integration, and interactive workflows
1716
1755
 
1717
1756
  ## Acknowledgements
1718
1757
 
package/dist/FastMCP.d.ts CHANGED
@@ -10,6 +10,13 @@ import http from 'http';
10
10
  import { StrictEventEmitter } from 'strict-event-emitter-types';
11
11
  import { z } from 'zod';
12
12
 
13
+ interface Logger {
14
+ debug(...args: unknown[]): void;
15
+ error(...args: unknown[]): void;
16
+ info(...args: unknown[]): void;
17
+ log(...args: unknown[]): void;
18
+ warn(...args: unknown[]): void;
19
+ }
13
20
  type SSEServer = {
14
21
  close: () => Promise<void>;
15
22
  };
@@ -247,6 +254,11 @@ type ServerOptions<T extends FastMCPSessionAuth> = {
247
254
  status?: number;
248
255
  };
249
256
  instructions?: string;
257
+ /**
258
+ * Custom logger instance. If not provided, defaults to console.
259
+ * Use this to integrate with your own logging system.
260
+ */
261
+ logger?: Logger;
250
262
  name: string;
251
263
  /**
252
264
  * Configuration for OAuth well-known discovery endpoints that can be exposed
@@ -534,9 +546,10 @@ declare class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth>
534
546
  get loggingLevel(): LoggingLevel;
535
547
  get roots(): Root[];
536
548
  get server(): Server;
537
- constructor({ auth, instructions, name, ping, prompts, resources, resourcesTemplates, roots, tools, transportType, utils, version, }: {
549
+ constructor({ auth, instructions, logger, name, ping, prompts, resources, resourcesTemplates, roots, tools, transportType, utils, version, }: {
538
550
  auth?: T;
539
551
  instructions?: string;
552
+ logger: Logger;
540
553
  name: string;
541
554
  ping?: ServerOptions<T>["ping"];
542
555
  prompts: Prompt<T>[];
@@ -616,4 +629,4 @@ declare class FastMCP<T extends FastMCPSessionAuth = FastMCPSessionAuth> extends
616
629
  stop(): Promise<void>;
617
630
  }
618
631
 
619
- export { type AudioContent, type Content, type ContentResult, type Context, FastMCP, type FastMCPEvents, FastMCPSession, type FastMCPSessionEvents, type ImageContent, type InputPrompt, type InputPromptArgument, type LoggingLevel, type Progress, type Prompt, type PromptArgument, type Resource, type ResourceContent, type ResourceResult, type ResourceTemplate, type ResourceTemplateArgument, type SSEServer, type SerializableValue, type ServerOptions, type TextContent, type Tool, type ToolParameters, UnexpectedStateError, UserError, audioContent, imageContent };
632
+ export { type AudioContent, type Content, type ContentResult, type Context, FastMCP, type FastMCPEvents, FastMCPSession, type FastMCPSessionEvents, type ImageContent, type InputPrompt, type InputPromptArgument, type Logger, type LoggingLevel, type Progress, type Prompt, type PromptArgument, type Resource, type ResourceContent, type ResourceResult, type ResourceTemplate, type ResourceTemplateArgument, type SSEServer, type SerializableValue, type ServerOptions, type TextContent, type Tool, type ToolParameters, UnexpectedStateError, UserError, audioContent, imageContent };
package/dist/FastMCP.js CHANGED
@@ -237,6 +237,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
237
237
  #capabilities = {};
238
238
  #clientCapabilities;
239
239
  #connectionState = "connecting";
240
+ #logger;
240
241
  #loggingLevel = "info";
241
242
  #needsEventLoopFlush = false;
242
243
  #pingConfig;
@@ -251,6 +252,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
251
252
  constructor({
252
253
  auth,
253
254
  instructions,
255
+ logger,
254
256
  name,
255
257
  ping,
256
258
  prompts,
@@ -264,6 +266,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
264
266
  }) {
265
267
  super();
266
268
  this.#auth = auth;
269
+ this.#logger = logger;
267
270
  this.#pingConfig = ping;
268
271
  this.#rootsConfig = roots;
269
272
  this.#needsEventLoopFlush = transportType === "httpStream";
@@ -316,7 +319,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
316
319
  try {
317
320
  await this.#server.close();
318
321
  } catch (error) {
319
- console.error("[FastMCP error]", "could not close server", error);
322
+ this.#logger.error("[FastMCP error]", "could not close server", error);
320
323
  }
321
324
  }
322
325
  async connect(transport) {
@@ -338,7 +341,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
338
341
  await delay(retryDelay);
339
342
  }
340
343
  if (!this.#clientCapabilities) {
341
- console.warn(
344
+ this.#logger.warn(
342
345
  `[FastMCP warning] could not infer client capabilities after ${maxAttempts} attempts. Connection may be unstable.`
343
346
  );
344
347
  }
@@ -348,11 +351,11 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
348
351
  this.#roots = roots?.roots || [];
349
352
  } catch (e) {
350
353
  if (e instanceof McpError && e.code === ErrorCode.MethodNotFound) {
351
- console.debug(
354
+ this.#logger.debug(
352
355
  "[FastMCP debug] listRoots method not supported by client"
353
356
  );
354
357
  } else {
355
- console.error(
358
+ this.#logger.error(
356
359
  `[FastMCP error] received error listing roots.
357
360
 
358
361
  ${e instanceof Error ? e.stack : JSON.stringify(e)}`
@@ -369,17 +372,17 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
369
372
  } catch {
370
373
  const logLevel = pingConfig.logLevel;
371
374
  if (logLevel === "debug") {
372
- console.debug("[FastMCP debug] server ping failed");
375
+ this.#logger.debug("[FastMCP debug] server ping failed");
373
376
  } else if (logLevel === "warning") {
374
- console.warn(
377
+ this.#logger.warn(
375
378
  "[FastMCP warning] server is not responding to ping"
376
379
  );
377
380
  } else if (logLevel === "error") {
378
- console.error(
381
+ this.#logger.error(
379
382
  "[FastMCP error] server is not responding to ping"
380
383
  );
381
384
  } else {
382
- console.info("[FastMCP info] server ping failed");
385
+ this.#logger.info("[FastMCP info] server ping failed");
383
386
  }
384
387
  }
385
388
  }, pingConfig.intervalMs);
@@ -565,7 +568,7 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
565
568
  }
566
569
  setupErrorHandling() {
567
570
  this.#server.onerror = (error) => {
568
- console.error("[FastMCP error]", error);
571
+ this.#logger.error("[FastMCP error]", error);
569
572
  };
570
573
  }
571
574
  setupLoggingHandlers() {
@@ -731,7 +734,7 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
731
734
  }
732
735
  setupRootsHandlers() {
733
736
  if (this.#rootsConfig?.enabled === false) {
734
- console.debug(
737
+ this.#logger.debug(
735
738
  "[FastMCP debug] roots capability explicitly disabled via config"
736
739
  );
737
740
  return;
@@ -747,11 +750,11 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
747
750
  });
748
751
  }).catch((error) => {
749
752
  if (error instanceof McpError && error.code === ErrorCode.MethodNotFound) {
750
- console.debug(
753
+ this.#logger.debug(
751
754
  "[FastMCP debug] listRoots method not supported by client"
752
755
  );
753
756
  } else {
754
- console.error(
757
+ this.#logger.error(
755
758
  `[FastMCP error] received error listing roots.
756
759
 
757
760
  ${error instanceof Error ? error.stack : JSON.stringify(error)}`
@@ -761,7 +764,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
761
764
  }
762
765
  );
763
766
  } else {
764
- console.debug(
767
+ this.#logger.debug(
765
768
  "[FastMCP debug] roots capability not available, not setting up notification handler"
766
769
  );
767
770
  }
@@ -827,7 +830,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
827
830
  await new Promise((resolve) => setImmediate(resolve));
828
831
  }
829
832
  } catch (progressError) {
830
- console.warn(
833
+ this.#logger.warn(
831
834
  `[FastMCP warning] Failed to report progress for tool '${request.params.name}':`,
832
835
  progressError instanceof Error ? progressError.message : String(progressError)
833
836
  );
@@ -885,7 +888,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
885
888
  await new Promise((resolve) => setImmediate(resolve));
886
889
  }
887
890
  } catch (streamError) {
888
- console.warn(
891
+ this.#logger.warn(
889
892
  `[FastMCP warning] Failed to stream content for tool '${request.params.name}':`,
890
893
  streamError instanceof Error ? streamError.message : String(streamError)
891
894
  );
@@ -968,12 +971,14 @@ var FastMCP = class extends FastMCPEventEmitter {
968
971
  this.options = options;
969
972
  this.#options = options;
970
973
  this.#authenticate = options.authenticate;
974
+ this.#logger = options.logger || console;
971
975
  }
972
976
  get sessions() {
973
977
  return this.#sessions;
974
978
  }
975
979
  #authenticate;
976
980
  #httpStreamServer = null;
981
+ #logger;
977
982
  #options;
978
983
  #prompts = [];
979
984
  #resources = [];
@@ -1073,6 +1078,7 @@ var FastMCP = class extends FastMCPEventEmitter {
1073
1078
  const transport = new StdioServerTransport();
1074
1079
  const session = new FastMCPSession({
1075
1080
  instructions: this.#options.instructions,
1081
+ logger: this.#logger,
1076
1082
  name: this.#options.name,
1077
1083
  ping: this.#options.ping,
1078
1084
  prompts: this.#prompts,
@@ -1092,7 +1098,7 @@ var FastMCP = class extends FastMCPEventEmitter {
1092
1098
  } else if (config.transportType === "httpStream") {
1093
1099
  const httpConfig = config.httpStream;
1094
1100
  if (httpConfig.stateless) {
1095
- console.info(
1101
+ this.#logger.info(
1096
1102
  `[FastMCP info] Starting server in stateless mode on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`
1097
1103
  );
1098
1104
  this.#httpStreamServer = await startHTTPServer({
@@ -1109,7 +1115,7 @@ var FastMCP = class extends FastMCPEventEmitter {
1109
1115
  onClose: async () => {
1110
1116
  },
1111
1117
  onConnect: async () => {
1112
- console.debug(
1118
+ this.#logger.debug(
1113
1119
  `[FastMCP debug] Stateless HTTP Stream request handled`
1114
1120
  );
1115
1121
  },
@@ -1138,7 +1144,7 @@ var FastMCP = class extends FastMCPEventEmitter {
1138
1144
  },
1139
1145
  onConnect: async (session) => {
1140
1146
  this.#sessions.push(session);
1141
- console.info(`[FastMCP info] HTTP Stream session established`);
1147
+ this.#logger.info(`[FastMCP info] HTTP Stream session established`);
1142
1148
  this.emit("connect", {
1143
1149
  session
1144
1150
  });
@@ -1149,10 +1155,10 @@ var FastMCP = class extends FastMCPEventEmitter {
1149
1155
  port: httpConfig.port,
1150
1156
  streamEndpoint: httpConfig.endpoint
1151
1157
  });
1152
- console.info(
1158
+ this.#logger.info(
1153
1159
  `[FastMCP info] server is running on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`
1154
1160
  );
1155
- console.info(
1161
+ this.#logger.info(
1156
1162
  `[FastMCP info] Transport type: httpStream (Streamable HTTP, not SSE)`
1157
1163
  );
1158
1164
  }
@@ -1178,6 +1184,7 @@ var FastMCP = class extends FastMCPEventEmitter {
1178
1184
  ) : this.#tools;
1179
1185
  return new FastMCPSession({
1180
1186
  auth,
1187
+ logger: this.#logger,
1181
1188
  name: this.#options.name,
1182
1189
  ping: this.#options.ping,
1183
1190
  prompts: this.#prompts,
@@ -1235,7 +1242,7 @@ var FastMCP = class extends FastMCPEventEmitter {
1235
1242
  return;
1236
1243
  }
1237
1244
  } catch (error) {
1238
- console.error("[FastMCP error] health endpoint error", error);
1245
+ this.#logger.error("[FastMCP error] health endpoint error", error);
1239
1246
  }
1240
1247
  }
1241
1248
  const oauthConfig = this.#options.oauth;
@@ -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 { EventStore } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { RequestOptions } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n CallToolRequestSchema,\n ClientCapabilities,\n CompleteRequestSchema,\n CreateMessageRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n GetPromptResult,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListResourcesResult,\n ListResourceTemplatesRequestSchema,\n ListResourceTemplatesResult,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n ResourceLink,\n Root,\n RootsListChangedNotificationSchema,\n ServerCapabilities,\n SetLevelRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { EventEmitter } from \"events\";\nimport { readFile } from \"fs/promises\";\nimport Fuse from \"fuse.js\";\nimport http from \"http\";\nimport { startHTTPServer } from \"mcp-proxy\";\nimport { StrictEventEmitter } from \"strict-event-emitter-types\";\nimport { setTimeout as delay } from \"timers/promises\";\nimport { fetch } from \"undici\";\nimport parseURITemplate from \"uri-templates\";\nimport { toJsonSchema } from \"xsschema\";\nimport { z } from \"zod\";\n\nexport type SSEServer = {\n close: () => Promise<void>;\n};\n\ntype FastMCPEvents<T extends FastMCPSessionAuth> = {\n connect: (event: { session: FastMCPSession<T> }) => void;\n disconnect: (event: { session: FastMCPSession<T> }) => void;\n};\n\ntype FastMCPSessionEvents = {\n error: (event: { error: Error }) => void;\n ready: () => void;\n rootsChanged: (event: { roots: Root[] }) => void;\n};\n\nexport const imageContent = async (\n input: { buffer: Buffer } | { path: string } | { url: string },\n): Promise<ImageContent> => {\n let rawData: Buffer;\n\n try {\n if (\"url\" in input) {\n try {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(\n `Server responded with status: ${response.status} - ${response.statusText}`,\n );\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } catch (error) {\n throw new Error(\n `Failed to fetch image from URL (${input.url}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n } else if (\"path\" in input) {\n try {\n rawData = await readFile(input.path);\n } catch (error) {\n throw new Error(\n `Failed to read image from path (${input.path}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\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 { fileTypeFromBuffer } = await import(\"file-type\");\n const mimeType = await fileTypeFromBuffer(rawData);\n\n if (!mimeType || !mimeType.mime.startsWith(\"image/\")) {\n console.warn(\n `Warning: Content may not be a valid image. Detected MIME: ${\n mimeType?.mime || \"unknown\"\n }`,\n );\n }\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n data: base64Data,\n mimeType: mimeType?.mime ?? \"image/png\",\n type: \"image\",\n } as const;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n } else {\n throw new Error(`Unexpected error processing image: ${String(error)}`);\n }\n }\n};\n\nexport const audioContent = async (\n input: { buffer: Buffer } | { path: string } | { url: string },\n): Promise<AudioContent> => {\n let rawData: Buffer;\n\n try {\n if (\"url\" in input) {\n try {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(\n `Server responded with status: ${response.status} - ${response.statusText}`,\n );\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } catch (error) {\n throw new Error(\n `Failed to fetch audio from URL (${input.url}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n } else if (\"path\" in input) {\n try {\n rawData = await readFile(input.path);\n } catch (error) {\n throw new Error(\n `Failed to read audio from path (${input.path}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\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 { fileTypeFromBuffer } = await import(\"file-type\");\n const mimeType = await fileTypeFromBuffer(rawData);\n\n if (!mimeType || !mimeType.mime.startsWith(\"audio/\")) {\n console.warn(\n `Warning: Content may not be a valid audio file. Detected MIME: ${\n mimeType?.mime || \"unknown\"\n }`,\n );\n }\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n data: base64Data,\n mimeType: mimeType?.mime ?? \"audio/mpeg\",\n type: \"audio\",\n } as const;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n } else {\n throw new Error(`Unexpected error processing audio: ${String(error)}`);\n }\n }\n};\n\ntype Context<T extends FastMCPSessionAuth> = {\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 reportProgress: (progress: Progress) => Promise<void>;\n session: T | undefined;\n streamContent: (content: Content | Content[]) => Promise<void>;\n};\n\ntype Extra = unknown;\n\ntype Extras = Record<string, Extra>;\n\ntype Literal = boolean | null | number | string | undefined;\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 SerializableValue =\n | { [key: string]: SerializableValue }\n | Literal\n | SerializableValue[];\n\ntype TextContent = {\n text: string;\n type: \"text\";\n};\n\ntype ToolParameters = StandardSchemaV1;\n\nabstract class FastMCPError extends Error {\n public constructor(message?: string) {\n super(message);\n this.name = new.target.name;\n }\n}\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\nconst TextContentZodSchema = z\n .object({\n /**\n * The text content of the message.\n */\n text: z.string(),\n type: z.literal(\"text\"),\n })\n .strict() satisfies z.ZodType<TextContent>;\n\ntype ImageContent = {\n data: string;\n mimeType: string;\n type: \"image\";\n};\n\nconst ImageContentZodSchema = z\n .object({\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 type: z.literal(\"image\"),\n })\n .strict() satisfies z.ZodType<ImageContent>;\n\ntype AudioContent = {\n data: string;\n mimeType: string;\n type: \"audio\";\n};\n\nconst AudioContentZodSchema = z\n .object({\n /**\n * The base64-encoded audio data.\n */\n data: z.string().base64(),\n mimeType: z.string(),\n type: z.literal(\"audio\"),\n })\n .strict() satisfies z.ZodType<AudioContent>;\n\ntype ResourceContent = {\n resource: {\n blob?: string;\n mimeType?: string;\n text?: string;\n uri: string;\n };\n type: \"resource\";\n};\n\nconst ResourceContentZodSchema = z\n .object({\n resource: z.object({\n blob: z.string().optional(),\n mimeType: z.string().optional(),\n text: z.string().optional(),\n uri: z.string(),\n }),\n type: z.literal(\"resource\"),\n })\n .strict() satisfies z.ZodType<ResourceContent>;\n\nconst ResourceLinkZodSchema = z.object({\n description: z.string().optional(),\n mimeType: z.string().optional(),\n name: z.string(),\n title: z.string().optional(),\n type: z.literal(\"resource_link\"),\n uri: z.string(),\n}) satisfies z.ZodType<ResourceLink>;\n\ntype Content =\n | AudioContent\n | ImageContent\n | ResourceContent\n | ResourceLink\n | TextContent;\n\nconst ContentZodSchema = z.discriminatedUnion(\"type\", [\n TextContentZodSchema,\n ImageContentZodSchema,\n AudioContentZodSchema,\n ResourceContentZodSchema,\n ResourceLinkZodSchema,\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 hasMore?: boolean;\n total?: number;\n values: string[];\n};\n\n/**\n * https://github.com/modelcontextprotocol/typescript-sdk/blob/3164da64d085ec4e022ae881329eee7b72f208d4/src/types.ts#L983-L1003\n */\nconst CompletionZodSchema = z.object({\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 /**\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 * An array of completion values. Must not exceed 100 items.\n */\n values: z.array(z.string()).max(100),\n}) satisfies z.ZodType<Completion>;\n\ntype ArgumentValueCompleter<T extends FastMCPSessionAuth = FastMCPSessionAuth> =\n (value: string, auth?: T) => Promise<Completion>;\n\ntype InputPrompt<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n Arguments extends InputPromptArgument<T>[] = InputPromptArgument<T>[],\n Args = PromptArgumentsToObject<Arguments>,\n> = {\n arguments?: InputPromptArgument<T>[];\n description?: string;\n load: (args: Args, auth?: T) => Promise<PromptResult>;\n name: string;\n};\n\ntype InputPromptArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> =\n Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n enum?: string[];\n name: string;\n required?: boolean;\n }>;\n\ntype InputResourceTemplate<\n T extends FastMCPSessionAuth,\n Arguments extends\n InputResourceTemplateArgument<T>[] = InputResourceTemplateArgument<T>[],\n> = {\n arguments: Arguments;\n description?: string;\n load: (\n args: ResourceTemplateArgumentsToObject<Arguments>,\n auth?: T,\n ) => Promise<ResourceResult | ResourceResult[]>;\n mimeType?: string;\n name: string;\n uriTemplate: string;\n};\n\ntype InputResourceTemplateArgument<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> = Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n name: string;\n required?: boolean;\n}>;\n\ntype LoggingLevel =\n | \"alert\"\n | \"critical\"\n | \"debug\"\n | \"emergency\"\n | \"error\"\n | \"info\"\n | \"notice\"\n | \"warning\";\n\ntype Prompt<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n Arguments extends PromptArgument<T>[] = PromptArgument<T>[],\n Args = PromptArgumentsToObject<Arguments>,\n> = {\n arguments?: PromptArgument<T>[];\n complete?: (name: string, value: string, auth?: T) => Promise<Completion>;\n description?: string;\n load: (args: Args, auth?: T) => Promise<PromptResult>;\n name: string;\n};\n\ntype PromptArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> =\n Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n enum?: string[];\n name: string;\n required?: boolean;\n }>;\n\ntype PromptArgumentsToObject<T extends { name: string; required?: boolean }[]> =\n {\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 PromptResult = Pick<GetPromptResult, \"messages\"> | string;\n\ntype Resource<T extends FastMCPSessionAuth> = {\n complete?: (name: string, value: string, auth?: T) => Promise<Completion>;\n description?: string;\n load: (auth?: T) => Promise<ResourceResult | ResourceResult[]>;\n mimeType?: string;\n name: string;\n uri: string;\n};\n\ntype ResourceResult =\n | {\n blob: string;\n mimeType?: string;\n uri?: string;\n }\n | {\n mimeType?: string;\n text: string;\n uri?: string;\n };\n\ntype ResourceTemplate<\n T extends FastMCPSessionAuth,\n Arguments extends\n ResourceTemplateArgument<T>[] = ResourceTemplateArgument<T>[],\n> = {\n arguments: Arguments;\n complete?: (name: string, value: string, auth?: T) => Promise<Completion>;\n description?: string;\n load: (\n args: ResourceTemplateArgumentsToObject<Arguments>,\n auth?: T,\n ) => Promise<ResourceResult | ResourceResult[]>;\n mimeType?: string;\n name: string;\n uriTemplate: string;\n};\n\ntype ResourceTemplateArgument<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> = Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n name: string;\n required?: boolean;\n}>;\n\ntype ResourceTemplateArgumentsToObject<T extends { name: string }[]> = {\n [K in T[number][\"name\"]]: string;\n};\n\ntype SamplingResponse = {\n content: AudioContent | ImageContent | TextContent;\n model: string;\n role: \"assistant\" | \"user\";\n stopReason?: \"endTurn\" | \"maxTokens\" | \"stopSequence\" | string;\n};\n\ntype ServerOptions<T extends FastMCPSessionAuth> = {\n authenticate?: Authenticate<T>;\n /**\n * Configuration for the health-check endpoint that can be exposed when the\n * server is running using the HTTP Stream transport. When enabled, the\n * server will respond to an HTTP GET request with the configured path (by\n * default \"/health\") rendering a plain-text response (by default \"ok\") and\n * the configured status code (by default 200).\n *\n * The endpoint is only added when the server is started with\n * `transportType: \"httpStream\"` – it is ignored for the stdio transport.\n */\n health?: {\n /**\n * When set to `false` the health-check endpoint is disabled.\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Plain-text body returned by the endpoint.\n * @default \"ok\"\n */\n message?: string;\n\n /**\n * HTTP path that should be handled.\n * @default \"/health\"\n */\n path?: string;\n\n /**\n * HTTP response status that will be returned.\n * @default 200\n */\n status?: number;\n };\n instructions?: string;\n name: string;\n\n /**\n * Configuration for OAuth well-known discovery endpoints that can be exposed\n * when the server is running using HTTP-based transports (SSE or HTTP Stream).\n * When enabled, the server will respond to requests for OAuth discovery endpoints\n * with the configured metadata.\n *\n * The endpoints are only added when the server is started with\n * `transportType: \"httpStream\"` – they are ignored for the stdio transport.\n * Both SSE and HTTP Stream transports support OAuth endpoints.\n */\n oauth?: {\n /**\n * OAuth Authorization Server metadata for /.well-known/oauth-authorization-server\n *\n * This endpoint follows RFC 8414 (OAuth 2.0 Authorization Server Metadata)\n * and provides metadata about the OAuth 2.0 authorization server.\n *\n * Required by MCP Specification 2025-03-26\n */\n authorizationServer?: {\n authorizationEndpoint: string;\n codeChallengeMethodsSupported?: string[];\n // DPoP support\n dpopSigningAlgValuesSupported?: string[];\n grantTypesSupported?: string[];\n\n introspectionEndpoint?: string;\n // Required\n issuer: string;\n // Common optional\n jwksUri?: string;\n opPolicyUri?: string;\n opTosUri?: string;\n registrationEndpoint?: string;\n responseModesSupported?: string[];\n responseTypesSupported: string[];\n revocationEndpoint?: string;\n scopesSupported?: string[];\n serviceDocumentation?: string;\n tokenEndpoint: string;\n tokenEndpointAuthMethodsSupported?: string[];\n tokenEndpointAuthSigningAlgValuesSupported?: string[];\n\n uiLocalesSupported?: string[];\n };\n\n /**\n * Whether OAuth discovery endpoints should be enabled.\n */\n enabled: boolean;\n\n /**\n * OAuth Protected Resource metadata for `/.well-known/oauth-protected-resource`\n *\n * This endpoint follows {@link https://www.rfc-editor.org/rfc/rfc9728.html | RFC 9728}\n * and provides metadata describing how an OAuth 2.0 protected resource (in this case,\n * an MCP server) expects to be accessed.\n *\n * When configured, FastMCP will automatically serve this metadata at the\n * `/.well-known/oauth-protected-resource` endpoint. The `authorizationServers` and `resource`\n * fields are required. All others are optional and will be omitted from the published\n * metadata if not specified.\n *\n * This satisfies the requirements of the MCP Authorization specification's\n * {@link https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#authorization-server-location | Authorization Server Location section}.\n *\n * Clients consuming this metadata MUST validate that any presented values comply with\n * RFC 9728, including strict validation of the `resource` identifier and intended audience\n * when access tokens are issued and presented (per RFC 8707 §2).\n *\n * @remarks Required by MCP Specification version 2025-06-18\n */\n protectedResource?: {\n /**\n * Allows for additional metadata fields beyond those defined in RFC 9728.\n *\n * @remarks This supports vendor-specific or experimental extensions.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2.3 | RFC 9728 §2.3}\n */\n [key: string]: unknown;\n\n /**\n * Supported values for the `authorization_details` parameter (RFC 9396).\n *\n * @remarks Used when fine-grained access control is in play.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.23 | RFC 9728 §2.2.23}\n */\n authorizationDetailsTypesSupported?: string[];\n\n /**\n * List of OAuth 2.0 authorization server issuer identifiers.\n *\n * These correspond to ASes that can issue access tokens for this protected resource.\n * MCP clients use these values to locate the relevant `/.well-known/oauth-authorization-server`\n * metadata for initiating the OAuth flow.\n *\n * @remarks Required by the MCP spec. MCP servers MUST provide at least one issuer.\n * Clients are responsible for choosing among them (see RFC 9728 §7.6).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.3 | RFC 9728 §2.2.3}\n */\n authorizationServers: string[];\n\n /**\n * List of supported methods for presenting OAuth 2.0 bearer tokens.\n *\n * @remarks Valid values are `header`, `body`, and `query`.\n * If omitted, clients MAY assume only `header` is supported, per RFC 6750.\n * This is a client-side interpretation and not a serialization default.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.9 | RFC 9728 §2.2.9}\n */\n bearerMethodsSupported?: string[];\n\n /**\n * Whether this resource requires all access tokens to be DPoP-bound.\n *\n * @remarks If omitted, clients SHOULD assume this is `false`.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.27 | RFC 9728 §2.2.27}\n */\n dpopBoundAccessTokensRequired?: boolean;\n\n /**\n * Supported algorithms for verifying DPoP proofs (RFC 9449).\n *\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.25 | RFC 9728 §2.2.25}\n */\n dpopSigningAlgValuesSupported?: string[];\n\n /**\n * JWKS URI of this resource. Used to validate access tokens or sign responses.\n *\n * @remarks When present, this MUST be an `https:` URI pointing to a valid JWK Set (RFC 7517).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.5 | RFC 9728 §2.2.5}\n */\n jwksUri?: string;\n\n /**\n * Canonical OAuth resource identifier for this protected resource (the MCP server).\n *\n * @remarks Typically the base URL of the MCP server. Clients MUST use this as the\n * `resource` parameter in authorization and token requests (per RFC 8707).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.1 | RFC 9728 §2.2.1}\n */\n resource: string;\n\n /**\n * URL to developer-accessible documentation for this resource.\n *\n * @remarks This field MAY be localized.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.15 | RFC 9728 §2.2.15}\n */\n resourceDocumentation?: string;\n\n /**\n * Human-readable name for display purposes (e.g., in UIs).\n *\n * @remarks This field MAY be localized using language tags (`resource_name#en`, etc.).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.13 | RFC 9728 §2.2.13}\n */\n resourceName?: string;\n\n /**\n * URL to a human-readable policy page describing acceptable use.\n *\n * @remarks This field MAY be localized.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.17 | RFC 9728 §2.2.17}\n */\n resourcePolicyUri?: string;\n\n /**\n * Supported JWS algorithms for signed responses from this resource (e.g., response signing).\n *\n * @remarks MUST NOT include `none`.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.11 | RFC 9728 §2.2.11}\n */\n resourceSigningAlgValuesSupported?: string[];\n\n /**\n * URL to the protected resource’s Terms of Service.\n *\n * @remarks This field MAY be localized.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.19 | RFC 9728 §2.2.19}\n */\n resourceTosUri?: string;\n\n /**\n * Supported OAuth scopes for requesting access to this resource.\n *\n * @remarks Useful for discovery, but clients SHOULD still request the minimal scope required.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.7 | RFC 9728 §2.2.7}\n */\n scopesSupported?: string[];\n\n /**\n * Developer-accessible documentation for how to use the service (not end-user docs).\n *\n * @remarks Semantically equivalent to `resourceDocumentation`, but included under its\n * alternate name for compatibility with tools or schemas expecting either.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.15 | RFC 9728 §2.2.15}\n */\n serviceDocumentation?: string;\n\n /**\n * Whether mutual-TLS-bound access tokens are required.\n *\n * @remarks If omitted, clients SHOULD assume this is `false` (client-side behavior).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.21 | RFC 9728 §2.2.21}\n */\n tlsClientCertificateBoundAccessTokens?: boolean;\n };\n };\n\n ping?: {\n /**\n * Whether ping should be enabled by default.\n * - true for SSE or HTTP Stream\n * - false for stdio\n */\n enabled?: boolean;\n /**\n * Interval\n * @default 5000 (5s)\n */\n intervalMs?: number;\n /**\n * Logging level for ping-related messages.\n * @default 'debug'\n */\n logLevel?: LoggingLevel;\n };\n /**\n * Configuration for roots capability\n */\n roots?: {\n /**\n * Whether roots capability should be enabled\n * Set to false to completely disable roots support\n * @default true\n */\n enabled?: boolean;\n };\n /**\n * General utilities\n */\n utils?: {\n formatInvalidParamsErrorMessage?: (\n issues: readonly StandardSchemaV1.Issue[],\n ) => string;\n };\n version: `${number}.${number}.${number}`;\n};\n\ntype Tool<\n T extends FastMCPSessionAuth,\n Params extends ToolParameters = ToolParameters,\n> = {\n annotations?: {\n /**\n * When true, the tool leverages incremental content streaming\n * Return void for tools that handle all their output via streaming\n */\n streamingHint?: boolean;\n } & ToolAnnotations;\n canAccess?: (auth: T) => boolean;\n description?: string;\n\n execute: (\n args: StandardSchemaV1.InferOutput<Params>,\n context: Context<T>,\n ) => Promise<\n | AudioContent\n | ContentResult\n | ImageContent\n | ResourceContent\n | ResourceLink\n | string\n | TextContent\n | void\n >;\n name: string;\n parameters?: Params;\n timeoutMs?: number;\n};\n\n/**\n * Tool annotations as defined in MCP Specification (2025-03-26)\n * These provide hints about a tool's behavior.\n */\ntype ToolAnnotations = {\n /**\n * If true, the tool may perform destructive updates\n * Only meaningful when readOnlyHint is false\n * @default true\n */\n destructiveHint?: boolean;\n\n /**\n * If true, calling the tool repeatedly with the same arguments has no additional effect\n * Only meaningful when readOnlyHint is false\n * @default false\n */\n idempotentHint?: boolean;\n\n /**\n * If true, the tool may interact with an \"open world\" of external entities\n * @default true\n */\n openWorldHint?: boolean;\n\n /**\n * If true, indicates the tool does not modify its environment\n * @default false\n */\n readOnlyHint?: boolean;\n\n /**\n * A human-readable title for the tool, useful for UI display\n */\n title?: string;\n};\n\nconst FastMCPSessionEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPSessionEvents>;\n} = EventEmitter;\n\ntype Authenticate<T> = (request: http.IncomingMessage) => Promise<T>;\n\ntype FastMCPSessionAuth = Record<string, unknown> | undefined;\n\nclass FastMCPSessionEventEmitter extends FastMCPSessionEventEmitterBase {}\n\nexport class FastMCPSession<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> extends FastMCPSessionEventEmitter {\n public get clientCapabilities(): ClientCapabilities | null {\n return this.#clientCapabilities ?? null;\n }\n public get isReady(): boolean {\n return this.#connectionState === \"ready\";\n }\n public get loggingLevel(): LoggingLevel {\n return this.#loggingLevel;\n }\n public get roots(): Root[] {\n return this.#roots;\n }\n public get server(): Server {\n return this.#server;\n }\n #auth: T | undefined;\n #capabilities: ServerCapabilities = {};\n #clientCapabilities?: ClientCapabilities;\n #connectionState: \"closed\" | \"connecting\" | \"error\" | \"ready\" = \"connecting\";\n #loggingLevel: LoggingLevel = \"info\";\n #needsEventLoopFlush: boolean = false;\n #pingConfig?: ServerOptions<T>[\"ping\"];\n\n #pingInterval: null | ReturnType<typeof setInterval> = null;\n\n #prompts: Prompt<T>[] = [];\n\n #resources: Resource<T>[] = [];\n\n #resourceTemplates: ResourceTemplate<T>[] = [];\n\n #roots: Root[] = [];\n\n #rootsConfig?: ServerOptions<T>[\"roots\"];\n\n #server: Server;\n\n #utils?: ServerOptions<T>[\"utils\"];\n\n constructor({\n auth,\n instructions,\n name,\n ping,\n prompts,\n resources,\n resourcesTemplates,\n roots,\n tools,\n transportType,\n utils,\n version,\n }: {\n auth?: T;\n instructions?: string;\n name: string;\n ping?: ServerOptions<T>[\"ping\"];\n prompts: Prompt<T>[];\n resources: Resource<T>[];\n resourcesTemplates: InputResourceTemplate<T>[];\n roots?: ServerOptions<T>[\"roots\"];\n tools: Tool<T>[];\n transportType?: \"httpStream\" | \"stdio\";\n utils?: ServerOptions<T>[\"utils\"];\n version: string;\n }) {\n super();\n\n this.#auth = auth;\n this.#pingConfig = ping;\n this.#rootsConfig = roots;\n this.#needsEventLoopFlush = transportType === \"httpStream\";\n\n if (tools.length) {\n this.#capabilities.tools = {};\n }\n\n if (resources.length || resourcesTemplates.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, instructions: instructions },\n );\n\n this.#utils = utils;\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 || resourcesTemplates.length) {\n for (const resource of resources) {\n this.addResource(resource);\n }\n\n this.setupResourceHandlers(resources);\n\n if (resourcesTemplates.length) {\n for (const resourceTemplate of resourcesTemplates) {\n this.addResourceTemplate(resourceTemplate);\n }\n\n this.setupResourceTemplateHandlers(resourcesTemplates);\n }\n }\n\n if (prompts.length) {\n this.setupPromptHandlers(prompts);\n }\n }\n\n public async close() {\n this.#connectionState = \"closed\";\n\n if (this.#pingInterval) {\n clearInterval(this.#pingInterval);\n }\n\n try {\n await this.#server.close();\n } catch (error) {\n console.error(\"[FastMCP error]\", \"could not close server\", error);\n }\n }\n\n public async connect(transport: Transport) {\n if (this.#server.transport) {\n throw new UnexpectedStateError(\"Server is already connected\");\n }\n\n this.#connectionState = \"connecting\";\n\n try {\n await this.#server.connect(transport);\n\n let attempt = 0;\n const maxAttempts = 10;\n const retryDelay = 100;\n\n while (attempt++ < maxAttempts) {\n const capabilities = this.#server.getClientCapabilities();\n\n if (capabilities) {\n this.#clientCapabilities = capabilities;\n break;\n }\n\n await delay(retryDelay);\n }\n\n if (!this.#clientCapabilities) {\n console.warn(\n `[FastMCP warning] could not infer client capabilities after ${maxAttempts} attempts. Connection may be unstable.`,\n );\n }\n\n if (\n this.#clientCapabilities?.roots?.listChanged &&\n typeof this.#server.listRoots === \"function\"\n ) {\n try {\n const roots = await this.#server.listRoots();\n this.#roots = roots?.roots || [];\n } catch (e) {\n if (e instanceof McpError && e.code === ErrorCode.MethodNotFound) {\n console.debug(\n \"[FastMCP debug] listRoots method not supported by client\",\n );\n } else {\n console.error(\n `[FastMCP error] received error listing roots.\\n\\n${\n e instanceof Error ? e.stack : JSON.stringify(e)\n }`,\n );\n }\n }\n }\n\n if (this.#clientCapabilities) {\n const pingConfig = this.#getPingConfig(transport);\n\n if (pingConfig.enabled) {\n this.#pingInterval = setInterval(async () => {\n try {\n await this.#server.ping();\n } catch {\n // The reason we are not emitting an error here is because some clients\n // seem to not respond to the ping request, and we don't want to crash the server,\n // e.g., https://github.com/punkpeye/fastmcp/issues/38.\n const logLevel = pingConfig.logLevel;\n\n if (logLevel === \"debug\") {\n console.debug(\"[FastMCP debug] server ping failed\");\n } else if (logLevel === \"warning\") {\n console.warn(\n \"[FastMCP warning] server is not responding to ping\",\n );\n } else if (logLevel === \"error\") {\n console.error(\n \"[FastMCP error] server is not responding to ping\",\n );\n } else {\n console.info(\"[FastMCP info] server ping failed\");\n }\n }\n }, pingConfig.intervalMs);\n }\n }\n\n // Mark connection as ready and emit event\n this.#connectionState = \"ready\";\n this.emit(\"ready\");\n } catch (error) {\n this.#connectionState = \"error\";\n const errorEvent = {\n error: error instanceof Error ? error : new Error(String(error)),\n };\n this.emit(\"error\", errorEvent);\n throw error;\n }\n }\n\n public async requestSampling(\n message: z.infer<typeof CreateMessageRequestSchema>[\"params\"],\n options?: RequestOptions,\n ): Promise<SamplingResponse> {\n return this.#server.createMessage(message, options);\n }\n\n public waitForReady(): Promise<void> {\n if (this.isReady) {\n return Promise.resolve();\n }\n\n if (\n this.#connectionState === \"error\" ||\n this.#connectionState === \"closed\"\n ) {\n return Promise.reject(\n new Error(`Connection is in ${this.#connectionState} state`),\n );\n }\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(\n new Error(\n \"Connection timeout: Session failed to become ready within 5 seconds\",\n ),\n );\n }, 5000);\n\n this.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve();\n });\n\n this.once(\"error\", (event) => {\n clearTimeout(timeout);\n reject(event.error);\n });\n });\n }\n\n #getPingConfig(transport: Transport): {\n enabled: boolean;\n intervalMs: number;\n logLevel: LoggingLevel;\n } {\n const pingConfig = this.#pingConfig || {};\n\n let defaultEnabled = false;\n\n if (\"type\" in transport) {\n // Enable by default for SSE and HTTP streaming\n if (transport.type === \"httpStream\") {\n defaultEnabled = true;\n }\n }\n\n return {\n enabled:\n pingConfig.enabled !== undefined ? pingConfig.enabled : defaultEnabled,\n intervalMs: pingConfig.intervalMs || 5000,\n logLevel: pingConfig.logLevel || \"debug\",\n };\n }\n\n private addPrompt(inputPrompt: InputPrompt<T>) {\n const completers: Record<string, ArgumentValueCompleter<T>> = {};\n const enums: Record<string, string[]> = {};\n const fuseInstances: Record<string, Fuse<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 fuseInstances[argument.name] = new Fuse(argument.enum, {\n includeScore: true,\n threshold: 0.3, // More flexible matching!\n });\n }\n }\n\n const prompt = {\n ...inputPrompt,\n complete: async (name: string, value: string, auth?: T) => {\n if (completers[name]) {\n return await completers[name](value, auth);\n }\n\n if (fuseInstances[name]) {\n const result = fuseInstances[name].search(value);\n\n return {\n total: result.length,\n values: result.map((item) => item.item),\n };\n }\n\n return {\n values: [],\n };\n },\n };\n\n this.#prompts.push(prompt);\n }\n\n private addResource(inputResource: Resource<T>) {\n this.#resources.push(inputResource);\n }\n\n private addResourceTemplate(inputResourceTemplate: InputResourceTemplate<T>) {\n const completers: Record<string, ArgumentValueCompleter<T>> = {};\n\n for (const argument of inputResourceTemplate.arguments ?? []) {\n if (argument.complete) {\n completers[argument.name] = argument.complete;\n }\n }\n\n const resourceTemplate = {\n ...inputResourceTemplate,\n complete: async (name: string, value: string, auth?: T) => {\n if (completers[name]) {\n return await completers[name](value, auth);\n }\n\n return {\n values: [],\n };\n },\n };\n\n this.#resourceTemplates.push(resourceTemplate);\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 this.#auth,\n ),\n );\n\n return {\n completion,\n };\n }\n\n if (request.params.ref.type === \"ref/resource\") {\n const resource = this.#resourceTemplates.find(\n (resource) => resource.uriTemplate === request.params.ref.uri,\n );\n\n if (!resource) {\n throw new UnexpectedStateError(\"Unknown resource\", {\n request,\n });\n }\n\n if (!(\"uriTemplate\" in resource)) {\n throw new UnexpectedStateError(\"Unexpected resource\");\n }\n\n if (!resource.complete) {\n throw new UnexpectedStateError(\n \"Resource does not support completion\",\n {\n request,\n },\n );\n }\n\n const completion = CompletionZodSchema.parse(\n await resource.complete(\n request.params.argument.name,\n request.params.argument.value,\n this.#auth,\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 setupErrorHandling() {\n this.#server.onerror = (error) => {\n console.error(\"[FastMCP error]\", error);\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 setupPromptHandlers(prompts: Prompt<T>[]) {\n this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: prompts.map((prompt) => {\n return {\n arguments: prompt.arguments,\n complete: prompt.complete,\n description: prompt.description,\n name: prompt.name,\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 `Prompt '${request.params.name}' requires argument '${arg.name}': ${\n arg.description || \"No description provided\"\n }`,\n );\n }\n }\n\n let result: Awaited<ReturnType<Prompt<T>[\"load\"]>>;\n\n try {\n result = await prompt.load(\n args as Record<string, string | undefined>,\n this.#auth,\n );\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to load prompt '${request.params.name}': ${errorMessage}`,\n );\n }\n\n if (typeof result === \"string\") {\n return {\n description: prompt.description,\n messages: [\n {\n content: { text: result, type: \"text\" },\n role: \"user\",\n },\n ],\n };\n } else {\n return {\n description: prompt.description,\n messages: result.messages,\n };\n }\n });\n }\n\n private setupResourceHandlers(resources: Resource<T>[]) {\n this.#server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: resources.map((resource) => ({\n description: resource.description,\n mimeType: resource.mimeType,\n name: resource.name,\n uri: resource.uri,\n })),\n } satisfies ListResourcesResult;\n });\n\n this.#server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request) => {\n if (\"uri\" in request.params) {\n const resource = resources.find(\n (resource) =>\n \"uri\" in resource && resource.uri === request.params.uri,\n );\n\n if (!resource) {\n for (const resourceTemplate of this.#resourceTemplates) {\n const uriTemplate = parseURITemplate(\n resourceTemplate.uriTemplate,\n );\n\n const match = uriTemplate.fromUri(request.params.uri);\n\n if (!match) {\n continue;\n }\n\n const uri = uriTemplate.fill(match);\n\n const result = await resourceTemplate.load(match, this.#auth);\n\n const resources = Array.isArray(result) ? result : [result];\n return {\n contents: resources.map((resource) => ({\n ...resource,\n description: resourceTemplate.description,\n mimeType: resource.mimeType ?? resourceTemplate.mimeType,\n name: resourceTemplate.name,\n uri: resource.uri ?? uri,\n })),\n };\n }\n\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Resource not found: '${request.params.uri}'. Available resources: ${\n resources.map((r) => r.uri).join(\", \") || \"none\"\n }`,\n );\n }\n\n if (!(\"uri\" in resource)) {\n throw new UnexpectedStateError(\"Resource does not support reading\");\n }\n\n let maybeArrayResult: Awaited<ReturnType<Resource<T>[\"load\"]>>;\n\n try {\n maybeArrayResult = await resource.load(this.#auth);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to load resource '${resource.name}' (${resource.uri}): ${errorMessage}`,\n {\n uri: resource.uri,\n },\n );\n }\n\n const resourceResults = Array.isArray(maybeArrayResult)\n ? maybeArrayResult\n : [maybeArrayResult];\n\n return {\n contents: resourceResults.map((result) => ({\n ...result,\n mimeType: result.mimeType ?? resource.mimeType,\n name: resource.name,\n uri: result.uri ?? resource.uri,\n })),\n };\n }\n\n throw new UnexpectedStateError(\"Unknown resource request\", {\n request,\n });\n },\n );\n }\n\n private setupResourceTemplateHandlers(\n resourceTemplates: ResourceTemplate<T>[],\n ) {\n this.#server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n async () => {\n return {\n resourceTemplates: resourceTemplates.map((resourceTemplate) => ({\n description: resourceTemplate.description,\n mimeType: resourceTemplate.mimeType,\n name: resourceTemplate.name,\n uriTemplate: resourceTemplate.uriTemplate,\n })),\n } satisfies ListResourceTemplatesResult;\n },\n );\n }\n\n private setupRootsHandlers() {\n if (this.#rootsConfig?.enabled === false) {\n console.debug(\n \"[FastMCP debug] roots capability explicitly disabled via config\",\n );\n return;\n }\n\n // Only set up roots notification handling if the server supports it\n if (typeof this.#server.listRoots === \"function\") {\n this.#server.setNotificationHandler(\n RootsListChangedNotificationSchema,\n () => {\n this.#server\n .listRoots()\n .then((roots) => {\n this.#roots = roots.roots;\n\n this.emit(\"rootsChanged\", {\n roots: roots.roots,\n });\n })\n .catch((error) => {\n if (\n error instanceof McpError &&\n error.code === ErrorCode.MethodNotFound\n ) {\n console.debug(\n \"[FastMCP debug] listRoots method not supported by client\",\n );\n } else {\n console.error(\n `[FastMCP error] received error listing roots.\\n\\n${\n error instanceof Error ? error.stack : JSON.stringify(error)\n }`,\n );\n }\n });\n },\n );\n } else {\n console.debug(\n \"[FastMCP debug] roots capability not available, not setting up notification handler\",\n );\n }\n }\n\n private setupToolHandlers(tools: Tool<T>[]) {\n this.#server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: await Promise.all(\n tools.map(async (tool) => {\n return {\n annotations: tool.annotations,\n description: tool.description,\n inputSchema: tool.parameters\n ? await toJsonSchema(tool.parameters)\n : {\n additionalProperties: false,\n properties: {},\n type: \"object\",\n }, // More complete schema for Cursor compatibility\n name: tool.name,\n };\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: unknown = undefined;\n\n if (tool.parameters) {\n const parsed = await tool.parameters[\"~standard\"].validate(\n request.params.arguments,\n );\n\n if (parsed.issues) {\n const friendlyErrors = this.#utils?.formatInvalidParamsErrorMessage\n ? this.#utils.formatInvalidParamsErrorMessage(parsed.issues)\n : parsed.issues\n .map((issue) => {\n const path = issue.path?.join(\".\") || \"root\";\n return `${path}: ${issue.message}`;\n })\n .join(\", \");\n\n throw new McpError(\n ErrorCode.InvalidParams,\n `Tool '${request.params.name}' parameter validation failed: ${friendlyErrors}. Please check the parameter types and values according to the tool's schema.`,\n );\n }\n\n args = parsed.value;\n }\n\n const progressToken = request.params?._meta?.progressToken;\n\n let result: ContentResult;\n\n try {\n const reportProgress = async (progress: Progress) => {\n try {\n await this.#server.notification({\n method: \"notifications/progress\",\n params: {\n ...progress,\n progressToken,\n },\n });\n\n if (this.#needsEventLoopFlush) {\n await new Promise((resolve) => setImmediate(resolve));\n }\n } catch (progressError) {\n console.warn(\n `[FastMCP warning] Failed to report progress for tool '${request.params.name}':`,\n progressError instanceof Error\n ? progressError.message\n : String(progressError),\n );\n }\n };\n\n const log = {\n debug: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"debug\",\n });\n },\n error: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"error\",\n });\n },\n info: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"info\",\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"warning\",\n });\n },\n };\n\n // Create a promise for tool execution\n // Streams partial results while a tool is still executing\n // Enables progressive rendering and real-time feedback\n const streamContent = async (content: Content | Content[]) => {\n const contentArray = Array.isArray(content) ? content : [content];\n\n try {\n await this.#server.notification({\n method: \"notifications/tool/streamContent\",\n params: {\n content: contentArray,\n toolName: request.params.name,\n },\n });\n\n if (this.#needsEventLoopFlush) {\n await new Promise((resolve) => setImmediate(resolve));\n }\n } catch (streamError) {\n console.warn(\n `[FastMCP warning] Failed to stream content for tool '${request.params.name}':`,\n streamError instanceof Error\n ? streamError.message\n : String(streamError),\n );\n }\n };\n\n const executeToolPromise = tool.execute(args, {\n log,\n reportProgress,\n session: this.#auth,\n streamContent,\n });\n\n // Handle timeout if specified\n const maybeStringResult = (await (tool.timeoutMs\n ? Promise.race([\n executeToolPromise,\n new Promise<never>((_, reject) => {\n const timeoutId = setTimeout(() => {\n reject(\n new UserError(\n `Tool '${request.params.name}' timed out after ${tool.timeoutMs}ms. Consider increasing timeoutMs or optimizing the tool implementation.`,\n ),\n );\n }, tool.timeoutMs);\n\n // If promise resolves first\n executeToolPromise.finally(() => clearTimeout(timeoutId));\n }),\n ])\n : executeToolPromise)) as\n | AudioContent\n | ContentResult\n | ImageContent\n | null\n | ResourceContent\n | ResourceLink\n | string\n | TextContent\n | undefined;\n\n // Without this test, we are running into situations where the last progress update is not reported.\n // See the 'reports multiple progress updates without buffering' test in FastMCP.test.ts before refactoring.\n await delay(1);\n\n if (maybeStringResult === undefined || maybeStringResult === null) {\n result = ContentResultZodSchema.parse({\n content: [],\n });\n } else if (typeof maybeStringResult === \"string\") {\n result = ContentResultZodSchema.parse({\n content: [{ text: maybeStringResult, type: \"text\" }],\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: [{ text: error.message, type: \"text\" }],\n isError: true,\n };\n }\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n text: `Tool '${request.params.name}' execution failed: ${errorMessage}`,\n type: \"text\",\n },\n ],\n isError: true,\n };\n }\n\n return result;\n });\n }\n}\n\n/**\n * Converts camelCase to snake_case for OAuth endpoint responses\n */\nfunction camelToSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Converts an object with camelCase keys to snake_case keys\n */\nfunction convertObjectToSnakeCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = camelToSnakeCase(key);\n result[snakeKey] = value;\n }\n\n return result;\n}\n\nconst FastMCPEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPEvents<FastMCPSessionAuth>>;\n} = EventEmitter;\n\nclass FastMCPEventEmitter extends FastMCPEventEmitterBase {}\n\nexport class FastMCP<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> extends FastMCPEventEmitter {\n public get sessions(): FastMCPSession<T>[] {\n return this.#sessions;\n }\n #authenticate: Authenticate<T> | undefined;\n #httpStreamServer: null | SSEServer = null;\n #options: ServerOptions<T>;\n #prompts: InputPrompt<T>[] = [];\n #resources: Resource<T>[] = [];\n #resourcesTemplates: InputResourceTemplate<T>[] = [];\n #sessions: FastMCPSession<T>[] = [];\n\n #tools: Tool<T>[] = [];\n\n constructor(public options: ServerOptions<T>) {\n super();\n\n this.#options = options;\n this.#authenticate = options.authenticate;\n }\n\n /**\n * Adds a prompt to the server.\n */\n public addPrompt<const Args extends InputPromptArgument<T>[]>(\n prompt: InputPrompt<T, Args>,\n ) {\n this.#prompts.push(prompt);\n }\n\n /**\n * Adds a resource to the server.\n */\n public addResource(resource: Resource<T>) {\n this.#resources.push(resource);\n }\n\n /**\n * Adds a resource template to the server.\n */\n public addResourceTemplate<\n const Args extends InputResourceTemplateArgument[],\n >(resource: InputResourceTemplate<T, Args>) {\n this.#resourcesTemplates.push(resource);\n }\n\n /**\n * Adds a tool to the server.\n */\n public addTool<Params extends ToolParameters>(tool: Tool<T, Params>) {\n this.#tools.push(tool as unknown as Tool<T>);\n }\n\n /**\n * Embeds a resource by URI, making it easy to include resources in tool responses.\n *\n * @param uri - The URI of the resource to embed\n * @returns Promise<ResourceContent> - The embedded resource content\n */\n public async embedded(uri: string): Promise<ResourceContent[\"resource\"]> {\n // First, try to find a direct resource match\n const directResource = this.#resources.find(\n (resource) => resource.uri === uri,\n );\n\n if (directResource) {\n const result = await directResource.load();\n const results = Array.isArray(result) ? result : [result];\n const firstResult = results[0];\n\n const resourceData: ResourceContent[\"resource\"] = {\n mimeType: directResource.mimeType,\n uri,\n };\n\n if (\"text\" in firstResult) {\n resourceData.text = firstResult.text;\n }\n\n if (\"blob\" in firstResult) {\n resourceData.blob = firstResult.blob;\n }\n\n return resourceData;\n }\n\n // Try to match against resource templates\n for (const template of this.#resourcesTemplates) {\n // Check if the URI starts with the template base\n const templateBase = template.uriTemplate.split(\"{\")[0];\n\n if (uri.startsWith(templateBase)) {\n const params: Record<string, string> = {};\n const templateParts = template.uriTemplate.split(\"/\");\n const uriParts = uri.split(\"/\");\n\n for (let i = 0; i < templateParts.length; i++) {\n const templatePart = templateParts[i];\n\n if (templatePart?.startsWith(\"{\") && templatePart.endsWith(\"}\")) {\n const paramName = templatePart.slice(1, -1);\n const paramValue = uriParts[i];\n\n if (paramValue) {\n params[paramName] = paramValue;\n }\n }\n }\n\n const result = await template.load(\n params as ResourceTemplateArgumentsToObject<\n typeof template.arguments\n >,\n );\n\n const resourceData: ResourceContent[\"resource\"] = {\n mimeType: template.mimeType,\n uri,\n };\n\n if (\"text\" in result) {\n resourceData.text = result.text;\n }\n\n if (\"blob\" in result) {\n resourceData.blob = result.blob;\n }\n\n return resourceData; // The resource we're looking for\n }\n }\n\n throw new UnexpectedStateError(`Resource not found: ${uri}`, { uri });\n }\n\n /**\n * Starts the server.\n */\n public async start(\n options?: Partial<{\n httpStream: {\n enableJsonResponse?: boolean;\n endpoint?: `/${string}`;\n eventStore?: EventStore;\n port: number;\n stateless?: boolean;\n };\n transportType: \"httpStream\" | \"stdio\";\n }>,\n ) {\n const config = this.#parseRuntimeConfig(options);\n\n if (config.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n const session = new FastMCPSession<T>({\n instructions: this.#options.instructions,\n name: this.#options.name,\n ping: this.#options.ping,\n prompts: this.#prompts,\n resources: this.#resources,\n resourcesTemplates: this.#resourcesTemplates,\n roots: this.#options.roots,\n tools: this.#tools,\n transportType: \"stdio\",\n utils: this.#options.utils,\n version: this.#options.version,\n });\n\n await session.connect(transport);\n\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session: session as FastMCPSession<FastMCPSessionAuth>,\n });\n } else if (config.transportType === \"httpStream\") {\n const httpConfig = config.httpStream;\n\n if (httpConfig.stateless) {\n // Stateless mode - create new server instance for each request\n console.info(\n `[FastMCP info] Starting server in stateless mode on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`,\n );\n\n this.#httpStreamServer = await startHTTPServer<FastMCPSession<T>>({\n createServer: async (request) => {\n let auth: T | undefined;\n\n if (this.#authenticate) {\n auth = await this.#authenticate(request);\n }\n\n // In stateless mode, create a new session for each request\n // without persisting it in the sessions array\n return this.#createSession(auth);\n },\n enableJsonResponse: httpConfig.enableJsonResponse,\n eventStore: httpConfig.eventStore,\n // In stateless mode, we don't track sessions\n onClose: async () => {\n // No session tracking in stateless mode\n },\n onConnect: async () => {\n // No persistent session tracking in stateless mode\n console.debug(\n `[FastMCP debug] Stateless HTTP Stream request handled`,\n );\n },\n onUnhandledRequest: async (req, res) => {\n await this.#handleUnhandledRequest(req, res, true);\n },\n port: httpConfig.port,\n stateless: true,\n streamEndpoint: httpConfig.endpoint,\n });\n } else {\n // Regular mode with session management\n this.#httpStreamServer = await startHTTPServer<FastMCPSession<T>>({\n createServer: async (request) => {\n let auth: T | undefined;\n\n if (this.#authenticate) {\n auth = await this.#authenticate(request);\n }\n\n return this.#createSession(auth);\n },\n enableJsonResponse: httpConfig.enableJsonResponse,\n eventStore: httpConfig.eventStore,\n onClose: async (session) => {\n this.emit(\"disconnect\", {\n session: session as FastMCPSession<FastMCPSessionAuth>,\n });\n },\n onConnect: async (session) => {\n this.#sessions.push(session);\n\n console.info(`[FastMCP info] HTTP Stream session established`);\n\n this.emit(\"connect\", {\n session: session as FastMCPSession<FastMCPSessionAuth>,\n });\n },\n\n onUnhandledRequest: async (req, res) => {\n await this.#handleUnhandledRequest(req, res, false);\n },\n port: httpConfig.port,\n streamEndpoint: httpConfig.endpoint,\n });\n\n console.info(\n `[FastMCP info] server is running on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`,\n );\n console.info(\n `[FastMCP info] Transport type: httpStream (Streamable HTTP, not SSE)`,\n );\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.#httpStreamServer) {\n await this.#httpStreamServer.close();\n }\n }\n\n /**\n * Creates a new FastMCPSession instance with the current configuration.\n * Used both for regular sessions and stateless requests.\n */\n #createSession(auth?: T): FastMCPSession<T> {\n const allowedTools = auth\n ? this.#tools.filter((tool) =>\n tool.canAccess ? tool.canAccess(auth) : true,\n )\n : this.#tools;\n return new FastMCPSession<T>({\n auth,\n name: this.#options.name,\n ping: this.#options.ping,\n prompts: this.#prompts,\n resources: this.#resources,\n resourcesTemplates: this.#resourcesTemplates,\n roots: this.#options.roots,\n tools: allowedTools,\n transportType: \"httpStream\",\n utils: this.#options.utils,\n version: this.#options.version,\n });\n }\n\n /**\n * Handles unhandled HTTP requests with health, readiness, and OAuth endpoints\n */\n #handleUnhandledRequest = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n isStateless = false,\n ) => {\n const healthConfig = this.#options.health ?? {};\n\n const enabled =\n healthConfig.enabled === undefined ? true : healthConfig.enabled;\n\n if (enabled) {\n const path = healthConfig.path ?? \"/health\";\n const url = new URL(req.url || \"\", \"http://localhost\");\n\n try {\n if (req.method === \"GET\" && url.pathname === path) {\n res\n .writeHead(healthConfig.status ?? 200, {\n \"Content-Type\": \"text/plain\",\n })\n .end(healthConfig.message ?? \"✓ Ok\");\n\n return;\n }\n\n // Enhanced readiness check endpoint\n if (req.method === \"GET\" && url.pathname === \"/ready\") {\n if (isStateless) {\n // In stateless mode, we're always ready if the server is running\n const response = {\n mode: \"stateless\",\n ready: 1,\n status: \"ready\",\n total: 1,\n };\n\n res\n .writeHead(200, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(response));\n } else {\n const readySessions = this.#sessions.filter(\n (s) => s.isReady,\n ).length;\n const totalSessions = this.#sessions.length;\n const allReady =\n readySessions === totalSessions && totalSessions > 0;\n\n const response = {\n ready: readySessions,\n status: allReady\n ? \"ready\"\n : totalSessions === 0\n ? \"no_sessions\"\n : \"initializing\",\n total: totalSessions,\n };\n\n res\n .writeHead(allReady ? 200 : 503, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(response));\n }\n\n return;\n }\n } catch (error) {\n console.error(\"[FastMCP error] health endpoint error\", error);\n }\n }\n\n // Handle OAuth well-known endpoints\n const oauthConfig = this.#options.oauth;\n if (oauthConfig?.enabled && req.method === \"GET\") {\n const url = new URL(req.url || \"\", \"http://localhost\");\n\n if (\n url.pathname === \"/.well-known/oauth-authorization-server\" &&\n oauthConfig.authorizationServer\n ) {\n const metadata = convertObjectToSnakeCase(\n oauthConfig.authorizationServer,\n );\n res\n .writeHead(200, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(metadata));\n return;\n }\n\n if (\n url.pathname === \"/.well-known/oauth-protected-resource\" &&\n oauthConfig.protectedResource\n ) {\n const metadata = convertObjectToSnakeCase(\n oauthConfig.protectedResource,\n );\n res\n .writeHead(200, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(metadata));\n return;\n }\n }\n\n // If the request was not handled above, return 404\n res.writeHead(404).end();\n };\n #parseRuntimeConfig(\n overrides?: Partial<{\n httpStream: {\n enableJsonResponse?: boolean;\n endpoint?: `/${string}`;\n port: number;\n stateless?: boolean;\n };\n transportType: \"httpStream\" | \"stdio\";\n }>,\n ):\n | {\n httpStream: {\n enableJsonResponse?: boolean;\n endpoint: `/${string}`;\n eventStore?: EventStore;\n port: number;\n stateless?: boolean;\n };\n transportType: \"httpStream\";\n }\n | { transportType: \"stdio\" } {\n const args = process.argv.slice(2);\n const getArg = (name: string) => {\n const index = args.findIndex((arg) => arg === `--${name}`);\n\n return index !== -1 && index + 1 < args.length\n ? args[index + 1]\n : undefined;\n };\n\n const transportArg = getArg(\"transport\");\n const portArg = getArg(\"port\");\n const endpointArg = getArg(\"endpoint\");\n const statelessArg = getArg(\"stateless\");\n\n const envTransport = process.env.FASTMCP_TRANSPORT;\n const envPort = process.env.FASTMCP_PORT;\n const envEndpoint = process.env.FASTMCP_ENDPOINT;\n const envStateless = process.env.FASTMCP_STATELESS;\n\n // Overrides > CLI > env > defaults\n const transportType =\n overrides?.transportType ||\n (transportArg === \"http-stream\" ? \"httpStream\" : transportArg) ||\n envTransport ||\n \"stdio\";\n\n if (transportType === \"httpStream\") {\n const port = parseInt(\n overrides?.httpStream?.port?.toString() || portArg || envPort || \"8080\",\n );\n const endpoint =\n overrides?.httpStream?.endpoint || endpointArg || envEndpoint || \"/mcp\";\n const enableJsonResponse =\n overrides?.httpStream?.enableJsonResponse || false;\n const stateless =\n overrides?.httpStream?.stateless ||\n statelessArg === \"true\" ||\n envStateless === \"true\" ||\n false;\n\n return {\n httpStream: {\n enableJsonResponse,\n endpoint: endpoint as `/${string}`,\n port,\n stateless,\n },\n transportType: \"httpStream\" as const,\n };\n }\n\n return { transportType: \"stdio\" as const };\n }\n}\n\nexport type {\n AudioContent,\n Content,\n ContentResult,\n Context,\n FastMCPEvents,\n FastMCPSessionEvents,\n ImageContent,\n InputPrompt,\n InputPromptArgument,\n LoggingLevel,\n Progress,\n Prompt,\n PromptArgument,\n Resource,\n ResourceContent,\n ResourceLink,\n ResourceResult,\n ResourceTemplate,\n ResourceTemplateArgument,\n SerializableValue,\n ServerOptions,\n TextContent,\n Tool,\n ToolParameters,\n};\n"],"mappings":"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","names":["prompt","resource","resources","tool"]}
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 { EventStore } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { RequestOptions } from \"@modelcontextprotocol/sdk/shared/protocol.js\";\nimport { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n CallToolRequestSchema,\n ClientCapabilities,\n CompleteRequestSchema,\n CreateMessageRequestSchema,\n ErrorCode,\n GetPromptRequestSchema,\n GetPromptResult,\n ListPromptsRequestSchema,\n ListResourcesRequestSchema,\n ListResourcesResult,\n ListResourceTemplatesRequestSchema,\n ListResourceTemplatesResult,\n ListToolsRequestSchema,\n McpError,\n ReadResourceRequestSchema,\n ResourceLink,\n Root,\n RootsListChangedNotificationSchema,\n ServerCapabilities,\n SetLevelRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { EventEmitter } from \"events\";\nimport { readFile } from \"fs/promises\";\nimport Fuse from \"fuse.js\";\nimport http from \"http\";\nimport { startHTTPServer } from \"mcp-proxy\";\nimport { StrictEventEmitter } from \"strict-event-emitter-types\";\nimport { setTimeout as delay } from \"timers/promises\";\nimport { fetch } from \"undici\";\nimport parseURITemplate from \"uri-templates\";\nimport { toJsonSchema } from \"xsschema\";\nimport { z } from \"zod\";\n\nexport interface Logger {\n debug(...args: unknown[]): void;\n error(...args: unknown[]): void;\n info(...args: unknown[]): void;\n log(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n}\n\nexport type SSEServer = {\n close: () => Promise<void>;\n};\n\ntype FastMCPEvents<T extends FastMCPSessionAuth> = {\n connect: (event: { session: FastMCPSession<T> }) => void;\n disconnect: (event: { session: FastMCPSession<T> }) => void;\n};\n\ntype FastMCPSessionEvents = {\n error: (event: { error: Error }) => void;\n ready: () => void;\n rootsChanged: (event: { roots: Root[] }) => void;\n};\n\nexport const imageContent = async (\n input: { buffer: Buffer } | { path: string } | { url: string },\n): Promise<ImageContent> => {\n let rawData: Buffer;\n\n try {\n if (\"url\" in input) {\n try {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(\n `Server responded with status: ${response.status} - ${response.statusText}`,\n );\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } catch (error) {\n throw new Error(\n `Failed to fetch image from URL (${input.url}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n } else if (\"path\" in input) {\n try {\n rawData = await readFile(input.path);\n } catch (error) {\n throw new Error(\n `Failed to read image from path (${input.path}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\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 { fileTypeFromBuffer } = await import(\"file-type\");\n const mimeType = await fileTypeFromBuffer(rawData);\n\n if (!mimeType || !mimeType.mime.startsWith(\"image/\")) {\n console.warn(\n `Warning: Content may not be a valid image. Detected MIME: ${\n mimeType?.mime || \"unknown\"\n }`,\n );\n }\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n data: base64Data,\n mimeType: mimeType?.mime ?? \"image/png\",\n type: \"image\",\n } as const;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n } else {\n throw new Error(`Unexpected error processing image: ${String(error)}`);\n }\n }\n};\n\nexport const audioContent = async (\n input: { buffer: Buffer } | { path: string } | { url: string },\n): Promise<AudioContent> => {\n let rawData: Buffer;\n\n try {\n if (\"url\" in input) {\n try {\n const response = await fetch(input.url);\n\n if (!response.ok) {\n throw new Error(\n `Server responded with status: ${response.status} - ${response.statusText}`,\n );\n }\n\n rawData = Buffer.from(await response.arrayBuffer());\n } catch (error) {\n throw new Error(\n `Failed to fetch audio from URL (${input.url}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n } else if (\"path\" in input) {\n try {\n rawData = await readFile(input.path);\n } catch (error) {\n throw new Error(\n `Failed to read audio from path (${input.path}): ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\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 { fileTypeFromBuffer } = await import(\"file-type\");\n const mimeType = await fileTypeFromBuffer(rawData);\n\n if (!mimeType || !mimeType.mime.startsWith(\"audio/\")) {\n console.warn(\n `Warning: Content may not be a valid audio file. Detected MIME: ${\n mimeType?.mime || \"unknown\"\n }`,\n );\n }\n\n const base64Data = rawData.toString(\"base64\");\n\n return {\n data: base64Data,\n mimeType: mimeType?.mime ?? \"audio/mpeg\",\n type: \"audio\",\n } as const;\n } catch (error) {\n if (error instanceof Error) {\n throw error;\n } else {\n throw new Error(`Unexpected error processing audio: ${String(error)}`);\n }\n }\n};\n\ntype Context<T extends FastMCPSessionAuth> = {\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 reportProgress: (progress: Progress) => Promise<void>;\n session: T | undefined;\n streamContent: (content: Content | Content[]) => Promise<void>;\n};\n\ntype Extra = unknown;\n\ntype Extras = Record<string, Extra>;\n\ntype Literal = boolean | null | number | string | undefined;\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 SerializableValue =\n | { [key: string]: SerializableValue }\n | Literal\n | SerializableValue[];\n\ntype TextContent = {\n text: string;\n type: \"text\";\n};\n\ntype ToolParameters = StandardSchemaV1;\n\nabstract class FastMCPError extends Error {\n public constructor(message?: string) {\n super(message);\n this.name = new.target.name;\n }\n}\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\nconst TextContentZodSchema = z\n .object({\n /**\n * The text content of the message.\n */\n text: z.string(),\n type: z.literal(\"text\"),\n })\n .strict() satisfies z.ZodType<TextContent>;\n\ntype ImageContent = {\n data: string;\n mimeType: string;\n type: \"image\";\n};\n\nconst ImageContentZodSchema = z\n .object({\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 type: z.literal(\"image\"),\n })\n .strict() satisfies z.ZodType<ImageContent>;\n\ntype AudioContent = {\n data: string;\n mimeType: string;\n type: \"audio\";\n};\n\nconst AudioContentZodSchema = z\n .object({\n /**\n * The base64-encoded audio data.\n */\n data: z.string().base64(),\n mimeType: z.string(),\n type: z.literal(\"audio\"),\n })\n .strict() satisfies z.ZodType<AudioContent>;\n\ntype ResourceContent = {\n resource: {\n blob?: string;\n mimeType?: string;\n text?: string;\n uri: string;\n };\n type: \"resource\";\n};\n\nconst ResourceContentZodSchema = z\n .object({\n resource: z.object({\n blob: z.string().optional(),\n mimeType: z.string().optional(),\n text: z.string().optional(),\n uri: z.string(),\n }),\n type: z.literal(\"resource\"),\n })\n .strict() satisfies z.ZodType<ResourceContent>;\n\nconst ResourceLinkZodSchema = z.object({\n description: z.string().optional(),\n mimeType: z.string().optional(),\n name: z.string(),\n title: z.string().optional(),\n type: z.literal(\"resource_link\"),\n uri: z.string(),\n}) satisfies z.ZodType<ResourceLink>;\n\ntype Content =\n | AudioContent\n | ImageContent\n | ResourceContent\n | ResourceLink\n | TextContent;\n\nconst ContentZodSchema = z.discriminatedUnion(\"type\", [\n TextContentZodSchema,\n ImageContentZodSchema,\n AudioContentZodSchema,\n ResourceContentZodSchema,\n ResourceLinkZodSchema,\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 hasMore?: boolean;\n total?: number;\n values: string[];\n};\n\n/**\n * https://github.com/modelcontextprotocol/typescript-sdk/blob/3164da64d085ec4e022ae881329eee7b72f208d4/src/types.ts#L983-L1003\n */\nconst CompletionZodSchema = z.object({\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 /**\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 * An array of completion values. Must not exceed 100 items.\n */\n values: z.array(z.string()).max(100),\n}) satisfies z.ZodType<Completion>;\n\ntype ArgumentValueCompleter<T extends FastMCPSessionAuth = FastMCPSessionAuth> =\n (value: string, auth?: T) => Promise<Completion>;\n\ntype InputPrompt<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n Arguments extends InputPromptArgument<T>[] = InputPromptArgument<T>[],\n Args = PromptArgumentsToObject<Arguments>,\n> = {\n arguments?: InputPromptArgument<T>[];\n description?: string;\n load: (args: Args, auth?: T) => Promise<PromptResult>;\n name: string;\n};\n\ntype InputPromptArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> =\n Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n enum?: string[];\n name: string;\n required?: boolean;\n }>;\n\ntype InputResourceTemplate<\n T extends FastMCPSessionAuth,\n Arguments extends\n InputResourceTemplateArgument<T>[] = InputResourceTemplateArgument<T>[],\n> = {\n arguments: Arguments;\n description?: string;\n load: (\n args: ResourceTemplateArgumentsToObject<Arguments>,\n auth?: T,\n ) => Promise<ResourceResult | ResourceResult[]>;\n mimeType?: string;\n name: string;\n uriTemplate: string;\n};\n\ntype InputResourceTemplateArgument<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> = Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n name: string;\n required?: boolean;\n}>;\n\ntype LoggingLevel =\n | \"alert\"\n | \"critical\"\n | \"debug\"\n | \"emergency\"\n | \"error\"\n | \"info\"\n | \"notice\"\n | \"warning\";\n\ntype Prompt<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n Arguments extends PromptArgument<T>[] = PromptArgument<T>[],\n Args = PromptArgumentsToObject<Arguments>,\n> = {\n arguments?: PromptArgument<T>[];\n complete?: (name: string, value: string, auth?: T) => Promise<Completion>;\n description?: string;\n load: (args: Args, auth?: T) => Promise<PromptResult>;\n name: string;\n};\n\ntype PromptArgument<T extends FastMCPSessionAuth = FastMCPSessionAuth> =\n Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n enum?: string[];\n name: string;\n required?: boolean;\n }>;\n\ntype PromptArgumentsToObject<T extends { name: string; required?: boolean }[]> =\n {\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 PromptResult = Pick<GetPromptResult, \"messages\"> | string;\n\ntype Resource<T extends FastMCPSessionAuth> = {\n complete?: (name: string, value: string, auth?: T) => Promise<Completion>;\n description?: string;\n load: (auth?: T) => Promise<ResourceResult | ResourceResult[]>;\n mimeType?: string;\n name: string;\n uri: string;\n};\n\ntype ResourceResult =\n | {\n blob: string;\n mimeType?: string;\n uri?: string;\n }\n | {\n mimeType?: string;\n text: string;\n uri?: string;\n };\n\ntype ResourceTemplate<\n T extends FastMCPSessionAuth,\n Arguments extends\n ResourceTemplateArgument<T>[] = ResourceTemplateArgument<T>[],\n> = {\n arguments: Arguments;\n complete?: (name: string, value: string, auth?: T) => Promise<Completion>;\n description?: string;\n load: (\n args: ResourceTemplateArgumentsToObject<Arguments>,\n auth?: T,\n ) => Promise<ResourceResult | ResourceResult[]>;\n mimeType?: string;\n name: string;\n uriTemplate: string;\n};\n\ntype ResourceTemplateArgument<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> = Readonly<{\n complete?: ArgumentValueCompleter<T>;\n description?: string;\n name: string;\n required?: boolean;\n}>;\n\ntype ResourceTemplateArgumentsToObject<T extends { name: string }[]> = {\n [K in T[number][\"name\"]]: string;\n};\n\ntype SamplingResponse = {\n content: AudioContent | ImageContent | TextContent;\n model: string;\n role: \"assistant\" | \"user\";\n stopReason?: \"endTurn\" | \"maxTokens\" | \"stopSequence\" | string;\n};\n\ntype ServerOptions<T extends FastMCPSessionAuth> = {\n authenticate?: Authenticate<T>;\n /**\n * Configuration for the health-check endpoint that can be exposed when the\n * server is running using the HTTP Stream transport. When enabled, the\n * server will respond to an HTTP GET request with the configured path (by\n * default \"/health\") rendering a plain-text response (by default \"ok\") and\n * the configured status code (by default 200).\n *\n * The endpoint is only added when the server is started with\n * `transportType: \"httpStream\"` – it is ignored for the stdio transport.\n */\n health?: {\n /**\n * When set to `false` the health-check endpoint is disabled.\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Plain-text body returned by the endpoint.\n * @default \"ok\"\n */\n message?: string;\n\n /**\n * HTTP path that should be handled.\n * @default \"/health\"\n */\n path?: string;\n\n /**\n * HTTP response status that will be returned.\n * @default 200\n */\n status?: number;\n };\n instructions?: string;\n /**\n * Custom logger instance. If not provided, defaults to console.\n * Use this to integrate with your own logging system.\n */\n logger?: Logger;\n name: string;\n\n /**\n * Configuration for OAuth well-known discovery endpoints that can be exposed\n * when the server is running using HTTP-based transports (SSE or HTTP Stream).\n * When enabled, the server will respond to requests for OAuth discovery endpoints\n * with the configured metadata.\n *\n * The endpoints are only added when the server is started with\n * `transportType: \"httpStream\"` – they are ignored for the stdio transport.\n * Both SSE and HTTP Stream transports support OAuth endpoints.\n */\n oauth?: {\n /**\n * OAuth Authorization Server metadata for /.well-known/oauth-authorization-server\n *\n * This endpoint follows RFC 8414 (OAuth 2.0 Authorization Server Metadata)\n * and provides metadata about the OAuth 2.0 authorization server.\n *\n * Required by MCP Specification 2025-03-26\n */\n authorizationServer?: {\n authorizationEndpoint: string;\n codeChallengeMethodsSupported?: string[];\n // DPoP support\n dpopSigningAlgValuesSupported?: string[];\n grantTypesSupported?: string[];\n\n introspectionEndpoint?: string;\n // Required\n issuer: string;\n // Common optional\n jwksUri?: string;\n opPolicyUri?: string;\n opTosUri?: string;\n registrationEndpoint?: string;\n responseModesSupported?: string[];\n responseTypesSupported: string[];\n revocationEndpoint?: string;\n scopesSupported?: string[];\n serviceDocumentation?: string;\n tokenEndpoint: string;\n tokenEndpointAuthMethodsSupported?: string[];\n tokenEndpointAuthSigningAlgValuesSupported?: string[];\n\n uiLocalesSupported?: string[];\n };\n\n /**\n * Whether OAuth discovery endpoints should be enabled.\n */\n enabled: boolean;\n\n /**\n * OAuth Protected Resource metadata for `/.well-known/oauth-protected-resource`\n *\n * This endpoint follows {@link https://www.rfc-editor.org/rfc/rfc9728.html | RFC 9728}\n * and provides metadata describing how an OAuth 2.0 protected resource (in this case,\n * an MCP server) expects to be accessed.\n *\n * When configured, FastMCP will automatically serve this metadata at the\n * `/.well-known/oauth-protected-resource` endpoint. The `authorizationServers` and `resource`\n * fields are required. All others are optional and will be omitted from the published\n * metadata if not specified.\n *\n * This satisfies the requirements of the MCP Authorization specification's\n * {@link https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#authorization-server-location | Authorization Server Location section}.\n *\n * Clients consuming this metadata MUST validate that any presented values comply with\n * RFC 9728, including strict validation of the `resource` identifier and intended audience\n * when access tokens are issued and presented (per RFC 8707 §2).\n *\n * @remarks Required by MCP Specification version 2025-06-18\n */\n protectedResource?: {\n /**\n * Allows for additional metadata fields beyond those defined in RFC 9728.\n *\n * @remarks This supports vendor-specific or experimental extensions.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2.3 | RFC 9728 §2.3}\n */\n [key: string]: unknown;\n\n /**\n * Supported values for the `authorization_details` parameter (RFC 9396).\n *\n * @remarks Used when fine-grained access control is in play.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.23 | RFC 9728 §2.2.23}\n */\n authorizationDetailsTypesSupported?: string[];\n\n /**\n * List of OAuth 2.0 authorization server issuer identifiers.\n *\n * These correspond to ASes that can issue access tokens for this protected resource.\n * MCP clients use these values to locate the relevant `/.well-known/oauth-authorization-server`\n * metadata for initiating the OAuth flow.\n *\n * @remarks Required by the MCP spec. MCP servers MUST provide at least one issuer.\n * Clients are responsible for choosing among them (see RFC 9728 §7.6).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.3 | RFC 9728 §2.2.3}\n */\n authorizationServers: string[];\n\n /**\n * List of supported methods for presenting OAuth 2.0 bearer tokens.\n *\n * @remarks Valid values are `header`, `body`, and `query`.\n * If omitted, clients MAY assume only `header` is supported, per RFC 6750.\n * This is a client-side interpretation and not a serialization default.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.9 | RFC 9728 §2.2.9}\n */\n bearerMethodsSupported?: string[];\n\n /**\n * Whether this resource requires all access tokens to be DPoP-bound.\n *\n * @remarks If omitted, clients SHOULD assume this is `false`.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.27 | RFC 9728 §2.2.27}\n */\n dpopBoundAccessTokensRequired?: boolean;\n\n /**\n * Supported algorithms for verifying DPoP proofs (RFC 9449).\n *\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.25 | RFC 9728 §2.2.25}\n */\n dpopSigningAlgValuesSupported?: string[];\n\n /**\n * JWKS URI of this resource. Used to validate access tokens or sign responses.\n *\n * @remarks When present, this MUST be an `https:` URI pointing to a valid JWK Set (RFC 7517).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.5 | RFC 9728 §2.2.5}\n */\n jwksUri?: string;\n\n /**\n * Canonical OAuth resource identifier for this protected resource (the MCP server).\n *\n * @remarks Typically the base URL of the MCP server. Clients MUST use this as the\n * `resource` parameter in authorization and token requests (per RFC 8707).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.1 | RFC 9728 §2.2.1}\n */\n resource: string;\n\n /**\n * URL to developer-accessible documentation for this resource.\n *\n * @remarks This field MAY be localized.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.15 | RFC 9728 §2.2.15}\n */\n resourceDocumentation?: string;\n\n /**\n * Human-readable name for display purposes (e.g., in UIs).\n *\n * @remarks This field MAY be localized using language tags (`resource_name#en`, etc.).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.13 | RFC 9728 §2.2.13}\n */\n resourceName?: string;\n\n /**\n * URL to a human-readable policy page describing acceptable use.\n *\n * @remarks This field MAY be localized.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.17 | RFC 9728 §2.2.17}\n */\n resourcePolicyUri?: string;\n\n /**\n * Supported JWS algorithms for signed responses from this resource (e.g., response signing).\n *\n * @remarks MUST NOT include `none`.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.11 | RFC 9728 §2.2.11}\n */\n resourceSigningAlgValuesSupported?: string[];\n\n /**\n * URL to the protected resource’s Terms of Service.\n *\n * @remarks This field MAY be localized.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.19 | RFC 9728 §2.2.19}\n */\n resourceTosUri?: string;\n\n /**\n * Supported OAuth scopes for requesting access to this resource.\n *\n * @remarks Useful for discovery, but clients SHOULD still request the minimal scope required.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.7 | RFC 9728 §2.2.7}\n */\n scopesSupported?: string[];\n\n /**\n * Developer-accessible documentation for how to use the service (not end-user docs).\n *\n * @remarks Semantically equivalent to `resourceDocumentation`, but included under its\n * alternate name for compatibility with tools or schemas expecting either.\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.15 | RFC 9728 §2.2.15}\n */\n serviceDocumentation?: string;\n\n /**\n * Whether mutual-TLS-bound access tokens are required.\n *\n * @remarks If omitted, clients SHOULD assume this is `false` (client-side behavior).\n * @see {@link https://www.rfc-editor.org/rfc/rfc9728.html#section-2-2.21 | RFC 9728 §2.2.21}\n */\n tlsClientCertificateBoundAccessTokens?: boolean;\n };\n };\n\n ping?: {\n /**\n * Whether ping should be enabled by default.\n * - true for SSE or HTTP Stream\n * - false for stdio\n */\n enabled?: boolean;\n /**\n * Interval\n * @default 5000 (5s)\n */\n intervalMs?: number;\n /**\n * Logging level for ping-related messages.\n * @default 'debug'\n */\n logLevel?: LoggingLevel;\n };\n /**\n * Configuration for roots capability\n */\n roots?: {\n /**\n * Whether roots capability should be enabled\n * Set to false to completely disable roots support\n * @default true\n */\n enabled?: boolean;\n };\n /**\n * General utilities\n */\n utils?: {\n formatInvalidParamsErrorMessage?: (\n issues: readonly StandardSchemaV1.Issue[],\n ) => string;\n };\n version: `${number}.${number}.${number}`;\n};\n\ntype Tool<\n T extends FastMCPSessionAuth,\n Params extends ToolParameters = ToolParameters,\n> = {\n annotations?: {\n /**\n * When true, the tool leverages incremental content streaming\n * Return void for tools that handle all their output via streaming\n */\n streamingHint?: boolean;\n } & ToolAnnotations;\n canAccess?: (auth: T) => boolean;\n description?: string;\n\n execute: (\n args: StandardSchemaV1.InferOutput<Params>,\n context: Context<T>,\n ) => Promise<\n | AudioContent\n | ContentResult\n | ImageContent\n | ResourceContent\n | ResourceLink\n | string\n | TextContent\n | void\n >;\n name: string;\n parameters?: Params;\n timeoutMs?: number;\n};\n\n/**\n * Tool annotations as defined in MCP Specification (2025-03-26)\n * These provide hints about a tool's behavior.\n */\ntype ToolAnnotations = {\n /**\n * If true, the tool may perform destructive updates\n * Only meaningful when readOnlyHint is false\n * @default true\n */\n destructiveHint?: boolean;\n\n /**\n * If true, calling the tool repeatedly with the same arguments has no additional effect\n * Only meaningful when readOnlyHint is false\n * @default false\n */\n idempotentHint?: boolean;\n\n /**\n * If true, the tool may interact with an \"open world\" of external entities\n * @default true\n */\n openWorldHint?: boolean;\n\n /**\n * If true, indicates the tool does not modify its environment\n * @default false\n */\n readOnlyHint?: boolean;\n\n /**\n * A human-readable title for the tool, useful for UI display\n */\n title?: string;\n};\n\nconst FastMCPSessionEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPSessionEvents>;\n} = EventEmitter;\n\ntype Authenticate<T> = (request: http.IncomingMessage) => Promise<T>;\n\ntype FastMCPSessionAuth = Record<string, unknown> | undefined;\n\nclass FastMCPSessionEventEmitter extends FastMCPSessionEventEmitterBase {}\n\nexport class FastMCPSession<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> extends FastMCPSessionEventEmitter {\n public get clientCapabilities(): ClientCapabilities | null {\n return this.#clientCapabilities ?? null;\n }\n public get isReady(): boolean {\n return this.#connectionState === \"ready\";\n }\n public get loggingLevel(): LoggingLevel {\n return this.#loggingLevel;\n }\n public get roots(): Root[] {\n return this.#roots;\n }\n public get server(): Server {\n return this.#server;\n }\n #auth: T | undefined;\n #capabilities: ServerCapabilities = {};\n #clientCapabilities?: ClientCapabilities;\n #connectionState: \"closed\" | \"connecting\" | \"error\" | \"ready\" = \"connecting\";\n #logger: Logger;\n #loggingLevel: LoggingLevel = \"info\";\n #needsEventLoopFlush: boolean = false;\n #pingConfig?: ServerOptions<T>[\"ping\"];\n\n #pingInterval: null | ReturnType<typeof setInterval> = null;\n\n #prompts: Prompt<T>[] = [];\n\n #resources: Resource<T>[] = [];\n\n #resourceTemplates: ResourceTemplate<T>[] = [];\n\n #roots: Root[] = [];\n\n #rootsConfig?: ServerOptions<T>[\"roots\"];\n\n #server: Server;\n\n #utils?: ServerOptions<T>[\"utils\"];\n\n constructor({\n auth,\n instructions,\n logger,\n name,\n ping,\n prompts,\n resources,\n resourcesTemplates,\n roots,\n tools,\n transportType,\n utils,\n version,\n }: {\n auth?: T;\n instructions?: string;\n logger: Logger;\n name: string;\n ping?: ServerOptions<T>[\"ping\"];\n prompts: Prompt<T>[];\n resources: Resource<T>[];\n resourcesTemplates: InputResourceTemplate<T>[];\n roots?: ServerOptions<T>[\"roots\"];\n tools: Tool<T>[];\n transportType?: \"httpStream\" | \"stdio\";\n utils?: ServerOptions<T>[\"utils\"];\n version: string;\n }) {\n super();\n\n this.#auth = auth;\n this.#logger = logger;\n this.#pingConfig = ping;\n this.#rootsConfig = roots;\n this.#needsEventLoopFlush = transportType === \"httpStream\";\n\n if (tools.length) {\n this.#capabilities.tools = {};\n }\n\n if (resources.length || resourcesTemplates.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, instructions: instructions },\n );\n\n this.#utils = utils;\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 || resourcesTemplates.length) {\n for (const resource of resources) {\n this.addResource(resource);\n }\n\n this.setupResourceHandlers(resources);\n\n if (resourcesTemplates.length) {\n for (const resourceTemplate of resourcesTemplates) {\n this.addResourceTemplate(resourceTemplate);\n }\n\n this.setupResourceTemplateHandlers(resourcesTemplates);\n }\n }\n\n if (prompts.length) {\n this.setupPromptHandlers(prompts);\n }\n }\n\n public async close() {\n this.#connectionState = \"closed\";\n\n if (this.#pingInterval) {\n clearInterval(this.#pingInterval);\n }\n\n try {\n await this.#server.close();\n } catch (error) {\n this.#logger.error(\"[FastMCP error]\", \"could not close server\", error);\n }\n }\n\n public async connect(transport: Transport) {\n if (this.#server.transport) {\n throw new UnexpectedStateError(\"Server is already connected\");\n }\n\n this.#connectionState = \"connecting\";\n\n try {\n await this.#server.connect(transport);\n\n let attempt = 0;\n const maxAttempts = 10;\n const retryDelay = 100;\n\n while (attempt++ < maxAttempts) {\n const capabilities = this.#server.getClientCapabilities();\n\n if (capabilities) {\n this.#clientCapabilities = capabilities;\n break;\n }\n\n await delay(retryDelay);\n }\n\n if (!this.#clientCapabilities) {\n this.#logger.warn(\n `[FastMCP warning] could not infer client capabilities after ${maxAttempts} attempts. Connection may be unstable.`,\n );\n }\n\n if (\n this.#clientCapabilities?.roots?.listChanged &&\n typeof this.#server.listRoots === \"function\"\n ) {\n try {\n const roots = await this.#server.listRoots();\n this.#roots = roots?.roots || [];\n } catch (e) {\n if (e instanceof McpError && e.code === ErrorCode.MethodNotFound) {\n this.#logger.debug(\n \"[FastMCP debug] listRoots method not supported by client\",\n );\n } else {\n this.#logger.error(\n `[FastMCP error] received error listing roots.\\n\\n${\n e instanceof Error ? e.stack : JSON.stringify(e)\n }`,\n );\n }\n }\n }\n\n if (this.#clientCapabilities) {\n const pingConfig = this.#getPingConfig(transport);\n\n if (pingConfig.enabled) {\n this.#pingInterval = setInterval(async () => {\n try {\n await this.#server.ping();\n } catch {\n // The reason we are not emitting an error here is because some clients\n // seem to not respond to the ping request, and we don't want to crash the server,\n // e.g., https://github.com/punkpeye/fastmcp/issues/38.\n const logLevel = pingConfig.logLevel;\n\n if (logLevel === \"debug\") {\n this.#logger.debug(\"[FastMCP debug] server ping failed\");\n } else if (logLevel === \"warning\") {\n this.#logger.warn(\n \"[FastMCP warning] server is not responding to ping\",\n );\n } else if (logLevel === \"error\") {\n this.#logger.error(\n \"[FastMCP error] server is not responding to ping\",\n );\n } else {\n this.#logger.info(\"[FastMCP info] server ping failed\");\n }\n }\n }, pingConfig.intervalMs);\n }\n }\n\n // Mark connection as ready and emit event\n this.#connectionState = \"ready\";\n this.emit(\"ready\");\n } catch (error) {\n this.#connectionState = \"error\";\n const errorEvent = {\n error: error instanceof Error ? error : new Error(String(error)),\n };\n this.emit(\"error\", errorEvent);\n throw error;\n }\n }\n\n public async requestSampling(\n message: z.infer<typeof CreateMessageRequestSchema>[\"params\"],\n options?: RequestOptions,\n ): Promise<SamplingResponse> {\n return this.#server.createMessage(message, options);\n }\n\n public waitForReady(): Promise<void> {\n if (this.isReady) {\n return Promise.resolve();\n }\n\n if (\n this.#connectionState === \"error\" ||\n this.#connectionState === \"closed\"\n ) {\n return Promise.reject(\n new Error(`Connection is in ${this.#connectionState} state`),\n );\n }\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n reject(\n new Error(\n \"Connection timeout: Session failed to become ready within 5 seconds\",\n ),\n );\n }, 5000);\n\n this.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve();\n });\n\n this.once(\"error\", (event) => {\n clearTimeout(timeout);\n reject(event.error);\n });\n });\n }\n\n #getPingConfig(transport: Transport): {\n enabled: boolean;\n intervalMs: number;\n logLevel: LoggingLevel;\n } {\n const pingConfig = this.#pingConfig || {};\n\n let defaultEnabled = false;\n\n if (\"type\" in transport) {\n // Enable by default for SSE and HTTP streaming\n if (transport.type === \"httpStream\") {\n defaultEnabled = true;\n }\n }\n\n return {\n enabled:\n pingConfig.enabled !== undefined ? pingConfig.enabled : defaultEnabled,\n intervalMs: pingConfig.intervalMs || 5000,\n logLevel: pingConfig.logLevel || \"debug\",\n };\n }\n\n private addPrompt(inputPrompt: InputPrompt<T>) {\n const completers: Record<string, ArgumentValueCompleter<T>> = {};\n const enums: Record<string, string[]> = {};\n const fuseInstances: Record<string, Fuse<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 fuseInstances[argument.name] = new Fuse(argument.enum, {\n includeScore: true,\n threshold: 0.3, // More flexible matching!\n });\n }\n }\n\n const prompt = {\n ...inputPrompt,\n complete: async (name: string, value: string, auth?: T) => {\n if (completers[name]) {\n return await completers[name](value, auth);\n }\n\n if (fuseInstances[name]) {\n const result = fuseInstances[name].search(value);\n\n return {\n total: result.length,\n values: result.map((item) => item.item),\n };\n }\n\n return {\n values: [],\n };\n },\n };\n\n this.#prompts.push(prompt);\n }\n\n private addResource(inputResource: Resource<T>) {\n this.#resources.push(inputResource);\n }\n\n private addResourceTemplate(inputResourceTemplate: InputResourceTemplate<T>) {\n const completers: Record<string, ArgumentValueCompleter<T>> = {};\n\n for (const argument of inputResourceTemplate.arguments ?? []) {\n if (argument.complete) {\n completers[argument.name] = argument.complete;\n }\n }\n\n const resourceTemplate = {\n ...inputResourceTemplate,\n complete: async (name: string, value: string, auth?: T) => {\n if (completers[name]) {\n return await completers[name](value, auth);\n }\n\n return {\n values: [],\n };\n },\n };\n\n this.#resourceTemplates.push(resourceTemplate);\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 this.#auth,\n ),\n );\n\n return {\n completion,\n };\n }\n\n if (request.params.ref.type === \"ref/resource\") {\n const resource = this.#resourceTemplates.find(\n (resource) => resource.uriTemplate === request.params.ref.uri,\n );\n\n if (!resource) {\n throw new UnexpectedStateError(\"Unknown resource\", {\n request,\n });\n }\n\n if (!(\"uriTemplate\" in resource)) {\n throw new UnexpectedStateError(\"Unexpected resource\");\n }\n\n if (!resource.complete) {\n throw new UnexpectedStateError(\n \"Resource does not support completion\",\n {\n request,\n },\n );\n }\n\n const completion = CompletionZodSchema.parse(\n await resource.complete(\n request.params.argument.name,\n request.params.argument.value,\n this.#auth,\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 setupErrorHandling() {\n this.#server.onerror = (error) => {\n this.#logger.error(\"[FastMCP error]\", error);\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 setupPromptHandlers(prompts: Prompt<T>[]) {\n this.#server.setRequestHandler(ListPromptsRequestSchema, async () => {\n return {\n prompts: prompts.map((prompt) => {\n return {\n arguments: prompt.arguments,\n complete: prompt.complete,\n description: prompt.description,\n name: prompt.name,\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 `Prompt '${request.params.name}' requires argument '${arg.name}': ${\n arg.description || \"No description provided\"\n }`,\n );\n }\n }\n\n let result: Awaited<ReturnType<Prompt<T>[\"load\"]>>;\n\n try {\n result = await prompt.load(\n args as Record<string, string | undefined>,\n this.#auth,\n );\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to load prompt '${request.params.name}': ${errorMessage}`,\n );\n }\n\n if (typeof result === \"string\") {\n return {\n description: prompt.description,\n messages: [\n {\n content: { text: result, type: \"text\" },\n role: \"user\",\n },\n ],\n };\n } else {\n return {\n description: prompt.description,\n messages: result.messages,\n };\n }\n });\n }\n\n private setupResourceHandlers(resources: Resource<T>[]) {\n this.#server.setRequestHandler(ListResourcesRequestSchema, async () => {\n return {\n resources: resources.map((resource) => ({\n description: resource.description,\n mimeType: resource.mimeType,\n name: resource.name,\n uri: resource.uri,\n })),\n } satisfies ListResourcesResult;\n });\n\n this.#server.setRequestHandler(\n ReadResourceRequestSchema,\n async (request) => {\n if (\"uri\" in request.params) {\n const resource = resources.find(\n (resource) =>\n \"uri\" in resource && resource.uri === request.params.uri,\n );\n\n if (!resource) {\n for (const resourceTemplate of this.#resourceTemplates) {\n const uriTemplate = parseURITemplate(\n resourceTemplate.uriTemplate,\n );\n\n const match = uriTemplate.fromUri(request.params.uri);\n\n if (!match) {\n continue;\n }\n\n const uri = uriTemplate.fill(match);\n\n const result = await resourceTemplate.load(match, this.#auth);\n\n const resources = Array.isArray(result) ? result : [result];\n return {\n contents: resources.map((resource) => ({\n ...resource,\n description: resourceTemplate.description,\n mimeType: resource.mimeType ?? resourceTemplate.mimeType,\n name: resourceTemplate.name,\n uri: resource.uri ?? uri,\n })),\n };\n }\n\n throw new McpError(\n ErrorCode.MethodNotFound,\n `Resource not found: '${request.params.uri}'. Available resources: ${\n resources.map((r) => r.uri).join(\", \") || \"none\"\n }`,\n );\n }\n\n if (!(\"uri\" in resource)) {\n throw new UnexpectedStateError(\"Resource does not support reading\");\n }\n\n let maybeArrayResult: Awaited<ReturnType<Resource<T>[\"load\"]>>;\n\n try {\n maybeArrayResult = await resource.load(this.#auth);\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n throw new McpError(\n ErrorCode.InternalError,\n `Failed to load resource '${resource.name}' (${resource.uri}): ${errorMessage}`,\n {\n uri: resource.uri,\n },\n );\n }\n\n const resourceResults = Array.isArray(maybeArrayResult)\n ? maybeArrayResult\n : [maybeArrayResult];\n\n return {\n contents: resourceResults.map((result) => ({\n ...result,\n mimeType: result.mimeType ?? resource.mimeType,\n name: resource.name,\n uri: result.uri ?? resource.uri,\n })),\n };\n }\n\n throw new UnexpectedStateError(\"Unknown resource request\", {\n request,\n });\n },\n );\n }\n\n private setupResourceTemplateHandlers(\n resourceTemplates: ResourceTemplate<T>[],\n ) {\n this.#server.setRequestHandler(\n ListResourceTemplatesRequestSchema,\n async () => {\n return {\n resourceTemplates: resourceTemplates.map((resourceTemplate) => ({\n description: resourceTemplate.description,\n mimeType: resourceTemplate.mimeType,\n name: resourceTemplate.name,\n uriTemplate: resourceTemplate.uriTemplate,\n })),\n } satisfies ListResourceTemplatesResult;\n },\n );\n }\n\n private setupRootsHandlers() {\n if (this.#rootsConfig?.enabled === false) {\n this.#logger.debug(\n \"[FastMCP debug] roots capability explicitly disabled via config\",\n );\n return;\n }\n\n // Only set up roots notification handling if the server supports it\n if (typeof this.#server.listRoots === \"function\") {\n this.#server.setNotificationHandler(\n RootsListChangedNotificationSchema,\n () => {\n this.#server\n .listRoots()\n .then((roots) => {\n this.#roots = roots.roots;\n\n this.emit(\"rootsChanged\", {\n roots: roots.roots,\n });\n })\n .catch((error) => {\n if (\n error instanceof McpError &&\n error.code === ErrorCode.MethodNotFound\n ) {\n this.#logger.debug(\n \"[FastMCP debug] listRoots method not supported by client\",\n );\n } else {\n this.#logger.error(\n `[FastMCP error] received error listing roots.\\n\\n${\n error instanceof Error ? error.stack : JSON.stringify(error)\n }`,\n );\n }\n });\n },\n );\n } else {\n this.#logger.debug(\n \"[FastMCP debug] roots capability not available, not setting up notification handler\",\n );\n }\n }\n\n private setupToolHandlers(tools: Tool<T>[]) {\n this.#server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: await Promise.all(\n tools.map(async (tool) => {\n return {\n annotations: tool.annotations,\n description: tool.description,\n inputSchema: tool.parameters\n ? await toJsonSchema(tool.parameters)\n : {\n additionalProperties: false,\n properties: {},\n type: \"object\",\n }, // More complete schema for Cursor compatibility\n name: tool.name,\n };\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: unknown = undefined;\n\n if (tool.parameters) {\n const parsed = await tool.parameters[\"~standard\"].validate(\n request.params.arguments,\n );\n\n if (parsed.issues) {\n const friendlyErrors = this.#utils?.formatInvalidParamsErrorMessage\n ? this.#utils.formatInvalidParamsErrorMessage(parsed.issues)\n : parsed.issues\n .map((issue) => {\n const path = issue.path?.join(\".\") || \"root\";\n return `${path}: ${issue.message}`;\n })\n .join(\", \");\n\n throw new McpError(\n ErrorCode.InvalidParams,\n `Tool '${request.params.name}' parameter validation failed: ${friendlyErrors}. Please check the parameter types and values according to the tool's schema.`,\n );\n }\n\n args = parsed.value;\n }\n\n const progressToken = request.params?._meta?.progressToken;\n\n let result: ContentResult;\n\n try {\n const reportProgress = async (progress: Progress) => {\n try {\n await this.#server.notification({\n method: \"notifications/progress\",\n params: {\n ...progress,\n progressToken,\n },\n });\n\n if (this.#needsEventLoopFlush) {\n await new Promise((resolve) => setImmediate(resolve));\n }\n } catch (progressError) {\n this.#logger.warn(\n `[FastMCP warning] Failed to report progress for tool '${request.params.name}':`,\n progressError instanceof Error\n ? progressError.message\n : String(progressError),\n );\n }\n };\n\n const log = {\n debug: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"debug\",\n });\n },\n error: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"error\",\n });\n },\n info: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"info\",\n });\n },\n warn: (message: string, context?: SerializableValue) => {\n this.#server.sendLoggingMessage({\n data: {\n context,\n message,\n },\n level: \"warning\",\n });\n },\n };\n\n // Create a promise for tool execution\n // Streams partial results while a tool is still executing\n // Enables progressive rendering and real-time feedback\n const streamContent = async (content: Content | Content[]) => {\n const contentArray = Array.isArray(content) ? content : [content];\n\n try {\n await this.#server.notification({\n method: \"notifications/tool/streamContent\",\n params: {\n content: contentArray,\n toolName: request.params.name,\n },\n });\n\n if (this.#needsEventLoopFlush) {\n await new Promise((resolve) => setImmediate(resolve));\n }\n } catch (streamError) {\n this.#logger.warn(\n `[FastMCP warning] Failed to stream content for tool '${request.params.name}':`,\n streamError instanceof Error\n ? streamError.message\n : String(streamError),\n );\n }\n };\n\n const executeToolPromise = tool.execute(args, {\n log,\n reportProgress,\n session: this.#auth,\n streamContent,\n });\n\n // Handle timeout if specified\n const maybeStringResult = (await (tool.timeoutMs\n ? Promise.race([\n executeToolPromise,\n new Promise<never>((_, reject) => {\n const timeoutId = setTimeout(() => {\n reject(\n new UserError(\n `Tool '${request.params.name}' timed out after ${tool.timeoutMs}ms. Consider increasing timeoutMs or optimizing the tool implementation.`,\n ),\n );\n }, tool.timeoutMs);\n\n // If promise resolves first\n executeToolPromise.finally(() => clearTimeout(timeoutId));\n }),\n ])\n : executeToolPromise)) as\n | AudioContent\n | ContentResult\n | ImageContent\n | null\n | ResourceContent\n | ResourceLink\n | string\n | TextContent\n | undefined;\n\n // Without this test, we are running into situations where the last progress update is not reported.\n // See the 'reports multiple progress updates without buffering' test in FastMCP.test.ts before refactoring.\n await delay(1);\n\n if (maybeStringResult === undefined || maybeStringResult === null) {\n result = ContentResultZodSchema.parse({\n content: [],\n });\n } else if (typeof maybeStringResult === \"string\") {\n result = ContentResultZodSchema.parse({\n content: [{ text: maybeStringResult, type: \"text\" }],\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: [{ text: error.message, type: \"text\" }],\n isError: true,\n };\n }\n\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n text: `Tool '${request.params.name}' execution failed: ${errorMessage}`,\n type: \"text\",\n },\n ],\n isError: true,\n };\n }\n\n return result;\n });\n }\n}\n\n/**\n * Converts camelCase to snake_case for OAuth endpoint responses\n */\nfunction camelToSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Converts an object with camelCase keys to snake_case keys\n */\nfunction convertObjectToSnakeCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = camelToSnakeCase(key);\n result[snakeKey] = value;\n }\n\n return result;\n}\n\nconst FastMCPEventEmitterBase: {\n new (): StrictEventEmitter<EventEmitter, FastMCPEvents<FastMCPSessionAuth>>;\n} = EventEmitter;\n\nclass FastMCPEventEmitter extends FastMCPEventEmitterBase {}\n\nexport class FastMCP<\n T extends FastMCPSessionAuth = FastMCPSessionAuth,\n> extends FastMCPEventEmitter {\n public get sessions(): FastMCPSession<T>[] {\n return this.#sessions;\n }\n #authenticate: Authenticate<T> | undefined;\n #httpStreamServer: null | SSEServer = null;\n #logger: Logger;\n #options: ServerOptions<T>;\n #prompts: InputPrompt<T>[] = [];\n #resources: Resource<T>[] = [];\n #resourcesTemplates: InputResourceTemplate<T>[] = [];\n #sessions: FastMCPSession<T>[] = [];\n\n #tools: Tool<T>[] = [];\n\n constructor(public options: ServerOptions<T>) {\n super();\n\n this.#options = options;\n this.#authenticate = options.authenticate;\n this.#logger = options.logger || console;\n }\n\n /**\n * Adds a prompt to the server.\n */\n public addPrompt<const Args extends InputPromptArgument<T>[]>(\n prompt: InputPrompt<T, Args>,\n ) {\n this.#prompts.push(prompt);\n }\n\n /**\n * Adds a resource to the server.\n */\n public addResource(resource: Resource<T>) {\n this.#resources.push(resource);\n }\n\n /**\n * Adds a resource template to the server.\n */\n public addResourceTemplate<\n const Args extends InputResourceTemplateArgument[],\n >(resource: InputResourceTemplate<T, Args>) {\n this.#resourcesTemplates.push(resource);\n }\n\n /**\n * Adds a tool to the server.\n */\n public addTool<Params extends ToolParameters>(tool: Tool<T, Params>) {\n this.#tools.push(tool as unknown as Tool<T>);\n }\n\n /**\n * Embeds a resource by URI, making it easy to include resources in tool responses.\n *\n * @param uri - The URI of the resource to embed\n * @returns Promise<ResourceContent> - The embedded resource content\n */\n public async embedded(uri: string): Promise<ResourceContent[\"resource\"]> {\n // First, try to find a direct resource match\n const directResource = this.#resources.find(\n (resource) => resource.uri === uri,\n );\n\n if (directResource) {\n const result = await directResource.load();\n const results = Array.isArray(result) ? result : [result];\n const firstResult = results[0];\n\n const resourceData: ResourceContent[\"resource\"] = {\n mimeType: directResource.mimeType,\n uri,\n };\n\n if (\"text\" in firstResult) {\n resourceData.text = firstResult.text;\n }\n\n if (\"blob\" in firstResult) {\n resourceData.blob = firstResult.blob;\n }\n\n return resourceData;\n }\n\n // Try to match against resource templates\n for (const template of this.#resourcesTemplates) {\n // Check if the URI starts with the template base\n const templateBase = template.uriTemplate.split(\"{\")[0];\n\n if (uri.startsWith(templateBase)) {\n const params: Record<string, string> = {};\n const templateParts = template.uriTemplate.split(\"/\");\n const uriParts = uri.split(\"/\");\n\n for (let i = 0; i < templateParts.length; i++) {\n const templatePart = templateParts[i];\n\n if (templatePart?.startsWith(\"{\") && templatePart.endsWith(\"}\")) {\n const paramName = templatePart.slice(1, -1);\n const paramValue = uriParts[i];\n\n if (paramValue) {\n params[paramName] = paramValue;\n }\n }\n }\n\n const result = await template.load(\n params as ResourceTemplateArgumentsToObject<\n typeof template.arguments\n >,\n );\n\n const resourceData: ResourceContent[\"resource\"] = {\n mimeType: template.mimeType,\n uri,\n };\n\n if (\"text\" in result) {\n resourceData.text = result.text;\n }\n\n if (\"blob\" in result) {\n resourceData.blob = result.blob;\n }\n\n return resourceData; // The resource we're looking for\n }\n }\n\n throw new UnexpectedStateError(`Resource not found: ${uri}`, { uri });\n }\n\n /**\n * Starts the server.\n */\n public async start(\n options?: Partial<{\n httpStream: {\n enableJsonResponse?: boolean;\n endpoint?: `/${string}`;\n eventStore?: EventStore;\n port: number;\n stateless?: boolean;\n };\n transportType: \"httpStream\" | \"stdio\";\n }>,\n ) {\n const config = this.#parseRuntimeConfig(options);\n\n if (config.transportType === \"stdio\") {\n const transport = new StdioServerTransport();\n const session = new FastMCPSession<T>({\n instructions: this.#options.instructions,\n logger: this.#logger,\n name: this.#options.name,\n ping: this.#options.ping,\n prompts: this.#prompts,\n resources: this.#resources,\n resourcesTemplates: this.#resourcesTemplates,\n roots: this.#options.roots,\n tools: this.#tools,\n transportType: \"stdio\",\n utils: this.#options.utils,\n version: this.#options.version,\n });\n\n await session.connect(transport);\n\n this.#sessions.push(session);\n\n this.emit(\"connect\", {\n session: session as FastMCPSession<FastMCPSessionAuth>,\n });\n } else if (config.transportType === \"httpStream\") {\n const httpConfig = config.httpStream;\n\n if (httpConfig.stateless) {\n // Stateless mode - create new server instance for each request\n this.#logger.info(\n `[FastMCP info] Starting server in stateless mode on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`,\n );\n\n this.#httpStreamServer = await startHTTPServer<FastMCPSession<T>>({\n createServer: async (request) => {\n let auth: T | undefined;\n\n if (this.#authenticate) {\n auth = await this.#authenticate(request);\n }\n\n // In stateless mode, create a new session for each request\n // without persisting it in the sessions array\n return this.#createSession(auth);\n },\n enableJsonResponse: httpConfig.enableJsonResponse,\n eventStore: httpConfig.eventStore,\n // In stateless mode, we don't track sessions\n onClose: async () => {\n // No session tracking in stateless mode\n },\n onConnect: async () => {\n // No persistent session tracking in stateless mode\n this.#logger.debug(\n `[FastMCP debug] Stateless HTTP Stream request handled`,\n );\n },\n onUnhandledRequest: async (req, res) => {\n await this.#handleUnhandledRequest(req, res, true);\n },\n port: httpConfig.port,\n stateless: true,\n streamEndpoint: httpConfig.endpoint,\n });\n } else {\n // Regular mode with session management\n this.#httpStreamServer = await startHTTPServer<FastMCPSession<T>>({\n createServer: async (request) => {\n let auth: T | undefined;\n\n if (this.#authenticate) {\n auth = await this.#authenticate(request);\n }\n\n return this.#createSession(auth);\n },\n enableJsonResponse: httpConfig.enableJsonResponse,\n eventStore: httpConfig.eventStore,\n onClose: async (session) => {\n this.emit(\"disconnect\", {\n session: session as FastMCPSession<FastMCPSessionAuth>,\n });\n },\n onConnect: async (session) => {\n this.#sessions.push(session);\n\n this.#logger.info(`[FastMCP info] HTTP Stream session established`);\n\n this.emit(\"connect\", {\n session: session as FastMCPSession<FastMCPSessionAuth>,\n });\n },\n\n onUnhandledRequest: async (req, res) => {\n await this.#handleUnhandledRequest(req, res, false);\n },\n port: httpConfig.port,\n streamEndpoint: httpConfig.endpoint,\n });\n\n this.#logger.info(\n `[FastMCP info] server is running on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`,\n );\n this.#logger.info(\n `[FastMCP info] Transport type: httpStream (Streamable HTTP, not SSE)`,\n );\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.#httpStreamServer) {\n await this.#httpStreamServer.close();\n }\n }\n\n /**\n * Creates a new FastMCPSession instance with the current configuration.\n * Used both for regular sessions and stateless requests.\n */\n #createSession(auth?: T): FastMCPSession<T> {\n const allowedTools = auth\n ? this.#tools.filter((tool) =>\n tool.canAccess ? tool.canAccess(auth) : true,\n )\n : this.#tools;\n return new FastMCPSession<T>({\n auth,\n logger: this.#logger,\n name: this.#options.name,\n ping: this.#options.ping,\n prompts: this.#prompts,\n resources: this.#resources,\n resourcesTemplates: this.#resourcesTemplates,\n roots: this.#options.roots,\n tools: allowedTools,\n transportType: \"httpStream\",\n utils: this.#options.utils,\n version: this.#options.version,\n });\n }\n\n /**\n * Handles unhandled HTTP requests with health, readiness, and OAuth endpoints\n */\n #handleUnhandledRequest = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n isStateless = false,\n ) => {\n const healthConfig = this.#options.health ?? {};\n\n const enabled =\n healthConfig.enabled === undefined ? true : healthConfig.enabled;\n\n if (enabled) {\n const path = healthConfig.path ?? \"/health\";\n const url = new URL(req.url || \"\", \"http://localhost\");\n\n try {\n if (req.method === \"GET\" && url.pathname === path) {\n res\n .writeHead(healthConfig.status ?? 200, {\n \"Content-Type\": \"text/plain\",\n })\n .end(healthConfig.message ?? \"✓ Ok\");\n\n return;\n }\n\n // Enhanced readiness check endpoint\n if (req.method === \"GET\" && url.pathname === \"/ready\") {\n if (isStateless) {\n // In stateless mode, we're always ready if the server is running\n const response = {\n mode: \"stateless\",\n ready: 1,\n status: \"ready\",\n total: 1,\n };\n\n res\n .writeHead(200, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(response));\n } else {\n const readySessions = this.#sessions.filter(\n (s) => s.isReady,\n ).length;\n const totalSessions = this.#sessions.length;\n const allReady =\n readySessions === totalSessions && totalSessions > 0;\n\n const response = {\n ready: readySessions,\n status: allReady\n ? \"ready\"\n : totalSessions === 0\n ? \"no_sessions\"\n : \"initializing\",\n total: totalSessions,\n };\n\n res\n .writeHead(allReady ? 200 : 503, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(response));\n }\n\n return;\n }\n } catch (error) {\n this.#logger.error(\"[FastMCP error] health endpoint error\", error);\n }\n }\n\n // Handle OAuth well-known endpoints\n const oauthConfig = this.#options.oauth;\n if (oauthConfig?.enabled && req.method === \"GET\") {\n const url = new URL(req.url || \"\", \"http://localhost\");\n\n if (\n url.pathname === \"/.well-known/oauth-authorization-server\" &&\n oauthConfig.authorizationServer\n ) {\n const metadata = convertObjectToSnakeCase(\n oauthConfig.authorizationServer,\n );\n res\n .writeHead(200, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(metadata));\n return;\n }\n\n if (\n url.pathname === \"/.well-known/oauth-protected-resource\" &&\n oauthConfig.protectedResource\n ) {\n const metadata = convertObjectToSnakeCase(\n oauthConfig.protectedResource,\n );\n res\n .writeHead(200, {\n \"Content-Type\": \"application/json\",\n })\n .end(JSON.stringify(metadata));\n return;\n }\n }\n\n // If the request was not handled above, return 404\n res.writeHead(404).end();\n };\n #parseRuntimeConfig(\n overrides?: Partial<{\n httpStream: {\n enableJsonResponse?: boolean;\n endpoint?: `/${string}`;\n port: number;\n stateless?: boolean;\n };\n transportType: \"httpStream\" | \"stdio\";\n }>,\n ):\n | {\n httpStream: {\n enableJsonResponse?: boolean;\n endpoint: `/${string}`;\n eventStore?: EventStore;\n port: number;\n stateless?: boolean;\n };\n transportType: \"httpStream\";\n }\n | { transportType: \"stdio\" } {\n const args = process.argv.slice(2);\n const getArg = (name: string) => {\n const index = args.findIndex((arg) => arg === `--${name}`);\n\n return index !== -1 && index + 1 < args.length\n ? args[index + 1]\n : undefined;\n };\n\n const transportArg = getArg(\"transport\");\n const portArg = getArg(\"port\");\n const endpointArg = getArg(\"endpoint\");\n const statelessArg = getArg(\"stateless\");\n\n const envTransport = process.env.FASTMCP_TRANSPORT;\n const envPort = process.env.FASTMCP_PORT;\n const envEndpoint = process.env.FASTMCP_ENDPOINT;\n const envStateless = process.env.FASTMCP_STATELESS;\n\n // Overrides > CLI > env > defaults\n const transportType =\n overrides?.transportType ||\n (transportArg === \"http-stream\" ? \"httpStream\" : transportArg) ||\n envTransport ||\n \"stdio\";\n\n if (transportType === \"httpStream\") {\n const port = parseInt(\n overrides?.httpStream?.port?.toString() || portArg || envPort || \"8080\",\n );\n const endpoint =\n overrides?.httpStream?.endpoint || endpointArg || envEndpoint || \"/mcp\";\n const enableJsonResponse =\n overrides?.httpStream?.enableJsonResponse || false;\n const stateless =\n overrides?.httpStream?.stateless ||\n statelessArg === \"true\" ||\n envStateless === \"true\" ||\n false;\n\n return {\n httpStream: {\n enableJsonResponse,\n endpoint: endpoint as `/${string}`,\n port,\n stateless,\n },\n transportType: \"httpStream\" as const,\n };\n }\n\n return { transportType: \"stdio\" as const };\n }\n}\n\nexport type {\n AudioContent,\n Content,\n ContentResult,\n Context,\n FastMCPEvents,\n FastMCPSessionEvents,\n ImageContent,\n InputPrompt,\n InputPromptArgument,\n LoggingLevel,\n Progress,\n Prompt,\n PromptArgument,\n Resource,\n ResourceContent,\n ResourceLink,\n ResourceResult,\n ResourceTemplate,\n ResourceTemplateArgument,\n SerializableValue,\n ServerOptions,\n TextContent,\n Tool,\n ToolParameters,\n};\n"],"mappings":"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","names":["prompt","resource","resources","tool"]}
package/jsr.json CHANGED
@@ -3,5 +3,5 @@
3
3
  "include": ["src/FastMCP.ts", "src/bin/fastmcp.ts"],
4
4
  "license": "MIT",
5
5
  "name": "@punkpeye/fastmcp",
6
- "version": "3.13.0"
6
+ "version": "3.14.0"
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastmcp",
3
- "version": "3.13.0",
3
+ "version": "3.14.0",
4
4
  "main": "dist/FastMCP.js",
5
5
  "scripts": {
6
6
  "build": "tsup",
package/src/FastMCP.ts CHANGED
@@ -38,6 +38,14 @@ import parseURITemplate from "uri-templates";
38
38
  import { toJsonSchema } from "xsschema";
39
39
  import { z } from "zod";
40
40
 
41
+ export interface Logger {
42
+ debug(...args: unknown[]): void;
43
+ error(...args: unknown[]): void;
44
+ info(...args: unknown[]): void;
45
+ log(...args: unknown[]): void;
46
+ warn(...args: unknown[]): void;
47
+ }
48
+
41
49
  export type SSEServer = {
42
50
  close: () => Promise<void>;
43
51
  };
@@ -570,6 +578,11 @@ type ServerOptions<T extends FastMCPSessionAuth> = {
570
578
  status?: number;
571
579
  };
572
580
  instructions?: string;
581
+ /**
582
+ * Custom logger instance. If not provided, defaults to console.
583
+ * Use this to integrate with your own logging system.
584
+ */
585
+ logger?: Logger;
573
586
  name: string;
574
587
 
575
588
  /**
@@ -924,6 +937,7 @@ export class FastMCPSession<
924
937
  #capabilities: ServerCapabilities = {};
925
938
  #clientCapabilities?: ClientCapabilities;
926
939
  #connectionState: "closed" | "connecting" | "error" | "ready" = "connecting";
940
+ #logger: Logger;
927
941
  #loggingLevel: LoggingLevel = "info";
928
942
  #needsEventLoopFlush: boolean = false;
929
943
  #pingConfig?: ServerOptions<T>["ping"];
@@ -947,6 +961,7 @@ export class FastMCPSession<
947
961
  constructor({
948
962
  auth,
949
963
  instructions,
964
+ logger,
950
965
  name,
951
966
  ping,
952
967
  prompts,
@@ -960,6 +975,7 @@ export class FastMCPSession<
960
975
  }: {
961
976
  auth?: T;
962
977
  instructions?: string;
978
+ logger: Logger;
963
979
  name: string;
964
980
  ping?: ServerOptions<T>["ping"];
965
981
  prompts: Prompt<T>[];
@@ -974,6 +990,7 @@ export class FastMCPSession<
974
990
  super();
975
991
 
976
992
  this.#auth = auth;
993
+ this.#logger = logger;
977
994
  this.#pingConfig = ping;
978
995
  this.#rootsConfig = roots;
979
996
  this.#needsEventLoopFlush = transportType === "httpStream";
@@ -1043,7 +1060,7 @@ export class FastMCPSession<
1043
1060
  try {
1044
1061
  await this.#server.close();
1045
1062
  } catch (error) {
1046
- console.error("[FastMCP error]", "could not close server", error);
1063
+ this.#logger.error("[FastMCP error]", "could not close server", error);
1047
1064
  }
1048
1065
  }
1049
1066
 
@@ -1073,7 +1090,7 @@ export class FastMCPSession<
1073
1090
  }
1074
1091
 
1075
1092
  if (!this.#clientCapabilities) {
1076
- console.warn(
1093
+ this.#logger.warn(
1077
1094
  `[FastMCP warning] could not infer client capabilities after ${maxAttempts} attempts. Connection may be unstable.`,
1078
1095
  );
1079
1096
  }
@@ -1087,11 +1104,11 @@ export class FastMCPSession<
1087
1104
  this.#roots = roots?.roots || [];
1088
1105
  } catch (e) {
1089
1106
  if (e instanceof McpError && e.code === ErrorCode.MethodNotFound) {
1090
- console.debug(
1107
+ this.#logger.debug(
1091
1108
  "[FastMCP debug] listRoots method not supported by client",
1092
1109
  );
1093
1110
  } else {
1094
- console.error(
1111
+ this.#logger.error(
1095
1112
  `[FastMCP error] received error listing roots.\n\n${
1096
1113
  e instanceof Error ? e.stack : JSON.stringify(e)
1097
1114
  }`,
@@ -1114,17 +1131,17 @@ export class FastMCPSession<
1114
1131
  const logLevel = pingConfig.logLevel;
1115
1132
 
1116
1133
  if (logLevel === "debug") {
1117
- console.debug("[FastMCP debug] server ping failed");
1134
+ this.#logger.debug("[FastMCP debug] server ping failed");
1118
1135
  } else if (logLevel === "warning") {
1119
- console.warn(
1136
+ this.#logger.warn(
1120
1137
  "[FastMCP warning] server is not responding to ping",
1121
1138
  );
1122
1139
  } else if (logLevel === "error") {
1123
- console.error(
1140
+ this.#logger.error(
1124
1141
  "[FastMCP error] server is not responding to ping",
1125
1142
  );
1126
1143
  } else {
1127
- console.info("[FastMCP info] server ping failed");
1144
+ this.#logger.info("[FastMCP info] server ping failed");
1128
1145
  }
1129
1146
  }
1130
1147
  }, pingConfig.intervalMs);
@@ -1360,7 +1377,7 @@ export class FastMCPSession<
1360
1377
 
1361
1378
  private setupErrorHandling() {
1362
1379
  this.#server.onerror = (error) => {
1363
- console.error("[FastMCP error]", error);
1380
+ this.#logger.error("[FastMCP error]", error);
1364
1381
  };
1365
1382
  }
1366
1383
 
@@ -1564,7 +1581,7 @@ export class FastMCPSession<
1564
1581
 
1565
1582
  private setupRootsHandlers() {
1566
1583
  if (this.#rootsConfig?.enabled === false) {
1567
- console.debug(
1584
+ this.#logger.debug(
1568
1585
  "[FastMCP debug] roots capability explicitly disabled via config",
1569
1586
  );
1570
1587
  return;
@@ -1589,11 +1606,11 @@ export class FastMCPSession<
1589
1606
  error instanceof McpError &&
1590
1607
  error.code === ErrorCode.MethodNotFound
1591
1608
  ) {
1592
- console.debug(
1609
+ this.#logger.debug(
1593
1610
  "[FastMCP debug] listRoots method not supported by client",
1594
1611
  );
1595
1612
  } else {
1596
- console.error(
1613
+ this.#logger.error(
1597
1614
  `[FastMCP error] received error listing roots.\n\n${
1598
1615
  error instanceof Error ? error.stack : JSON.stringify(error)
1599
1616
  }`,
@@ -1603,7 +1620,7 @@ export class FastMCPSession<
1603
1620
  },
1604
1621
  );
1605
1622
  } else {
1606
- console.debug(
1623
+ this.#logger.debug(
1607
1624
  "[FastMCP debug] roots capability not available, not setting up notification handler",
1608
1625
  );
1609
1626
  }
@@ -1686,7 +1703,7 @@ export class FastMCPSession<
1686
1703
  await new Promise((resolve) => setImmediate(resolve));
1687
1704
  }
1688
1705
  } catch (progressError) {
1689
- console.warn(
1706
+ this.#logger.warn(
1690
1707
  `[FastMCP warning] Failed to report progress for tool '${request.params.name}':`,
1691
1708
  progressError instanceof Error
1692
1709
  ? progressError.message
@@ -1753,7 +1770,7 @@ export class FastMCPSession<
1753
1770
  await new Promise((resolve) => setImmediate(resolve));
1754
1771
  }
1755
1772
  } catch (streamError) {
1756
- console.warn(
1773
+ this.#logger.warn(
1757
1774
  `[FastMCP warning] Failed to stream content for tool '${request.params.name}':`,
1758
1775
  streamError instanceof Error
1759
1776
  ? streamError.message
@@ -1879,6 +1896,7 @@ export class FastMCP<
1879
1896
  }
1880
1897
  #authenticate: Authenticate<T> | undefined;
1881
1898
  #httpStreamServer: null | SSEServer = null;
1899
+ #logger: Logger;
1882
1900
  #options: ServerOptions<T>;
1883
1901
  #prompts: InputPrompt<T>[] = [];
1884
1902
  #resources: Resource<T>[] = [];
@@ -1892,6 +1910,7 @@ export class FastMCP<
1892
1910
 
1893
1911
  this.#options = options;
1894
1912
  this.#authenticate = options.authenticate;
1913
+ this.#logger = options.logger || console;
1895
1914
  }
1896
1915
 
1897
1916
  /**
@@ -2029,6 +2048,7 @@ export class FastMCP<
2029
2048
  const transport = new StdioServerTransport();
2030
2049
  const session = new FastMCPSession<T>({
2031
2050
  instructions: this.#options.instructions,
2051
+ logger: this.#logger,
2032
2052
  name: this.#options.name,
2033
2053
  ping: this.#options.ping,
2034
2054
  prompts: this.#prompts,
@@ -2053,7 +2073,7 @@ export class FastMCP<
2053
2073
 
2054
2074
  if (httpConfig.stateless) {
2055
2075
  // Stateless mode - create new server instance for each request
2056
- console.info(
2076
+ this.#logger.info(
2057
2077
  `[FastMCP info] Starting server in stateless mode on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`,
2058
2078
  );
2059
2079
 
@@ -2077,7 +2097,7 @@ export class FastMCP<
2077
2097
  },
2078
2098
  onConnect: async () => {
2079
2099
  // No persistent session tracking in stateless mode
2080
- console.debug(
2100
+ this.#logger.debug(
2081
2101
  `[FastMCP debug] Stateless HTTP Stream request handled`,
2082
2102
  );
2083
2103
  },
@@ -2110,7 +2130,7 @@ export class FastMCP<
2110
2130
  onConnect: async (session) => {
2111
2131
  this.#sessions.push(session);
2112
2132
 
2113
- console.info(`[FastMCP info] HTTP Stream session established`);
2133
+ this.#logger.info(`[FastMCP info] HTTP Stream session established`);
2114
2134
 
2115
2135
  this.emit("connect", {
2116
2136
  session: session as FastMCPSession<FastMCPSessionAuth>,
@@ -2124,10 +2144,10 @@ export class FastMCP<
2124
2144
  streamEndpoint: httpConfig.endpoint,
2125
2145
  });
2126
2146
 
2127
- console.info(
2147
+ this.#logger.info(
2128
2148
  `[FastMCP info] server is running on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`,
2129
2149
  );
2130
- console.info(
2150
+ this.#logger.info(
2131
2151
  `[FastMCP info] Transport type: httpStream (Streamable HTTP, not SSE)`,
2132
2152
  );
2133
2153
  }
@@ -2157,6 +2177,7 @@ export class FastMCP<
2157
2177
  : this.#tools;
2158
2178
  return new FastMCPSession<T>({
2159
2179
  auth,
2180
+ logger: this.#logger,
2160
2181
  name: this.#options.name,
2161
2182
  ping: this.#options.ping,
2162
2183
  prompts: this.#prompts,
@@ -2242,7 +2263,7 @@ export class FastMCP<
2242
2263
  return;
2243
2264
  }
2244
2265
  } catch (error) {
2245
- console.error("[FastMCP error] health endpoint error", error);
2266
+ this.#logger.error("[FastMCP error] health endpoint error", error);
2246
2267
  }
2247
2268
  }
2248
2269
 
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Example FastMCP server demonstrating custom logger implementations.
3
+ *
4
+ * Features demonstrated:
5
+ * - Simple custom logger implementation
6
+ * - File-based logging example
7
+ * - Winston logger adapter
8
+ * - Pino logger adapter
9
+ *
10
+ */
11
+
12
+ import { z } from "zod";
13
+
14
+ import { FastMCP, Logger } from "../FastMCP.js";
15
+
16
+ // Example 1: Simple Custom Logger Implementation
17
+ class SimpleCustomLogger implements Logger {
18
+ debug(...args: unknown[]): void {
19
+ console.log("[CUSTOM DEBUG]", new Date().toISOString(), ...args);
20
+ }
21
+
22
+ error(...args: unknown[]): void {
23
+ console.error("[CUSTOM ERROR]", new Date().toISOString(), ...args);
24
+ }
25
+
26
+ info(...args: unknown[]): void {
27
+ console.info("[CUSTOM INFO]", new Date().toISOString(), ...args);
28
+ }
29
+
30
+ log(...args: unknown[]): void {
31
+ console.log("[CUSTOM LOG]", new Date().toISOString(), ...args);
32
+ }
33
+
34
+ warn(...args: unknown[]): void {
35
+ console.warn("[CUSTOM WARN]", new Date().toISOString(), ...args);
36
+ }
37
+ }
38
+
39
+ // Example 2: File-based Logger
40
+ // class FileLogger implements Logger {
41
+ // debug(...args: unknown[]): void {
42
+ // this.logToFile('DEBUG', ...args);
43
+ // }
44
+
45
+ // error(...args: unknown[]): void {
46
+ // this.logToFile('ERROR', ...args);
47
+ // }
48
+
49
+ // info(...args: unknown[]): void {
50
+ // this.logToFile('INFO', ...args);
51
+ // }
52
+
53
+ // log(...args: unknown[]): void {
54
+ // this.logToFile('LOG', ...args);
55
+ // }
56
+
57
+ // warn(...args: unknown[]): void {
58
+ // this.logToFile('WARN', ...args);
59
+ // }
60
+
61
+ // private logToFile(level: string, ...args: unknown[]): void {
62
+ // const timestamp = new Date().toISOString();
63
+ // const message = `[${timestamp}] [${level}] ${args.map(arg =>
64
+ // typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
65
+ // ).join(' ')}\n`;
66
+
67
+ // // In a real implementation, you might use fs.appendFile or a logging library
68
+ // console.log(message.trim());
69
+ // }
70
+ // }
71
+
72
+ // Example 3: Winston Logger Adapter
73
+ // To use this example, install winston: npm install winston
74
+ // import winston from 'winston';
75
+
76
+ // class WinstonLoggerAdapter implements Logger {
77
+ // private winston: winston.Logger;
78
+
79
+ // constructor() {
80
+ // this.winston = winston.createLogger({
81
+ // level: 'debug',
82
+ // format: winston.format.combine(
83
+ // winston.format.timestamp(),
84
+ // winston.format.errors({ stack: true }),
85
+ // winston.format.json()
86
+ // ),
87
+ // transports: [
88
+ // new winston.transports.Console({
89
+ // format: winston.format.combine(
90
+ // winston.format.colorize(),
91
+ // winston.format.simple()
92
+ // )
93
+ // }),
94
+ // new winston.transports.File({ filename: 'fastmcp.log' })
95
+ // ]
96
+ // });
97
+ // }
98
+
99
+ // debug(...args: unknown[]): void {
100
+ // this.winston.debug(this.formatArgs(args));
101
+ // }
102
+
103
+ // error(...args: unknown[]): void {
104
+ // this.winston.error(this.formatArgs(args));
105
+ // }
106
+
107
+ // info(...args: unknown[]): void {
108
+ // this.winston.info(this.formatArgs(args));
109
+ // }
110
+
111
+ // log(...args: unknown[]): void {
112
+ // this.winston.info(this.formatArgs(args));
113
+ // }
114
+
115
+ // warn(...args: unknown[]): void {
116
+ // this.winston.warn(this.formatArgs(args));
117
+ // }
118
+
119
+ // private formatArgs(args: unknown[]): string {
120
+ // return args.map(arg =>
121
+ // typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
122
+ // ).join(' ');
123
+ // }
124
+ // }
125
+
126
+ // Example 4: Pino Logger Adapter
127
+ // To use this example, install pino: npm install pino
128
+ // import pino from 'pino';
129
+ //
130
+ // class PinoLoggerAdapter implements Logger {
131
+ // private pino: pino.Logger;
132
+ //
133
+ // constructor() {
134
+ // this.pino = pino({
135
+ // level: 'debug',
136
+ // transport: {
137
+ // target: 'pino-pretty',
138
+ // options: {
139
+ // colorize: true,
140
+ // translateTime: 'SYS:standard',
141
+ // ignore: 'pid,hostname'
142
+ // }
143
+ // }
144
+ // });
145
+ // }
146
+ //
147
+ // debug(...args: unknown[]): void {
148
+ // this.pino.debug(this.formatMessage(args));
149
+ // }
150
+ //
151
+ // error(...args: unknown[]): void {
152
+ // this.pino.error(this.formatMessage(args));
153
+ // }
154
+ //
155
+ // info(...args: unknown[]): void {
156
+ // this.pino.info(this.formatMessage(args));
157
+ // }
158
+ //
159
+ // log(...args: unknown[]): void {
160
+ // this.pino.info(this.formatMessage(args));
161
+ // }
162
+ //
163
+ // warn(...args: unknown[]): void {
164
+ // this.pino.warn(this.formatMessage(args));
165
+ // }
166
+ //
167
+ // private formatMessage(args: unknown[]): string {
168
+ // return args.map(arg =>
169
+ // typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
170
+ // ).join(' ');
171
+ // }
172
+ // }
173
+
174
+ // Choose which logger to use (uncomment the one you want to use)
175
+ const logger = new SimpleCustomLogger();
176
+ // const logger = new FileLogger();
177
+ // const logger = new WinstonLoggerAdapter();
178
+ // const logger = new PinoLoggerAdapter();
179
+
180
+ const server = new FastMCP({
181
+ logger: logger,
182
+ name: "custom-logger-example",
183
+ version: "1.0.0",
184
+ });
185
+
186
+ server.addTool({
187
+ description: "A test tool that demonstrates custom logging",
188
+ execute: async (args) => {
189
+ return `Received: ${args.message}`;
190
+ },
191
+ name: "test_tool",
192
+ parameters: z.object({
193
+ message: z.string().describe("A message to log"),
194
+ }),
195
+ });
196
+
197
+ // Start the server with stdio transport
198
+ server.start({ transportType: "stdio" }).catch((error: unknown) => {
199
+ console.error("Failed to start server:", error);
200
+ process.exit(1);
201
+ });