@orkestrel/mcp 0.0.26 → 0.0.27

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.
@@ -11,6 +11,7 @@ import { MCPClientTransportInterface as MCPClientTransportInterface_2 } from '@o
11
11
  import { MCPContinuationInterface } from '@orkestrel/mcp';
12
12
  import { MCPDispatcherInterface } from '@orkestrel/mcp';
13
13
  import { MCPEra } from '@orkestrel/mcp';
14
+ import { MCPHeaderParameter } from '@orkestrel/mcp';
14
15
  import { MCPLegacyVersion } from '@orkestrel/mcp';
15
16
  import { MCPStreamControllerInterface } from '@orkestrel/mcp';
16
17
  import { MCPTransportInterface } from '@orkestrel/mcp';
@@ -172,12 +173,17 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
172
173
  * Creates the Streamable-HTTP POST handler used by `createMCPRoutes`.
173
174
  *
174
175
  * @remarks
175
- * Modern requests require matching protocol/method headers and a matching name header only
176
- * for `tools/call`; mismatch returns HTTP `400` + `-32020`. Headerless `initialize` is
177
- * accepted, while every other headerless request needs a live legacy session to supply its
178
- * pinned version. A legacy-shaped request carrying a protocol header is admitted only for a
179
- * legacy revision; any other value, the modern revision included, returns HTTP `400` + `-32022`
180
- * whose `supported` names the legacy revisions this door accepts. A present origin must occur in `origin.origins` unless validation is
176
+ * Modern requests require matching protocol/method headers and a matching name header on each
177
+ * method carrying a named target — `tools/call` and `prompts/get` against `params.name`,
178
+ * `resources/read` against `params.uri` with a Base64-sentinel value decoded before the
179
+ * comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
180
+ * A protocol header naming a MODERN revision holds the request to that revision whatever shape
181
+ * its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
182
+ * Headerless `initialize` is accepted, while every other headerless request needs a live legacy
183
+ * session to supply its pinned version. A legacy-shaped request carrying a protocol header is
184
+ * otherwise admitted only for a legacy revision; a revision this server does not implement
185
+ * returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
186
+ * A present origin must occur in `origin.origins` unless validation is
181
187
  * explicitly delegated upstream. Modern dispatch errors use their protocol status map; legacy
182
188
  * errors remain in-band at HTTP `200`. A streamed response composes the fetch-standard request
183
189
  * signal with response-body cancellation and supplies the result to every dispatched modern
@@ -397,7 +403,8 @@ export declare function createStdioClientTransport(options: StdioClientTransport
397
403
  * @param mcp - The transport-agnostic {@link MCPDispatcherInterface} to expose over stdio
398
404
  * @param options - Optional injectable `input` / `output` streams; see
399
405
  * {@link StdioServerOptions}
400
- * @returns A `{ start(): void; stop(): void }` handle to arm / tear down the pump
406
+ * @returns A {@link StdioServerInterface} handle to arm / tear down the pump; `stop()` ends
407
+ * that handle's lifetime permanently
401
408
  *
402
409
  * @example
403
410
  * ```ts
@@ -410,10 +417,7 @@ export declare function createStdioClientTransport(options: StdioClientTransport
410
417
  * createStdioServer(createMCPLegacy(mcp)).start() // answers `initialize` too; pass `mcp` alone for modern-only
411
418
  * ```
412
419
  */
413
- export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions): {
414
- start(): void;
415
- stop(): void;
416
- };
420
+ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions): StdioServerInterface;
417
421
 
