fastmcp 4.22.8 → 4.22.10

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.
@@ -668,10 +668,6 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
668
668
  }
669
669
  }
670
670
  async close() {
671
- this.#connectionState = "closed";
672
- if (this.#pingInterval) {
673
- clearInterval(this.#pingInterval);
674
- }
675
671
  this.#abortSession();
676
672
  try {
677
673
  await this.#server.close();
@@ -828,11 +824,16 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
828
824
  });
829
825
  }
830
826
  /**
831
- * Cancels the `signal` held by every tool still executing on this session.
832
- * Idempotent, so the close path and the transport's own close handler can
833
- * both call it.
827
+ * Marks the session closed, stops pinging the client and cancels the
828
+ * `signal` held by every tool still executing on this session. Idempotent,
829
+ * so the close path and the transport's own close handler can both call it.
834
830
  */
835
831
  #abortSession() {
832
+ this.#connectionState = "closed";
833
+ if (this.#pingInterval) {
834
+ clearInterval(this.#pingInterval);
835
+ this.#pingInterval = null;
836
+ }
836
837
  if (!this.#abortController.signal.aborted) {
837
838
  this.#abortController.abort(new SessionError("Session closed"));
838
839
  }
@@ -1013,6 +1014,9 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1013
1014
  void this.#loadRoots(true);
1014
1015
  } else {
1015
1016
  await this.#loadRoots(false);
1017
+ if (this.#abortController.signal.aborted) {
1018
+ return;
1019
+ }
1016
1020
  }
1017
1021
  if (this.#clientCapabilities) {
1018
1022
  const pingConfig = this.#getPingConfig();
@@ -1775,6 +1779,35 @@ function parseBasicAuthHeader(authHeader) {
1775
1779
  return null;
1776
1780
  }
1777
1781
  }
