@orkestrel/mcp 0.0.28 → 0.0.29

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.
@@ -6,7 +6,8 @@ let _orkestrel_emitter = require("@orkestrel/emitter");
6
6
  let _orkestrel_tool = require("@orkestrel/tool");
7
7
  //#region src/core/constants.ts
8
8
  /**
9
- * Names the revision offered and defaulted to in the legacy `initialize` handshake.
9
+ * Names the revision offered and defaulted to in the legacy `initialize` handshake,
10
+ * `'2025-11-25'`.
10
11
  *
11
12
  * @remarks
12
13
  * This is deliberately a legacy revision, and the newest one supported. 2026-07-28 is stateless
@@ -14,12 +15,15 @@ let _orkestrel_tool = require("@orkestrel/tool");
14
15
  * it is asking to negotiate a revision with no negotiation.
15
16
  */
16
17
  var MCP_HANDSHAKE_VERSION = "2025-11-25";
17
- /** Names the older legacy revision the optional legacy decorator accepts and an adapter can pin. */
18
+ /**
19
+ * Names the older legacy revision the optional legacy decorator accepts and an adapter can pin,
20
+ * `'2025-06-18'`.
21
+ */
18
22
  var MCP_FALLBACK_VERSION = "2025-06-18";
19
- /** Names the modern revision offered by an unpinned client during discovery. */
23
+ /** Names the modern revision offered by an unpinned client during discovery, `'2026-07-28'`. */
20
24
  var MCP_MODERN_VERSION = "2026-07-28";
21
25
  /**
22
- * Lists the modern MCP protocol revisions a bare server accepts and advertises.
26
+ * Lists the modern MCP protocol revisions a bare server accepts and advertises, `2026-07-28`.
23
27
  *
24
28
  * @remarks
25
29
  * Frozen in discovery-advertisement order. Legacy revisions are absent because
@@ -27,7 +31,10 @@ var MCP_MODERN_VERSION = "2026-07-28";
27
31
  * decorator own them.
28
32
  */
29
33
  var SUPPORTED_MODERN_PROTOCOL_VERSIONS = Object.freeze([MCP_MODERN_VERSION]);
30
- /** Lists the protocol revisions accepted by the optional legacy decorator. */
34
+ /**
35
+ * Lists the protocol revisions accepted by the optional legacy decorator, `2025-11-25` and
36
+ * `2025-06-18`.
37
+ */
31
38
  var SUPPORTED_LEGACY_PROTOCOL_VERSIONS = Object.freeze([MCP_HANDSHAKE_VERSION, MCP_FALLBACK_VERSION]);
32
39
  /**
33
40
  * Lists the protocol revisions the `isMCPVersion` guard admits, spanning the modern and legacy
@@ -48,8 +55,8 @@ var MCP_META_SUBSCRIPTION = "io.modelcontextprotocol/subscriptionId";
48
55
  * Names the reserved extension key identifying the stable Tasks extension.
49
56
  *
50
57
  * @remarks
51
- * The ONE spelling of it in this package, and the identity of the immutable snapshot dated
52
- * 2026-07-28 this package implements. A client declares it per REQUEST, under
58
+ * The one spelling of it in this package, and the identity of the immutable snapshot dated
59
+ * 2026-07-28 this package implements. A client declares it per request, under
53
60
  * `_meta['io.modelcontextprotocol/clientCapabilities'].extensions`; a server advertises it
54
61
  * under `server/discover`'s `capabilities.extensions`. Both sides carry an empty object —
55
62
  * the extension defines no options, so presence is the entire declaration.
@@ -59,10 +66,10 @@ var MCP_EXTENSION_TASKS = "io.modelcontextprotocol/tasks";
59
66
  * Names the opening marker of the Base64 sentinel a standard MCP header value travels in.
60
67
  *
61
68
  * @remarks
62
- * The markers are LOWERCASE and exact, and this constant with {@link MCP_SENTINEL_SUFFIX} is
63
- * their ONE spelling in this package: {@link import('@orkestrel/mcp').encodeSentinel} builds a
69
+ * The markers are lowercase and exact, and this constant with {@link MCP_SENTINEL_SUFFIX} is
70
+ * their one spelling in this package: {@link import('@orkestrel/mcp').encodeSentinel} builds a
64
71
  * sentinel from them and {@link import('@orkestrel/mcp').decodeSentinel} recognizes one by
65
- * them, so the two directions cannot drift apart.
72
+ * them, so the directions cannot drift apart.
66
73
  */
67
74
  var MCP_SENTINEL_PREFIX = "=?base64?";
68
75
  /** Names the closing marker of the Base64 sentinel a standard MCP header value travels in. */
@@ -81,7 +88,7 @@ var MCP_PARAM_PREFIX = "Mcp-Param-";
81
88
  * Names the Streamable-HTTP transport header that carries the MCP session id.
82
89
  *
83
90
  * @remarks
84
- * A STATEFUL server sends it on the `initialize` reply, and
91
+ * A stateful server sends it on the `initialize` reply, and
85
92
  * {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport} echoes it as a
86
93
  * request header on every subsequent request, so a client passes that server's session
87
94
  * validation unchanged.
@@ -114,7 +121,7 @@ var MCP_NAME_HEADER = "mcp-name";
114
121
  * Identifies the tool-schema annotation key naming the header one parameter projects into.
115
122
  *
116
123
  * @remarks
117
- * It is valid ONLY on a primitive property schema statically reachable from the `inputSchema`
124
+ * It is valid only on a primitive property schema statically reachable from the `inputSchema`
118
125
  * root through `properties` keys alone. An occurrence anywhere else — under `items`, a
119
126
  * composition or conditional keyword, or a `$ref` target — makes the whole tool definition
120
127
  * invalid, which is what {@link import('@orkestrel/mcp').buildHeaderParameters} decides.
@@ -158,7 +165,7 @@ var MCP_HEADER_MISMATCH = -32020;
158
165
  * declared.
159
166
  *
160
167
  * @remarks
161
- * The GENERIC code for the whole condition, not one capability's code. This server answers
168
+ * The generic code for the whole condition, not one capability's code. This server answers
162
169
  * it in more than one place — a `tools/call` that needs `elicitation`, and a `tasks/*` request
163
170
  * whose client never declared `io.modelcontextprotocol/tasks` — and they are told apart by