418
422
  /**
419
423
  * Creates the WebSocket CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
@@ -655,7 +659,8 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
655
659
  * initialize result's `protocolVersion` is likewise captured, but only
656
660
  * when it is a SUPPORTED value, and echoed as `mcp-protocol-version` alone on
657
661
  * subsequent legacy requests. Modern requests instead derive protocol and method
658
- * headers from the message, plus the name header only for `tools/call`.
662
+ * headers from the message, plus the name header only for `tools/call` — carried in the
663
+ * protocol's Base64 sentinel form whenever the tool name cannot ride as plain ASCII.
659
664
  * Before initialize returns, neither captured legacy header is sent.
660
665
  * `close()` clears the captured protocol so a reconnect's `initialize`
661
666
  * POST is headerless; the captured `session` persists across `close()`.
@@ -837,10 +842,15 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
837
842
  * Infers the first required MCP HTTP header that is missing or mismatched.
838
843
  *
839
844
  * @remarks
840
- * A modern request derives its protocol, method, and tools/call-only name expectations from
841
- * the JSON-RPC body. A legacy request body requires a protocol header after initialization,
842
- * while a supplied legacy session version additionally diagnoses a header that disagrees with
843
- * the active session. Messages name the expected value but never echo the client-supplied one.
845
+ * A modern request derives its protocol, method, and name expectations from the JSON-RPC body,
846
+ * the name expectation scoped to the methods {@link inferHeaderTarget} reads a target for. A
847
+ * name header carrying the Base64 sentinel is decoded through
848
+ * {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
849
+ * to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
850
+ * and therefore mismatches, which is how an invalid header value is refused. A legacy request
851
+ * body requires a protocol header after initialization, while a supplied legacy session
852
+ * version additionally diagnoses a header that disagrees with the active session. Messages
853
+ * name the expected value but never echo the client-supplied one.
844
854
  *
845
855
  * @param request - The HTTP request carrying the headers
846
856
  * @param reference - The parsed invocation body, or the active legacy session version
@@ -854,6 +864,31 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
854
864
  */
855
865
  export declare function inferHeaderIssue(request: Request, reference: JSONRPCInvocation | MCPVersion): MCPHeaderIssue | undefined;
856
866
 
867
+ /**
868
+ * Infers the target one modern request's `Mcp-Name` header must carry.
869
+ *
870
+ * @remarks
871
+ * The protocol scopes the header to the methods whose body carries a name-shaped field, and
872
+ * names the field per method: `tools/call` and `prompts/get` carry `params.name`, and
873
+ * `resources/read` carries `params.uri`. Every other method — `server/discover`, `tools/list`,
874
+ * `resources/list`, `prompts/list` — has nothing to derive a target from, so the header is not
875
+ * required there and a peer that sent one anyway is not held to it.
876
+ *
877
+ * A method within the scope whose named member is absent or is not a string reads as no
878
+ * target. There is nothing for a header to match, and refusing the request over a body member
879
+ * the header rule does not own would report a parameter fault as a header fault. Total.
880
+ *
881
+ * @param request - The parsed modern invocation to read the target from
882
+ * @returns The target the header must carry, or `undefined` when the method carries none
883
+ *
884
+ * @example
885
+ * ```ts
886
+ * inferHeaderTarget({ jsonrpc: '2.0', id: 1, method: 'resources/read', params: { uri: 'file:///a' } })
887
+ * // → 'file:///a'
888
+ * ```
889
+ */
890
+ export declare function inferHeaderTarget(request: JSONRPCInvocation): string | undefined;
891
+
857
892
  /**
858
893
  * Infers the legacy revision an `initialize` request negotiates.
859
894
  *
@@ -872,6 +907,41 @@ export declare function inferHeaderIssue(request: Request, reference: JSONRPCInv
872
907
  */
873
908
  export declare function inferLegacyVersion(request: JSONRPCInvocation): MCPLegacyVersion;
874
909
 
