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.
@@ -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
- __init4() {this._requestToStreamMapping = /* @__PURE__ */ new Map()}
33
- __init5() {this._standaloneSseStreamId = "_GET_stream"}
34
- __init6() {this._started = false}
35
- __init7() {this._streamMapping = /* @__PURE__ */ new Map()}
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
- if (writer || this._eventStore) {
111
+ const store = this.shouldPersist(streamId, writer) ? this._eventStore : void 0;
112
+ if (writer || store) {
95
113
  try {
96
114
  let eventId;
97
- if (this._eventStore) {
98
- eventId = await this._eventStore.storeEvent(streamId, message);
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', _6 => _6._onsessionclosed, 'optionalCall', _7 => _7(_nullishCoalesce(this.sessionId, () => ( "")))]);
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', _8 => _8.includes, 'call', _9 => _9("text/event-stream")])) {
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', _10 => _10.includes, 'call', _11 => _11("application/json")]) && !_optionalChain([acceptHeader, 'optionalAccess', _12 => _12.includes, 'call', _13 => _13("text/event-stream")])) {
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', _14 => _14.includes, 'call', _15 => _15("application/json")])) {
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', _16 => _16._onsessioninitialized, 'optionalCall', _17 => _17(this.sessionId)]);
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', _18 => _18.includes, 'call', _19 => _19("application/json")]));
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', _20 => _20.onmessage, 'optionalCall', _21 => _21(message, { authInfo: void 0 })]);
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 || this._jsonResponseCollectors.has(streamId)) {
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', _22 => _22.onerror, 'optionalCall', _23 => _23(
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((id) => collector.responses.get(id)).filter((response) => response !== void 0)
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', _24 => _24.includes, 'call', _25 => _25("application/json")]) && !_optionalChain([acceptHeader, 'optionalAccess', _26 => _26.includes, 'call', _27 => _27("text/event-stream")])) {
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', _28 => _28.includes, 'call', _29 => _29("application/json")])) {
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', _30 => _30.includes, 'call', _31 => _31("text/event-stream")])) {
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', _32 => _32.name]);
749
- const promptArgs = _optionalChain([params, 'optionalAccess', _33 => _33.arguments]);
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', _34 => _34.uri]);
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', _35 => _35.name]);
841
- const toolArgs = _optionalChain([params, 'optionalAccess', _36 => _36.arguments]);
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', _37 => _37.path, 'optionalAccess', _38 => _38.join, 'call', _39 => _39(".")]) || "root";
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(