fastmcp 4.23.8 → 4.23.9

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/strictInputSchema.ts"],"sourcesContent":["import type { JsonSchema } from \"xsschema\";\n\nimport { z } from \"zod\";\n\n/**\n * Gives a tool's input or output schema the `type: \"object\"` MCP requires at\n * its root, unless it states a type of its own. A union of objects converts to\n * a bare `anyOf` or `oneOf`, and the SDK client rejects a whole `tools/list`\n * that contains one, so a single such tool would hide every other. Arguments\n * and structured content are always objects, so this only rules out values a\n * tool can never be sent or return.\n */\nexport function objectRootSchema(schema: JsonSchema): JsonSchema {\n return schema.type === undefined ? { ...schema, type: \"object\" } : schema;\n}\n\n/**\n * Closes tool input objects to undeclared keys, like xsschema's\n * `strictJsonSchema`, except for objects made only of additional properties.\n * Closing one of those advertises an object that accepts nothing but `{}`,\n * while the tool's own validation still takes any key, so it keeps whatever\n * `additionalProperties` it declared — or none:\n *\n * - a dictionary, whose `additionalProperties` is a schema (`z.record()`, an\n * OpenAPI map), keeps its value schema;\n * - a free-form object, which declares `additionalProperties: true`, or, as an\n * argument, is a bare `{ type: \"object\" }` (OpenAPI's usual spelling), stays\n * open.\n *\n * A plain object that declares an empty `properties`, or a bare one at the\n * top of the input, is still closed: that is how a schema says \"no keys\" —\n * `z.object({})`, or a tool without arguments. Root schemas with `allOf`,\n * `anyOf`, `oneOf`, or `$ref` are left as declared, even with their own\n * `properties`: `additionalProperties` only sees adjacent properties, so\n * adding it would reject keys declared by a branch or reference target.\n *\n * The root is given an object type (see `objectRootSchema`).\n *\n * Shared by both runtimes, so a tool's advertised input schema does not depend\n * on whether it is served by `FastMCP` or `EdgeFastMCP`.\n */\nexport function strictInputSchema(\n schema: JsonSchema,\n nested = false,\n): JsonSchema {\n const { $ref, additionalProperties, allOf, anyOf, oneOf, properties } =\n schema;\n\n if (!nested && ($ref || allOf || anyOf || oneOf)) {\n return objectRootSchema(schema);\n }\n\n const keepsAdditionalProperties =\n Object.keys(properties ?? {}).length === 0 &&\n (typeof additionalProperties === \"object\" ||\n additionalProperties === true ||\n (nested && additionalProperties === undefined && !properties));\n\n const strict = {\n ...schema,\n ...(keepsAdditionalProperties ? {} : { additionalProperties: false }),\n ...(properties && {\n properties: Object.fromEntries(\n Object.entries(properties).map(([key, value]) => [\n key,\n typeof value === \"object\" && value.type === \"object\"\n ? strictInputSchema(value, true)\n : value,\n ]),\n ),\n }),\n };\n\n return nested ? strict : objectRootSchema(strict);\n}\n\n/**\n * Converts Zod 4 tool parameters by what a call may send (`io: \"input\"`), so a\n * transformed field advertises the type it parses and a defaulted one is\n * optional. Input-side conversion leaves a plain `z.object()` open, since it\n * strips undeclared keys rather than rejecting them; this closes it, as\n * output-side conversion did, at any depth — including inside arrays, unions\n * and record values, which `strictInputSchema` does not reach. Explicit\n * catchalls (`z.looseObject()`, `.catchall()`) keep what they declare.\n */\nexport function zodInputSchema(\n schema: z.ZodType,\n target?: \"draft-7\",\n): JsonSchema {\n return z.toJSONSchema(schema, {\n io: \"input\",\n override: ({ jsonSchema, zodSchema }) => {\n const def = zodSchema._zod.def;\n if (\n def.type === \"object\" &&\n !def.catchall &&\n jsonSchema.additionalProperties === undefined\n ) {\n jsonSchema.additionalProperties = false;\n }\n },\n target,\n }) as JsonSchema;\n}\n"],"mappings":";AAEA,SAAS,SAAS;AAUX,SAAS,iBAAiB,QAAgC;AAC/D,SAAO,OAAO,SAAS,SAAY,EAAE,GAAG,QAAQ,MAAM,SAAS,IAAI;AACrE;AA2BO,SAAS,kBACd,QACA,SAAS,OACG;AACZ,QAAM,EAAE,MAAM,sBAAsB,OAAO,OAAO,OAAO,WAAW,IAClE;AAEF,MAAI,CAAC,WAAW,QAAQ,SAAS,SAAS,QAAQ;AAChD,WAAO,iBAAiB,MAAM;AAAA,EAChC;AAEA,QAAM,4BACJ,OAAO,KAAK,cAAc,CAAC,CAAC,EAAE,WAAW,MACxC,OAAO,yBAAyB,YAC/B,yBAAyB,QACxB,UAAU,yBAAyB,UAAa,CAAC;AAEtD,QAAM,SAAS;AAAA,IACb,GAAG;AAAA,IACH,GAAI,4BAA4B,CAAC,IAAI,EAAE,sBAAsB,MAAM;AAAA,IACnE,GAAI,cAAc;AAAA,MAChB,YAAY,OAAO;AAAA,QACjB,OAAO,QAAQ,UAAU,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAAA,UAC/C;AAAA,UACA,OAAO,UAAU,YAAY,MAAM,SAAS,WACxC,kBAAkB,OAAO,IAAI,IAC7B;AAAA,QACN,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO,SAAS,SAAS,iBAAiB,MAAM;AAClD;AAWO,SAAS,eACd,QACA,QACY;AACZ,SAAO,EAAE,aAAa,QAAQ;AAAA,IAC5B,IAAI;AAAA,IACJ,UAAU,CAAC,EAAE,YAAY,UAAU,MAAM;AACvC,YAAM,MAAM,UAAU,KAAK;AAC3B,UACE,IAAI,SAAS,YACb,CAAC,IAAI,YACL,WAAW,yBAAyB,QACpC;AACA,mBAAW,uBAAuB;AAAA,MACpC;AAAA,IACF;AAAA,IACA;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -1,11 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkAJRXMUZRcjs = require('./chunk-AJRXMUZR.cjs');
3
+ var _chunk4OWHPJELcjs = require('./chunk-4OWHPJEL.cjs');
4
4
 
