fastmcp 4.23.5 → 4.23.6

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.
@@ -225,6 +225,16 @@ function unwrapDefault(value) {
225
225
 
226
226
  // src/FastMCP.ts
227
227
  var MEDIA_FETCH_TIMEOUT_MS = 3e4;
228
+ var isSvg = (buffer) => {
229
+ let text = buffer.toString("utf8").trimStart();
230
+ const preamble = /^(?:<\?[\s\S]*?\?>|<!--[\s\S]*?-->|<!DOCTYPE\s[^[>]*(?:\[[\s\S]*?\]\s*)?>)/;
231
+ let match = preamble.exec(text);
232
+ while (match) {
233
+ text = text.slice(match[0].length).trimStart();
234
+ match = preamble.exec(text);
235
+ }
236
+ return /^<svg(?:\s|\/?>)/.test(text);
237
+ };
228
238
  var imageContent = async (input) => {
229
239
  let rawData;
230
240
  try {
@@ -267,16 +277,17 @@ var imageContent = async (input) => {
267
277
  );
268
278
  }
269
279
  const { fileTypeFromBuffer } = await import("file-type");
270
- const mimeType = await fileTypeFromBuffer(rawData);
271
- if (!mimeType || !mimeType.mime.startsWith("image/")) {
280
+ const fileType = await fileTypeFromBuffer(rawData);
281
+ const mimeType = (!fileType || fileType.mime === "application/xml") && isSvg(rawData) ? "image/svg+xml" : fileType?.mime;
282
+ if (!mimeType || !mimeType.startsWith("image/")) {
272
283
  console.warn(
273
- `Warning: Content may not be a valid image. Detected MIME: ${mimeType?.mime || "unknown"}`
284
+ `Warning: Content may not be a valid image. Detected MIME: ${mimeType || "unknown"}`
274
285
  );
275
286
  }
276
287
  const base64Data = rawData.toString("base64");
277
288
  return {
278
289
  data: base64Data,
279
- mimeType: mimeType?.mime ?? "image/png",
290
+ mimeType: mimeType ?? "image/png",
280
291
  type: "image"
281
292
  };
282
293
  } catch (error) {
@@ -287,8 +298,27 @@ var imageContent = async (input) => {
287
298
  }
288
299
  }
289
300
  };
301
+ var AUDIO_CONTAINER_TYPES = /* @__PURE__ */ new Map([
302
+ ["video/3gpp", "audio/3gpp"],
303
+ ["video/3gpp2", "audio/3gpp2"],
304
+ ["video/matroska", "audio/matroska"],
305
+ ["video/mp4", "audio/mp4"],
306
+ ["video/ogg", "audio/ogg"],
307
+ ["video/webm", "audio/webm"]
308
+ ]);
309
+ var resolveAudioMimeType = (detected, declared) => {
310
+ if (detected?.startsWith("audio/")) {
311
+ return detected;
312
+ }
313
+ const declaredMime = declared?.split(";", 1)[0].trim().toLowerCase();
314
+ if (declaredMime?.startsWith("audio/")) {
315
+ return declaredMime;
316
+ }
317
+ return (detected && AUDIO_CONTAINER_TYPES.get(detected)) ?? detected;
318
+ };
290
319
  var audioContent = async (input) => {
291
320
  let rawData;
321
+ let declaredMimeType = null;
292
322
  try {
293
323
  if ("url" in input) {
294
324
  const timeoutMs = input.timeoutMs ?? MEDIA_FETCH_TIMEOUT_MS;
@@ -302,6 +332,7 @@ var audioContent = async (input) => {
302
332
  `Server responded with status: ${response.status} - ${response.statusText}`
303
333
  );
304
334
  }
335
+ declaredMimeType = response.headers.get("content-type");
305
336
  rawData = Buffer.from(await response.arrayBuffer());
306
337
  } catch (error) {
307
338
  if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) {
@@ -329,16 +360,17 @@ var audioContent = async (input) => {
329
360
  );
330
361
  }
331
362
  const { fileTypeFromBuffer } = await import("file-type");
332
- const mimeType = await fileTypeFromBuffer(rawData);
333
- if (!mimeType || !mimeType.mime.startsWith("audio/")) {
363
+ const detected = await fileTypeFromBuffer(rawData);
364
+ const mimeType = resolveAudioMimeType(detected?.mime, declaredMimeType);
365
+ if (!mimeType || !mimeType.startsWith("audio/")) {
334
366
  console.warn(
335
- `Warning: Content may not be a valid audio file. Detected MIME: ${mimeType?.mime || "unknown"}`
367
+ `Warning: Content may not be a valid audio file. Detected MIME: ${mimeType || "unknown"}`
336
368
  );
337
369
  }
338
370
  const base64Data = rawData.toString("base64");
339
371
  return {
340
372
  data: base64Data,
341
- mimeType: mimeType?.mime ?? "audio/mpeg",
373
+ mimeType: mimeType ?? "audio/mpeg",
342
374
  type: "audio"
343
375
  };
344
376
  } catch (error) {
@@ -545,6 +577,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
545
577
  * to interested clients only.
546
578
  */
547
579
  #subscriptions = /* @__PURE__ */ new Set();
580
+ #tools;
548
581
  #transportType;
549
582
  /**
550
583
  * Capabilities this session has already warned about in `#canRefresh`, so a
@@ -553,6 +586,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
553
586
  */
554
587
  #unnegotiatedWarnings = /* @__PURE__ */ new Set();
555
588
  #utils;
589
+ #visibleTools = [];
556
590
  constructor({
557
591
  auth,
558
592
  authRequired = false,
@@ -587,6 +621,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
587
621
  this.#sessionId = sessionId;
588
622
  this.#stateless = stateless;
589
623
  this.#streamKeepaliveConfig = streamKeepalive;
624
+ this.#tools = tools;
590
625
  this.#transportType = transportType;
591
626
  this.#needsEventLoopFlush = transportType === "httpStream";
592
627
  const supportsTools = hasTools ?? tools.length > 0;
@@ -620,7 +655,9 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
620
655
  this.setupRootsHandlers();
621
656
  this.setupCompleteHandlers();
622
657
  if (supportsTools) {
623
- this.setupToolHandlers(tools);
658
+ this.setupToolHandlers(
659
+ toolsVisibleTo(tools, this.#auth, this.#authRequired)
660
+ );
624
661
  }
625
662
  if (resources.length || resourcesTemplates.length) {
626
663
  for (const resource of resources) {
@@ -741,6 +778,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
741
778
  if (!this.#canRefresh("tools")) {
742
779
  return;
743
780
  }
781
+ this.#tools = tools;
744
782
  this.setupToolHandlers(
745
783
  toolsVisibleTo(tools, this.#auth, this.#authRequired)
746
784
  );
@@ -765,6 +803,11 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
765
803
  */
766
804
  updateAuth(auth) {
767
805
  this.#auth = auth;
806
+ const tools = toolsVisibleTo(this.#tools, auth, this.#authRequired);
807
+ if (tools.length !== this.#visibleTools.length || tools.some((tool, index) => tool !== this.#visibleTools[index])) {
808
+ this.setupToolHandlers(tools);
809
+ this.triggerListChangedNotification("notifications/tools/list_changed");
810
+ }
768
811
  }
769
812
  waitForReady() {
770
813
  if (this.isReady) {
@@ -842,31 +885,31 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
842
885
  * are tied to a tool call's progress token / streaming notification,
843
886
  * which resource and prompt reads do not have.
844
887
  */
845
- #createLoadContext(meta) {
888
+ #createLoadContext(meta, sendNotification) {
846
889
  return {
847
890
  client: {
848
891
  version: this.#server.getClientVersion()
849
892
  },
850
893
  elicit: (params, options) => this.#server.elicitInput(params, options),
851
- log: this.#createLog(),
894
+ log: this.#createLog(sendNotification),
852
895
  requestId: typeof meta?.requestId === "string" ? meta.requestId : void 0,
853
896
  session: this.#auth,
854
897
  sessionId: this.sessionId
855
898
  };
856
899
  }
857
- #createLog() {
900
+ #createLog(sendNotification) {
858
901
  return {
859
902
  debug: (message, context) => {
860
- this.#sendLog("debug", message, context);
903
+ this.#sendLog(sendNotification, "debug", message, context);
861
904
  },
862
905
  error: (message, context) => {
863
- this.#sendLog("error", message, context);
906
+ this.#sendLog(sendNotification, "error", message, context);
864
907
  },
865
908
  info: (message, context) => {
866
- this.#sendLog("info", message, context);
909
+ this.#sendLog(sendNotification, "info", message, context);
867
910
  },
868
911
  warn: (message, context) => {
869
- this.#sendLog("warning", message, context);
912
+ this.#sendLog(sendNotification, "warning", message, context);
870
913
  }
871
914
  };
872
915
  }
@@ -932,22 +975,23 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
932
975
  }
933
976
  }
934
977
  /**
935
- * `sendLoggingMessage` rejects once the client has hung up, and these four
978
+ * Sending rejects once the client has hung up, and these four
936
979
  * callers are synchronous, so the rejection has nowhere to go. Swallow it the
937
980
  * way every other notification sender in this class does, or a client that
938
981
  * disconnects mid-tool takes the server process down with it.
939
982
  */
940
- #sendLog(level, message, context) {
983
+ #sendLog(sendNotification, level, message, context) {
941
984
  if (this.#loggingLevel && LOGGING_LEVEL_SEVERITY[level] < LOGGING_LEVEL_SEVERITY[this.#loggingLevel]) {
942
985
  return;
943
986
  }
944
- this.#server.sendLoggingMessage({
987
+ const params = {
945
988
  data: {
946
989
  context,
947
990
  message
948
991
  },
949
992
  level
950
- }).catch((error) => {
993
+ };
994
+ sendNotification({ method: "notifications/message", params }).catch(() => this.#server.sendLoggingMessage(params)).catch((error) => {
951
995
  this.#logger.error(
952
996
  `[FastMCP error] failed to send ${level} log notification.
953
997
 
@@ -1258,54 +1302,60 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1258
1302
  prompts: cachedPromptsList
1259
1303
  };
1260
1304
  });
1261
- this.#server.setRequestHandler(GetPromptRequestSchema, async (request) => {
1262
- const prompt = this.#prompts.get(request.params.name);
1263
- if (!prompt) {
1264
- throw new McpError(
1265
- ErrorCode.InvalidParams,
1266
- `Unknown prompt: ${request.params.name}`
1267
- );
1268
- }
1269
- const args = request.params.arguments;
1270
- for (const arg of prompt.arguments ?? []) {
1271
- if (arg.required && !(args && Object.hasOwn(args, arg.name))) {
1305
+ this.#server.setRequestHandler(
1306
+ GetPromptRequestSchema,
1307
+ async (request, extra) => {
1308
+ const prompt = this.#prompts.get(request.params.name);
1309
+ if (!prompt) {
1272
1310
  throw new McpError(
1273
1311
  ErrorCode.InvalidParams,
1274
- `Prompt '${request.params.name}' requires argument '${arg.name}': ${arg.description || "No description provided"}`
1312
+ `Unknown prompt: ${request.params.name}`
1275
1313
  );
1276
1314
  }
1315
+ const args = request.params.arguments;
1316
+ for (const arg of prompt.arguments ?? []) {
1317
+ if (arg.required && !(args && Object.hasOwn(args, arg.name))) {
1318
+ throw new McpError(
1319
+ ErrorCode.InvalidParams,
1320
+ `Prompt '${request.params.name}' requires argument '${arg.name}': ${arg.description || "No description provided"}`
1321
+ );
1322
+ }
1323
+ }
1324
+ let result;
1325
+ try {
1326
+ result = await prompt.load(
1327
+ args ?? {},
1328
+ this.#auth,
1329
+ this.#createLoadContext(
1330
+ request.params?._meta,
1331
+ extra.sendNotification
1332
+ )
1333
+ );
1334
+ } catch (error) {
1335
+ const errorMessage = error instanceof Error ? error.message : String(error);
1336
+ throw new McpError(
1337
+ ErrorCode.InternalError,
1338
+ `Failed to load prompt '${request.params.name}': ${errorMessage}`
1339
+ );
1340
+ }
1341
+ if (typeof result === "string") {
1342
+ return {
1343
+ description: prompt.description,
1344
+ messages: [
1345
+ {
1346
+ content: { text: result, type: "text" },
1347
+ role: "user"
1348
+ }
1349
+ ]
1350
+ };
1351
+ } else {
1352
+ return {
1353
+ description: prompt.description,
1354
+ messages: result.messages
1355
+ };
1356
+ }
1277
1357
  }
1278
- let result;
1279
- try {
1280
- result = await prompt.load(
1281
- args ?? {},
1282
- this.#auth,
1283
- this.#createLoadContext(request.params?._meta)
1284
- );
1285
- } catch (error) {
1286
- const errorMessage = error instanceof Error ? error.message : String(error);
1287
- throw new McpError(
1288
- ErrorCode.InternalError,
1289
- `Failed to load prompt '${request.params.name}': ${errorMessage}`
1290
- );
1291
- }
1292
- if (typeof result === "string") {
1293
- return {
1294
- description: prompt.description,
1295
- messages: [
1296
- {
1297
- content: { text: result, type: "text" },
1298
- role: "user"
1299
- }
1300
- ]
1301
- };
1302
- } else {
1303
- return {
1304
- description: prompt.description,
1305
- messages: result.messages
1306
- };
1307
- }
1308
- });
1358
+ );
1309
1359
  }
1310
1360
  setupResourceHandlers() {
1311
1361
  let cachedResourcesList = null;
@@ -1329,7 +1379,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1329
1379
  });
1330
1380
  this.#server.setRequestHandler(
1331
1381
  ReadResourceRequestSchema,
1332
- async (request) => {
1382
+ async (request, extra) => {
1333
1383
  if ("uri" in request.params) {
1334
1384
  const resource = this.#resources.get(request.params.uri);
1335
1385
  if (!resource) {
@@ -1345,7 +1395,10 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1345
1395
  const result = await resourceTemplate.load(
1346
1396
  match,
1347
1397
  this.#auth,
1348
- this.#createLoadContext(request.params?._meta)
1398
+ this.#createLoadContext(
1399
+ request.params?._meta,
1400
+ extra.sendNotification
1401
+ )
1349
1402
  );
1350
1403
  const resources = Array.isArray(result) ? result : [result];
1351
1404
  return {
@@ -1371,7 +1424,10 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1371
1424
  try {
1372
1425
  maybeArrayResult = await resource.load(
1373
1426
  this.#auth,
1374
- this.#createLoadContext(request.params?._meta)
1427
+ this.#createLoadContext(
1428
+ request.params?._meta,
1429
+ extra.sendNotification
1430
+ )
1375
1431
  );
1376
1432
  } catch (error) {
1377
1433
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -1471,6 +1527,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1471
1527
  }
1472
1528
  }
1473
1529
  setupToolHandlers(tools) {
1530
+ this.#visibleTools = tools;
1474
1531
  const toolsMap = new Map(tools.map((tool) => [tool.name, tool]));
1475
1532
  let cachedToolsList = null;
1476
1533
  this.#server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -1553,7 +1610,7 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
1553
1610
  );
1554
1611
  }
1555
1612
  };
1556
- const log = this.#createLog();
1613
+ const log = this.#createLog(extra.sendNotification);
1557
1614
  const streamContent = async (content) => {
1558
1615
  const contentArray = Array.isArray(content) ? content : [content];
1559
1616
  try {
@@ -2217,7 +2274,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2217
2274
  roots: this.#options.roots,
2218
2275
  streamKeepalive: this.#options.streamKeepalive,
2219
2276
  title: this.#options.title,
2220
- tools: toolsVisibleTo(this.#tools, auth, authRequired),
2277
+ tools: this.#tools,
2221
2278
  transportType: "stdio",
2222
2279
  utils: this.#options.utils,
2223
2280
  version: this.#options.version,
@@ -2428,7 +2485,7 @@ var FastMCP = class extends FastMCPEventEmitter {
2428
2485
  stateless,
2429
2486
  streamKeepalive: this.#options.streamKeepalive,
2430
2487
  title: this.#options.title,
2431
- tools: toolsVisibleTo(this.#tools, auth, authRequired),
2488
+ tools: this.#tools,
2432
2489
  transportType: "httpStream",
2433
2490
  utils: this.#options.utils,
2434
2491
  version: this.#options.version,
@@ -3156,4 +3213,4 @@ export {
3156
3213
  FastMCPSession,
3157
3214
  FastMCP
3158
3215
  };
3159
- //# sourceMappingURL=chunk-OUD755CE.js.map
3216
+ //# sourceMappingURL=chunk-5QKBH7JC.js.map