fastmcp 3.13.0 → 3.14.1

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;