910
+ /**
911
+ * Infers the refusal one `tools/call` earns for a `Mcp-Param-*` header the body contradicts.
912
+ *
913
+ * @remarks
914
+ * The custom-header half of the standard-header seam {@link inferHeaderIssue} owns, and it
915
+ * takes the SERVED definition's projections rather than a header issue: SEP-2243 scopes the
916
+ * rule to the `Mcp-Param-*` names the server's OWN tool definitions annotate, so a name no
917
+ * parameter claims is another party's header and travels through untouched.
918
+ *
919
+ * For each recognized parameter the body's value at the parameter's own property path fixes
920
+ * the expectation. A value the call omits or supplies as `null` requires no header, and a
921
+ * header sent anyway is refused because it asserts something the body never said. A value the
922
+ * call does supply requires its header: an absent one, a Base64 sentinel whose payload is
923
+ * invalid, and a decoded value that disagrees are each refused. An `integer` parameter
924
+ * compares numerically, so a peer that padded its decimal still matches. A supplied value
925
+ * whose runtime shape contradicts the declared type is left alone — the tool's own argument
926
+ * validation owns that disagreement, and refusing it here would report an argument fault as a
927
+ * header fault.
928
+ *
929
+ * Messages name the field and the body path the expectation came from, and never echo the
930
+ * value the peer supplied.
931
+ *
932
+ * @param request - The HTTP request carrying the headers
933
+ * @param parameters - The projections the served tool definition declares
934
+ * @param values - The call's `arguments` record
935
+ * @returns The refusal message for the first disagreeing parameter, or `undefined`
936
+ *
937
+ * @example
938
+ * ```ts
939
+ * inferParameterRefusal(request, [{ name: 'Region', path: ['region'], primitive: 'string' }], {})
940
+ * // → undefined when the request carries no `Mcp-Param-Region` either
941
+ * ```
942
+ */
943
+ export declare function inferParameterRefusal(request: Request, parameters: readonly MCPHeaderParameter[], values: unknown): string | undefined;
944
+
875
945
  /**
876
946
  * Infers the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
877
947
  *
@@ -1474,6 +1544,30 @@ export declare interface StdioClientTransportOptions {
1474
1544
  readonly delivery?: number;
1475
1545
  }
1476
1546
 
1547
+ /**
1548
+ * The stdio INGRESS handle {@link import('./factories.js').createStdioServer} returns — arms
1549
+ * and tears down the newline-delimited JSON-RPC pump over the {@link StdioServerOptions}
1550
+ * stream pair.
1551
+ *
1552
+ * @remarks
1553
+ * - `start()` — arm the pump: subscribe to `input`, and dispatch every complete line through
1554
+ * the bound {@link import('@src/core').MCPDispatcherInterface}, writing each defined
1555
+ * response back to `output`. The subscriptions are attached by the time the call returns.
1556
+ * The pump arms ONCE, so a repeated `start()` attaches nothing further and an inbound
1557
+ * request still draws exactly one reply.
1558
+ * - `stop()` — unbind the pump and close the transport: the listeners `start()` put on
1559
+ * `input` / `output` are removed, every pending `send` rejects, and `input` is released so
1560
+ * the process can exit. The release is complete by the time the call returns, and a
1561
+ * repeated `stop()` does nothing.
1562
+ * - **One lifetime per handle.** `stop()` ends it permanently: a `start()` issued afterwards
1563
+ * arms nothing, and serving again takes a fresh
1564
+ * {@link import('./factories.js').createStdioServer} over a live stream pair.
1565
+ */
1566
+ export declare interface StdioServerInterface {
1567
+ start(): void;
1568
+ stop(): void;
1569
+ }
1570
+
1477
1571
  /**
1478
1572
  * Options for `createStdioServer` — the injectable stdin/stdout streams the server
1479
1573
  * transport reads newline-delimited JSON-RPC requests from and writes responses to.
@@ -1597,10 +1691,12 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
1597
1691
  * non-JSON / non-message frame surfaces on `error` and is dropped. The socket's `close`
1598
1692
  * / `error` bridge to this transport's events.
1599
1693
  * - **Outbound (`send`).** `send(message)` writes one masked text frame. A socket write is not
1600
- * confirmed, so this transport answers a closed channel from its OWN state: a `send` with no
1601
- * bound socket — before `start()`, after `close()`, or after the peer ended the socket —
1602
- * REJECTS with `WebSocket transport is not connected`. It neither drops the message (the
1603
- * browser face's posture) nor queues it for a connection this transport is not holding.
1694
+ * confirmed, so this transport answers a closed channel from its own state AND the socket's
1695
+ * `readyState`: a `send` with no bound socket — before `start()`, after `close()`, or after the
1696
+ * peer ended the socket — and a `send` on a bound socket that is not `OPEN` both REJECT with
1697
+ * `WebSocket transport is not connected`. It neither drops the message nor queues it for a
1698
+ * connection this transport is not holding — the browser face queues a pre-open send, and this
1699
+ * one, holding no connection to flush it onto, rejects that too.
1604
1700
  * - **`close()`** unsubscribes from the socket, closes it, and fires `close` (idempotent). An
1605
1701
  * upgrade still on the wire is DESTROYED, so a `close()` during the handshake ends the
1606
1702
  * transport at once instead of waiting for a peer that may never answer — the suspended
@@ -1706,8 +1802,13 @@ export declare interface WebSocketServerOptions {
1706
1802
  * a non-JSON or non-message frame is surfaced on `error` and DROPPED, never thrown. It
1707
1803
  * also bridges the socket's `close` → this transport's `close`, and the socket's `error`.
1708
1804
  * - **Outbound (`send`).** `send(message)` writes one text frame
1709
- * (`nodeWs.send(JSON.stringify(message))`); the underlying wrapper no-ops a write on a
1710
- * non-open socket, so a closed connection drops silently rather than throwing.
1805
+ * (`nodeWs.send(JSON.stringify(message))`). The underlying wrapper no-ops a write on a
1806
+ * non-open socket and confirms nothing, so this bridge answers a closed channel from its own
1807
+ * state and the socket's `readyState`: a `send` after `close()`, after the peer's close, or on
1808
+ * a socket that is not `OPEN` REJECTS with `WebSocket transport is not connected` rather than
1809
+ * resolving on a frame nobody wrote. `bindServer` catches that rejection and routes it to the
1810
+ * dispatcher's `error` event, and it aborts every in-flight request the moment this transport's
1811
+ * `close` fires — so a peer that disconnects mid-request is answered by no write at all.
1711
1812
  * - **`close()`** removes the subscriptions `start()` installed on the socket, closes the
1712
1813
  * underlying socket (the RFC 6455 close handshake), and fires the transport's `close` event
1713
1814
  * (idempotent — a second `close`, or a socket-driven close, emits once). A frame that arrives
@@ -11,6 +11,7 @@ import { MCPClientTransportInterface as MCPClientTransportInterface_2 } from '@o
11
11
  import { MCPContinuationInterface } from '@orkestrel/mcp';
12
12
  import { MCPDispatcherInterface } from '@orkestrel/mcp';
13
13
  import { MCPEra } from '@orkestrel/mcp';
14
+ import { MCPHeaderParameter } from '@orkestrel/mcp';
14
15
  import { MCPLegacyVersion } from '@orkestrel/mcp';
15
16
  import { MCPStreamControllerInterface } from '@orkestrel/mcp';
16
17
  import { MCPTransportInterface } from '@orkestrel/mcp';
@@ -172,12 +173,17 @@ export declare function createMCPContinuation(secret: TokenSecret): MCPContinuat
172
173
  * Creates the Streamable-HTTP POST handler used by `createMCPRoutes`.
173
174
  *
174
175
  * @remarks
175
- * Modern requests require matching protocol/method headers and a matching name header only
176
- * for `tools/call`; mismatch returns HTTP `400` + `-32020`. Headerless `initialize` is
177
- * accepted, while every other headerless request needs a live legacy session to supply its
178
- * pinned version. A legacy-shaped request carrying a protocol header is admitted only for a
179
- * legacy revision; any other value, the modern revision included, returns HTTP `400` + `-32022`
180
- * whose `supported` names the legacy revisions this door accepts. A present origin must occur in `origin.origins` unless validation is
176
+ * Modern requests require matching protocol/method headers and a matching name header on each
177
+ * method carrying a named target — `tools/call` and `prompts/get` against `params.name`,
178
+ * `resources/read` against `params.uri` with a Base64-sentinel value decoded before the
179
+ * comparison; a missing, mismatched, or invalidly encoded value returns HTTP `400` + `-32020`.
180
+ * A protocol header naming a MODERN revision holds the request to that revision whatever shape
181
+ * its body arrived in, so a body with no parsable modern `_meta` returns HTTP `400` + `-32602`.
182
+ * Headerless `initialize` is accepted, while every other headerless request needs a live legacy
183
+ * session to supply its pinned version. A legacy-shaped request carrying a protocol header is
184
+ * otherwise admitted only for a legacy revision; a revision this server does not implement
185
+ * returns HTTP `400` + `-32022` whose `supported` names the legacy revisions this door accepts.
186
+ * A present origin must occur in `origin.origins` unless validation is
181
187
  * explicitly delegated upstream. Modern dispatch errors use their protocol status map; legacy
182
188
  * errors remain in-band at HTTP `200`. A streamed response composes the fetch-standard request
183
189
  * signal with response-body cancellation and supplies the result to every dispatched modern
@@ -397,7 +403,8 @@ export declare function createStdioClientTransport(options: StdioClientTransport
397
403
  * @param mcp - The transport-agnostic {@link MCPDispatcherInterface} to expose over stdio
398
404
  * @param options - Optional injectable `input` / `output` streams; see
399
405
  * {@link StdioServerOptions}
400
- * @returns A `{ start(): void; stop(): void }` handle to arm / tear down the pump
406
+ * @returns A {@link StdioServerInterface} handle to arm / tear down the pump; `stop()` ends
407
+ * that handle's lifetime permanently
401
408
  *
402
409
  * @example
403
410
  * ```ts
@@ -410,10 +417,7 @@ export declare function createStdioClientTransport(options: StdioClientTransport
410
417
  * createStdioServer(createMCPLegacy(mcp)).start() // answers `initialize` too; pass `mcp` alone for modern-only
411
418
  * ```
412
419
  */