164
171
  * `error.data.requiredCapabilities` alone (`{ elicitation: {} }` against
@@ -205,7 +212,7 @@ var DEFAULT_MCP_LIMITS = Object.freeze({
205
212
  * Holds the one empty argument record every argument-less modern `tools/call` runs with.
206
213
  *
207
214
  * @remarks
208
- * Frozen and null-prototype, and SHARED: two calls that name no `arguments` receive the same
215
+ * Frozen and null-prototype, and shared: two calls that name no `arguments` receive the same
209
216
  * reference, so nothing a tool writes into its own `arguments` can survive into the next
210
217
  * call — the write fails instead. That failure is a tool-domain failure like any other: the
211
218
  * registry isolates it into a `success: false` result, which reaches the peer as an
@@ -229,7 +236,7 @@ var JSONRPC_INVALID_PARAMS = -32602;
229
236
  * valid request.
230
237
  *
231
238
  * @remarks
232
- * The code every MODERN internal fault answers with — a provider, handler, continuation,
239
+ * The code every modern internal fault answers with — a provider, handler, continuation,
233
240
  * capacity, stream-source, normalization, or serialization failure the server contained.
234
241
  * It is detail-free on the wire: the caught value reaches the application through the
235
242
  * server's `error` event and never through the response.
@@ -239,7 +246,7 @@ var JSONRPC_INTERNAL_ERROR = -32603;
239
246
  * Names the JSON-RPC 2.0 implementation-defined server error (the `-32000` to `-32099` range).
240
247
  *
241
248
  * @remarks
242
- * Retained for the LEGACY branch alone. A modern fault answers
249
+ * Retained for the legacy branch alone. A modern fault answers
243
250
  * {@link JSONRPC_INTERNAL_ERROR}; this code survives only where an old-wire peer was
244
251
  * already characterized against it.
245
252
  */
@@ -438,19 +445,19 @@ function snapshotToolResult(value, limits) {
438
445
  *
439
446
  * @remarks
440
447
  * Total — a non-message returns `undefined`, never throws. The input must
441
- * ALREADY be `JSON.parse`d: the raw-string parse (which can throw on malformed
448
+ * already be `JSON.parse`d: the raw-string parse (which can throw on malformed
442
449
  * JSON) happens in `MCPServer.handle` inside a try/catch that maps a parse failure
443
450
  * to a `-32700` response.
444
451
  *
445
- * A defined result is an OWNED CANONICAL SNAPSHOT, never the input reference: it is
452
+ * A defined result is an owned canonical snapshot, never the input reference: it is
446
453
  * rebuilt from the canonical text and deeply frozen, so `-0` arrives as `0`. Every record
447
- * was SERIALIZED with its keys sorted, but the rebuilt object enumerates its own keys the
454
+ * was serialized with its keys sorted, but the rebuilt object enumerates its own keys the
448
455
  * way JavaScript does, so an integer-like `'9'` still precedes `'10'`: the result's key
449
456
  * order is neither promised nor generally the canonical one. A caller who needs canonical
450
- * BYTES takes them from `serializeJSON`/`snapshotJSON` rather than re-stringifying this
457
+ * bytes takes them from `serializeJSON`/`snapshotJSON` rather than re-stringifying this
451
458
  * result. Identity is not preserved and is not promised.
452
459
  *
453
- * The parser's sound partner is the COMPOSITE `isJSONRPCMessage(value) &&
460
+ * The parser's sound partner is the composite `isJSONRPCMessage(value) &&
454
461
  * isBoundedJSON(value, limits)`, and against it both halves of the soundness law
455
462
  * hold by construction:
456
463
  *
@@ -458,12 +465,12 @@ function snapshotToolResult(value, limits) {
458
465
  * is applied to the exact frozen reference returned.
459
466
  * - Every input satisfying the composite is admitted rather than rejected, because
460
467
  * `isBoundedJSON` is this parser's own admission test — the same canonical
461
- * serializer under the same `limits` — so the two cannot disagree about the bound.
468
+ * serializer under the same `limits` — so they cannot disagree about the bound.
462
469
  *
463
- * {@link isJSONRPCMessage} ALONE is not that partner. It is clone-backed and so already
470
+ * {@link isJSONRPCMessage} Alone is not that partner. It is clone-backed and so already
464
471
  * exact about shape, but it carries no size or depth bound — so guard-valid values
465
472
  * exist that this parser rejects: a message nested deeper than `limits.depth`, and one
466
- * whose canonical text exceeds `limits.bytes`. Those are named causes, NOT a complete
473
+ * whose canonical text exceeds `limits.bytes`. Those are named causes, not a complete
467
474
  * boundary. Among values `isJSONRPCMessage` already admits, the admitted set is exactly
468
475
  * what canonical serialization accepts under `limits`, so a caller who needs that line
469
476
  * tests it with `isBoundedJSON` rather than inferring it from this list.
@@ -493,7 +500,7 @@ function parseJSONRPCMessage(value, limits = {
493
500
  * This is the validity step after {@link isModernRequest}: a defined result can
494
501
  * only come from a guard-positive request, while a guard-positive request returns
495
502
  * `undefined` when its required modern metadata is malformed — and also when the
496
- * request falls outside the bound this parser INHERITS by routing through
503
+ * request falls outside the bound this parser inherits by routing through
497
504
  * {@link parseJSONRPCMessage} under the same `limits`. The version
498
505
  * must be a string but need not be supported; unsupported strings belong to the
499
506
  * dedicated protocol-version error path. Client identity is optional, but when
@@ -542,11 +549,11 @@ function parseRequestContext(value, limits = {
542
549
  * @remarks
543
550
  * This parser does not open the opaque continuation carrier; the configured
544
551
  * continuation port performs that boundary first. The protected
545
- * payload binds the authenticated principal, absolute expiry, ORIGINAL request id, version,
552
+ * payload binds the authenticated principal, absolute expiry, original request id, version,
546
553
  * method, the exact round that was issued, tool name, argument digest, and optional
547
554
  * application state. Every member is required except application state: a payload missing its
548
555
  * round cannot have the client's answers enforced, so it is refused rather than admitted
549
- * unenforced. An EMPTY round is refused for the same reason — a retry against it would answer
556
+ * unenforced. An empty round is refused for the same reason — a retry against it would answer
550
557
  * no question at all. Total over malformed or hostile input.
551
558
  *
552
559
  * @param value - The opened canonical continuation value to parse
@@ -626,7 +633,7 @@ function supportsFormElicitation(value) {
626
633
  * Computes the capabilities one round of input requests needs and the client did not declare.
627
634
  *
628
635
  * @remarks
629
- * The protocol's rule is about SENDING: a server never issues a request kind the client's
636
+ * The protocol's rule is about sending: a server never issues a request kind the client's
630
637
  * declared capabilities exclude. So this reads the round rather than the method, and it
631
638
  * answers with the refusal's own payload — the `requiredCapabilities` record a
632
639
  * `MissingRequiredClientCapability` error carries, keyed by each missing capability, in the
@@ -638,7 +645,7 @@ function supportsFormElicitation(value) {
638
645
  * recognize needs nothing, because {@link import('./validators.js').isMCPInputRequestMap}
639
646
  * has already refused the round it would have travelled in. Total over hostile input.
640
647
  *
641
- * The `elicitation` value names the ARM the round needs, so a client can act on the refusal
648
+ * The `elicitation` value names the arm the round needs, so a client can act on the refusal
642
649
  * by declaring exactly what the payload asks for. A missing URL arm answers `{ url: {} }`, a
643
650
  * missing form arm answers the empty record this package reads as form-only, and a round
644
651
  * needing both answers `{ form: {}, url: {} }`. An empty record for a URL round would name
@@ -689,14 +696,14 @@ function computeMissingCapabilities(requests, capabilities) {
689
696
  *
690
697
  * @remarks
691
698
  * The declaration lives at `extensions['io.modelcontextprotocol/tasks']` and the schema
692
- * types its value EXACTLY EMPTY — `Record<string, never>`, an object with no additional
699
+ * types its value exactly empty — `Record<string, never>`, an object with no additional
693
700
  * properties. So the key's presence is the whole declaration, and the value carries the
694
701
  * whole of the check: a `true` or a string there is a client speaking a different protocol
695
702
  * rather than a shorthand, and a member inside the object is a client declaring an option
696
703
  * this extension does not define. Both are refused, because a server that accepted either
697
704
  * would be reading a shape no peer can produce from the snapshot's own schema.
698
705
  *
699
- * A client declares this PER REQUEST. Nothing here consults a session, because the modern
706
+ * A client declares this per request. Nothing here consults a session, because the modern
700
707
  * revision is stateless and a capability declared once at connect time says nothing about
701
708
  * the request in hand. Total over hostile input.
702
709
  *
@@ -977,7 +984,7 @@ function buildProgressNotification(token, progress) {
977
984
  * Builds one official cancellation notification for a request already sent.
978
985
  *
979
986
  * @remarks
980
- * `requestId` and `reason` are WIRE SPELLINGS carried verbatim from the dated schema's
987
+ * `requestId` and `reason` are wire spellings carried verbatim from the dated schema's
981
988
  * `CancelledNotificationParams`, and so is the `cancelled` in the method name — this
982
989
  * package's own vocabulary says `abort`, but the method is the protocol's and does not
983
990
  * change. The notification is FIRE-AND-FORGET in the strongest sense: it carries no id,
@@ -1015,7 +1022,7 @@ function buildCancelledNotification(id, reason) {
1015
1022
  * Determines whether one method may answer with a given modern `resultType`.
1016
1023
  *
1017
1024
  * @remarks
1018
- * The dated protocol lets a `tools/call` answer in more than one way — it COMPLETED, it became a
1025
+ * The dated protocol lets a `tools/call` answer in more than one way — it completed, it became a
1019
1026
  * durable task, or it needs another round trip — while every other method this client
1020
1027
  * issues has exactly one legal answer. So the arm a peer chose is only meaningful beside
1021
1028
  * the method it answers, and this is the one place that pairing is decided.
@@ -1044,9 +1051,9 @@ function matchesResultType(method, resultType) {
1044
1051
  * Concatenates an MCP tool-call result's text content blocks into one string.
1045
1052
  *
1046
1053
  * @remarks
1047
- * The inverse of a server splitting a value into text block(s), and TOTAL: a non-record
1054
+ * The inverse of a server splitting a value into text block(s), and total: a non-record
1048
1055
  * result, a non-array `content`, or a non-string `text` contributes nothing rather than
1049
- * throwing. What it returns is a RENDERING — the prose a model reads — and not the tool's
1056
+ * throwing. What it returns is a rendering — the prose a model reads — and not the tool's
1050
1057
  * value, which travels as `structuredContent` whenever the peer sent one.
1051
1058
  *
1052
1059
  * @param result - The unknown result payload to read content blocks from
@@ -1073,12 +1080,12 @@ function extractContentText(result) {
1073
1080
  * the arms the protocol gives a shape to, and deriving the tool's value from the one it
1074
1081
  * does not:
1075
1082
  *
1076
- * - A peer's `structuredContent` is PREFERRED over the content blocks, because it is the
1083
+ * - A peer's `structuredContent` is preferred over the content blocks, because it is the
1077
1084
  * tool's value in its original structure while the blocks are a rendering beside it. Its
1078
1085
  * mere presence decides — an explicit `null` is a value the tool returned, not an absence.
1079
1086
  * - With no structured value the legacy shape applies: the value was JSON-serialized into
1080
1087
  * the text block(s), so parse them and fall back to the raw string when they are not JSON.
1081
- * - A remote tool FAILURE (`isError: true`) THROWS the error text, so an agent's tool
1088
+ * - A remote tool failure (`isError: true`) throws the error text, so an agent's tool
1082
1089
  * registry isolates it into a failure result exactly as it would a local throw.
1083
1090
  *
1084
1091
  * @param name - The tool's name, used only to describe a failure that carried no text
@@ -1160,14 +1167,14 @@ function buildJSONRPCResult(id, result) {
1160
1167
  * as an `error` object.
1161
1168
  *
1162
1169
  * @remarks
1163
- * An `undefined` `id` is OMITTED from the envelope rather than serialized as `null`:
1170
+ * An `undefined` `id` is omitted from the envelope rather than serialized as `null`:
1164
1171
  * MCP overrides the base specification here, so a peer that could not have its id
1165
1172
  * read receives a response with no `id` member at all.
1166
1173
  *
1167
1174
  * @param id - The failed request's id, or `undefined` when none could be read
1168
1175
  * @param code - One of the reserved JSON-RPC codes (see `./constants.js`)
1169
1176
  * @param message - A short human description of the failure
1170
- * @param data - An OPTIONAL machine-readable payload (omitted from the envelope when absent)
1177
+ * @param data - An optional machine-readable payload (omitted from the envelope when absent)
1171
1178
  * @returns The error response envelope
1172
1179
  */
1173
1180
  function buildJSONRPCError(id, code, message, data) {
@@ -1189,11 +1196,11 @@ function buildJSONRPCError(id, code, message, data) {
1189
1196
  * receives.
1190
1197
  *
1191
1198
  * @remarks
1192
- * The ONE place a cancellation signal is resolved. A caller may have no signal to
1199
+ * The one place a cancellation signal is resolved. A caller may have no signal to
1193
1200
  * offer; a dispatched method always has one to observe, so a missing signal becomes
1194
1201
  * a real signal rather than an absence every downstream handler would have to case on.
1195
1202
  *
1196
- * The resolved signal is the request's LIFETIME, which is strictly wider than the
1203
+ * The resolved signal is the request's lifetime, which is strictly wider than the
1197
1204
  * caller's: it composes the caller's signal, when there is one, with the `lifetime`
1198
1205
  * dispatch owns and aborts once the answer this request produced is finished. That is
1199
1206
  * what wakes a stream producer parked on an event that will never arrive after its
@@ -1446,7 +1453,7 @@ function buildSubscriptionResult(id, identity) {
1446
1453
  * `capabilities.resources` and `capabilities.prompts` appear only for servers with their
1447
1454
  * respective managers and derive notification flags from the configured subscription filter.
1448
1455
  * `capabilities.completions` is independent and appears only with a completion provider.
1449
- * `capabilities.extensions` appears only for a server that CONFIGURED the extension it
1456
+ * `capabilities.extensions` appears only for a server that configured the extension it
1450
1457
  * would name. An advertisement is a promise a client is entitled to act on, so a server
1451
1458
  * with no `task` policy omits the member entirely rather than advertising an empty
1452
1459
  * record — and its discovery answer stays byte-for-byte what it was before the extension
@@ -1502,7 +1509,7 @@ function buildInitializeResult(name, version, requested) {
1502
1509
  * before it hands the string on.
1503
1510
  *
1504
1511
  * @remarks
1505
- * The bound is checked FIRST, against the raw string, so an oversized message is never
1512
+ * The bound is checked first, against the raw string, so an oversized message is never
1506
1513
  * parsed at all: a decoder that parses before it measures has already spent the work
1507
1514
  * the bound exists to refuse. A message over the bound, malformed JSON, and a well-formed
1508
1515
  * value that is not a JSON-RPC message are one answer — `undefined` — because a binder does
@@ -1529,12 +1536,12 @@ function decodeBoundedMessage(message, limits) {
1529
1536
  * Decodes one inbound frame and delivers it onto a transport emitter as `message` or `error`.
1530
1537
  *
1531
1538
  * @remarks
1532
- * The ONE inbound fold every message-carrying transport in this package runs: parse the frame,
1539
+ * The one inbound fold every message-carrying transport in this package runs: parse the frame,
1533
1540
  * narrow it with `parseJSONRPCMessage`, emit `message` for a well-formed
1534
1541
  * {@link JSONRPCMessage}, and emit `error` for anything else. Total — an adversarial frame
1535
1542
  * produces an `error` emission and never a throw.
1536
1543
  *
1537
- * The two failures report differently on purpose. Unparsable text emits the CAUGHT parse
1544
+ * The failures report differently on purpose. Unparsable text emits the caught parse
1538
1545
  * error, which names the offending position; well-formed JSON that is not a JSON-RPC message
1539
1546
  * has no caught value to report, so it emits `fault` — the carrier's own wording, passed in
1540
1547
  * rather than forked into a second copy of this body.
@@ -1586,15 +1593,15 @@ function decodeEvent(data) {
1586
1593
  }
1587
1594
  /**
1588
1595
  * Decodes a `fetch` Response's Server-Sent-Events body into the JSON-RPC messages it
1589
- * carried — the CLIENT-side inverse of a server's Streamable-HTTP SSE response.
1596
+ * carried — the client-side inverse of a server's Streamable-HTTP SSE response.
1590
1597
  *
1591
1598
  * @remarks
1592
1599
  * Reads the whole `response.body` stream chunk-by-chunk through a `TextDecoder({ stream: true
1593
1600
  * })` (handling a multi-byte character split across reads) and `@orkestrel/sse`'s
1594
1601
  * {@link SSEParserInterface} (handling a partial line or in-progress event split across
1595
1602
  * reads), then narrows each dispatched event's `data` to a {@link JSONRPCMessage} through
1596
- * {@link decodeEvent} (so a non-message or non-JSON `data:` event is DROPPED, never thrown —
1597
- * total). It reuses the SAME `SSEParser` a server's `createStream` seam serializes against, so
1603
+ * {@link decodeEvent} (so a non-message or non-JSON `data:` event is dropped, never thrown —
1604
+ * total). It reuses the same `SSEParser` a server's `createStream` seam serializes against, so
1598
1605
  * the wire round-trips. A `null` body (no stream) yields no messages;
1599
1606
  * {@link import('./transports/HTTPClientTransport.js').HTTPClientTransport} reads a
1600
1607
  * request/response SSE reply (the server sends one `data:` event then ends), so this drains to
@@ -1661,7 +1668,7 @@ function buildResponseError(response, type) {
1661
1668
  * second spelling of a byte, so it is refused: `=?base64?QR==?=` reaches for the byte
1662
1669
  * `=?base64?QQ==?=` spells canonically, and only the canonical spelling decodes. A malformed
1663
1670
  * payload answers `undefined` rather than falling back to the literal, because the protocol
1664
- * requires a server to REJECT invalid characters, and a fallback would admit the very value
1671
+ * requires a server to reject invalid characters, and a fallback would admit the very value
1665
1672
  * the rule exists to refuse. A value missing either marker is a literal and comes back
1666
1673
  * unchanged.
1667
1674
  *
@@ -1707,7 +1714,7 @@ function decodeSentinel(value) {
1707
1714
  *
1708
1715
  * @remarks
1709
1716
  * The exact inverse of {@link decodeSentinel}, and its membership rule is stated as that
1710
- * inverse rather than as a second list that could drift: a value travels LITERALLY when it is
1717
+ * inverse rather than as a second list that could drift: a value travels literally when it is
1711
1718
  * plain printable ASCII — every code point in `U+0020`–`U+007E`, the RFC 9110 field-value
1712
1719
  * range this package admits — and {@link decodeSentinel} gives it back unchanged. Every other
1713
1720
  * value travels wrapped in {@link MCP_SENTINEL_PREFIX} and {@link MCP_SENTINEL_SUFFIX}, the
@@ -1747,7 +1754,7 @@ function encodeSentinel(value) {
1747
1754
  *
1748
1755
  * @remarks
1749
1756
  * The companion of {@link extractHeaderAnnotations}, which reads only the annotations a
1750
- * `properties` chain reaches. Comparing the two answers is how
1757
+ * `properties` chain reaches. Comparing the answers is how
1751
1758
  * {@link buildHeaderParameters} decides reachability without a second walk that would have
1752
1759
  * to re-state which JSON Schema keywords are traversable: an annotation the reachable walk
1753
1760
  * did not read is one sitting under `items`, a composition or conditional keyword, a `$ref`
@@ -1791,12 +1798,12 @@ function countHeaderAnnotations(value) {
1791
1798
  * Reachability is the protocol's own rule: an annotation counts only where a chain of
1792
1799
  * `properties` keys leads to it from the `inputSchema` root, so `path` is both the schema
1793
1800
  * position and the position the call's `arguments` carry the value at. A property named
1794
- * `items` is reachable like any other, because the chain is read by key POSITION rather than
1801
+ * `items` is reachable like any other, because the chain is read by key position rather than
1795
1802
  * by key name.
1796
1803
  *
1797
1804
  * `undefined` means the definition is invalid rather than empty: a reachable annotation whose
1798
1805
  * value is not an {@link import('./validators.js').isFieldToken} token, one sitting on the
1799
- * schema ROOT (which is no property), one on a leaf whose declared type is not an
1806
+ * schema root (which is no property), one on a leaf whose declared type is not an
1800
1807
  * {@link import('./validators.js').isMCPHeaderPrimitive} primitive, or a chain deeper than
1801
1808
  * `DEFAULT_MCP_LIMITS.depth` — which is also what makes a self-referential schema terminate.
1802
1809
  * A node that is not a record carries nothing and answers an empty list, because a leaf the
@@ -1840,8 +1847,8 @@ function extractHeaderAnnotations(schema, path) {
1840
1847
  *
1841
1848
  * @remarks
1842
1849
  * The single decision both sides of the protocol make about an annotated tool: an HTTP
1843
- * CLIENT excludes a definition this refuses from the `tools/list` result it delivers, and a
1844
- * SERVER recognizes exactly the `Mcp-Param-*` names this returns for its own definitions.
1850
+ * client excludes a definition this refuses from the `tools/list` result it delivers, and a
1851
+ * server recognizes exactly the `Mcp-Param-*` names this returns for its own definitions.
1845
1852
  *
1846
1853
  * `undefined` means the definition is invalid, and every rule the protocol states produces
1847
1854
  * it: a value that is not an RFC 9110 token, a non-primitive or untyped annotated leaf, a
@@ -1878,11 +1885,11 @@ function buildHeaderParameters(schema) {
1878
1885
  * Renders one projected argument as the text its `Mcp-Param-*` header carries.
1879
1886
  *
1880
1887
  * @remarks
1881
- * The protocol's conversion table, and the ONE place it is stated: a string travels as
1888
+ * The protocol's conversion table, and the one place it is stated: a string travels as
1882
1889
  * itself, an integer in decimal, and a boolean as lowercase `true` or `false`. The value's
1883
1890
  * runtime shape must match the leaf's declared type, so a schema that declares `integer` and
1884
1891
  * an argument that supplies a string, a fraction, or a magnitude outside the IEEE 754 safe
1885
- * range carries NOTHING — a header that cannot round-trip the body value is worse than an
1892
+ * range carries nothing — a header that cannot round-trip the body value is worse than an
1886
1893
  * absent one, and the tool's own argument validation owns the disagreement.
1887
1894
  *
1888
1895
  * @param value - The argument value read at the parameter's path
@@ -1967,7 +1974,7 @@ function extractToolSchema(response, name) {
1967
1974
  * {@link buildCancelledNotification}.
1968
1975
  *
1969
1976
  * @remarks
1970
- * `requestId` is the WIRE SPELLING carried verbatim from the dated schema, and it must be a
1977
+ * `requestId` is the wire spelling carried verbatim from the dated schema, and it must be a
1971
1978
  * real {@link JSONRPCId}: `null` is not one, and neither is an absent member, so a
1972
1979
  * malformed frame reads as "cancels nothing" rather than as an error. Anything that is not a
1973
1980
  * `notifications/cancelled` notification — a response, a request that happens to use the
@@ -1988,14 +1995,14 @@ function readCancelledId(message) {
1988
1995
  }
1989
1996
  /**
1990
1997
  * Pumps a controlled serialized exchange onto a transport — every notification in order, then
1991
- * the terminating response — and END the exchange however the pump leaves.
1998
+ * the terminating response — and end the exchange however the pump leaves.
1992
1999
  *
1993
2000
  * @remarks
1994
2001
  * The generator's `return` value is a message like any other on the wire: it is sent
1995
- * LAST and closes the exchange. Sends are awaited one at a time so the transport
2002
+ * last and closes the exchange. Sends are awaited one at a time so the transport
1996
2003
  * receives the sequence in the order the method produced it.
1997
2004
  *
1998
- * The first parameter is the CONTROLLED arm rather than a bare
2005
+ * The first parameter is the controlled arm rather than a bare
1999
2006
  * {@link import('./types.js').MCPTextStream}, and that is the whole point of it: this pump is
2000
2007
  * an owner, and an owner needs a lifecycle member to discharge its obligation with. A bare
2001
2008
  * generator has none, so an exit where nothing was cancelled — a `send` that threw two
@@ -2005,7 +2012,7 @@ function readCancelledId(message) {
2005
2012
  * is a no-op for an exchange that already ended on its terminal.
2006
2013
  *
2007
2014
  * The `finally` is spelled explicitly rather than with `await using` because this package's
2008
- * declared Node floor cannot PARSE `await using` — `target: ESNext` emits the declaration
2015
+ * declared Node floor cannot parse `await using` — `target: ESNext` emits the declaration
2009
2016
  * verbatim, and a floor engine rejects the whole module at load. The obligation discharged is
2010
2017
  * identical either way.
2011
2018
  *
@@ -2040,36 +2047,36 @@ async function sendStream(stream, transport) {
2040
2047
  * `server.handle` already turns a malformed message into a serialized `-32700` /
2041
2048
  * `-32600` reply and a notification into `undefined` (no reply), so this binder parses
2042
2049
  * nothing the server would parse differently: it decodes each inbound message through
2043
- * {@link decodeBoundedMessage} under `server.limit`, the SERVER'S OWN bound, so a message
2050
+ * {@link decodeBoundedMessage} under `server.limit`, the server's own bound, so a message
2044
2051
  * the server would refuse is never parsed here either and still receives its `-32700` from
2045
- * the one place that words it. A HELD-OPEN reply arrives as an
2052
+ * the one place that words it. A held-open reply arrives as an
2046
2053
  * {@link import('./types.js').MCPTextStreamControllerInterface} instead of a string: this is
2047
2054
  * the one place that pumps it, writing each notification in order and then the generator's
2048
2055
  * returned terminating response ({@link sendStream}). A `transport.send` throw or rejection —
2049
2056
  * mid-stream included — is caught and routed
2050
2057
  * to `server.emitter`'s `error` event (never rethrown, never an unhandled rejection);
2051
2058
  * a listener on that event that itself throws is swallowed (the end of the line —
2052
- * the caller's own bug, never this binder's). A fault raised AFTER its own request was
2059
+ * the caller's own bug, never this binder's). A fault raised after its own request was
2053
2060
  * cancelled reports nothing, because a cancellation is not a fault.
2054
2061
  *
2055
- * **This binder OWNS every exchange it starts, and ends each one on every exit.** It holds one
2062
+ * **This binder owns every exchange it starts, and ends each one on every exit.** It holds one
2056
2063
  * `AbortController` per live request, keyed by the request's id and deleted whenever that
2057
2064
  * request leaves — normally, by a throw, or by cancellation — and it supplies that signal to
2058
2065
  * `handle` as {@link import('./types.js').MCPDispatchOptions}. These consequences follow.
2059
- * An inbound `notifications/cancelled` ABORTS the request it names, which is how the message-
2066
+ * An inbound `notifications/cancelled` aborts the request it names, which is how the message-
2060
2067
  * based cancellation path reaches a tool on the carriers that have one (stdio, WebSocket,
2061
- * `MessagePort`); a cancelled request writes NO response, because a peer that asked for a call
2068
+ * `MessagePort`); a cancelled request writes no response, because a peer that asked for a call
2062
2069
  * to stop is not answered by it; and the transport's `closed` signal aborts every request
2063
2070
  * still in flight, so an exchange being pumped when the carrier dies ends with it instead of
2064
2071
  * writing into a socket nobody is holding.
2065
2072
  *
2066
- * `listen`/`closed` are REPLACE semantics (§ port contract): the returned unbind
2067
- * DETACHES by replacing this binder's own handlers with no-ops, so a subsequent
2068
- * `bindServer` call on the SAME transport is never double-dispatched by a stale
2073
+ * `listen`/`closed` are replace semantics (§ port contract): the returned unbind
2074
+ * detaches by replacing this binder's own handlers with no-ops, so a subsequent
2075
+ * `bindServer` call on the same transport is never double-dispatched by a stale
2069
2076
  * subscription left behind — an unbind→rebind cycle yields exactly one reply per
2070
2077
  * request. Unbinding is itself an owner exit: it aborts and retires every request still in
2071
2078
  * flight before detaching, so `unbind()` then `close()` and `close()` then `unbind()` end the
2072
- * same exchanges. It does NOT close the transport; that remains the caller's decision.
2079
+ * same exchanges. It does not close the transport; that remains the caller's decision.
2073
2080
  *
2074
2081
  * @param server - The transport-agnostic server to dispatch inbound messages over
2075
2082
  * @param transport - The duplex channel to pipe the server over
@@ -2129,7 +2136,7 @@ function bindServer(server, transport) {
2129
2136
  }
2130
2137
  /**
2131
2138
  * Pipes an {@link MCPTransportInterface} into an {@link MCPClientInterface} — every
2132
- * inbound message is decoded and delivered onto the client's OWN transport
2139
+ * inbound message is decoded and delivered onto the client's own transport
2133
2140
  * (`client.transport.emitter`'s `message` / `close` events), resolving/rejecting the
2134
2141
  * client's correlated pending requests exactly as a direct reply would.
2135
2142
  *
@@ -2137,30 +2144,30 @@ function bindServer(server, transport) {
2137
2144
  * The client's outbound writes flow through `client.transport.send` — its existing,
2138
2145
  * unmodified request/response correlation — so `client` must have been constructed
2139
2146
  * with a {@link import('./types.js').MCPMessageTransportInterface} that itself carries
2140
- * the SAME `transport` (see {@link import('./factories.js').createDuplexClientTransport},
2147
+ * the same `transport` (see {@link import('./factories.js').createDuplexClientTransport},
2141
2148
  * the additive factory that adapts an {@link MCPTransportInterface} into that shape);
2142
2149
  * this binder then completes the inbound half by decoding each message and pushing it
2143
2150
  * onto `client.transport.emitter` (an {@link import('@orkestrel/emitter').EmitterInterface}
2144
2151
  * exposes `emit`, so no client modification is needed). A malformed / non-JSON-RPC
2145
- * inbound message is DROPPED (total — never throws); a delivery fault is routed to
2152
+ * inbound message is dropped (total — never throws); a delivery fault is routed to
2146
2153
  * `client.transport.emitter`'s `error` event (never rethrown). The returned unbind
2147
- * DETACHES this binder (further inbound messages and the transport's `closed` signal are
2148
- * ignored) WITHOUT closing the transport.
2154
+ * detaches this binder (further inbound messages and the transport's `closed` signal are
2155
+ * ignored) without closing the transport.
2149
2156
  *
2150
- * `listen`/`closed` are REPLACE semantics (§ port contract): the returned unbind
2151
- * DETACHES by replacing this binder's own handlers with no-ops, so a subsequent
2152
- * `bindClient` call on the SAME transport is never double-dispatched by a stale
2157
+ * `listen`/`closed` are replace semantics (§ port contract): the returned unbind
2158
+ * detaches by replacing this binder's own handlers with no-ops, so a subsequent
2159
+ * `bindClient` call on the same transport is never double-dispatched by a stale
2153
2160
  * subscription left behind — an unbind→rebind cycle delivers exactly one `message`
2154
2161
  * emit per inbound reply.
2155
2162
  *
2156
2163
  * **This binder needs no live-request registry, and the asymmetry with {@link bindServer} is
2157
- * real rather than an omission.** A server binder holds the lifetime of work it STARTED, so an
2164
+ * real rather than an omission.** A server binder holds the lifetime of work it started, so an
2158
2165
  * inbound `notifications/cancelled` has something to reach; a client binder starts no work —
2159
2166
  * `MCPClient` already owns its pending entries and already writes the cancellation frame
2160
2167
  * itself when a caller's `signal` aborts, on a carrier declaring `duplex`. Adding a registry
2161
- * here would be a second correlation table for ids the client is already correlating, and two
2162
- * tables for one fact drift. The one obligation this binder does carry is delivery: a
2163
- * malformed / non-JSON-RPC inbound message is DROPPED (total — never throws).
2168
+ * here would be a second correlation table for ids the client is already correlating, and a
2169
+ * pair of tables for one fact drift. The one obligation this binder does carry is delivery: a
2170
+ * malformed / non-JSON-RPC inbound message is dropped (total — never throws).
2164
2171
  *
2165
2172
  * @param client - The transport-agnostic client whose transport to deliver messages onto
2166
2173
  * @param transport - The duplex channel to pipe the client over
@@ -2244,8 +2251,8 @@ function isMCPResultMetaObject(value) {
2244
2251
  * subscription id.
2245
2252
  *
2246
2253
  * @remarks
2247
- * The reserved key is OPTIONAL, so a frame delivered outside a `subscriptions/listen`
2248
- * stream passes with no stamp at all. When the key IS present its value must be a valid
2254
+ * The reserved key is optional, so a frame delivered outside a `subscriptions/listen`
2255
+ * stream passes with no stamp at all. When the key is present its value must be a valid
2249
2256
  * {@link JSONRPCId}, because a stamp naming nothing addressable is worse than no stamp.
2250
2257
  *
2251
2258
  * @param value - The unknown value to inspect
@@ -2412,7 +2419,7 @@ function isAbsoluteURI(value) {
2412
2419
  * Determines whether a value is one RFC 3339 `full-date` naming a real calendar day.
2413
2420
  *
2414
2421
  * @remarks
2415
- * RFC 3339 §5.6 defines `date-mday` as `01-28`, `29`, `30`, or `31` BASED ON the month and
2422
+ * RFC 3339 §5.6 defines `date-mday` as `01-28`, `29`, `30`, or `31` based on the month and
2416
2423
  * year, so the grammar is not satisfied by shape alone: `2026-02-30` and `2025-02-29` are
2417
2424
  * well-formed triples that name no day, and a downstream `new Date` rolls each of them
2418
2425
  * silently onto a different date rather than refusing it. February's length follows the
@@ -2420,7 +2427,7 @@ function isAbsoluteURI(value) {
2420
2427
  *
2421
2428
  * The check is pure integer arithmetic on the matched fields and never constructs a `Date`,
2422
2429
  * because `Date` is exactly the component that performs the rollover this guard exists to
2423
- * refuse. It is a SYNTAX guard: no time zone, locale, calendar era, or leap second applies.
2430
+ * refuse. It is a syntax guard: no time zone, locale, calendar era, or leap second applies.
2424
2431
  *
2425
2432
  * @param value - The unknown value to inspect
2426
2433
  * @returns True if the value is an RFC 3339 `full-date` for a day that exists; false otherwise
@@ -2894,7 +2901,7 @@ function isMCPContent(value) {
2894
2901
  *
2895
2902
  * @remarks
2896
2903
  * The open contract's guard: a record carrying a string `resultType` and, when
2897
- * present, exact result metadata. It deliberately does NOT narrow `resultType` to a
2904
+ * present, exact result metadata. It deliberately does not narrow `resultType` to a
2898
2905
  * known value, because the dated schema keeps adding them — a caller that needs a
2899
2906
  * specific result uses that result's own guard, which narrows to its literal.
2900
2907
  * Mutually exclusive with {@link isMCPLegacyResult} on every input: this one needs
@@ -2969,7 +2976,7 @@ function isMCPCallResult(value) {
2969
2976
  * proof: this is what stands between a manager that answers a numeric `taskId` and a
2970
2977
  * client that would receive one. `ttlMs` accepts `null` because the schema uses it to
2971
2978
  * mean "no expiry", which is distinct from an absent field, and both durations must be
2972
- * INTEGER milliseconds because the schema formats them `int`.
2979
+ * integer milliseconds because the schema formats them `int`.
2973
2980
  *
2974
2981
  * @param value - The unknown value to inspect
2975
2982
  * @returns True if the value is a well-formed `resultType: 'task'` result; false otherwise
@@ -3020,7 +3027,7 @@ function isMCPTaskStatus(value) {
3020
3027
  * the requests to answer, `completed` owns the deferred call's result, `failed` owns the
3021
3028
  * JSON-RPC error that ended it, and `working` / `cancelled` own nothing further.
3022
3029
  *
3023
- * A `completed` task's `result` is checked as an OBJECT and no further. The schema declares
3030
+ * A `completed` task's `result` is checked as an object and no further. The schema declares
3024
3031
  * it an open record, so its contents belong to whichever method was deferred; a guard that
3025
3032
  * demanded a protocol result here would refuse payloads the extension permits.
3026
3033
  * `ttlMs` and `pollIntervalMs` are integer milliseconds, per the schema's `int` formats.
@@ -3062,11 +3069,11 @@ function isMCPTaskDetail(value) {
3062
3069
  * Determines whether a value is the wire answer to `tasks/get`.
3063
3070
  *
3064
3071
  * @remarks
3065
- * {@link isMCPTaskDetail} plus the stamp the METHOD owes. The schema types a `tasks/get`
3072
+ * {@link isMCPTaskDetail} plus the stamp the method owes. The schema types a `tasks/get`
3066
3073
  * reply as the detail intersected with the standard result, so `resultType: 'complete'` is
3067
3074
  * part of the answer rather than decoration on it — and an unstamped payload, or one
3068
3075
  * carrying the creation answer's `resultType: 'task'`, is a peer answering some other
3069
- * shape. Use this guard wherever a `tasks/get` REPLY is read; use
3076
+ * shape. Use this guard wherever a `tasks/get` reply is read; use
3070
3077
  * {@link isMCPTaskDetail} wherever a consumer's manager answers directly.
3071
3078
  *
3072
3079
  * `_meta` is checked only when present, and only as result metadata: the server identity a
@@ -3096,14 +3103,14 @@ function isMCPTaskDetailResult(value) {
3096
3103
  * Determines whether a value is a `notifications/tasks` frame carrying a task snapshot.
3097
3104
  *
3098
3105
  * @remarks
3099
- * The ADMISSION guard for a task transition: a subscription producer is consumer-written,
3106
+ * The admission guard for a task transition: a subscription producer is consumer-written,
3100
3107
  * so the frame it hands over is foreign input, and this is what stands between a mutated
3101
3108
  * or half-built snapshot and a subscribed client. Both halves are checked — the method
3102
3109
  * literal the extension fixes, and params that hold together as an
3103
3110
  * {@link MCPTaskDetail} — because either alone admits a frame the other rejects.
3104
3111
  *
3105
- * `_meta` is checked for SHAPE WHEN PRESENT and nothing more. The reserved subscription
3106
- * stamp is the SERVER'S to write, after this guard admits the frame and the matcher agrees
3112
+ * `_meta` is checked for shape when present and nothing more. The reserved subscription
3113
+ * stamp is the server's to write, after this guard admits the frame and the matcher agrees
3107
3114
  * to it, so a guard that demanded the stamp would refuse every frame a producer emits.
3108
3115
  *
3109
3116
  * @param value - The unknown value to inspect
@@ -3514,13 +3521,13 @@ function isMCPElicitResult(value) {
3514
3521
  * Determines whether accepted elicitation content satisfies the exact schema that was issued.
3515
3522
  *
3516
3523
  * @remarks
3517
- * {@link isMCPElicitResult} says a response has the SHAPE of a response; this says the
3518
- * response answers the QUESTION that was asked. A server that protects the schema it issued
3524
+ * {@link isMCPElicitResult} says a response has the shape of a response; this says the
3525
+ * response answers the question that was asked. A server that protects the schema it issued
3519
3526
  * and then never enforces it has bought nothing, so this guard closes that gap: it is what
3520
3527
  * turns a bound schema into a checked one.
3521
3528
  *
3522
3529
  * Every own value must be one {@link MCPElicitValue} — a string, a finite number, a boolean,
3523
- * or an array of strings. A value whose name is DECLARED in `schema.properties` must in
3530
+ * or an array of strings. A value whose name is declared in `schema.properties` must in
3524
3531
  * addition satisfy that field's schema: `integer` rejects a fraction, `minimum` / `maximum`
3525
3532
  * bound a number, `minLength` / `maxLength` bound a string by code points, `enum` and `oneOf`
3526
3533
  * bound it to a declared member, `format` is enforced (`uri` by {@link isAbsoluteURI}, `email`
@@ -3529,9 +3536,9 @@ function isMCPElicitResult(value) {
3529
3536
  * `maxItems` with every entry drawn from its `items.enum` or `items.anyOf`. Every name listed
3530
3537
  * in `schema.required` must be present.
3531
3538
  *
3532
- * An UNDECLARED property remains valid: the restricted schema is open by default, so a client
3539
+ * An undeclared property remains valid: the restricted schema is open by default, so a client
3533
3540
  * that answers more than it was asked is not refused for it. A `schema` that is not itself a
3534
- * valid {@link MCPElicitSchema} admits NOTHING — an unenforceable schema is never a permissive
3541
+ * valid {@link MCPElicitSchema} admits nothing — an unenforceable schema is never a permissive
3535
3542
  * one — which is why `schema` is accepted as `unknown` and checked rather than trusted. Total
3536
3543
  * over hostile content and hostile schemas alike.
3537
3544
  *
@@ -3724,10 +3731,11 @@ function isMCPSampleContent(value) {
3724
3731
  *
3725
3732
  * @remarks
3726
3733
  * The schema's `CreateMessageResult` types `content` as an `anyOf` over one
3727
- * {@link isMCPSampleContent} block or an ARRAY of them, so both are admitted here: a
3734
+ * {@link isMCPSampleContent} block or an array of them, so both are admitted here: a
3728
3735
  * tool-using model answers with `tool_use` and `tool_result` blocks, and a model answering in
3729
3736
  * several parts answers with the array. `stopReason` stays an open string because the schema
3730
- * names four values and permits any other a provider reports. Total over hostile input.
3737
+ * names `endTurn`, `stopSequence`, `maxTokens`, and `toolUse` and permits any other a provider
3738
+ * reports. Total over hostile input.
3731
3739
  *
3732
3740
  * @param value - The unknown value to inspect
3733
3741
  * @returns True if `value` has the sampling-completion shape; false otherwise
@@ -3768,12 +3776,12 @@ function isMCPSampleResult(value) {
3768
3776
  * Determines whether a response answers the exact embedded request that was issued.
3769
3777
  *
3770
3778
  * @remarks
3771
- * A response carries no `method` of its own, so the ISSUED request selects which arm applies
3779
+ * A response carries no `method` of its own, so the issued request selects which arm applies
3772
3780
  * — the same way {@link isElicitContent} takes the issued schema rather than trusting the
3773
3781
  * content to describe itself. A form elicitation is checked twice: once for the response
3774
3782
  * shape and once, on `accept`, for the content against the schema that round issued. A
3775
3783
  * URL-mode elicitation issues no schema, so only the shape is checked. A request this
3776
- * package cannot recognize admits NOTHING, because an unrecognized question has no correct
3784
+ * package cannot recognize admits nothing, because an unrecognized question has no correct
3777
3785
  * answer. Total over hostile responses and hostile requests alike.
3778
3786
  *
3779
3787
  * @param value - The client's answer to check
@@ -3836,7 +3844,7 @@ function isMCPInputResult(value) {
3836
3844
  *
3837
3845
  * @remarks
3838
3846
  * A request is a record with `jsonrpc === '2.0'`, a string `method`, and an `id`
3839
- * that {@link isJSONRPCId} accepts. An id-less call is NOT a request — it is a
3847
+ * that {@link isJSONRPCId} accepts. An id-less call is not a request — it is a
3840
3848
  * {@link JSONRPCNotification}, which {@link isJSONRPCNotification} answers for. The
3841
3849
  * guards are mutually exclusive on every input: this one requires a valid `id`
3842
3850
  * value, that one requires no own `id` member at all. `params`, when present, must
@@ -3865,7 +3873,7 @@ function isJSONRPCRequest(value) {
3865
3873
  * Determines whether a parsed value is a {@link JSONRPCNotification}.
3866
3874
  *
3867
3875
  * @remarks
3868
- * A notification is a request-shaped call carrying NO `id` member — the protocol
3876
+ * A notification is a request-shaped call carrying no `id` member — the protocol
3869
3877
  * forbids one, because nothing answers a notification. `params`, when present, must
3870
3878
  * be a record. Total: any other input returns `false`.
3871
3879
  *
@@ -3906,8 +3914,8 @@ function isJSONRPCInvocation(value) {
3906
3914
  * arm of a response.
3907
3915
  *
3908
3916
  * @remarks
3909
- * A result answers a request, so `id` is REQUIRED and must be a valid
3910
- * {@link isJSONRPCId}. The envelope must own a `result` and must NOT own an `error`,
3917
+ * A result answers a request, so `id` is required and must be a valid
3918
+ * {@link isJSONRPCId}. The envelope must own a `result` and must not own an `error`,
3911
3919
  * which is what makes this guard and {@link isJSONRPCErrorResponse} mutually
3912
3920
  * exclusive on every input. `result` itself must be an object: either a modern
3913
3921
  * {@link isMCPResult} or a legacy {@link isMCPLegacyResult}, never a bare primitive.
@@ -3935,17 +3943,17 @@ function isJSONRPCResultResponse(value) {
3935
3943
  * Determines whether a value is one JSON-RPC `error` member.
3936
3944
  *
3937
3945
  * @remarks
3938
- * The failure OBJECT, not the envelope carrying it — the shape a failed response owns
3946
+ * The failure object, not the envelope carrying it — the shape a failed response owns
3939
3947
  * under `error`, and the shape a `failed` {@link MCPTaskDetail} owns under the same name,
3940
3948
  * which is why it is one guard rather than the same checks written twice.
3941
3949
  *
3942
- * It is deliberately STRUCTURAL rather than exact-JSON: `data` is declared `unknown`, so
3950
+ * It is deliberately structural rather than exact-JSON: `data` is declared `unknown`, so
3943
3951
  * requiring the whole object to survive a JSON clone would refuse a legal error that
3944
3952
  * carried a non-JSON payload. Both callers here hand it an already-owned value.
3945
3953
  *
3946
3954
  * That choice is why the key reads are guarded. Every sibling guard clones first, and a
3947
3955
  * clone reads each key once behind a boundary that already owns totality; this one is the
3948
- * family's only DIRECT reader, so it meets `code` and `message` exactly as the value defines
3956
+ * family's only direct reader, so it meets `code` and `message` exactly as the value defines
3949
3957
  * them — including as accessors that throw. Reading a named key off an unowned value is
3950
3958
  * itself the hostile step, and it is bounded here rather than allowed to escape. Total.
3951
3959
  *
@@ -3968,9 +3976,9 @@ function isJSONRPCError(value) {
3968
3976
  * arm of a response.
3969
3977
  *
3970
3978
  * @remarks
3971
- * `id` is OPTIONAL here and only here: a peer that could not read the failed
3972
- * request's id OMITS the member rather than sending `null`, so an absent `id` is
3973
- * valid and a `null` one is not. The envelope must own an `error` and must NOT own a
3979
+ * `id` is optional here and only here: a peer that could not read the failed
3980
+ * request's id omits the member rather than sending `null`, so an absent `id` is
3981
+ * valid and a `null` one is not. The envelope must own an `error` and must not own a
3974
3982
  * `result`. `error` carries an integer `code` and a string `message`. Total.
3975
3983
  *
3976
3984
  * @param value - The already-parsed value to test
@@ -4060,9 +4068,9 @@ function isModernRequest(value) {
4060
4068
  * Infers the wire era for an MCP protocol revision.
4061
4069
  *
4062
4070
  * @remarks
4063
- * The era is READ from the two era guards rather than restated here, so a revision added
4071
+ * The era is read from the era guards rather than restated here, so a revision added
4064
4072
  * to {@link SUPPORTED_MODERN_PROTOCOL_VERSIONS} or {@link SUPPORTED_LEGACY_PROTOCOL_VERSIONS}
4065
- * carries its era with it and no third list can disagree with those two.
4073
+ * carries its era with it and no further list can disagree with them.
4066
4074
  *
4067
4075
  * @param version - The protocol revision to classify
4068
4076
  * @returns `'modern'` for a revision a bare server accepts, `'legacy'` for a revision the
@@ -4076,10 +4084,10 @@ function inferEra(version) {
4076
4084
  * Infers the wire era one invocation's own structure selects.
4077
4085
  *
4078
4086
  * @remarks
4079
- * The STRUCTURAL read, distinct from {@link inferEra}'s read of a revision string: era is fixed
4087
+ * The structural read, distinct from {@link inferEra}'s read of a revision string: era is fixed
4080
4088
  * by the reserved modern metadata a request carries, so this answers for a message whose
4081
4089
  * revision has not been read and cannot answer `undefined` — every invocation took one of the
4082
- * two published wire shapes. It is what an observation surface reports and what an ingress
4090
+ * published wire shapes. It is what an observation surface reports and what an ingress
4083
4091
  * routes on, so both derive it here rather than each spelling the ternary out.
4084
4092
  *
4085
4093
  * @param invocation - The invocation whose structure selects the era
@@ -4103,15 +4111,15 @@ function inferVersion(offered) {
4103
4111
  for (const version of SUPPORTED_MODERN_PROTOCOL_VERSIONS) if (offered.includes(version)) return version;
4104
4112
  }
4105
4113
  /**
4106
- * Infers the protocol version an outbound message announces itself with — the ONE
4114
+ * Infers the protocol version an outbound message announces itself with — the one
4107
4115
  * projection every HTTP client transport stamps `mcp-protocol-version` from.
4108
4116
  *
4109
4117
  * @remarks
4110
- * This is deliberately the SAME read the server's own expectation performs
4118
+ * This is deliberately the same read the server's own expectation performs
4111
4119
  * ({@link import('@orkestrel/mcp/server').inferHeaderIssue}): a modern request's reserved
4112
- * `_meta` version, accepted whenever it is a string. It is NOT
4120
+ * `_meta` version, accepted whenever it is a string. It is not
4113
4121
  * {@link import('./parsers.js').parseRequestContext}, and the difference is the whole
4114
- * point. That parser answers a different question — is the modern metadata WELL FORMED
4122
+ * point. That parser answers a different question — is the modern metadata well formed
4115
4123
  * and refuses a request whose capability declaration or logging level is malformed. Such a
4116
4124
  * request is still modern (era is fixed by key presence) and the server still demands the
4117
4125
  * header for it, so projecting through the parser withholds a header the peer requires and
@@ -4120,7 +4128,7 @@ function inferVersion(offered) {
4120
4128
  * A non-modern message projects nothing: a legacy request's version comes from the
4121
4129
  * `initialize` handshake the transport captured, not from the message.
4122
4130
  *
4123
- * Header NAMES stay with the transports that own the wire (see `constants.ts`); core owns
4131
+ * Header names stay with the transports that own the wire (see `constants.ts`); core owns
4124
4132
  * the value this projection derives, which is the part the browser and Node faces disagreed about.
4125
4133
  *
4126
4134
  * @param message - The outbound message about to be written
@@ -4145,10 +4153,10 @@ function inferRequestVersion(message) {
4145
4153
  *
4146
4154
  * @remarks
4147
4155
  * - **One seam.** The server registers its built-in modern methods here at construction
4148
- * and resolves EVERY modern method from here, so a consumer's method and a built-in
4156
+ * and resolves every modern method from here, so a consumer's method and a built-in
4149
4157
  * are the same kind of thing on the same path.
4150
4158
  * - **Registration is a write, not a merge.** `add` under a name already present
4151
- * REPLACES it, which is how a consumer overrides a built-in; there is no precedence
4159
+ * replaces it, which is how a consumer overrides a built-in; there is no precedence
4152
4160
  * rule to remember.
4153
4161
  * - **A narrower contract than a `Map`.** Callers register and resolve; they cannot
4154
4162
  * iterate, clear, or otherwise reach the server's internal state through it.
@@ -4301,16 +4309,16 @@ var MCPProgressReporter = class {
4301
4309
  * through.
4302
4310
  *
4303
4311
  * @remarks
4304
- * A native async generator decides cancellation with a QUEUE: `return()` and `throw()` wait
4312
+ * A native async generator decides cancellation with a queue: `return()` and `throw()` wait
4305
4313
  * behind a `next()` the producer has not answered, so a consumer abandoning a source parked
4306
4314
  * on an event that will never arrive waits forever for its own cancellation. This class
4307
- * arbitrates instead of queueing. It keeps at most ONE read outstanding against the source,
4308
- * settles the consumer's read itself, aborts the request's lifetime BEFORE it delegates
4315
+ * arbitrates instead of queueing. It keeps at most one read outstanding against the source,
4316
+ * settles the consumer's read itself, aborts the request's lifetime before it delegates
4309
4317
  * cleanup to the producer — so a cooperating producer is woken rather than waited on —
4310
4318
  * contains every promise the producer settles late, and makes every closure path idempotent.
4311
4319
  *
4312
4320
  * The closures are deliberately different answers: the source's own return is the
4313
- * terminal RESPONSE, `return(value)` is the consumer saying it has the answer already, and
4321
+ * terminal response, `return(value)` is the consumer saying it has the answer already, and
4314
4322
  * {@link stop} is an owner saying there will be no answer at all. Only the source's own
4315
4323
  * return is a message a peer ever sees.
4316
4324
  *
@@ -4318,7 +4326,7 @@ var MCPProgressReporter = class {
4318
4326
  * generator is suspended inside, so the signal is how an uncooperative producer is asked to
4319
4327
  * finish, and this controller never blocks its consumer on the answer.
4320
4328
  *
4321
- * **What this class does NOT have is an owner of last resort.** No finalizer, no timer, no
4329
+ * **What this class does not have is an owner of last resort.** No finalizer, no timer, no
4322
4330
  * timeout ends an exchange nobody released. That absence is the design: an exchange holds a
4323
4331
  * producer, a request lifetime and a live server slot, so a silent background release would
4324
4332
  * turn "a pump forgot its obligation" from a reproducible defect into a nondeterministic one,
@@ -4499,15 +4507,15 @@ var MCPStreamController = class {
4499
4507
  * serialized.
4500
4508
  *
4501
4509
  * @remarks
4502
- * A TRANSLATION boundary and deliberately nothing else. It serializes each message and the
4510
+ * A translation boundary and deliberately nothing else. It serializes each message and the
4503
4511
  * terminating response, and every lifecycle decision — return, throw, dispose, stop — ends
4504
4512
  * the typed exchange beneath it rather than this face. That is the whole design constraint: a
4505
- * serialized face implemented as its own async generator would add a SECOND operation queue,
4513
+ * serialized face implemented as its own async generator would add a second operation queue,
4506
4514
  * and the queue is exactly the defect the typed controller exists to remove — a `return()`
4507
4515
  * promptly settled at the text face and left queued at the typed one cancels nothing.
4508
4516
  *
4509
4517
  * One member is a narrowing rather than a pass-through, and it is worth knowing before it
4510
- * surprises a producer. `return` receives a STRING; it cannot rebuild the typed
4518
+ * surprises a producer. `return` receives a string; it cannot rebuild the typed
4511
4519
  * `JSONRPCResponse` the typed face would close on, and inventing one by parsing the
4512
4520
  * argument back would make this face decide what the exchange ended with. So it ends the
4513
4521
  * typed exchange with {@link MCPStreamControllerInterface.stop} — no terminal — and answers
@@ -4516,11 +4524,11 @@ var MCPStreamController = class {
4516
4524
  * the honest translation of "the consumer already has its answer" when the answer is opaque
4517
4525
  * text, not a downgrade to work around.
4518
4526
  *
4519
- * It accepts only a CONTROLLED typed stream. A raw generator would have no lifecycle to
4527
+ * It accepts only a controlled typed stream. A raw generator would have no lifecycle to
4520
4528
  * delegate to, and this class refuses to grow one of its own.
4521
4529
  *
4522
4530
  * Delegation is total and it is what makes the ownership obligation transitive: `return`,
4523
- * `throw`, `stop`, and dispose each end the TYPED exchange, so a pump holding only this
4531
+ * `throw`, `stop`, and dispose each end the typed exchange, so a pump holding only this
4524
4532
  * serialized face still releases the producer, the request lifetime, and the live server slot
4525
4533
  * behind it. There is no owner of last resort here either, for the same reason there is none
4526
4534
  * on the typed face.
@@ -4567,7 +4575,7 @@ var MCPTextStreamController = class {
4567
4575
  * @remarks
4568
4576
  * The typed exchange ends with no terminal, because a string is not a
4569
4577
  * `JSONRPCResponse` and this face never parses one back out of its argument. The
4570
- * supplied text is the answer to THIS consumer, and a cooperating producer sees its
4578
+ * supplied text is the answer to this consumer, and a cooperating producer sees its
4571
4579
  * cancellation path rather than its normal return.
4572
4580
  *
4573
4581
  * @param value - The serialized terminal the consumer is ending on
@@ -4604,7 +4612,7 @@ var MCPTextStreamController = class {
4604
4612
  *
4605
4613
  * @remarks
4606
4614
  * Delegates downward exactly as {@link stop} does — disposing the serialized arm is
4607
- * disposing the exchange, never just this adapter.
4615
+ * disposing the exchange, never this adapter alone.
4608
4616
  *
4609
4617
  * @returns Resolves once the typed exchange has ended
4610
4618
  */
@@ -4660,7 +4668,8 @@ var MCPLegacy = class {
4660
4668
  const parsed = (0, _orkestrel_contract.parseJSON)(message);
4661
4669
  if (isModernRequest(parsed) || !isJSONRPCInvocation(parsed)) return this.#options.dispatcher.handle(message, options);
4662
4670
  const answer = await this.#legacy(parsed, options);
4663
- return answer === void 0 ? void 0 : JSON.stringify(answer);
4671
+ if (answer === void 0) return void 0;
4672
+ return Symbol.asyncIterator in answer ? new MCPTextStreamController(answer) : JSON.stringify(answer);
4664
4673
  }
4665
4674
  async #legacy(invocation, options) {
4666
4675
  if (invocation.id === void 0) return void 0;
@@ -4684,13 +4693,42 @@ var MCPLegacy = class {
4684
4693
  }
4685
4694
  async #forward(request, options) {
4686
4695
  const translated = legacyInvocationToModern(request);
4687
- const answer = await this.#options.dispatcher.dispatch(translated, options);
4688
- if (Symbol.asyncIterator in answer) {
4689
- answer.stop();
4690
- await answer[Symbol.asyncDispose]();
4691
- return this.#unsupported(request.id, "stream");
4696
+ const metadata = request.method === "tools/call" ? request.params?.["_meta"] : void 0;
4697
+ const candidate = (0, _orkestrel_contract.isRecord)(metadata) ? metadata["progressToken"] : void 0;
4698
+ const token = (0, _orkestrel_contract.isString)(candidate) || (0, _orkestrel_contract.isInteger)(candidate) ? candidate : void 0;
4699
+ if (token === void 0) {
4700
+ const answer = await this.#options.dispatcher.dispatch(translated, options);
4701
+ if (Symbol.asyncIterator in answer) {
4702
+ answer.stop();
4703
+ await answer[Symbol.asyncDispose]();
4704
+ return this.#unsupported(request.id, "stream");
4705
+ }
4706
+ return this.#project(answer, request.id);
4707
+ }
4708
+ const closure = new AbortController();
4709
+ const resolved = buildMethodOptions(options ?? {}, closure.signal);
4710
+ try {
4711
+ const answer = await this.#options.dispatcher.dispatch(translated, resolved);
4712
+ if (Symbol.asyncIterator in answer) return new MCPStreamController(this.#progress(answer, request.id, token), resolved.signal, closure);
4713
+ closure.abort();
4714
+ return this.#project(answer, request.id);
4715
+ } catch (error) {
4716
+ closure.abort(error);
4717
+ throw error;
4718
+ }
4719
+ }
4720
+ async *#progress(stream, id, token) {
4721
+ try {
4722
+ while (true) {
4723
+ const frame = await stream.next();
4724
+ if (frame.done === true) return this.#project(frame.value, id);
4725
+ const params = frame.value.params;
4726
+ if (frame.value.method !== "notifications/progress" || !(0, _orkestrel_contract.isRecord)(params) || params["progressToken"] !== token || !isMCPProgress(params)) return this.#unsupported(id, "stream");
4727
+ yield frame.value;
4728
+ }
4729
+ } finally {
4730
+ await stream[Symbol.asyncDispose]();
4692
4731
  }
4693
- return this.#project(answer, request.id);
4694
4732
  }
4695
4733
  #project(answer, id) {
4696
4734
  if (answer.error !== void 0) return answer.error.code === -32021 ? this.#unsupported(id, this.#capability(answer)) : answer;
@@ -4936,7 +4974,7 @@ var MCPLegacyClientTransport = class {
4936
4974
  //#endregion
4937
4975
  //#region src/core/MCPServer.ts
4938
4976
  /**
4939
- * Dispatches JSON-RPC 2.0 requests over a live {@link ToolManagerInterface}, with NO
4977
+ * Dispatches JSON-RPC 2.0 requests over a live {@link ToolManagerInterface}, with no
4940
4978
  * transport coupling.
4941
4979
  *
4942
4980
  * @remarks
@@ -4946,7 +4984,7 @@ var MCPLegacyClientTransport = class {
4946
4984
  * `handle(message)` is the string boundary: it
4947
4985
  * `JSON.parse`s the raw message (a failure → a `-32700` response), narrows it to
4948
4986
  * an invocation (a non-invocation → a `-32600` response, with the unreadable `id`
4949
- * OMITTED rather than nulled), dispatches, and serializes the
4987
+ * omitted rather than nulled), dispatches, and serializes the
4950
4988
  * response back to a string (`undefined` for a notification).
4951
4989
  * - **One modern seam.** `server/discover`, `tools/list`, `tools/call`, and
4952
4990
  * `subscriptions/listen` are always registered; `resources/*`, `prompts/*`, and
@@ -5637,19 +5675,19 @@ var MCPServer = class {
5637
5675
  //#endregion
5638
5676
  //#region src/core/MCPTaskClient.ts
5639
5677
  /**
5640
- * Issues the `tasks/*` methods over one correlated-request door — the CLIENT half of the
5678
+ * Issues the `tasks/*` methods over one correlated-request door — the client half of the
5641
5679
  * stable Tasks extension, exposed as an {@link import('./types.js').MCPClientInterface}'s
5642
5680
  * `tasks`.
5643
5681
  *
5644
5682
  * @remarks
5645
5683
  * - **The mirror of the server-side port, minus `start`.** An
5646
5684
  * {@link import('./types.js').MCPTaskManagerInterface} is the consumer's durable store the
5647
- * SERVER creates tasks in; this is the client's read/answer/stop access to the tasks a peer
5685
+ * server creates tasks in; this is the client's read/answer/stop access to the tasks a peer
5648
5686
  * already created. Creation is missing on purpose: the extension gives a client no flag and
5649
5687
  * no parameter to ask for a task, so `start` has no wire method to be.
5650
5688
  * - **No plural accessor, no loop, no cache.** MCP defines no `tasks/list`, so nothing here
5651
5689
  * enumerates. A task snapshot's `pollIntervalMs` is carried untouched and a one-shot read
5652
- * sits beside it; the SCHEDULE is the consumer's, because this package has no durable place
5690
+ * sits beside it; the schedule is the consumer's, because this package has no durable place
5653
5691
  * to keep a task, no idea when the application still cares, and no lifetime to hang a timer
5654
5692
  * on that outlives the request it was born from. An instance left alone writes nothing.
5655
5693
  * - **One channel.** Every request goes through the injected
@@ -5697,20 +5735,20 @@ var MCPTaskClient = class {
5697
5735
  //#endregion
5698
5736
  //#region src/core/MCPClient.ts
5699
5737
  /**
5700
- * Connects to a REMOTE MCP server over any injected {@link MCPMessageTransportInterface},
5738
+ * Connects to a remote MCP server over any injected {@link MCPMessageTransportInterface},
5701
5739
  * negotiates the modern revision, and exposes the server's tools as local
5702
5740
  * {@link ToolInterface}s an agent can run.
5703
5741
  *
5704
5742
  * @remarks
5705
- * - **The mirror of `MCPServer`.** The server DISPATCHES requests over a tool registry;
5706
- * this client ISSUES them over a transport. `connect` probes `server/discover` and exposes
5743
+ * - **The mirror of `MCPServer`.** The server dispatches requests over a tool registry;
5744
+ * this client issues them over a transport. `connect` probes `server/discover` and exposes
5707
5745
  * the negotiated `version`; a legacy peer requires an explicit transport adapter.
5708
5746
  * `tools()` lists the remote tools and wraps each as a
5709
5747
  * local {@link ToolInterface} whose `execute` calls back through `call`; `call` runs a
5710
5748
  * remote `tools/call` and reports the arm the peer answered with — a value, a durable
5711
5749
  * task, or a request for more input (a remote `isError: true` throws locally, so an
5712
5750
  * agent's {@link import('@orkestrel/tool').ToolManagerInterface} isolates it into a
5713
- * `success: false` result just like a local throw). A wrapped tool cannot hand an agent
5751
+ * `success: false` result exactly like a local throw). A wrapped tool cannot hand an agent
5714
5752
  * a deferred answer, so a non-`'complete'` arm throws there.
5715
5753
  * - **Request↔response correlation.** Each request is tagged with a monotonic numeric
5716
5754
  * `id` ({@link #nextId}); a single transport `message` subscription resolves / rejects
@@ -5718,9 +5756,9 @@ var MCPTaskClient = class {
5718
5756
  * every pending request because the peer could not identify which request failed. A
5719
5757
  * server-initiated message is re-surfaced on the `notification` event, except a progress
5720
5758
  * frame claimed by the request that asked
5721
- * for it; a RESPONSE correlating to nothing pending is discarded, because the request it
5759
+ * for it; a response correlating to nothing pending is discarded, because the request it
5722
5760
  * answers has already settled.
5723
- * - **Per-request cancellation.** `call`'s `options.signal` withdraws ONE caller from ONE
5761
+ * - **Per-request cancellation.** `call`'s `options.signal` withdraws one caller from one
5724
5762
  * request: the pending entry rejects on every carrier, and `notifications/cancelled` goes
5725
5763
  * out only where the transport declares itself duplex — the dated revision defines no
5726
5764
  * client-to-server notification over Streamable HTTP, where closing the response stream
@@ -5737,7 +5775,7 @@ var MCPTaskClient = class {
5737
5775
  * discovery probe uses that same configured deadline, so a silent peer cannot hold
5738
5776
  * negotiation indefinitely.
5739
5777
  * `AbortSignal.timeout` (never a raw `setTimeout`) rejects only that pending request, and the
5740
- * same deadline bounds the WAIT on the transport's `close`, the one wait no drain and no signal
5778
+ * same deadline bounds the wait on the transport's `close`, the one wait no drain and no signal
5741
5779
  * can reach. It bounds the wait rather than the close, which keeps running, so a retry joins it
5742
5780
  * instead of shutting one connection down twice.
5743
5781
  * - **Transport-agnostic.** Imports only core siblings (JSON-RPC + the tool vocabulary);
@@ -6271,7 +6309,7 @@ var MCPClient = class {
6271
6309
  //#endregion
6272
6310
  //#region src/core/transports/HTTPClientTransport.ts
6273
6311
  /**
6274
- * Drives a REMOTE Streamable-HTTP MCP server over `fetch` — a CLIENT
6312
+ * Drives a remote Streamable-HTTP MCP server over `fetch` — a client
6275
6313
  * {@link MCPMessageTransportInterface} for the Model Context Protocol, the egress mirror of
6276
6314
  * the server's `createMCPRoutes`.
6277
6315
  *
@@ -6283,7 +6321,7 @@ var MCPClient = class {
6283
6321
  * class, so a reply reaches a page and a Node process through the same decode.
6284
6322
  * - **Request/response over `fetch`.** `send(message)` POSTs the JSON-serialized
6285
6323
  * message to `options.url` with `content-type: application/json` and an
6286
- * `Accept` of BOTH `application/json` and `text/event-stream` (so the server may
6324
+ * `Accept` of both `application/json` and `text/event-stream` (so the server may
6287
6325
  * answer with either framing) — plus any `options.headers` (for example, an `Authorization`
6288
6326
  * bearer). It then decodes the reply and emits each decoded {@link JSONRPCMessage} on
6289
6327
  * the `message` event the {@link import('@orkestrel/mcp').MCPClientInterface} subscribes
@@ -6296,12 +6334,12 @@ var MCPClient = class {
6296
6334
  * Accepted (a notification) carries no body and emits nothing.
6297
6335
  * - **Session and protocol headers.** `start()` is a no-op (a
6298
6336
  * request/response transport opens no long-lived connection). The
6299
- * `mcp-session-id` response header, when a STATEFUL server sends one (on
6300
- * `initialize`), is captured into `session` and then ECHOED as the
6301
- * `mcp-session-id` request header on every SUBSEQUENT request — so an
6337
+ * `mcp-session-id` response header, when a stateful server sends one (on
6338
+ * `initialize`), is captured into `session` and then echoed as the
6339
+ * `mcp-session-id` request header on every subsequent request — so an
6302
6340
  * `MCPClient` passes a stateful server's session validation. The
6303
6341
  * initialize result's `protocolVersion` is likewise captured, but only
6304
- * when it is a SUPPORTED value, and echoed as `mcp-protocol-version` alone on
6342
+ * when it is a supported value, and echoed as `mcp-protocol-version` alone on
6305
6343
  * subsequent legacy requests. Modern requests instead derive protocol and method
6306
6344
  * headers from the message, plus the name header only for `tools/call` — carried in the
6307
6345
  * protocol's Base64 sentinel form whenever the tool name cannot ride as plain ASCII.
@@ -6309,11 +6347,11 @@ var MCPClient = class {
6309
6347
  * `close()` clears the captured protocol so a reconnect's `initialize`
6310
6348
  * POST is headerless; the captured `session` persists across `close()`.
6311
6349
  * - **`close()` releases what is in flight.** Every `fetch` this transport still has open is
6312
- * ABORTED, which cancels the response body a `send` is reading — an SSE reply the server
6350
+ * aborted, which cancels the response body a `send` is reading — an SSE reply the server
6313
6351
  * never ends would otherwise outlive the transport, with nothing left able to reach it. The
6314
6352
  * aborted read surfaces on `error` and the `send` reporting it resolves. `close()` is
6315
6353
  * idempotent (one `close` event per connected lifetime), and `start()` opens the next one.
6316
- * - **Total at the boundary, and a non-success reply REJECTS.** Every reply is narrowed
6354
+ * - **Total at the boundary, and a non-success reply rejects.** Every reply is narrowed
6317
6355
  * (`parseJSONRPCMessage`, the SSE decoder). A non-message success reply is dropped, never
6318
6356
  * asserted. A non-success reply that carries no valid JSON-RPC message rejects `send` with
6319
6357
  * an error naming its HTTP status and body shape — the peer answered, and answering the
@@ -6503,20 +6541,35 @@ var HTTPClientTransport = class {
6503
6541
  * {@link import('@orkestrel/emitter').EmitterHooks} (see {@link MCPServerOptions})
6504
6542
  * @returns A working {@link MCPServerInterface}
6505
6543
  *
6506
- * @example
6544
+ * @example Expose a tool registry over MCP
6507
6545
  * ```ts
6508
6546
  * import { createMCPServer } from '@orkestrel/mcp'
6509
6547
  * import { createTool, createToolManager } from '@orkestrel/tool'
6510
6548
  *
6511
6549
  * const tools = createToolManager()
6550
+ * tools.add(
6551
+ * createTool({
6552
+ * name: 'search',
6553
+ * description: 'Search the docs',
6554
+ * execute: (a) => find(String(a.query)),
6555
+ * }),
6556
+ * )
6512
6557
  * tools.add(createTool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
6513
6558
  *
6514
- * const server = createMCPServer({ identity: { name: 'calculator', version: '1.0.0' }, tools })
6559
+ * const server = createMCPServer({ identity: { name: 'docs', version: '1.0.0' }, tools })
6515
6560
  * server.emitter.on('request', (method, id) => log(method, id))
6516
6561
  *
6517
- * // A transport pumps message strings through `handle`:
6518
- * const reply = await server.handle('{"jsonrpc":"2.0","method":"tools/list","id":1,"params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}')
6519
- * // reply '{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"add","inputSchema":{"type":"object"}}],"resultType":"complete","ttlMs":60000,"cacheScope":"private","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"calculator","version":"1.0.0"}}}}'
6562
+ * // A transport reads a framed message string and writes the reply:
6563
+ * for await (const message of transport) {
6564
+ * const reply = await server.handle(message)
6565
+ * if (reply !== undefined) await transport.send(reply) // a notification has no reply
6566
+ * }
6567
+ *
6568
+ * // `handle` also answers one message string on its own:
6569
+ * const listed = await server.handle(
6570
+ * '{"jsonrpc":"2.0","method":"tools/list","id":1,"params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}',
6571
+ * )
6572
+ * // listed → '{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"search","inputSchema":{"type":"object"},"description":"Search the docs"},{"name":"add","inputSchema":{"type":"object"}}],"resultType":"complete","ttlMs":60000,"cacheScope":"private","_meta":{"io.modelcontextprotocol/serverInfo":{"name":"docs","version":"1.0.0"}}}}'
6520
6573
  * ```
6521
6574
  */
6522
6575
  function createMCPServer(options) {
@@ -6525,6 +6578,10 @@ function createMCPServer(options) {
6525
6578
  /**
6526
6579
  * Decorates one MCP server with the fixed legacy method translation.
6527
6580
  *
6581
+ * @remarks
6582
+ * Adds support for the `2025-11-25` and `2025-06-18` legacy revisions. Removing this one
6583
+ * decorator removes that legacy surface while leaving the modern dispatcher unchanged.
6584
+ *
6528
6585
  * @param server - The sole modern dispatcher and handshake identity source
6529
6586
  * @returns A dispatcher accepting both modern and legacy invocations
6530
6587
  */
@@ -6535,14 +6592,14 @@ function createMCPLegacy(server) {
6535
6592
  });
6536
6593
  }
6537
6594
  /**
6538
- * Creates a transport-agnostic Model Context Protocol CLIENT — connects to a REMOTE
6595
+ * Creates a transport-agnostic Model Context Protocol client — connects to a remote
6539
6596
  * MCP server over an injected {@link import('./types.js').MCPMessageTransportInterface},
6540
6597
  * negotiates the modern revision through `server/discover`, and exposes the server's tools as local
6541
6598
  * {@link import('@orkestrel/tool').ToolInterface}s an agent can run.
6542
6599
  *
6543
6600
  * @remarks
6544
6601
  * The egress mirror of {@link createMCPServer}: where the server exposes a local tool
6545
- * registry over MCP, the client USES a remote server's tools. `connect()` discovers,
6602
+ * registry over MCP, the client uses a remote server's tools. `connect()` discovers,
6546
6603
  * validates, and exposes the negotiated modern protocol; a legacy peer requires
6547
6604
  * {@link createMCPLegacyClientTransport}. `tools()` lists + wraps the remote
6548
6605
  * tools (each `execute` calls back over the wire),
@@ -6552,7 +6609,7 @@ function createMCPLegacy(server) {
6552
6609
  * `fetch`) lives in the published server environment; the client itself is provider-agnostic. Subscribe
6553
6610
  * to `connect` / `disconnect` / `notification` through `client.emitter.on(...)`.
6554
6611
  *
6555
- * @param options - `transport` (the carrier; REQUIRED), an optional `identity`
6612
+ * @param options - `transport` (the carrier; required), an optional `identity`
6556
6613
  * (the client identity), `timeout` (the per-request deadline), and the reserved `on`
6557
6614
  * {@link import('@orkestrel/emitter').EmitterHooks} (see {@link MCPClientOptions})
6558
6615
  * @returns A working {@link MCPClientInterface}
@@ -6598,7 +6655,7 @@ function createMCPLegacyClientTransport(transport, options) {
6598
6655
  * existing shape.
6599
6656
  *
6600
6657
  * @remarks
6601
- * Hand the RESULT to `createMCPClient({ transport })`, then pass the SAME
6658
+ * Hand the result to `createMCPClient({ transport })`, then pass the same
6602
6659
  * `transport` to {@link import('./helpers.js').bindClient} to complete the inbound
6603
6660
  * wiring: `send` serializes each outbound {@link JSONRPCMessage} and writes it through
6604
6661
  * `transport.send`; `close` closes the underlying
@@ -6606,10 +6663,10 @@ function createMCPLegacyClientTransport(transport, options) {
6606
6663
  * it is handed in — there is no separate connect step at this layer); `session` is
6607
6664
  * always `undefined` (session correlation is a higher-level concern the duplex port
6608
6665
  * does not carry); and `duplex` is always `true`, because carrying frames in both
6609
- * directions at any moment is exactly what the adapted port is — a claim DRIVEN over a real
6666
+ * directions at any moment is exactly what the adapted port is — a claim driven over a real
6610
6667
  * `MessageChannel` and a real scope pair (a client-initiated `notifications/cancelled`
6611
6668
  * observed arriving at the peer) rather than read back off this literal. The literal is
6612
- * true of the PORT, and stays true only while the port has a peer: close the far half and
6669
+ * true of the port, and stays true only while the port has a peer: close the far half and
6613
6670
  * this transport still declares `true` while carrying nothing, which is the one thing a
6614
6671
  * per-carrier declaration cannot express. Inbound delivery (`emitter`'s `message` / `close` events) is
6615
6672
  * `bindClient`'s job, not this factory's — the returned object exposes a `message`-