fastmcp 4.16.2 → 4.16.4
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/dist/edge/index.cjs +120 -42
- package/dist/edge/index.cjs.map +1 -1
- package/dist/edge/index.d.cts +26 -3
- package/dist/edge/index.d.ts +26 -3
- package/dist/edge/index.js +108 -30
- package/dist/edge/index.js.map +1 -1
- package/package.json +1 -1
package/dist/edge/index.cjs
CHANGED
|
@@ -17,6 +17,7 @@ var _zodtojsonschema = require('zod-to-json-schema');
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
|
|
20
21
|
var MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
|
|
21
22
|
var WebStreamableHTTPServerTransport = (_class = class {
|
|
22
23
|
|
|
@@ -29,12 +30,27 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
29
30
|
__init3() {this._jsonResponseCollectors = /* @__PURE__ */ new Map()}
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Replays whose stream id is still being resolved. `replayEventsAfter` only
|
|
35
|
+
* reveals which stream it was replaying once it settles, so a standalone
|
|
36
|
+
* send during that window has nothing to test against; persist rather than
|
|
37
|
+
* drop, since over-storing for the length of a replay beats losing an event.
|
|
38
|
+
*/
|
|
39
|
+
__init4() {this._replaysInFlight = 0}
|
|
40
|
+
__init5() {this._requestToStreamMapping = /* @__PURE__ */ new Map()}
|
|
41
|
+
__init6() {this._standaloneSseStreamId = "_GET_stream"}
|
|
42
|
+
/**
|
|
43
|
+
* Whether a client has held the standalone GET stream during this session.
|
|
44
|
+
* Latched rather than read from `_streamMapping`, so persistence survives
|
|
45
|
+
* the disconnect window the store exists to cover while still telling a
|
|
46
|
+
* dropped stream apart from one that was never opened. Reset with the rest
|
|
47
|
+
* of the per-session state, since the next session starts with no stream.
|
|
48
|
+
*/
|
|
49
|
+
__init7() {this._standaloneStreamOpened = false}
|
|
50
|
+
__init8() {this._started = false}
|
|
51
|
+
__init9() {this._streamMapping = /* @__PURE__ */ new Map()}
|
|
36
52
|
|
|
37
|
-
constructor(options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);
|
|
53
|
+
constructor(options) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);
|
|
38
54
|
this.sessionIdGenerator = options.sessionIdGenerator;
|
|
39
55
|
this._enableJsonResponse = _nullishCoalesce(options.enableJsonResponse, () => ( false));
|
|
40
56
|
this._eventStore = options.eventStore;
|
|
@@ -52,6 +68,7 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
52
68
|
}
|
|
53
69
|
}
|
|
54
70
|
this._streamMapping.clear();
|
|
71
|
+
this._standaloneStreamOpened = false;
|
|
55
72
|
this.releasePendingRequests();
|
|
56
73
|
this._started = false;
|
|
57
74
|
_optionalChain([this, 'access', _ => _.onclose, 'optionalCall', _2 => _2()]);
|
|
@@ -91,11 +108,12 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
91
108
|
return;
|
|
92
109
|
}
|
|
93
110
|
const writer = this._streamMapping.get(streamId);
|
|
94
|
-
|
|
111
|
+
const store = this.shouldPersist(streamId, writer) ? this._eventStore : void 0;
|
|
112
|
+
if (writer || store) {
|
|
95
113
|
try {
|
|
96
114
|
let eventId;
|
|
97
|
-
if (
|
|
98
|
-
eventId = await
|
|
115
|
+
if (store) {
|
|
116
|
+
eventId = await store.storeEvent(streamId, message);
|
|
99
117
|
}
|
|
100
118
|
if (writer) {
|
|
101
119
|
if (eventId === void 0) {
|
|
@@ -104,23 +122,29 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
104
122
|
await this.writeSSEEventWithId(writer, eventId, message);
|
|
105
123
|
}
|
|
106
124
|
}
|
|
107
|
-
if (requestId !== void 0 && isResponse) {
|
|
108
|
-
this._requestToStreamMapping.delete(requestId);
|
|
109
|
-
const streamInUse = Array.from(
|
|
110
|
-
this._requestToStreamMapping.values()
|
|
111
|
-
).includes(streamId);
|
|
112
|
-
if (!streamInUse) {
|
|
113
|
-
this._streamMapping.delete(streamId);
|
|
114
|
-
if (writer) {
|
|
115
|
-
await writer.close();
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
125
|
} catch (error) {
|
|
120
126
|
_optionalChain([this, 'access', _4 => _4.onerror, 'optionalCall', _5 => _5(
|
|
121
127
|
error instanceof Error ? error : new Error(String(error))
|
|
122
128
|
)]);
|
|
123
129
|
}
|
|
130
|
+
if (requestId !== void 0 && isResponse) {
|
|
131
|
+
this._requestToStreamMapping.delete(requestId);
|
|
132
|
+
const streamInUse = Array.from(
|
|
133
|
+
this._requestToStreamMapping.values()
|
|
134
|
+
).includes(streamId);
|
|
135
|
+
if (!streamInUse) {
|
|
136
|
+
this._streamMapping.delete(streamId);
|
|
137
|
+
if (writer) {
|
|
138
|
+
try {
|
|
139
|
+
await writer.close();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
_optionalChain([this, 'access', _6 => _6.onerror, 'optionalCall', _7 => _7(
|
|
142
|
+
error instanceof Error ? error : new Error(String(error))
|
|
143
|
+
)]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
124
148
|
}
|
|
125
149
|
}
|
|
126
150
|
}
|
|
@@ -183,8 +207,9 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
183
207
|
}
|
|
184
208
|
}
|
|
185
209
|
this._streamMapping.clear();
|
|
210
|
+
this._standaloneStreamOpened = false;
|
|
186
211
|
this.releasePendingRequests();
|
|
187
|
-
await _optionalChain([this, 'access',
|
|
212
|
+
await _optionalChain([this, 'access', _8 => _8._onsessionclosed, 'optionalCall', _9 => _9(_nullishCoalesce(this.sessionId, () => ( "")))]);
|
|
188
213
|
this.sessionId = void 0;
|
|
189
214
|
return new Response(null, {
|
|
190
215
|
headers: this.getResponseHeaders(),
|
|
@@ -196,7 +221,7 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
196
221
|
*/
|
|
197
222
|
async handleGetRequest(request) {
|
|
198
223
|
const acceptHeader = request.headers.get("accept");
|
|
199
|
-
if (!_optionalChain([acceptHeader, 'optionalAccess',
|
|
224
|
+
if (!_optionalChain([acceptHeader, 'optionalAccess', _10 => _10.includes, 'call', _11 => _11("text/event-stream")])) {
|
|
200
225
|
return this.createErrorResponse(
|
|
201
226
|
406,
|
|
202
227
|
-32e3,
|
|
@@ -245,7 +270,7 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
245
270
|
*/
|
|
246
271
|
async handlePostRequest(request, parsedBody) {
|
|
247
272
|
const acceptHeader = request.headers.get("accept");
|
|
248
|
-
if (!_optionalChain([acceptHeader, 'optionalAccess',
|
|
273
|
+
if (!_optionalChain([acceptHeader, 'optionalAccess', _12 => _12.includes, 'call', _13 => _13("application/json")]) && !_optionalChain([acceptHeader, 'optionalAccess', _14 => _14.includes, 'call', _15 => _15("text/event-stream")])) {
|
|
249
274
|
return this.createErrorResponse(
|
|
250
275
|
406,
|
|
251
276
|
-32e3,
|
|
@@ -253,7 +278,7 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
253
278
|
);
|
|
254
279
|
}
|
|
255
280
|
const contentType = request.headers.get("content-type");
|
|
256
|
-
if (!_optionalChain([contentType, 'optionalAccess',
|
|
281
|
+
if (!_optionalChain([contentType, 'optionalAccess', _16 => _16.includes, 'call', _17 => _17("application/json")])) {
|
|
257
282
|
return this.createErrorResponse(
|
|
258
283
|
415,
|
|
259
284
|
-32e3,
|
|
@@ -323,14 +348,14 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
323
348
|
}
|
|
324
349
|
if (hasInitRequest && this.sessionIdGenerator) {
|
|
325
350
|
this.sessionId = this.sessionIdGenerator();
|
|
326
|
-
await _optionalChain([this, 'access',
|
|
351
|
+
await _optionalChain([this, 'access', _18 => _18._onsessioninitialized, 'optionalCall', _19 => _19(this.sessionId)]);
|
|
327
352
|
} else if (requestSessionId) {
|
|
328
353
|
if (this.sessionIdGenerator && this.sessionId !== requestSessionId) {
|
|
329
354
|
return this.createErrorResponse(404, -32001, "Session not found");
|
|
330
355
|
}
|
|
331
356
|
}
|
|
332
357
|
const requestIds = messages.filter((msg) => _typesjs.isJSONRPCRequest.call(void 0, msg)).map((msg) => msg.id);
|
|
333
|
-
const useJsonResponse = this._enableJsonResponse && Boolean(_optionalChain([acceptHeader, 'optionalAccess',
|
|
358
|
+
const useJsonResponse = this._enableJsonResponse && Boolean(_optionalChain([acceptHeader, 'optionalAccess', _20 => _20.includes, 'call', _21 => _21("application/json")]));
|
|
334
359
|
let jsonResponses;
|
|
335
360
|
let sseReadable;
|
|
336
361
|
if (requestIds.length > 0) {
|
|
@@ -359,7 +384,7 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
359
384
|
}
|
|
360
385
|
}
|
|
361
386
|
for (const message of messages) {
|
|
362
|
-
_optionalChain([this, 'access',
|
|
387
|
+
_optionalChain([this, 'access', _22 => _22.onmessage, 'optionalCall', _23 => _23(message, { authInfo: void 0 })]);
|
|
363
388
|
}
|
|
364
389
|
if (requestIds.length === 0) {
|
|
365
390
|
return new Response(null, {
|
|
@@ -369,6 +394,12 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
369
394
|
}
|
|
370
395
|
if (jsonResponses) {
|
|
371
396
|
const responses = await jsonResponses;
|
|
397
|
+
if (responses.length === 0) {
|
|
398
|
+
return new Response(null, {
|
|
399
|
+
headers: this.getResponseHeaders(),
|
|
400
|
+
status: 202
|
|
401
|
+
});
|
|
402
|
+
}
|
|
372
403
|
const responseBody = JSON.stringify(isBatch ? responses : responses[0]);
|
|
373
404
|
return new Response(responseBody, {
|
|
374
405
|
headers: {
|
|
@@ -401,6 +432,7 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
401
432
|
}
|
|
402
433
|
const { readable, writable } = new TransformStream();
|
|
403
434
|
const writer = writable.getWriter();
|
|
435
|
+
this._replaysInFlight += 1;
|
|
404
436
|
try {
|
|
405
437
|
const streamId = await this._eventStore.replayEventsAfter(lastEventId, {
|
|
406
438
|
send: async (eventId, message) => {
|
|
@@ -411,6 +443,8 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
411
443
|
} catch (error) {
|
|
412
444
|
await writer.close();
|
|
413
445
|
return this.createErrorResponse(500, -32e3, `Replay failed: ${error}`);
|
|
446
|
+
} finally {
|
|
447
|
+
this._replaysInFlight -= 1;
|
|
414
448
|
}
|
|
415
449
|
return new Response(readable, {
|
|
416
450
|
headers: {
|
|
@@ -433,13 +467,19 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
433
467
|
* therefore never produce a response: the same drain `send()` performs once
|
|
434
468
|
* a response has been written, minus the response. Idempotent, so it is a
|
|
435
469
|
* no-op if the response won the race against the cancellation.
|
|
436
|
-
*
|
|
437
|
-
* JSON-mode POSTs are left alone: `_jsonResponseCollectors` owns their
|
|
438
|
-
* settlement.
|
|
439
470
|
*/
|
|
440
471
|
async releaseCancelledRequest(requestId) {
|
|
441
472
|
const streamId = this._requestToStreamMapping.get(requestId);
|
|
442
|
-
if (streamId === void 0
|
|
473
|
+
if (streamId === void 0) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
const collector = this._jsonResponseCollectors.get(streamId);
|
|
477
|
+
if (collector) {
|
|
478
|
+
collector.expectedIds = collector.expectedIds.filter(
|
|
479
|
+
(id) => id !== requestId
|
|
480
|
+
);
|
|
481
|
+
this._requestToStreamMapping.delete(requestId);
|
|
482
|
+
this.settleJsonResponse(streamId, collector);
|
|
443
483
|
return;
|
|
444
484
|
}
|
|
445
485
|
this._requestToStreamMapping.delete(requestId);
|
|
@@ -455,7 +495,7 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
455
495
|
try {
|
|
456
496
|
await writer.close();
|
|
457
497
|
} catch (error) {
|
|
458
|
-
_optionalChain([this, 'access',
|
|
498
|
+
_optionalChain([this, 'access', _24 => _24.onerror, 'optionalCall', _25 => _25(
|
|
459
499
|
error instanceof Error ? error : new Error(String(error))
|
|
460
500
|
)]);
|
|
461
501
|
}
|
|
@@ -469,7 +509,20 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
469
509
|
releasePendingRequests() {
|
|
470
510
|
for (const collector of this._jsonResponseCollectors.values()) {
|
|
471
511
|
collector.resolve(
|
|
472
|
-
collector.expectedIds.map(
|
|
512
|
+
collector.expectedIds.map(
|
|
513
|
+
(id) => _nullishCoalesce(collector.responses.get(id), () => ( // Nobody can answer this one now. Dropping it silently would leave
|
|
514
|
+
// the POST with an empty body for a single request and an empty
|
|
515
|
+
// array for a batch, neither of which a client can read as a
|
|
516
|
+
// result.
|
|
517
|
+
{
|
|
518
|
+
error: {
|
|
519
|
+
code: _typesjs.ErrorCode.ConnectionClosed,
|
|
520
|
+
message: "Connection closed: the session ended before this request was answered"
|
|
521
|
+
},
|
|
522
|
+
id,
|
|
523
|
+
jsonrpc: "2.0"
|
|
524
|
+
}))
|
|
525
|
+
)
|
|
473
526
|
);
|
|
474
527
|
}
|
|
475
528
|
this._jsonResponseCollectors.clear();
|
|
@@ -493,7 +546,32 @@ var WebStreamableHTTPServerTransport = (_class = class {
|
|
|
493
546
|
}
|
|
494
547
|
collector.resolve(responses);
|
|
495
548
|
}
|
|
549
|
+
/**
|
|
550
|
+
* Whether an event addressed to `streamId` should go to the event store.
|
|
551
|
+
*
|
|
552
|
+
* Everything but the standalone stream is persisted whenever a store is
|
|
553
|
+
* configured: those ids exist only because their stream did. The standalone
|
|
554
|
+
* id is a constant, so it resolves whether or not a client ever opened that
|
|
555
|
+
* stream, and storing before the first GET writes events nothing can reach —
|
|
556
|
+
* replay is keyed off Last-Event-Id, and a client that never held the stream
|
|
557
|
+
* was never handed an id to resume from.
|
|
558
|
+
*/
|
|
559
|
+
shouldPersist(streamId, writer) {
|
|
560
|
+
if (this._eventStore === void 0) {
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
if (streamId !== this._standaloneSseStreamId) {
|
|
564
|
+
return true;
|
|
565
|
+
}
|
|
566
|
+
return this._standaloneStreamOpened || // A live writer means the stream is open now, which is stronger evidence
|
|
567
|
+
// than the latch. Belt and braces: it keeps a writer reaching this point
|
|
568
|
+
// without `trackStream` from producing an SSE event with no `id:`.
|
|
569
|
+
writer !== void 0 || this._replaysInFlight > 0;
|
|
570
|
+
}
|
|
496
571
|
trackStream(streamId, writer) {
|
|
572
|
+
if (streamId === this._standaloneSseStreamId) {
|
|
573
|
+
this._standaloneStreamOpened = true;
|
|
574
|
+
}
|
|
497
575
|
this._streamMapping.set(streamId, writer);
|
|
498
576
|
const removeStreamIfCurrent = () => {
|
|
499
577
|
if (this._streamMapping.get(streamId) !== writer) {
|
|
@@ -624,7 +702,7 @@ var EdgeFastMCP = class {
|
|
|
624
702
|
*/
|
|
625
703
|
async #handleMcpRequest(request) {
|
|
626
704
|
const acceptHeader = request.headers.get("accept");
|
|
627
|
-
if (!_optionalChain([acceptHeader, 'optionalAccess',
|
|
705
|
+
if (!_optionalChain([acceptHeader, 'optionalAccess', _26 => _26.includes, 'call', _27 => _27("application/json")]) && !_optionalChain([acceptHeader, 'optionalAccess', _28 => _28.includes, 'call', _29 => _29("text/event-stream")])) {
|
|
628
706
|
return this.#errorResponse(
|
|
629
707
|
406,
|
|
630
708
|
-32e3,
|
|
@@ -632,7 +710,7 @@ var EdgeFastMCP = class {
|
|
|
632
710
|
);
|
|
633
711
|
}
|
|
634
712
|
const contentType = request.headers.get("content-type");
|
|
635
|
-
if (!_optionalChain([contentType, 'optionalAccess',
|
|
713
|
+
if (!_optionalChain([contentType, 'optionalAccess', _30 => _30.includes, 'call', _31 => _31("application/json")])) {
|
|
636
714
|
return this.#errorResponse(
|
|
637
715
|
415,
|
|
638
716
|
-32e3,
|
|
@@ -677,7 +755,7 @@ var EdgeFastMCP = class {
|
|
|
677
755
|
*/
|
|
678
756
|
async #handleMcpSseRequest(request) {
|
|
679
757
|
const acceptHeader = request.headers.get("accept");
|
|
680
|
-
if (!_optionalChain([acceptHeader, 'optionalAccess',
|
|
758
|
+
if (!_optionalChain([acceptHeader, 'optionalAccess', _32 => _32.includes, 'call', _33 => _33("text/event-stream")])) {
|
|
681
759
|
return this.#errorResponse(
|
|
682
760
|
406,
|
|
683
761
|
-32e3,
|
|
@@ -745,8 +823,8 @@ var EdgeFastMCP = class {
|
|
|
745
823
|
* Handle prompts/get request
|
|
746
824
|
*/
|
|
747
825
|
async #handlePromptsGet(id, params) {
|
|
748
|
-
const promptName = _optionalChain([params, 'optionalAccess',
|
|
749
|
-
const promptArgs = _optionalChain([params, 'optionalAccess',
|
|
826
|
+
const promptName = _optionalChain([params, 'optionalAccess', _34 => _34.name]);
|
|
827
|
+
const promptArgs = _optionalChain([params, 'optionalAccess', _35 => _35.arguments]);
|
|
750
828
|
const prompt = this.#prompts.find((p) => p.name === promptName);
|
|
751
829
|
if (!prompt) {
|
|
752
830
|
return this.#rpcError(
|
|
@@ -808,7 +886,7 @@ var EdgeFastMCP = class {
|
|
|
808
886
|
* Handle resources/read request
|
|
809
887
|
*/
|
|
810
888
|
async #handleResourcesRead(id, params) {
|
|
811
|
-
const uri = _optionalChain([params, 'optionalAccess',
|
|
889
|
+
const uri = _optionalChain([params, 'optionalAccess', _36 => _36.uri]);
|
|
812
890
|
const resource = this.#resources.find((r) => r.uri === uri);
|
|
813
891
|
if (!resource) {
|
|
814
892
|
return this.#rpcError(
|
|
@@ -837,8 +915,8 @@ var EdgeFastMCP = class {
|
|
|
837
915
|
* Handle tools/call request
|
|
838
916
|
*/
|
|
839
917
|
async #handleToolsCall(id, params) {
|
|
840
|
-
const toolName = _optionalChain([params, 'optionalAccess',
|
|
841
|
-
const toolArgs = _optionalChain([params, 'optionalAccess',
|
|
918
|
+
const toolName = _optionalChain([params, 'optionalAccess', _37 => _37.name]);
|
|
919
|
+
const toolArgs = _optionalChain([params, 'optionalAccess', _38 => _38.arguments]);
|
|
842
920
|
const tool = this.#tools.find((t) => t.name === toolName);
|
|
843
921
|
if (!tool) {
|
|
844
922
|
return this.#rpcError(
|
|
@@ -861,7 +939,7 @@ var EdgeFastMCP = class {
|
|
|
861
939
|
}
|
|
862
940
|
if (parsed.issues) {
|
|
863
941
|
const friendlyErrors = parsed.issues.map((issue) => {
|
|
864
|
-
const path = _optionalChain([issue, 'access',
|
|
942
|
+
const path = _optionalChain([issue, 'access', _39 => _39.path, 'optionalAccess', _40 => _40.join, 'call', _41 => _41(".")]) || "root";
|
|
865
943
|
return `${path}: ${issue.message}`;
|
|
866
944
|
}).join(", ");
|
|
867
945
|
return this.#rpcError(
|