413
- export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions): {
414
- start(): void;
415
- stop(): void;
416
- };
420
+ export declare function createStdioServer(mcp: MCPDispatcherInterface, options?: StdioServerOptions): StdioServerInterface;
417
421
 
418
422
  /**
419
423
  * Creates the WebSocket CLIENT transport for an {@link import('@orkestrel/mcp').MCPClientInterface}
@@ -655,7 +659,8 @@ export declare function extractLines(buffer: string, chunk: string): LineExtract
655
659
  * initialize result's `protocolVersion` is likewise captured, but only
656
660
  * when it is a SUPPORTED value, and echoed as `mcp-protocol-version` alone on
657
661
  * subsequent legacy requests. Modern requests instead derive protocol and method
658
- * headers from the message, plus the name header only for `tools/call`.
662
+ * headers from the message, plus the name header only for `tools/call` — carried in the
663
+ * protocol's Base64 sentinel form whenever the tool name cannot ride as plain ASCII.
659
664
  * Before initialize returns, neither captured legacy header is sent.
660
665
  * `close()` clears the captured protocol so a reconnect's `initialize`
661
666
  * POST is headerless; the captured `session` persists across `close()`.
@@ -837,10 +842,15 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
837
842
  * Infers the first required MCP HTTP header that is missing or mismatched.
838
843
  *
839
844
  * @remarks
840
- * A modern request derives its protocol, method, and tools/call-only name expectations from
841
- * the JSON-RPC body. A legacy request body requires a protocol header after initialization,
842
- * while a supplied legacy session version additionally diagnoses a header that disagrees with
843
- * the active session. Messages name the expected value but never echo the client-supplied one.
845
+ * A modern request derives its protocol, method, and name expectations from the JSON-RPC body,
846
+ * the name expectation scoped to the methods {@link inferHeaderTarget} reads a target for. A
847
+ * name header carrying the Base64 sentinel is decoded through
848
+ * {@link import('@orkestrel/mcp').decodeSentinel} before the comparison, so a peer that had
849
+ * to encode its value still matches; a sentinel whose payload is invalid decodes to nothing
850
+ * and therefore mismatches, which is how an invalid header value is refused. A legacy request
851
+ * body requires a protocol header after initialization, while a supplied legacy session
852
+ * version additionally diagnoses a header that disagrees with the active session. Messages
853
+ * name the expected value but never echo the client-supplied one.
844
854
  *
845
855
  * @param request - The HTTP request carrying the headers
846
856
  * @param reference - The parsed invocation body, or the active legacy session version
@@ -854,6 +864,31 @@ export declare interface HTTPTransportOptions<TState = unknown> extends HTTPHand
854
864
  */
