@orkestrel/mcp 0.0.10 → 0.0.12

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.
@@ -677,6 +677,27 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
677
677
  readonly path?: string;
678
678
  }
679
679
 
680
+ /**
681
+ * Infer the first required MCP HTTP header that is missing or mismatched.
682
+ *
683
+ * @remarks
684
+ * A modern request derives its protocol, method, and tools/call-only name expectations from
685
+ * the JSON-RPC body. A legacy request body requires a protocol header after initialization,
686
+ * while a supplied legacy session version additionally diagnoses a header that disagrees with
687
+ * the active session. Messages name the expected value but never echo the client-supplied one.
688
+ *
689
+ * @param request - The HTTP request carrying the headers
690
+ * @param reference - The parsed request body, or the active legacy session version
691
+ * @returns The first header issue, or `undefined` when the applicable headers agree
692
+ *
693
+ * @example
694
+ * ```ts
695
+ * const issue = inferHeaderIssue(request, rpcRequest)
696
+ * issue?.header // 'Mcp-Method' when that field is absent or mismatched
697
+ * ```
698
+ */
699
+ export declare function inferHeaderIssue(request: Request, reference: JSONRPCRequest | MCPVersion): MCPHeaderIssue | undefined;
700
+
680
701
  /**
681
702
  * Infer the legacy revision an `initialize` request negotiates.
682
703
  *
@@ -718,21 +739,6 @@ export declare interface LineExtraction {
718
739
  readonly remainder: string;
719
740
  }
720
741
 
721
- /**
722
- * Whether a modern HTTP request's required standard headers match its JSON-RPC body.
723
- *
724
- * @remarks
725
- * Requires `MCP-Protocol-Version` to equal the reserved `_meta` version and `Mcp-Method`
726
- * to equal `method`. `Mcp-Name` is required only for `tools/call`, where it must equal
727
- * `params.name`; discovery and listing requests need no name because none is derivable.
728
- * Legacy requests return `false` because this predicate models the modern contract only.
729
- *
730
- * @param request - The HTTP request carrying the headers
731
- * @param message - The parsed JSON-RPC request body
732
- * @returns `true` only when every method-applicable modern header matches
733
- */
734
- export declare function matchesModernHeaders(request: Request, message: JSONRPCRequest): boolean;
735
-
736
742
  /** The modern Streamable-HTTP request header carrying the JSON-RPC method name. */
737
743
  export declare const MCP_METHOD_HEADER = "mcp-method";
738
744
 
@@ -788,6 +794,21 @@ export declare const MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
788
794
  */
789
795
  export declare type MCPCallerHandler<TState = unknown> = (request: Request, context: RouteContext<string, TState> | undefined) => unknown;
790
796
 
797
+ /**
798
+ * One required MCP HTTP header that is absent or disagrees with its server-derived value.
799
+ *
800
+ * @remarks
801
+ * - `header` — the canonical HTTP field name safe to show to an integrator.
802
+ * - `reason` — whether the field is absent or carries a mismatched value.
803
+ * - `message` — the refusal message naming the expected body, session, or server value without
804
+ * echoing the client-supplied header value.
805
+ */
806
+ export declare interface MCPHeaderIssue {
807
+ readonly header: 'MCP-Protocol-Version' | 'Mcp-Method' | 'Mcp-Name';
808
+ readonly reason: 'missing' | 'mismatched';
809
+ readonly message: string;
810
+ }
811
+
791
812
  /**
792
813
  * Shared SSE keepalive options for held-open HTTP responses.
793
814
  *
@@ -677,6 +677,27 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
677
677
  readonly path?: string;
678
678
  }
679
679
 
680
+ /**
681
+ * Infer the first required MCP HTTP header that is missing or mismatched.
682
+ *
683
+ * @remarks
684
+ * A modern request derives its protocol, method, and tools/call-only name expectations from
685
+ * the JSON-RPC body. A legacy request body requires a protocol header after initialization,
686
+ * while a supplied legacy session version additionally diagnoses a header that disagrees with
687
+ * the active session. Messages name the expected value but never echo the client-supplied one.
688
+ *
689
+ * @param request - The HTTP request carrying the headers
690
+ * @param reference - The parsed request body, or the active legacy session version
691
+ * @returns The first header issue, or `undefined` when the applicable headers agree
692
+ *
693
+ * @example
694
+ * ```ts
695
+ * const issue = inferHeaderIssue(request, rpcRequest)
696
+ * issue?.header // 'Mcp-Method' when that field is absent or mismatched
697
+ * ```
698
+ */
699
+ export declare function inferHeaderIssue(request: Request, reference: JSONRPCRequest | MCPVersion): MCPHeaderIssue | undefined;
700
+
680
701
  /**
681
702
  * Infer the legacy revision an `initialize` request negotiates.
682
703
  *
@@ -718,21 +739,6 @@ export declare interface LineExtraction {
718
739
  readonly remainder: string;
719
740
  }
720
741
 
721
- /**
722
- * Whether a modern HTTP request's required standard headers match its JSON-RPC body.
723
- *
724
- * @remarks
725
- * Requires `MCP-Protocol-Version` to equal the reserved `_meta` version and `Mcp-Method`
726
- * to equal `method`. `Mcp-Name` is required only for `tools/call`, where it must equal
727
- * `params.name`; discovery and listing requests need no name because none is derivable.
728
- * Legacy requests return `false` because this predicate models the modern contract only.
729
- *
730
- * @param request - The HTTP request carrying the headers
731
- * @param message - The parsed JSON-RPC request body
732
- * @returns `true` only when every method-applicable modern header matches
733
- */
734
- export declare function matchesModernHeaders(request: Request, message: JSONRPCRequest): boolean;
735
-
736
742
  /** The modern Streamable-HTTP request header carrying the JSON-RPC method name. */
