fastmcp 3.12.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
@@ -18,6 +18,7 @@ A TypeScript framework for building [MCP](https://glama.ai/mcp) servers capable
18
18
  - [Logging](#logging)
19
19
  - [Error handling](#errors)
20
20
  - [HTTP Streaming](#http-streaming) (with SSE compatibility)
21
+ - [Stateless mode](#stateless-mode) for serverless deployments
21
22
  - CORS (enabled by default)
22
23
  - [Progress notifications](#progress)
23
24
  - [Streaming output](#streaming-output)
@@ -178,6 +179,52 @@ const transport = new SSEClientTransport(new URL(`http://localhost:8080/sse`));
178
179
  await client.connect(transport);
179
180
  ```
180
181
 
182
+ #### Stateless Mode
183
+
184
+ FastMCP supports stateless operation for HTTP streaming, where each request is handled independently without maintaining persistent sessions. This is ideal for serverless environments, load-balanced deployments, or when session state isn't required.
185
+
186
+ In stateless mode:
187
+
188
+ - No sessions are tracked on the server
189
+ - Each request creates a temporary session that's discarded after the response
190
+ - Reduced memory usage and better scalability
191
+ - Perfect for stateless deployment environments
192
+
193
+ You can enable stateless mode by adding the `stateless: true` option:
194
+
195
+ ```ts
196
+ server.start({
197
+ transportType: "httpStream",
198
+ httpStream: {
199
+ port: 8080,
200
+ stateless: true,
201
+ },
202
+ });
203
+ ```
204
+
205
+ > **Note:** Stateless mode is only available with HTTP streaming transport. Features that depend on persistent sessions (like session-specific state) will not be available in stateless mode.
206
+
207
+ You can also enable stateless mode using CLI arguments or environment variables:
208
+
209
+ ```bash
210
+ # Via CLI argument
211
+ npx fastmcp dev src/server.ts --transport http-stream --port 8080 --stateless true
212
+
213
+ # Via environment variable
214
+ FASTMCP_STATELESS=true npx fastmcp dev src/server.ts
215
+ ```
216
+
217
+ The `/ready` health check endpoint will indicate when the server is running in stateless mode:
218
+
219
+ ```json
220
+ {
221
+ "mode": "stateless",
222
+ "ready": 1,
223
+ "status": "ready",
224
+ "total": 1
225
+ }
226
+ ```
227
+
181
228
  ## Core Concepts
182
229
 
183
230
  ### Tools
@@ -649,6 +696,44 @@ server.addTool({
649
696
  });
650
697
  ```
651
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
+
652
737
  #### Logging
653
738
 
654
739
  Tools can log messages to the client using the `log` object in the context object:
@@ -1666,6 +1751,7 @@ Refer to this [issue](https://github.com/punkpeye/fastmcp/issues/25#issuecomment
1666
1751
  - [cswkim/discogs-mcp-server](https://github.com/cswkim/discogs-mcp-server) - connects to the Discogs API for interacting with your music collection
1667
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
1668
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
1669
1755
 
1670
1756
  ## Acknowledgements
1671
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>[];
@@ -606,6 +619,7 @@ declare class FastMCP<T extends FastMCPSessionAuth = FastMCPSessionAuth> extends
606
619
  endpoint?: `/${string}`;
607
620
  eventStore?: EventStore;
608
621
  port: number;
622
+ stateless?: boolean;
609
623
  };
610
624
  transportType: "httpStream" | "stdio";
611
625
  }>): Promise<void>;
@@ -615,4 +629,4 @@ declare class FastMCP<T extends FastMCPSessionAuth = FastMCPSessionAuth> extends
615
629
  stop(): Promise<void>;
616
630
  }
617
631
 
618
- 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,
@@ -1091,109 +1097,71 @@ var FastMCP = class extends FastMCPEventEmitter {
1091
1097
  });
1092
1098
  } else if (config.transportType === "httpStream") {
1093
1099
  const httpConfig = config.httpStream;
1094
- this.#httpStreamServer = await startHTTPServer({
1095
- createServer: async (request) => {
1096
- let auth;
1097
- if (this.#authenticate) {
1098
- auth = await this.#authenticate(request);
1099
- }
1100
- const allowedTools = auth ? this.#tools.filter(
1101
- (tool) => tool.canAccess ? tool.canAccess(auth) : true
1102
- ) : this.#tools;
1103
- return new FastMCPSession({
1104
- auth,
1105
- name: this.#options.name,
1106
- ping: this.#options.ping,
1107
- prompts: this.#prompts,
1108
- resources: this.#resources,
1109
- resourcesTemplates: this.#resourcesTemplates,
1110
- roots: this.#options.roots,
1111
- tools: allowedTools,
1112
- transportType: "httpStream",
1113
- utils: this.#options.utils,
1114
- version: this.#options.version
1115
- });
1116
- },
1117
- enableJsonResponse: httpConfig.enableJsonResponse,
1118
- eventStore: httpConfig.eventStore,
1119
- onClose: async (session) => {
1120
- this.emit("disconnect", {
1121
- session
1122
- });
1123
- },
1124
- onConnect: async (session) => {
1125
- this.#sessions.push(session);
1126
- console.info(`[FastMCP info] HTTP Stream session established`);
1127
- this.emit("connect", {
1128
- session
1129
- });
1130
- },
1131
- onUnhandledRequest: async (req, res) => {
1132
- const healthConfig = this.#options.health ?? {};
1133
- const enabled = healthConfig.enabled === void 0 ? true : healthConfig.enabled;
1134
- if (enabled) {
1135
- const path = healthConfig.path ?? "/health";
1136
- const url = new URL(req.url || "", "http://localhost");
1137
- try {
1138
- if (req.method === "GET" && url.pathname === path) {
1139
- res.writeHead(healthConfig.status ?? 200, {
1140
- "Content-Type": "text/plain"
1141
- }).end(healthConfig.message ?? "\u2713 Ok");
1142
- return;
1143
- }
1144
- if (req.method === "GET" && url.pathname === "/ready") {
1145
- const readySessions = this.#sessions.filter(
1146
- (s) => s.isReady
1147
- ).length;
1148
- const totalSessions = this.#sessions.length;
1149
- const allReady = readySessions === totalSessions && totalSessions > 0;
1150
- const response = {
1151
- ready: readySessions,
1152
- status: allReady ? "ready" : totalSessions === 0 ? "no_sessions" : "initializing",
1153
- total: totalSessions
1154
- };
1155
- res.writeHead(allReady ? 200 : 503, {
1156
- "Content-Type": "application/json"
1157
- }).end(JSON.stringify(response));
1158
- return;
1159
- }
1160
- } catch (error) {
1161
- console.error("[FastMCP error] health endpoint error", error);
1162
- }
1163
- }
1164
- const oauthConfig = this.#options.oauth;
1165
- if (oauthConfig?.enabled && req.method === "GET") {
1166
- const url = new URL(req.url || "", "http://localhost");
1167
- if (url.pathname === "/.well-known/oauth-authorization-server" && oauthConfig.authorizationServer) {
1168
- const metadata = convertObjectToSnakeCase(
1169
- oauthConfig.authorizationServer
1170
- );
1171
- res.writeHead(200, {
1172
- "Content-Type": "application/json"
1173
- }).end(JSON.stringify(metadata));
1174
- return;
1100
+ if (httpConfig.stateless) {
1101
+ this.#logger.info(
1102
+ `[FastMCP info] Starting server in stateless mode on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`
1103
+ );
1104
+ this.#httpStreamServer = await startHTTPServer({
1105
+ createServer: async (request) => {
1106
+ let auth;
1107
+ if (this.#authenticate) {
1108
+ auth = await this.#authenticate(request);
1175
1109
  }
1176
- if (url.pathname === "/.well-known/oauth-protected-resource" && oauthConfig.protectedResource) {
1177
- const metadata = convertObjectToSnakeCase(
1178
- oauthConfig.protectedResource
1179
- );
1180
- res.writeHead(200, {
1181
- "Content-Type": "application/json"
1182
- }).end(JSON.stringify(metadata));
1183
- return;
1110
+ return this.#createSession(auth);
1111
+ },
1112
+ enableJsonResponse: httpConfig.enableJsonResponse,
1113
+ eventStore: httpConfig.eventStore,
1114
+ // In stateless mode, we don't track sessions
1115
+ onClose: async () => {
1116
+ },
1117
+ onConnect: async () => {
1118
+ this.#logger.debug(
1119
+ `[FastMCP debug] Stateless HTTP Stream request handled`
1120
+ );
1121
+ },
1122
+ onUnhandledRequest: async (req, res) => {
1123
+ await this.#handleUnhandledRequest(req, res, true);
1124
+ },
1125
+ port: httpConfig.port,
1126
+ stateless: true,
1127
+ streamEndpoint: httpConfig.endpoint
1128
+ });
1129
+ } else {
1130
+ this.#httpStreamServer = await startHTTPServer({
1131
+ createServer: async (request) => {
1132
+ let auth;
1133
+ if (this.#authenticate) {
1134
+ auth = await this.#authenticate(request);
1184
1135
  }
1185
- }
1186
- res.writeHead(404).end();
1187
- },
1188
- port: httpConfig.port,
1189
- streamEndpoint: httpConfig.endpoint
1190
- });
1191
- console.info(
1192
- `[FastMCP info] server is running on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`
1193
- );
1194
- console.info(
1195
- `[FastMCP info] Transport type: httpStream (Streamable HTTP, not SSE)`
1196
- );
1136
+ return this.#createSession(auth);
1137
+ },
1138
+ enableJsonResponse: httpConfig.enableJsonResponse,
1139
+ eventStore: httpConfig.eventStore,
1140
+ onClose: async (session) => {
1141
+ this.emit("disconnect", {
1142
+ session
1143
+ });
1144
+ },
1145
+ onConnect: async (session) => {
1146
+ this.#sessions.push(session);
1147
+ this.#logger.info(`[FastMCP info] HTTP Stream session established`);
1148
+ this.emit("connect", {
1149
+ session
1150
+ });
1151
+ },
1152
+ onUnhandledRequest: async (req, res) => {
1153
+ await this.#handleUnhandledRequest(req, res, false);
1154
+ },
1155
+ port: httpConfig.port,
1156
+ streamEndpoint: httpConfig.endpoint
1157
+ });
1158
+ this.#logger.info(
1159
+ `[FastMCP info] server is running on HTTP Stream at http://localhost:${httpConfig.port}${httpConfig.endpoint}`
1160
+ );
1161
+ this.#logger.info(
1162
+ `[FastMCP info] Transport type: httpStream (Streamable HTTP, not SSE)`
1163
+ );
1164
+ }
1197
1165
  } else {
1198
1166
  throw new Error("Invalid transport type");
1199
1167
  }
