@orkestrel/mcp 0.0.27 → 0.0.28

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.
@@ -1,10 +1,11 @@
1
- import { arrayOf, attempt, cloneJSONRecord, cloneJSONValue, isArray, isBoolean, isFiniteNumber, isInteger, isJSONValue, isNumber, isRecord, isString, isUndefined, sanitizeBudget } from "@orkestrel/contract";
1
+ import { arrayOf, attempt, cloneJSONRecord, cloneJSONValue, isArray, isBoolean, isFiniteNumber, isInteger, isJSONValue, isNumber, isRecord, isString, isUndefined, parseJSON, sanitizeBudget } from "@orkestrel/contract";
2
2
  import { decodeBase64, decodeUTF8, encodeBase64, encodeHex } from "@orkestrel/codec";
3
+ import { createSSEParser } from "@orkestrel/sse";
3
4
  import { Emitter } from "@orkestrel/emitter";
4
5
  import { Tool } from "@orkestrel/tool";
5
6
  //#region src/core/constants.ts
6
7
  /**
7
- * The revision offered and defaulted to in the legacy `initialize` handshake.
8
+ * Names the revision offered and defaulted to in the legacy `initialize` handshake.
8
9
  *
9
10
  * @remarks
10
11
  * This is deliberately a legacy revision, and the newest one supported. 2026-07-28 is stateless
@@ -12,12 +13,12 @@ import { Tool } from "@orkestrel/tool";
12
13
  * it is asking to negotiate a revision with no negotiation.
13
14
  */
14
15
  var MCP_HANDSHAKE_VERSION = "2025-11-25";
15
- /** The older legacy revision the optional legacy decorator accepts and an adapter can pin. */
16
+ /** Names the older legacy revision the optional legacy decorator accepts and an adapter can pin. */
16
17
  var MCP_FALLBACK_VERSION = "2025-06-18";
17
- /** The modern revision offered by an unpinned client during discovery. */
18
+ /** Names the modern revision offered by an unpinned client during discovery. */
18
19
  var MCP_MODERN_VERSION = "2026-07-28";
19
20
  /**
20
- * The modern MCP protocol revisions a bare server accepts and advertises.
21
+ * Lists the modern MCP protocol revisions a bare server accepts and advertises.
21
22
  *
22
23
  * @remarks
23
24
  * Frozen in discovery-advertisement order. Legacy revisions are absent because
@@ -25,22 +26,25 @@ var MCP_MODERN_VERSION = "2026-07-28";
25
26
  * decorator own them.
26
27
  */
27
28
  var SUPPORTED_MODERN_PROTOCOL_VERSIONS = Object.freeze([MCP_MODERN_VERSION]);
28
- /** The protocol revisions accepted by the optional legacy decorator. */
29
+ /** Lists the protocol revisions accepted by the optional legacy decorator. */
29
30
  var SUPPORTED_LEGACY_PROTOCOL_VERSIONS = Object.freeze([MCP_HANDSHAKE_VERSION, MCP_FALLBACK_VERSION]);
30
- /** The protocol revisions the `isMCPVersion` guard admits, spanning the modern and legacy eras. */
31
+ /**
32
+ * Lists the protocol revisions the `isMCPVersion` guard admits, spanning the modern and legacy
33
+ * eras.
34
+ */
31
35
  var SUPPORTED_MCP_VERSIONS = Object.freeze([...SUPPORTED_MODERN_PROTOCOL_VERSIONS, ...SUPPORTED_LEGACY_PROTOCOL_VERSIONS]);
32
- /** Reserved modern `_meta` key carrying the request's protocol revision. */
36
+ /** Names the reserved modern `_meta` key carrying the request's protocol revision. */
33
37
  var MCP_META_VERSION = "io.modelcontextprotocol/protocolVersion";
34
- /** Reserved modern `_meta` key carrying the client's open capability record. */
38
+ /** Names the reserved modern `_meta` key carrying the client's open capability record. */
35
39
  var MCP_META_CAPABILITIES = "io.modelcontextprotocol/clientCapabilities";
36
- /** Reserved modern `_meta` key carrying the optional client identity. */
40
+ /** Names the reserved modern `_meta` key carrying the optional client identity. */
37
41
  var MCP_META_CLIENT = "io.modelcontextprotocol/clientInfo";
38
- /** Reserved modern `_meta` key carrying the server identity on results. */
42
+ /** Names the reserved modern `_meta` key carrying the server identity on results. */
39
43
  var MCP_META_SERVER = "io.modelcontextprotocol/serverInfo";
40
- /** Reserved modern `_meta` key carrying a `subscriptions/listen` request id. */
44
+ /** Names the reserved modern `_meta` key carrying a `subscriptions/listen` request id. */
41
45
  var MCP_META_SUBSCRIPTION = "io.modelcontextprotocol/subscriptionId";
42
46
  /**
43
- * The reserved extension key identifying the stable Tasks extension.
47
+ * Names the reserved extension key identifying the stable Tasks extension.
44
48
  *
45
49
  * @remarks
46
50
  * The ONE spelling of it in this package, and the identity of the immutable snapshot dated
@@ -51,7 +55,7 @@ var MCP_META_SUBSCRIPTION = "io.modelcontextprotocol/subscriptionId";
51
55
  */
52
56
  var MCP_EXTENSION_TASKS = "io.modelcontextprotocol/tasks";
53
57
  /**
54
- * The opening marker of the Base64 sentinel a standard MCP header value travels in.
58
+ * Names the opening marker of the Base64 sentinel a standard MCP header value travels in.
55
59
  *
56
60
  * @remarks
57
61
  * The markers are LOWERCASE and exact, and this constant with {@link MCP_SENTINEL_SUFFIX} is
@@ -60,10 +64,10 @@ var MCP_EXTENSION_TASKS = "io.modelcontextprotocol/tasks";
60
64
  * them, so the two directions cannot drift apart.
61
65
  */
62
66
  var MCP_SENTINEL_PREFIX = "=?base64?";
63
- /** The closing marker of the Base64 sentinel a standard MCP header value travels in. */
67
+ /** Names the closing marker of the Base64 sentinel a standard MCP header value travels in. */
64
68
  var MCP_SENTINEL_SUFFIX = "?=";
65
69
  /**
66
- * The request-header prefix an `x-mcp-header` annotation projects a tool argument onto.
70
+ * Names the request-header prefix an `x-mcp-header` annotation projects a tool argument onto.
67
71
  *
68
72
  * @remarks
69
73
  * The full field name is this prefix followed by the annotation's own value verbatim, so
@@ -73,7 +77,40 @@ var MCP_SENTINEL_SUFFIX = "?=";
73
77
  */
74
78
  var MCP_PARAM_PREFIX = "Mcp-Param-";
75
79
  /**
76
- * The tool-schema annotation key naming the header one parameter projects into.
80
+ * Names the Streamable-HTTP transport header that carries the MCP session id.
81
+ *
82
+ * @remarks
83
+ * A STATEFUL server sends it on the `initialize` reply, and
84
+ * {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport} echoes it as a
85
+ * request header on every subsequent request, so a client passes that server's session
86
+ * validation unchanged.
87
+ */
88
+ var MCP_SESSION_HEADER = "mcp-session-id";
89
+ /**
90
+ * Names the Streamable-HTTP transport header carrying the MCP protocol version.
91
+ *
92
+ * @remarks
93
+ * A modern request derives it from its own `_meta`; a legacy request echoes the revision the
94
+ * `initialize` result negotiated on each subsequent request.
95
+ */
96
+ var MCP_PROTOCOL_VERSION_HEADER = "mcp-protocol-version";
97
+ /**
98
+ * Names the modern Streamable-HTTP request header carrying the JSON-RPC method.
99
+ *
100
+ * @remarks
101
+ * It is stamped on every modern request and on no legacy request.
102
+ */
103
+ var MCP_METHOD_HEADER = "mcp-method";
104
+ /**
105
+ * Names the modern Streamable-HTTP request header carrying a named target.
106
+ *
107
+ * @remarks
108
+ * The HTTP client transport stamps it only for `tools/call`, from that request's `params.name`,
109
+ * in the Base64 sentinel form whenever the name cannot ride as plain ASCII.
110
+ */
111
+ var MCP_NAME_HEADER = "mcp-name";
112
+ /**
113
+ * Identifies the tool-schema annotation key naming the header one parameter projects into.
77
114
  *
78
115
  * @remarks
79
116
  * It is valid ONLY on a primitive property schema statically reachable from the `inputSchema`
@@ -83,7 +120,23 @@ var MCP_PARAM_PREFIX = "Mcp-Param-";
83
120
  */
84
121
  var MCP_HEADER_ANNOTATION = "x-mcp-header";
85
122
  /**
86
- * The `tools/list` pages one modern `tools/call` walks to reach its own annotations.
123
+ * Names the WebSocket subprotocol `createWebSocketClientTransport` requests by default
124
+ * `'mcp'`, which `createWebSocketServer` selects when the client offers it. Per RFC 6455
125
+ * §4.1 a client MUST fail the connection if the server returns
126
+ * a subprotocol it did not request; Node ≥ 22 (undici) enforces this strictly, so the
127
+ * default bakes the correct value in. Override `WebSocketClientTransportOptions.protocols`
128
+ * only when connecting to a foreign server that speaks a different subprotocol (or `[]`
129
+ * for no subprotocol negotiation at all).
130
+ *
131
+ * @remarks
132
+ * The client sends it in `Sec-WebSocket-Protocol` and the server echoes it in its `101`
133
+ * handshake, so an MCP WebSocket endpoint is distinguishable from any other WebSocket on the
134
+ * same path. The default WebSocket upgrade path is the same `'/mcp'` the HTTP transport mounts
135
+ * at — the upgrade is selected by the `Upgrade: websocket` header, not a separate path.
136
+ */
137
+ var MCP_WEBSOCKET_SUBPROTOCOL = "mcp";
138
+ /**
139
+ * Bounds the `tools/list` pages one modern `tools/call` walks to reach its own annotations.
87
140
  *
88
141
  * @remarks
89
142
  * The HTTP POST handler reads a called tool's {@link MCP_HEADER_ANNOTATION} annotations by
@@ -97,10 +150,11 @@ var MCP_HEADER_ANNOTATION = "x-mcp-header";
97
150
  * answer a name no served definition annotates receives.
98
151
  */
99
152
  var MCP_LOOKUP_PAGES = 8;
100
- /** MCP reserved error: required HTTP metadata does not match the request body. */
153
+ /** Names the MCP reserved error for required HTTP metadata that does not match the request body. */
101
154
  var MCP_HEADER_MISMATCH = -32020;
102
155
  /**
103
- * MCP reserved error: an operation needs a client capability that was not declared.
156
+ * Names the MCP reserved error for an operation needing a client capability that was not
157
+ * declared.
104
158
  *
105
159
  * @remarks
106
160
  * The GENERIC code for the whole condition, not one capability's code. This server answers
@@ -113,10 +167,10 @@ var MCP_HEADER_MISMATCH = -32020;
113
167
  * schema is what a peer implements against.
114
168
  */
115
169
  var MCP_MISSING_CAPABILITY = -32021;
116
- /** MCP reserved error: a request names an unsupported protocol revision. */
170
+ /** Names the MCP reserved error for a request naming an unsupported protocol revision. */
117
171
  var MCP_UNSUPPORTED_VERSION = -32022;
118
172
  /**
119
- * Default modern result freshness lifetime in milliseconds.
173
+ * Sets the default modern result freshness lifetime in milliseconds.
120
174
  *
121
175
  * @remarks
122
176
  * `ttlMs` is required on cacheable results, while zero means immediately stale
@@ -124,7 +178,8 @@ var MCP_UNSUPPORTED_VERSION = -32022;
124
178
  */
125
179
  var DEFAULT_MCP_CACHE_TTL = 6e4;
126
180
  /**
127
- * Secure server bounds used when the matching `limit` option leaf is absent or malformed.
181
+ * Sets the secure server bounds used when the matching `limit` option leaf is absent or
182
+ * malformed.
128
183
  *
129
184
  * @remarks
130
185
  * One MiB admits ordinary JSON-RPC requests and substantial tool arguments; 16 KiB admits
@@ -146,7 +201,7 @@ var DEFAULT_MCP_LIMITS = Object.freeze({
146
201
  depth: 32
147
202
  });
148
203
  /**
149
- * The one empty argument record every argument-less modern `tools/call` runs with.
204
+ * Holds the one empty argument record every argument-less modern `tools/call` runs with.
150
205
  *
151
206
  * @remarks
152
207
  * Frozen and null-prototype, and SHARED: two calls that name no `arguments` receive the same
@@ -160,16 +215,17 @@ var DEFAULT_MCP_LIMITS = Object.freeze({
160
215
  * `arguments.constructor` is `undefined` here rather than a function.
161
216
  */
162
217
  var EMPTY_MCP_ARGUMENTS = Object.freeze(Object.create(null));
163
- /** JSON-RPC 2.0 reserved error: invalid JSON was received (the message did not parse). */
218
+ /** Names the JSON-RPC 2.0 reserved error for invalid JSON received (the message did not parse). */
164
219
  var JSONRPC_PARSE_ERROR = -32700;
165
- /** JSON-RPC 2.0 reserved error: the payload was not a valid Request object. */
220
+ /** Names the JSON-RPC 2.0 reserved error for a payload that was not a valid Request object. */
166
221
  var JSONRPC_INVALID_REQUEST = -32600;