5
5
 
6
6
 
7
7
 
8
- var _chunkT2F3RYH2cjs = require('./chunk-T2F3RYH2.cjs');
8
+ var _chunk6TDYSPLCcjs = require('./chunk-6TDYSPLC.cjs');
9
9
 
10
10
  // src/FastMCP.ts
11
11
  var _indexjs = require('@modelcontextprotocol/sdk/server/index.js');
@@ -141,7 +141,7 @@ var DiscoveryDocumentCache = class {
141
141
  throw error;
142
142
  }
143
143
  if (!res.ok) {
144
- await _chunkAJRXMUZRcjs.cancelResponseBody.call(void 0, res);
144
+ await _chunk4OWHPJELcjs.cancelResponseBody.call(void 0, res);
145
145
  throw new Error(
146
146
  `Failed to fetch discovery document from ${url}: ${res.status} ${res.statusText}`
147
147
  );
@@ -189,9 +189,16 @@ function jsonSchemaAdapter(schema) {
189
189
  async function compileSchema(schema) {
190
190
  const dialect = _optionalChain([schema, 'access', _2 => _2.$schema, 'optionalAccess', _3 => _3.replace, 'call', _4 => _4(/^http:\/\//, "https://"), 'access', _5 => _5.replace, 'call', _6 => _6(/#$/, "")]);
191
191
  const isDraft2020 = dialect === "https://json-schema.org/draft/2020-12/schema";
192
+ const isDraft2019 = dialect === "https://json-schema.org/draft/2019-09/schema";
192
193
  let ajvModule;
193
194
  try {
194
- ajvModule = isDraft2020 ? await Promise.resolve().then(() => _interopRequireWildcard(require("ajv/dist/2020.js"))) : await Promise.resolve().then(() => _interopRequireWildcard(require("ajv")));
195
+ if (isDraft2020) {
196
+ ajvModule = await Promise.resolve().then(() => _interopRequireWildcard(require("ajv/dist/2020.js")));
197
+ } else if (isDraft2019) {
198
+ ajvModule = await Promise.resolve().then(() => _interopRequireWildcard(require("ajv/dist/2019.js")));
199
+ } else {
200
+ ajvModule = await Promise.resolve().then(() => _interopRequireWildcard(require("ajv")));
201
+ }
195
202
  } catch (e2) {
196
203
  throw new Error(
197
204
  'The "ajv" package is required to validate JSON Schema tool parameters. Install it with: npm install ajv'
@@ -207,7 +214,9 @@ async function compileSchema(schema) {
207
214
  addFormats(ajv);
208
215
  } catch (e3) {
209
216
  }
210
- return ajv.compile(isDraft2020 ? { ...schema, $schema: dialect } : schema);
217
+ return ajv.compile(
218
+ isDraft2020 || isDraft2019 ? { ...schema, $schema: dialect } : schema
219
+ );
211
220
  }
212
221
  function toIssue(error) {
213
222
  const path = error.instancePath.split("/").slice(1).map((segment) => segment.replaceAll("~1", "/").replaceAll("~0", "~")).map((segment) => {
@@ -249,7 +258,7 @@ var imageContent = async (input) => {
249
258
  signal: AbortSignal.timeout(timeoutMs)
250
259
  });
251
260
  if (!response.ok) {
252
- await _chunkAJRXMUZRcjs.cancelResponseBody.call(void 0, response);
261
+ await _chunk4OWHPJELcjs.cancelResponseBody.call(void 0, response);
253
262
  throw new Error(
254
263
  `Server responded with status: ${response.status} - ${response.statusText}`
255
264
  );
@@ -331,7 +340,7 @@ var audioContent = async (input) => {
331
340
  signal: AbortSignal.timeout(timeoutMs)
332
341
  });
333
342
  if (!response.ok) {
334
- await _chunkAJRXMUZRcjs.cancelResponseBody.call(void 0, response);
343
+ await _chunk4OWHPJELcjs.cancelResponseBody.call(void 0, response);
335
344
  throw new Error(
336
345
  `Server responded with status: ${response.status} - ${response.statusText}`
337
346
  );
@@ -490,6 +499,28 @@ var ServerState = /* @__PURE__ */ ((ServerState2) => {
490
499
  })(ServerState || {});
491
500
  var FastMCPSessionEventEmitter = class extends FastMCPSessionEventEmitterBase {
492
501
  };
502
+ var lazyRequestBody = (req) => {
503
+ let iterator;
504
+ return new ReadableStream(
505
+ {
506
+ async cancel(reason) {
507
+ await _optionalChain([iterator, 'optionalAccess', _23 => _23.return, 'optionalCall', _24 => _24(reason)]);
508
+ },
509
+ async pull(controller) {
510
+ iterator ??= req[Symbol.asyncIterator]();
511
+ const { done, value } = await iterator.next();
512
+ if (done) {
513
+ controller.close();
514
+ return;
515
+ }
516
+ controller.enqueue(
517
+ typeof value === "string" ? Buffer.from(value) : new Uint8Array(value)
518
+ );
519
+ }
520
+ },
521
+ { highWaterMark: 0 }
522
+ );
523
+ };
493
524
  var FastMCPSession = class extends FastMCPSessionEventEmitter {
494
525
  get clientCapabilities() {
495
526
  return _nullishCoalesce(this.#clientCapabilities, () => ( null));
@@ -522,7 +553,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
522
553
  */
523
554
  get sessionId() {
524
555
  if (this.#sessionId === void 0) {
525
- const transportSessionId = _optionalChain([this, 'access', _23 => _23.#server, 'access', _24 => _24.transport, 'optionalAccess', _25 => _25.sessionId]);
556
+ const transportSessionId = _optionalChain([this, 'access', _25 => _25.#server, 'access', _26 => _26.transport, 'optionalAccess', _27 => _27.sessionId]);
526
557
  if (typeof transportSessionId === "string") {
527
558
  this.#sessionId = transportSessionId;
528
559
  }
@@ -913,7 +944,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
913
944
  },
914
945
  elicit: this.#createElicit(requestId),
915
946
  log: this.#createLog(sendNotification),
916
- requestId: typeof _optionalChain([meta, 'optionalAccess', _26 => _26.requestId]) === "string" ? meta.requestId : void 0,
947
+ requestId: typeof _optionalChain([meta, 'optionalAccess', _28 => _28.requestId]) === "string" ? meta.requestId : void 0,
917
948
  session: this.#auth,
918
949
  sessionId: this.sessionId
919
950
  };
@@ -935,8 +966,12 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
935
966
  };
936
967
  }
937
968
  #formatSchemaIssues(issues) {
938
- return _optionalChain([this, 'access', _27 => _27.#utils, 'optionalAccess', _28 => _28.formatInvalidParamsErrorMessage]) ? this.#utils.formatInvalidParamsErrorMessage(issues) : issues.map((issue) => {
939
- const path = _optionalChain([issue, 'access', _29 => _29.path, 'optionalAccess', _30 => _30.join, 'call', _31 => _31(".")]) || "root";
969
+ return _optionalChain([this, 'access', _29 => _29.#utils, 'optionalAccess', _30 => _30.formatInvalidParamsErrorMessage]) ? this.#utils.formatInvalidParamsErrorMessage(issues) : issues.map((issue) => {
970
+ const path = _optionalChain([issue, 'access', _31 => _31.path, 'optionalAccess', _32 => _32.map, 'call', _33 => _33(
971
+ (segment) => String(
972
+ typeof segment === "object" && segment !== null ? segment.key : segment
973
+ )
974
+ ), 'access', _34 => _34.join, 'call', _35 => _35(".")]) || "root";
940
975
  return `${path}: ${issue.message}`;
941
976
  }).join(", ");
942
977
  }
@@ -958,7 +993,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
958
993
  * is bounded and retried, and the result is announced with `rootsChanged`.
959
994
  */
960
995
  async #loadRoots(streamableHttp) {
961
- if (_optionalChain([this, 'access', _32 => _32.#rootsConfig, 'optionalAccess', _33 => _33.enabled]) === false || !_optionalChain([this, 'access', _34 => _34.#clientCapabilities, 'optionalAccess', _35 => _35.roots, 'optionalAccess', _36 => _36.listChanged]) || typeof this.#server.listRoots !== "function") {
996
+ if (_optionalChain([this, 'access', _36 => _36.#rootsConfig, 'optionalAccess', _37 => _37.enabled]) === false || !_optionalChain([this, 'access', _38 => _38.#clientCapabilities, 'optionalAccess', _39 => _39.roots, 'optionalAccess', _40 => _40.listChanged]) || typeof this.#server.listRoots !== "function") {
962
997
  return;
963
998
  }
964
999
  const maxAttempts = streamableHttp ? 5 : 1;
@@ -968,7 +1003,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
968
1003
  void 0,
969
1004
  streamableHttp ? { timeout: 1e3 } : void 0
970
1005
  );
971
- this.#roots = _optionalChain([roots, 'optionalAccess', _37 => _37.roots]) || [];
1006
+ this.#roots = _optionalChain([roots, 'optionalAccess', _41 => _41.roots]) || [];
972
1007
  if (streamableHttp) {
973
1008
  this.emit("rootsChanged", { roots: this.#roots });
974
1009
  }
@@ -1106,7 +1141,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1106
1141
  */
1107
1142
  #startStreamKeepalive(extra, toolName) {
1108
1143
  const config = this.#streamKeepaliveConfig;
1109
- if (!_optionalChain([config, 'optionalAccess', _38 => _38.enabled])) {
1144
+ if (!_optionalChain([config, 'optionalAccess', _42 => _42.enabled]) || _optionalChain([extra, 'access', _43 => _43.signal, 'optionalAccess', _44 => _44.aborted])) {
1110
1145
  return () => {
1111
1146
  };
1112
1147
  }
@@ -1126,9 +1161,12 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1126
1161
  );
1127
1162
  });
1128
1163
  }, intervalMs);
1129
- _optionalChain([timer, 'access', _39 => _39.unref, 'optionalCall', _40 => _40()]);
1130
- const stop = () => clearInterval(timer);
1131
- _optionalChain([extra, 'access', _41 => _41.signal, 'optionalAccess', _42 => _42.addEventListener, 'call', _43 => _43("abort", stop, { once: true })]);
1164
+ _optionalChain([timer, 'access', _45 => _45.unref, 'optionalCall', _46 => _46()]);
1165
+ const stop = () => {
1166
+ clearInterval(timer);
1167
+ _optionalChain([extra, 'access', _47 => _47.signal, 'optionalAccess', _48 => _48.removeEventListener, 'call', _49 => _49("abort", stop)]);
1168
+ };
1169
+ _optionalChain([extra, 'access', _50 => _50.signal, 'optionalAccess', _51 => _51.addEventListener, 'call', _52 => _52("abort", stop, { once: true })]);
1132
1170
  return stop;
1133
1171
  }
1134
1172
  async #validateStructuredContent(tool, value, toolName) {
@@ -1348,7 +1386,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1348
1386
  _nullishCoalesce(args, () => ( {})),
1349
1387
  this.#auth,
1350
1388
  this.#createLoadContext(
1351
- _optionalChain([request, 'access', _44 => _44.params, 'optionalAccess', _45 => _45._meta]),
1389
+ _optionalChain([request, 'access', _53 => _53.params, 'optionalAccess', _54 => _54._meta]),
1352
1390
  extra.sendNotification,
1353
1391
  extra.requestId
1354
1392
  )
@@ -1418,7 +1456,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1418
1456
  match,
1419
1457
  this.#auth,
1420
1458
  this.#createLoadContext(
1421
- _optionalChain([request, 'access', _46 => _46.params, 'optionalAccess', _47 => _47._meta]),
1459
+ _optionalChain([request, 'access', _55 => _55.params, 'optionalAccess', _56 => _56._meta]),
1422
1460
  extra.sendNotification,
1423
1461
  extra.requestId
1424
1462
  )
@@ -1448,7 +1486,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1448
1486
  maybeArrayResult = await resource.load(
1449
1487
  this.#auth,
1450
1488
  this.#createLoadContext(
1451
- _optionalChain([request, 'access', _48 => _48.params, 'optionalAccess', _49 => _49._meta]),
1489
+ _optionalChain([request, 'access', _57 => _57.params, 'optionalAccess', _58 => _58._meta]),
1452
1490
  extra.sendNotification,
1453
1491
  extra.requestId
1454
1492
  )
@@ -1514,7 +1552,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1514
1552
  );
1515
1553
  }
1516
1554
  setupRootsHandlers() {
1517
- if (_optionalChain([this, 'access', _50 => _50.#rootsConfig, 'optionalAccess', _51 => _51.enabled]) === false) {
1555
+ if (_optionalChain([this, 'access', _59 => _59.#rootsConfig, 'optionalAccess', _60 => _60.enabled]) === false) {
1518
1556
  this.#logger.debug(
1519
1557
  "[FastMCP debug] roots capability explicitly disabled via config"
1520
1558
  );
@@ -1565,8 +1603,8 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1565
1603
  return {
1566
1604
  annotations: tool.annotations,
1567
1605
  description: tool.description,
1568
- inputSchema: tool.parameters ? _chunkT2F3RYH2cjs.strictInputSchema.call(void 0,
1569
- tool.parameters["~standard"].vendor === "zod" && "_zod" in tool.parameters ? _chunkT2F3RYH2cjs.zodInputSchema.call(void 0, tool.parameters, "draft-7") : await _xsschema.toJsonSchema.call(void 0, tool.parameters)
1606
+ inputSchema: tool.parameters ? _chunk6TDYSPLCcjs.strictInputSchema.call(void 0,
1607
+ tool.parameters["~standard"].vendor === "zod" && "_zod" in tool.parameters ? _chunk6TDYSPLCcjs.zodInputSchema.call(void 0, tool.parameters, "draft-7") : await _xsschema.toJsonSchema.call(void 0, tool.parameters)
1570
1608
  ) : {
1571
1609
  additionalProperties: false,
1572
1610
  properties: {},
@@ -1574,7 +1612,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1574
1612
  },
1575
1613
  name: tool.name,
1576
1614
  ...tool.outputSchema && {
1577
- outputSchema: _chunkT2F3RYH2cjs.objectRootSchema.call(void 0,
1615
+ outputSchema: _chunk6TDYSPLCcjs.objectRootSchema.call(void 0,
1578
1616
  await _xsschema.toJsonSchema.call(void 0, tool.outputSchema)
1579
1617
  )
1580
1618
  },
@@ -1611,15 +1649,35 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1611
1649
  }
1612
1650
  args = parsed.value;
1613
1651
  }
1614
- const progressToken = _optionalChain([request, 'access', _52 => _52.params, 'optionalAccess', _53 => _53._meta, 'optionalAccess', _54 => _54.progressToken]);
1652
+ const progressToken = _optionalChain([request, 'access', _61 => _61.params, 'optionalAccess', _62 => _62._meta, 'optionalAccess', _63 => _63.progressToken]);
1615
1653
  let result;
1654
+ const timeoutAbort = new AbortController();
1655
+ const signal = AbortSignal.any([
1656
+ timeoutAbort.signal,
1657
+ this.#abortController.signal,
1658
+ // Only ever aborted for an explicit `notifications/cancelled`; the
1659
+ // session signal above is what covers a client that simply left.
1660
+ ...extra.signal ? [extra.signal] : []
1661
+ ]);
1662
+ const sendNotification = async (notification) => {
1663
+ if (signal.aborted) {
1664
+ return;
1665
+ }
1666
+ try {
1667
+ await extra.sendNotification(notification);
1668
+ } catch (error) {
1669
+ if (!signal.aborted) {
1670
+ throw error;
1671
+ }
1672
+ }
1673
+ };
1616
1674
  try {
1617
1675
  const reportProgress = async (progress) => {
1618
1676
  if (progressToken === void 0) {
1619
1677
  return;
1620
1678
  }
1621
1679
  try {
1622
- await extra.sendNotification({
1680
+ await sendNotification({
1623
1681
  method: "notifications/progress",
1624
1682
  params: {
1625
1683
  ...progress,
@@ -1636,11 +1694,11 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1636
1694
  );
1637
1695
  }
1638
1696
  };
1639
- const log = this.#createLog(extra.sendNotification);
1697
+ const log = this.#createLog(sendNotification);
1640
1698
  const streamContent = async (content) => {
1641
1699
  const contentArray = Array.isArray(content) ? content : [content];
1642
1700
  try {
1643
- await extra.sendNotification({
1701
+ await sendNotification({
1644
1702
  method: "notifications/tool/streamContent",
1645
1703
  params: {
1646
1704
  content: contentArray,
@@ -1663,14 +1721,6 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1663
1721
  toolName: request.params.name
1664
1722
  });
1665
1723
  }
1666
- const timeoutAbort = new AbortController();
1667
- const signal = AbortSignal.any([
1668
- timeoutAbort.signal,
1669
- this.#abortController.signal,
1670
- // Only ever aborted for an explicit `notifications/cancelled`; the
1671
- // session signal above is what covers a client that simply left.
1672
- ...extra.signal ? [extra.signal] : []
1673
- ]);
1674
1724
  const executeToolPromise = Promise.resolve(
1675
1725
  tool.execute(args, {
1676
1726
  client: {
@@ -1679,7 +1729,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1679
1729
  elicit: this.#createElicit(extra.requestId),
1680
1730
  log,
1681
1731
  reportProgress,
1682
- requestId: typeof _optionalChain([request, 'access', _55 => _55.params, 'optionalAccess', _56 => _56._meta, 'optionalAccess', _57 => _57.requestId]) === "string" ? request.params._meta.requestId : void 0,
1732
+ requestId: typeof _optionalChain([request, 'access', _64 => _64.params, 'optionalAccess', _65 => _65._meta, 'optionalAccess', _66 => _66.requestId]) === "string" ? request.params._meta.requestId : void 0,
1683
1733
  session: this.#auth,
1684
1734
  sessionId: this.sessionId,
1685
1735
  signal,
@@ -1687,25 +1737,37 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1687
1737
  })
1688
1738
  );
1689
1739
  const stopStreamKeepalive = this.#startStreamKeepalive(
1690
- extra,
1740
+ { sendNotification, signal },
1691
1741
  request.params.name
1692
1742
  );
1693
- const maybeStringResult = await (tool.timeoutMs ? Promise.race([
1694
- executeToolPromise,
1695
- new Promise((_, reject) => {
1696
- const timeoutId = setTimeout(() => {
1697
- const timedOut = new UserError(
1743
+ let timeoutId;
1744
+ let stopWaitingForAbort = () => {
1745
+ };
1746
+ const aborted = new Promise((_, reject) => {
1747
+ const onAbort = () => reject(signal.reason);
1748
+ stopWaitingForAbort = () => signal.removeEventListener("abort", onAbort);
1749
+ signal.addEventListener("abort", onAbort, { once: true });
1750
+ if (signal.aborted) {
1751
+ onAbort();
1752
+ }
1753
+ });
1754
+ if (tool.timeoutMs && !signal.aborted) {
1755
+ timeoutId = setTimeout(() => {
1756
+ timeoutAbort.abort(
1757
+ new UserError(
1698
1758
  `Tool '${request.params.name}' timed out after ${tool.timeoutMs}ms. Consider increasing timeoutMs or optimizing the tool implementation.`
1699
- );
1700
- timeoutAbort.abort(timedOut);
1701
- reject(timedOut);
1702
- }, tool.timeoutMs);
1703
- executeToolPromise.then(
1704
- () => clearTimeout(timeoutId),
1705
- () => clearTimeout(timeoutId)
1759
+ )
1706
1760
  );
1707
- })
1708
- ]) : executeToolPromise).finally(stopStreamKeepalive);
1761
+ }, tool.timeoutMs);
1762
+ }
1763
+ const maybeStringResult = await Promise.race([
1764
+ executeToolPromise,
1765
+ aborted
1766
+ ]).finally(() => {
1767
+ clearTimeout(timeoutId);
1768
+ stopWaitingForAbort();
1769
+ stopStreamKeepalive();
1770
+ });
1709
1771
  await _promises3.setTimeout.call(void 0, 1);
1710
1772
  if (maybeStringResult === void 0 || maybeStringResult === null) {
1711
1773
  result = ContentResultZodSchema.parse({
@@ -1746,6 +1808,11 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1746
1808
  } else {
1747
1809
  result = ContentResultZodSchema.parse(maybeStringResult);
1748
1810
  }
1811
+ if (tool.outputSchema && result.structuredContent === void 0 && !result.isError) {
1812
+ throw new UserError(
1813
+ `Tool '${request.params.name}' returned no structured content. Please return a value that matches the tool's outputSchema.`
1814
+ );
1815
+ }
1749
1816
  } catch (error) {
1750
1817
  if (error instanceof UserError) {
1751
1818
  let structuredContent = error.extras;
@@ -1833,7 +1900,7 @@ function normalizePath(path) {
1833
1900
  return path.startsWith("/") ? path : `/${path}`;
1834
1901
  }
1835
1902
  function parseBasicAuthHeader(authHeader) {
1836
- const basicMatch = _optionalChain([authHeader, 'optionalAccess', _58 => _58.match, 'call', _59 => _59(/^Basic\s+(.+)$/)]);
1903
+ const basicMatch = _optionalChain([authHeader, 'optionalAccess', _67 => _67.match, 'call', _68 => _68(/^Basic\s+(.+)$/)]);
1837
1904
  if (!basicMatch) return null;
1838
1905
  try {
1839
1906
  const credentials = Buffer.from(basicMatch[1], "base64").toString("utf-8");
@@ -2017,13 +2084,12 @@ var FastMCP = class extends FastMCPEventEmitter {
2017
2084
  * Adds resource templates to the server.
2018
2085
  */
2019
2086
  addResourceTemplates(resources) {
2020
- const newResourceTemplateNames = new Set(
2021
- resources.map((resource) => resource.name)
2022
- );
2023
- this.#resourcesTemplates = this.#resourcesTemplates.filter(
2024
- (t) => !newResourceTemplateNames.has(t.name)
2025
- );
2026
- this.#resourcesTemplates.push(...resources);
2087
+ for (const resource of resources) {
2088
+ this.#resourcesTemplates = this.#resourcesTemplates.filter(
2089
+ (template) => template.name !== resource.name
2090
+ );
2091
+ this.#resourcesTemplates.push(resource);
2092
+ }
2027
2093
  if (this.#serverState === "running" /* Running */) {
2028
2094
  this.#resourceTemplatesListChanged(this.#resourcesTemplates);
2029
2095
  }
@@ -2280,6 +2346,13 @@ var FastMCP = class extends FastMCPEventEmitter {
2280
2346
  async start(options) {
2281
2347
  const config = this.#parseRuntimeConfig(options);
2282
2348
  if (config.transportType === "stdio") {
2349
+ const logger = _nullishCoalesce(this.#options.logger, () => ( {
2350
+ debug: (...args) => console.error(...args),
2351
+ error: (...args) => console.error(...args),
2352
+ info: (...args) => console.error(...args),
2353
+ log: (...args) => console.error(...args),
2354
+ warn: (...args) => console.warn(...args)
2355
+ }));
2283
2356
  const transport = new (0, _stdiojs.StdioServerTransport)();
2284
2357
  let auth;
2285
2358
  const authRequired = this.#authenticate !== void 0;
@@ -2306,7 +2379,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2306
2379
  hasTools: this.#tools.length > 0,
2307
2380
  icons: this.#options.icons,
2308
2381
  instructions: this.#options.instructions,
2309
- logger: this.#logger,
2382
+ logger,
2310
2383
  name: this.#options.name,
2311
2384
  onToolCall: this.#options.onToolCall,
2312
2385
  ping: this.#options.ping,
@@ -2497,9 +2570,9 @@ var FastMCP = class extends FastMCPEventEmitter {
2497
2570
  const originalOnClose = transport.onclose;
2498
2571
  transport.onclose = () => {
2499
2572
  closed = true;
2500
- _optionalChain([onClose, 'optionalCall', _60 => _60()]);
2573
+ _optionalChain([onClose, 'optionalCall', _69 => _69()]);
2501
2574
  this.#removeSession(session);
2502
- _optionalChain([originalOnClose, 'optionalCall', _61 => _61()]);
2575
+ _optionalChain([originalOnClose, 'optionalCall', _70 => _70()]);
2503
2576
  };
2504
2577
  await session.connect(transport);
2505
2578
  if (closed) {
@@ -2564,7 +2637,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2564
2637
  */
2565
2638
  #createUnauthorizedResponse(message) {
2566
2639
  const oauth = this.#options.oauth;
2567
- const resource = _optionalChain([oauth, 'optionalAccess', _62 => _62.enabled]) ? _optionalChain([oauth, 'access', _63 => _63.protectedResource, 'optionalAccess', _64 => _64.resource]) : void 0;
2640
+ const resource = _optionalChain([oauth, 'optionalAccess', _71 => _71.enabled]) ? _optionalChain([oauth, 'access', _72 => _72.protectedResource, 'optionalAccess', _73 => _73.resource]) : void 0;
2568
2641
  const wwwAuthenticateParts = [
2569
2642
  'error="invalid_token"',
2570
2643
  `error_description="${message.replace(/"/g, '\\"')}"`
@@ -2703,7 +2776,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2703
2776
  }
2704
2777
  }
2705
2778
  const oauthConfig = this.#options.oauth;
2706
- if (_optionalChain([oauthConfig, 'optionalAccess', _65 => _65.enabled]) && req.method === "GET") {
2779
+ if (_optionalChain([oauthConfig, 'optionalAccess', _74 => _74.enabled]) && req.method === "GET") {
2707
2780
  const url2 = new URL(req.url || "", `http://${host}`);
2708
2781
  const authorizationServerMetadataPath = joinPaths(
2709
2782
  "",
@@ -2737,8 +2810,8 @@ var FastMCP = class extends FastMCPEventEmitter {
2737
2810
  }
2738
2811
  }
2739
2812
  }
2740
- const oauthProxy = _optionalChain([oauthConfig, 'optionalAccess', _66 => _66.proxy]);
2741
- if (oauthProxy && _optionalChain([oauthConfig, 'optionalAccess', _67 => _67.enabled])) {
2813
+ const oauthProxy = _optionalChain([oauthConfig, 'optionalAccess', _75 => _75.proxy]);
2814
+ if (oauthProxy && _optionalChain([oauthConfig, 'optionalAccess', _76 => _76.enabled])) {
2742
2815
  const url2 = new URL(req.url || "", `http://${host}`);
2743
2816
  const oauthPath = basePathRelativePath;
2744
2817
  try {
@@ -2791,7 +2864,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2791
2864
  const statusCode = error.statusCode || 400;
2792
2865
  res.writeHead(statusCode, OAUTH_CREDENTIAL_RESPONSE_HEADERS).end(
2793
2866
  JSON.stringify(
2794
- _optionalChain([error, 'access', _68 => _68.toJSON, 'optionalCall', _69 => _69()]) || {
2867
+ _optionalChain([error, 'access', _77 => _77.toJSON, 'optionalCall', _78 => _78()]) || {
2795
2868
  error: "invalid_request"
2796
2869
  }
2797
2870
  )
@@ -2818,7 +2891,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2818
2891
  } catch (error) {
2819
2892
  res.writeHead(400, { "Content-Type": "application/json" }).end(
2820
2893
  JSON.stringify(
2821
- _optionalChain([error, 'access', _70 => _70.toJSON, 'optionalCall', _71 => _71()]) || {
2894
+ _optionalChain([error, 'access', _79 => _79.toJSON, 'optionalCall', _80 => _80()]) || {
2822
2895
  error: "invalid_request"
2823
2896
  }
2824
2897
  )
@@ -2840,7 +2913,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2840
2913
  } catch (error) {
2841
2914
  res.writeHead(400, { "Content-Type": "application/json" }).end(
2842
2915
  JSON.stringify(
2843
- _optionalChain([error, 'access', _72 => _72.toJSON, 'optionalCall', _73 => _73()]) || {
2916
+ _optionalChain([error, 'access', _81 => _81.toJSON, 'optionalCall', _82 => _82()]) || {
2844
2917
  error: "server_error"
2845
2918
  }
2846
2919
  )
@@ -2909,7 +2982,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2909
2982
  } catch (error) {
2910
2983
  res.writeHead(400, { "Content-Type": "application/json" }).end(
2911
2984
  JSON.stringify(
2912
- _optionalChain([error, 'access', _74 => _74.toJSON, 'optionalCall', _75 => _75()]) || {
2985
+ _optionalChain([error, 'access', _83 => _83.toJSON, 'optionalCall', _84 => _84()]) || {
2913
2986
  error: "server_error"
2914
2987
  }
2915
2988
  )
@@ -2967,8 +3040,8 @@ var FastMCP = class extends FastMCPEventEmitter {
2967
3040
  const basicAuth = parseBasicAuthHeader(
2968
3041
  req.headers.authorization
2969
3042
  );
2970
- const clientId = _optionalChain([basicAuth, 'optionalAccess', _76 => _76.clientId]) || params.get("client_id") || "";
2971
- const clientSecret = _nullishCoalesce(_nullishCoalesce(_optionalChain([basicAuth, 'optionalAccess', _77 => _77.clientSecret]), () => ( params.get("client_secret"))), () => ( void 0));
3043
+ const clientId = _optionalChain([basicAuth, 'optionalAccess', _85 => _85.clientId]) || params.get("client_id") || "";
3044
+ const clientSecret = _nullishCoalesce(_nullishCoalesce(_optionalChain([basicAuth, 'optionalAccess', _86 => _86.clientSecret]), () => ( params.get("client_secret"))), () => ( void 0));
2972
3045
  let response;
2973
3046
  if (grantType === "authorization_code") {
2974
3047
  response = await oauthProxy.exchangeAuthorizationCode({
@@ -2998,7 +3071,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2998
3071
  const statusCode = error.statusCode || 400;
2999
3072
  res.writeHead(statusCode, OAUTH_CREDENTIAL_RESPONSE_HEADERS).end(
3000
3073
  JSON.stringify(
3001
- _optionalChain([error, 'access', _78 => _78.toJSON, 'optionalCall', _79 => _79()]) || {
3074
+ _optionalChain([error, 'access', _87 => _87.toJSON, 'optionalCall', _88 => _88()]) || {
3002
3075
  error: "invalid_request"
3003
3076
  }
3004
3077
  )
@@ -3024,7 +3097,7 @@ var FastMCP = class extends FastMCPEventEmitter {
3024
3097
  * regardless (RFC 7235), so no config is needed just to get the header.
3025
3098
  */
3026
3099
  #httpStreamOAuthConfig() {
3027
- const resource = _optionalChain([this, 'access', _80 => _80.#options, 'access', _81 => _81.oauth, 'optionalAccess', _82 => _82.enabled]) ? _optionalChain([this, 'access', _83 => _83.#options, 'access', _84 => _84.oauth, 'access', _85 => _85.protectedResource, 'optionalAccess', _86 => _86.resource]) : void 0;
3100
+ const resource = _optionalChain([this, 'access', _89 => _89.#options, 'access', _90 => _90.oauth, 'optionalAccess', _91 => _91.enabled]) ? _optionalChain([this, 'access', _92 => _92.#options, 'access', _93 => _93.oauth, 'access', _94 => _94.protectedResource, 'optionalAccess', _95 => _95.resource]) : void 0;
3028
3101
  if (!resource) {
3029
3102
  return {};
3030
3103
  }
@@ -3090,9 +3163,7 @@ var FastMCP = class extends FastMCPEventEmitter {
3090
3163
  const hasBody = method !== "GET" && method !== "HEAD";
3091
3164
  if (hasBody) {
3092
3165
  return new Request(url.toString(), {
3093
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
3094
- body: req,
3095
- // Node.js IncomingMessage is readable stream
3166
+ body: lazyRequestBody(req),
3096
3167
  duplex: "half",
3097
3168
  // Required for streaming bodies
3098
3169
  headers,
@@ -3132,24 +3203,24 @@ var FastMCP = class extends FastMCPEventEmitter {
3132
3203
  const envBasePath = process.env.FASTMCP_BASE_PATH;
3133
3204
  const envStateless = process.env.FASTMCP_STATELESS;
3134
3205
  const envHost = process.env.FASTMCP_HOST;
3135
- const transportType = _optionalChain([overrides, 'optionalAccess', _87 => _87.transportType]) || parseTransportSetting(transportArg, "--transport") || parseTransportSetting(envTransport, "FASTMCP_TRANSPORT") || "stdio";
3206
+ const transportType = _optionalChain([overrides, 'optionalAccess', _96 => _96.transportType]) || parseTransportSetting(transportArg, "--transport") || parseTransportSetting(envTransport, "FASTMCP_TRANSPORT") || "stdio";
3136
3207
  if (transportType === "httpStream") {
3137
3208
  const port = parseInt(
3138
- _optionalChain([overrides, 'optionalAccess', _88 => _88.httpStream, 'optionalAccess', _89 => _89.port, 'optionalAccess', _90 => _90.toString, 'call', _91 => _91()]) || portArg || envPort || "8080"
3209
+ _optionalChain([overrides, 'optionalAccess', _97 => _97.httpStream, 'optionalAccess', _98 => _98.port, 'optionalAccess', _99 => _99.toString, 'call', _100 => _100()]) || portArg || envPort || "8080"
3139
3210
  );
3140
- const host = _optionalChain([overrides, 'optionalAccess', _92 => _92.httpStream, 'optionalAccess', _93 => _93.host]) || hostArg || envHost || "localhost";
3141
- const endpoint = _optionalChain([overrides, 'optionalAccess', _94 => _94.httpStream, 'optionalAccess', _95 => _95.endpoint]) || endpointArg || envEndpoint || "/mcp";
3211
+ const host = _optionalChain([overrides, 'optionalAccess', _101 => _101.httpStream, 'optionalAccess', _102 => _102.host]) || hostArg || envHost || "localhost";
3212
+ const endpoint = _optionalChain([overrides, 'optionalAccess', _103 => _103.httpStream, 'optionalAccess', _104 => _104.endpoint]) || endpointArg || envEndpoint || "/mcp";
3142
3213
  const basePath = normalizeBasePath(
3143
- _optionalChain([overrides, 'optionalAccess', _96 => _96.httpStream, 'optionalAccess', _97 => _97.basePath]) || basePathArg || envBasePath
3214
+ _optionalChain([overrides, 'optionalAccess', _105 => _105.httpStream, 'optionalAccess', _106 => _106.basePath]) || basePathArg || envBasePath
3144
3215
  );
3145
- const enableJsonResponse = _optionalChain([overrides, 'optionalAccess', _98 => _98.httpStream, 'optionalAccess', _99 => _99.enableJsonResponse]) || false;
3146
- const stateless = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _100 => _100.httpStream, 'optionalAccess', _101 => _101.stateless]), () => ( // A bare `--stateless` means `--stateless true`.
3216
+ const enableJsonResponse = _optionalChain([overrides, 'optionalAccess', _107 => _107.httpStream, 'optionalAccess', _108 => _108.enableJsonResponse]) || false;
3217
+ const stateless = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([overrides, 'optionalAccess', _109 => _109.httpStream, 'optionalAccess', _110 => _110.stateless]), () => ( // A bare `--stateless` means `--stateless true`.
3147
3218
  (statelessArg === "" ? true : parseBooleanSetting(statelessArg, "--stateless")))), () => ( parseBooleanSetting(envStateless, "FASTMCP_STATELESS"))), () => ( false));
3148
- const cors = _optionalChain([overrides, 'optionalAccess', _102 => _102.httpStream, 'optionalAccess', _103 => _103.cors]);
3149
- const eventStore = _optionalChain([overrides, 'optionalAccess', _104 => _104.httpStream, 'optionalAccess', _105 => _105.eventStore]);
3150
- const sslCa = _optionalChain([overrides, 'optionalAccess', _106 => _106.httpStream, 'optionalAccess', _107 => _107.sslCa]);
3151
- const sslCert = _optionalChain([overrides, 'optionalAccess', _108 => _108.httpStream, 'optionalAccess', _109 => _109.sslCert]);
3152
- const sslKey = _optionalChain([overrides, 'optionalAccess', _110 => _110.httpStream, 'optionalAccess', _111 => _111.sslKey]);
3219
+ const cors = _optionalChain([overrides, 'optionalAccess', _111 => _111.httpStream, 'optionalAccess', _112 => _112.cors]);
3220
+ const eventStore = _optionalChain([overrides, 'optionalAccess', _113 => _113.httpStream, 'optionalAccess', _114 => _114.eventStore]);
3221
+ const sslCa = _optionalChain([overrides, 'optionalAccess', _115 => _115.httpStream, 'optionalAccess', _116 => _116.sslCa]);
3222
+ const sslCert = _optionalChain([overrides, 'optionalAccess', _117 => _117.httpStream, 'optionalAccess', _118 => _118.sslCert]);
3223
+ const sslKey = _optionalChain([overrides, 'optionalAccess', _119 => _119.httpStream, 'optionalAccess', _120 => _120.sslKey]);
3153
3224
  return {
3154
3225
  httpStream: {
3155
3226
  basePath,
@@ -3241,7 +3312,7 @@ var FastMCP = class extends FastMCPEventEmitter {
3241
3312
  */
3242
3313
  #useEndpointAsResource(endpoint) {
3243
3314
  const oauth = this.#options.oauth;
3244
- if (this.#providerResourceBase === void 0 || !_optionalChain([oauth, 'optionalAccess', _112 => _112.protectedResource]))
3315
+ if (this.#providerResourceBase === void 0 || !_optionalChain([oauth, 'optionalAccess', _121 => _121.protectedResource]))
3245
3316
  return;
3246
3317
  this.#options = {
3247
3318
  ...this.#options,
@@ -3270,4 +3341,4 @@ var FastMCP = class extends FastMCPEventEmitter {
3270
3341
 
3271
3342
 
3272
3343
  exports.DiscoveryDocumentCache = DiscoveryDocumentCache; exports.jsonSchemaAdapter = jsonSchemaAdapter; exports.MEDIA_FETCH_TIMEOUT_MS = MEDIA_FETCH_TIMEOUT_MS; exports.imageContent = imageContent; exports.audioContent = audioContent; exports.FastMCPError = FastMCPError; exports.SessionError = SessionError; exports.UnexpectedStateError = UnexpectedStateError; exports.UserError = UserError; exports.ServerState = ServerState; exports.FastMCPSession = FastMCPSession; exports.FastMCP = FastMCP;
3273
- //# sourceMappingURL=chunk-XEP3OP7Q.cjs.map
3344
+ //# sourceMappingURL=chunk-PV5VLW7R.cjs.map