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.
- package/README.md +5 -0
- package/dist/FastMCP.cjs +2 -2
- package/dist/FastMCP.js +1 -1
- package/dist/{chunk-MI6R72UA.cjs → chunk-2PXF6JQB.cjs} +168 -111
- package/dist/chunk-2PXF6JQB.cjs.map +1 -0
- package/dist/{chunk-OUD755CE.js → chunk-5QKBH7JC.js} +127 -70
- package/dist/chunk-5QKBH7JC.js.map +1 -0
- package/dist/edge/index.cjs +3 -0
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.js +3 -0
- package/dist/edge/index.js.map +1 -1
- package/dist/examples/custom-routes.cjs +2 -2
- package/dist/examples/custom-routes.js +1 -1
- package/dist/openapi/index.cjs +6 -6
- package/dist/openapi/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MI6R72UA.cjs.map +0 -1
- package/dist/chunk-OUD755CE.js.map +0 -1
|
@@ -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
|
|
271
|
-
|
|
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
|
|
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
|
|
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
|
|
333
|
-
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
*
|
|
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
|
-
|
|
987
|
+
const params = {
|
|
945
988
|
data: {
|
|
946
989
|
context,
|
|
947
990
|
message
|
|
948
991
|
},
|
|
949
992
|
level
|
|
950
|
-
}
|
|
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(
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
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
|
-
`
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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:
|
|
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:
|
|
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-
|
|
3216
|
+
//# sourceMappingURL=chunk-5QKBH7JC.js.map
|