855
865
  export declare function inferHeaderIssue(request: Request, reference: JSONRPCInvocation | MCPVersion): MCPHeaderIssue | undefined;
856
866
 
867
+ /**
868
+ * Infers the target one modern request's `Mcp-Name` header must carry.
869
+ *
870
+ * @remarks
871
+ * The protocol scopes the header to the methods whose body carries a name-shaped field, and
872
+ * names the field per method: `tools/call` and `prompts/get` carry `params.name`, and
873
+ * `resources/read` carries `params.uri`. Every other method — `server/discover`, `tools/list`,
874
+ * `resources/list`, `prompts/list` — has nothing to derive a target from, so the header is not
875
+ * required there and a peer that sent one anyway is not held to it.
876
+ *
877
+ * A method within the scope whose named member is absent or is not a string reads as no
878
+ * target. There is nothing for a header to match, and refusing the request over a body member
879
+ * the header rule does not own would report a parameter fault as a header fault. Total.
880
+ *
881
+ * @param request - The parsed modern invocation to read the target from
882
+ * @returns The target the header must carry, or `undefined` when the method carries none
883
+ *
884
+ * @example
885
+ * ```ts
886
+ * inferHeaderTarget({ jsonrpc: '2.0', id: 1, method: 'resources/read', params: { uri: 'file:///a' } })
887
+ * // → 'file:///a'
888
+ * ```
889
+ */
890
+ export declare function inferHeaderTarget(request: JSONRPCInvocation): string | undefined;
891
+
857
892
  /**
858
893
  * Infers the legacy revision an `initialize` request negotiates.
859
894
  *
@@ -872,6 +907,41 @@ export declare function inferHeaderIssue(request: Request, reference: JSONRPCInv
872
907
  */
