@orkestrel/mcp 0.0.24 → 0.0.26

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.
@@ -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
@@ -547,7 +565,7 @@ function inferHeaderIssue(request, reference) {
547
565
  return {
548
566
  header: "MCP-Protocol-Version",
549
567
  reason: "missing",
550
- message: `Required MCP-Protocol-Version header is missing; this server offers '${_src_core.MCP_PROTOCOL_VERSION}'.`
568
+ message: `Required MCP-Protocol-Version header is missing; this server offers '${_src_core.MCP_HANDSHAKE_VERSION}'.`
551
569
  };
552
570
  }
553
571
  const message = reference;
@@ -594,16 +612,20 @@ function inferHeaderIssue(request, reference) {
594
612
  *
595
613
  * @remarks
596
614
  * A supported legacy request is pinned exactly. A modern, malformed, absent, or unsupported
597
- * request selects the newest supported legacy revision, matching the core initialize result.
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.
598
622
  *
599
623
  * @param request - The legacy initialize invocation
600
624
  * @returns The negotiated legacy protocol revision
601
625
  */
602
626
  function inferLegacyVersion(request) {
603
627
  const requested = request.params?.["protocolVersion"];
604
- const version = (0, _src_core.inferVersion)((0, _orkestrel_contract.isString)(requested) ? [requested] : []);
605
- if (version !== void 0 && (0, _src_core.inferEra)(version) === "legacy") return version;
606
- return _src_core.MCP_PROTOCOL_VERSION;
628
+ return (0, _src_core.isMCPLegacyVersion)(requested) ? requested : _src_core.MCP_HANDSHAKE_VERSION;
607
629
  }
608
630
  /**
609
631
  * Infers the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
@@ -766,7 +788,9 @@ var HTTPDisconnect = class {
766
788
  * Modern requests require matching protocol/method headers and a matching name header only
767
789
  * for `tools/call`; mismatch returns HTTP `400` + `-32020`. Headerless `initialize` is
768
790
  * accepted, while every other headerless request needs a live legacy session to supply its
769
- * pinned version. A present origin must occur in `origin.origins` unless validation is
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
770
794
  * explicitly delegated upstream. Modern dispatch errors use their protocol status map; legacy
771
795
  * errors remain in-band at HTTP `200`. A streamed response composes the fetch-standard request
772
796
  * signal with response-body cancellation and supplies the result to every dispatched modern
@@ -821,8 +845,8 @@ function createMCPPostHandler(mcp, options) {
821
845
  const issue = inferHeaderIssue(request, invocation);
822
846
  if (issue !== void 0) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.MCP_HEADER_MISMATCH, issue.message), { status: 400 });
823
847
  if (era === "legacy") {
824
- if (protocol !== null && !(0, _src_core.isMCPVersion)(protocol)) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.MCP_UNSUPPORTED_VERSION, `Unsupported MCP protocol version '${protocol}'`, {
825
- supported: _src_core.SUPPORTED_PROTOCOL_VERSIONS,
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,
826
850
  requested: protocol
827
851
  }), { status: 400 });
828
852
  }
@@ -890,8 +914,10 @@ function createMCPPostHandler(mcp, options) {
890
914
  * aborted read surfaces on `error` and the `send` reporting it resolves. `close()` is
891
915
  * idempotent (one `close` event per connected lifetime), and `start()` opens the next one.
892
916
  * - **Total at the boundary.** Every reply is narrowed (`parseJSONRPCMessage`,
893
- * the SSE decoder) a non-message reply is dropped, never asserted; a `fetch` /
894
- * decode failure surfaces on the `error` event rather than escaping `send`.
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`.
895
921
  * - **Observable.** Owns the `emitter` ({@link MCPClientTransportEventMap}); fires
896
922
  * `message` per decoded reply, `error` on a fault, and `close` on `close()`.
897
923
  *
@@ -986,18 +1012,20 @@ var HTTPClientTransport = class {
986
1012
  async #deliver(response) {
987
1013
  if (response.status === 202) return;
988
1014
  const type = response.headers.get("content-type") ?? "";
1015
+ let messages = [];
1016
+ let failure;
989
1017
  try {
990
- if (type.includes("text/event-stream")) {
991
- for (const message of await readEventStream(response)) this.#capture(message);
992
- return;
993
- }
994
- if (type.includes("application/json")) {
1018
+ if (type.includes("text/event-stream")) messages = await readEventStream(response);
1019
+ else if (type.includes("application/json")) {
995
1020
  const message = (0, _src_core.parseJSONRPCMessage)(await response.json());
996
- if (message !== void 0) this.#capture(message);
1021
+ if (message !== void 0) messages = [message];
997
1022
  }
998
1023
  } catch (error) {
999
- this.#emitter.emit("error", error);
1024
+ failure = { error };
1000
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);
1001
1029
  }
1002
1030
  #capture(message) {
1003
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"];
@@ -1468,7 +1496,7 @@ var WebSocketClientTransport = class {
1468
1496
  * ```ts
1469
1497
  * const transport = new StdioClientTransport({ command: 'node', args: ['./server.js'] })
1470
1498
  * const client = new MCPClient({ transport })
1471
- * await client.connect() // start() spawns the child, then the MCP initialize runs over stdio
1499
+ * await client.connect() // start() spawns the child, then modern discovery runs over stdio
1472
1500
  * ```
1473
1501
  */
1474
1502
  var StdioClientTransport = class {
@@ -2228,6 +2256,7 @@ exports.WebSocketServerTransport = WebSocketServerTransport;
2228
2256
  exports.acceptsEventStream = acceptsEventStream;
2229
2257
  exports.allowsOrigin = allowsOrigin;
2230
2258
  exports.bridgeMessageTransport = bridgeMessageTransport;
2259
+ exports.buildResponseError = buildResponseError;
2231
2260
  exports.createHTTPClientTransport = createHTTPClientTransport;
2232
2261
  exports.createMCPContinuation = createMCPContinuation;
2233
2262
  exports.createMCPPostHandler = createMCPPostHandler;