167
- /** JSON-RPC 2.0 reserved error: the requested method does not exist. */
222
+ /** Names the JSON-RPC 2.0 reserved error for a requested method that does not exist. */
168
223
  var JSONRPC_METHOD_NOT_FOUND = -32601;
169
- /** JSON-RPC 2.0 reserved error: the method's parameters were invalid. */
224
+ /** Names the JSON-RPC 2.0 reserved error for a method's invalid parameters. */
170
225
  var JSONRPC_INVALID_PARAMS = -32602;
171
226
  /**
172
- * JSON-RPC 2.0 reserved error: the server failed while handling an otherwise valid request.
227
+ * Names the JSON-RPC 2.0 reserved error for a server that failed while handling an otherwise
228
+ * valid request.
173
229
  *
174
230
  * @remarks
175
231
  * The code every MODERN internal fault answers with — a provider, handler, continuation,
@@ -179,7 +235,7 @@ var JSONRPC_INVALID_PARAMS = -32602;
179
235
  */
180
236
  var JSONRPC_INTERNAL_ERROR = -32603;
181
237
  /**
182
- * JSON-RPC 2.0 implementation-defined server error (the `-32000` to `-32099` range).
238
+ * Names the JSON-RPC 2.0 implementation-defined server error (the `-32000` to `-32099` range).
183
239
  *
184
240
  * @remarks
185
241
  * Retained for the LEGACY branch alone. A modern fault answers
@@ -187,21 +243,27 @@ var JSONRPC_INTERNAL_ERROR = -32603;
187
243
  * already characterized against it.
188
244
  */
189
245
  var JSONRPC_SERVER_ERROR = -32e3;
190
- /** The default client name reported in the MCP `initialize` handshake (`clientInfo.name`). */
191
- var DEFAULT_MCP_CLIENT_NAME = "taverna";
192
- /** The default client version reported in the MCP `initialize` handshake (`clientInfo.version`). */
246
+ /**
247
+ * Supplies the default client name reported in the MCP `initialize` handshake
248
+ * (`clientInfo.name`).
249
+ */
250
+ var DEFAULT_MCP_CLIENT_NAME = "@orkestrel/mcp";
251
+ /**
252
+ * Supplies the default client version reported in the MCP `initialize` handshake
253
+ * (`clientInfo.version`).
254
+ */
193
255
  var DEFAULT_MCP_CLIENT_VERSION = "1.0.0";
194
256
  /**
195
- * The default per-request deadline (ms) an `MCPClient` applies when `options.timeout`
257
+ * Sets the default per-request deadline (ms) an `MCPClient` applies when `options.timeout`
196
258
  * is unset — a request the remote server does not answer within it rejects.
197
259
  */
198
260
  var DEFAULT_MCP_REQUEST_TIMEOUT = 3e4;
199
- /** The default number of subscription frames retained while no client read is parked. */
261
+ /** Sets the default number of subscription frames retained while no client read is parked. */
200
262
  var DEFAULT_MCP_SUBSCRIPTION_CAPACITY = 64;
201
263
  //#endregion
202
264
  //#region src/core/errors.ts
203
265
  /**
204
- * A Model Context Protocol error preserving its machine-readable numeric code and
266
+ * Preserves a Model Context Protocol error's machine-readable numeric code and
205
267
  * optional structured context.
206
268
  *
207
269
  * @remarks
@@ -244,7 +306,7 @@ var MCPError = class extends Error {
244
306
  * Determines whether an unknown value is an {@link MCPError}.
245
307
  *
246
308
  * @param value - The unknown value to inspect
247
- * @returns `true` only when the value is an `MCPError`
309
+ * @returns True if the value is an `MCPError`; false otherwise
248
310
  *
249
311
  * @example
250
312
  * ```ts
@@ -497,7 +559,7 @@ function parseRequestContext(value, limits = {
497
559
  function parseMCPInputState(value) {
498
560
  try {
499
561
  if (!isString(value)) return void 0;
500
- const parsed = JSON.parse(value);
562
+ const parsed = parseJSON(value);
501
563
  if (!isRecord(parsed)) return void 0;
502
564
  const principal = parsed["principal"];
503
565
  const expiry = parsed["expiry"];
@@ -539,15 +601,15 @@ function parseMCPInputState(value) {
539
601
  * does not authorize a form request. Total over hostile input.
540
602
  *
541
603
  * @param value - The client capability record to inspect
542
- * @returns `true` when form-mode elicitation is declared
604
+ * @returns True if form-mode elicitation is declared; false otherwise
543
605
  *
544
606
  * @example
545
607
  * ```ts
546
- * isFormElicitationSupported({ elicitation: {} }) // true — implicit form mode
547
- * isFormElicitationSupported({ elicitation: { url: {} } }) // false
608
+ * supportsFormElicitation({ elicitation: {} }) // true — implicit form mode
609
+ * supportsFormElicitation({ elicitation: { url: {} } }) // false
548
610
  * ```
549
611
  */
