@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
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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
|
|
1695
|
-
if (
|
|
1696
|
-
else if (
|
|
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,
|
|
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;
|