1782
+ function parseBooleanSetting(value, source) {
1783
+ if (!value) {
1784
+ return void 0;
1785
+ }
1786
+ switch (value.toLowerCase()) {
1787
+ case "false":
1788
+ return false;
1789
+ case "true":
1790
+ return true;
1791
+ default:
1792
+ throw new Error(`${source} must be "true" or "false", got "${value}"`);
1793
+ }
1794
+ }
1795
+ function parseTransportSetting(value, source) {
1796
+ if (!value) {
1797
+ return void 0;
1798
+ }
1799
+ switch (value.toLowerCase()) {
1800
+ case "http-stream":
1801
+ case "httpstream":
1802
+ return "httpStream";
1803
+ case "stdio":
1804
+ return "stdio";
1805
+ default:
1806
+ throw new Error(
1807
+ `${source} must be "stdio", "httpStream" or "http-stream", got "${value}"`
1808
+ );
1809
+ }
1810
+ }
1778
1811
  function protectedResourceMetadataUrl(resource) {
1779
1812
  let url;
1780
1813
  try {
@@ -1972,7 +2005,8 @@ var FastMCP = class extends FastMCPEventEmitter {
1972
2005
  *
1973
2006
  * The transport's lifecycle belongs to the caller: `stop()` does not close
1974
2007
  * transports passed here. Close the returned session (or the transport) when
1975
- * you are done with it.
2008
+ * you are done with it. If the client hangs up before the session is set up,
2009
+ * the returned session is already closed and is not listed in `sessions`.
1976
2010
  *
1977
2011
  * @param transport - An already-constructed MCP server transport.
1978
2012
  * @param auth - Session auth, equivalent to what `authenticate` would return.
@@ -1980,21 +2014,7 @@ var FastMCP = class extends FastMCPEventEmitter {
1980
2014
  */
1981
2015
  async connect(transport, auth) {
1982
2016
  const session = this.#createSession(auth);
1983
- await session.connect(transport);
1984
- this.#sessions.push(session);
1985
- session.once("error", () => {
1986
- this.#removeSession(session);
1987
- });
1988
- const originalOnClose = transport.onclose;
1989
- transport.onclose = () => {
1990
- this.#removeSession(session);
1991
- if (originalOnClose) {
1992
- originalOnClose();
1993
- }
1994
- };
1995
- this.emit("connect", {
1996
- session
1997
- });
2017
+ await this.#connectSession(session, transport);
1998
2018
  this.#serverState = "running" /* Running */;
1999
2019
  return session;
2000
2020
  }
@@ -2232,7 +2252,6 @@ var FastMCP = class extends FastMCPEventEmitter {
2232
2252
  version: this.#options.version,
2233
2253
  websiteUrl: this.#options.websiteUrl
2234
2254
  });
2235
- await session.connect(transport);
2236
2255
  let stdinClosed = false;
2237
2256
  const onStdinClose = () => {
2238
2257
  if (stdinClosed) return;
@@ -2244,29 +2263,9 @@ var FastMCP = class extends FastMCPEventEmitter {
2244
2263
  };
2245
2264
  process.stdin.on("close", onStdinClose);
2246
2265
  process.stdin.on("end", onStdinClose);
2247
- this.#sessions.push(session);
2248
- session.once("error", () => {
2249
- this.#removeSession(session);
2250
- });
2251
- if (transport.onclose) {
2252
- const originalOnClose = transport.onclose;
2253
- transport.onclose = () => {
2254
- process.stdin.off("close", onStdinClose);
2255
- process.stdin.off("end", onStdinClose);
2256
- this.#removeSession(session);
2257
- if (originalOnClose) {
2258
- originalOnClose();
2259
- }
2260
- };
2261
- } else {
2262
- transport.onclose = () => {
2263
- process.stdin.off("close", onStdinClose);
2264
- process.stdin.off("end", onStdinClose);
2265
- this.#removeSession(session);
2266
- };
2267
- }
2268
- this.emit("connect", {
2269
- session
2266
+ await this.#connectSession(session, transport, () => {
2267
+ process.stdin.off("close", onStdinClose);
2268
+ process.stdin.off("end", onStdinClose);
2270
2269
  });
2271
2270
  this.#serverState = "running" /* Running */;
2272
2271
  } else if (config.transportType === "httpStream") {
@@ -2388,6 +2387,32 @@ var FastMCP = class extends FastMCPEventEmitter {
2388
2387
  }
2389
2388
  this.#serverState = "stopped" /* Stopped */;
2390
2389
  }
2390
+ /**
2391
+ * Connects `session` to `transport` and lists it in `sessions` until the
2392
+ * transport closes. The close is watched from before the session connects,
2393
+ * so a client that hangs up during setup leaves no session behind.
2394
+ */
2395
+ async #connectSession(session, transport, onClose) {
2396
+ let closed = false;
2397
+ const originalOnClose = transport.onclose;
2398
+ transport.onclose = () => {
2399
+ closed = true;
2400
+ onClose?.();
2401
+ this.#removeSession(session);
2402
+ originalOnClose?.();
2403
+ };
2404
+ await session.connect(transport);
2405
+ if (closed) {
2406
+ return;
2407
+ }
2408
+ this.#sessions.push(session);
2409
+ session.once("error", () => {
2410
+ this.#removeSession(session);
2411
+ });
2412
+ this.emit("connect", {
2413
+ session
2414
+ });
2415
+ }
2391
2416
  /**
2392
2417
  * Creates a new FastMCPSession instance with the current configuration.
2393
2418
  * Used both for regular sessions and stateless requests.
@@ -2982,8 +3007,17 @@ var FastMCP = class extends FastMCPEventEmitter {
2982
3007
  #parseRuntimeConfig(overrides) {
2983
3008
  const args = process.argv.slice(2);
2984
3009
  const getArg = (name) => {
2985
- const index = args.findIndex((arg) => arg === `--${name}`);
2986
- return index !== -1 && index + 1 < args.length ? args[index + 1] : void 0;
3010
+ const index = args.findIndex(
3011
+ (arg) => arg === `--${name}` || arg.startsWith(`--${name}=`)
3012
+ );
3013
+ if (index === -1) {
3014
+ return void 0;
3015
+ }
3016
+ if (args[index] !== `--${name}`) {
3017
+ return args[index].slice(name.length + 3);
3018
+ }
3019
+ const value = args[index + 1];
3020
+ return value === void 0 || value.startsWith("--") ? "" : value;
2987
3021
  };
2988
3022
  const transportArg = getArg("transport");
2989
3023
  const portArg = getArg("port");
@@ -2997,7 +3031,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2997
3031
  const envBasePath = process.env.FASTMCP_BASE_PATH;
2998
3032
  const envStateless = process.env.FASTMCP_STATELESS;
2999
3033
  const envHost = process.env.FASTMCP_HOST;
3000
- const transportType = overrides?.transportType || (transportArg === "http-stream" ? "httpStream" : transportArg) || envTransport || "stdio";
3034
+ const transportType = overrides?.transportType || parseTransportSetting(transportArg, "--transport") || parseTransportSetting(envTransport, "FASTMCP_TRANSPORT") || "stdio";
3001
3035
  if (transportType === "httpStream") {
3002
3036
  const port = parseInt(
3003
3037
  overrides?.httpStream?.port?.toString() || portArg || envPort || "8080"
@@ -3008,7 +3042,8 @@ var FastMCP = class extends FastMCPEventEmitter {
3008
3042
  overrides?.httpStream?.basePath || basePathArg || envBasePath
3009
3043
  );
3010
3044
  const enableJsonResponse = overrides?.httpStream?.enableJsonResponse || false;
3011
- const stateless = overrides?.httpStream?.stateless ?? (statelessArg !== void 0 ? statelessArg === "true" : envStateless === "true");
3045
+ const stateless = overrides?.httpStream?.stateless ?? // A bare `--stateless` means `--stateless true`.
3046
+ (statelessArg === "" ? true : parseBooleanSetting(statelessArg, "--stateless")) ?? parseBooleanSetting(envStateless, "FASTMCP_STATELESS") ?? false;
3012
3047
  const cors = overrides?.httpStream?.cors;
3013
3048
  const eventStore = overrides?.httpStream?.eventStore;
3014
3049
  const sslCa = overrides?.httpStream?.sslCa;
@@ -3134,4 +3169,4 @@ export {
3134
3169
  FastMCPSession,
3135
3170
  FastMCP
3136
3171
  };
3137
- //# sourceMappingURL=chunk-76QWMCYJ.js.map
3172
+ //# sourceMappingURL=chunk-RVBVCLCO.js.map