@orkestrel/mcp 0.0.36 → 0.0.38

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.
@@ -170,8 +170,8 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
170
170
  * comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
171
171
  * A protocol header naming a modern revision holds the request to that revision whatever shape
172
172
  * its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
173
- * Headerless `initialize` is accepted, while every other headerless request needs a live legacy
174
- * session to supply its pinned version. A legacy-shaped request carrying a protocol header is
173
+ * Headerless `initialize` and id-bearing legacy `ping` are accepted; other headerless requests
174
+ * need a live legacy session to supply its pinned version. A legacy-shaped request carrying a protocol header is
175
175
  * otherwise admitted only for a legacy revision; a revision this server does not implement
176
176
  * returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
177
177
  * A present origin must occur in `origin.origins` unless validation is
@@ -282,7 +282,9 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
282
282
  * readSessionHeader}: a valid id touches the entry and sets `context.state.session`; an
283
283
  * absent / unknown id whose (guarded) body parses to an `initialize` request ({@link
284
284
  * isInitializeRequest}) mints a fresh {@link MCPSession} (`crypto.randomUUID()`, the `session`
285
- * options group) and sets `context.state.session`; neither → {@link rejectUnknownSession}
285
+ * options group) and sets `context.state.session`. An id-bearing legacy `ping` with no
286
+ * session header passes through without session state, a supplied protocol header, or a
287
+ * response stamp. Other unresolved requests → {@link rejectUnknownSession}
286
288
  * (`404`). The
287
289
  * minted entry pins the negotiated legacy revision, which is supplied to a later headerless
288
290
  * live-session request. It then
@@ -713,8 +715,8 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
713
715
  * {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
714
716
  * to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
715
717
  * and therefore mismatches, which is how an invalid header value is refused. A legacy request
716
- * body requires a protocol header after initialization. Messages name the expected value but
717
- * never echo the client-supplied one.
718
+ * body requires a protocol header except for `initialize` and an id-bearing legacy `ping`.
719
+ * Messages name the expected value but never echo the client-supplied one.
718
720
  *
719
721
  * The expectation a live session supplies is a different rule over a different input, so it
720
722
  * is {@link inferSessionHeaderIssue} rather than a second arm of this one.
@@ -170,8 +170,8 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
170
170
  * comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
171
171
  * A protocol header naming a modern revision holds the request to that revision whatever shape
172
172
  * its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
173
- * Headerless `initialize` is accepted, while every other headerless request needs a live legacy
174
- * session to supply its pinned version. A legacy-shaped request carrying a protocol header is
173
+ * Headerless `initialize` and id-bearing legacy `ping` are accepted; other headerless requests
174
+ * need a live legacy session to supply its pinned version. A legacy-shaped request carrying a protocol header is
175
175
  * otherwise admitted only for a legacy revision; a revision this server does not implement
176
176
  * returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
177
177
  * A present origin must occur in `origin.origins` unless validation is
@@ -282,7 +282,9 @@ export declare function createMCPRoutes<TState = unknown>(mcp: MCPDispatcherInte
282
282
  * readSessionHeader}: a valid id touches the entry and sets `context.state.session`; an
283
283
  * absent / unknown id whose (guarded) body parses to an `initialize` request ({@link
284
284
  * isInitializeRequest}) mints a fresh {@link MCPSession} (`crypto.randomUUID()`, the `session`
285
- * options group) and sets `context.state.session`; neither → {@link rejectUnknownSession}
285
+ * options group) and sets `context.state.session`. An id-bearing legacy `ping` with no
286
+ * session header passes through without session state, a supplied protocol header, or a
287
+ * response stamp. Other unresolved requests → {@link rejectUnknownSession}
286
288
  * (`404`). The
287
289
  * minted entry pins the negotiated legacy revision, which is supplied to a later headerless
288
290
  * live-session request. It then
