fastmcp 4.19.0 → 4.20.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.
@@ -1,3 +1,7 @@
1
+ import {
2
+ cancelResponseBody
3
+ } from "./chunk-LRXZYGFR.js";
4
+
1
5
  // src/FastMCP.ts
2
6
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
@@ -27,17 +31,6 @@ import parseURITemplate from "uri-templates";
27
31
  import { strictJsonSchema, toJsonSchema } from "xsschema";
28
32
  import { z } from "zod";
29
33
 
30
- // src/cancelResponseBody.ts
31
- var cancelResponseBody = async (response) => {
32
- if (!response.body) {
33
- return;
34
- }
35
- try {
36
- await response.body.cancel();
37
- } catch {
38
- }
39
- };
40
-
41
34
  // src/DiscoveryDocumentCache.ts
42
35
  var DiscoveryDocumentCache = class {
43
36
  get size() {
@@ -2089,15 +2082,14 @@ var FastMCP = class extends FastMCPEventEmitter {
2089
2082
  this.#logger.info(
2090
2083
  `[FastMCP info] Starting server in stateless mode on HTTP Stream at ${protocol}://${httpConfig.host}:${httpConfig.port}${streamEndpoint}`
2091
2084
  );
2085
+ const authenticate = this.#authenticate ? this.#memoizedAuthenticate() : void 0;
2092
2086
  this.#httpStreamServer = await startHTTPServer({
2093
- ...this.#authenticate ? { authenticate: this.#authenticate } : {},
2087
+ ...authenticate ? { authenticate } : {},
2094
2088
  cors: httpConfig.cors,
2095
2089
  createServer: async (request) => {
2096
2090
  let auth;
2097
- if (this.#authenticate) {
2098
- auth = this.#requireAuthenticated(
2099
- await this.#authenticate(request)
2100
- );
2091
+ if (authenticate) {
2092
+ auth = this.#requireAuthenticated(await authenticate(request));
2101
2093
  }
2102
2094
  const sessionId = Array.isArray(request.headers["mcp-session-id"]) ? request.headers["mcp-session-id"][0] : request.headers["mcp-session-id"];
2103
2095
  return this.#createSession(auth, sessionId, true);
@@ -2138,15 +2130,14 @@ var FastMCP = class extends FastMCPEventEmitter {
2138
2130
  streamEndpoint
2139
2131
  });
2140
2132
  } else {
2133
+ const authenticate = this.#authenticate ? this.#memoizedAuthenticate() : void 0;
2141
2134
  this.#httpStreamServer = await startHTTPServer({
2142
- ...this.#authenticate ? { authenticate: this.#authenticate } : {},
2135
+ ...authenticate ? { authenticate } : {},
2143
2136
  cors: httpConfig.cors,
2144
2137
  createServer: async (request) => {
2145
2138
  let auth;
2146
- if (this.#authenticate) {
2147
- auth = this.#requireAuthenticated(
2148
- await this.#authenticate(request)
2149
- );
2139
+ if (authenticate) {
2140
+ auth = this.#requireAuthenticated(await authenticate(request));
2150
2141
  }
2151
2142
  const sessionId = Array.isArray(request.headers["mcp-session-id"]) ? request.headers["mcp-session-id"][0] : request.headers["mcp-session-id"];
2152
2143
  return this.#createSession(auth, sessionId);
@@ -2699,6 +2690,40 @@ var FastMCP = class extends FastMCPEventEmitter {
2699
2690
  }
2700
2691
  }
2701
2692
  };
2693
+ /**
2694
+ * On `httpStream`, `authenticate` is handed to both mcp-proxy (which calls it
2695
+ * once per request to gate the 401) and this server's own `createServer`
2696
+ * callback (which calls it again to obtain the session auth). Both receive
2697
+ * the identical `IncomingMessage`, so a single request would otherwise be
2698
+ * authenticated twice — halving the effective budget of any non-idempotent
2699
+ * `authenticate` (see #352).
2700
+ *
2701
+ * This wraps `#authenticate` in a per-request memo keyed on the request
2702
+ * object, collapsing the two calls into one real invocation. The *promise* is
2703
+ * cached (not the resolved value) so a second call that arrives before the
2704
+ * first settles joins it rather than starting a competing attempt. Keys are
2705
+ * the request objects themselves, so each request is authenticated exactly
2706
+ * once, a distinct request is authenticated afresh, and entries are collected
2707
+ * with their requests — nothing leaks across requests. A rejected or nullish
2708
+ * result is cached as-is, so a failed authentication is never seen as a
2709
+ * success by the second caller.
2710
+ */
2711
+ #memoizedAuthenticate() {
2712
+ const authenticate = this.#authenticate;
2713
+ const cache = /* @__PURE__ */ new WeakMap();
2714
+ return (request) => {
2715
+ if (!request) {
2716
+ return authenticate(request);
2717
+ }
2718
+ const cached = cache.get(request);
2719
+ if (cached) {
2720
+ return cached;
2721
+ }
2722
+ const result = Promise.resolve(authenticate(request));
2723
+ cache.set(request, result);
2724
+ return result;
2725
+ };
2726
+ }
2702
2727
  /**
2703
2728
  * Converts Node.js IncomingMessage to Web Request for Hono
2704
2729
  */
@@ -2867,4 +2892,4 @@ export {
2867
2892
  FastMCPSession,
2868
2893
  FastMCP
2869
2894
  };
2870
- //# sourceMappingURL=chunk-UODI5E6U.js.map
2895
+ //# sourceMappingURL=chunk-XJWV6G5Q.js.map