@orkestrel/mcp 0.0.23 → 0.0.25
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/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +797 -212
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +496 -111
- package/dist/src/core/index.d.ts +496 -111
- package/dist/src/core/index.js +780 -210
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +60 -19
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +44 -7
- package/dist/src/server/index.d.ts +44 -7
- package/dist/src/server/index.js +61 -21
- package/dist/src/server/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -100,6 +100,24 @@ var DEFAULT_MCP_DELIVERY = 1e4;
|
|
|
100
100
|
//#endregion
|
|
101
101
|
//#region src/server/helpers.ts
|
|
102
102
|
/**
|
|
103
|
+
* Builds the error for a non-success HTTP response that carried no JSON-RPC message.
|
|
104
|
+
*
|
|
105
|
+
* @param response - The response whose status is reported
|
|
106
|
+
* @param type - The response's content type, or an empty string when absent
|
|
107
|
+
* @returns An error naming the HTTP status and unsupported response shape
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const error = buildResponseError(new Response('', { status: 500 }), '')
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
function buildResponseError(response, type) {
|
|
115
|
+
if (type.includes("application/json")) return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained an application/json body that was not a JSON-RPC message`);
|
|
116
|
+
if (type.includes("text/event-stream")) return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained a text/event-stream body without a JSON-RPC message`);
|
|
117
|
+
const shape = type === "" ? "a body without a content type" : `an unsupported '${type}' body`;
|
|
118
|
+
return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained ${shape}`);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
103
121
|
* Creates a readable stream from its pull and cancellation behaviours.
|
|
104
122
|
*
|
|
105
123
|
* @param pull - The behaviour that supplies the stream's next chunk
|
|
@@ -371,6 +389,14 @@ function extractLines(buffer, chunk) {
|
|
|
371
389
|
* The completion callback is the writable channel's backpressure boundary. A callback error and
|
|
372
390
|
* a synchronous `write` throw reject the returned promise with the original value.
|
|
373
391
|
*
|
|
392
|
+
* That callback is the ONLY thing that settles the promise: this helper holds no timer and no
|
|
393
|
+
* abort, so an output that neither confirms nor fails the write parks the promise for as long as
|
|
394
|
+
* the caller-owned stream holds the callback. A caller wanting a bound races this promise against
|
|
395
|
+
* one it owns — {@link import('./transports/StdioServerTransport.js').StdioServerTransport}
|
|
396
|
+
* registers such a bound per send and rejects it on `close()`, so closing the transport settles
|
|
397
|
+
* the CALLER's `send` while the abandoned write stays with the stream that still holds its
|
|
398
|
+
* callback, reachable from nothing the transport retains.
|
|
399
|
+
*
|
|
374
400
|
* @param output - The writable stream that receives the line
|
|
375
401
|
* @param line - The complete line to write
|
|
376
402
|
* @returns Resolves when the stream confirms the write; rejects when the write fails
|
|
@@ -539,7 +565,7 @@ function inferHeaderIssue(request, reference) {
|
|
|
539
565
|
return {
|
|
540
566
|
header: "MCP-Protocol-Version",
|
|
541
567
|
reason: "missing",
|
|
542
|
-
message: `Required MCP-Protocol-Version header is missing; this server offers '${_src_core.
|
|
568
|
+
message: `Required MCP-Protocol-Version header is missing; this server offers '${_src_core.MCP_HANDSHAKE_VERSION}'.`
|
|
543
569
|
};
|
|
544
570
|
}
|
|
545
571
|
const message = reference;
|
|
@@ -586,16 +612,20 @@ function inferHeaderIssue(request, reference) {
|
|
|
586
612
|
*
|
|
587
613
|
* @remarks
|
|
588
614
|
* A supported legacy request is pinned exactly. A modern, malformed, absent, or unsupported
|
|
589
|
-
* request selects the newest supported legacy revision
|
|
615
|
+
* request selects the newest supported legacy revision. The read is deliberately the SAME one
|
|
616
|
+
* {@link import('@orkestrel/mcp').buildInitializeResult} performs — `isMCPLegacyVersion` over
|
|
617
|
+
* the requested revision — because the session version this pins and the version that result
|
|
618
|
+
* echoes must be the one value. Routing through `inferVersion` cannot do it: that inferer is
|
|
619
|
+
* modern-only, so it answers `undefined` for every legacy offer and the session would pin
|
|
620
|
+
* `2025-11-25` while the handshake echoed `2025-06-18`, which the client's own protocol
|
|
621
|
+
* header then contradicts.
|
|
590
622
|
*
|
|
591
623
|
* @param request - The legacy initialize invocation
|
|
592
624
|
* @returns The negotiated legacy protocol revision
|
|
593
625
|
*/
|
|
594
626
|
function inferLegacyVersion(request) {
|
|
595
627
|
const requested = request.params?.["protocolVersion"];
|
|
596
|
-
|
|
597
|
-
if (version !== void 0 && (0, _src_core.inferEra)(version) === "legacy") return version;
|
|
598
|
-
return _src_core.MCP_PROTOCOL_VERSION;
|
|
628
|
+
return (0, _src_core.isMCPLegacyVersion)(requested) ? requested : _src_core.MCP_HANDSHAKE_VERSION;
|
|
599
629
|
}
|
|
600
630
|
/**
|
|
601
631
|
* Infers the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
|
|
@@ -758,7 +788,9 @@ var HTTPDisconnect = class {
|
|
|
758
788
|
* Modern requests require matching protocol/method headers and a matching name header only
|
|
759
789
|
* for `tools/call`; mismatch returns HTTP `400` + `-32020`. Headerless `initialize` is
|
|
760
790
|
* accepted, while every other headerless request needs a live legacy session to supply its
|
|
761
|
-
* pinned version. A
|
|
791
|
+
* pinned version. A legacy-shaped request carrying a protocol header is admitted only for a
|
|
792
|
+
* legacy revision; any other value, the modern revision included, returns HTTP `400` + `-32022`
|
|
793
|
+
* whose `supported` names the legacy revisions this door accepts. A present origin must occur in `origin.origins` unless validation is
|
|
762
794
|
* explicitly delegated upstream. Modern dispatch errors use their protocol status map; legacy
|
|
763
795
|
* errors remain in-band at HTTP `200`. A streamed response composes the fetch-standard request
|
|
764
796
|
* signal with response-body cancellation and supplies the result to every dispatched modern
|
|
@@ -813,8 +845,8 @@ function createMCPPostHandler(mcp, options) {
|
|
|
813
845
|
const issue = inferHeaderIssue(request, invocation);
|
|
814
846
|
if (issue !== void 0) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.MCP_HEADER_MISMATCH, issue.message), { status: 400 });
|
|
815
847
|
if (era === "legacy") {
|
|
816
|
-
if (protocol !== null && !(0, _src_core.
|
|
817
|
-
supported: _src_core.
|
|
848
|
+
if (protocol !== null && !(0, _src_core.isMCPLegacyVersion)(protocol)) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.MCP_UNSUPPORTED_VERSION, `Unsupported MCP protocol version '${protocol}'`, {
|
|
849
|
+
supported: _src_core.SUPPORTED_LEGACY_PROTOCOL_VERSIONS,
|
|
818
850
|
requested: protocol
|
|
819
851
|
}), { status: 400 });
|
|
820
852
|
}
|
|
@@ -882,8 +914,10 @@ function createMCPPostHandler(mcp, options) {
|
|
|
882
914
|
* aborted read surfaces on `error` and the `send` reporting it resolves. `close()` is
|
|
883
915
|
* idempotent (one `close` event per connected lifetime), and `start()` opens the next one.
|
|
884
916
|
* - **Total at the boundary.** Every reply is narrowed (`parseJSONRPCMessage`,
|
|
885
|
-
* the SSE decoder)
|
|
886
|
-
*
|
|
917
|
+
* the SSE decoder). A non-message success reply is dropped, never asserted. A non-success
|
|
918
|
+
* reply that carries no valid JSON-RPC message rejects `send` with its HTTP status and body
|
|
919
|
+
* shape. A valid JSON-RPC error body is emitted at any HTTP status. A `fetch` / decode failure
|
|
920
|
+
* on a success response surfaces on the `error` event rather than escaping `send`.
|
|
887
921
|
* - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); fires
|
|
888
922
|
* `message` per decoded reply, `error` on a fault, and `close` on `close()`.
|
|
889
923
|
*
|
|
@@ -978,18 +1012,20 @@ var HTTPClientTransport = class {
|
|
|
978
1012
|
async #deliver(response) {
|
|
979
1013
|
if (response.status === 202) return;
|
|
980
1014
|
const type = response.headers.get("content-type") ?? "";
|
|
1015
|
+
let messages = [];
|
|
1016
|
+
let failure;
|
|
981
1017
|
try {
|
|
982
|
-
if (type.includes("text/event-stream"))
|
|
983
|
-
|
|
984
|
-
return;
|
|
985
|
-
}
|
|
986
|
-
if (type.includes("application/json")) {
|
|
1018
|
+
if (type.includes("text/event-stream")) messages = await readEventStream(response);
|
|
1019
|
+
else if (type.includes("application/json")) {
|
|
987
1020
|
const message = (0, _src_core.parseJSONRPCMessage)(await response.json());
|
|
988
|
-
if (message !== void 0)
|
|
1021
|
+
if (message !== void 0) messages = [message];
|
|
989
1022
|
}
|
|
990
1023
|
} catch (error) {
|
|
991
|
-
|
|
1024
|
+
failure = { error };
|
|
992
1025
|
}
|
|
1026
|
+
for (const message of messages) this.#capture(message);
|
|
1027
|
+
if (!response.ok && messages.length === 0) throw buildResponseError(response, type);
|
|
1028
|
+
if (failure !== void 0) this.#emitter.emit("error", failure.error);
|
|
993
1029
|
}
|
|
994
1030
|
#capture(message) {
|
|
995
1031
|
if ((0, _src_core.isJSONRPCResponse)(message) && (0, _orkestrel_contract.isRecord)(message.result) && (0, _src_core.isMCPVersion)(message.result["protocolVersion"])) this.#protocol = message.result["protocolVersion"];
|
|
@@ -1240,7 +1276,11 @@ var WebSocketServerTransport = class {
|
|
|
1240
1276
|
* event (the reply the {@link import('@orkestrel/mcp').MCPClientInterface} correlates by `id`); a
|
|
1241
1277
|
* non-JSON / non-message frame surfaces on `error` and is dropped. The socket's `close`
|
|
1242
1278
|
* / `error` bridge to this transport's events.
|
|
1243
|
-
* - **Outbound (`send`).** `send(message)` writes one masked text frame.
|
|
1279
|
+
* - **Outbound (`send`).** `send(message)` writes one masked text frame. A socket write is not
|
|
1280
|
+
* confirmed, so this transport answers a closed channel from its OWN state: a `send` with no
|
|
1281
|
+
* bound socket — before `start()`, after `close()`, or after the peer ended the socket —
|
|
1282
|
+
* REJECTS with `WebSocket transport is not connected`. It neither drops the message (the
|
|
1283
|
+
* browser face's posture) nor queues it for a connection this transport is not holding.
|
|
1244
1284
|
* - **`close()`** unsubscribes from the socket, closes it, and fires `close` (idempotent). An
|
|
1245
1285
|
* upgrade still on the wire is DESTROYED, so a `close()` during the handshake ends the
|
|
1246
1286
|
* transport at once instead of waiting for a peer that may never answer — the suspended
|
|
@@ -1456,7 +1496,7 @@ var WebSocketClientTransport = class {
|
|
|
1456
1496
|
* ```ts
|
|
1457
1497
|
* const transport = new StdioClientTransport({ command: 'node', args: ['./server.js'] })
|
|
1458
1498
|
* const client = new MCPClient({ transport })
|
|
1459
|
-
* await client.connect() // start() spawns the child, then
|
|
1499
|
+
* await client.connect() // start() spawns the child, then modern discovery runs over stdio
|
|
1460
1500
|
* ```
|
|
1461
1501
|
*/
|
|
1462
1502
|
var StdioClientTransport = class {
|
|
@@ -2216,6 +2256,7 @@ exports.WebSocketServerTransport = WebSocketServerTransport;
|
|
|
2216
2256
|
exports.acceptsEventStream = acceptsEventStream;
|
|
2217
2257
|
exports.allowsOrigin = allowsOrigin;
|
|
2218
2258
|
exports.bridgeMessageTransport = bridgeMessageTransport;
|
|
2259
|
+
exports.buildResponseError = buildResponseError;
|
|
2219
2260
|
exports.createHTTPClientTransport = createHTTPClientTransport;
|
|
2220
2261
|
exports.createMCPContinuation = createMCPContinuation;
|
|
2221
2262
|
exports.createMCPPostHandler = createMCPPostHandler;
|