@@ -713,8 +715,8 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
713
715
  * {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
714
716
  * to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
715
717
  * and therefore mismatches, which is how an invalid header value is refused. A legacy request
716
- * body requires a protocol header after initialization. Messages name the expected value but
717
- * never echo the client-supplied one.
718
+ * body requires a protocol header except for `initialize` and an id-bearing legacy `ping`.
719
+ * Messages name the expected value but never echo the client-supplied one.
718
720
  *
719
721
  * The expectation a live session supplies is a different rule over a different input, so it
720
722
  * is {@link inferSessionHeaderIssue} rather than a second arm of this one.
@@ -1,5 +1,5 @@
1
1
  import { PROCESS_GRACE } from "@orkestrel/process";
2
- import { HTTPClientTransport, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, MCP_HANDSHAKE_VERSION, MCP_HEADER_MISMATCH, MCP_LOOKUP_PAGES, MCP_META_VERSION, MCP_METHOD_HEADER, MCP_MISSING_CAPABILITY, MCP_NAME_HEADER, MCP_PARAM_PREFIX, MCP_PROTOCOL_VERSION_HEADER, MCP_SESSION_HEADER, MCP_UNSUPPORTED_VERSION, MCP_WEBSOCKET_SUBPROTOCOL, SUPPORTED_LEGACY_PROTOCOL_VERSIONS, bindServer, buildHeaderParameters, buildJSONRPCError, decodeEvent, decodeSentinel, deliverMessage, extractToolSchema, inferRequestEra, isInitializeRequest, isJSONRPCInvocation, isMCPLegacyVersion, isMCPModernVersion, isModernRequest, parseJSONRPCMessage, parseRequestContext, renderHeaderValue } from "../core/index.js";
2
+ import { HTTPClientTransport, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, MCP_HANDSHAKE_VERSION, MCP_HEADER_MISMATCH, MCP_LOOKUP_PAGES, MCP_META_VERSION, MCP_METHOD_HEADER, MCP_MISSING_CAPABILITY, MCP_NAME_HEADER, MCP_PARAM_PREFIX, MCP_PROTOCOL_VERSION_HEADER, MCP_SESSION_HEADER, MCP_UNSUPPORTED_VERSION, MCP_WEBSOCKET_SUBPROTOCOL, SUPPORTED_LEGACY_PROTOCOL_VERSIONS, bindServer, buildHeaderParameters, buildJSONRPCError, decodeEvent, decodeSentinel, deliverMessage, extractToolSchema, inferRequestEra, isInitializeRequest, isJSONRPCInvocation, isMCPLegacyVersion, isMCPModernVersion, isModernRequest, isPingRequest, parseJSONRPCMessage, parseRequestContext, renderHeaderValue } from "../core/index.js";
3
3
  import { isError, isObject, isRecord, isString, parseJSON, sanitizeBudget } from "@orkestrel/contract";
4
4
  import { createStream, signToken, verifyToken } from "@orkestrel/server";
5
5
  import { Emitter } from "@orkestrel/emitter";
@@ -371,8 +371,8 @@ function inferHeaderTarget(request) {
371
371
  * {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
372
372
  * to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
373
373
  * and therefore mismatches, which is how an invalid header value is refused. A legacy request
374
- * body requires a protocol header after initialization. Messages name the expected value but
375
- * never echo the client-supplied one.
374
+ * body requires a protocol header except for `initialize` and an id-bearing legacy `ping`.
375
+ * Messages name the expected value but never echo the client-supplied one.
376
376
  *
377
377
  * The expectation a live session supplies is a different rule over a different input, so it
378
378
  * is {@link inferSessionHeaderIssue} rather than a second arm of this one.
@@ -390,7 +390,7 @@ function inferHeaderTarget(request) {
390
390
  function inferHeaderIssue(request, invocation) {
391
391
  const protocol = request.headers.get(MCP_PROTOCOL_VERSION_HEADER);
392
392
  if (!isModernRequest(invocation)) {
393
- if (isInitializeRequest(invocation) || protocol !== null) return void 0;
393
+ if (isInitializeRequest(invocation) || isPingRequest(invocation) || protocol !== null) return void 0;
394
394
  return {
395
395
  header: "MCP-Protocol-Version",
396
396
  reason: "missing",
@@ -718,8 +718,8 @@ var HTTPDisconnect = class {
718
718
  * comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
719
719
  * A protocol header naming a modern revision holds the request to that revision whatever shape
720
720
  * its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
721
- * Headerless `initialize` is accepted, while every other headerless request needs a live legacy
722
- * session to supply its pinned version. A legacy-shaped request carrying a protocol header is
721
+ * Headerless `initialize` and id-bearing legacy `ping` are accepted; other headerless requests
722
+ * need a live legacy session to supply its pinned version. A legacy-shaped request carrying a protocol header is
723
723
  * otherwise admitted only for a legacy revision; a revision this server does not implement
724
724
  * returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
725
725
  * A present origin must occur in `origin.origins` unless validation is
@@ -1978,7 +1978,9 @@ function createStdioServer(mcp, options) {
1978
1978
  * readSessionHeader}: a valid id touches the entry and sets `context.state.session`; an
1979
1979
  * absent / unknown id whose (guarded) body parses to an `initialize` request ({@link
1980
1980
  * isInitializeRequest}) mints a fresh {@link MCPSession} (`crypto.randomUUID()`, the `session`
1981
- * options group) and sets `context.state.session`; neither → {@link rejectUnknownSession}
1981
+ * options group) and sets `context.state.session`. An id-bearing legacy `ping` with no
1982
+ * session header passes through without session state, a supplied protocol header, or a
1983
+ * response stamp. Other unresolved requests → {@link rejectUnknownSession}
1982
1984
  * (`404`). The
1983
1985
  * minted entry pins the negotiated legacy revision, which is supplied to a later headerless
1984
1986
  * live-session request. It then
@@ -2107,7 +2109,13 @@ function createMCPSession(options) {
2107
2109
  version: inferLegacyVersion(parsed)
2108
2110
  };
2109
2111
  entry = created;
2110
- } else return rejectUnknownSession();
2112
+ } else if (id === void 0 && isPingRequest(parsed)) return next(new Request(context.url, {
2113
+ method: "POST",
2114
+ headers: request.headers,
2115
+ body: text,
2116
+ signal: request.signal
2117
+ }));
2118
+ else return rejectUnknownSession();
2111
2119
  }
2112
2120
  if (!Reflect.set(context.state, "session", entry.session)) throw new Error("MCP session state is not writable");
2113
2121
  const headers = new Headers(request.headers);