@@ -1206,6 +1174,101 @@ var FastMCP = class extends FastMCPEventEmitter {
1206
1174
  await this.#httpStreamServer.close();
1207
1175
  }
1208
1176
  }
1177
+ /**
1178
+ * Creates a new FastMCPSession instance with the current configuration.
1179
+ * Used both for regular sessions and stateless requests.
1180
+ */
1181
+ #createSession(auth) {
1182
+ const allowedTools = auth ? this.#tools.filter(
1183
+ (tool) => tool.canAccess ? tool.canAccess(auth) : true
1184
+ ) : this.#tools;
1185
+ return new FastMCPSession({
1186
+ auth,
1187
+ logger: this.#logger,
1188
+ name: this.#options.name,
1189
+ ping: this.#options.ping,
1190
+ prompts: this.#prompts,
1191
+ resources: this.#resources,
1192
+ resourcesTemplates: this.#resourcesTemplates,
1193
+ roots: this.#options.roots,
1194
+ tools: allowedTools,
1195
+ transportType: "httpStream",
1196
+ utils: this.#options.utils,
1197
+ version: this.#options.version
1198
+ });
1199
+ }
1200
+ /**
1201
+ * Handles unhandled HTTP requests with health, readiness, and OAuth endpoints
1202
+ */
1203
+ #handleUnhandledRequest = async (req, res, isStateless = false) => {
1204
+ const healthConfig = this.#options.health ?? {};
1205
+ const enabled = healthConfig.enabled === void 0 ? true : healthConfig.enabled;
1206
+ if (enabled) {
1207
+ const path = healthConfig.path ?? "/health";
1208
+ const url = new URL(req.url || "", "http://localhost");
1209
+ try {
1210
+ if (req.method === "GET" && url.pathname === path) {
1211
+ res.writeHead(healthConfig.status ?? 200, {
1212
+ "Content-Type": "text/plain"
1213
+ }).end(healthConfig.message ?? "\u2713 Ok");
1214
+ return;
1215
+ }
1216
+ if (req.method === "GET" && url.pathname === "/ready") {
1217
+ if (isStateless) {
1218
+ const response = {
1219
+ mode: "stateless",
1220
+ ready: 1,
1221
+ status: "ready",
1222
+ total: 1
1223
+ };
1224
+ res.writeHead(200, {
1225
+ "Content-Type": "application/json"
1226
+ }).end(JSON.stringify(response));
1227
+ } else {
1228
+ const readySessions = this.#sessions.filter(
1229
+ (s) => s.isReady
1230
+ ).length;
1231
+ const totalSessions = this.#sessions.length;
1232
+ const allReady = readySessions === totalSessions && totalSessions > 0;
1233
+ const response = {
1234
+ ready: readySessions,
1235
+ status: allReady ? "ready" : totalSessions === 0 ? "no_sessions" : "initializing",
1236
+ total: totalSessions
1237
+ };
1238
+ res.writeHead(allReady ? 200 : 503, {
1239
+ "Content-Type": "application/json"
1240
+ }).end(JSON.stringify(response));
1241
+ }
1242
+ return;
1243
+ }
1244
+ } catch (error) {
1245
+ this.#logger.error("[FastMCP error] health endpoint error", error);
1246
+ }
1247
+ }
1248
+ const oauthConfig = this.#options.oauth;
1249
+ if (oauthConfig?.enabled && req.method === "GET") {
1250
+ const url = new URL(req.url || "", "http://localhost");
1251
+ if (url.pathname === "/.well-known/oauth-authorization-server" && oauthConfig.authorizationServer) {
1252
+ const metadata = convertObjectToSnakeCase(
1253
+ oauthConfig.authorizationServer
1254
+ );
1255
+ res.writeHead(200, {
1256
+ "Content-Type": "application/json"
1257
+ }).end(JSON.stringify(metadata));
1258
+ return;
1259
+ }
1260
+ if (url.pathname === "/.well-known/oauth-protected-resource" && oauthConfig.protectedResource) {
1261
+ const metadata = convertObjectToSnakeCase(
1262
+ oauthConfig.protectedResource
1263
+ );
1264
+ res.writeHead(200, {
1265
+ "Content-Type": "application/json"
1266
+ }).end(JSON.stringify(metadata));
1267
+ return;
1268
+ }
1269
+ }
1270
+ res.writeHead(404).end();
1271
+ };
1209
1272
  #parseRuntimeConfig(overrides) {
