@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.
@@ -145,27 +145,6 @@ function allowsOrigin(request, options) {
145
145
  return options?.origins?.includes(parsed.origin) ?? false;
146
146
  }
147
147
  /**
148
- * Whether a modern HTTP request's required standard headers match its JSON-RPC body.
149
- *
150
- * @remarks
151
- * Requires `MCP-Protocol-Version` to equal the reserved `_meta` version and `Mcp-Method`
152
- * to equal `method`. `Mcp-Name` is required only for `tools/call`, where it must equal
153
- * `params.name`; discovery and listing requests need no name because none is derivable.
154
- * Legacy requests return `false` because this predicate models the modern contract only.
155
- *
156
- * @param request - The HTTP request carrying the headers
157
- * @param message - The parsed JSON-RPC request body
158
- * @returns `true` only when every method-applicable modern header matches
159
- */
160
- function matchesModernHeaders(request, message) {
161
- if (!(0, _src_core.isModernRequest)(message)) return false;
162
- const version = ((0, _orkestrel_contract.isRecord)(message.params?.["_meta"]) ? message.params["_meta"] : void 0)?.[_src_core.MCP_META_VERSION];
163
- if (!(0, _orkestrel_contract.isString)(version) || request.headers.get("mcp-protocol-version") !== version || request.headers.get("mcp-method") !== message.method) return false;
164
- if (message.method !== "tools/call") return true;
165
- const name = message.params?.["name"];
166
- return (0, _orkestrel_contract.isString)(name) && request.headers.get("mcp-name") === name;
167
- }
168
- /**
169
148
  * Read the request's `mcp-session-id` header — the session id a stateful transport
170
149
  * validates, or `undefined` when absent.
171
150
  *
@@ -419,6 +398,87 @@ function bridgeMessageTransport(transport) {
419
398
  //#endregion
420
399
  //#region src/server/inferers.ts
421
400
  /**
401
+ * Infer the first required MCP HTTP header that is missing or mismatched.
402
+ *
403
+ * @remarks
404
+ * A modern request derives its protocol, method, and tools/call-only name expectations from
405
+ * the JSON-RPC body. A legacy request body requires a protocol header after initialization,
406
+ * while a supplied legacy session version additionally diagnoses a header that disagrees with
407
+ * the active session. Messages name the expected value but never echo the client-supplied one.
408
+ *
409
+ * @param request - The HTTP request carrying the headers
410
+ * @param reference - The parsed request body, or the active legacy session version
411
+ * @returns The first header issue, or `undefined` when the applicable headers agree
412
+ *
413
+ * @example
414
+ * ```ts
415
+ * const issue = inferHeaderIssue(request, rpcRequest)
416
+ * issue?.header // 'Mcp-Method' when that field is absent or mismatched
417
+ * ```
418
+ */
419
+ function inferHeaderIssue(request, reference) {
420
+ const protocol = request.headers.get(MCP_PROTOCOL_VERSION_HEADER);
421
+ if ((0, _orkestrel_contract.isString)(reference)) {
422
+ if (protocol === null) return {
423
+ header: "MCP-Protocol-Version",
424
+ reason: "missing",
425
+ message: `Required MCP-Protocol-Version header is missing; the active session uses '${reference}'.`
426
+ };
427
+ if (protocol !== reference) return {
428
+ header: "MCP-Protocol-Version",
429
+ reason: "mismatched",
430
+ message: `MCP-Protocol-Version header does not match the active session version '${reference}'.`
431
+ };
432
+ return;
433
+ }
434
+ if (!(0, _src_core.isModernRequest)(reference)) {
435
+ if ((0, _src_core.isInitializeRequest)(reference) || protocol !== null) return void 0;
436
+ return {
437
+ header: "MCP-Protocol-Version",
438
+ reason: "missing",
439
+ message: `Required MCP-Protocol-Version header is missing; this server offers '${_src_core.MCP_PROTOCOL_VERSION}'.`
440
+ };
441
+ }
442
+ const message = reference;
443
+ const version = ((0, _orkestrel_contract.isRecord)(message.params?.["_meta"]) ? message.params["_meta"] : void 0)?.[_src_core.MCP_META_VERSION];
444
+ if (!(0, _orkestrel_contract.isString)(version)) return void 0;
445
+ if (protocol === null) return {
446
+ header: "MCP-Protocol-Version",
447
+ reason: "missing",
448
+ message: `Required MCP-Protocol-Version header is missing; the request body version is '${version}'.`
449
+ };
450
+ if (protocol !== version) return {
451
+ header: "MCP-Protocol-Version",
452
+ reason: "mismatched",
453
+ message: `MCP-Protocol-Version header does not match the request body version '${version}'.`
454
+ };
455
+ const method = request.headers.get(MCP_METHOD_HEADER);
456
+ if (method === null) return {
457
+ header: "Mcp-Method",
458
+ reason: "missing",
459
+ message: `Required Mcp-Method header is missing; the request body method is '${message.method}'.`
460
+ };
461
+ if (method !== message.method) return {
462
+ header: "Mcp-Method",
463
+ reason: "mismatched",
464
+ message: `Mcp-Method header does not match the request body method '${message.method}'.`
465
+ };
466
+ if (message.method !== "tools/call") return void 0;
467
+ const name = message.params?.["name"];
468
+ if (!(0, _orkestrel_contract.isString)(name)) return void 0;
469
+ const header = request.headers.get(MCP_NAME_HEADER);
470
+ if (header === null) return {
471
+ header: "Mcp-Name",
472
+ reason: "missing",
473
+ message: `Required Mcp-Name header is missing; the request body tool name is '${name}'.`
474
+ };
475
+ if (header !== name) return {
476
+ header: "Mcp-Name",
477
+ reason: "mismatched",
478
+ message: `Mcp-Name header does not match the request body tool name '${name}'.`
479
+ };
480
+ }
481
+ /**
422
482
  * Infer the legacy revision an `initialize` request negotiates.
423
483
  *
424
484
  * @remarks
@@ -590,9 +650,10 @@ function createMCPPostHandler(mcp, options) {
590
650
  const protocol = request.headers.get(MCP_PROTOCOL_VERSION_HEADER);
591
651
  if (era === "modern") {
592
652
  if ((0, _src_core.parseRequestContext)(rpcRequest) === void 0) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.JSONRPC_INVALID_PARAMS, "Invalid params: malformed modern request metadata"), { status: 400 });
593
- if (!matchesModernHeaders(request, rpcRequest)) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.MCP_HEADER_MISMATCH, "MCP request headers do not match the request body"), { status: 400 });
594
- } else {
595
- if (protocol === null && !(0, _src_core.isInitializeRequest)(rpcRequest)) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.MCP_HEADER_MISMATCH, "MCP request headers do not match the request body"), { status: 400 });
653
+ }
654
+ const issue = inferHeaderIssue(request, rpcRequest);
655
+ if (issue !== void 0) return Response.json((0, _src_core.buildJSONRPCError)(id, _src_core.MCP_HEADER_MISMATCH, issue.message), { status: 400 });
656
+ if (era === "legacy") {
596
657
  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}'`, {
597
658
  supported: _src_core.SUPPORTED_PROTOCOL_VERSIONS,
598
659
  requested: protocol
@@ -1691,11 +1752,11 @@ function createMCPSession(options) {
1691
1752
  if (!Reflect.set(context.state, "session", entry.session)) throw new Error("MCP session state is not writable");
1692
1753
  const headers = new Headers(request.headers);
1693
1754
  if (parsed === void 0 || !(0, _src_core.isInitializeRequest)(parsed)) {
1694
- const protocol = headers.get(MCP_PROTOCOL_VERSION_HEADER);
1695
- if (protocol === null) headers.set(MCP_PROTOCOL_VERSION_HEADER, entry.version);
1696
- else if (protocol !== entry.version) {
1755
+ const issue = inferHeaderIssue(request, entry.version);
1756
+ if (issue?.reason === "missing") headers.set(MCP_PROTOCOL_VERSION_HEADER, entry.version);
1757
+ else if (issue !== void 0) {
1697
1758
  const requestId = parsed !== void 0 && "method" in parsed ? parsed.id ?? null : null;
1698
- return Response.json((0, _src_core.buildJSONRPCError)(requestId, _src_core.MCP_HEADER_MISMATCH, "MCP protocol version does not match the active session"), { status: 400 });
1759
+ return Response.json((0, _src_core.buildJSONRPCError)(requestId, _src_core.MCP_HEADER_MISMATCH, issue.message), { status: 400 });
1699
1760
  }
1700
1761
  }
1701
1762
  const response = await next(new Request(context.url, {
@@ -1746,9 +1807,9 @@ exports.createWebSocketServer = createWebSocketServer;
1746
1807
  exports.decodeEvent = decodeEvent;
1747
1808
  exports.dispatchLines = dispatchLines;
1748
1809
  exports.extractLines = extractLines;
1810
+ exports.inferHeaderIssue = inferHeaderIssue;
1749
1811
  exports.inferLegacyVersion = inferLegacyVersion;
1750
1812
  exports.inferStatus = inferStatus;
1751
- exports.matchesModernHeaders = matchesModernHeaders;
1752
1813
  exports.readEventStream = readEventStream;
1753
1814
  exports.readLastEventId = readLastEventId;
1754
1815
  exports.readSessionHeader = readSessionHeader;