@frontmcp/testing 1.8.7 → 1.9.0
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/client/mcp-test-client.d.ts.map +1 -1
- package/esm/fixtures/index.mjs +524 -31
- package/esm/index.mjs +525 -31
- package/esm/matchers/index.mjs +3 -0
- package/esm/package.json +8 -6
- package/esm/perf/index.mjs +466 -7
- package/esm/perf/perf-setup.mjs +3 -0
- package/esm/playwright/index.mjs +3 -0
- package/esm/setup.mjs +3 -0
- package/fixtures/fixture-types.d.ts +19 -6
- package/fixtures/fixture-types.d.ts.map +1 -1
- package/fixtures/index.d.ts +1 -1
- package/fixtures/index.d.ts.map +1 -1
- package/fixtures/index.js +521 -31
- package/fixtures/test-fixture.d.ts.map +1 -1
- package/index.d.ts +2 -1
- package/index.d.ts.map +1 -1
- package/index.js +523 -31
- package/package.json +8 -6
- package/perf/index.js +463 -7
- package/transport/index.d.ts +1 -0
- package/transport/index.d.ts.map +1 -1
- package/transport/sse.transport.d.ts +80 -0
- package/transport/sse.transport.d.ts.map +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-test-client.d.ts","sourceRoot":"","sources":["../../src/client/mcp-test-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACzB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp-test-client.d.ts","sourceRoot":"","sources":["../../src/client/mcp-test-client.ts"],"names":[],"mappings":"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"}
|
package/esm/fixtures/index.mjs
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
var __frontmcpModule = typeof process !== "undefined" && typeof process.getBuiltinModule === "function" ? process.getBuiltinModule("module") : undefined;
|
|
2
|
+
const require = __frontmcpModule ? __frontmcpModule.createRequire(import.meta.url || 'file:///') : undefined;
|
|
3
|
+
// __frontmcpCreateRequire
|
|
1
4
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
5
|
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
6
|
}) : x)(function(x) {
|
|
@@ -489,8 +492,453 @@ function isInterceptedError(err) {
|
|
|
489
492
|
return typeof err === "object" && err !== null && interceptedErrors.has(err);
|
|
490
493
|
}
|
|
491
494
|
|
|
492
|
-
// libs/testing/src/transport/
|
|
495
|
+
// libs/testing/src/transport/sse.transport.ts
|
|
493
496
|
var DEFAULT_TIMEOUT = 3e4;
|
|
497
|
+
var SseTransport = class {
|
|
498
|
+
baseUrl;
|
|
499
|
+
entryPath;
|
|
500
|
+
timeout;
|
|
501
|
+
authHeaders;
|
|
502
|
+
publicMode;
|
|
503
|
+
debug;
|
|
504
|
+
clientInfo;
|
|
505
|
+
notificationHandler;
|
|
506
|
+
interceptors;
|
|
507
|
+
elicitationHandler;
|
|
508
|
+
state = "disconnected";
|
|
509
|
+
authToken;
|
|
510
|
+
sessionId;
|
|
511
|
+
messageEndpoint;
|
|
512
|
+
stream;
|
|
513
|
+
pending = /* @__PURE__ */ new Map();
|
|
514
|
+
connectionCount = 0;
|
|
515
|
+
reconnectCount = 0;
|
|
516
|
+
lastRequestHeaders = {};
|
|
517
|
+
constructor(config) {
|
|
518
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
519
|
+
this.entryPath = config.entryPath;
|
|
520
|
+
this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
521
|
+
this.authHeaders = config.auth?.headers ?? {};
|
|
522
|
+
this.authToken = config.auth?.token;
|
|
523
|
+
this.publicMode = config.publicMode ?? false;
|
|
524
|
+
this.debug = config.debug ?? false;
|
|
525
|
+
this.clientInfo = config.clientInfo;
|
|
526
|
+
this.notificationHandler = config.notificationHandler;
|
|
527
|
+
this.interceptors = config.interceptors;
|
|
528
|
+
this.elicitationHandler = config.elicitationHandler;
|
|
529
|
+
}
|
|
530
|
+
async connect() {
|
|
531
|
+
this.state = "connecting";
|
|
532
|
+
this.connectionCount++;
|
|
533
|
+
try {
|
|
534
|
+
if (!this.publicMode && !this.authToken) {
|
|
535
|
+
await this.requestAnonymousToken();
|
|
536
|
+
}
|
|
537
|
+
await this.openStream();
|
|
538
|
+
this.state = "connected";
|
|
539
|
+
this.log(`Connected; messages go to ${this.messageEndpoint}`);
|
|
540
|
+
} catch (error) {
|
|
541
|
+
this.state = "error";
|
|
542
|
+
this.stream?.abort();
|
|
543
|
+
this.stream = void 0;
|
|
544
|
+
throw error;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async request(message) {
|
|
548
|
+
this.ensureConnected();
|
|
549
|
+
const startTime = Date.now();
|
|
550
|
+
if (this.interceptors) {
|
|
551
|
+
const intercepted = await this.interceptors.processRequest(message, {
|
|
552
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
553
|
+
transport: "sse",
|
|
554
|
+
sessionId: this.sessionId
|
|
555
|
+
});
|
|
556
|
+
switch (intercepted.type) {
|
|
557
|
+
case "mock":
|
|
558
|
+
return await this.interceptors.processResponse(
|
|
559
|
+
message,
|
|
560
|
+
intercepted.response,
|
|
561
|
+
Date.now() - startTime
|
|
562
|
+
);
|
|
563
|
+
case "error":
|
|
564
|
+
throw markInterceptedError(intercepted.error);
|
|
565
|
+
case "continue":
|
|
566
|
+
message = intercepted.request;
|
|
567
|
+
break;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
let response;
|
|
571
|
+
if (message.id === void 0) {
|
|
572
|
+
await this.post(JSON.stringify(message));
|
|
573
|
+
response = { jsonrpc: "2.0", id: null, result: void 0 };
|
|
574
|
+
} else {
|
|
575
|
+
response = await this.postAndAwait(message.id, JSON.stringify(message));
|
|
576
|
+
}
|
|
577
|
+
if (this.interceptors) {
|
|
578
|
+
response = await this.interceptors.processResponse(message, response, Date.now() - startTime);
|
|
579
|
+
}
|
|
580
|
+
return response;
|
|
581
|
+
}
|
|
582
|
+
async notify(message) {
|
|
583
|
+
this.ensureConnected();
|
|
584
|
+
const result = await this.post(JSON.stringify(message));
|
|
585
|
+
if (!result.ok) this.log(`HTTP ${result.status} on notification: ${result.body}`);
|
|
586
|
+
}
|
|
587
|
+
async sendRaw(data) {
|
|
588
|
+
this.ensureConnected();
|
|
589
|
+
let id;
|
|
590
|
+
try {
|
|
591
|
+
const parsed = JSON.parse(data);
|
|
592
|
+
if (typeof parsed.id === "string" || typeof parsed.id === "number") id = parsed.id;
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
595
|
+
if (id !== void 0) return this.postAndAwait(id, data);
|
|
596
|
+
const result = await this.post(data);
|
|
597
|
+
if (!result.ok) {
|
|
598
|
+
return { jsonrpc: "2.0", id: null, error: { code: -32700, message: "Parse error", data: result.body } };
|
|
599
|
+
}
|
|
600
|
+
return { jsonrpc: "2.0", id: null, result: void 0 };
|
|
601
|
+
}
|
|
602
|
+
async close() {
|
|
603
|
+
this.closeStream("Transport closed");
|
|
604
|
+
this.state = "disconnected";
|
|
605
|
+
this.sessionId = void 0;
|
|
606
|
+
this.messageEndpoint = void 0;
|
|
607
|
+
this.log("SSE transport closed");
|
|
608
|
+
}
|
|
609
|
+
isConnected() {
|
|
610
|
+
return this.state === "connected";
|
|
611
|
+
}
|
|
612
|
+
getState() {
|
|
613
|
+
return this.state;
|
|
614
|
+
}
|
|
615
|
+
getSessionId() {
|
|
616
|
+
return this.sessionId;
|
|
617
|
+
}
|
|
618
|
+
setAuthToken(token) {
|
|
619
|
+
this.authToken = token;
|
|
620
|
+
}
|
|
621
|
+
setTimeout(ms) {
|
|
622
|
+
this.timeout = ms;
|
|
623
|
+
}
|
|
624
|
+
getMessageEndpoint() {
|
|
625
|
+
return this.messageEndpoint;
|
|
626
|
+
}
|
|
627
|
+
getConnectionCount() {
|
|
628
|
+
return this.connectionCount;
|
|
629
|
+
}
|
|
630
|
+
getReconnectCount() {
|
|
631
|
+
return this.reconnectCount;
|
|
632
|
+
}
|
|
633
|
+
getLastRequestHeaders() {
|
|
634
|
+
return { ...this.lastRequestHeaders };
|
|
635
|
+
}
|
|
636
|
+
setInterceptors(interceptors2) {
|
|
637
|
+
this.interceptors = interceptors2;
|
|
638
|
+
}
|
|
639
|
+
getInterceptors() {
|
|
640
|
+
return this.interceptors;
|
|
641
|
+
}
|
|
642
|
+
setElicitationHandler(handler) {
|
|
643
|
+
this.elicitationHandler = handler;
|
|
644
|
+
}
|
|
645
|
+
async simulateDisconnect() {
|
|
646
|
+
this.closeStream("Simulated disconnect");
|
|
647
|
+
this.state = "disconnected";
|
|
648
|
+
this.sessionId = void 0;
|
|
649
|
+
this.messageEndpoint = void 0;
|
|
650
|
+
}
|
|
651
|
+
async waitForReconnect(timeoutMs) {
|
|
652
|
+
this.reconnectCount++;
|
|
653
|
+
let timer;
|
|
654
|
+
try {
|
|
655
|
+
await Promise.race([
|
|
656
|
+
this.connect(),
|
|
657
|
+
new Promise((_, reject) => {
|
|
658
|
+
timer = setTimeout(() => reject(new Error("Timeout waiting for reconnection")), timeoutMs);
|
|
659
|
+
})
|
|
660
|
+
]);
|
|
661
|
+
} finally {
|
|
662
|
+
clearTimeout(timer);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
666
|
+
// STREAM
|
|
667
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
668
|
+
/** Open `GET <entry>/sse` and wait for the `endpoint` event. */
|
|
669
|
+
async openStream() {
|
|
670
|
+
const controller = new AbortController();
|
|
671
|
+
this.stream = controller;
|
|
672
|
+
const url = this.sseUrl();
|
|
673
|
+
const { "Content-Type": _contentType, ...headers } = this.buildHeaders();
|
|
674
|
+
this.log(`GET ${url}`);
|
|
675
|
+
const connectTimer = setTimeout(() => controller.abort(), this.timeout);
|
|
676
|
+
let response;
|
|
677
|
+
try {
|
|
678
|
+
response = await fetch(url, {
|
|
679
|
+
method: "GET",
|
|
680
|
+
headers: { ...headers, Accept: "text/event-stream" },
|
|
681
|
+
signal: controller.signal
|
|
682
|
+
});
|
|
683
|
+
} catch (error) {
|
|
684
|
+
clearTimeout(connectTimer);
|
|
685
|
+
throw new Error(
|
|
686
|
+
isAbortError(error) ? `SSE connection to ${url} timed out after ${this.timeout}ms` : `SSE connection to ${url} failed: ${errorMessage(error)}`,
|
|
687
|
+
{ cause: error }
|
|
688
|
+
);
|
|
689
|
+
}
|
|
690
|
+
const isEventStream = response.headers.get("content-type")?.includes("text/event-stream") ?? false;
|
|
691
|
+
if (!response.ok || !response.body || !isEventStream) {
|
|
692
|
+
clearTimeout(connectTimer);
|
|
693
|
+
const body = await response.text().catch(() => "");
|
|
694
|
+
throw new Error(
|
|
695
|
+
`SSE connection to ${url} failed: HTTP ${response.status} ${response.statusText}${body ? ` \u2014 ${body}` : ""}. The legacy HTTP+SSE transport needs a server that serves it (@FrontMcp({ transport: { protocol: { legacy: true } } })).`
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
const endpoint = new Promise((resolve, reject) => {
|
|
699
|
+
const reader = response.body?.getReader();
|
|
700
|
+
if (!reader) {
|
|
701
|
+
reject(new Error(`SSE connection to ${url} has no body`));
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
void this.readStream(controller, reader, resolve, reject);
|
|
705
|
+
});
|
|
706
|
+
try {
|
|
707
|
+
const data = await endpoint;
|
|
708
|
+
const messageUrl = new URL(data, url);
|
|
709
|
+
this.messageEndpoint = messageUrl.toString();
|
|
710
|
+
this.sessionId = messageUrl.searchParams.get("sessionId") ?? void 0;
|
|
711
|
+
} finally {
|
|
712
|
+
clearTimeout(connectTimer);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
async readStream(controller, reader, onEndpoint, onEndpointError) {
|
|
716
|
+
const decoder = new TextDecoder();
|
|
717
|
+
let buffer = "";
|
|
718
|
+
let endpointSeen = false;
|
|
719
|
+
try {
|
|
720
|
+
for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
|
|
721
|
+
const { completeEvents, remainder } = splitSSEEvents(buffer + decoder.decode(chunk.value, { stream: true }));
|
|
722
|
+
buffer = remainder;
|
|
723
|
+
for (const text of completeEvents) {
|
|
724
|
+
const event = parseSSEEvent(text);
|
|
725
|
+
if (!event) continue;
|
|
726
|
+
if (event.type === "endpoint") {
|
|
727
|
+
if (!endpointSeen) {
|
|
728
|
+
endpointSeen = true;
|
|
729
|
+
onEndpoint(event.data);
|
|
730
|
+
}
|
|
731
|
+
continue;
|
|
732
|
+
}
|
|
733
|
+
await this.handleMessage(event.data);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
} catch (error) {
|
|
737
|
+
if (!controller.signal.aborted) this.log("SSE stream failed:", error);
|
|
738
|
+
} finally {
|
|
739
|
+
reader.releaseLock();
|
|
740
|
+
}
|
|
741
|
+
if (!endpointSeen) {
|
|
742
|
+
onEndpointError(
|
|
743
|
+
new Error(
|
|
744
|
+
controller.signal.aborted ? `SSE stream at ${this.sseUrl()} sent no endpoint event within ${this.timeout}ms` : `SSE stream at ${this.sseUrl()} ended before the server sent its endpoint`
|
|
745
|
+
)
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
if (this.stream === controller) {
|
|
749
|
+
this.stream = void 0;
|
|
750
|
+
this.rejectPending("SSE stream closed by the server");
|
|
751
|
+
if (this.state === "connected") this.state = "disconnected";
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
async handleMessage(data) {
|
|
755
|
+
let message;
|
|
756
|
+
try {
|
|
757
|
+
message = JSON.parse(data);
|
|
758
|
+
} catch {
|
|
759
|
+
this.log("Ignoring SSE event that is not JSON:", data);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
if (!message || typeof message !== "object") return;
|
|
763
|
+
if (typeof message.method === "string") {
|
|
764
|
+
const request = message;
|
|
765
|
+
if (request.id === void 0 || request.id === null) {
|
|
766
|
+
this.notificationHandler?.(request);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
await this.answerServerRequest(request);
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
if (message.id !== void 0 && message.id !== null) {
|
|
773
|
+
const pending = this.pending.get(message.id);
|
|
774
|
+
if (pending) {
|
|
775
|
+
clearTimeout(pending.timer);
|
|
776
|
+
this.pending.delete(message.id);
|
|
777
|
+
pending.resolve(message);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
/** Server→client requests: elicitation goes to the handler, `ping` is answered, the rest refused. */
|
|
782
|
+
async answerServerRequest(request) {
|
|
783
|
+
const id = request.id;
|
|
784
|
+
let reply;
|
|
785
|
+
if (request.method === "elicitation/create") {
|
|
786
|
+
reply = { jsonrpc: "2.0", id, result: await this.elicit(request.params) };
|
|
787
|
+
} else if (request.method === "ping") {
|
|
788
|
+
reply = { jsonrpc: "2.0", id, result: {} };
|
|
789
|
+
} else {
|
|
790
|
+
reply = { jsonrpc: "2.0", id, error: { code: -32601, message: `Method not found: ${request.method}` } };
|
|
791
|
+
}
|
|
792
|
+
const result = await this.post(JSON.stringify(reply));
|
|
793
|
+
if (!result.ok) this.log(`HTTP ${result.status} answering ${request.method}: ${result.body}`);
|
|
794
|
+
}
|
|
795
|
+
async elicit(params) {
|
|
796
|
+
if (!this.elicitationHandler) return { action: "decline" };
|
|
797
|
+
try {
|
|
798
|
+
return await this.elicitationHandler(params);
|
|
799
|
+
} catch (error) {
|
|
800
|
+
this.log("Elicitation handler error:", error);
|
|
801
|
+
return { action: "cancel" };
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
closeStream(reason) {
|
|
805
|
+
const controller = this.stream;
|
|
806
|
+
this.stream = void 0;
|
|
807
|
+
controller?.abort();
|
|
808
|
+
this.rejectPending(reason);
|
|
809
|
+
}
|
|
810
|
+
rejectPending(reason) {
|
|
811
|
+
for (const [id, pending] of this.pending) {
|
|
812
|
+
clearTimeout(pending.timer);
|
|
813
|
+
pending.resolve({ jsonrpc: "2.0", id, error: { code: -32e3, message: reason } });
|
|
814
|
+
}
|
|
815
|
+
this.pending.clear();
|
|
816
|
+
}
|
|
817
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
818
|
+
// POSTING
|
|
819
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
820
|
+
/** POST a message and resolve with the response the stream carries for `id`. */
|
|
821
|
+
async postAndAwait(id, body) {
|
|
822
|
+
const answer = new Promise((resolve) => {
|
|
823
|
+
const timer = setTimeout(() => {
|
|
824
|
+
this.pending.delete(id);
|
|
825
|
+
resolve({ jsonrpc: "2.0", id, error: { code: -32e3, message: `Request timeout after ${this.timeout}ms` } });
|
|
826
|
+
}, this.timeout);
|
|
827
|
+
this.pending.set(id, { resolve, timer });
|
|
828
|
+
});
|
|
829
|
+
const result = await this.post(body);
|
|
830
|
+
if (!result.ok) {
|
|
831
|
+
const pending = this.pending.get(id);
|
|
832
|
+
if (pending) {
|
|
833
|
+
clearTimeout(pending.timer);
|
|
834
|
+
this.pending.delete(id);
|
|
835
|
+
}
|
|
836
|
+
return {
|
|
837
|
+
jsonrpc: "2.0",
|
|
838
|
+
id,
|
|
839
|
+
error: { code: -32e3, message: `HTTP ${result.status}: ${result.statusText}`, data: result.body }
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
return answer;
|
|
843
|
+
}
|
|
844
|
+
async post(body) {
|
|
845
|
+
const endpoint = this.messageEndpoint;
|
|
846
|
+
if (!endpoint) throw new Error("SSE transport has no message endpoint. Call connect() first.");
|
|
847
|
+
const headers = this.buildHeaders();
|
|
848
|
+
this.lastRequestHeaders = headers;
|
|
849
|
+
this.log(`POST ${endpoint}`, body);
|
|
850
|
+
const controller = new AbortController();
|
|
851
|
+
const timer = setTimeout(() => controller.abort(), this.timeout);
|
|
852
|
+
try {
|
|
853
|
+
const response = await fetch(endpoint, { method: "POST", headers, body, signal: controller.signal });
|
|
854
|
+
const text = await response.text();
|
|
855
|
+
return { ok: response.ok, status: response.status, statusText: response.statusText, body: text };
|
|
856
|
+
} catch (error) {
|
|
857
|
+
if (isAbortError(error)) {
|
|
858
|
+
return { ok: false, status: 0, statusText: `timeout after ${this.timeout}ms`, body: "" };
|
|
859
|
+
}
|
|
860
|
+
throw error;
|
|
861
|
+
} finally {
|
|
862
|
+
clearTimeout(timer);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
866
|
+
// HELPERS
|
|
867
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
868
|
+
async requestAnonymousToken() {
|
|
869
|
+
const url = new URL(this.baseUrl);
|
|
870
|
+
url.pathname = `${url.pathname.replace(/\/+$/, "")}/oauth/token`;
|
|
871
|
+
url.search = "";
|
|
872
|
+
const tokenUrl = url.toString();
|
|
873
|
+
try {
|
|
874
|
+
const response = await fetch(tokenUrl, {
|
|
875
|
+
method: "POST",
|
|
876
|
+
headers: { "Content-Type": "application/json" },
|
|
877
|
+
body: JSON.stringify({ grant_type: "anonymous", client_id: crypto.randomUUID(), resource: this.baseUrl })
|
|
878
|
+
});
|
|
879
|
+
if (!response.ok) {
|
|
880
|
+
this.log(`No anonymous token (HTTP ${response.status})`);
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
const token = await response.json();
|
|
884
|
+
if (token.access_token) this.authToken = token.access_token;
|
|
885
|
+
} catch (error) {
|
|
886
|
+
this.log("Error requesting anonymous token:", error);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
/** `<baseUrl><entryPath>/sse`, built from URL parts so query params on `baseUrl` survive. */
|
|
890
|
+
sseUrl() {
|
|
891
|
+
const url = new URL(this.baseUrl);
|
|
892
|
+
const entry = (this.entryPath ?? "").replace(/^\/+|\/+$/g, "");
|
|
893
|
+
const basePath = url.pathname.replace(/\/+$/, "");
|
|
894
|
+
url.pathname = entry ? `${basePath}/${entry}/sse` : `${basePath}/sse`;
|
|
895
|
+
return url.toString();
|
|
896
|
+
}
|
|
897
|
+
buildHeaders() {
|
|
898
|
+
const headers = {
|
|
899
|
+
"Content-Type": "application/json",
|
|
900
|
+
Accept: "application/json, text/event-stream"
|
|
901
|
+
};
|
|
902
|
+
if (this.clientInfo) headers["User-Agent"] = `${this.clientInfo.name}/${this.clientInfo.version}`;
|
|
903
|
+
if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
904
|
+
if (this.sessionId) headers["mcp-session-id"] = this.sessionId;
|
|
905
|
+
Object.assign(headers, this.authHeaders);
|
|
906
|
+
return headers;
|
|
907
|
+
}
|
|
908
|
+
ensureConnected() {
|
|
909
|
+
if (this.state !== "connected") {
|
|
910
|
+
throw new Error("Transport not connected. Call connect() first.");
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
log(message, data) {
|
|
914
|
+
if (this.debug) {
|
|
915
|
+
console.log(`[SSE] ${message}`, data ?? "");
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
};
|
|
919
|
+
function splitSSEEvents(buffer) {
|
|
920
|
+
const endsWithCarriageReturn = buffer.endsWith("\r");
|
|
921
|
+
const normalized = (endsWithCarriageReturn ? buffer.slice(0, -1) : buffer).replace(/\r\n?/g, "\n");
|
|
922
|
+
const completeEvents = normalized.split("\n\n");
|
|
923
|
+
const remainder = completeEvents.pop() ?? "";
|
|
924
|
+
return { completeEvents, remainder: endsWithCarriageReturn ? `${remainder}\r` : remainder };
|
|
925
|
+
}
|
|
926
|
+
function parseSSEEvent(text) {
|
|
927
|
+
let type = "message";
|
|
928
|
+
const data = [];
|
|
929
|
+
for (const line of text.split("\n")) {
|
|
930
|
+
if (line.startsWith(":")) continue;
|
|
931
|
+
const colon = line.indexOf(":");
|
|
932
|
+
const field = colon === -1 ? line : line.slice(0, colon);
|
|
933
|
+
const value = colon === -1 ? "" : line.slice(colon + 1).replace(/^ /, "");
|
|
934
|
+
if (field === "event") type = value;
|
|
935
|
+
else if (field === "data") data.push(value);
|
|
936
|
+
}
|
|
937
|
+
return data.length > 0 ? { type, data: data.join("\n") } : void 0;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// libs/testing/src/transport/streamable-http.transport.ts
|
|
941
|
+
var DEFAULT_TIMEOUT2 = 3e4;
|
|
494
942
|
var NOTIFICATION_STREAM_REOPEN_DELAY_MS = 250;
|
|
495
943
|
var StreamableHttpTransport = class {
|
|
496
944
|
config;
|
|
@@ -512,7 +960,7 @@ var StreamableHttpTransport = class {
|
|
|
512
960
|
baseUrl: config.baseUrl.replace(/\/$/, ""),
|
|
513
961
|
// Remove trailing slash
|
|
514
962
|
entryPath: config.entryPath,
|
|
515
|
-
timeout: config.timeout ??
|
|
963
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT2,
|
|
516
964
|
auth: config.auth ?? {},
|
|
517
965
|
publicMode: config.publicMode ?? false,
|
|
518
966
|
debug: config.debug ?? false,
|
|
@@ -852,7 +1300,7 @@ var StreamableHttpTransport = class {
|
|
|
852
1300
|
let buffer = "";
|
|
853
1301
|
try {
|
|
854
1302
|
for (let chunk = await reader.read(); !chunk.done; chunk = await reader.read()) {
|
|
855
|
-
const { completeEvents, remainder } =
|
|
1303
|
+
const { completeEvents, remainder } = splitSSEEvents2(buffer + decoder.decode(chunk.value, { stream: true }));
|
|
856
1304
|
buffer = remainder;
|
|
857
1305
|
for (const eventText of completeEvents) {
|
|
858
1306
|
for (const event of this.parseSSEEvents(eventText, void 0).events) {
|
|
@@ -1186,7 +1634,7 @@ var StreamableHttpTransport = class {
|
|
|
1186
1634
|
};
|
|
1187
1635
|
}
|
|
1188
1636
|
};
|
|
1189
|
-
function
|
|
1637
|
+
function splitSSEEvents2(buffer) {
|
|
1190
1638
|
const endsWithCarriageReturn = buffer.endsWith("\r");
|
|
1191
1639
|
const normalized = (endsWithCarriageReturn ? buffer.slice(0, -1) : buffer).replace(/\r\n?/g, "\n");
|
|
1192
1640
|
const completeEvents = normalized.split("\n\n");
|
|
@@ -1424,7 +1872,7 @@ var McpTestClientBuilder = class {
|
|
|
1424
1872
|
};
|
|
1425
1873
|
|
|
1426
1874
|
// libs/testing/src/client/mcp-test-client.ts
|
|
1427
|
-
var
|
|
1875
|
+
var DEFAULT_TIMEOUT3 = 3e4;
|
|
1428
1876
|
var DEFAULT_PROTOCOL_VERSION = "2025-06-18";
|
|
1429
1877
|
var UNSUPPORTED_PROTOCOL_VERSIONS = ["2026-07-28"];
|
|
1430
1878
|
var DEFAULT_CLIENT_INFO = {
|
|
@@ -1494,7 +1942,7 @@ var McpTestClient = class {
|
|
|
1494
1942
|
transport: config.transport ?? "streamable-http",
|
|
1495
1943
|
auth: config.auth ?? {},
|
|
1496
1944
|
publicMode: config.publicMode ?? false,
|
|
1497
|
-
timeout: config.timeout ??
|
|
1945
|
+
timeout: config.timeout ?? DEFAULT_TIMEOUT3,
|
|
1498
1946
|
debug: config.debug ?? false,
|
|
1499
1947
|
protocolVersion: config.protocolVersion ?? DEFAULT_PROTOCOL_VERSION,
|
|
1500
1948
|
clientInfo: config.clientInfo ?? DEFAULT_CLIENT_INFO,
|
|
@@ -2260,7 +2708,18 @@ var McpTestClient = class {
|
|
|
2260
2708
|
notificationHandler: this.recordNotification
|
|
2261
2709
|
});
|
|
2262
2710
|
case "sse":
|
|
2263
|
-
|
|
2711
|
+
return new SseTransport({
|
|
2712
|
+
baseUrl,
|
|
2713
|
+
entryPath: this.config.entryPath,
|
|
2714
|
+
timeout: this.config.timeout,
|
|
2715
|
+
auth: this.config.auth,
|
|
2716
|
+
publicMode: this.config.publicMode,
|
|
2717
|
+
debug: this.config.debug,
|
|
2718
|
+
interceptors: this._interceptors,
|
|
2719
|
+
clientInfo: this.config.clientInfo,
|
|
2720
|
+
elicitationHandler: this._elicitationHandler,
|
|
2721
|
+
notificationHandler: this.recordNotification
|
|
2722
|
+
});
|
|
2264
2723
|
default:
|
|
2265
2724
|
throw new Error(`Unknown transport type: ${this.config.transport}`);
|
|
2266
2725
|
}
|
|
@@ -3325,7 +3784,19 @@ function jestGlobals() {
|
|
|
3325
3784
|
);
|
|
3326
3785
|
}
|
|
3327
3786
|
}
|
|
3328
|
-
|
|
3787
|
+
function newScope(parent) {
|
|
3788
|
+
return {
|
|
3789
|
+
skipped: parent?.skipped ?? false,
|
|
3790
|
+
reason: parent?.reason,
|
|
3791
|
+
config: {},
|
|
3792
|
+
envs: [],
|
|
3793
|
+
configured: false,
|
|
3794
|
+
cleanupRegistered: false,
|
|
3795
|
+
beforeEachHooks: [],
|
|
3796
|
+
afterEachHooks: []
|
|
3797
|
+
};
|
|
3798
|
+
}
|
|
3799
|
+
var scopes = [newScope()];
|
|
3329
3800
|
function currentScope() {
|
|
3330
3801
|
return scopes[scopes.length - 1];
|
|
3331
3802
|
}
|
|
@@ -3333,9 +3804,8 @@ var serverEntries = /* @__PURE__ */ new Map();
|
|
|
3333
3804
|
function resolveConfig(chain) {
|
|
3334
3805
|
const merged = {};
|
|
3335
3806
|
for (const scope of chain) {
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
if (env) merged.env = { ...merged.env, ...env };
|
|
3807
|
+
Object.assign(merged, scope.config);
|
|
3808
|
+
for (const env of scope.envs) merged.env = { ...merged.env, ...env };
|
|
3339
3809
|
}
|
|
3340
3810
|
return merged;
|
|
3341
3811
|
}
|
|
@@ -3597,17 +4067,39 @@ function resolveServerCommand(server) {
|
|
|
3597
4067
|
function runWithFixtures(fn, chain) {
|
|
3598
4068
|
return async (...extra) => {
|
|
3599
4069
|
const fixtures = await buildFixtures(chain);
|
|
3600
|
-
let
|
|
4070
|
+
let failure;
|
|
3601
4071
|
try {
|
|
4072
|
+
for (const scope of chain) {
|
|
4073
|
+
for (const hook of scope.beforeEachHooks) await hook(fixtures);
|
|
4074
|
+
}
|
|
3602
4075
|
await fn(fixtures, ...extra);
|
|
3603
4076
|
} catch (error) {
|
|
3604
|
-
|
|
3605
|
-
throw error;
|
|
3606
|
-
} finally {
|
|
3607
|
-
await cleanupTestFixtures(fixtures, testFailed);
|
|
4077
|
+
failure = { error };
|
|
3608
4078
|
}
|
|
4079
|
+
try {
|
|
4080
|
+
await runAfterEachHooks(chain, fixtures);
|
|
4081
|
+
} catch (error) {
|
|
4082
|
+
failure ??= { error };
|
|
4083
|
+
}
|
|
4084
|
+
await cleanupTestFixtures(fixtures, failure !== void 0);
|
|
4085
|
+
if (failure) throw failure.error;
|
|
3609
4086
|
};
|
|
3610
4087
|
}
|
|
4088
|
+
async function runAfterEachHooks(chain, fixtures) {
|
|
4089
|
+
let firstError;
|
|
4090
|
+
let failed = false;
|
|
4091
|
+
for (let i = chain.length - 1; i >= 0; i--) {
|
|
4092
|
+
for (const hook of chain[i].afterEachHooks) {
|
|
4093
|
+
try {
|
|
4094
|
+
await hook(fixtures);
|
|
4095
|
+
} catch (error) {
|
|
4096
|
+
if (!failed) firstError = error;
|
|
4097
|
+
failed = true;
|
|
4098
|
+
}
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
if (failed) throw firstError;
|
|
4102
|
+
}
|
|
3611
4103
|
function skipTitle(name, scope) {
|
|
3612
4104
|
return scope.reason ? `${name} (skipped: ${scope.reason})` : name;
|
|
3613
4105
|
}
|
|
@@ -3622,11 +4114,9 @@ function testWithFixtures(name, fn) {
|
|
|
3622
4114
|
}
|
|
3623
4115
|
function use(config) {
|
|
3624
4116
|
const scope = currentScope();
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
env: config.env ? { ...scope.config.env, ...config.env } : scope.config.env
|
|
3629
|
-
};
|
|
4117
|
+
const { env, ...rest } = config;
|
|
4118
|
+
scope.config = { ...scope.config, ...rest };
|
|
4119
|
+
if (env) scope.envs.push(env);
|
|
3630
4120
|
scope.configured = true;
|
|
3631
4121
|
if (!scope.cleanupRegistered) {
|
|
3632
4122
|
scope.cleanupRegistered = true;
|
|
@@ -3677,14 +4167,7 @@ function each(table, ...tagged) {
|
|
|
3677
4167
|
}
|
|
3678
4168
|
function withScope(fn) {
|
|
3679
4169
|
return (...args) => {
|
|
3680
|
-
|
|
3681
|
-
scopes.push({
|
|
3682
|
-
skipped: parent.skipped,
|
|
3683
|
-
reason: parent.reason,
|
|
3684
|
-
config: {},
|
|
3685
|
-
configured: false,
|
|
3686
|
-
cleanupRegistered: false
|
|
3687
|
-
});
|
|
4170
|
+
scopes.push(newScope(currentScope()));
|
|
3688
4171
|
try {
|
|
3689
4172
|
fn(...args);
|
|
3690
4173
|
} finally {
|
|
@@ -3722,12 +4205,22 @@ var describeWithScope = Object.assign(
|
|
|
3722
4205
|
each: scopedEach(() => jestGlobals().describe.each)
|
|
3723
4206
|
}
|
|
3724
4207
|
);
|
|
4208
|
+
function eachHook(kind) {
|
|
4209
|
+
return (fn, timeout) => {
|
|
4210
|
+
if (typeof fn === "function" && fn.length > 0) {
|
|
4211
|
+
const scope = currentScope();
|
|
4212
|
+
(kind === "beforeEach" ? scope.beforeEachHooks : scope.afterEachHooks).push(fn);
|
|
4213
|
+
return;
|
|
4214
|
+
}
|
|
4215
|
+
jestGlobals()[kind](fn, timeout);
|
|
4216
|
+
};
|
|
4217
|
+
}
|
|
3725
4218
|
var test = testWithFixtures;
|
|
3726
4219
|
test.use = use;
|
|
3727
4220
|
test.describe = describeWithScope;
|
|
3728
4221
|
test.beforeAll = ((...args) => jestGlobals().beforeAll(...args));
|
|
3729
|
-
test.beforeEach = (
|
|
3730
|
-
test.afterEach = (
|
|
4222
|
+
test.beforeEach = eachHook("beforeEach");
|
|
4223
|
+
test.afterEach = eachHook("afterEach");
|
|
3731
4224
|
test.afterAll = ((...args) => jestGlobals().afterAll(...args));
|
|
3732
4225
|
test.skip = skip;
|
|
3733
4226
|
test.only = only;
|