873
908
  export declare function inferLegacyVersion(request: JSONRPCInvocation): MCPLegacyVersion;
874
909
 
910
+ /**
911
+ * Infers the refusal one `tools/call` earns for a `Mcp-Param-*` header the body contradicts.
912
+ *
913
+ * @remarks
914
+ * The custom-header half of the standard-header seam {@link inferHeaderIssue} owns, and it
915
+ * takes the SERVED definition's projections rather than a header issue: SEP-2243 scopes the
916
+ * rule to the `Mcp-Param-*` names the server's OWN tool definitions annotate, so a name no
917
+ * parameter claims is another party's header and travels through untouched.
918
+ *
919
+ * For each recognized parameter the body's value at the parameter's own property path fixes
920
+ * the expectation. A value the call omits or supplies as `null` requires no header, and a
921
+ * header sent anyway is refused because it asserts something the body never said. A value the
922
+ * call does supply requires its header: an absent one, a Base64 sentinel whose payload is
923
+ * invalid, and a decoded value that disagrees are each refused. An `integer` parameter
924
+ * compares numerically, so a peer that padded its decimal still matches. A supplied value
925
+ * whose runtime shape contradicts the declared type is left alone — the tool's own argument
926
+ * validation owns that disagreement, and refusing it here would report an argument fault as a
927
+ * header fault.
928
+ *
929
+ * Messages name the field and the body path the expectation came from, and never echo the
930
+ * value the peer supplied.
931
+ *
932
+ * @param request - The HTTP request carrying the headers
933
+ * @param parameters - The projections the served tool definition declares
934
+ * @param values - The call's `arguments` record
935
+ * @returns The refusal message for the first disagreeing parameter, or `undefined`
936
+ *
937
+ * @example
938
+ * ```ts
939
+ * inferParameterRefusal(request, [{ name: 'Region', path: ['region'], primitive: 'string' }], {})
940
+ * // → undefined when the request carries no `Mcp-Param-Region` either
941
+ * ```
942
+ */
943
+ export declare function inferParameterRefusal(request: Request, parameters: readonly MCPHeaderParameter[], values: unknown): string | undefined;
944
+
875
945
  /**
876
946
  * Infers the HTTP status for one MCP dispatch outcome without changing its JSON-RPC body.
877
947
  *
@@ -1474,6 +1544,30 @@ export declare interface StdioClientTransportOptions {
1474
1544
  readonly delivery?: number;
1475
1545
  }
1476
1546
 
1547
+ /**
1548
+ * The stdio INGRESS handle {@link import('./factories.js').createStdioServer} returns — arms
1549
+ * and tears down the newline-delimited JSON-RPC pump over the {@link StdioServerOptions}
1550
+ * stream pair.
1551
+ *
1552
+ * @remarks
1553
+ * - `start()` — arm the pump: subscribe to `input`, and dispatch every complete line through
1554
+ * the bound {@link import('@src/core').MCPDispatcherInterface}, writing each defined
1555
+ * response back to `output`. The subscriptions are attached by the time the call returns.
1556
+ * The pump arms ONCE, so a repeated `start()` attaches nothing further and an inbound
1557
+ * request still draws exactly one reply.
1558
+ * - `stop()` — unbind the pump and close the transport: the listeners `start()` put on
1559
+ * `input` / `output` are removed, every pending `send` rejects, and `input` is released so
1560
+ * the process can exit. The release is complete by the time the call returns, and a
1561
+ * repeated `stop()` does nothing.
1562
+ * - **One lifetime per handle.** `stop()` ends it permanently: a `start()` issued afterwards
1563
+ * arms nothing, and serving again takes a fresh
1564
+ * {@link import('./factories.js').createStdioServer} over a live stream pair.
1565
+ */
1566
+ export declare interface StdioServerInterface {
1567
+ start(): void;
1568
+ stop(): void;
1569
+ }
1570
+
1477
1571
  /**
1478
1572
  * Options for `createStdioServer` — the injectable stdin/stdout streams the server
1479
1573
  * transport reads newline-delimited JSON-RPC requests from and writes responses to.
@@ -1597,10 +1691,12 @@ export declare function upgradeRequestPath(request: IncomingMessage): string;
1597
1691
  * non-JSON / non-message frame surfaces on `error` and is dropped. The socket's `close`
1598
1692
  * / `error` bridge to this transport's events.
1599
1693
  * - **Outbound (`send`).** `send(message)` writes one masked text frame. A socket write is not
1600
- * confirmed, so this transport answers a closed channel from its OWN state: a `send` with no
1601
- * bound socket — before `start()`, after `close()`, or after the peer ended the socket —
1602
- * REJECTS with `WebSocket transport is not connected`. It neither drops the message (the
1603
- * browser face's posture) nor queues it for a connection this transport is not holding.
1694
+ * confirmed, so this transport answers a closed channel from its own state AND the socket's
1695
+ * `readyState`: a `send` with no bound socket — before `start()`, after `close()`, or after the
1696
+ * peer ended the socket — and a `send` on a bound socket that is not `OPEN` both REJECT with
1697
+ * `WebSocket transport is not connected`. It neither drops the message nor queues it for a
1698
+ * connection this transport is not holding — the browser face queues a pre-open send, and this
1699
+ * one, holding no connection to flush it onto, rejects that too.
1604
1700
  * - **`close()`** unsubscribes from the socket, closes it, and fires `close` (idempotent). An
1605
1701
  * upgrade still on the wire is DESTROYED, so a `close()` during the handshake ends the
1606
1702
  * transport at once instead of waiting for a peer that may never answer — the suspended
@@ -1706,8 +1802,13 @@ export declare interface WebSocketServerOptions {
1706
1802
  * a non-JSON or non-message frame is surfaced on `error` and DROPPED, never thrown. It
1707
1803
  * also bridges the socket's `close` → this transport's `close`, and the socket's `error`.
1708
1804
  * - **Outbound (`send`).** `send(message)` writes one text frame
1709
- * (`nodeWs.send(JSON.stringify(message))`); the underlying wrapper no-ops a write on a
1710
- * non-open socket, so a closed connection drops silently rather than throwing.
1805
+ * (`nodeWs.send(JSON.stringify(message))`). The underlying wrapper no-ops a write on a
1806
+ * non-open socket and confirms nothing, so this bridge answers a closed channel from its own
1807
+ * state and the socket's `readyState`: a `send` after `close()`, after the peer's close, or on
1808
+ * a socket that is not `OPEN` REJECTS with `WebSocket transport is not connected` rather than
1809
+ * resolving on a frame nobody wrote. `bindServer` catches that rejection and routes it to the
1810
+ * dispatcher's `error` event, and it aborts every in-flight request the moment this transport's
1811
+ * `close` fires — so a peer that disconnects mid-request is answered by no write at all.
1711
1812
  * - **`close()`** removes the subscriptions `start()` installed on the socket, closes the
1712
1813
  * underlying socket (the RFC 6455 close handshake), and fires the transport's `close` event
1713
1814
  * (idempotent — a second `close`, or a socket-driven close, emits once). A frame that arrives