@orkestrel/mcp 0.0.19 → 0.0.21
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.
- package/README.md +23 -22
- package/dist/src/browser/index.d.ts +92 -70
- package/dist/src/browser/index.js +164 -81
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +269 -257
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +351 -358
- package/dist/src/core/index.d.ts +351 -358
- package/dist/src/core/index.js +270 -257
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +331 -173
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +266 -146
- package/dist/src/server/index.d.ts +266 -146
- package/dist/src/server/index.js +331 -173
- package/dist/src/server/index.js.map +1 -1
- package/package.json +13 -10
package/dist/src/core/index.js
CHANGED
|
@@ -56,11 +56,11 @@ var MCP_HEADER_MISMATCH = -32020;
|
|
|
56
56
|
*
|
|
57
57
|
* @remarks
|
|
58
58
|
* The GENERIC code for the whole condition, not one capability's code. This server answers
|
|
59
|
-
* it in
|
|
60
|
-
* client never declared `io.modelcontextprotocol/tasks` — and
|
|
59
|
+
* it in more than one place — a `tools/call` that needs `elicitation`, and a `tasks/*` request
|
|
60
|
+
* whose client never declared `io.modelcontextprotocol/tasks` — and they are told apart by
|
|
61
61
|
* `error.data.requiredCapabilities` alone (`{ elicitation: {} }` against
|
|
62
|
-
* `{ extensions: { 'io.modelcontextprotocol/tasks': {} } }`). They are
|
|
63
|
-
* condition, so a
|
|
62
|
+
* `{ extensions: { 'io.modelcontextprotocol/tasks': {} } }`). They are instances of the same
|
|
63
|
+
* condition, so a separate numeral would describe the same fact twice. The Tasks extension's
|
|
64
64
|
* own draft prose still shows `-32003` in examples; the dated core schema fixes this code,
|
|
65
65
|
* and the dated schema is what a peer implements against.
|
|
66
66
|
*/
|
|
@@ -147,20 +147,18 @@ var DEFAULT_MCP_CLIENT_VERSION = "1.0.0";
|
|
|
147
147
|
* is unset — a request the remote server does not answer within it rejects.
|
|
148
148
|
*/
|
|
149
149
|
var DEFAULT_MCP_REQUEST_TIMEOUT = 3e4;
|
|
150
|
-
/** The maximum discovery-probe deadline used when a client deadline is configured. */
|
|
151
|
-
var DEFAULT_MCP_PROBE_TIMEOUT = 50;
|
|
152
150
|
//#endregion
|
|
153
151
|
//#region src/core/errors.ts
|
|
154
152
|
/**
|
|
155
|
-
* A
|
|
156
|
-
*
|
|
153
|
+
* A Model Context Protocol error preserving its machine-readable numeric code and
|
|
154
|
+
* optional structured context.
|
|
157
155
|
*
|
|
158
156
|
* @remarks
|
|
159
|
-
* {@link MCPClient} throws this error
|
|
160
|
-
* Local lifecycle and transport conditions such
|
|
161
|
-
* remain plain `Error`s.
|
|
162
|
-
* unchanged and is `undefined` when the peer omitted it.
|
|
163
|
-
* reserved paths: `-32020` carries no context, `-32021` may carry
|
|
157
|
+
* {@link MCPClient} throws this error for a remote JSON-RPC `error` response and for a
|
|
158
|
+
* locally detected protocol incompatibility. Local lifecycle and transport conditions such
|
|
159
|
+
* as disconnects and request timeouts remain plain `Error`s. For a remote response, `context`
|
|
160
|
+
* carries the optional `error.data` unchanged and is `undefined` when the peer omitted it.
|
|
161
|
+
* This includes the modern reserved paths: `-32020` carries no context, `-32021` may carry
|
|
164
162
|
* `requiredCapabilities`, and `-32022` carries the peer's `supported` revisions and
|
|
165
163
|
* `requested` revision for negotiation recovery.
|
|
166
164
|
*
|
|
@@ -179,7 +177,7 @@ var MCPError = class extends Error {
|
|
|
179
177
|
code;
|
|
180
178
|
context;
|
|
181
179
|
/**
|
|
182
|
-
*
|
|
180
|
+
* Creates an MCP protocol error.
|
|
183
181
|
*
|
|
184
182
|
* @param message - The human-readable JSON-RPC error message
|
|
185
183
|
* @param code - The machine-readable numeric JSON-RPC error code
|
|
@@ -192,7 +190,7 @@ var MCPError = class extends Error {
|
|
|
192
190
|
}
|
|
193
191
|
};
|
|
194
192
|
/**
|
|
195
|
-
*
|
|
193
|
+
* Determines whether an unknown value is an {@link MCPError}.
|
|
196
194
|
*
|
|
197
195
|
* @param value - The unknown value to inspect
|
|
198
196
|
* @returns `true` only when the value is an `MCPError`
|
|
@@ -213,7 +211,7 @@ function isMCPError(value) {
|
|
|
213
211
|
//#endregion
|
|
214
212
|
//#region src/core/inferers.ts
|
|
215
213
|
/**
|
|
216
|
-
*
|
|
214
|
+
* Infers the wire era for an MCP protocol revision.
|
|
217
215
|
*
|
|
218
216
|
* @param version - The protocol revision to classify
|
|
219
217
|
* @returns `'modern'` for `2026-07-28`, `'legacy'` for either supported legacy
|
|
@@ -228,7 +226,7 @@ function inferEra(version) {
|
|
|
228
226
|
}
|
|
229
227
|
}
|
|
230
228
|
/**
|
|
231
|
-
*
|
|
229
|
+
* Infers the newest supported protocol revision present in a peer's offer.
|
|
232
230
|
*
|
|
233
231
|
* @param offered - The protocol revisions offered by the peer
|
|
234
232
|
* @returns The newest locally supported offered revision, or `undefined`
|
|
@@ -237,12 +235,12 @@ function inferVersion(offered) {
|
|
|
237
235
|
for (const version of SUPPORTED_PROTOCOL_VERSIONS) if (offered.includes(version)) return version;
|
|
238
236
|
}
|
|
239
237
|
/**
|
|
240
|
-
*
|
|
238
|
+
* Infers the protocol version an outbound message announces itself with — the ONE
|
|
241
239
|
* projection every HTTP client transport stamps `mcp-protocol-version` from.
|
|
242
240
|
*
|
|
243
241
|
* @remarks
|
|
244
242
|
* This is deliberately the SAME read the server's own expectation performs
|
|
245
|
-
* ({@link import('@
|
|
243
|
+
* ({@link import('@orkestrel/mcp/server').inferHeaderIssue}): a modern request's reserved
|
|
246
244
|
* `_meta` version, accepted whenever it is a string. It is NOT
|
|
247
245
|
* {@link import('./parsers.js').parseRequestContext}, and the difference is the whole
|
|
248
246
|
* point. That parser answers a different question — is the modern metadata WELL FORMED —
|
|
@@ -255,7 +253,7 @@ function inferVersion(offered) {
|
|
|
255
253
|
* `initialize` handshake the transport captured, not from the message.
|
|
256
254
|
*
|
|
257
255
|
* Header NAMES stay with the transports that own the wire (see `constants.ts`); core owns
|
|
258
|
-
* the value this projection derives, which is the part the
|
|
256
|
+
* the value this projection derives, which is the part the browser and Node faces disagreed about.
|
|
259
257
|
*
|
|
260
258
|
* @param message - The outbound message about to be written
|
|
261
259
|
* @returns The version to announce, or `undefined` when the message announces none
|
|
@@ -273,7 +271,7 @@ function inferRequestVersion(message) {
|
|
|
273
271
|
//#endregion
|
|
274
272
|
//#region src/core/cloners.ts
|
|
275
273
|
/**
|
|
276
|
-
*
|
|
274
|
+
* Snapshots one bounded exact JSON value together with its canonical wire serialization.
|
|
277
275
|
*
|
|
278
276
|
* The returned value is an owned, deeply frozen graph reconstructed from the canonical text;
|
|
279
277
|
* the frozen tuple shares no mutable structure with the input. Invalid exact-JSON shapes,
|
|
@@ -303,10 +301,10 @@ function snapshotJSON(value, limits) {
|
|
|
303
301
|
return Object.freeze(snapshot);
|
|
304
302
|
}
|
|
305
303
|
/**
|
|
306
|
-
*
|
|
304
|
+
* Snapshots one exact Tool result and the canonical wire text of a defined success value.
|
|
307
305
|
*
|
|
308
|
-
* A success must have exactly
|
|
309
|
-
* and `value`. A failure must instead have exactly `id`, `name`, `success: false`, and a string
|
|
306
|
+
* A success must have exactly the own enumerable data properties `id`, `name`,
|
|
307
|
+
* `success: true`, and `value`. A failure must instead have exactly `id`, `name`, `success: false`, and a string
|
|
310
308
|
* `error`. The returned result and tuple are frozen. Only a defined success value crosses the
|
|
311
309
|
* bounded JSON ownership seam; it becomes an owned deeply frozen value and receives canonical
|
|
312
310
|
* text. Value-less successes and failures pair with `undefined` text. Non-records, symbol keys,
|
|
@@ -381,11 +379,11 @@ function snapshotToolResult(value, limits) {
|
|
|
381
379
|
//#endregion
|
|
382
380
|
//#region src/core/parsers.ts
|
|
383
381
|
/**
|
|
384
|
-
*
|
|
382
|
+
* Narrows an already-parsed value to a {@link JSONRPCMessage}, or `undefined` when
|
|
385
383
|
* it is not one.
|
|
386
384
|
*
|
|
387
385
|
* @remarks
|
|
388
|
-
* Total
|
|
386
|
+
* Total — a non-message returns `undefined`, never throws. The input must
|
|
389
387
|
* ALREADY be `JSON.parse`d: the raw-string parse (which can throw on malformed
|
|
390
388
|
* JSON) happens in `MCPServer.handle` inside a try/catch that maps a parse failure
|
|
391
389
|
* to a `-32700` response.
|
|
@@ -399,7 +397,7 @@ function snapshotToolResult(value, limits) {
|
|
|
399
397
|
* result. Identity is not preserved and is not promised.
|
|
400
398
|
*
|
|
401
399
|
* The parser's sound partner is the COMPOSITE `isJSONRPCMessage(value) &&
|
|
402
|
-
* isBoundedJSON(value, limits)`, and against it both halves of the
|
|
400
|
+
* isBoundedJSON(value, limits)`, and against it both halves of the soundness law
|
|
403
401
|
* hold by construction:
|
|
404
402
|
*
|
|
405
403
|
* - Every non-`undefined` result satisfies {@link isJSONRPCMessage}, because the guard
|
|
@@ -435,7 +433,7 @@ function parseJSONRPCMessage(value, limits = {
|
|
|
435
433
|
return snapshot !== void 0 && isJSONRPCMessage(snapshot[0]) ? snapshot[0] : void 0;
|
|
436
434
|
}
|
|
437
435
|
/**
|
|
438
|
-
*
|
|
436
|
+
* Parses the reserved modern request metadata into an {@link MCPRequestContext}.
|
|
439
437
|
*
|
|
440
438
|
* @remarks
|
|
441
439
|
* This is the validity step after {@link isModernRequest}: a defined result can
|
|
@@ -485,7 +483,7 @@ function parseRequestContext(value, limits = {
|
|
|
485
483
|
}
|
|
486
484
|
}
|
|
487
485
|
/**
|
|
488
|
-
*
|
|
486
|
+
* Parses the opened value carried by an opaque `requestState` continuation.
|
|
489
487
|
*
|
|
490
488
|
* @remarks
|
|
491
489
|
* This parser does not open the opaque continuation carrier; the configured
|
|
@@ -543,7 +541,69 @@ function parseMCPInputState(value) {
|
|
|
543
541
|
//#endregion
|
|
544
542
|
//#region src/core/helpers.ts
|
|
545
543
|
/**
|
|
546
|
-
*
|
|
544
|
+
* Determines whether a client capability record declares form-mode elicitation.
|
|
545
|
+
*
|
|
546
|
+
* @remarks
|
|
547
|
+
* The protocol's empty `elicitation` object is the implicit form-only declaration.
|
|
548
|
+
* A non-empty declaration must carry a record-valued `form` member; URL-only support
|
|
549
|
+
* does not authorize a form request. Total over hostile input.
|
|
550
|
+
*
|
|
551
|
+
* @param value - The client capability record to inspect
|
|
552
|
+
* @returns `true` when form-mode elicitation is declared
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* isFormElicitationSupported({ elicitation: {} }) // true — implicit form mode
|
|
557
|
+
* isFormElicitationSupported({ elicitation: { url: {} } }) // false
|
|
558
|
+
* ```
|
|
559
|
+
*/
|
|
560
|
+
function isFormElicitationSupported(value) {
|
|
561
|
+
const owned = attempt(() => cloneJSONRecord(value));
|
|
562
|
+
if (!owned.success) return false;
|
|
563
|
+
try {
|
|
564
|
+
const elicitation = owned.value["elicitation"];
|
|
565
|
+
if (!isRecord(elicitation)) return false;
|
|
566
|
+
if (isRecord(elicitation["form"])) return true;
|
|
567
|
+
return Object.keys(elicitation).length === 0;
|
|
568
|
+
} catch {
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Determines whether a client capability record declares the draft Tasks extension.
|
|
574
|
+
*
|
|
575
|
+
* @remarks
|
|
576
|
+
* The declaration lives at `extensions['io.modelcontextprotocol/tasks']` and its value is
|
|
577
|
+
* an empty object, so this is a PRESENCE check: the extension defines no options, and a
|
|
578
|
+
* server that read one would be reading a field no client can meaningfully set. The value
|
|
579
|
+
* must still be a record, because that is the shape the capability record declares — a
|
|
580
|
+
* `true` or a string there is a client speaking a different protocol, not a shorthand.
|
|
581
|
+
*
|
|
582
|
+
* A client declares this PER REQUEST. Nothing here consults a session, because the modern
|
|
583
|
+
* revision is stateless and a capability declared once at connect time says nothing about
|
|
584
|
+
* the request in hand. Total over hostile input.
|
|
585
|
+
*
|
|
586
|
+
* @param value - The client capability record to inspect
|
|
587
|
+
* @returns `true` when the tasks extension is declared
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* ```ts
|
|
591
|
+
* isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
|
|
592
|
+
* isTaskSupported({ extensions: {} }) // false — the key is the declaration
|
|
593
|
+
* ```
|
|
594
|
+
*/
|
|
595
|
+
function isTaskSupported(value) {
|
|
596
|
+
const owned = attempt(() => cloneJSONRecord(value));
|
|
597
|
+
if (!owned.success) return false;
|
|
598
|
+
try {
|
|
599
|
+
const extensions = owned.value["extensions"];
|
|
600
|
+
return isRecord(extensions) && isRecord(extensions["io.modelcontextprotocol/tasks"]);
|
|
601
|
+
} catch {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Serializes one exact JSON value deterministically within explicit bounds.
|
|
547
607
|
*
|
|
548
608
|
* @param value - The unknown value to validate and serialize
|
|
549
609
|
* @param limits - Serialized byte, key, and depth limits
|
|
@@ -762,7 +822,7 @@ function serializeJSON(value, limits) {
|
|
|
762
822
|
return serialized.success ? serialized.value : void 0;
|
|
763
823
|
}
|
|
764
824
|
/**
|
|
765
|
-
*
|
|
825
|
+
* Computes a lowercase host-neutral SHA-256 digest of one bounded canonical JSON value.
|
|
766
826
|
*
|
|
767
827
|
* @param value - The unknown value to validate and digest
|
|
768
828
|
* @param limits - Serialized byte, key, and depth limits
|
|
@@ -775,7 +835,7 @@ async function digestJSON(value, limits) {
|
|
|
775
835
|
return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
776
836
|
}
|
|
777
837
|
/**
|
|
778
|
-
*
|
|
838
|
+
* Builds one official progress notification for the original request stream.
|
|
779
839
|
*
|
|
780
840
|
* @param token - The request's original opaque progress token
|
|
781
841
|
* @param progress - The finite progress payload
|
|
@@ -794,7 +854,7 @@ function buildProgressNotification(token, progress) {
|
|
|
794
854
|
};
|
|
795
855
|
}
|
|
796
856
|
/**
|
|
797
|
-
*
|
|
857
|
+
* Builds one official cancellation notification for a request already sent.
|
|
798
858
|
*
|
|
799
859
|
* @remarks
|
|
800
860
|
* `requestId` and `reason` are WIRE SPELLINGS carried verbatim from the dated schema's
|
|
@@ -832,15 +892,15 @@ function buildCancelledNotification(id, reason) {
|
|
|
832
892
|
};
|
|
833
893
|
}
|
|
834
894
|
/**
|
|
835
|
-
*
|
|
895
|
+
* Determines whether one method may answer with a given modern `resultType`.
|
|
836
896
|
*
|
|
837
897
|
* @remarks
|
|
838
|
-
* The dated protocol lets a `tools/call` answer
|
|
898
|
+
* The dated protocol lets a `tools/call` answer in more than one way — it COMPLETED, it became a
|
|
839
899
|
* durable task, or it needs another round trip — while every other method this client
|
|
840
900
|
* issues has exactly one legal answer. So the arm a peer chose is only meaningful beside
|
|
841
901
|
* the method it answers, and this is the one place that pairing is decided.
|
|
842
902
|
*
|
|
843
|
-
* The rule is deliberately
|
|
903
|
+
* The rule is deliberately closed: an unrecognized `resultType` is refused for every
|
|
844
904
|
* method, including `tools/call`. A client that carried an arm it cannot name would hand
|
|
845
905
|
* its caller a value whose meaning it invented.
|
|
846
906
|
*
|
|
@@ -861,7 +921,7 @@ function matchesResultType(method, resultType) {
|
|
|
861
921
|
return resultType === "task" || resultType === "input_required";
|
|
862
922
|
}
|
|
863
923
|
/**
|
|
864
|
-
*
|
|
924
|
+
* Concatenates an MCP tool-call result's text content blocks into one string.
|
|
865
925
|
*
|
|
866
926
|
* @remarks
|
|
867
927
|
* The inverse of a server splitting a value into text block(s), and TOTAL: a non-record
|
|
@@ -885,12 +945,12 @@ function extractContentText(result) {
|
|
|
885
945
|
return parts.join("\n");
|
|
886
946
|
}
|
|
887
947
|
/**
|
|
888
|
-
*
|
|
948
|
+
* Narrows one `tools/call` answer to the arm the peer chose.
|
|
889
949
|
*
|
|
890
950
|
* @remarks
|
|
891
|
-
* The
|
|
951
|
+
* The arms {@link matchesResultType} admits are the whole space this sees, because a
|
|
892
952
|
* `resultType` the client cannot name is refused at correlation. What is left is validating
|
|
893
|
-
* the
|
|
953
|
+
* the arms the protocol gives a shape to, and deriving the tool's value from the one it
|
|
894
954
|
* does not:
|
|
895
955
|
*
|
|
896
956
|
* - A peer's `structuredContent` is PREFERRED over the content blocks, because it is the
|
|
@@ -938,7 +998,7 @@ function buildCallOutcome(name, result) {
|
|
|
938
998
|
});
|
|
939
999
|
}
|
|
940
1000
|
/**
|
|
941
|
-
*
|
|
1001
|
+
* Builds the canonical Tool call for one validated MCP `tools/call` request.
|
|
942
1002
|
*
|
|
943
1003
|
* @param request - The original MCP request
|
|
944
1004
|
* @param caller - Optional consumer-asserted caller context
|
|
@@ -957,7 +1017,7 @@ function buildToolCall(request, caller, args) {
|
|
|
957
1017
|
};
|
|
958
1018
|
}
|
|
959
1019
|
/**
|
|
960
|
-
*
|
|
1020
|
+
* Builds a JSON-RPC success {@link JSONRPCResultResponse} — the `id` echoed, the
|
|
961
1021
|
* method's value as `result`.
|
|
962
1022
|
*
|
|
963
1023
|
* @remarks
|
|
@@ -976,7 +1036,7 @@ function buildJSONRPCResult(id, result) {
|
|
|
976
1036
|
};
|
|
977
1037
|
}
|
|
978
1038
|
/**
|
|
979
|
-
*
|
|
1039
|
+
* Builds a JSON-RPC error {@link JSONRPCErrorResponse} — the `id` echoed, the failure
|
|
980
1040
|
* as an `error` object.
|
|
981
1041
|
*
|
|
982
1042
|
* @remarks
|
|
@@ -1005,7 +1065,7 @@ function buildJSONRPCError(id, code, message, data) {
|
|
|
1005
1065
|
};
|
|
1006
1066
|
}
|
|
1007
1067
|
/**
|
|
1008
|
-
*
|
|
1068
|
+
* Resolves the caller-facing dispatch options into the options a dispatched method
|
|
1009
1069
|
* receives.
|
|
1010
1070
|
*
|
|
1011
1071
|
* @remarks
|
|
@@ -1039,7 +1099,7 @@ function buildMethodOptions(options, lifetime) {
|
|
|
1039
1099
|
};
|
|
1040
1100
|
}
|
|
1041
1101
|
/**
|
|
1042
|
-
*
|
|
1102
|
+
* Maps a {@link ToolManagerInterface}'s definitions to MCP `tools/list` descriptors
|
|
1043
1103
|
* — renaming `parameters` to the wire's `inputSchema`.
|
|
1044
1104
|
*
|
|
1045
1105
|
* @remarks
|
|
@@ -1082,7 +1142,7 @@ function buildModernResult(result, identity, ttl, scope) {
|
|
|
1082
1142
|
};
|
|
1083
1143
|
}
|
|
1084
1144
|
/**
|
|
1085
|
-
*
|
|
1145
|
+
* Intersects a requested subscription filter with the notification families a server supports.
|
|
1086
1146
|
*
|
|
1087
1147
|
* @param requested - The notification families requested by the client
|
|
1088
1148
|
* @param supported - The notification families the server can actually produce
|
|
@@ -1102,7 +1162,7 @@ function buildSubscriptionFilter(requested, supported) {
|
|
|
1102
1162
|
};
|
|
1103
1163
|
}
|
|
1104
1164
|
/**
|
|
1105
|
-
*
|
|
1165
|
+
* Determines whether a produced notification belongs to an honoured subscription filter.
|
|
1106
1166
|
*
|
|
1107
1167
|
* @param notification - The server notification offered by the configured producer
|
|
1108
1168
|
* @param filter - The filter acknowledged to the client
|
|
@@ -1117,7 +1177,7 @@ function matchesSubscriptionNotification(notification, filter) {
|
|
|
1117
1177
|
return typeof uri === "string" && filter.resourceSubscriptions?.includes(uri) === true;
|
|
1118
1178
|
}
|
|
1119
1179
|
/**
|
|
1120
|
-
*
|
|
1180
|
+
* Stamps a subscription notification with the request id reserved for its held-open stream.
|
|
1121
1181
|
*
|
|
1122
1182
|
* @param notification - The notification to copy and stamp
|
|
1123
1183
|
* @param id - The `subscriptions/listen` request id
|
|
@@ -1138,7 +1198,7 @@ function stampSubscriptionNotification(notification, id) {
|
|
|
1138
1198
|
};
|
|
1139
1199
|
}
|
|
1140
1200
|
/**
|
|
1141
|
-
*
|
|
1201
|
+
* Builds the first notification carrying a subscription id for a listen request.
|
|
1142
1202
|
*
|
|
1143
1203
|
* @param notifications - The exact notification filter the server will honour
|
|
1144
1204
|
* @param id - The `subscriptions/listen` request id
|
|
@@ -1152,7 +1212,7 @@ function buildSubscriptionAcknowledgement(notifications, id) {
|
|
|
1152
1212
|
}, id);
|
|
1153
1213
|
}
|
|
1154
1214
|
/**
|
|
1155
|
-
*
|
|
1215
|
+
* Builds the terminating response for a subscription source that closes gracefully.
|
|
1156
1216
|
*
|
|
1157
1217
|
* @param id - The `subscriptions/listen` request id
|
|
1158
1218
|
* @param identity - The server identity included by the modern result stamping site
|
|
@@ -1162,7 +1222,7 @@ function buildSubscriptionResult(id, identity) {
|
|
|
1162
1222
|
return buildJSONRPCResult(id, buildModernResult({ _meta: { [MCP_META_SUBSCRIPTION]: id } }, identity));
|
|
1163
1223
|
}
|
|
1164
1224
|
/**
|
|
1165
|
-
*
|
|
1225
|
+
* Builds the mandatory modern `server/discover` result.
|
|
1166
1226
|
*
|
|
1167
1227
|
* @remarks
|
|
1168
1228
|
* `capabilities.resources` and `capabilities.prompts` appear only for servers with their
|
|
@@ -1194,7 +1254,7 @@ function buildDiscoverResult(options) {
|
|
|
1194
1254
|
}, options.identity, options.cache?.ttl ?? 6e4, options.cache?.scope);
|
|
1195
1255
|
}
|
|
1196
1256
|
/**
|
|
1197
|
-
*
|
|
1257
|
+
* Builds the MCP `initialize` result — the negotiated protocol version, the
|
|
1198
1258
|
* advertised capabilities, and the server identity.
|
|
1199
1259
|
*
|
|
1200
1260
|
* @remarks
|
|
@@ -1221,7 +1281,7 @@ function buildInitializeResult(name, version, requested) {
|
|
|
1221
1281
|
};
|
|
1222
1282
|
}
|
|
1223
1283
|
/**
|
|
1224
|
-
*
|
|
1284
|
+
* Decodes one raw inbound message within an explicit bound — the decode a binder performs
|
|
1225
1285
|
* before it hands the string on.
|
|
1226
1286
|
*
|
|
1227
1287
|
* @remarks
|
|
@@ -1229,14 +1289,14 @@ function buildInitializeResult(name, version, requested) {
|
|
|
1229
1289
|
* `JSON.parse`d at all: a decoder that parses before it measures has already spent the work
|
|
1230
1290
|
* the bound exists to refuse. A message over the bound, malformed JSON, and a well-formed
|
|
1231
1291
|
* value that is not a JSON-RPC message are one answer — `undefined` — because a binder does
|
|
1232
|
-
* exactly the same thing with
|
|
1292
|
+
* exactly the same thing with each of them: nothing, and let
|
|
1233
1293
|
* {@link import('./types.js').MCPServerInterface.handle} produce the wire refusal from the
|
|
1234
1294
|
* same bound.
|
|
1235
1295
|
*
|
|
1236
|
-
* Total
|
|
1296
|
+
* Total — never throws, whatever the input.
|
|
1237
1297
|
*
|
|
1238
1298
|
* @param message - The raw inbound JSON-RPC message string
|
|
1239
|
-
* @param limits - The byte and depth bounds to decode within (the server's own,
|
|
1299
|
+
* @param limits - The byte and depth bounds to decode within (the server's own, from `limit`)
|
|
1240
1300
|
* @returns The decoded message, or `undefined` when it is over the bound or is not one
|
|
1241
1301
|
*
|
|
1242
1302
|
* @example
|
|
@@ -1250,7 +1310,7 @@ function decodeBoundedMessage(message, limits) {
|
|
|
1250
1310
|
return parsed.success ? parseJSONRPCMessage(parsed.value, limits) : void 0;
|
|
1251
1311
|
}
|
|
1252
1312
|
/**
|
|
1253
|
-
*
|
|
1313
|
+
* Reads the request id an inbound `notifications/cancelled` names — the inverse of
|
|
1254
1314
|
* {@link buildCancelledNotification}.
|
|
1255
1315
|
*
|
|
1256
1316
|
* @remarks
|
|
@@ -1258,7 +1318,7 @@ function decodeBoundedMessage(message, limits) {
|
|
|
1258
1318
|
* real {@link JSONRPCId}: `null` is not one, and neither is an absent member, so a
|
|
1259
1319
|
* malformed frame reads as "cancels nothing" rather than as an error. Anything that is not a
|
|
1260
1320
|
* `notifications/cancelled` notification — a response, a request that happens to use the
|
|
1261
|
-
* method name, another notification — reads the same way. Total
|
|
1321
|
+
* method name, another notification — reads the same way. Total.
|
|
1262
1322
|
*
|
|
1263
1323
|
* @param message - The decoded inbound message to read
|
|
1264
1324
|
* @returns The id of the request being cancelled, or `undefined` when the message cancels nothing
|
|
@@ -1274,7 +1334,7 @@ function readCancelledId(message) {
|
|
|
1274
1334
|
return isJSONRPCId(requested) ? requested : void 0;
|
|
1275
1335
|
}
|
|
1276
1336
|
/**
|
|
1277
|
-
*
|
|
1337
|
+
* Pumps a controlled serialized exchange onto a transport — every notification in order, then
|
|
1278
1338
|
* the terminating response — and END the exchange however the pump leaves.
|
|
1279
1339
|
*
|
|
1280
1340
|
* @remarks
|
|
@@ -1319,9 +1379,9 @@ async function sendStream(stream, transport) {
|
|
|
1319
1379
|
}
|
|
1320
1380
|
}
|
|
1321
1381
|
/**
|
|
1322
|
-
*
|
|
1382
|
+
* Pipes an {@link MCPTransportInterface} into an {@link MCPDispatcherInterface} — every
|
|
1323
1383
|
* inbound message runs through `server.handle`, and a defined reply is written back
|
|
1324
|
-
*
|
|
1384
|
+
* through `transport.send`.
|
|
1325
1385
|
*
|
|
1326
1386
|
* @remarks
|
|
1327
1387
|
* `server.handle` already turns a malformed message into a serialized `-32700` /
|
|
@@ -1342,7 +1402,7 @@ async function sendStream(stream, transport) {
|
|
|
1342
1402
|
* **This binder OWNS every exchange it starts, and ends each one on every exit.** It holds one
|
|
1343
1403
|
* `AbortController` per live request, keyed by the request's id and deleted whenever that
|
|
1344
1404
|
* request leaves — normally, by a throw, or by cancellation — and it supplies that signal to
|
|
1345
|
-
* `handle` as {@link import('./types.js').MCPDispatchOptions}.
|
|
1405
|
+
* `handle` as {@link import('./types.js').MCPDispatchOptions}. These consequences follow.
|
|
1346
1406
|
* An inbound `notifications/cancelled` ABORTS the request it names, which is how the message-
|
|
1347
1407
|
* based cancellation path reaches a tool on the carriers that have one (stdio, WebSocket,
|
|
1348
1408
|
* `MessagePort`); a cancelled request writes NO response, because a peer that asked for a call
|
|
@@ -1415,7 +1475,7 @@ function bindServer(server, transport) {
|
|
|
1415
1475
|
};
|
|
1416
1476
|
}
|
|
1417
1477
|
/**
|
|
1418
|
-
*
|
|
1478
|
+
* Pipes an {@link MCPTransportInterface} into an {@link MCPClientInterface} — every
|
|
1419
1479
|
* inbound message is decoded and delivered onto the client's OWN transport
|
|
1420
1480
|
* (`client.transport.emitter`'s `message` / `close` events), resolving/rejecting the
|
|
1421
1481
|
* client's correlated pending requests exactly as a direct reply would.
|
|
@@ -1429,7 +1489,7 @@ function bindServer(server, transport) {
|
|
|
1429
1489
|
* this binder then completes the inbound half by decoding each message and pushing it
|
|
1430
1490
|
* onto `client.transport.emitter` (an {@link import('@orkestrel/emitter').EmitterInterface}
|
|
1431
1491
|
* exposes `emit`, so no client modification is needed). A malformed / non-JSON-RPC
|
|
1432
|
-
* inbound message is DROPPED (
|
|
1492
|
+
* inbound message is DROPPED (total — never throws); a delivery fault is routed to
|
|
1433
1493
|
* `client.transport.emitter`'s `error` event (never rethrown). The returned unbind
|
|
1434
1494
|
* DETACHES this binder (further inbound messages and the transport's `closed` signal are
|
|
1435
1495
|
* ignored) WITHOUT closing the transport.
|
|
@@ -1447,7 +1507,7 @@ function bindServer(server, transport) {
|
|
|
1447
1507
|
* itself when a caller's `signal` aborts, on a carrier declaring `duplex`. Adding a registry
|
|
1448
1508
|
* here would be a second correlation table for ids the client is already correlating, and two
|
|
1449
1509
|
* tables for one fact drift. The one obligation this binder does carry is delivery: a
|
|
1450
|
-
* malformed / non-JSON-RPC inbound message is DROPPED (
|
|
1510
|
+
* malformed / non-JSON-RPC inbound message is DROPPED (total — never throws).
|
|
1451
1511
|
*
|
|
1452
1512
|
* @param client - The transport-agnostic client whose transport to deliver messages onto
|
|
1453
1513
|
* @param transport - The duplex channel to pipe the client over
|
|
@@ -1497,11 +1557,11 @@ function bindClient(client, transport) {
|
|
|
1497
1557
|
}
|
|
1498
1558
|
//#endregion
|
|
1499
1559
|
//#region src/core/validators.ts
|
|
1500
|
-
/**
|
|
1560
|
+
/** Determines whether a value is an exact finite JSON object. */
|
|
1501
1561
|
function isJSONObject(value) {
|
|
1502
1562
|
return attempt(() => cloneJSONRecord(value)).success;
|
|
1503
1563
|
}
|
|
1504
|
-
/**
|
|
1564
|
+
/** Determines whether a string follows the dated MCP `_meta` key grammar. */
|
|
1505
1565
|
function isMCPMetaKey(value) {
|
|
1506
1566
|
if (!isString(value)) return false;
|
|
1507
1567
|
const slash = value.indexOf("/");
|
|
@@ -1514,24 +1574,24 @@ function isMCPMetaKey(value) {
|
|
|
1514
1574
|
}
|
|
1515
1575
|
return name.length === 0 || /^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?$/.test(name);
|
|
1516
1576
|
}
|
|
1517
|
-
/**
|
|
1577
|
+
/** Determines whether a value is exact finite MCP metadata with valid keys. */
|
|
1518
1578
|
function isMCPMetaObject(value) {
|
|
1519
1579
|
const owned = attempt(() => cloneJSONRecord(value));
|
|
1520
1580
|
return owned.success && Object.keys(owned.value).every((key) => isMCPMetaKey(key));
|
|
1521
1581
|
}
|
|
1522
|
-
/**
|
|
1582
|
+
/** Determines whether a value is exact result metadata with a valid reserved server identity. */
|
|
1523
1583
|
function isMCPResultMetaObject(value) {
|
|
1524
1584
|
const owned = attempt(() => cloneJSONRecord(value));
|
|
1525
1585
|
if (!owned.success || !Object.keys(owned.value).every((key) => isMCPMetaKey(key))) return false;
|
|
1526
1586
|
const identity = owned.value[MCP_META_SERVER];
|
|
1527
1587
|
return isUndefined(identity) || isMCPIdentity(identity);
|
|
1528
1588
|
}
|
|
1529
|
-
/**
|
|
1589
|
+
/** Determines whether a value is one dated MCP logging level. */
|
|
1530
1590
|
function isMCPLoggingLevel(value) {
|
|
1531
1591
|
return value === "debug" || value === "info" || value === "notice" || value === "warning" || value === "error" || value === "critical" || value === "alert" || value === "emergency";
|
|
1532
1592
|
}
|
|
1533
1593
|
/**
|
|
1534
|
-
*
|
|
1594
|
+
* Determines whether a value is standard padded base64 as required by JSON Schema `byte` format.
|
|
1535
1595
|
*
|
|
1536
1596
|
* @param value - The unknown value to inspect
|
|
1537
1597
|
* @returns Whether the value is an empty or completely padded standard base64 encoding
|
|
@@ -1540,7 +1600,7 @@ function isStandardBase64(value) {
|
|
|
1540
1600
|
return isString(value) && /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value);
|
|
1541
1601
|
}
|
|
1542
1602
|
/**
|
|
1543
|
-
*
|
|
1603
|
+
* Determines whether a value is one absolute URI under RFC 3986 syntax.
|
|
1544
1604
|
*
|
|
1545
1605
|
* @remarks
|
|
1546
1606
|
* This host-neutral syntax guard does not resolve, normalize, decode, fetch, or apply a
|
|
@@ -1629,7 +1689,7 @@ function isAbsoluteURI(value) {
|
|
|
1629
1689
|
return /^[A-Za-z0-9\-._~!$&'()*+,;=%]*$/.test(host);
|
|
1630
1690
|
}
|
|
1631
1691
|
/**
|
|
1632
|
-
*
|
|
1692
|
+
* Determines whether a value is one RFC 3339 `full-date` naming a real calendar day.
|
|
1633
1693
|
*
|
|
1634
1694
|
* @remarks
|
|
1635
1695
|
* RFC 3339 §5.6 defines `date-mday` as `01-28`, `29`, `30`, or `31` BASED ON the month and
|
|
@@ -1664,7 +1724,7 @@ function isRFC3339Date(value) {
|
|
|
1664
1724
|
return day <= (month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31);
|
|
1665
1725
|
}
|
|
1666
1726
|
/**
|
|
1667
|
-
*
|
|
1727
|
+
* Determines whether a value is one RFC 3339 `date-time` naming a real calendar day.
|
|
1668
1728
|
*
|
|
1669
1729
|
* @remarks
|
|
1670
1730
|
* The `full-date` half is {@link isRFC3339Date}, so an impossible day is refused here for
|
|
@@ -1691,7 +1751,7 @@ function isRFC3339DateTime(value) {
|
|
|
1691
1751
|
return matched !== null && isRFC3339Date(matched[1]);
|
|
1692
1752
|
}
|
|
1693
1753
|
/**
|
|
1694
|
-
*
|
|
1754
|
+
* Determines whether a value is one exact finite MCP progress payload.
|
|
1695
1755
|
*
|
|
1696
1756
|
* @param value - The unknown value to inspect
|
|
1697
1757
|
* @returns Whether required progress and optional total/message fields match the dated schema
|
|
@@ -1710,7 +1770,7 @@ function isMCPProgress(value) {
|
|
|
1710
1770
|
}
|
|
1711
1771
|
}
|
|
1712
1772
|
/**
|
|
1713
|
-
*
|
|
1773
|
+
* Determines whether a value carries valid dated-schema MCP content annotations.
|
|
1714
1774
|
*
|
|
1715
1775
|
* @param value - The unknown value to inspect
|
|
1716
1776
|
* @returns Whether the value is valid MCP annotations
|
|
@@ -1731,7 +1791,7 @@ function isMCPAnnotations(value) {
|
|
|
1731
1791
|
}
|
|
1732
1792
|
}
|
|
1733
1793
|
/**
|
|
1734
|
-
*
|
|
1794
|
+
* Determines whether a value is one exact dated-schema MCP icon.
|
|
1735
1795
|
*
|
|
1736
1796
|
* @param value - The unknown value to inspect
|
|
1737
1797
|
* @returns Whether the value is a valid MCP icon
|
|
@@ -1750,7 +1810,7 @@ function isMCPIcon(value) {
|
|
|
1750
1810
|
return false;
|
|
1751
1811
|
}
|
|
1752
1812
|
}
|
|
1753
|
-
/**
|
|
1813
|
+
/** Determines whether a value is one complete dated MCP implementation identity. */
|
|
1754
1814
|
function isMCPIdentity(value) {
|
|
1755
1815
|
const owned = attempt(() => cloneJSONRecord(value));
|
|
1756
1816
|
if (!owned.success) return false;
|
|
@@ -1766,7 +1826,7 @@ function isMCPIdentity(value) {
|
|
|
1766
1826
|
return false;
|
|
1767
1827
|
}
|
|
1768
1828
|
}
|
|
1769
|
-
/**
|
|
1829
|
+
/** Determines whether a value is one exact open dated client-capability declaration. */
|
|
1770
1830
|
function isMCPClientCapabilities(value) {
|
|
1771
1831
|
const owned = attempt(() => cloneJSONRecord(value));
|
|
1772
1832
|
if (!owned.success) return false;
|
|
@@ -1796,7 +1856,7 @@ function isMCPClientCapabilities(value) {
|
|
|
1796
1856
|
return false;
|
|
1797
1857
|
}
|
|
1798
1858
|
}
|
|
1799
|
-
/**
|
|
1859
|
+
/** Determines whether a value is one exact open dated server-capability declaration. */
|
|
1800
1860
|
function isMCPServerCapabilities(value) {
|
|
1801
1861
|
const owned = attempt(() => cloneJSONRecord(value));
|
|
1802
1862
|
if (!owned.success) return false;
|
|
@@ -1822,7 +1882,7 @@ function isMCPServerCapabilities(value) {
|
|
|
1822
1882
|
}
|
|
1823
1883
|
}
|
|
1824
1884
|
/**
|
|
1825
|
-
*
|
|
1885
|
+
* Determines whether a value is embedded textual MCP resource contents.
|
|
1826
1886
|
*
|
|
1827
1887
|
* @param value - The unknown value to inspect
|
|
1828
1888
|
* @returns Whether the value is embedded textual resource contents
|
|
@@ -1841,7 +1901,7 @@ function isMCPTextResource(value) {
|
|
|
1841
1901
|
}
|
|
1842
1902
|
}
|
|
1843
1903
|
/**
|
|
1844
|
-
*
|
|
1904
|
+
* Determines whether a value is embedded blob MCP resource contents.
|
|
1845
1905
|
*
|
|
1846
1906
|
* @param value - The unknown value to inspect
|
|
1847
1907
|
* @returns Whether the value is embedded blob resource contents
|
|
@@ -1860,7 +1920,7 @@ function isMCPBlobResource(value) {
|
|
|
1860
1920
|
}
|
|
1861
1921
|
}
|
|
1862
1922
|
/**
|
|
1863
|
-
*
|
|
1923
|
+
* Determines whether a value is one `resources/list` descriptor.
|
|
1864
1924
|
*
|
|
1865
1925
|
* @param value - The unknown value to inspect
|
|
1866
1926
|
* @returns Whether the value is a valid resource descriptor
|
|
@@ -1878,7 +1938,7 @@ function isMCPResource(value) {
|
|
|
1878
1938
|
}
|
|
1879
1939
|
}
|
|
1880
1940
|
/**
|
|
1881
|
-
*
|
|
1941
|
+
* Determines whether a value is one resource-template descriptor.
|
|
1882
1942
|
*
|
|
1883
1943
|
* @remarks
|
|
1884
1944
|
* This guard validates the descriptor shape. Template expansion and the RFC 6570 feature
|
|
@@ -1899,7 +1959,7 @@ function isMCPResourceTemplate(value) {
|
|
|
1899
1959
|
}
|
|
1900
1960
|
}
|
|
1901
1961
|
/**
|
|
1902
|
-
*
|
|
1962
|
+
* Determines whether a value is structurally discriminated resource contents.
|
|
1903
1963
|
*
|
|
1904
1964
|
* @param value - The unknown value to inspect
|
|
1905
1965
|
* @returns Whether exactly one of `text` and `blob` is present and valid
|
|
@@ -1913,7 +1973,7 @@ function isMCPResourceContents(value) {
|
|
|
1913
1973
|
return text ? isMCPTextResource(resource) : isMCPBlobResource(resource);
|
|
1914
1974
|
}
|
|
1915
1975
|
/**
|
|
1916
|
-
*
|
|
1976
|
+
* Determines whether a value carries the shared optional pagination cursor.
|
|
1917
1977
|
*
|
|
1918
1978
|
* @param value - The unknown value to inspect
|
|
1919
1979
|
* @returns Whether a present `cursor` is a string
|
|
@@ -1923,7 +1983,7 @@ function isMCPPaginationParams(value) {
|
|
|
1923
1983
|
return owned.success && (isUndefined(owned.value["cursor"]) || isString(owned.value["cursor"]));
|
|
1924
1984
|
}
|
|
1925
1985
|
/**
|
|
1926
|
-
*
|
|
1986
|
+
* Determines whether a value is one consumer-owned resource page.
|
|
1927
1987
|
*
|
|
1928
1988
|
* @param value - The unknown value to inspect
|
|
1929
1989
|
* @returns Whether the resources and optional following cursor are valid
|
|
@@ -1936,7 +1996,7 @@ function isMCPResourcePage(value) {
|
|
|
1936
1996
|
return Array.isArray(resources) && resources.every((resource) => isMCPResource(resource)) && (isUndefined(cursor) || isString(cursor));
|
|
1937
1997
|
}
|
|
1938
1998
|
/**
|
|
1939
|
-
*
|
|
1999
|
+
* Determines whether a value is one consumer-owned resource-template page.
|
|
1940
2000
|
*
|
|
1941
2001
|
* @param value - The unknown value to inspect
|
|
1942
2002
|
* @returns Whether the templates and optional following cursor are valid
|
|
@@ -1949,7 +2009,7 @@ function isMCPResourceTemplatePage(value) {
|
|
|
1949
2009
|
return Array.isArray(templates) && templates.every((template) => isMCPResourceTemplate(template)) && (isUndefined(cursor) || isString(cursor));
|
|
1950
2010
|
}
|
|
1951
2011
|
/**
|
|
1952
|
-
*
|
|
2012
|
+
* Determines whether a value is a string-valued MCP argument record.
|
|
1953
2013
|
*
|
|
1954
2014
|
* @param value - The unknown value to inspect
|
|
1955
2015
|
* @returns Whether every own argument value is a string
|
|
@@ -1959,7 +2019,7 @@ function isMCPStringArguments(value) {
|
|
|
1959
2019
|
return owned.success && Object.values(owned.value).every((argument) => isString(argument));
|
|
1960
2020
|
}
|
|
1961
2021
|
/**
|
|
1962
|
-
*
|
|
2022
|
+
* Determines whether a value is one prompt argument descriptor.
|
|
1963
2023
|
*
|
|
1964
2024
|
* @param value - The unknown value to inspect
|
|
1965
2025
|
* @returns Whether the prompt argument descriptor is valid
|
|
@@ -1971,7 +2031,7 @@ function isMCPPromptArgument(value) {
|
|
|
1971
2031
|
return isString(argument["name"]) && (isUndefined(argument["title"]) || isString(argument["title"])) && (isUndefined(argument["description"]) || isString(argument["description"])) && (isUndefined(argument["required"]) || isBoolean(argument["required"]));
|
|
1972
2032
|
}
|
|
1973
2033
|
/**
|
|
1974
|
-
*
|
|
2034
|
+
* Determines whether a value is one `prompts/list` descriptor.
|
|
1975
2035
|
*
|
|
1976
2036
|
* @param value - The unknown value to inspect
|
|
1977
2037
|
* @returns Whether the prompt descriptor is valid
|
|
@@ -1985,7 +2045,7 @@ function isMCPPrompt(value) {
|
|
|
1985
2045
|
return isString(prompt["name"]) && (isUndefined(prompt["title"]) || isString(prompt["title"])) && (isUndefined(prompt["description"]) || isString(prompt["description"])) && (isUndefined(argumentsValue) || Array.isArray(argumentsValue) && argumentsValue.every((argument) => isMCPPromptArgument(argument))) && (isUndefined(icons) || Array.isArray(icons) && icons.every((icon) => isMCPIcon(icon))) && (isUndefined(prompt["_meta"]) || isMCPMetaObject(prompt["_meta"]));
|
|
1986
2046
|
}
|
|
1987
2047
|
/**
|
|
1988
|
-
*
|
|
2048
|
+
* Determines whether a value is one prompt message with existing rich content.
|
|
1989
2049
|
*
|
|
1990
2050
|
* @param value - The unknown value to inspect
|
|
1991
2051
|
* @returns Whether the role and content are valid
|
|
@@ -1995,7 +2055,7 @@ function isMCPPromptMessage(value) {
|
|
|
1995
2055
|
return owned.success && (owned.value["role"] === "user" || owned.value["role"] === "assistant") && isMCPContent(owned.value["content"]);
|
|
1996
2056
|
}
|
|
1997
2057
|
/**
|
|
1998
|
-
*
|
|
2058
|
+
* Determines whether a value is one consumer-owned prompt page.
|
|
1999
2059
|
*
|
|
2000
2060
|
* @param value - The unknown value to inspect
|
|
2001
2061
|
* @returns Whether the prompts and optional following cursor are valid
|
|
@@ -2008,7 +2068,7 @@ function isMCPPromptPage(value) {
|
|
|
2008
2068
|
return Array.isArray(prompts) && prompts.every((prompt) => isMCPPrompt(prompt)) && (isUndefined(cursor) || isString(cursor));
|
|
2009
2069
|
}
|
|
2010
2070
|
/**
|
|
2011
|
-
*
|
|
2071
|
+
* Determines whether a value is one complete `prompts/get` result.
|
|
2012
2072
|
*
|
|
2013
2073
|
* @param value - The unknown value to inspect
|
|
2014
2074
|
* @returns Whether the prompt result and all messages are valid
|
|
@@ -2021,7 +2081,7 @@ function isMCPPromptGetResult(value) {
|
|
|
2021
2081
|
return result["resultType"] === "complete" && (isUndefined(result["description"]) || isString(result["description"])) && Array.isArray(messages) && messages.every((message) => isMCPPromptMessage(message)) && (isUndefined(result["_meta"]) || isMCPResultMetaObject(result["_meta"]));
|
|
2022
2082
|
}
|
|
2023
2083
|
/**
|
|
2024
|
-
*
|
|
2084
|
+
* Determines whether a value is a prompt or resource-template completion reference.
|
|
2025
2085
|
*
|
|
2026
2086
|
* @param value - The unknown value to inspect
|
|
2027
2087
|
* @returns Whether the discriminated reference is valid
|
|
@@ -2033,7 +2093,7 @@ function isMCPCompletionReference(value) {
|
|
|
2033
2093
|
return reference["type"] === "ref/prompt" ? isString(reference["name"]) : reference["type"] === "ref/resource" && isString(reference["uri"]);
|
|
2034
2094
|
}
|
|
2035
2095
|
/**
|
|
2036
|
-
*
|
|
2096
|
+
* Determines whether a value is one `completion/complete` parameter object.
|
|
2037
2097
|
*
|
|
2038
2098
|
* @param value - The unknown value to inspect
|
|
2039
2099
|
* @returns Whether its reference, fragment, and optional string context are valid
|
|
@@ -2052,7 +2112,7 @@ function isMCPCompletionParams(value) {
|
|
|
2052
2112
|
return isMCPCompletionReference(params["ref"]) && isString(argument.value["name"]) && isString(argument.value["value"]);
|
|
2053
2113
|
}
|
|
2054
2114
|
/**
|
|
2055
|
-
*
|
|
2115
|
+
* Determines whether a value is one host-produced completion candidate set.
|
|
2056
2116
|
*
|
|
2057
2117
|
* @param value - The unknown value to inspect
|
|
2058
2118
|
* @returns Whether its candidates and optional result facts are valid
|
|
@@ -2066,7 +2126,7 @@ function isMCPCompletion(value) {
|
|
|
2066
2126
|
return Array.isArray(values) && values.every((candidate) => isString(candidate)) && (isUndefined(total) || isInteger(total) && total >= 0) && (isUndefined(completion["hasMore"]) || isBoolean(completion["hasMore"]));
|
|
2067
2127
|
}
|
|
2068
2128
|
/**
|
|
2069
|
-
*
|
|
2129
|
+
* Determines whether a value is one complete, capped `completion/complete` result.
|
|
2070
2130
|
*
|
|
2071
2131
|
* @param value - The unknown value to inspect
|
|
2072
2132
|
* @returns Whether the result is complete and carries at most 100 candidates
|
|
@@ -2078,7 +2138,7 @@ function isMCPCompletionResult(value) {
|
|
|
2078
2138
|
return isMCPCompletion(completion) && completion.values.length <= 100 && (isUndefined(owned.value["_meta"]) || isMCPResultMetaObject(owned.value["_meta"]));
|
|
2079
2139
|
}
|
|
2080
2140
|
/**
|
|
2081
|
-
*
|
|
2141
|
+
* Determines whether a value is one exact dated-schema MCP tool content block.
|
|
2082
2142
|
*
|
|
2083
2143
|
* @param value - The unknown value to inspect
|
|
2084
2144
|
* @returns Whether the value is valid MCP content
|
|
@@ -2110,7 +2170,7 @@ function isMCPContent(value) {
|
|
|
2110
2170
|
}
|
|
2111
2171
|
}
|
|
2112
2172
|
/**
|
|
2113
|
-
*
|
|
2173
|
+
* Determines whether a value is one modern MCP result.
|
|
2114
2174
|
*
|
|
2115
2175
|
* @remarks
|
|
2116
2176
|
* The open contract's guard: a record carrying a string `resultType` and, when
|
|
@@ -2140,7 +2200,7 @@ function isMCPResult(value) {
|
|
|
2140
2200
|
return isUndefined(metadata) || isMCPResultMetaObject(metadata);
|
|
2141
2201
|
}
|
|
2142
2202
|
/**
|
|
2143
|
-
*
|
|
2203
|
+
* Determines whether a value is one legacy-era MCP result.
|
|
2144
2204
|
*
|
|
2145
2205
|
* @remarks
|
|
2146
2206
|
* The legacy revision has no result discriminator, so the absence of `resultType` is
|
|
@@ -2161,7 +2221,7 @@ function isMCPLegacyResult(value) {
|
|
|
2161
2221
|
return owned.success && !Object.hasOwn(owned.value, "resultType");
|
|
2162
2222
|
}
|
|
2163
2223
|
/**
|
|
2164
|
-
*
|
|
2224
|
+
* Determines whether a value is a complete modern MCP tool result.
|
|
2165
2225
|
*
|
|
2166
2226
|
* @param value - The unknown value to inspect
|
|
2167
2227
|
* @returns Whether the value is a complete MCP call result
|
|
@@ -2181,7 +2241,7 @@ function isMCPCallResult(value) {
|
|
|
2181
2241
|
}
|
|
2182
2242
|
}
|
|
2183
2243
|
/**
|
|
2184
|
-
*
|
|
2244
|
+
* Determines whether a value is a modern MCP task-creation result.
|
|
2185
2245
|
*
|
|
2186
2246
|
* @remarks
|
|
2187
2247
|
* The runtime enforcement of {@link MCPTaskManagerInterface.start}'s declared return
|
|
@@ -2215,7 +2275,7 @@ function isMCPTaskResult(value) {
|
|
|
2215
2275
|
}
|
|
2216
2276
|
}
|
|
2217
2277
|
/**
|
|
2218
|
-
*
|
|
2278
|
+
* Determines whether a value is one of the extension's task lifecycle states.
|
|
2219
2279
|
*
|
|
2220
2280
|
* @param value - The unknown value to inspect
|
|
2221
2281
|
* @returns Whether the value is an {@link MCPTaskStatus}
|
|
@@ -2230,7 +2290,7 @@ function isMCPTaskStatus(value) {
|
|
|
2230
2290
|
return value === "working" || value === "input_required" || value === "completed" || value === "failed" || value === "cancelled";
|
|
2231
2291
|
}
|
|
2232
2292
|
/**
|
|
2233
|
-
*
|
|
2293
|
+
* Determines whether a value is one durable task's full snapshot.
|
|
2234
2294
|
*
|
|
2235
2295
|
* @remarks
|
|
2236
2296
|
* The runtime enforcement of {@link MCPTaskManagerInterface.task}'s declared return shape,
|
|
@@ -2273,7 +2333,7 @@ function isMCPTaskDetail(value) {
|
|
|
2273
2333
|
}
|
|
2274
2334
|
}
|
|
2275
2335
|
/**
|
|
2276
|
-
*
|
|
2336
|
+
* Determines whether a value is a string within a UTF-8 byte bound.
|
|
2277
2337
|
*
|
|
2278
2338
|
* @param value - The unknown value to inspect
|
|
2279
2339
|
* @param bytes - The maximum accepted encoded bytes
|
|
@@ -2304,7 +2364,7 @@ function isBoundedString(value, bytes) {
|
|
|
2304
2364
|
return true;
|
|
2305
2365
|
}
|
|
2306
2366
|
/**
|
|
2307
|
-
*
|
|
2367
|
+
* Determines whether a value is bounded, cycle-free exact JSON.
|
|
2308
2368
|
*
|
|
2309
2369
|
* @remarks
|
|
2310
2370
|
* Traversal is iterative, ancestor-aware, and contained by {@link attempt}; deep input,
|
|
@@ -2324,7 +2384,7 @@ function isBoundedJSON(value, limits) {
|
|
|
2324
2384
|
return serializeJSON(value, limits) !== void 0;
|
|
2325
2385
|
}
|
|
2326
2386
|
/**
|
|
2327
|
-
*
|
|
2387
|
+
* Determines whether a value is a valid JSON-RPC correlation id — a string or a
|
|
2328
2388
|
* finite integer.
|
|
2329
2389
|
*
|
|
2330
2390
|
* @remarks
|
|
@@ -2332,7 +2392,7 @@ function isBoundedJSON(value, limits) {
|
|
|
2332
2392
|
* absence is what makes a call a notification) and `null` is not one either (MCP
|
|
2333
2393
|
* omits an unreadable id rather than nulling it). A runtime numeric id must be a
|
|
2334
2394
|
* finite integer; an empty string is a legal id, because the dated schema imposes
|
|
2335
|
-
* no minimum length. Total
|
|
2395
|
+
* no minimum length. Total: any other input returns `false`.
|
|
2336
2396
|
*
|
|
2337
2397
|
* @param value - The already-parsed value to test
|
|
2338
2398
|
* @returns `true` when `value` is a string or a finite integer
|
|
@@ -2350,7 +2410,7 @@ function isJSONRPCId(value) {
|
|
|
2350
2410
|
return isString(value) || isInteger(value);
|
|
2351
2411
|
}
|
|
2352
2412
|
/**
|
|
2353
|
-
*
|
|
2413
|
+
* Determines whether a value is a supported {@link MCPVersion}.
|
|
2354
2414
|
*
|
|
2355
2415
|
* @param value - The unknown value to inspect
|
|
2356
2416
|
* @returns `true` when the value is one of {@link SUPPORTED_PROTOCOL_VERSIONS}
|
|
@@ -2359,7 +2419,7 @@ function isMCPVersion(value) {
|
|
|
2359
2419
|
return isString(value) && SUPPORTED_PROTOCOL_VERSIONS.some((version) => version === value);
|
|
2360
2420
|
}
|
|
2361
2421
|
/**
|
|
2362
|
-
*
|
|
2422
|
+
* Determines whether a value is an MCP {@link MCPSubscriptionFilter}.
|
|
2363
2423
|
*
|
|
2364
2424
|
* @remarks
|
|
2365
2425
|
* Every filter field is optional. Boolean notification families accept only booleans, and
|
|
@@ -2384,69 +2444,7 @@ function isMCPSubscriptionFilter(value) {
|
|
|
2384
2444
|
return isUndefined(subscriptions) || arrayOf(isString)(subscriptions);
|
|
2385
2445
|
}
|
|
2386
2446
|
/**
|
|
2387
|
-
*
|
|
2388
|
-
*
|
|
2389
|
-
* @remarks
|
|
2390
|
-
* The protocol's empty `elicitation` object is the implicit form-only declaration.
|
|
2391
|
-
* A non-empty declaration must carry a record-valued `form` member; URL-only support
|
|
2392
|
-
* does not authorize a form request. Total over hostile input.
|
|
2393
|
-
*
|
|
2394
|
-
* @param value - The client capability record to inspect
|
|
2395
|
-
* @returns `true` when form-mode elicitation is declared
|
|
2396
|
-
*
|
|
2397
|
-
* @example
|
|
2398
|
-
* ```ts
|
|
2399
|
-
* isFormElicitationSupported({ elicitation: {} }) // true — implicit form mode
|
|
2400
|
-
* isFormElicitationSupported({ elicitation: { url: {} } }) // false
|
|
2401
|
-
* ```
|
|
2402
|
-
*/
|
|
2403
|
-
function isFormElicitationSupported(value) {
|
|
2404
|
-
const owned = attempt(() => cloneJSONRecord(value));
|
|
2405
|
-
if (!owned.success) return false;
|
|
2406
|
-
try {
|
|
2407
|
-
const elicitation = owned.value["elicitation"];
|
|
2408
|
-
if (!isRecord(elicitation)) return false;
|
|
2409
|
-
if (isRecord(elicitation["form"])) return true;
|
|
2410
|
-
return Object.keys(elicitation).length === 0;
|
|
2411
|
-
} catch {
|
|
2412
|
-
return false;
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
|
-
/**
|
|
2416
|
-
* Determine whether a client capability record declares the draft Tasks extension.
|
|
2417
|
-
*
|
|
2418
|
-
* @remarks
|
|
2419
|
-
* The declaration lives at `extensions['io.modelcontextprotocol/tasks']` and its value is
|
|
2420
|
-
* an empty object, so this is a PRESENCE check: the extension defines no options, and a
|
|
2421
|
-
* server that read one would be reading a field no client can meaningfully set. The value
|
|
2422
|
-
* must still be a record, because that is the shape the capability record declares — a
|
|
2423
|
-
* `true` or a string there is a client speaking a different protocol, not a shorthand.
|
|
2424
|
-
*
|
|
2425
|
-
* A client declares this PER REQUEST. Nothing here consults a session, because the modern
|
|
2426
|
-
* revision is stateless and a capability declared once at connect time says nothing about
|
|
2427
|
-
* the request in hand. Total over hostile input.
|
|
2428
|
-
*
|
|
2429
|
-
* @param value - The client capability record to inspect
|
|
2430
|
-
* @returns `true` when the tasks extension is declared
|
|
2431
|
-
*
|
|
2432
|
-
* @example
|
|
2433
|
-
* ```ts
|
|
2434
|
-
* isTaskSupported({ extensions: { 'io.modelcontextprotocol/tasks': {} } }) // true
|
|
2435
|
-
* isTaskSupported({ extensions: {} }) // false — the key is the declaration
|
|
2436
|
-
* ```
|
|
2437
|
-
*/
|
|
2438
|
-
function isTaskSupported(value) {
|
|
2439
|
-
const owned = attempt(() => cloneJSONRecord(value));
|
|
2440
|
-
if (!owned.success) return false;
|
|
2441
|
-
try {
|
|
2442
|
-
const extensions = owned.value["extensions"];
|
|
2443
|
-
return isRecord(extensions) && isRecord(extensions["io.modelcontextprotocol/tasks"]);
|
|
2444
|
-
} catch {
|
|
2445
|
-
return false;
|
|
2446
|
-
}
|
|
2447
|
-
}
|
|
2448
|
-
/**
|
|
2449
|
-
* Determine whether a value is one restricted primitive form-elicitation schema.
|
|
2447
|
+
* Determines whether a value is one restricted primitive form-elicitation schema.
|
|
2450
2448
|
*
|
|
2451
2449
|
* @param value - The unknown value to inspect
|
|
2452
2450
|
* @returns `true` for a supported boolean, numeric, string, or string-array schema
|
|
@@ -2502,7 +2500,7 @@ function isMCPElicitFieldSchema(value) {
|
|
|
2502
2500
|
}
|
|
2503
2501
|
}
|
|
2504
2502
|
/**
|
|
2505
|
-
*
|
|
2503
|
+
* Determines whether a value is the restricted top-level object schema a form elicitation issues.
|
|
2506
2504
|
*
|
|
2507
2505
|
* @remarks
|
|
2508
2506
|
* The schema half of {@link isMCPElicitForm}, exported on its own because the issued schema
|
|
@@ -2536,7 +2534,7 @@ function isMCPElicitSchema(value) {
|
|
|
2536
2534
|
}
|
|
2537
2535
|
}
|
|
2538
2536
|
/**
|
|
2539
|
-
*
|
|
2537
|
+
* Determines whether a value is a form-mode elicitation parameter object.
|
|
2540
2538
|
*
|
|
2541
2539
|
* @param value - The unknown value to inspect
|
|
2542
2540
|
* @returns `true` when `value` has the restricted form elicitation shape
|
|
@@ -2562,7 +2560,7 @@ function isMCPElicitForm(value) {
|
|
|
2562
2560
|
}
|
|
2563
2561
|
}
|
|
2564
2562
|
/**
|
|
2565
|
-
*
|
|
2563
|
+
* Determines whether a value is a URL-mode elicitation parameter object.
|
|
2566
2564
|
*
|
|
2567
2565
|
* @param value - The unknown value to inspect
|
|
2568
2566
|
* @returns `true` when `value` has the URL elicitation shape
|
|
@@ -2583,7 +2581,7 @@ function isMCPElicitURL(value) {
|
|
|
2583
2581
|
}
|
|
2584
2582
|
}
|
|
2585
2583
|
/**
|
|
2586
|
-
*
|
|
2584
|
+
* Determines whether a value is an embedded `elicitation/create` request.
|
|
2587
2585
|
*
|
|
2588
2586
|
* @param value - The unknown value to inspect
|
|
2589
2587
|
* @returns `true` when `value` is a form- or URL-mode elicitation request
|
|
@@ -2608,7 +2606,7 @@ function isMCPElicitRequest(value) {
|
|
|
2608
2606
|
}
|
|
2609
2607
|
}
|
|
2610
2608
|
/**
|
|
2611
|
-
*
|
|
2609
|
+
* Determines whether a value is one legal embedded multi-round-trip request.
|
|
2612
2610
|
*
|
|
2613
2611
|
* @param value - The unknown value to inspect
|
|
2614
2612
|
* @returns `true` for elicitation, deprecated sampling, or deprecated roots requests
|
|
@@ -2632,7 +2630,7 @@ function isMCPInputRequest(value) {
|
|
|
2632
2630
|
}
|
|
2633
2631
|
}
|
|
2634
2632
|
/**
|
|
2635
|
-
*
|
|
2633
|
+
* Determines whether a value is a server-keyed map of embedded input requests.
|
|
2636
2634
|
*
|
|
2637
2635
|
* @param value - The unknown value to inspect
|
|
2638
2636
|
* @returns `true` when every own value is a legal {@link MCPInputRequest}
|
|
@@ -2652,7 +2650,7 @@ function isMCPInputRequestMap(value) {
|
|
|
2652
2650
|
}
|
|
2653
2651
|
}
|
|
2654
2652
|
/**
|
|
2655
|
-
*
|
|
2653
|
+
* Determines whether a value is one elicitation response.
|
|
2656
2654
|
*
|
|
2657
2655
|
* @param value - The unknown value to inspect
|
|
2658
2656
|
* @returns `true` when action/content have the protocol shape
|
|
@@ -2679,7 +2677,7 @@ function isMCPElicitResult(value) {
|
|
|
2679
2677
|
}
|
|
2680
2678
|
}
|
|
2681
2679
|
/**
|
|
2682
|
-
*
|
|
2680
|
+
* Determines whether accepted elicitation content satisfies the exact schema that was issued.
|
|
2683
2681
|
*
|
|
2684
2682
|
* @remarks
|
|
2685
2683
|
* {@link isMCPElicitResult} says a response has the SHAPE of a response; this says the
|
|
@@ -2701,7 +2699,7 @@ function isMCPElicitResult(value) {
|
|
|
2701
2699
|
* that answers more than it was asked is not refused for it. A `schema` that is not itself a
|
|
2702
2700
|
* valid {@link MCPElicitSchema} admits NOTHING — an unenforceable schema is never a permissive
|
|
2703
2701
|
* one — which is why `schema` is accepted as `unknown` and checked rather than trusted. Total
|
|
2704
|
-
*
|
|
2702
|
+
* over hostile content and hostile schemas alike.
|
|
2705
2703
|
*
|
|
2706
2704
|
* @param value - The accepted response content to check
|
|
2707
2705
|
* @param schema - The exact {@link MCPElicitSchema} that was issued with the elicitation
|
|
@@ -2786,7 +2784,7 @@ function isElicitContent(value, schema) {
|
|
|
2786
2784
|
}
|
|
2787
2785
|
}
|
|
2788
2786
|
/**
|
|
2789
|
-
*
|
|
2787
|
+
* Determines whether a value is an MCP input-required result.
|
|
2790
2788
|
*
|
|
2791
2789
|
* @remarks
|
|
2792
2790
|
* Enforces the at-least-one-of rule at runtime: `inputRequests`, `requestState`, or
|
|
@@ -2819,15 +2817,15 @@ function isMCPInputResult(value) {
|
|
|
2819
2817
|
}
|
|
2820
2818
|
}
|
|
2821
2819
|
/**
|
|
2822
|
-
*
|
|
2820
|
+
* Determines whether a parsed value is a {@link JSONRPCRequest}.
|
|
2823
2821
|
*
|
|
2824
2822
|
* @remarks
|
|
2825
2823
|
* A request is a record with `jsonrpc === '2.0'`, a string `method`, and an `id`
|
|
2826
2824
|
* that {@link isJSONRPCId} accepts. An id-less call is NOT a request — it is a
|
|
2827
2825
|
* {@link JSONRPCNotification}, which {@link isJSONRPCNotification} answers for. The
|
|
2828
|
-
*
|
|
2826
|
+
* guards are mutually exclusive on every input: this one requires a valid `id`
|
|
2829
2827
|
* value, that one requires no own `id` member at all. `params`, when present, must
|
|
2830
|
-
* be a record. Total
|
|
2828
|
+
* be a record. Total: any other input returns `false`.
|
|
2831
2829
|
*
|
|
2832
2830
|
* @param value - The already-parsed value to test
|
|
2833
2831
|
* @returns `true` when `value` is a valid JSON-RPC request
|
|
@@ -2849,12 +2847,12 @@ function isJSONRPCRequest(value) {
|
|
|
2849
2847
|
return isUndefined(params) || isRecord(params);
|
|
2850
2848
|
}
|
|
2851
2849
|
/**
|
|
2852
|
-
*
|
|
2850
|
+
* Determines whether a parsed value is a {@link JSONRPCNotification}.
|
|
2853
2851
|
*
|
|
2854
2852
|
* @remarks
|
|
2855
2853
|
* A notification is a request-shaped call carrying NO `id` member — the protocol
|
|
2856
2854
|
* forbids one, because nothing answers a notification. `params`, when present, must
|
|
2857
|
-
* be a record. Total
|
|
2855
|
+
* be a record. Total: any other input returns `false`.
|
|
2858
2856
|
*
|
|
2859
2857
|
* @param value - The already-parsed value to test
|
|
2860
2858
|
* @returns `true` when `value` is a valid JSON-RPC notification
|
|
@@ -2875,12 +2873,12 @@ function isJSONRPCNotification(value) {
|
|
|
2875
2873
|
return isUndefined(params) || isRecord(params);
|
|
2876
2874
|
}
|
|
2877
2875
|
/**
|
|
2878
|
-
*
|
|
2876
|
+
* Determines whether a parsed value is a {@link JSONRPCInvocation} — a request or a
|
|
2879
2877
|
* notification.
|
|
2880
2878
|
*
|
|
2881
2879
|
* @remarks
|
|
2882
2880
|
* The union of {@link isJSONRPCRequest} and {@link isJSONRPCNotification}, which are
|
|
2883
|
-
* mutually exclusive, so a positive answer names exactly one arm. Total
|
|
2881
|
+
* mutually exclusive, so a positive answer names exactly one arm. Total.
|
|
2884
2882
|
*
|
|
2885
2883
|
* @param value - The already-parsed value to test
|
|
2886
2884
|
* @returns `true` when `value` is a valid JSON-RPC request or notification
|
|
@@ -2889,7 +2887,7 @@ function isJSONRPCInvocation(value) {
|
|
|
2889
2887
|
return isJSONRPCRequest(value) || isJSONRPCNotification(value);
|
|
2890
2888
|
}
|
|
2891
2889
|
/**
|
|
2892
|
-
*
|
|
2890
|
+
* Determines whether a parsed value is a {@link JSONRPCResultResponse} — the success
|
|
2893
2891
|
* arm of a response.
|
|
2894
2892
|
*
|
|
2895
2893
|
* @remarks
|
|
@@ -2898,7 +2896,7 @@ function isJSONRPCInvocation(value) {
|
|
|
2898
2896
|
* which is what makes this guard and {@link isJSONRPCErrorResponse} mutually
|
|
2899
2897
|
* exclusive on every input. `result` itself must be an object: either a modern
|
|
2900
2898
|
* {@link isMCPResult} or a legacy {@link isMCPLegacyResult}, never a bare primitive.
|
|
2901
|
-
* Total
|
|
2899
|
+
* Total.
|
|
2902
2900
|
*
|
|
2903
2901
|
* @param value - The already-parsed value to test
|
|
2904
2902
|
* @returns `true` when `value` is a valid JSON-RPC result response
|
|
@@ -2919,22 +2917,22 @@ function isJSONRPCResultResponse(value) {
|
|
|
2919
2917
|
return isMCPResult(result) || isMCPLegacyResult(result);
|
|
2920
2918
|
}
|
|
2921
2919
|
/**
|
|
2922
|
-
*
|
|
2920
|
+
* Determines whether a value is one JSON-RPC `error` member.
|
|
2923
2921
|
*
|
|
2924
2922
|
* @remarks
|
|
2925
2923
|
* The failure OBJECT, not the envelope carrying it — the shape a failed response owns
|
|
2926
2924
|
* under `error`, and the shape a `failed` {@link MCPTaskDetail} owns under the same name,
|
|
2927
|
-
* which is why it is one guard rather than the same
|
|
2925
|
+
* which is why it is one guard rather than the same checks written twice.
|
|
2928
2926
|
*
|
|
2929
2927
|
* It is deliberately STRUCTURAL rather than exact-JSON: `data` is declared `unknown`, so
|
|
2930
2928
|
* requiring the whole object to survive a JSON clone would refuse a legal error that
|
|
2931
2929
|
* carried a non-JSON payload. Both callers here hand it an already-owned value.
|
|
2932
2930
|
*
|
|
2933
|
-
* That choice is why the
|
|
2931
|
+
* That choice is why the key reads are guarded. Every sibling guard clones first, and a
|
|
2934
2932
|
* clone reads each key once behind a boundary that already owns totality; this one is the
|
|
2935
2933
|
* family's only DIRECT reader, so it meets `code` and `message` exactly as the value defines
|
|
2936
2934
|
* them — including as accessors that throw. Reading a named key off an unowned value is
|
|
2937
|
-
* itself the hostile step, and it is bounded here rather than allowed to escape. Total
|
|
2935
|
+
* itself the hostile step, and it is bounded here rather than allowed to escape. Total.
|
|
2938
2936
|
*
|
|
2939
2937
|
* @param value - The already-parsed value to test
|
|
2940
2938
|
* @returns `true` when `value` carries an integer `code` and a string `message`
|
|
@@ -2951,14 +2949,14 @@ function isJSONRPCError(value) {
|
|
|
2951
2949
|
return read.success && read.value;
|
|
2952
2950
|
}
|
|
2953
2951
|
/**
|
|
2954
|
-
*
|
|
2952
|
+
* Determines whether a parsed value is a {@link JSONRPCErrorResponse} — the failure
|
|
2955
2953
|
* arm of a response.
|
|
2956
2954
|
*
|
|
2957
2955
|
* @remarks
|
|
2958
2956
|
* `id` is OPTIONAL here and only here: a peer that could not read the failed
|
|
2959
2957
|
* request's id OMITS the member rather than sending `null`, so an absent `id` is
|
|
2960
2958
|
* valid and a `null` one is not. The envelope must own an `error` and must NOT own a
|
|
2961
|
-
* `result`. `error` carries an integer `code` and a string `message`. Total
|
|
2959
|
+
* `result`. `error` carries an integer `code` and a string `message`. Total.
|
|
2962
2960
|
*
|
|
2963
2961
|
* @param value - The already-parsed value to test
|
|
2964
2962
|
* @returns `true` when `value` is a valid JSON-RPC error response
|
|
@@ -2979,10 +2977,10 @@ function isJSONRPCErrorResponse(value) {
|
|
|
2979
2977
|
return isJSONRPCError(response["error"]);
|
|
2980
2978
|
}
|
|
2981
2979
|
/**
|
|
2982
|
-
*
|
|
2980
|
+
* Determines whether a parsed value is a {@link JSONRPCResponse}.
|
|
2983
2981
|
*
|
|
2984
2982
|
* @remarks
|
|
2985
|
-
* The union of the
|
|
2983
|
+
* The union of the mutually exclusive arms. Total.
|
|
2986
2984
|
*
|
|
2987
2985
|
* @param value - The already-parsed value to test
|
|
2988
2986
|
* @returns `true` when `value` is a valid JSON-RPC response
|
|
@@ -2991,11 +2989,11 @@ function isJSONRPCResponse(value) {
|
|
|
2991
2989
|
return isJSONRPCResultResponse(value) || isJSONRPCErrorResponse(value);
|
|
2992
2990
|
}
|
|
2993
2991
|
/**
|
|
2994
|
-
*
|
|
2992
|
+
* Determines whether a parsed value is a {@link JSONRPCMessage} — an invocation or a
|
|
2995
2993
|
* response.
|
|
2996
2994
|
*
|
|
2997
2995
|
* @remarks
|
|
2998
|
-
* The union of {@link isJSONRPCInvocation} and {@link isJSONRPCResponse}. Total
|
|
2996
|
+
* The union of {@link isJSONRPCInvocation} and {@link isJSONRPCResponse}. Total.
|
|
2999
2997
|
*
|
|
3000
2998
|
* @param value - The already-parsed value to test
|
|
3001
2999
|
* @returns `true` when `value` is a valid JSON-RPC message
|
|
@@ -3004,7 +3002,7 @@ function isJSONRPCMessage(value) {
|
|
|
3004
3002
|
return isJSONRPCInvocation(value) || isJSONRPCResponse(value);
|
|
3005
3003
|
}
|
|
3006
3004
|
/**
|
|
3007
|
-
*
|
|
3005
|
+
* Determines whether a parsed value is an MCP `initialize` invocation.
|
|
3008
3006
|
*
|
|
3009
3007
|
* @param value - The already-parsed value to test
|
|
3010
3008
|
* @returns `true` when `value` is a valid `initialize` request or notification
|
|
@@ -3020,7 +3018,7 @@ function isInitializeRequest(value) {
|
|
|
3020
3018
|
return owned.success && isJSONRPCInvocation(owned.value) && owned.value["method"] === "initialize";
|
|
3021
3019
|
}
|
|
3022
3020
|
/**
|
|
3023
|
-
*
|
|
3021
|
+
* Determines whether a JSON-RPC invocation uses the modern per-request MCP wire shape.
|
|
3024
3022
|
*
|
|
3025
3023
|
* @remarks
|
|
3026
3024
|
* Presence routes and validity answers: this guard checks only that
|
|
@@ -3111,7 +3109,7 @@ var MCPProgressReporter = class {
|
|
|
3111
3109
|
#last;
|
|
3112
3110
|
#stopped = false;
|
|
3113
3111
|
/**
|
|
3114
|
-
*
|
|
3112
|
+
* Creates one non-durable progress slot for an active request.
|
|
3115
3113
|
*
|
|
3116
3114
|
* @param token - The opaque progress token copied into each notification
|
|
3117
3115
|
* @param limits - The byte, key, and depth bounds applied to every progress payload
|
|
@@ -3126,7 +3124,7 @@ var MCPProgressReporter = class {
|
|
|
3126
3124
|
else signal.addEventListener("abort", this.#listener, { once: true });
|
|
3127
3125
|
}
|
|
3128
3126
|
/**
|
|
3129
|
-
*
|
|
3127
|
+
* Publishes one bounded, strictly increasing progress value and awaits its consumption.
|
|
3130
3128
|
*
|
|
3131
3129
|
* @param progress - The progress payload to own and hand to the serial consumer
|
|
3132
3130
|
* @returns A promise resolving after {@link take} consumes the item
|
|
@@ -3147,7 +3145,7 @@ var MCPProgressReporter = class {
|
|
|
3147
3145
|
await consumed.promise;
|
|
3148
3146
|
}
|
|
3149
3147
|
/**
|
|
3150
|
-
*
|
|
3148
|
+
* Takes the next progress notification, waiting for the single producer slot when empty.
|
|
3151
3149
|
*
|
|
3152
3150
|
* @returns The official progress notification carrying the original token
|
|
3153
3151
|
* @throws When the reporter stops or another consumer already has a pending take
|
|
@@ -3171,7 +3169,7 @@ var MCPProgressReporter = class {
|
|
|
3171
3169
|
return buildProgressNotification(this.#token, progress);
|
|
3172
3170
|
}
|
|
3173
3171
|
/**
|
|
3174
|
-
*
|
|
3172
|
+
* Stops the reporter permanently, rejects pending work, and detaches its abort listener.
|
|
3175
3173
|
*
|
|
3176
3174
|
* Repeated calls are idempotent. No queued or replayable progress survives the first call.
|
|
3177
3175
|
*
|
|
@@ -3213,10 +3211,10 @@ var MCPProgressReporter = class {
|
|
|
3213
3211
|
* cleanup to the producer — so a cooperating producer is woken rather than waited on —
|
|
3214
3212
|
* contains every promise the producer settles late, and makes every closure path idempotent.
|
|
3215
3213
|
*
|
|
3216
|
-
* The
|
|
3214
|
+
* The closures are deliberately different answers: the source's own return is the
|
|
3217
3215
|
* terminal RESPONSE, `return(value)` is the consumer saying it has the answer already, and
|
|
3218
|
-
* {@link stop} is an owner saying there will be no answer at all. Only the
|
|
3219
|
-
* message a peer ever sees.
|
|
3216
|
+
* {@link stop} is an owner saying there will be no answer at all. Only the source's own
|
|
3217
|
+
* return is a message a peer ever sees.
|
|
3220
3218
|
*
|
|
3221
3219
|
* A producer's own resource cleanup remains the producer's: JavaScript cannot settle work a
|
|
3222
3220
|
* generator is suspended inside, so the signal is how an uncooperative producer is asked to
|
|
@@ -3249,7 +3247,7 @@ var MCPStreamController = class {
|
|
|
3249
3247
|
#error;
|
|
3250
3248
|
#closed = false;
|
|
3251
3249
|
/**
|
|
3252
|
-
*
|
|
3250
|
+
* Controls one produced stream for the lifetime of one request.
|
|
3253
3251
|
*
|
|
3254
3252
|
* @param source - The produced held-open result whose cancellation this controller owns
|
|
3255
3253
|
* @param signal - The request signal every closure settles against; its abort ends the exchange
|
|
@@ -3264,7 +3262,7 @@ var MCPStreamController = class {
|
|
|
3264
3262
|
else signal.addEventListener("abort", this.#listener, { once: true });
|
|
3265
3263
|
}
|
|
3266
3264
|
/**
|
|
3267
|
-
*
|
|
3265
|
+
* Reads the next message, or the terminating response that ends the exchange.
|
|
3268
3266
|
*
|
|
3269
3267
|
* @remarks
|
|
3270
3268
|
* At most one read is outstanding against the source at a time, and a rival read is
|
|
@@ -3299,7 +3297,7 @@ var MCPStreamController = class {
|
|
|
3299
3297
|
};
|
|
3300
3298
|
}
|
|
3301
3299
|
/**
|
|
3302
|
-
*
|
|
3300
|
+
* Ends the exchange because the consumer already has its answer.
|
|
3303
3301
|
*
|
|
3304
3302
|
* @param value - The terminal the consumer is ending on
|
|
3305
3303
|
* @returns That terminal as the iteration's `return`
|
|
@@ -3313,7 +3311,7 @@ var MCPStreamController = class {
|
|
|
3313
3311
|
};
|
|
3314
3312
|
}
|
|
3315
3313
|
/**
|
|
3316
|
-
*
|
|
3314
|
+
* Ends the exchange with a failure the consumer is raising.
|
|
3317
3315
|
*
|
|
3318
3316
|
* @param error - The failure to end the exchange with
|
|
3319
3317
|
* @returns Never — the returned promise always rejects
|
|
@@ -3324,7 +3322,7 @@ var MCPStreamController = class {
|
|
|
3324
3322
|
throw error;
|
|
3325
3323
|
}
|
|
3326
3324
|
/**
|
|
3327
|
-
*
|
|
3325
|
+
* Ends the exchange permanently, with no terminal response.
|
|
3328
3326
|
*
|
|
3329
3327
|
* @remarks
|
|
3330
3328
|
* Idempotent, and the operation an owner that is not the consumer uses: a transport whose
|
|
@@ -3339,7 +3337,7 @@ var MCPStreamController = class {
|
|
|
3339
3337
|
if (!this.#closed) this.#fail(this.#closure.signal.reason);
|
|
3340
3338
|
}
|
|
3341
3339
|
/**
|
|
3342
|
-
*
|
|
3340
|
+
* Ends the exchange when the scope that owns it exits.
|
|
3343
3341
|
*
|
|
3344
3342
|
* @remarks
|
|
3345
3343
|
* {@link stop} under the name a `finally` calls it by, so a pump discharges its ownership
|
|
@@ -3352,7 +3350,7 @@ var MCPStreamController = class {
|
|
|
3352
3350
|
this.stop();
|
|
3353
3351
|
}
|
|
3354
3352
|
/**
|
|
3355
|
-
*
|
|
3353
|
+
* Iterates this exchange — the controller is its own iterator.
|
|
3356
3354
|
*
|
|
3357
3355
|
* @returns This controller
|
|
3358
3356
|
*/
|
|
@@ -3441,7 +3439,7 @@ var MCPStreamController = class {
|
|
|
3441
3439
|
var MCPTextStreamController = class {
|
|
3442
3440
|
#stream;
|
|
3443
3441
|
/**
|
|
3444
|
-
*
|
|
3442
|
+
* Mirrors one controlled typed exchange as its serialized sequence.
|
|
3445
3443
|
*
|
|
3446
3444
|
* @param stream - The controlled typed stream this face serializes and delegates into
|
|
3447
3445
|
*/
|
|
@@ -3449,7 +3447,7 @@ var MCPTextStreamController = class {
|
|
|
3449
3447
|
this.#stream = stream;
|
|
3450
3448
|
}
|
|
3451
3449
|
/**
|
|
3452
|
-
*
|
|
3450
|
+
* Reads the next serialized message, or the serialized terminating response.
|
|
3453
3451
|
*
|
|
3454
3452
|
* @returns The next message as a string, or the terminal as the iteration's `return`
|
|
3455
3453
|
* @throws Whatever ended the typed exchange, unserialized — an abort is not a message
|
|
@@ -3466,7 +3464,7 @@ var MCPTextStreamController = class {
|
|
|
3466
3464
|
};
|
|
3467
3465
|
}
|
|
3468
3466
|
/**
|
|
3469
|
-
*
|
|
3467
|
+
* Ends the exchange because the consumer already has its answer.
|
|
3470
3468
|
*
|
|
3471
3469
|
* @remarks
|
|
3472
3470
|
* The typed exchange ends with no terminal, because a string is not a
|
|
@@ -3485,7 +3483,7 @@ var MCPTextStreamController = class {
|
|
|
3485
3483
|
};
|
|
3486
3484
|
}
|
|
3487
3485
|
/**
|
|
3488
|
-
*
|
|
3486
|
+
* Ends the exchange with a failure the consumer is raising.
|
|
3489
3487
|
*
|
|
3490
3488
|
* @param error - The failure to end the exchange with
|
|
3491
3489
|
* @returns Never — the returned promise always rejects
|
|
@@ -3496,7 +3494,7 @@ var MCPTextStreamController = class {
|
|
|
3496
3494
|
throw error;
|
|
3497
3495
|
}
|
|
3498
3496
|
/**
|
|
3499
|
-
*
|
|
3497
|
+
* Ends the typed exchange permanently, with no terminal response.
|
|
3500
3498
|
*
|
|
3501
3499
|
* @returns Nothing
|
|
3502
3500
|
*/
|
|
@@ -3504,7 +3502,7 @@ var MCPTextStreamController = class {
|
|
|
3504
3502
|
this.#stream.stop();
|
|
3505
3503
|
}
|
|
3506
3504
|
/**
|
|
3507
|
-
*
|
|
3505
|
+
* Ends the typed exchange when the scope that owns this face exits.
|
|
3508
3506
|
*
|
|
3509
3507
|
* @remarks
|
|
3510
3508
|
* Delegates downward exactly as {@link stop} does — disposing the serialized arm is
|
|
@@ -3516,7 +3514,7 @@ var MCPTextStreamController = class {
|
|
|
3516
3514
|
this.#stream.stop();
|
|
3517
3515
|
}
|
|
3518
3516
|
/**
|
|
3519
|
-
*
|
|
3517
|
+
* Iterates this exchange — the controller is its own iterator.
|
|
3520
3518
|
*
|
|
3521
3519
|
* @returns This controller
|
|
3522
3520
|
*/
|
|
@@ -3527,7 +3525,7 @@ var MCPTextStreamController = class {
|
|
|
3527
3525
|
//#endregion
|
|
3528
3526
|
//#region src/core/MCPLegacy.ts
|
|
3529
3527
|
/**
|
|
3530
|
-
*
|
|
3528
|
+
* Translates the fixed legacy method set onto one modern dispatcher.
|
|
3531
3529
|
*
|
|
3532
3530
|
* @remarks
|
|
3533
3531
|
* This decorator owns no execution engine or result normalizer. Modern invocations
|
|
@@ -3538,7 +3536,7 @@ var MCPTextStreamController = class {
|
|
|
3538
3536
|
var MCPLegacy = class {
|
|
3539
3537
|
#options;
|
|
3540
3538
|
/**
|
|
3541
|
-
*
|
|
3539
|
+
* Creates a legacy decorator.
|
|
3542
3540
|
*
|
|
3543
3541
|
* @param options - The sole dispatcher and legacy handshake identity
|
|
3544
3542
|
*/
|
|
@@ -3647,7 +3645,7 @@ var MCPLegacy = class {
|
|
|
3647
3645
|
* requests over a live {@link ToolManagerInterface}, with NO transport coupling.
|
|
3648
3646
|
*
|
|
3649
3647
|
* @remarks
|
|
3650
|
-
* -
|
|
3648
|
+
* - **`dispatch` and `handle`.** `dispatch(invocation)` runs an already-parsed invocation and
|
|
3651
3649
|
* resolves a {@link JSONRPCResponse} for a request — or `undefined` for a
|
|
3652
3650
|
* {@link JSONRPCNotification}, which carries no `id` and is answered by nothing.
|
|
3653
3651
|
* `handle(message)` is the string boundary: it
|
|
@@ -3663,8 +3661,8 @@ var MCPLegacy = class {
|
|
|
3663
3661
|
* every dispatch: the same path a later method or a consumer's own takes, with an
|
|
3664
3662
|
* unregistered method still answering `-32601`.
|
|
3665
3663
|
* - **Provider-agnostic.** Imports only core siblings — JSON-RPC + the tool registry,
|
|
3666
|
-
* no HTTP, no model. Wire fields are narrowed
|
|
3667
|
-
* - **Observable
|
|
3664
|
+
* no HTTP, no model. Wire fields are narrowed with the contract guards (no `as`).
|
|
3665
|
+
* - **Observable.** The owned `emitter` fires `request` at the top of every
|
|
3668
3666
|
* dispatch; the emitter isolates a listener throw and routes it to its `error` handler
|
|
3669
3667
|
* (the `error` option), so a listener throw can never escape the dispatch.
|
|
3670
3668
|
*
|
|
@@ -3785,6 +3783,11 @@ var MCPServer = class {
|
|
|
3785
3783
|
}
|
|
3786
3784
|
async #modern(request, options) {
|
|
3787
3785
|
const id = request.id;
|
|
3786
|
+
const method = request.method;
|
|
3787
|
+
if (!isModernRequest(request)) {
|
|
3788
|
+
if (this.#methods.method(method) === void 0) return buildJSONRPCError(id, JSONRPC_METHOD_NOT_FOUND, `Method not found: ${method}`);
|
|
3789
|
+
return buildJSONRPCError(id, JSONRPC_INVALID_PARAMS, "Invalid params: request declares no protocol version");
|
|
3790
|
+
}
|
|
3788
3791
|
const context = parseRequestContext(request, {
|
|
3789
3792
|
bytes: this.#limits.message,
|
|
3790
3793
|
depth: this.#limits.depth
|
|
@@ -4313,7 +4316,7 @@ var MCPServer = class {
|
|
|
4313
4316
|
//#endregion
|
|
4314
4317
|
//#region src/core/MCPTaskClient.ts
|
|
4315
4318
|
/**
|
|
4316
|
-
* The CLIENT half of the draft Tasks extension — the
|
|
4319
|
+
* The CLIENT half of the draft Tasks extension — the `tasks/*` methods over one
|
|
4317
4320
|
* correlated-request door, exposed as an {@link import('./types.js').MCPClientInterface}'s
|
|
4318
4321
|
* `tasks`.
|
|
4319
4322
|
*
|
|
@@ -4333,7 +4336,7 @@ var MCPServer = class {
|
|
|
4333
4336
|
* client's id space, pending table, deadline policy, and `disconnect` drain rather than
|
|
4334
4337
|
* opening a second path to the same peer.
|
|
4335
4338
|
* - **The peer authorizes.** Nothing is checked locally. A server that never configured the
|
|
4336
|
-
* extension answers
|
|
4339
|
+
* extension answers each of them `-32601`, and a task that never existed, one whose TTL purged
|
|
4337
4340
|
* it, and one belonging to another principal are all the same `-32602` — a refusal this
|
|
4338
4341
|
* client passes on rather than resolving into a distinction the peer refused to publish.
|
|
4339
4342
|
*
|
|
@@ -4410,16 +4413,16 @@ var MCPTaskClient = class {
|
|
|
4410
4413
|
* exit — the deadline, an abort, a rejecting `send`, the answer itself, and the
|
|
4411
4414
|
* teardown's drain — without any of those paths knowing they exist.
|
|
4412
4415
|
* - **Per-request deadline.** An ordinary `#request` carries `this.#timeout`. The initial
|
|
4413
|
-
* discovery probe uses that same
|
|
4414
|
-
*
|
|
4416
|
+
* discovery probe uses that same configured deadline, so a silent peer cannot hold
|
|
4417
|
+
* negotiation indefinitely.
|
|
4415
4418
|
* `AbortSignal.timeout` (never a raw `setTimeout`) rejects only that pending request, and the
|
|
4416
4419
|
* same deadline bounds the WAIT on the transport's `close`, the one wait no drain and no signal
|
|
4417
4420
|
* can reach. It bounds the wait rather than the close, which keeps running, so a retry joins it
|
|
4418
4421
|
* instead of shutting one connection down twice.
|
|
4419
4422
|
* - **Transport-agnostic.** Imports only core siblings (JSON-RPC + the tool vocabulary);
|
|
4420
|
-
* the concrete transport is injected. Wire fields are narrowed
|
|
4423
|
+
* the concrete transport is injected. Wire fields are narrowed with the contract
|
|
4421
4424
|
* guards (no `as`).
|
|
4422
|
-
* - **Observable
|
|
4425
|
+
* - **Observable.** The owned `emitter` fires `connect` / `disconnect` /
|
|
4423
4426
|
* `notification` / `error`; the emitter isolates a listener throw and routes it to its
|
|
4424
4427
|
* `error` handler (the `error` option), so a listener throw can never escape.
|
|
4425
4428
|
*
|
|
@@ -4440,7 +4443,6 @@ var MCPClient = class {
|
|
|
4440
4443
|
#capabilities;
|
|
4441
4444
|
#pin;
|
|
4442
4445
|
#timeout;
|
|
4443
|
-
#probe;
|
|
4444
4446
|
#tasks;
|
|
4445
4447
|
#pending = /* @__PURE__ */ new Map();
|
|
4446
4448
|
#nextId = 0;
|
|
@@ -4455,6 +4457,11 @@ var MCPClient = class {
|
|
|
4455
4457
|
#era = void 0;
|
|
4456
4458
|
#offer;
|
|
4457
4459
|
constructor(options) {
|
|
4460
|
+
const requested = options.version;
|
|
4461
|
+
if (requested !== void 0 && !isMCPVersion(requested)) throw new MCPError("Unsupported protocol version", MCP_UNSUPPORTED_VERSION, {
|
|
4462
|
+
supported: SUPPORTED_PROTOCOL_VERSIONS,
|
|
4463
|
+
requested
|
|
4464
|
+
});
|
|
4458
4465
|
this.#emitter = new Emitter({
|
|
4459
4466
|
...options.on !== void 0 ? { on: options.on } : {},
|
|
4460
4467
|
...options.error !== void 0 ? { error: options.error } : {}
|
|
@@ -4465,10 +4472,9 @@ var MCPClient = class {
|
|
|
4465
4472
|
version: "1.0.0"
|
|
4466
4473
|
};
|
|
4467
4474
|
this.#capabilities = options.capabilities ?? {};
|
|
4468
|
-
this.#pin =
|
|
4469
|
-
this.#offer =
|
|
4475
|
+
this.#pin = requested;
|
|
4476
|
+
this.#offer = requested ?? "2026-07-28";
|
|
4470
4477
|
this.#timeout = options.timeout ?? 3e4;
|
|
4471
|
-
this.#probe = options.timeout === void 0 ? DEFAULT_MCP_REQUEST_TIMEOUT : Math.min(options.timeout, 50);
|
|
4472
4478
|
this.#tasks = new MCPTaskClient({
|
|
4473
4479
|
request: this.#request.bind(this),
|
|
4474
4480
|
timeout: this.#timeout
|
|
@@ -4490,9 +4496,6 @@ var MCPClient = class {
|
|
|
4490
4496
|
get tasks() {
|
|
4491
4497
|
return this.#tasks;
|
|
4492
4498
|
}
|
|
4493
|
-
on(event, handler) {
|
|
4494
|
-
this.#emitter.on(event, handler);
|
|
4495
|
-
}
|
|
4496
4499
|
async connect() {
|
|
4497
4500
|
for (;;) {
|
|
4498
4501
|
const closing = this.#disconnecting;
|
|
@@ -4525,7 +4528,7 @@ var MCPClient = class {
|
|
|
4525
4528
|
}
|
|
4526
4529
|
}
|
|
4527
4530
|
async discover() {
|
|
4528
|
-
const received = await this.#request("server/discover", void 0, this.#
|
|
4531
|
+
const received = await this.#request("server/discover", void 0, this.#timeout, this.#version ?? this.#offer);
|
|
4529
4532
|
const owned = attempt(() => cloneJSONRecord(received));
|
|
4530
4533
|
if (!owned.success) throw new MCPError("MCP server returned a malformed discovery result", JSONRPC_INVALID_PARAMS);
|
|
4531
4534
|
const result = owned.value;
|
|
@@ -4735,7 +4738,13 @@ var MCPClient = class {
|
|
|
4735
4738
|
await this.#initialize(generation, MCP_PROTOCOL_VERSION);
|
|
4736
4739
|
return;
|
|
4737
4740
|
}
|
|
4738
|
-
|
|
4741
|
+
let version;
|
|
4742
|
+
if (this.#pin === void 0) version = inferVersion(discovery.supportedVersions);
|
|
4743
|
+
else if (discovery.supportedVersions.includes(this.#pin)) version = this.#pin;
|
|
4744
|
+
else throw new MCPError("MCP server does not support the pinned protocol version", MCP_UNSUPPORTED_VERSION, {
|
|
4745
|
+
supported: discovery.supportedVersions,
|
|
4746
|
+
requested: this.#pin
|
|
4747
|
+
});
|
|
4739
4748
|
if (version === void 0) throw new MCPError("MCP server supports no compatible protocol version", MCP_UNSUPPORTED_VERSION, { supported: discovery.supportedVersions });
|
|
4740
4749
|
if (generation !== this.#generation) throw new Error("MCP client disconnected");
|
|
4741
4750
|
this.#version = version;
|
|
@@ -4811,6 +4820,10 @@ var MCPClient = class {
|
|
|
4811
4820
|
if (protocol === void 0) throw new Error("MCP server returned no protocol version");
|
|
4812
4821
|
if (!isString(protocol)) throw new Error("MCP server returned a malformed protocol version");
|
|
4813
4822
|
if (!isMCPVersion(protocol) || inferEra(protocol) !== "legacy") throw new Error(`MCP server negotiated unsupported protocol version '${protocol}'`);
|
|
4823
|
+
if (this.#pin !== void 0 && protocol !== this.#pin) throw new MCPError("MCP server negotiated a different protocol version than the client pinned", MCP_UNSUPPORTED_VERSION, {
|
|
4824
|
+
requested: this.#pin,
|
|
4825
|
+
negotiated: protocol
|
|
4826
|
+
});
|
|
4814
4827
|
if (generation !== this.#generation) throw new Error("MCP client disconnected");
|
|
4815
4828
|
await Promise.race([this.#transport.send({
|
|
4816
4829
|
jsonrpc: "2.0",
|
|
@@ -4844,7 +4857,7 @@ var MCPClient = class {
|
|
|
4844
4857
|
//#endregion
|
|
4845
4858
|
//#region src/core/factories.ts
|
|
4846
4859
|
/**
|
|
4847
|
-
*
|
|
4860
|
+
* Creates a transport-agnostic Model Context Protocol server — exposes a live
|
|
4848
4861
|
* {@link import('@orkestrel/tool').ToolManagerInterface} and an optional
|
|
4849
4862
|
* {@link import('./types.js').MCPResourceManagerInterface},
|
|
4850
4863
|
* {@link import('./types.js').MCPPromptManagerInterface}, and
|
|
@@ -4857,7 +4870,7 @@ var MCPClient = class {
|
|
|
4857
4870
|
* and no model. The {@link import('@orkestrel/tool').ToolManagerInterface} already
|
|
4858
4871
|
* isolates a thrown tool into a `success: false` result (surfaced as an MCP
|
|
4859
4872
|
* `isError: true` tool result), so a misbehaving tool never crashes a dispatch. Subscribe to the
|
|
4860
|
-
* `request` event
|
|
4873
|
+
* `request` event through `server.emitter.on('request', …)` for tracing.
|
|
4861
4874
|
*
|
|
4862
4875
|
* @param options - `identity` (the server identity), `tools` (the live tool
|
|
4863
4876
|
* registry), optional `resources` (the consumer-owned resource registry), optional
|
|
@@ -4869,7 +4882,8 @@ var MCPClient = class {
|
|
|
4869
4882
|
*
|
|
4870
4883
|
* @example
|
|
4871
4884
|
* ```ts
|
|
4872
|
-
* import { createMCPServer
|
|
4885
|
+
* import { createMCPServer } from '@orkestrel/mcp'
|
|
4886
|
+
* import { createTool, createToolManager } from '@orkestrel/tool'
|
|
4873
4887
|
*
|
|
4874
4888
|
* const tools = createToolManager()
|
|
4875
4889
|
* tools.add(createTool({ name: 'add', execute: (a) => Number(a.x) + Number(a.y) }))
|
|
@@ -4886,7 +4900,7 @@ function createMCPServer(options) {
|
|
|
4886
4900
|
return new MCPServer(options);
|
|
4887
4901
|
}
|
|
4888
4902
|
/**
|
|
4889
|
-
*
|
|
4903
|
+
* Decorates one MCP server with the fixed legacy method translation.
|
|
4890
4904
|
*
|
|
4891
4905
|
* @param server - The sole modern dispatcher and handshake identity source
|
|
4892
4906
|
* @returns A dispatcher accepting both modern and legacy invocations
|
|
@@ -4898,7 +4912,7 @@ function createMCPLegacy(server) {
|
|
|
4898
4912
|
});
|
|
4899
4913
|
}
|
|
4900
4914
|
/**
|
|
4901
|
-
*
|
|
4915
|
+
* Creates a transport-agnostic Model Context Protocol CLIENT — connects to a REMOTE
|
|
4902
4916
|
* MCP server over an injected {@link import('./types.js').MCPClientTransportInterface},
|
|
4903
4917
|
* runs the `initialize` handshake, and exposes the server's tools as local
|
|
4904
4918
|
* {@link import('@orkestrel/tool').ToolInterface}s an agent can run.
|
|
@@ -4912,8 +4926,7 @@ function createMCPLegacy(server) {
|
|
|
4912
4926
|
* locally, so an agent's {@link import('@orkestrel/tool').ToolManagerInterface}
|
|
4913
4927
|
* isolates it). The transport is injected — a concrete one (the HTTP transport over
|
|
4914
4928
|
* `fetch`) lives in the published server environment; the client itself is provider-agnostic. Subscribe
|
|
4915
|
-
* to `connect` / `disconnect` / `notification`
|
|
4916
|
-
* `client.emitter.on(...)`).
|
|
4929
|
+
* to `connect` / `disconnect` / `notification` through `client.emitter.on(...)`.
|
|
4917
4930
|
*
|
|
4918
4931
|
* @param options - `transport` (the carrier; REQUIRED), an optional `identity`
|
|
4919
4932
|
* (the client identity), `timeout` (the per-request deadline), and the reserved `on`
|
|
@@ -4922,7 +4935,7 @@ function createMCPLegacy(server) {
|
|
|
4922
4935
|
*
|
|
4923
4936
|
* @example
|
|
4924
4937
|
* ```ts
|
|
4925
|
-
* import { createMCPClient } from '@
|
|
4938
|
+
* import { createMCPClient } from '@orkestrel/mcp'
|
|
4926
4939
|
* import { createHTTPClientTransport } from '@orkestrel/mcp/server'
|
|
4927
4940
|
*
|
|
4928
4941
|
* const client = createMCPClient({
|
|
@@ -4937,7 +4950,7 @@ function createMCPClient(options) {
|
|
|
4937
4950
|
return new MCPClient(options);
|
|
4938
4951
|
}
|
|
4939
4952
|
/**
|
|
4940
|
-
*
|
|
4953
|
+
* Adapts an {@link MCPTransportInterface} (the environment-agnostic duplex message
|
|
4941
4954
|
* channel) into a {@link MCPClientTransportInterface} — the additive bridge that lets
|
|
4942
4955
|
* `createMCPClient` run over the new port without any change to `MCPClient`'s
|
|
4943
4956
|
* existing shape.
|
|
@@ -4945,7 +4958,7 @@ function createMCPClient(options) {
|
|
|
4945
4958
|
* @remarks
|
|
4946
4959
|
* Hand the RESULT to `createMCPClient({ transport })`, then pass the SAME
|
|
4947
4960
|
* `transport` to {@link import('./helpers.js').bindClient} to complete the inbound
|
|
4948
|
-
* wiring: `send` serializes each outbound {@link JSONRPCMessage} and writes it
|
|
4961
|
+
* wiring: `send` serializes each outbound {@link JSONRPCMessage} and writes it through
|
|
4949
4962
|
* `transport.send`; `close` closes the underlying
|
|
4950
4963
|
* `transport`; `start` is a no-op (the duplex channel is already open by the time
|
|
4951
4964
|
* it is handed in — there is no separate connect step at this layer); `session` is
|
|
@@ -4985,6 +4998,6 @@ function createDuplexClientTransport(transport) {
|
|
|
4985
4998
|
};
|
|
4986
4999
|
}
|
|
4987
5000
|
//#endregion
|
|
4988
|
-
export { DEFAULT_MCP_CACHE_TTL, DEFAULT_MCP_CLIENT_NAME, DEFAULT_MCP_CLIENT_VERSION, DEFAULT_MCP_LIMITS,
|
|
5001
|
+
export { DEFAULT_MCP_CACHE_TTL, DEFAULT_MCP_CLIENT_NAME, DEFAULT_MCP_CLIENT_VERSION, DEFAULT_MCP_LIMITS, DEFAULT_MCP_REQUEST_TIMEOUT, 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, MCPMethodManager, MCPProgressReporter, MCPServer, MCPStreamController, MCPTaskClient, MCPTextStreamController, MCP_EXTENSION_TASKS, MCP_HEADER_MISMATCH, MCP_LEGACY_VERSION, MCP_META_CAPABILITIES, MCP_META_CLIENT, MCP_META_SERVER, MCP_META_SUBSCRIPTION, MCP_META_VERSION, MCP_MISSING_CAPABILITY, MCP_MODERN_VERSION, MCP_PROTOCOL_VERSION, MCP_UNSUPPORTED_VERSION, SUPPORTED_PROTOCOL_VERSIONS, bindClient, bindServer, buildCallOutcome, buildCancelledNotification, buildDiscoverResult, buildInitializeResult, buildJSONRPCError, buildJSONRPCResult, buildMethodOptions, buildModernResult, buildProgressNotification, buildSubscriptionAcknowledgement, buildSubscriptionFilter, buildSubscriptionResult, buildToolCall, buildToolDescriptors, createDuplexClientTransport, createMCPClient, createMCPLegacy, createMCPServer, decodeBoundedMessage, digestJSON, extractContentText, inferEra, inferRequestVersion, inferVersion, isAbsoluteURI, isBoundedJSON, isBoundedString, isElicitContent, 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, isMCPIcon, isMCPIdentity, isMCPInputRequest, isMCPInputRequestMap, isMCPInputResult, isMCPLegacyResult, isMCPLoggingLevel, isMCPMetaKey, isMCPMetaObject, isMCPPaginationParams, isMCPProgress, isMCPPrompt, isMCPPromptArgument, isMCPPromptGetResult, isMCPPromptMessage, isMCPPromptPage, isMCPResource, isMCPResourceContents, isMCPResourcePage, isMCPResourceTemplate, isMCPResourceTemplatePage, isMCPResult, isMCPResultMetaObject, isMCPServerCapabilities, isMCPStringArguments, isMCPSubscriptionFilter, isMCPTaskDetail, isMCPTaskResult, isMCPTaskStatus, isMCPTextResource, isMCPVersion, isModernRequest, isRFC3339Date, isRFC3339DateTime, isStandardBase64, isTaskSupported, matchesResultType, matchesSubscriptionNotification, parseJSONRPCMessage, parseMCPInputState, parseRequestContext, readCancelledId, sendStream, serializeJSON, snapshotJSON, snapshotToolResult, stampSubscriptionNotification };
|
|
4989
5002
|
|
|
4990
5003
|
//# sourceMappingURL=index.js.map
|