1210
1273
  const args = process.argv.slice(2);
1211
1274
  const getArg = (name) => {
@@ -1215,9 +1278,11 @@ var FastMCP = class extends FastMCPEventEmitter {
1215
1278
  const transportArg = getArg("transport");
1216
1279
  const portArg = getArg("port");
1217
1280
  const endpointArg = getArg("endpoint");
1281
+ const statelessArg = getArg("stateless");
1218
1282
  const envTransport = process.env.FASTMCP_TRANSPORT;
1219
1283
  const envPort = process.env.FASTMCP_PORT;
1220
1284
  const envEndpoint = process.env.FASTMCP_ENDPOINT;
1285
+ const envStateless = process.env.FASTMCP_STATELESS;
1221
1286
  const transportType = overrides?.transportType || (transportArg === "http-stream" ? "httpStream" : transportArg) || envTransport || "stdio";
1222
1287
  if (transportType === "httpStream") {
1223
1288
  const port = parseInt(
@@ -1225,11 +1290,13 @@ var FastMCP = class extends FastMCPEventEmitter {
1225
1290
  );
1226
1291
  const endpoint = overrides?.httpStream?.endpoint || endpointArg || envEndpoint || "/mcp";
1227
1292
  const enableJsonResponse = overrides?.httpStream?.enableJsonResponse || false;
1293
+ const stateless = overrides?.httpStream?.stateless || statelessArg === "true" || envStateless === "true" || false;
1228
1294
  return {
1229
1295
  httpStream: {
1230
1296
  enableJsonResponse,
1231
1297
  endpoint,
1232
- port
1298
+ port,
1299
+ stateless
1233
1300
  },
1234
1301
  transportType: "httpStream"
1235
1302
  };