737
743
  export declare const MCP_METHOD_HEADER = "mcp-method";
738
744
 
@@ -788,6 +794,21 @@ export declare const MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
788
794
  */
789
795
  export declare type MCPCallerHandler<TState = unknown> = (request: Request, context: RouteContext<string, TState> | undefined) => unknown;
790
796
 
797
+ /**
798
+ * One required MCP HTTP header that is absent or disagrees with its server-derived value.
799
+ *
800
+ * @remarks
801
+ * - `header` — the canonical HTTP field name safe to show to an integrator.
802
+ * - `reason` — whether the field is absent or carries a mismatched value.
803
+ * - `message` — the refusal message naming the expected body, session, or server value without
804
+ * echoing the client-supplied header value.
805
+ */
806
+ export declare interface MCPHeaderIssue {
807
+ readonly header: 'MCP-Protocol-Version' | 'Mcp-Method' | 'Mcp-Name';
808
+ readonly reason: 'missing' | 'mismatched';
809
+ readonly message: string;
810
+ }
811
+
791
812
  /**
792
813
  * Shared SSE keepalive options for held-open HTTP responses.
793
814
  *
@@ -144,27 +144,6 @@ function allowsOrigin(request, options) {
144
144
  return options?.origins?.includes(parsed.origin) ?? false;
145
145
  }
146
146
  /**
147
- * Whether a modern HTTP request's required standard headers match its JSON-RPC body.
148
- *
149
- * @remarks
150
- * Requires `MCP-Protocol-Version` to equal the reserved `_meta` version and `Mcp-Method`
151
- * to equal `method`. `Mcp-Name` is required only for `tools/call`, where it must equal
152
- * `params.name`; discovery and listing requests need no name because none is derivable.
153
- * Legacy requests return `false` because this predicate models the modern contract only.
154
- *
155
- * @param request - The HTTP request carrying the headers
156
- * @param message - The parsed JSON-RPC request body
157
- * @returns `true` only when every method-applicable modern header matches
158
- */
159
- function matchesModernHeaders(request, message) {
160
- if (!isModernRequest(message)) return false;
161
- const version = (isRecord(message.params?.["_meta"]) ? message.params["_meta"] : void 0)?.[MCP_META_VERSION];
162
- if (!isString(version) || request.headers.get("mcp-protocol-version") !== version || request.headers.get("mcp-method") !== message.method) return false;
163
- if (message.method !== "tools/call") return true;
164
- const name = message.params?.["name"];
165
- return isString(name) && request.headers.get("mcp-name") === name;
166
- }
167
- /**
168
147
  * Read the request's `mcp-session-id` header — the session id a stateful transport
169
148
  * validates, or `undefined` when absent.
170
149
  *
@@ -418,6 +397,87 @@ function bridgeMessageTransport(transport) {
418
397
  //#endregion
419
398
  //#region src/server/inferers.ts
420
399
  /**
400
+ * Infer the first required MCP HTTP header that is missing or mismatched.
401
+ *
402
+ * @remarks
403
+ * A modern request derives its protocol, method, and tools/call-only name expectations from
404
+ * the JSON-RPC body. A legacy request body requires a protocol header after initialization,
405
+ * while a supplied legacy session version additionally diagnoses a header that disagrees with
406
+ * the active session. Messages name the expected value but never echo the client-supplied one.
407
+ *
408
+ * @param request - The HTTP request carrying the headers
409
+ * @param reference - The parsed request body, or the active legacy session version
410
+ * @returns The first header issue, or `undefined` when the applicable headers agree
411
+ *
412
+ * @example
413
+ * ```ts
414
+ * const issue = inferHeaderIssue(request, rpcRequest)
415
+ * issue?.header // 'Mcp-Method' when that field is absent or mismatched
416
+ * ```
417
+ */
418
+ function inferHeaderIssue(request, reference) {
419
+ const protocol = request.headers.get(MCP_PROTOCOL_VERSION_HEADER);
420
+ if (isString(reference)) {
421
+ if (protocol === null) return {
422
+ header: "MCP-Protocol-Version",
423
+ reason: "missing",
424
+ message: `Required MCP-Protocol-Version header is missing; the active session uses '${reference}'.`
425
+ };
426
+ if (protocol !== reference) return {
427
+ header: "MCP-Protocol-Version",
428
+ reason: "mismatched",
429
+ message: `MCP-Protocol-Version header does not match the active session version '${reference}'.`
430
+ };
431
+ return;
432
+ }
433
+ if (!isModernRequest(reference)) {
434
+ if (isInitializeRequest(reference) || protocol !== null) return void 0;
435
+ return {
436
+ header: "MCP-Protocol-Version",
437
+ reason: "missing",
438
+ message: `Required MCP-Protocol-Version header is missing; this server offers '${MCP_PROTOCOL_VERSION}'.`
439
+ };
440
+ }
441
+ const message = reference;
442
+ const version = (isRecord(message.params?.["_meta"]) ? message.params["_meta"] : void 0)?.[MCP_META_VERSION];
443
+ if (!isString(version)) return void 0;
444
+ if (protocol === null) return {
445
+ header: "MCP-Protocol-Version",
446
+ reason: "missing",
447
+ message: `Required MCP-Protocol-Version header is missing; the request body version is '${version}'.`
448
+ };
449
+ if (protocol !== version) return {
450
+ header: "MCP-Protocol-Version",
451
+ reason: "mismatched",
452
+ message: `MCP-Protocol-Version header does not match the request body version '${version}'.`
453
+ };
454
+ const method = request.headers.get(MCP_METHOD_HEADER);
455
+ if (method === null) return {
456
+ header: "Mcp-Method",
457
+ reason: "missing",
458
+ message: `Required Mcp-Method header is missing; the request body method is '${message.method}'.`
459
+ };
460
+ if (method !== message.method) return {
461
+ header: "Mcp-Method",
462
+ reason: "mismatched",
463
+ message: `Mcp-Method header does not match the request body method '${message.method}'.`
464
+ };
465
+ if (message.method !== "tools/call") return void 0;
466
+ const name = message.params?.["name"];
467
+ if (!isString(name)) return void 0;
468
+ const header = request.headers.get(MCP_NAME_HEADER);
469
+ if (header === null) return {
470
+ header: "Mcp-Name",
471
+ reason: "missing",
472
+ message: `Required Mcp-Name header is missing; the request body tool name is '${name}'.`
473
+ };
474
+ if (header !== name) return {
475
+ header: "Mcp-Name",
476
+ reason: "mismatched",
477
+ message: `Mcp-Name header does not match the request body tool name '${name}'.`
478
+ };
479
+ }
480
+ /**
421
481
  * Infer the legacy revision an `initialize` request negotiates.
422
482
  *
423
483
  * @remarks
@@ -589,9 +649,10 @@ function createMCPPostHandler(mcp, options) {
589
649
  const protocol = request.headers.get(MCP_PROTOCOL_VERSION_HEADER);
590
650
  if (era === "modern") {
591
651
  if (parseRequestContext(rpcRequest) === void 0) return Response.json(buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: malformed modern request metadata"), { status: 400 });
592
- if (!matchesModernHeaders(request, rpcRequest)) return Response.json(buildJSONRPCError(id, MCP_HEADER_MISMATCH, "MCP request headers do not match the request body"), { status: 400 });
593
- } else {
594
- if (protocol === null && !isInitializeRequest(rpcRequest)) return Response.json(buildJSONRPCError(id, MCP_HEADER_MISMATCH, "MCP request headers do not match the request body"), { status: 400 });
652
+ }
653
+ const issue = inferHeaderIssue(request, rpcRequest);
654
+ if (issue !== void 0) return Response.json(buildJSONRPCError(id, MCP_HEADER_MISMATCH, issue.message), { status: 400 });
655
+ if (era === "legacy") {
595
656
  if (protocol !== null && !isMCPVersion(protocol)) return Response.json(buildJSONRPCError(id, MCP_UNSUPPORTED_VERSION, `Unsupported MCP protocol version '${protocol}'`, {
596
657
  supported: SUPPORTED_PROTOCOL_VERSIONS,
597
658
  requested: protocol
@@ -1690,11 +1751,11 @@ function createMCPSession(options) {
1690
1751
  if (!Reflect.set(context.state, "session", entry.session)) throw new Error("MCP session state is not writable");
1691
1752
  const headers = new Headers(request.headers);
1692
1753
  if (parsed === void 0 || !isInitializeRequest(parsed)) {
1693
- const protocol = headers.get(MCP_PROTOCOL_VERSION_HEADER);
1694
- if (protocol === null) headers.set(MCP_PROTOCOL_VERSION_HEADER, entry.version);
1695
- else if (protocol !== entry.version) {
1754
+ const issue = inferHeaderIssue(request, entry.version);
1755
+ if (issue?.reason === "missing") headers.set(MCP_PROTOCOL_VERSION_HEADER, entry.version);
1756
+ else if (issue !== void 0) {
1696
1757
  const requestId = parsed !== void 0 && "method" in parsed ? parsed.id ?? null : null;
1697
- return Response.json(buildJSONRPCError(requestId, MCP_HEADER_MISMATCH, "MCP protocol version does not match the active session"), { status: 400 });
1758
+ return Response.json(buildJSONRPCError(requestId, MCP_HEADER_MISMATCH, issue.message), { status: 400 });
1698
1759
  }
1699
1760
  }
1700
1761
  const response = await next(new Request(context.url, {
@@ -1712,6 +1773,6 @@ function createMCPSession(options) {
1712
1773
  };
1713
1774
  }
1714
1775
  //#endregion
1715
- export { DEFAULT_MCP_KEEPALIVE_INTERVAL, DEFAULT_MCP_PATH, DEFAULT_MCP_SESSION_CAPACITY, DEFAULT_MCP_SESSION_TTL, HTTPClientTransport, MCPSession, MCP_METHOD_HEADER, MCP_NAME_HEADER, MCP_PROTOCOL_VERSION_HEADER, MCP_SESSION_HEADER, MCP_WEBSOCKET_SUBPROTOCOL, SSE_BUFFERING_DISABLED, SSE_BUFFERING_HEADER, SSE_KEEPALIVE_COMMENT, StdioClientTransport, StdioServerTransport, WebSocketClientTransport, WebSocketServerTransport, acceptsEventStream, allowsOrigin, bridgeMessageTransport, createHTTPClientTransport, createMCPPostHandler, createMCPRoutes, createMCPSession, createReadableStream, createStdioClientTransport, createStdioServer, createWebSocketClientTransport, createWebSocketServer, decodeEvent, dispatchLines, extractLines, inferLegacyVersion, inferStatus, matchesModernHeaders, readEventStream, readLastEventId, readSessionHeader, rejectUnknownSession, upgradeRequestPath };
1776
+ export { DEFAULT_MCP_KEEPALIVE_INTERVAL, DEFAULT_MCP_PATH, DEFAULT_MCP_SESSION_CAPACITY, DEFAULT_MCP_SESSION_TTL, HTTPClientTransport, MCPSession, MCP_METHOD_HEADER, MCP_NAME_HEADER, MCP_PROTOCOL_VERSION_HEADER, MCP_SESSION_HEADER, MCP_WEBSOCKET_SUBPROTOCOL, SSE_BUFFERING_DISABLED, SSE_BUFFERING_HEADER, SSE_KEEPALIVE_COMMENT, StdioClientTransport, StdioServerTransport, WebSocketClientTransport, WebSocketServerTransport, acceptsEventStream, allowsOrigin, bridgeMessageTransport, createHTTPClientTransport, createMCPPostHandler, createMCPRoutes, createMCPSession, createReadableStream, createStdioClientTransport, createStdioServer, createWebSocketClientTransport, createWebSocketServer, decodeEvent, dispatchLines, extractLines, inferHeaderIssue, inferLegacyVersion, inferStatus, readEventStream, readLastEventId, readSessionHeader, rejectUnknownSession, upgradeRequestPath };
1716
1777
 
1717
1778
  //# sourceMappingURL=index.js.map