550
- function isFormElicitationSupported(value) {
612
+ function supportsFormElicitation(value) {
551
613
  const owned = attempt(() => cloneJSONRecord(value));
552
614
  if (!owned.success) return false;
553
615
  try {
@@ -570,7 +632,7 @@ function isFormElicitationSupported(value) {
570
632
  * `ClientCapabilities` shape the schema defines rather than as a list of names.
571
633
  *
572
634
  * Each kind maps to one declaration: `sampling/createMessage` to `sampling`, `roots/list` to
573
- * `roots`, a form elicitation to what {@link isFormElicitationSupported} accepts, and a
635
+ * `roots`, a form elicitation to what {@link supportsFormElicitation} accepts, and a
574
636
  * URL-mode elicitation to a record-valued `elicitation.url`. A request this package cannot
575
637
  * recognize needs nothing, because {@link import('./validators.js').isMCPInputRequestMap}
576
638
  * has already refused the round it would have travelled in. Total over hostile input.
@@ -611,7 +673,7 @@ function computeMissingCapabilities(requests, capabilities) {
611
673
  if (!isRecord(elicitation) || !isRecord(elicitation["url"])) urlUndeclared = true;
612
674
  continue;
613
675
  }
614
- if (!isFormElicitationSupported(declared)) formUndeclared = true;
676
+ if (!supportsFormElicitation(declared)) formUndeclared = true;
615
677
  }
616
678
  if (formUndeclared && !urlUndeclared) missing["elicitation"] = {};
617
679
  if (urlUndeclared && !formUndeclared) missing["elicitation"] = { url: {} };
@@ -638,16 +700,16 @@ function computeMissingCapabilities(requests, capabilities) {
638
700
  * the request in hand. Total over hostile input.
639
701
  *
640
702
  * @param value - The client capability record to inspect
641
- * @returns `true` when the tasks extension is declared as the schema's empty object
703
+ * @returns True if the tasks extension is declared as the schema's empty object; false otherwise
642
704
  *
643
705
  * @example
644
706
  * ```ts
645
- * isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
646
- * isTaskSupported({ extensions: {} }) // false — the key is the declaration
647
- * isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': { on: true } } }) // false
707
+ * supportsTask({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
708
+ * supportsTask({ extensions: {} }) // false — the key is the declaration
709
+ * supportsTask({ extensions: { 'io.modelcontextprotocol/tasks': { on: true } } }) // false
648
710
  * ```
649
711
  */
650
- function isTaskSupported(value) {
712
+ function supportsTask(value) {
651
713
  const owned = attempt(() => cloneJSONRecord(value));
652
714
  if (!owned.success) return false;
653
715
  try {
@@ -924,7 +986,7 @@ function buildProgressNotification(token, progress) {
924
986
  * rather than as a violation.
925
987
  *
926
988
  * Only write one on a carrier that accepts a client-initiated notification — see
927
- * {@link import('./types.js').MCPClientTransportInterface.duplex}. On Streamable HTTP the
989
+ * {@link import('./types.js').MCPMessageTransportInterface.duplex}. On Streamable HTTP the
928
990
  * dated revision defines no such frame, and closing the response stream is the
929
991
  * cancellation signal instead.
930
992
  *
@@ -963,7 +1025,7 @@ function buildCancelledNotification(id, reason) {
963
1025
  *
964
1026
  * @param method - The method the pending request was issued for
965
1027
  * @param resultType - The unknown `resultType` the peer answered with
966
- * @returns Whether that method may legally answer with that `resultType`
1028
+ * @returns True if that method may legally answer with that `resultType`; false otherwise
967
1029
  *
968
1030
  * @example
969
1031
  * ```ts
@@ -1318,7 +1380,7 @@ function buildSubscriptionFilter(requested, supported, enabled = false) {
1318
1380
  *
1319
1381
  * @param notification - The server notification offered by the configured producer
1320
1382
  * @param filter - The filter acknowledged to the client
1321
- * @returns `true` when the notification belongs on this subscription stream
1383
+ * @returns True if the notification belongs on this subscription stream; false otherwise
1322
1384
  */
1323
1385
  function matchesSubscriptionNotification(notification, filter) {
1324
1386
  if (notification.method === "notifications/tools/list_changed") return filter.toolsListChanged === true;
@@ -1440,7 +1502,7 @@ function buildInitializeResult(name, version, requested) {
1440
1502
  *
1441
1503
  * @remarks
1442
1504
  * The bound is checked FIRST, against the raw string, so an oversized message is never
1443
- * `JSON.parse`d at all: a decoder that parses before it measures has already spent the work
1505
+ * parsed at all: a decoder that parses before it measures has already spent the work
1444
1506
  * the bound exists to refuse. A message over the bound, malformed JSON, and a well-formed
1445
1507
  * value that is not a JSON-RPC message are one answer — `undefined` — because a binder does
1446
1508
  * exactly the same thing with each of them: nothing, and let
@@ -1460,8 +1522,129 @@ function buildInitializeResult(name, version, requested) {
1460
1522
  */
1461
1523
  function decodeBoundedMessage(message, limits) {
1462
1524
  if (!isBoundedString(message, limits.bytes)) return void 0;
1463
- const parsed = attempt(() => JSON.parse(message));
1464
- return parsed.success ? parseJSONRPCMessage(parsed.value, limits) : void 0;
1525
+ return parseJSONRPCMessage(parseJSON(message), limits);
1526
+ }
1527
+ /**
1528
+ * Decodes one inbound frame and delivers it onto a transport emitter as `message` or `error`.
1529
+ *
1530
+ * @remarks
1531
+ * The ONE inbound fold every message-carrying transport in this package runs: parse the frame,
1532
+ * narrow it with `parseJSONRPCMessage`, emit `message` for a well-formed
1533
+ * {@link JSONRPCMessage}, and emit `error` for anything else. Total — an adversarial frame
1534
+ * produces an `error` emission and never a throw.
1535
+ *
1536
+ * The two failures report differently on purpose. Unparsable text emits the CAUGHT parse
1537
+ * error, which names the offending position; well-formed JSON that is not a JSON-RPC message
1538
+ * has no caught value to report, so it emits `fault` — the carrier's own wording, passed in
1539
+ * rather than forked into a second copy of this body.
1540
+ *
1541
+ * @param emitter - The transport's emitter to deliver onto
1542
+ * @param text - One inbound frame's raw text
1543
+ * @param fault - The message for the error emitted when the frame parses but is not JSON-RPC
1544
+ *
1545
+ * @example
1546
+ * ```ts
1547
+ * deliverMessage(transport.emitter, frame, 'non-JSON-RPC WebSocket frame')
1548
+ * ```
1549
+ */
1550
+ function deliverMessage(emitter, text, fault) {
1551
+ let parsed;
1552
+ try {
1553
+ parsed = JSON.parse(text);
1554
+ } catch (error) {
1555
+ emitter.emit("error", error);
1556
+ return;
1557
+ }
1558
+ const message = parseJSONRPCMessage(parsed);
1559
+ if (message === void 0) {
1560
+ emitter.emit("error", new Error(fault));
1561
+ return;
1562
+ }
1563
+ emitter.emit("message", message);
1564
+ }
1565
+ /**
1566
+ * Decodes one SSE event's `data` string into a {@link JSONRPCMessage}, or `undefined`
1567
+ * when it is not one — the per-event step {@link readEventStream} folds over.
1568
+ *
1569
+ * @remarks
1570
+ * Parses the `data` (a peer serializes the JSON-RPC envelope as the event's `data`) with
1571
+ * `@orkestrel/contract`'s `parseJSON` — the declared JSON boundary, which answers `undefined`
1572
+ * instead of throwing — and narrows the parsed value with `parseJSONRPCMessage`. Total:
1573
+ * malformed JSON or a non-message value yields `undefined`, never throws.
1574
+ *
1575
+ * @param data - One SSE event's `data` payload
1576
+ * @returns The decoded {@link JSONRPCMessage}, or `undefined`
1577
+ *
1578
+ * @example
1579
+ * ```ts
1580
+ * decodeEvent('{"jsonrpc":"2.0","id":1,"result":{}}') // the decoded response
1581
+ * ```
1582
+ */
1583
+ function decodeEvent(data) {
1584
+ return parseJSONRPCMessage(parseJSON(data));
1585
+ }
1586
+ /**
1587
+ * Decodes a `fetch` Response's Server-Sent-Events body into the JSON-RPC messages it
1588
+ * carried — the CLIENT-side inverse of a server's Streamable-HTTP SSE response.
1589
+ *
1590
+ * @remarks
1591
+ * Reads the whole `response.body` stream chunk-by-chunk through a `TextDecoder({ stream: true
1592
+ * })` (handling a multi-byte character split across reads) and `@orkestrel/sse`'s
1593
+ * {@link SSEParserInterface} (handling a partial line or in-progress event split across
1594
+ * reads), then narrows each dispatched event's `data` to a {@link JSONRPCMessage} through
1595
+ * {@link decodeEvent} (so a non-message or non-JSON `data:` event is DROPPED, never thrown —
1596
+ * total). It reuses the SAME `SSEParser` a server's `createStream` seam serializes against, so
1597
+ * the wire round-trips. A `null` body (no stream) yields no messages;
1598
+ * {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport} reads a
1599
+ * request/response SSE reply (the server sends one `data:` event then ends), so this drains to
1600
+ * completion.
1601
+ *
1602
+ * @param response - The SSE `fetch` Response to decode (its `body` is read to completion)
1603
+ * @returns Every {@link JSONRPCMessage} the stream carried, in order
1604
+ *
1605
+ * @example
1606
+ * ```ts
1607
+ * const messages = await readEventStream(await fetch(url, { method: 'POST', body }))
1608
+ * ```
1609
+ */
1610
+ async function readEventStream(response) {
1611
+ const body = response.body;
1612
+ if (body === null) return [];
1613
+ const reader = body.getReader();
1614
+ const decoder = new TextDecoder();
1615
+ const parser = createSSEParser();
1616
+ const messages = [];
1617
+ try {
1618
+ for (;;) {
1619
+ const { done, value } = await reader.read();
1620
+ if (done) break;
1621
+ for (const event of parser.parse(decoder.decode(value, { stream: true }))) {
1622
+ const message = decodeEvent(event.data);
1623
+ if (message !== void 0) messages.push(message);
1624
+ }
1625
+ }
1626
+ } finally {
1627
+ reader.releaseLock();
1628
+ }
1629
+ return messages;
1630
+ }
1631
+ /**
1632
+ * Builds the error for a non-success HTTP response that carried no JSON-RPC message.
1633
+ *
1634
+ * @param response - The response whose status is reported
1635
+ * @param type - The response's content type, or an empty string when absent
1636
+ * @returns An error naming the HTTP status and unsupported response shape
1637
+ *
1638
+ * @example
1639
+ * ```ts
1640
+ * const error = buildResponseError(new Response('', { status: 500 }), '')
1641
+ * ```
1642
+ */
1643
+ function buildResponseError(response, type) {
1644
+ if (type.includes("application/json")) return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained an application/json body that was not a JSON-RPC message`);
1645
+ if (type.includes("text/event-stream")) return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained a text/event-stream body without a JSON-RPC message`);
1646
+ const shape = type === "" ? "a body without a content type" : `an unsupported '${type}' body`;
1647
+ return /* @__PURE__ */ new Error(`HTTP ${response.status} response contained ${shape}`);
1465
1648
  }
1466
1649
  /**
1467
1650
  * Reads the value one standard MCP request header carries, decoding the Base64 sentinel.
@@ -1721,7 +1904,7 @@ function renderHeaderValue(value, primitive) {
1721
1904
  *
1722
1905
  * @remarks
1723
1906
  * The projection SEP-2243 requires of an HTTP client, and the same derivation a server runs
1724
- * to know what the request should have carried. Each parameter's value is read at its exact
1907
+ * to know what the request must carry. Each parameter's value is read at its exact
1725
1908
  * property path in the call's own `arguments`; an absent or `null` value omits its header
1726
1909
  * entirely, which is the protocol's distinction between "not supplied" and "supplied empty".
1727
1910
  * The rendered text then travels through {@link encodeSentinel}, so a value carrying
@@ -1952,7 +2135,7 @@ function bindServer(server, transport) {
1952
2135
  * @remarks
1953
2136
  * The client's outbound writes flow through `client.transport.send` — its existing,
1954
2137
  * unmodified request/response correlation — so `client` must have been constructed
1955
- * with a {@link import('./types.js').MCPClientTransportInterface} that itself carries
2138
+ * with a {@link import('./types.js').MCPMessageTransportInterface} that itself carries
1956
2139
  * the SAME `transport` (see {@link import('./factories.js').createDuplexClientTransport},
1957
2140
  * the additive factory that adapts an {@link MCPTransportInterface} into that shape);
1958
2141
  * this binder then completes the inbound half by decoding each message and pushing it
@@ -2065,7 +2248,7 @@ function isMCPResultMetaObject(value) {
2065
2248
  * {@link JSONRPCId}, because a stamp naming nothing addressable is worse than no stamp.
2066
2249
  *
2067
2250
  * @param value - The unknown value to inspect
2068
- * @returns `true` when the value is exact metadata whose subscription stamp, if present, is valid
2251
+ * @returns True if the value is exact metadata whose subscription stamp, if present, is valid; false otherwise
2069
2252
  *
2070
2253
  * @example
2071
2254
  * ```ts
@@ -2088,7 +2271,7 @@ function isMCPLoggingLevel(value) {
2088
2271
  * Determines whether a value is standard padded base64 as required by JSON Schema `byte` format.
2089
2272
  *
2090
2273
  * @param value - The unknown value to inspect
2091
- * @returns Whether the value is an empty or completely padded standard base64 encoding
2274
+ * @returns True if the value is an empty or completely padded standard base64 encoding; false otherwise
2092
2275
  */
2093
2276
  function isStandardBase64(value) {
2094
2277
  return isString(value) && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
@@ -2104,7 +2287,7 @@ function isStandardBase64(value) {
2104
2287
  * {@link MCP_PARAM_PREFIX} and must survive as an HTTP field name.
2105
2288
  *
2106
2289
  * @param value - The unknown value to inspect
2107
- * @returns Whether the value is a non-empty RFC 9110 token
2290
+ * @returns True if the value is a non-empty RFC 9110 token; false otherwise
2108
2291
  *
2109
2292
  * @example
2110
2293
  * ```ts
@@ -2124,7 +2307,7 @@ function isFieldToken(value) {
2124
2307
  * exactly, and the server compares it numerically.
2125
2308
  *
2126
2309
  * @param value - The unknown value to inspect
2127
- * @returns Whether the value is one of `'string'`, `'integer'`, or `'boolean'`
2310
+ * @returns True if the value is one of `'string'`, `'integer'`, or `'boolean'`; false otherwise
2128
2311
  *
2129
2312
  * @example
2130
2313
  * ```ts
@@ -2143,7 +2326,7 @@ function isMCPHeaderPrimitive(value) {
2143
2326
  * scheme allowlist. Component scanning is bounded by the input length.
2144
2327
  *
2145
2328
  * @param value - The unknown value to inspect
2146
- * @returns Whether the value is an RFC 3986 URI rather than a relative reference
2329
+ * @returns True if the value is an RFC 3986 URI rather than a relative reference; false otherwise
2147
2330
  */
2148
2331
  function isAbsoluteURI(value) {
2149
2332
  if (!isString(value) || value.length === 0) return false;
@@ -2239,7 +2422,7 @@ function isAbsoluteURI(value) {
2239
2422
  * refuse. It is a SYNTAX guard: no time zone, locale, calendar era, or leap second applies.
2240
2423
  *
2241
2424
  * @param value - The unknown value to inspect
2242
- * @returns Whether the value is an RFC 3339 `full-date` for a day that exists
2425
+ * @returns True if the value is an RFC 3339 `full-date` for a day that exists; false otherwise
2243
2426
  *
2244
2427
  * @example
2245
2428
  * ```ts
@@ -2272,7 +2455,7 @@ function isRFC3339Date(value) {
2272
2455
  * second.
2273
2456
  *
2274
2457
  * @param value - The unknown value to inspect
2275
- * @returns Whether the value is an RFC 3339 `date-time` for a day that exists
2458
+ * @returns True if the value is an RFC 3339 `date-time` for a day that exists; false otherwise
2276
2459
  *
2277
2460
  * @example
2278
2461
  * ```ts
@@ -2290,7 +2473,7 @@ function isRFC3339DateTime(value) {
2290
2473
  * Determines whether a value is one exact finite MCP progress payload.
2291
2474
  *
2292
2475
  * @param value - The unknown value to inspect
2293
- * @returns Whether required progress and optional total/message fields match the dated schema
2476
+ * @returns True if required progress and optional total/message fields match the dated schema; false otherwise
2294
2477
  */
2295
2478
  function isMCPProgress(value) {
2296
2479
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2309,7 +2492,7 @@ function isMCPProgress(value) {
2309
2492
  * Determines whether a value carries valid dated-schema MCP content annotations.
2310
2493
  *
2311
2494
  * @param value - The unknown value to inspect
2312
- * @returns Whether the value is valid MCP annotations
2495
+ * @returns True if the value is valid MCP annotations; false otherwise
2313
2496
  */
2314
2497
  function isMCPAnnotations(value) {
2315
2498
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2330,7 +2513,7 @@ function isMCPAnnotations(value) {
2330
2513
  * Determines whether a value is one exact dated-schema MCP icon.
2331
2514
  *
2332
2515
  * @param value - The unknown value to inspect
2333
- * @returns Whether the value is a valid MCP icon
2516
+ * @returns True if the value is a valid MCP icon; false otherwise
2334
2517
  */
2335
2518
  function isMCPIcon(value) {
2336
2519
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2421,7 +2604,7 @@ function isMCPServerCapabilities(value) {
2421
2604
  * Determines whether a value is embedded textual MCP resource contents.
2422
2605
  *
2423
2606
  * @param value - The unknown value to inspect
2424
- * @returns Whether the value is embedded textual resource contents
2607
+ * @returns True if the value is embedded textual resource contents; false otherwise
2425
2608
  */
2426
2609
  function isMCPTextResource(value) {
2427
2610
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2440,7 +2623,7 @@ function isMCPTextResource(value) {
2440
2623
  * Determines whether a value is embedded blob MCP resource contents.
2441
2624
  *
2442
2625
  * @param value - The unknown value to inspect
2443
- * @returns Whether the value is embedded blob resource contents
2626
+ * @returns True if the value is embedded blob resource contents; false otherwise
2444
2627
  */
2445
2628
  function isMCPBlobResource(value) {
2446
2629
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2459,7 +2642,7 @@ function isMCPBlobResource(value) {
2459
2642
  * Determines whether a value is one `resources/list` descriptor.
2460
2643
  *
2461
2644
  * @param value - The unknown value to inspect
2462
- * @returns Whether the value is a valid resource descriptor
2645
+ * @returns True if the value is a valid resource descriptor; false otherwise
2463
2646
  */
2464
2647
  function isMCPResource(value) {
2465
2648
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2481,7 +2664,7 @@ function isMCPResource(value) {
2481
2664
  * level belong to the consumer-supplied resource manager; this package projects the string.
2482
2665
  *
2483
2666
  * @param value - The unknown value to inspect
2484
- * @returns Whether the value is a valid resource-template descriptor
2667
+ * @returns True if the value is a valid resource-template descriptor; false otherwise
2485
2668
  */
2486
2669
  function isMCPResourceTemplate(value) {
2487
2670
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2498,7 +2681,7 @@ function isMCPResourceTemplate(value) {
2498
2681
  * Determines whether a value is structurally discriminated resource contents.
2499
2682
  *
2500
2683
  * @param value - The unknown value to inspect
2501
- * @returns Whether exactly one of `text` and `blob` is present and valid
2684
+ * @returns True if exactly one of `text` and `blob` is present and valid; false otherwise
2502
2685
  */
2503
2686
  function isMCPResourceContents(value) {
2504
2687
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2512,7 +2695,7 @@ function isMCPResourceContents(value) {
2512
2695
  * Determines whether a value carries the shared optional pagination cursor.
2513
2696
  *
2514
2697
  * @param value - The unknown value to inspect
2515
- * @returns Whether a present `cursor` is a string
2698
+ * @returns True if a present `cursor` is a string; false otherwise
2516
2699
  */
2517
2700
  function isMCPPaginationParams(value) {
2518
2701
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2522,7 +2705,7 @@ function isMCPPaginationParams(value) {
2522
2705
  * Determines whether a value is one consumer-owned resource page.
2523
2706
  *
2524
2707
  * @param value - The unknown value to inspect
2525
- * @returns Whether the resources and optional following cursor are valid
2708
+ * @returns True if the resources and optional following cursor are valid; false otherwise
2526
2709
  */
2527
2710
  function isMCPResourcePage(value) {
2528
2711
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2535,7 +2718,7 @@ function isMCPResourcePage(value) {
2535
2718
  * Determines whether a value is one consumer-owned resource-template page.
2536
2719
  *
2537
2720
  * @param value - The unknown value to inspect
2538
- * @returns Whether the templates and optional following cursor are valid
2721
+ * @returns True if the templates and optional following cursor are valid; false otherwise
2539
2722
  */
2540
2723
  function isMCPResourceTemplatePage(value) {
2541
2724
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2548,7 +2731,7 @@ function isMCPResourceTemplatePage(value) {
2548
2731
  * Determines whether a value is a string-valued MCP argument record.
2549
2732
  *
2550
2733
  * @param value - The unknown value to inspect
2551
- * @returns Whether every own argument value is a string
2734
+ * @returns True if every own argument value is a string; false otherwise
2552
2735
  */
2553
2736
  function isMCPStringArguments(value) {
2554
2737
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2558,7 +2741,7 @@ function isMCPStringArguments(value) {
2558
2741
  * Determines whether a value is one prompt argument descriptor.
2559
2742
  *
2560
2743
  * @param value - The unknown value to inspect
2561
- * @returns Whether the prompt argument descriptor is valid
2744
+ * @returns True if the prompt argument descriptor is valid; false otherwise
2562
2745
  */
2563
2746
  function isMCPPromptArgument(value) {
2564
2747
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2570,7 +2753,7 @@ function isMCPPromptArgument(value) {
2570
2753
  * Determines whether a value is one `prompts/list` descriptor.
2571
2754
  *
2572
2755
  * @param value - The unknown value to inspect
2573
- * @returns Whether the prompt descriptor is valid
2756
+ * @returns True if the prompt descriptor is valid; false otherwise
2574
2757
  */
2575
2758
  function isMCPPrompt(value) {
2576
2759
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2584,7 +2767,7 @@ function isMCPPrompt(value) {
2584
2767
  * Determines whether a value is one prompt message with existing rich content.
2585
2768
  *
2586
2769
  * @param value - The unknown value to inspect
2587
- * @returns Whether the role and content are valid
2770
+ * @returns True if the role and content are valid; false otherwise
2588
2771
  */
2589
2772
  function isMCPPromptMessage(value) {
2590
2773
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2594,7 +2777,7 @@ function isMCPPromptMessage(value) {
2594
2777
  * Determines whether a value is one consumer-owned prompt page.
2595
2778
  *
2596
2779
  * @param value - The unknown value to inspect
2597
- * @returns Whether the prompts and optional following cursor are valid
2780
+ * @returns True if the prompts and optional following cursor are valid; false otherwise
2598
2781
  */
2599
2782
  function isMCPPromptPage(value) {
2600
2783
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2607,7 +2790,7 @@ function isMCPPromptPage(value) {
2607
2790
  * Determines whether a value is one complete `prompts/get` result.
2608
2791
  *
2609
2792
  * @param value - The unknown value to inspect
2610
- * @returns Whether the prompt result and all messages are valid
2793
+ * @returns True if the prompt result and all messages are valid; false otherwise
2611
2794
  */
2612
2795
  function isMCPPromptGetResult(value) {
2613
2796
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2620,7 +2803,7 @@ function isMCPPromptGetResult(value) {
2620
2803
  * Determines whether a value is a prompt or resource-template completion reference.
2621
2804
  *
2622
2805
  * @param value - The unknown value to inspect
2623
- * @returns Whether the discriminated reference is valid
2806
+ * @returns True if the discriminated reference is valid; false otherwise
2624
2807
  */
2625
2808
  function isMCPCompletionReference(value) {
2626
2809
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2632,7 +2815,7 @@ function isMCPCompletionReference(value) {
2632
2815
  * Determines whether a value is one `completion/complete` parameter object.
2633
2816
  *
2634
2817
  * @param value - The unknown value to inspect
2635
- * @returns Whether its reference, fragment, and optional string context are valid
2818
+ * @returns True if its reference, fragment, and optional string context are valid; false otherwise
2636
2819
  */
2637
2820
  function isMCPCompletionParams(value) {
2638
2821
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2651,7 +2834,7 @@ function isMCPCompletionParams(value) {
2651
2834
  * Determines whether a value is one host-produced completion candidate set.
2652
2835
  *
2653
2836
  * @param value - The unknown value to inspect
2654
- * @returns Whether its candidates and optional result facts are valid
2837
+ * @returns True if its candidates and optional result facts are valid; false otherwise
2655
2838
  */
2656
2839
  function isMCPCompletion(value) {
2657
2840
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2665,7 +2848,7 @@ function isMCPCompletion(value) {
2665
2848
  * Determines whether a value is one complete, capped `completion/complete` result.
2666
2849
  *
2667
2850
  * @param value - The unknown value to inspect
2668
- * @returns Whether the result is complete and carries at most 100 candidates
2851
+ * @returns True if the result is complete and carries at most 100 candidates; false otherwise
2669
2852
  */
2670
2853
  function isMCPCompletionResult(value) {
2671
2854
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2677,7 +2860,7 @@ function isMCPCompletionResult(value) {
2677
2860
  * Determines whether a value is one exact dated-schema MCP tool content block.
2678
2861
  *
2679
2862
  * @param value - The unknown value to inspect
2680
- * @returns Whether the value is valid MCP content
2863
+ * @returns True if the value is valid MCP content; false otherwise
2681
2864
  */
2682
2865
  function isMCPContent(value) {
2683
2866
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2718,7 +2901,7 @@ function isMCPContent(value) {
2718
2901
  * input.
2719
2902
  *
2720
2903
  * @param value - The unknown value to inspect
2721
- * @returns Whether the value is a modern result
2904
+ * @returns True if the value is a modern result; false otherwise
2722
2905
  *
2723
2906
  * @example
2724
2907
  * ```ts
@@ -2744,7 +2927,7 @@ function isMCPResult(value) {
2744
2927
  * hostile input.
2745
2928
  *
2746
2929
  * @param value - The unknown value to inspect
2747
- * @returns Whether the value is a legacy result
2930
+ * @returns True if the value is a legacy result; false otherwise
2748
2931
  *
2749
2932
  * @example
2750
2933
  * ```ts
@@ -2760,7 +2943,7 @@ function isMCPLegacyResult(value) {
2760
2943
  * Determines whether a value is a complete modern MCP tool result.
2761
2944
  *
2762
2945
  * @param value - The unknown value to inspect
2763
- * @returns Whether the value is a complete MCP call result
2946
+ * @returns True if the value is a complete MCP call result; false otherwise
2764
2947
  */
2765
2948
  function isMCPCallResult(value) {
2766
2949
  const owned = attempt(() => cloneJSONRecord(value));
@@ -2788,7 +2971,7 @@ function isMCPCallResult(value) {
2788
2971
  * INTEGER milliseconds because the schema formats them `int`.
2789
2972
  *
2790
2973
  * @param value - The unknown value to inspect
2791
- * @returns Whether the value is a well-formed `resultType: 'task'` result
2974
+ * @returns True if the value is a well-formed `resultType: 'task'` result; false otherwise
2792
2975
  *
2793
2976
  * @example
2794
2977
  * ```ts
@@ -2815,7 +2998,7 @@ function isMCPTaskResult(value) {
2815
2998
  * Determines whether a value is one of the extension's task lifecycle states.
2816
2999
  *
2817
3000
  * @param value - The unknown value to inspect
2818
- * @returns Whether the value is an {@link MCPTaskStatus}
3001
+ * @returns True if the value is an {@link MCPTaskStatus}; false otherwise
2819
3002
  *
2820
3003
  * @example
2821
3004
  * ```ts
@@ -2846,7 +3029,7 @@ function isMCPTaskStatus(value) {
2846
3029
  * What is checked is what this package publishes as the contract.
2847
3030
  *
2848
3031
  * @param value - The unknown value to inspect
2849
- * @returns Whether the value is a well-formed {@link MCPTaskDetail}
3032
+ * @returns True if the value is a well-formed {@link MCPTaskDetail}; false otherwise
2850
3033
  *
2851
3034
  * @example
2852
3035
  * ```ts
@@ -2889,7 +3072,7 @@ function isMCPTaskDetail(value) {
2889
3072
  * peer stamps there is the peer's to write.
2890
3073
  *
2891
3074
  * @param value - The unknown value to inspect
2892
- * @returns Whether the value is a well-formed {@link MCPTaskDetailResult}
3075
+ * @returns True if the value is a well-formed {@link MCPTaskDetailResult}; false otherwise
2893
3076
  *
2894
3077
  * @example
2895
3078
  * ```ts
@@ -2923,7 +3106,7 @@ function isMCPTaskDetailResult(value) {
2923
3106
  * to it, so a guard that demanded the stamp would refuse every frame a producer emits.
2924
3107
  *
2925
3108
  * @param value - The unknown value to inspect
2926
- * @returns Whether the value is a well-formed `notifications/tasks` notification
3109
+ * @returns True if the value is a well-formed `notifications/tasks` notification; false otherwise
2927
3110
  *
2928
3111
  * @example
2929
3112
  * ```ts
@@ -2951,7 +3134,7 @@ function isMCPTaskNotification(value) {
2951
3134
  *
2952
3135
  * @param value - The unknown value to inspect
2953
3136
  * @param bytes - The maximum accepted encoded bytes
2954
- * @returns `true` only for a string whose UTF-8 representation fits the bound
3137
+ * @returns True if `value` is a string whose UTF-8 representation fits the bound; false otherwise
2955
3138
  *
2956
3139
  * @example
2957
3140
  * ```ts
@@ -2987,7 +3170,7 @@ function isBoundedString(value, bytes) {
2987
3170
  *
2988
3171
  * @param value - The unknown value to inspect
2989
3172
  * @param limits - Serialized byte, optional key, and nesting-depth bounds
2990
- * @returns `true` only for safe JSON satisfying every bound
3173
+ * @returns True if `value` is safe JSON satisfying every bound; false otherwise
2991
3174
  *
2992
3175
  * @example
2993
3176
  * ```ts
@@ -3009,7 +3192,7 @@ function isBoundedJSON(value, limits) {
3009
3192
  * no minimum length. Total: any other input returns `false`.
3010
3193
  *
3011
3194
  * @param value - The already-parsed value to test
3012
- * @returns `true` when `value` is a string or a finite integer
3195
+ * @returns True if `value` is a string or a finite integer; false otherwise
3013
3196
  *
3014
3197
  * @example
3015
3198
  * ```ts
@@ -3027,7 +3210,7 @@ function isJSONRPCId(value) {
3027
3210
  * Determines whether a value is a supported {@link MCPVersion}.
3028
3211
  *
3029
3212
  * @param value - The unknown value to inspect
3030
- * @returns `true` when the value is one of {@link SUPPORTED_MCP_VERSIONS}
3213
+ * @returns True if the value is one of {@link SUPPORTED_MCP_VERSIONS}; false otherwise
3031
3214
  */
3032
3215
  function isMCPVersion(value) {
3033
3216
  return isString(value) && SUPPORTED_MCP_VERSIONS.some((version) => version === value);
@@ -3036,7 +3219,7 @@ function isMCPVersion(value) {
3036
3219
  * Determines whether a value is a modern protocol revision accepted by a bare server.
3037
3220
  *
3038
3221
  * @param value - The unknown value to inspect
3039
- * @returns `true` when the value is one of {@link SUPPORTED_MODERN_PROTOCOL_VERSIONS}
3222
+ * @returns True if the value is one of {@link SUPPORTED_MODERN_PROTOCOL_VERSIONS}; false otherwise
3040
3223
  */
3041
3224
  function isMCPModernVersion(value) {
3042
3225
  return isString(value) && SUPPORTED_MODERN_PROTOCOL_VERSIONS.some((version) => version === value);
@@ -3045,7 +3228,7 @@ function isMCPModernVersion(value) {
3045
3228
  * Determines whether a value is a revision accepted by the optional legacy decorator.
3046
3229
  *
3047
3230
  * @param value - The unknown value to inspect
3048
- * @returns `true` when the value is one of {@link SUPPORTED_LEGACY_PROTOCOL_VERSIONS}
3231
+ * @returns True if the value is one of {@link SUPPORTED_LEGACY_PROTOCOL_VERSIONS}; false otherwise
3049
3232
  */
3050
3233
  function isMCPLegacyVersion(value) {
3051
3234
  return isString(value) && SUPPORTED_LEGACY_PROTOCOL_VERSIONS.some((version) => version === value);
@@ -3064,7 +3247,7 @@ function isMCPLegacyVersion(value) {
3064
3247
  * the caller asked for.
3065
3248
  *
3066
3249
  * @param value - The unknown value to inspect
3067
- * @returns `true` when every recognized filter field has its protocol shape
3250
+ * @returns True if every recognized filter field has its protocol shape; false otherwise
3068
3251
  */
3069
3252
  function isMCPSubscriptionFilter(value) {
3070
3253
  const owned = attempt(() => cloneJSONRecord(value));
@@ -3085,7 +3268,7 @@ function isMCPSubscriptionFilter(value) {
3085
3268
  * Determines whether a value is a graceful `subscriptions/listen` result.
3086
3269
  *
3087
3270
  * @param value - The unknown value to inspect
3088
- * @returns `true` when the result is complete and carries a valid subscription id
3271
+ * @returns True if the result is complete and carries a valid subscription id; false otherwise
3089
3272
  */
3090
3273
  function isMCPSubscriptionResult(value) {
3091
3274
  const owned = attempt(() => cloneJSONRecord(value));
@@ -3097,7 +3280,7 @@ function isMCPSubscriptionResult(value) {
3097
3280
  * Determines whether a value is one restricted primitive form-elicitation schema.
3098
3281
  *
3099
3282
  * @param value - The unknown value to inspect
3100
- * @returns `true` for a supported boolean, numeric, string, or string-array schema
3283
+ * @returns True if `value` is a supported boolean, numeric, string, or string-array schema; false otherwise
3101
3284
  *
3102
3285
  * @example
3103
3286
  * ```ts
@@ -3160,7 +3343,7 @@ function isMCPElicitFieldSchema(value) {
3160
3343
  * an unrecognized top-level annotation is data rather than a rejection.
3161
3344
  *
3162
3345
  * @param value - The unknown value to inspect
3163
- * @returns `true` when `value` is a restricted object schema of supported field schemas
3346
+ * @returns True if `value` is a restricted object schema of supported field schemas; false otherwise
3164
3347
  *
3165
3348
  * @example
3166
3349
  * ```ts
@@ -3187,7 +3370,7 @@ function isMCPElicitSchema(value) {
3187
3370
  * Determines whether a value is a form-mode elicitation parameter object.
3188
3371
  *
3189
3372
  * @param value - The unknown value to inspect
3190
- * @returns `true` when `value` has the restricted form elicitation shape
3373
+ * @returns True if `value` has the restricted form elicitation shape; false otherwise
3191
3374
  *
3192
3375
  * @example
3193
3376
  * ```ts
@@ -3213,7 +3396,7 @@ function isMCPElicitForm(value) {
3213
3396
  * Determines whether a value is a URL-mode elicitation parameter object.
3214
3397
  *
3215
3398
  * @param value - The unknown value to inspect
3216
- * @returns `true` when `value` has the URL elicitation shape
3399
+ * @returns True if `value` has the URL elicitation shape; false otherwise
3217
3400
  *
3218
3401
  * @example
3219
3402
  * ```ts
@@ -3234,7 +3417,7 @@ function isMCPElicitURL(value) {
3234
3417
  * Determines whether a value is an embedded `elicitation/create` request.
3235
3418
  *
3236
3419
  * @param value - The unknown value to inspect
3237
- * @returns `true` when `value` is a form- or URL-mode elicitation request
3420
+ * @returns True if `value` is a form- or URL-mode elicitation request; false otherwise
3238
3421
  *
3239
3422
  * @example
3240
3423
  * ```ts
@@ -3259,7 +3442,7 @@ function isMCPElicitRequest(value) {
3259
3442
  * Determines whether a value is one legal embedded multi-round-trip request.
3260
3443
  *
3261
3444
  * @param value - The unknown value to inspect
3262
- * @returns `true` for an embedded elicitation, sampling, or roots request
3445
+ * @returns True if `value` is an embedded elicitation, sampling, or roots request; false otherwise
3263
3446
  *
3264
3447
  * @example
3265
3448
  * ```ts
@@ -3283,7 +3466,7 @@ function isMCPInputRequest(value) {
3283
3466
  * Determines whether a value is a consumer-keyed map of embedded input requests.
3284
3467
  *
3285
3468
  * @param value - The unknown value to inspect
3286
- * @returns `true` when every own value is a legal {@link MCPInputRequest}
3469
+ * @returns True if every own value is a legal {@link MCPInputRequest}; false otherwise
3287
3470
  *
3288
3471
  * @example
3289
3472
  * ```ts
@@ -3303,7 +3486,7 @@ function isMCPInputRequestMap(value) {
3303
3486
  * Determines whether a value is one elicitation response.
3304
3487
  *
3305
3488
  * @param value - The unknown value to inspect
3306
- * @returns `true` when action/content have the protocol shape
3489
+ * @returns True if action/content have the protocol shape; false otherwise
3307
3490
  *
3308
3491
  * @example
3309
3492
  * ```ts
@@ -3353,7 +3536,7 @@ function isMCPElicitResult(value) {
3353
3536
  *
3354
3537
  * @param value - The accepted response content to check
3355
3538
  * @param schema - The exact {@link MCPElicitSchema} that was issued with the elicitation
3356
- * @returns `true` when every declared and undeclared value is legal under `schema`
3539
+ * @returns True if every declared and undeclared value is legal under `schema`; false otherwise
3357
3540
  *
3358
3541
  * @example
3359
3542
  * ```ts
@@ -3442,7 +3625,7 @@ function isElicitContent(value, schema) {
3442
3625
  * including a URL-mode elicitation's `url`. Total over hostile input.
3443
3626
  *
3444
3627
  * @param value - The unknown value to inspect
3445
- * @returns `true` when `value` carries an absolute `uri` and an optional string `name`
3628
+ * @returns True if `value` carries an absolute `uri` and an optional string `name`; false otherwise
3446
3629
  *
3447
3630
  * @example
3448
3631
  * ```ts
@@ -3472,7 +3655,7 @@ function isMCPRoot(value) {
3472
3655
  * {@link isMCPRoot}. Total over hostile input.
3473
3656
  *
3474
3657
  * @param value - The unknown value to inspect
3475
- * @returns `true` when `value` carries an array of valid roots
3658
+ * @returns True if `value` carries an array of valid roots; false otherwise
3476
3659
  *
3477
3660
  * @example
3478
3661
  * ```ts
@@ -3505,7 +3688,7 @@ function isMCPRootResult(value) {
3505
3688
  * input.
3506
3689
  *
3507
3690
  * @param value - The unknown value to inspect
3508
- * @returns `true` when `value` is one legal sampling content block
3691
+ * @returns True if `value` is one legal sampling content block; false otherwise
3509
3692
  *
3510
3693
  * @example
3511
3694
  * ```ts
@@ -3546,7 +3729,7 @@ function isMCPSampleContent(value) {
3546
3729
  * names four values and permits any other a provider reports. Total over hostile input.
3547
3730
  *
3548
3731
  * @param value - The unknown value to inspect
3549
- * @returns `true` when `value` has the sampling-completion shape
3732
+ * @returns True if `value` has the sampling-completion shape; false otherwise
3550
3733
  *
3551
3734
  * @example
3552
3735
  * ```ts
@@ -3594,7 +3777,7 @@ function isMCPSampleResult(value) {
3594
3777
  *
3595
3778
  * @param value - The client's answer to check
3596
3779
  * @param request - The exact {@link MCPInputRequest} that was issued under the same key
3597
- * @returns `true` when the answer is legal for that request
3780
+ * @returns True if the answer is legal for that request; false otherwise
3598
3781
  *
3599
3782
  * @example
3600
3783
  * ```ts
@@ -3622,7 +3805,7 @@ function isMCPInputResponse(value, request) {
3622
3805
  * both must be present and valid. Total over hostile input.
3623
3806
  *
3624
3807
  * @param value - The unknown value to inspect
3625
- * @returns `true` when `value` is a valid input-required result
3808
+ * @returns True if `value` is a valid input-required result; false otherwise
3626
3809
  *
3627
3810
  * @example
3628
3811
  * ```ts
@@ -3659,7 +3842,7 @@ function isMCPInputResult(value) {
3659
3842
  * be a record. Total: any other input returns `false`.
3660
3843
  *
3661
3844
  * @param value - The already-parsed value to test
3662
- * @returns `true` when `value` is a valid JSON-RPC request
3845
+ * @returns True if `value` is a valid JSON-RPC request; false otherwise
3663
3846
  *
3664
3847
  * @example
3665
3848
  * ```ts
@@ -3686,7 +3869,7 @@ function isJSONRPCRequest(value) {
3686
3869
  * be a record. Total: any other input returns `false`.
3687
3870
  *
3688
3871
  * @param value - The already-parsed value to test
3689
- * @returns `true` when `value` is a valid JSON-RPC notification
3872
+ * @returns True if `value` is a valid JSON-RPC notification; false otherwise
3690
3873
  *
3691
3874
  * @example
3692
3875
  * ```ts
@@ -3712,7 +3895,7 @@ function isJSONRPCNotification(value) {
3712
3895
  * mutually exclusive, so a positive answer names exactly one arm. Total.
3713
3896
  *
3714
3897
  * @param value - The already-parsed value to test
3715
- * @returns `true` when `value` is a valid JSON-RPC request or notification
3898
+ * @returns True if `value` is a valid JSON-RPC request or notification; false otherwise
3716
3899
  */
3717
3900
  function isJSONRPCInvocation(value) {
3718
3901
  return isJSONRPCRequest(value) || isJSONRPCNotification(value);
@@ -3730,7 +3913,7 @@ function isJSONRPCInvocation(value) {
3730
3913
  * Total.
3731
3914
  *
3732
3915
  * @param value - The already-parsed value to test
3733
- * @returns `true` when `value` is a valid JSON-RPC result response
3916
+ * @returns True if `value` is a valid JSON-RPC result response; false otherwise
3734
3917
  *
3735
3918
  * @example
3736
3919
  * ```ts
@@ -3766,7 +3949,7 @@ function isJSONRPCResultResponse(value) {
3766
3949
  * itself the hostile step, and it is bounded here rather than allowed to escape. Total.
3767
3950
  *
3768
3951
  * @param value - The already-parsed value to test
3769
- * @returns `true` when `value` carries an integer `code` and a string `message`
3952
+ * @returns True if `value` carries an integer `code` and a string `message`; false otherwise
3770
3953
  *
3771
3954
  * @example
3772
3955
  * ```ts
@@ -3790,7 +3973,7 @@ function isJSONRPCError(value) {
3790
3973
  * `result`. `error` carries an integer `code` and a string `message`. Total.
3791
3974
  *
3792
3975
  * @param value - The already-parsed value to test
3793
- * @returns `true` when `value` is a valid JSON-RPC error response
3976
+ * @returns True if `value` is a valid JSON-RPC error response; false otherwise
3794
3977
  *
3795
3978
  * @example
3796
3979
  * ```ts
@@ -3814,7 +3997,7 @@ function isJSONRPCErrorResponse(value) {
3814
3997
  * The union of the mutually exclusive arms. Total.
3815
3998
  *
3816
3999
  * @param value - The already-parsed value to test
3817
- * @returns `true` when `value` is a valid JSON-RPC response
4000
+ * @returns True if `value` is a valid JSON-RPC response; false otherwise
3818
4001
  */
3819
4002
  function isJSONRPCResponse(value) {
3820
4003
  return isJSONRPCResultResponse(value) || isJSONRPCErrorResponse(value);
@@ -3827,7 +4010,7 @@ function isJSONRPCResponse(value) {
3827
4010
  * The union of {@link isJSONRPCInvocation} and {@link isJSONRPCResponse}. Total.
3828
4011
  *
3829
4012
  * @param value - The already-parsed value to test
3830
- * @returns `true` when `value` is a valid JSON-RPC message
4013
+ * @returns True if `value` is a valid JSON-RPC message; false otherwise
3831
4014
  */
3832
4015
  function isJSONRPCMessage(value) {
3833
4016
  return isJSONRPCInvocation(value) || isJSONRPCResponse(value);
@@ -3836,7 +4019,7 @@ function isJSONRPCMessage(value) {
3836
4019
  * Determines whether a parsed value is an MCP `initialize` invocation.
3837
4020
  *
3838
4021
  * @param value - The already-parsed value to test
3839
- * @returns `true` when `value` is a valid `initialize` request or notification
4022
+ * @returns True if `value` is a valid `initialize` request or notification; false otherwise
3840
4023
  *
3841
4024
  * @example
3842
4025
  * ```ts
@@ -3859,7 +4042,7 @@ function isInitializeRequest(value) {
3859
4042
  * legacy dispatch. Total over hostile and malformed input.
3860
4043
  *
3861
4044
  * @param value - The already-parsed value to inspect
3862
- * @returns `true` when the value is an invocation carrying the reserved version key
4045
+ * @returns True if the value is an invocation carrying the reserved version key; false otherwise
3863
4046
  */
3864
4047
  function isModernRequest(value) {
3865
4048
  const owned = attempt(() => cloneJSONRecord(value));
@@ -3889,6 +4072,27 @@ function inferEra(version) {
3889
4072
  if (isMCPLegacyVersion(version)) return "legacy";
3890
4073
  }
3891
4074
  /**
4075
+ * Infers the wire era one invocation's own structure selects.
4076
+ *
4077
+ * @remarks
4078
+ * The STRUCTURAL read, distinct from {@link inferEra}'s read of a revision string: era is fixed
4079
+ * by the reserved modern metadata a request carries, so this answers for a message whose
4080
+ * revision has not been read and cannot answer `undefined` — every invocation took one of the
4081
+ * two published wire shapes. It is what an observation surface reports and what an ingress
4082
+ * routes on, so both derive it here rather than each spelling the ternary out.
4083
+ *
4084
+ * @param invocation - The invocation whose structure selects the era
4085
+ * @returns `'modern'` when the invocation carries the modern request shape, `'legacy'` otherwise
4086
+ *
4087
+ * @example
4088
+ * ```ts
4089
+ * inferRequestEra({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: { _meta: meta } })
4090
+ * ```
4091
+ */
4092
+ function inferRequestEra(invocation) {
4093
+ return isModernRequest(invocation) ? "modern" : "legacy";
4094
+ }
4095
+ /**
3892
4096
  * Infers the newest supported modern protocol revision present in a peer's offer.
3893
4097
  *
3894
4098
  * @param offered - The protocol revisions offered by the peer
@@ -3934,7 +4138,7 @@ function inferRequestVersion(message) {
3934
4138
  //#endregion
3935
4139
  //#region src/core/MCPMethodManager.ts
3936
4140
  /**
3937
- * The modern method registry an {@link import('./types.js').MCPServerInterface}
4141
+ * Holds the modern methods an {@link import('./types.js').MCPServerInterface}
3938
4142
  * dispatches through — a name-keyed store of {@link MCPMethodHandler}s that owns its
3939
4143
  * map rather than exposing one.
3940
4144
  *
@@ -3968,7 +4172,7 @@ var MCPMethodManager = class {
3968
4172
  //#endregion
3969
4173
  //#region src/core/MCPProgressReporter.ts
3970
4174
  /**
3971
- * A bounded, request-scoped progress handoff between one producer and one serial consumer.
4175
+ * Hands bounded, request-scoped progress from one producer to one serial consumer.
3972
4176
  *
3973
4177
  * The reporter holds at most one owned progress item. {@link report} applies backpressure until
3974
4178
  * {@link take} consumes that slot. It has no replay, queue, concurrent-consumer coordination,
@@ -4092,7 +4296,8 @@ var MCPProgressReporter = class {
4092
4296
  //#endregion
4093
4297
  //#region src/core/MCPStreamController.ts
4094
4298
  /**
4095
- * The one cancellation engine every modern held-open result leaves `MCPServer` through.
4299
+ * Provides the one cancellation engine every modern held-open result leaves `MCPServer`
4300
+ * through.
4096
4301
  *
4097
4302
  * @remarks
4098
4303
  * A native async generator decides cancellation with a QUEUE: `return()` and `throw()` wait
@@ -4289,7 +4494,7 @@ var MCPStreamController = class {
4289
4494
  //#endregion
4290
4495
  //#region src/core/MCPTextStreamController.ts
4291
4496
  /**
4292
- * The string-boundary mirror of a controlled held-open result — the same exchange, already
4497
+ * Mirrors a controlled held-open result at the string boundary — the same exchange, already
4293
4498
  * serialized.
4294
4499
  *
4295
4500
  * @remarks
@@ -4451,12 +4656,7 @@ var MCPLegacy = class {
4451
4656
  }
4452
4657
  async handle(message, options) {
4453
4658
  if (!isBoundedString(message, this.limit.message)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request"));
4454
- let parsed;
4455
- try {
4456
- parsed = JSON.parse(message);
4457
- } catch {
4458
- return this.#options.dispatcher.handle(message, options);
4459
- }
4659
+ const parsed = parseJSON(message);
4460
4660
  if (isModernRequest(parsed) || !isJSONRPCInvocation(parsed)) return this.#options.dispatcher.handle(message, options);
4461
4661
  const answer = await this.#legacy(parsed, options);
4462
4662
  return answer === void 0 ? void 0 : JSON.stringify(answer);
@@ -4543,7 +4743,7 @@ var MCPLegacyClientTransport = class {
4543
4743
  if (requested !== void 0 && !isMCPLegacyVersion(requested)) throw new MCPError("Unsupported legacy protocol version", MCP_UNSUPPORTED_VERSION, { requested });
4544
4744
  this.#transport = transport;
4545
4745
  this.#client = options?.identity ?? {
4546
- name: "taverna",
4746
+ name: "@orkestrel/mcp",
4547
4747
  version: "1.0.0"
4548
4748
  };
4549
4749
  this.#capabilities = options?.capabilities ?? {};
@@ -4735,8 +4935,8 @@ var MCPLegacyClientTransport = class {
4735
4935
  //#endregion
4736
4936
  //#region src/core/MCPServer.ts
4737
4937
  /**
4738
- * A transport-agnostic Model Context Protocol server dispatches JSON-RPC 2.0
4739
- * requests over a live {@link ToolManagerInterface}, with NO transport coupling.
4938
+ * Dispatches JSON-RPC 2.0 requests over a live {@link ToolManagerInterface}, with NO
4939
+ * transport coupling.
4740
4940
  *
4741
4941
  * @remarks
4742
4942
  * - **`dispatch` and `handle`.** `dispatch(invocation)` runs an already-parsed invocation and
@@ -4813,8 +5013,21 @@ var MCPServer = class {
4813
5013
  if (decoded === void 0 || !("method" in decoded)) return buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request");
4814
5014
  return this.#dispatch(decoded, options);
4815
5015
  }
5016
+ async handle(message, options) {
5017
+ if (!isBoundedString(message, this.#limits.message)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
5018
+ const parsed = parseJSON(message);
5019
+ if (parsed === void 0) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
5020
+ const decoded = parseJSONRPCMessage(parsed, {
5021
+ bytes: this.#limits.message,
5022
+ depth: this.#limits.depth
5023
+ });
5024
+ if (decoded === void 0 || !("method" in decoded)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request"));
5025
+ const answer = await this.#dispatch(decoded, options ?? {});
5026
+ if (answer === void 0) return void 0;
5027
+ return Symbol.asyncIterator in answer ? new MCPTextStreamController(answer) : JSON.stringify(answer);
5028
+ }
4816
5029
  async #dispatch(invocation, options) {
4817
- this.#emitter.emit("request", invocation.method, invocation.id, "modern");
5030
+ this.#emitter.emit("request", invocation.method, invocation.id, inferRequestEra(invocation));
4818
5031
  if (invocation.id === void 0) return;
4819
5032
  const id = invocation.id;
4820
5033
  const metadata = invocation.params?.["_meta"];
@@ -4832,26 +5045,9 @@ var MCPServer = class {
4832
5045
  return this.#contain(error, id);
4833
5046
  }
4834
5047
  }
4835
- async handle(message, options) {
4836
- if (!isBoundedString(message, this.#limits.message)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
4837
- let parsed;
4838
- try {
4839
- parsed = JSON.parse(message);
4840
- } catch {
4841
- return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_PARSE_ERROR, "Parse error"));
4842
- }
4843
- const decoded = parseJSONRPCMessage(parsed, {
4844
- bytes: this.#limits.message,
4845
- depth: this.#limits.depth
4846
- });
4847
- if (decoded === void 0 || !("method" in decoded)) return JSON.stringify(buildJSONRPCError(void 0, JSONRPC_INVALID_REQUEST, "Invalid Request"));
4848
- const answer = await this.#dispatch(decoded, options ?? {});
4849
- if (answer === void 0) return void 0;
4850
- return Symbol.asyncIterator in answer ? new MCPTextStreamController(answer) : JSON.stringify(answer);
4851
- }
4852
5048
  #register() {
4853
- this.#methods.add("server/discover", async (request, _options) => this.#discover(request));
4854
- this.#methods.add("tools/list", async (request, _options) => this.#list(request));
5049
+ this.#methods.add("server/discover", async (request) => this.#discover(request));
5050
+ this.#methods.add("tools/list", async (request) => this.#list(request));
4855
5051
  this.#methods.add("tools/call", async (request, options) => this.#call(request, options));
4856
5052
  this.#methods.add("subscriptions/listen", async (request, options) => this.#subscribe(request, options));
4857
5053
  const resources = this.#options.resources;
@@ -5065,20 +5261,20 @@ var MCPServer = class {
5065
5261
  async #defer(request, call, options) {
5066
5262
  const configured = this.#options.task;
5067
5263
  if (configured === void 0) return void 0;
5068
- const deferral = {
5264
+ const deferred = {
5069
5265
  request,
5070
5266
  call,
5071
5267
  tools: this.#options.tools
5072
5268
  };
5073
- const key = await configured.defer(deferral, options);
5269
+ const key = await configured.deferral(deferred, options);
5074
5270
  if (isUndefined(key)) return void 0;
5075
5271
  if (!isString(key) || key.length === 0) return buildJSONRPCError(request.id, JSONRPC_INTERNAL_ERROR, "Server execution returned an invalid task key");
5076
5272
  const context = parseRequestContext(request, {
5077
5273
  bytes: this.#limits.message,
5078
5274
  depth: this.#limits.depth
5079
5275
  });
5080
- if (context === void 0 || !isTaskSupported(context.capabilities)) return buildJSONRPCError(request.id, MCP_MISSING_CAPABILITY, "Client does not support the required Tasks extension", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5081
- const created = await configured.tasks.start(key, deferral, options);
5276
+ if (context === void 0 || !supportsTask(context.capabilities)) return buildJSONRPCError(request.id, MCP_MISSING_CAPABILITY, "Client does not support the required Tasks extension", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5277
+ const created = await configured.tasks.start(key, deferred, options);
5082
5278
  const captured = snapshotJSON({
5083
5279
  resultType: "task",
5084
5280
  taskId: created.taskId,
@@ -5162,7 +5358,7 @@ var MCPServer = class {
5162
5358
  arguments: args
5163
5359
  }, options);
5164
5360
  if (selected === void 0) return void 0;
5165
- const round = this.#round(selected);
5361
+ const round = this.#ownRound(selected);
5166
5362
  const context = parseRequestContext(request, {
5167
5363
  bytes: this.#limits.message,
5168
5364
  depth: this.#limits.depth
@@ -5215,7 +5411,7 @@ var MCPServer = class {
5215
5411
  const state = parseMCPInputState(verified);
5216
5412
  if (state === void 0) return this.#contain(/* @__PURE__ */ new Error("Continuation port opened a malformed protected payload"), id);
5217
5413
  if (state.expiry <= Date.now() || state.id === id || state.version !== context.version || state.method !== request.method || state.name !== name || state.digest !== digest) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request state could not be verified for this retry");
5218
- const responses = this.#answers(state.requests, inputResponses);
5414
+ const responses = this.#checkAnswers(state.requests, inputResponses);
5219
5415
  if (responses === void 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: an input response is missing or malformed");
5220
5416
  const principal = await configured.principal(request, options);
5221
5417
  if (!isString(principal) || principal.length === 0 || state.principal !== principal) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request state could not be verified for this retry");
@@ -5228,13 +5424,13 @@ var MCPServer = class {
5228
5424
  }, options);
5229
5425
  if (state.expiry <= Date.now()) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request state could not be verified for this retry");
5230
5426
  if (selected === void 0) return void 0;
5231
- const round = this.#round(selected);
5427
+ const round = this.#ownRound(selected);
5232
5428
  if (round === void 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: input policy returned an invalid round or continuation context");
5233
5429
  const refusal = this.#gate(round, context, id);
5234
5430
  if (refusal !== void 0) return refusal;
5235
5431
  return this.#required(request, name, digest, round, principal, state.id, state.expiry);
5236
5432
  }
5237
- #answers(requests, responses) {
5433
+ #checkAnswers(requests, responses) {
5238
5434
  const answered = {};
5239
5435
  for (const [key, issued] of Object.entries(requests)) {
5240
5436
  const response = responses[key];
@@ -5243,7 +5439,7 @@ var MCPServer = class {
5243
5439
  }
5244
5440
  return Object.freeze(answered);
5245
5441
  }
5246
- #round(round) {
5442
+ #ownRound(round) {
5247
5443
  const owned = snapshotJSON(round, {
5248
5444
  bytes: this.#limits.content,
5249
5445
  keys: this.#limits.keys,
@@ -5331,7 +5527,7 @@ var MCPServer = class {
5331
5527
  }
5332
5528
  yield buildSubscriptionAcknowledgement(notifications, id);
5333
5529
  if (configured !== void 0) {
5334
- const iterator = (await configured.listen(notifications, options))[Symbol.asyncIterator]();
5530
+ const iterator = (await configured.producer(notifications, options))[Symbol.asyncIterator]();
5335
5531
  options.signal.addEventListener("abort", () => void iterator.return?.(void 0)?.catch(() => void 0), { once: true });
5336
5532
  for (let next = await iterator.next(); next.done !== true; next = await iterator.next()) {
5337
5533
  const owned = parseJSONRPCMessage(next.value, {
@@ -5350,22 +5546,22 @@ var MCPServer = class {
5350
5546
  slot.abort();
5351
5547
  }
5352
5548
  }
5353
- #named(request) {
5549
+ #readTaskId(request) {
5354
5550
  const id = request.id;
5355
5551
  const context = parseRequestContext(request, {
5356
5552
  bytes: this.#limits.message,
5357
5553
  depth: this.#limits.depth
5358
5554
  });
5359
- if (context === void 0 || !isTaskSupported(context.capabilities)) return buildJSONRPCError(id, MCP_MISSING_CAPABILITY, "Server requires the tasks extension capability for this request", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5555
+ if (context === void 0 || !supportsTask(context.capabilities)) return buildJSONRPCError(id, MCP_MISSING_CAPABILITY, "Server requires the tasks extension capability for this request", { requiredCapabilities: { extensions: { [MCP_EXTENSION_TASKS]: {} } } });
5360
5556
  const taskId = request.params?.["taskId"];
5361
5557
  if (!isBoundedString(taskId, this.#limits.state) || taskId.length === 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: a bounded string `taskId` is required");
5362
5558
  return taskId;
5363
5559
  }
5364
5560
  async #task(request, tasks, options) {
5365
5561
  const id = request.id;
5366
- const named = this.#named(request);
5367
- if (!isString(named)) return named;
5368
- const found = await tasks.task(named, options);
5562
+ const taskId = this.#readTaskId(request);
5563
+ if (!isString(taskId)) return taskId;
5564
+ const found = await tasks.task(taskId, options);
5369
5565
  if (found === void 0) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5370
5566
  const owned = snapshotJSON(found, {
5371
5567
  bytes: this.#limits.content,
@@ -5377,20 +5573,20 @@ var MCPServer = class {
5377
5573
  }
5378
5574
  async #update(request, tasks, options) {
5379
5575
  const id = request.id;
5380
- const named = this.#named(request);
5381
- if (!isString(named)) return named;
5576
+ const taskId = this.#readTaskId(request);
5577
+ if (!isString(taskId)) return taskId;
5382
5578
  const responses = request.params?.["inputResponses"];
5383
5579
  if (!isRecord(responses)) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: an `inputResponses` object is required");
5384
- if (!isMCPTaskDetail(await tasks.task(named, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5385
- await tasks.update(named, responses, options);
5580
+ if (!isMCPTaskDetail(await tasks.task(taskId, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5581
+ await tasks.update(taskId, responses, options);
5386
5582
  return buildJSONRPCResult(id, buildModernResult({}, this.#options.identity));
5387
5583
  }
5388
5584
  async #abort(request, tasks, options) {
5389
5585
  const id = request.id;
5390
- const named = this.#named(request);
5391
- if (!isString(named)) return named;
5392
- if (!isMCPTaskDetail(await tasks.task(named, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5393
- await tasks.abort(named, options);
5586
+ const taskId = this.#readTaskId(request);
5587
+ if (!isString(taskId)) return taskId;
5588
+ if (!isMCPTaskDetail(await tasks.task(taskId, options))) return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: no task is available for that `taskId`");
5589
+ await tasks.abort(taskId, options);
5394
5590
  return buildJSONRPCResult(id, buildModernResult({}, this.#options.identity));
5395
5591
  }
5396
5592
  #contain(error, id) {
@@ -5440,8 +5636,8 @@ var MCPServer = class {
5440
5636
  //#endregion
5441
5637
  //#region src/core/MCPTaskClient.ts
5442
5638
  /**
5443
- * The CLIENT half of the stable Tasks extension — the `tasks/*` methods over one
5444
- * correlated-request door, exposed as an {@link import('./types.js').MCPClientInterface}'s
5639
+ * Issues the `tasks/*` methods over one correlated-request door — the CLIENT half of the
5640
+ * stable Tasks extension, exposed as an {@link import('./types.js').MCPClientInterface}'s
5445
5641
  * `tasks`.
5446
5642
  *
5447
5643
  * @remarks
@@ -5500,9 +5696,9 @@ var MCPTaskClient = class {
5500
5696
  //#endregion
5501
5697
  //#region src/core/MCPClient.ts
5502
5698
  /**
5503
- * A transport-agnostic Model Context Protocol CLIENT connects to a REMOTE MCP server
5504
- * over an injected {@link MCPClientTransportInterface}, negotiates the modern revision, and
5505
- * exposes the server's tools as local {@link ToolInterface}s an agent can run.
5699
+ * Connects to a REMOTE MCP server over any injected {@link MCPMessageTransportInterface},
5700
+ * negotiates the modern revision, and exposes the server's tools as local
5701
+ * {@link ToolInterface}s an agent can run.
5506
5702
  *
5507
5703
  * @remarks
5508
5704
  * - **The mirror of `MCPServer`.** The server DISPATCHES requests over a tool registry;
@@ -5593,7 +5789,7 @@ var MCPClient = class {
5593
5789
  });
5594
5790
  this.#transport = options.transport;
5595
5791
  this.#identity = options.identity ?? {
5596
- name: "taverna",
5792
+ name: "@orkestrel/mcp",
5597
5793
  version: "1.0.0"
5598
5794
  };
5599
5795
  this.#capabilities = options.capabilities ?? {};
@@ -5734,18 +5930,19 @@ var MCPClient = class {
5734
5930
  } }
5735
5931
  }
5736
5932
  };
5737
- const subscription = {
5738
- queue: [],
5739
- capacity
5740
- };
5741
5933
  const abort = this.#abortSubscription.bind(this, id, signal);
5742
5934
  signal.addEventListener("abort", abort, { once: true });
5743
5935
  this.#pending.set(id, {
5744
5936
  method,
5745
5937
  signal,
5746
5938
  abort,
5747
- subscription
5939
+ subscription: {
5940
+ queue: [],
5941
+ capacity
5942
+ }
5748
5943
  });
5944
+ const subscription = this.#pending.get(id)?.subscription;
5945
+ if (subscription === void 0) throw new Error("MCP subscription state is missing");
5749
5946
  this.#transport.send(request).catch((error) => this.#settle(id, error, true));
5750
5947
  try {
5751
5948
  for (;;) {
@@ -6071,13 +6268,222 @@ var MCPClient = class {
6071
6268
  }
6072
6269
  };
6073
6270
  //#endregion
6271
+ //#region src/core/transports/HTTPClientTransport.ts
6272
+ /**
6273
+ * Drives a REMOTE Streamable-HTTP MCP server over `fetch` — a CLIENT
6274
+ * {@link MCPMessageTransportInterface} for the Model Context Protocol, the egress mirror of
6275
+ * the server's `createMCPRoutes`.
6276
+ *
6277
+ * @remarks
6278
+ * - **One class, both faces.** It touches `fetch`, `Response`, `AbortController`,
6279
+ * `AbortSignal`, and `WeakMap` alone, so it is host-independent and lives in core. Each
6280
+ * environment face publishes its own `createHTTPClientTransport` over it —
6281
+ * `@orkestrel/mcp/browser` and `@orkestrel/mcp/server` — and both factories return this
6282
+ * class, so a reply reaches a page and a Node process through the same decode.
6283
+ * - **Request/response over `fetch`.** `send(message)` POSTs the JSON-serialized
6284
+ * message to `options.url` with `content-type: application/json` and an
6285
+ * `Accept` of BOTH `application/json` and `text/event-stream` (so the server may
6286
+ * answer with either framing) — plus any `options.headers` (for example, an `Authorization`
6287
+ * bearer). It then decodes the reply and emits each decoded {@link JSONRPCMessage} on
6288
+ * the `message` event the {@link import('@orkestrel/mcp').MCPClientInterface} subscribes
6289
+ * to.
6290
+ * - **Both reply framings.** A `200` with an `application/json` body is parsed with
6291
+ * `parseJSONRPCMessage`; a `200` with a `text/event-stream` body is decoded with the
6292
+ * `@orkestrel/sse` {@link import('@orkestrel/sse').SSEParserInterface} ({@link
6293
+ * readEventStream}) — the inverse of the server's `createStream` seam, so the wire
6294
+ * round-trips. A `202`
6295
+ * Accepted (a notification) carries no body and emits nothing.
6296
+ * - **Session and protocol headers.** `start()` is a no-op (a
6297
+ * request/response transport opens no long-lived connection). The
6298
+ * `mcp-session-id` response header, when a STATEFUL server sends one (on
6299
+ * `initialize`), is captured into `session` and then ECHOED as the
6300
+ * `mcp-session-id` request header on every SUBSEQUENT request — so an
6301
+ * `MCPClient` passes a stateful server's session validation. The
6302
+ * initialize result's `protocolVersion` is likewise captured, but only
6303
+ * when it is a SUPPORTED value, and echoed as `mcp-protocol-version` alone on
6304
+ * subsequent legacy requests. Modern requests instead derive protocol and method
6305
+ * headers from the message, plus the name header only for `tools/call` — carried in the
6306
+ * protocol's Base64 sentinel form whenever the tool name cannot ride as plain ASCII.
6307
+ * Before initialize returns, neither captured legacy header is sent.
6308
+ * `close()` clears the captured protocol so a reconnect's `initialize`
6309
+ * POST is headerless; the captured `session` persists across `close()`.
6310
+ * - **`close()` releases what is in flight.** Every `fetch` this transport still has open is
6311
+ * ABORTED, which cancels the response body a `send` is reading — an SSE reply the server
6312
+ * never ends would otherwise outlive the transport, with nothing left able to reach it. The
6313
+ * aborted read surfaces on `error` and the `send` reporting it resolves. `close()` is
6314
+ * idempotent (one `close` event per connected lifetime), and `start()` opens the next one.
6315
+ * - **Total at the boundary, and a non-success reply REJECTS.** Every reply is narrowed
6316
+ * (`parseJSONRPCMessage`, the SSE decoder). A non-message success reply is dropped, never
6317
+ * asserted. A non-success reply that carries no valid JSON-RPC message rejects `send` with
6318
+ * an error naming its HTTP status and body shape — the peer answered, and answering the
6319
+ * caller's request with silence would leave it waiting out its own deadline for a failure
6320
+ * the transport already read. A valid JSON-RPC error body is emitted at any HTTP status,
6321
+ * because the protocol carries that outcome in band. A `fetch` or decode failure on a
6322
+ * success response surfaces on the `error` event rather than escaping `send`.
6323
+ * - **Observable.** Owns the `emitter` ({@link MCPMessageTransportEventMap}); fires
6324
+ * `message` per decoded reply, `error` on a fault, and `close` on `close()`.
6325
+ *
6326
+ * @example
6327
+ * ```ts
6328
+ * const transport = new HTTPClientTransport({ url: 'http://localhost:3000/mcp' })
6329
+ * const client = new MCPClient({ transport })
6330
+ * await client.connect()
6331
+ * ```
6332
+ */
6333
+ var HTTPClientTransport = class {
6334
+ #emitter;
6335
+ #url;
6336
+ #headers;
6337
+ #fetch;
6338
+ #timeout;
6339
+ #pending = /* @__PURE__ */ new Set();
6340
+ #parameters = /* @__PURE__ */ new Map();
6341
+ #stamps = /* @__PURE__ */ new WeakMap();
6342
+ #session = void 0;
6343
+ #protocol = void 0;
6344
+ #generation = 0;
6345
+ #closed = false;
6346
+ constructor(options) {
6347
+ this.#emitter = new Emitter();
6348
+ this.#url = options.url;
6349
+ this.#headers = options.headers ?? {};
6350
+ this.#fetch = options.fetch ?? globalThis.fetch.bind(globalThis);
6351
+ this.#timeout = options.timeout;
6352
+ }
6353
+ get emitter() {
6354
+ return this.#emitter;
6355
+ }
6356
+ get session() {
6357
+ return this.#session;
6358
+ }
6359
+ get duplex() {
6360
+ return false;
6361
+ }
6362
+ async start() {
6363
+ this.#closed = false;
6364
+ }
6365
+ async send(message) {
6366
+ this.#stamp(message);
6367
+ const request = new AbortController();
6368
+ this.#pending.add(request);
6369
+ try {
6370
+ await this.#exchange(message, request.signal);
6371
+ } finally {
6372
+ this.#pending.delete(request);
6373
+ }
6374
+ }
6375
+ async close() {
6376
+ if (this.#closed) return;
6377
+ this.#closed = true;
6378
+ for (const request of this.#pending) request.abort();
6379
+ this.#pending.clear();
6380
+ this.#protocol = void 0;
6381
+ this.#emitter.emit("close");
6382
+ }
6383
+ #stamp(message) {
6384
+ if (!isModernRequest(message) || message.method !== "tools/list") return;
6385
+ if (message.params?.["cursor"] === void 0) this.#generation += 1;
6386
+ this.#stamps.set(message, this.#generation);
6387
+ }
6388
+ async #exchange(message, signal) {
6389
+ let response;
6390
+ try {
6391
+ response = await this.#fetch(this.#url, {
6392
+ method: "POST",
6393
+ headers: {
6394
+ "content-type": "application/json",
6395
+ accept: "application/json, text/event-stream",
6396
+ ...this.#session === void 0 ? {} : { [MCP_SESSION_HEADER]: this.#session },
6397
+ ...this.#buildHeaders(message),
6398
+ ...this.#headers
6399
+ },
6400
+ body: JSON.stringify(message),
6401
+ signal: this.#timeout === void 0 ? signal : AbortSignal.any([signal, AbortSignal.timeout(this.#timeout)])
6402
+ });
6403
+ } catch (error) {
6404
+ this.#emitter.emit("error", error);
6405
+ return;
6406
+ }
6407
+ const session = response.headers.get(MCP_SESSION_HEADER);
6408
+ if (session !== null) this.#session = session;
6409
+ await this.#deliver(response, message);
6410
+ }
6411
+ #buildHeaders(message) {
6412
+ if (isModernRequest(message)) {
6413
+ const version = inferRequestVersion(message);
6414
+ const name = message.params?.["name"];
6415
+ return {
6416
+ ...version === void 0 ? {} : { [MCP_PROTOCOL_VERSION_HEADER]: version },
6417
+ [MCP_METHOD_HEADER]: message.method,
6418
+ ...message.method === "tools/call" && isString(name) ? {
6419
+ [MCP_NAME_HEADER]: encodeSentinel(name),
6420
+ ...buildHeaderProjection(this.#parameters.get(name) ?? [], message.params?.["arguments"])
6421
+ } : {}
6422
+ };
6423
+ }
6424
+ return this.#protocol === void 0 ? {} : { [MCP_PROTOCOL_VERSION_HEADER]: this.#protocol };
6425
+ }
6426
+ async #deliver(response, sent) {
6427
+ if (response.status === 202) return;
6428
+ const type = response.headers.get("content-type") ?? "";
6429
+ let messages = [];
6430
+ let failure;
6431
+ try {
6432
+ if (type.includes("text/event-stream")) messages = await readEventStream(response);
6433
+ else if (type.includes("application/json")) {
6434
+ const message = parseJSONRPCMessage(await response.json());
6435
+ if (message !== void 0) messages = [message];
6436
+ }
6437
+ } catch (error) {
6438
+ failure = { error };
6439
+ }
6440
+ for (const message of messages) this.#capture(message, sent);
6441
+ if (!response.ok && messages.length === 0) throw buildResponseError(response, type);
6442
+ if (failure !== void 0) this.#emitter.emit("error", failure.error);
6443
+ }
6444
+ #capture(message, sent) {
6445
+ if (isJSONRPCResponse(message) && isRecord(message.result) && isMCPVersion(message.result["protocolVersion"])) this.#protocol = message.result["protocolVersion"];
6446
+ this.#emitter.emit("message", this.#select(message, sent));
6447
+ }
6448
+ #select(message, sent) {
6449
+ if (!isModernRequest(sent) || sent.method !== "tools/list") return message;
6450
+ if (!isJSONRPCResponse(message) || message.error !== void 0) return message;
6451
+ const result = message.result;
6452
+ const listed = isRecord(result) ? result["tools"] : void 0;
6453
+ if (!isRecord(result) || !isArray(listed)) return message;
6454
+ const current = this.#stamps.get(sent) === this.#generation;
6455
+ if (current && sent.params?.["cursor"] === void 0) this.#parameters.clear();
6456
+ const kept = [];
6457
+ for (const tool of listed) {
6458
+ if (!isRecord(tool) || !isString(tool["name"])) {
6459
+ kept.push(tool);
6460
+ continue;
6461
+ }
6462
+ const parameters = buildHeaderParameters(tool["inputSchema"]);
6463
+ if (parameters === void 0) {
6464
+ this.#emitter.emit("error", /* @__PURE__ */ new Error(`MCP tool '${tool["name"]}' is excluded from tools/list: its inputSchema carries an invalid x-mcp-header annotation`));
6465
+ continue;
6466
+ }
6467
+ if (current) this.#parameters.set(tool["name"], parameters);
6468
+ kept.push(tool);
6469
+ }
6470
+ return {
6471
+ ...message,
6472
+ result: {
6473
+ ...result,
6474
+ tools: kept
6475
+ }
6476
+ };
6477
+ }
6478
+ };
6479
+ //#endregion
6074
6480
  //#region src/core/factories.ts
6075
6481
  /**
6076
6482
  * Creates a transport-agnostic Model Context Protocol server — exposes a live
6077
6483
  * {@link import('@orkestrel/tool').ToolManagerInterface} and an optional
6078
6484
  * {@link import('./types.js').MCPResourceManagerInterface},
6079
6485
  * {@link import('./types.js').MCPPromptManagerInterface}, and
6080
- * {@link import('./types.js').MCPCompletionManagerInterface} over JSON-RPC 2.0.
6486
+ * {@link import('./types.js').MCPCompletionInterface} over JSON-RPC 2.0.
6081
6487
  *
6082
6488
  * @remarks
6083
6489
  * Pump raw message strings through `handle` (parse → dispatch → serialize) from a
@@ -6129,7 +6535,7 @@ function createMCPLegacy(server) {
6129
6535
  }
6130
6536
  /**
6131
6537
  * Creates a transport-agnostic Model Context Protocol CLIENT — connects to a REMOTE
6132
- * MCP server over an injected {@link import('./types.js').MCPClientTransportInterface},
6538
+ * MCP server over an injected {@link import('./types.js').MCPMessageTransportInterface},
6133
6539
  * negotiates the modern revision through `server/discover`, and exposes the server's tools as local
6134
6540
  * {@link import('@orkestrel/tool').ToolInterface}s an agent can run.
6135
6541
  *
@@ -6160,7 +6566,7 @@ function createMCPLegacy(server) {
6160
6566
  * })
6161
6567
  * await client.connect()
6162
6568
  * agent.context.tools.add(await client.tools()) // give the agent the remote tools
6163
- * const value = await client.call('search', { query: 'mcp' })
6569
+ * const outcome = await client.call('search', { query: 'mcp' })
6164
6570
  * ```
6165
6571
  */
6166
6572
  function createMCPClient(options) {
@@ -6186,7 +6592,7 @@ function createMCPLegacyClientTransport(transport, options) {
6186
6592
  }
6187
6593
  /**
6188
6594
  * Adapts an {@link MCPTransportInterface} (the environment-agnostic duplex message
6189
- * channel) into a {@link MCPClientTransportInterface} — the additive bridge that lets
6595
+ * channel) into a {@link MCPMessageTransportInterface} — the additive bridge that lets
6190
6596
  * `createMCPClient` run over the new port without any change to `MCPClient`'s
6191
6597
  * existing shape.
6192
6598
  *
@@ -6209,7 +6615,7 @@ function createMCPLegacyClientTransport(transport, options) {
6209
6615
  * capable emitter for `bindClient` to push onto.
6210
6616
  *
6211
6617
  * @param transport - The duplex channel to adapt
6212
- * @returns A {@link MCPClientTransportInterface} `createMCPClient` can drive
6618
+ * @returns A {@link MCPMessageTransportInterface} `createMCPClient` can drive
6213
6619
  *
6214
6620
  * @example
6215
6621
  * ```ts
@@ -6233,6 +6639,6 @@ function createDuplexClientTransport(transport) {
6233
6639
  };
6234
6640
  }
6235
6641
  //#endregion
6236
- export { DEFAULT_MCP_CACHE_TTL, DEFAULT_MCP_CLIENT_NAME, DEFAULT_MCP_CLIENT_VERSION, DEFAULT_MCP_LIMITS, DEFAULT_MCP_REQUEST_TIMEOUT, DEFAULT_MCP_SUBSCRIPTION_CAPACITY, EMPTY_MCP_ARGUMENTS, JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, JSONRPC_SERVER_ERROR, MCPClient, MCPError, MCPLegacy, MCPLegacyClientTransport, MCPMethodManager, MCPProgressReporter, MCPServer, MCPStreamController, MCPTaskClient, MCPTextStreamController, MCP_EXTENSION_TASKS, MCP_FALLBACK_VERSION, MCP_HANDSHAKE_VERSION, MCP_HEADER_ANNOTATION, MCP_HEADER_MISMATCH, MCP_LOOKUP_PAGES, MCP_META_CAPABILITIES, MCP_META_CLIENT, MCP_META_SERVER, MCP_META_SUBSCRIPTION, MCP_META_VERSION, MCP_MISSING_CAPABILITY, MCP_MODERN_VERSION, MCP_PARAM_PREFIX, MCP_SENTINEL_PREFIX, MCP_SENTINEL_SUFFIX, MCP_UNSUPPORTED_VERSION, SUPPORTED_LEGACY_PROTOCOL_VERSIONS, SUPPORTED_MCP_VERSIONS, SUPPORTED_MODERN_PROTOCOL_VERSIONS, bindClient, bindServer, buildCallOutcome, buildCancelledNotification, buildDiscoverResult, buildHeaderParameters, buildHeaderProjection, buildInitializeResult, buildJSONRPCError, buildJSONRPCResult, buildMethodOptions, buildModernResult, buildProgressNotification, buildSubscriptionAcknowledgement, buildSubscriptionFilter, buildSubscriptionResult, buildToolCall, buildToolDescriptors, computeMissingCapabilities, countHeaderAnnotations, createDuplexClientTransport, createMCPClient, createMCPLegacy, createMCPLegacyClientTransport, createMCPServer, decodeBoundedMessage, decodeSentinel, digestJSON, encodeSentinel, extractContentText, extractHeaderAnnotations, extractToolSchema, inferEra, inferRequestVersion, inferVersion, isAbsoluteURI, isBoundedJSON, isBoundedString, isElicitContent, isFieldToken, isFormElicitationSupported, isInitializeRequest, isJSONObject, isJSONRPCError, isJSONRPCErrorResponse, isJSONRPCId, isJSONRPCInvocation, isJSONRPCMessage, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResponse, isJSONRPCResultResponse, isMCPAnnotations, isMCPBlobResource, isMCPCallResult, isMCPClientCapabilities, isMCPCompletion, isMCPCompletionParams, isMCPCompletionReference, isMCPCompletionResult, isMCPContent, isMCPElicitFieldSchema, isMCPElicitForm, isMCPElicitRequest, isMCPElicitResult, isMCPElicitSchema, isMCPElicitURL, isMCPError, isMCPHeaderPrimitive, isMCPIcon, isMCPIdentity, isMCPInputRequest, isMCPInputRequestMap, isMCPInputResponse, isMCPInputResult, isMCPLegacyResult, isMCPLegacyVersion, isMCPLoggingLevel, isMCPMetaKey, isMCPMetaObject, isMCPModernVersion, isMCPNotificationMetaObject, isMCPPaginationParams, isMCPProgress, isMCPPrompt, isMCPPromptArgument, isMCPPromptGetResult, isMCPPromptMessage, isMCPPromptPage, isMCPResource, isMCPResourceContents, isMCPResourcePage, isMCPResourceTemplate, isMCPResourceTemplatePage, isMCPResult, isMCPResultMetaObject, isMCPRoot, isMCPRootResult, isMCPSampleContent, isMCPSampleResult, isMCPServerCapabilities, isMCPStringArguments, isMCPSubscriptionFilter, isMCPSubscriptionResult, isMCPTaskDetail, isMCPTaskDetailResult, isMCPTaskNotification, isMCPTaskResult, isMCPTaskStatus, isMCPTextResource, isMCPVersion, isModernRequest, isRFC3339Date, isRFC3339DateTime, isStandardBase64, isTaskSupported, legacyInvocationToModern, legacyResultToModern, matchesResultType, matchesSubscriptionNotification, modernInvocationToLegacy, modernResultToLegacy, parseJSONRPCMessage, parseMCPInputState, parseRequestContext, readCancelledId, renderHeaderValue, sendStream, serializeJSON, snapshotJSON, snapshotToolResult, stampSubscriptionNotification };
6642
+ export { DEFAULT_MCP_CACHE_TTL, DEFAULT_MCP_CLIENT_NAME, DEFAULT_MCP_CLIENT_VERSION, DEFAULT_MCP_LIMITS, DEFAULT_MCP_REQUEST_TIMEOUT, DEFAULT_MCP_SUBSCRIPTION_CAPACITY, EMPTY_MCP_ARGUMENTS, HTTPClientTransport, JSONRPC_INTERNAL_ERROR, JSONRPC_INVALID_PARAMS, JSONRPC_INVALID_REQUEST, JSONRPC_METHOD_NOT_FOUND, JSONRPC_PARSE_ERROR, JSONRPC_SERVER_ERROR, MCPClient, MCPError, MCPLegacy, MCPLegacyClientTransport, MCPMethodManager, MCPProgressReporter, MCPServer, MCPStreamController, MCPTaskClient, MCPTextStreamController, MCP_EXTENSION_TASKS, MCP_FALLBACK_VERSION, MCP_HANDSHAKE_VERSION, MCP_HEADER_ANNOTATION, MCP_HEADER_MISMATCH, MCP_LOOKUP_PAGES, MCP_META_CAPABILITIES, MCP_META_CLIENT, MCP_META_SERVER, MCP_META_SUBSCRIPTION, MCP_META_VERSION, MCP_METHOD_HEADER, MCP_MISSING_CAPABILITY, MCP_MODERN_VERSION, MCP_NAME_HEADER, MCP_PARAM_PREFIX, MCP_PROTOCOL_VERSION_HEADER, MCP_SENTINEL_PREFIX, MCP_SENTINEL_SUFFIX, MCP_SESSION_HEADER, MCP_UNSUPPORTED_VERSION, MCP_WEBSOCKET_SUBPROTOCOL, SUPPORTED_LEGACY_PROTOCOL_VERSIONS, SUPPORTED_MCP_VERSIONS, SUPPORTED_MODERN_PROTOCOL_VERSIONS, bindClient, bindServer, buildCallOutcome, buildCancelledNotification, buildDiscoverResult, buildHeaderParameters, buildHeaderProjection, buildInitializeResult, buildJSONRPCError, buildJSONRPCResult, buildMethodOptions, buildModernResult, buildProgressNotification, buildResponseError, buildSubscriptionAcknowledgement, buildSubscriptionFilter, buildSubscriptionResult, buildToolCall, buildToolDescriptors, computeMissingCapabilities, countHeaderAnnotations, createDuplexClientTransport, createMCPClient, createMCPLegacy, createMCPLegacyClientTransport, createMCPServer, decodeBoundedMessage, decodeEvent, decodeSentinel, deliverMessage, digestJSON, encodeSentinel, extractContentText, extractHeaderAnnotations, extractToolSchema, inferEra, inferRequestEra, inferRequestVersion, inferVersion, isAbsoluteURI, isBoundedJSON, isBoundedString, isElicitContent, isFieldToken, isInitializeRequest, isJSONObject, isJSONRPCError, isJSONRPCErrorResponse, isJSONRPCId, isJSONRPCInvocation, isJSONRPCMessage, isJSONRPCNotification, isJSONRPCRequest, isJSONRPCResponse, isJSONRPCResultResponse, isMCPAnnotations, isMCPBlobResource, isMCPCallResult, isMCPClientCapabilities, isMCPCompletion, isMCPCompletionParams, isMCPCompletionReference, isMCPCompletionResult, isMCPContent, isMCPElicitFieldSchema, isMCPElicitForm, isMCPElicitRequest, isMCPElicitResult, isMCPElicitSchema, isMCPElicitURL, isMCPError, isMCPHeaderPrimitive, isMCPIcon, isMCPIdentity, isMCPInputRequest, isMCPInputRequestMap, isMCPInputResponse, isMCPInputResult, isMCPLegacyResult, isMCPLegacyVersion, isMCPLoggingLevel, isMCPMetaKey, isMCPMetaObject, isMCPModernVersion, isMCPNotificationMetaObject, isMCPPaginationParams, isMCPProgress, isMCPPrompt, isMCPPromptArgument, isMCPPromptGetResult, isMCPPromptMessage, isMCPPromptPage, isMCPResource, isMCPResourceContents, isMCPResourcePage, isMCPResourceTemplate, isMCPResourceTemplatePage, isMCPResult, isMCPResultMetaObject, isMCPRoot, isMCPRootResult, isMCPSampleContent, isMCPSampleResult, isMCPServerCapabilities, isMCPStringArguments, isMCPSubscriptionFilter, isMCPSubscriptionResult, isMCPTaskDetail, isMCPTaskDetailResult, isMCPTaskNotification, isMCPTaskResult, isMCPTaskStatus, isMCPTextResource, isMCPVersion, isModernRequest, isRFC3339Date, isRFC3339DateTime, isStandardBase64, legacyInvocationToModern, legacyResultToModern, matchesResultType, matchesSubscriptionNotification, modernInvocationToLegacy, modernResultToLegacy, parseJSONRPCMessage, parseMCPInputState, parseRequestContext, readCancelledId, readEventStream, renderHeaderValue, sendStream, serializeJSON, snapshotJSON, snapshotToolResult, stampSubscriptionNotification, supportsFormElicitation, supportsTask };
6237
6643
 
6238
6644
  //# sourceMappingURL=index.js.map