drafter-mcp 0.26.1 → 0.26.2

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.
Files changed (2) hide show
  1. package/drafter-mcp.js +1252 -384
  2. package/package.json +1 -1
package/drafter-mcp.js CHANGED
@@ -7,7 +7,7 @@ import { join, posix, win32 } from "node:path";
7
7
  import { parseArgs } from "node:util";
8
8
  import { constants } from "node:fs";
9
9
  import { lstat, open, readdir, realpath, stat } from "node:fs/promises";
10
- // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
10
+ // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/chunk-vYduTEsO.mjs
11
11
  var __create = Object.create;
12
12
  var __defProp = Object.defineProperty;
13
13
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -38,7 +38,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
38
38
  value: mod,
39
39
  enumerable: true
40
40
  }) : target, mod));
41
- // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs
41
+ // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/dialects-CDpMbily.mjs
42
42
  /**
43
43
  * Canonical `$schema` URIs per supported dialect (http + https variants, trailing-`#` stripped).
44
44
  */
@@ -7426,6 +7426,7 @@ const ServerResultSchema = union([
7426
7426
  ListToolsResultSchema,
7427
7427
  SubscriptionsListenResultSchema
7428
7428
  ]);
7429
+ // ../../node_modules/.pnpm/@modelcontextprotocol+core@2.4.0/node_modules/@modelcontextprotocol/core/dist/auth-UMQCo-ad.mjs
7429
7430
  /**
7430
7431
  * Reusable URL validation that disallows `javascript:` scheme
7431
7432
  */
@@ -7628,7 +7629,7 @@ const OAuthTokenRevocationRequestSchema = object({
7628
7629
  token: string(),
7629
7630
  token_type_hint: string().optional()
7630
7631
  }).strip();
7631
- // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs
7632
+ // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/src-DdvbAHrR.mjs
7632
7633
  /**
7633
7634
  * Cross-bundle `instanceof` support for the SDK error classes.
7634
7635
  *
@@ -11889,25 +11890,22 @@ const X_MCP_HEADER_KEY = "x-mcp-header";
11889
11890
  */
11890
11891
  const RFC9110_TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
11891
11892
  /**
11892
- * JSON Schema `type` values the spec admits on an `x-mcp-header` property.
11893
- *
11894
- * The spec text names `integer`, `string`, `boolean` and explicitly excludes
11895
- * `number`. The published conformance referee at the pinned release ships its
11896
- * `http-custom-headers` scenario with two `type: "number"` `x-mcp-header`
11897
- * parameters and expects the client to mirror them, so `number` is accepted
11898
- * here so that the conformance gate passes; the discrepancy is tracked
11899
- * upstream. Everything else (`object`, `array`, `null`, absent) is rejected.
11893
+ * JSON Schema `type` values the spec admits on an `x-mcp-header` property:
11894
+ * `integer`, `string` and `boolean`. `number` is explicitly not permitted
11895
+ * (2026-07-28 Streamable HTTP, "Schema Extension"), so a tool that annotates a
11896
+ * `number`-typed property is invalid (the server still checks its headers).
11897
+ * Everything else (`object`, `array`, `null`, absent) is rejected too.
11900
11898
  */
11901
11899
  const PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
11902
11900
  "string",
11903
11901
  "integer",
11904
- "boolean",
11905
- "number"
11902
+ "boolean"
11906
11903
  ]);
11907
11904
  /**
11908
11905
  * Scan a tool's JSON-serialized `inputSchema` for `x-mcp-header` declarations
11909
11906
  * and validate every constraint the spec places on them. Returns either the
11910
- * collected declarations (possibly empty) or the first violated constraint.
11907
+ * collected declarations (possibly empty) or the first violated constraint
11908
+ * (with the declarations too, when `number`-typed declarations are the only fault).
11911
11909
  *
11912
11910
  * The walk descends through `properties` at any depth (the spec's "any nesting
11913
11911
  * depth" clause). The static-reachability MUST is enforced as a structural
@@ -11920,6 +11918,7 @@ const PERMITTED_X_MCP_HEADER_TYPES = /* @__PURE__ */ new Set([
11920
11918
  function scanXMcpHeaderDeclarations(inputSchema) {
11921
11919
  const declarations = [];
11922
11920
  const seenLower = /* @__PURE__ */ new Map();
11921
+ let numberFault;
11923
11922
  const visit = (node, path, reachable) => {
11924
11923
  if (node === null || typeof node !== "object") return void 0;
11925
11924
  const schema = node;
@@ -11929,7 +11928,11 @@ function scanXMcpHeaderDeclarations(inputSchema) {
11929
11928
  if (typeof raw !== "string" || raw.length === 0) return `${pathName(path)}: x-mcp-header MUST be a non-empty string`;
11930
11929
  if (!RFC9110_TOKEN.test(raw)) return `${pathName(path)}: x-mcp-header '${raw}' is not a valid RFC 9110 token (no spaces, control characters or HTTP delimiters)`;
11931
11930
  const type = typeof schema.type === "string" ? schema.type : void 0;
11932
- if (type === void 0 || !PERMITTED_X_MCP_HEADER_TYPES.has(type)) return `${pathName(path)}: x-mcp-header is only permitted on primitive-typed properties (string, integer, boolean); got ${type ?? "<none>"}`;
11931
+ if (type === void 0 || !PERMITTED_X_MCP_HEADER_TYPES.has(type)) {
11932
+ const fault$1 = `${pathName(path)}: x-mcp-header is only permitted on primitive-typed properties (string, integer, boolean); got ${type ?? "<none>"}`;
11933
+ if (type !== "number") return fault$1;
11934
+ numberFault ??= fault$1;
11935
+ }
11933
11936
  const lower = raw.toLowerCase();
11934
11937
  const prior = seenLower.get(lower);
11935
11938
  if (prior !== void 0) return `x-mcp-header '${raw}' is not case-insensitively unique (also declared as '${prior}')`;
@@ -11956,12 +11959,17 @@ function scanXMcpHeaderDeclarations(inputSchema) {
11956
11959
  }
11957
11960
  };
11958
11961
  const fault = visit(inputSchema, [], true);
11959
- return fault === void 0 ? {
11962
+ if (fault !== void 0) return {
11963
+ valid: false,
11964
+ reason: numberFault ?? fault
11965
+ };
11966
+ return numberFault === void 0 ? {
11960
11967
  valid: true,
11961
11968
  declarations
11962
11969
  } : {
11963
11970
  valid: false,
11964
- reason: fault
11971
+ reason: numberFault,
11972
+ declarations
11965
11973
  };
11966
11974
  }
11967
11975
  /**
@@ -12003,8 +12011,115 @@ const OBJECT_VALUED_SUBSCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
12003
12011
  function pathName(path) {
12004
12012
  return path.length === 0 ? "<root>" : path.join(".");
12005
12013
  }
12014
+ /**
12015
+ * Inbound HTTP request classification and the inbound validation ladder
12016
+ * (protocol revision 2026-07-28).
12017
+ *
12018
+ * `classifyInboundRequest` is the body-primary era predicate for an HTTP
12019
+ * entry that serves both protocol eras on one endpoint. It is evaluated
12020
+ * exactly once, at the entry boundary, on the already-parsed request body:
12021
+ *
12022
+ * - `initialize` is a legacy-era request by definition (the modern era has no
12023
+ * `initialize` handshake) — unless it carries a valid envelope claim naming
12024
+ * a modern revision, in which case the claim wins and the request is
12025
+ * classified like any other enveloped request (the modern era then answers
12026
+ * it with method-not-found, exactly like every other method it does not
12027
+ * define).
12028
+ * - A request whose `params._meta` carries the reserved protocol-version key
12029
+ * claims the per-request envelope mechanism and classifies into the era the
12030
+ * named revision belongs to (a malformed envelope behind a present claim is
12031
+ * a validation error, never a silent fall back to legacy handling).
12032
+ * - A request without a claim is legacy-era traffic.
12033
+ * - The `MCP-Protocol-Version` header is a cross-check only *for
12034
+ * classification*: it never upgrades or downgrades a body-derived
12035
+ * classification, and a disagreement between header and body is an explicit
12036
+ * ladder outcome. Its absence likewise never changes the era — but the spec
12037
+ * requires the header on every modern *request* POST, so a
12038
+ * modern-classified request that omits it is refused one rung later, by
12039
+ * {@linkcode validateStandardRequestHeaders}, not here. Notification POSTs
12040
+ * are exempt (see the next bullet), and that rung enforces presence on
12041
+ * requests only.
12042
+ * - Notifications carry no envelope claim of their own under the current
12043
+ * spec, so for notification POSTs without a body claim the modern header is
12044
+ * determinative; the `Mcp-Method` header is validated against the body when
12045
+ * the message classifies modern and is never enforced on legacy traffic.
12046
+ * A notification that does carry a claim is treated body-primary like a
12047
+ * request, and a malformed claim is rejected the same way a request's
12048
+ * malformed claim is — never silently resolved against the header.
12049
+ * The notification-POST header cross-checks here are an SDK-defensive
12050
+ * posture, not a spec requirement: the spec leaves header rules for posted
12051
+ * notifications undefined (core client notifications do not occur over
12052
+ * Streamable HTTP); applying the request rules symmetrically is what an
12053
+ * ecosystem custom-notification POST expects, and the −32020 cells stay
12054
+ * passing for them.
12055
+ * - `GET`/`DELETE` (and any other non-`POST` method) are body-less 2025-era
12056
+ * session operations: the modern era is `POST`-only, so they are routed to
12057
+ * legacy serving when it is configured and rejected otherwise.
12058
+ * - Array (batch) bodies are classified element-wise: an array containing a
12059
+ * modern-claiming or invalid element is rejected, an all-legacy array is
12060
+ * legacy traffic unchanged, and a single-element array is still an array.
12061
+ *
12062
+ * The classifier returns plain values (it never throws and never touches a
12063
+ * transport): a routing outcome (`legacy`/`modern`) or a ladder rejection
12064
+ * carrying the JSON-RPC error to emit and the HTTP status to emit it with.
12065
+ * Legacy routing outcomes deliberately carry NO `MessageClassification` —
12066
+ * legacy and hand-wired traffic is never classified, which keeps its
12067
+ * dispatch behavior byte-identical to today's.
12068
+ *
12069
+ * Error codes for the modern-path rejection cells follow the published
12070
+ * conformance suite (and the spec text it asserts):
12071
+ *
12072
+ * - A header/body cross-check mismatch (the `MCP-Protocol-Version` header
12073
+ * disagreeing with the body, or the `Mcp-Method` header disagreeing with the
12074
+ * body method) is rejected with `-32020` (`HeaderMismatch`) on HTTP 400.
12075
+ * - A request whose protocol-version header names a modern revision but whose
12076
+ * body carries no `_meta` envelope claim — including an envelope present but
12077
+ * missing the required protocol-version key — is rejected with `-32602`
12078
+ * (invalid params) naming the missing key(s), on HTTP 400.
12079
+ *
12080
+ * Should a future spec revision or conformance release change these
12081
+ * assignments, the affected cells are re-derived against that release; the
12082
+ * `settled` flag on {@linkcode InboundLadderRejection} stays available to mark
12083
+ * a cell provisional again while such a change is in flight.
12084
+ */
12085
+ /**
12086
+ * The error code emitted for header/body cross-check mismatches: the
12087
+ * `MCP-Protocol-Version` header disagreeing with the body's envelope claim (or
12088
+ * with the body's classification), and the `Mcp-Method` header disagreeing
12089
+ * with the body method.
12090
+ *
12091
+ * `-32020` is the draft schema's `HEADER_MISMATCH` constant (the SEP-2243
12092
+ * `HeaderMismatch` code; the spec requires HTTP 400 for it), as also asserted
12093
+ * by the published conformance suite for header-validation failures. It has no
12094
+ * {@linkcode ProtocolErrorCode} member because it is not part of the 2025-era
12095
+ * wire vocabulary; the validation ladder is its only emitter.
12096
+ */
12097
+ const HEADER_MISMATCH_ERROR_CODE = -32020;
12006
12098
  ProtocolErrorCode.InvalidRequest, ProtocolErrorCode.UnsupportedProtocolVersion, ProtocolErrorCode.InvalidParams, ProtocolErrorCode.MethodNotFound, ProtocolErrorCode.InvalidParams, ProtocolErrorCode.MissingRequiredClientCapability;
12007
- ProtocolErrorCode.ParseError, ProtocolErrorCode.InvalidRequest, ProtocolErrorCode.MethodNotFound, ProtocolErrorCode.UnsupportedProtocolVersion, ProtocolErrorCode.MissingRequiredClientCapability;
12099
+ /**
12100
+ * HTTP status for ladder-originated JSON-RPC error codes.
12101
+ *
12102
+ * Keyed on origin, not on the bare code: this table only applies to errors
12103
+ * the ladder (or a pre-handler protocol gate) produced. Errors produced by
12104
+ * request handlers — whatever their code — stay in-band on HTTP 200, and are
12105
+ * never mapped to an HTTP status by this table; in particular `-32603` and
12106
+ * domain-specific codes never become a blanket 500. The single exception is
12107
+ * `MissingRequiredClientCapability` (-32021) — see
12108
+ * {@linkcode httpStatusForErrorCode}.
12109
+ *
12110
+ * `-32602` (invalid params) deliberately has NO entry: the only invalid-params
12111
+ * rejection that maps to HTTP 400 is the classifier's own envelope rung
12112
+ * short-circuit, which carries its HTTP status directly. A dispatch- or
12113
+ * handler-produced invalid-params error is always in-band.
12114
+ */
12115
+ const LADDER_ERROR_HTTP_STATUS = {
12116
+ [ProtocolErrorCode.ParseError]: 400,
12117
+ [ProtocolErrorCode.InvalidRequest]: 400,
12118
+ [ProtocolErrorCode.MethodNotFound]: 404,
12119
+ [ProtocolErrorCode.UnsupportedProtocolVersion]: 400,
12120
+ [ProtocolErrorCode.MissingRequiredClientCapability]: 400,
12121
+ [HEADER_MISMATCH_ERROR_CODE]: 400
12122
+ };
12008
12123
  function rejection(rung, cell, httpStatus, error, settled) {
12009
12124
  return {
12010
12125
  kind: "reject",
@@ -12141,22 +12256,26 @@ function standardSchemaToJsonSchema(schema, io = "input") {
12141
12256
  ...result
12142
12257
  };
12143
12258
  }
12259
+ const LOCAL_DEFINITION_REF = /^#\/(\$defs|definitions)\/([^/~%]+)$/;
12144
12260
  /**
12145
12261
  * A typeless JSON Schema root is "provably object-shaped" when either it carries object keywords
12146
12262
  * directly (`properties`/`patternProperties`/`additionalProperties`/`required`), or it is a
12147
12263
  * composition (`oneOf`/`anyOf`/`allOf`) whose every member is itself `type:'object'` or recursively
12148
- * provably object-shaped (e.g. a nested `discriminatedUnion`). `$ref` is not followed. Used to
12264
+ * provably object-shaped (e.g. a nested `discriminatedUnion`). A `$ref` is followed one hop, to an
12265
+ * entry of the root's `$defs`/`definitions` that is itself `type:'object'`. Used to
12149
12266
  * decide whether stamping `type:'object'` is safe (redundant-but-valid) versus self-contradictory.
12150
12267
  */
12151
- function isProvablyObjectShapedRoot(schema) {
12268
+ function isProvablyObjectShapedRoot(schema, root = schema) {
12152
12269
  if ("properties" in schema || "patternProperties" in schema || "additionalProperties" in schema || "required" in schema) return true;
12270
+ const ref = typeof schema.$ref === "string" && LOCAL_DEFINITION_REF.exec(schema.$ref);
12271
+ if (ref && root[ref[1]]?.[ref[2]]?.type === "object") return true;
12153
12272
  for (const key of [
12154
12273
  "oneOf",
12155
12274
  "anyOf",
12156
12275
  "allOf"
12157
12276
  ]) {
12158
12277
  const members = schema[key];
12159
- if (Array.isArray(members) && members.length > 0) return members.every((m) => m !== null && typeof m === "object" && (m.type === "object" || isProvablyObjectShapedRoot(m)));
12278
+ if (Array.isArray(members) && members.length > 0) return members.every((m) => m !== null && typeof m === "object" && (m.type === "object" || isProvablyObjectShapedRoot(m, "$id" in m ? {} : root)));
12160
12279
  }
12161
12280
  return false;
12162
12281
  }
@@ -13821,7 +13940,7 @@ function normalizeRawShapeSchema(schema) {
13821
13940
  if (!isStandardSchema(schema)) throw new TypeError("inputSchema/outputSchema/argsSchema must be a Standard Schema (e.g. z.object({...})) or a raw Zod shape ({ field: z.string() }).");
13822
13941
  return schema;
13823
13942
  }
13824
- // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/ajvProvider-CEoC__sr.mjs
13943
+ // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/ajvProvider-DyjnscPj.mjs
13825
13944
  var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
13826
13945
  Object.defineProperty(exports, "__esModule", { value: true });
13827
13946
  exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = void 0;
@@ -16499,6 +16618,28 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16499
16618
  const isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu);
16500
16619
  /** @type {(value: string) => boolean} */
16501
16620
  const isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u);
16621
+ /** @type {(value: string) => boolean} */
16622
+ const isPort = RegExp.prototype.test.bind(/^\d*$/u);
16623
+ /** @type {(value: string) => boolean} */
16624
+ const isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
16625
+ /** @type {(value: string) => boolean} */
16626
+ const isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
16627
+ /** @type {(value: string) => boolean} */
16628
+ const isPathCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/]$/u);
16629
+ /** @type {(value: string) => boolean} */
16630
+ const isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:@/?]$/u);
16631
+ /** @type {(value: string) => boolean} */
16632
+ const isUserinfoCharacter = RegExp.prototype.test.bind(/^[A-Za-z0-9\-._~!$&'()*+,;=:]$/u);
16633
+ const BYTE_HEX = new Array(256);
16634
+ {
16635
+ const HEX_DIGITS = "0123456789ABCDEF";
16636
+ for (let i = 0; i < 256; i++) BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
16637
+ }
16638
+ function percentEncodeNonAscii(cp) {
16639
+ if (cp < 2048) return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
16640
+ if (cp < 65536) return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
16641
+ return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
16642
+ }
16502
16643
  /**
16503
16644
  * @param {Array<string>} input
16504
16645
  * @returns {string}
@@ -16521,119 +16662,162 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16521
16662
  }
16522
16663
  return acc;
16523
16664
  }
16524
- /**
16525
- * @typedef {Object} GetIPV6Result
16526
- * @property {boolean} error - Indicates if there was an error parsing the IPv6 address.
16527
- * @property {string} address - The parsed IPv6 address.
16528
- * @property {string} [zone] - The zone identifier, if present.
16529
- */
16665
+ /** @type {(value: string) => boolean} */
16666
+ const isHextet = RegExp.prototype.test.bind(/^[\dA-Fa-f]{1,4}$/);
16667
+ /** @type {(value: string) => boolean} */
16668
+ const isIPvFuture = RegExp.prototype.test.bind(/^[vV][\dA-Fa-f]+\.[A-Za-z\d\-._~!$&'()*+,;=:]+$/);
16669
+ /** @type {(value: string) => boolean} */
16670
+ const isZoneCharacter = RegExp.prototype.test.bind(/^[A-Za-z\d\-._~]$/);
16530
16671
  /**
16531
16672
  * @param {string} value
16532
16673
  * @returns {boolean}
16533
16674
  */
16534
16675
  const nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u);
16535
16676
  /**
16536
- * @param {Array<string>} buffer
16677
+ * @param {string} zone
16537
16678
  * @returns {boolean}
16538
16679
  */
16539
- function consumeIsZone(buffer) {
16540
- buffer.length = 0;
16680
+ function isZoneIdentifier(zone) {
16681
+ if (zone.length === 0) return false;
16682
+ for (let i = 0; i < zone.length; i++) {
16683
+ if (isZoneCharacter(zone[i])) continue;
16684
+ if (zone[i] === "%" && i + 2 < zone.length && isHexPair(zone.slice(i + 1, i + 3))) {
16685
+ i += 2;
16686
+ continue;
16687
+ }
16688
+ return false;
16689
+ }
16541
16690
  return true;
16542
16691
  }
16543
16692
  /**
16544
- * @param {Array<string>} buffer
16545
- * @param {Array<string>} address
16546
- * @param {GetIPV6Result} output
16547
- * @returns {boolean}
16693
+ * Compresses the longest run of zero hextets to "::" per RFC 5952. A run of a
16694
+ * single zero hextet is left uncompressed. On ties the leftmost run wins.
16695
+ *
16696
+ * @param {string[]} hextets
16697
+ * @returns {string}
16548
16698
  */
16549
- function consumeHextets(buffer, address, output) {
16550
- if (buffer.length) {
16551
- const hex = stringArrayToHexStripped(buffer);
16552
- if (hex !== "") address.push(hex);
16553
- else {
16554
- output.error = true;
16555
- return false;
16699
+ function compressIPv6ZeroRun(hextets) {
16700
+ let bestStart = -1;
16701
+ let bestLength = 0;
16702
+ let runStart = -1;
16703
+ let runLength = 0;
16704
+ for (let i = 0; i < hextets.length; i++) if (hextets[i] === "0") {
16705
+ if (runStart === -1) runStart = i;
16706
+ runLength++;
16707
+ if (runLength > bestLength) {
16708
+ bestLength = runLength;
16709
+ bestStart = runStart;
16556
16710
  }
16557
- buffer.length = 0;
16711
+ } else {
16712
+ runStart = -1;
16713
+ runLength = 0;
16558
16714
  }
16559
- return true;
16715
+ if (bestLength < 2) return hextets.join(":");
16716
+ const head = hextets.slice(0, bestStart).join(":");
16717
+ const tail = hextets.slice(bestStart + bestLength).join(":");
16718
+ return head + "::" + tail;
16560
16719
  }
16561
16720
  /**
16721
+ * Validates an IPv6 address against the alternatives in RFC 3986 section
16722
+ * 3.2.2 and returns the same address with leading hextet zeroes removed.
16723
+ * An embedded IPv4 address counts as two hextets and is only valid at the end.
16724
+ *
16562
16725
  * @param {string} input
16563
- * @returns {GetIPV6Result}
16564
- */
16565
- function getIPV6(input) {
16566
- let tokenCount = 0;
16567
- const output = {
16568
- error: false,
16569
- address: "",
16570
- zone: ""
16571
- };
16572
- /** @type {Array<string>} */
16573
- const address = [];
16574
- /** @type {Array<string>} */
16575
- const buffer = [];
16576
- let endipv6Encountered = false;
16577
- let endIpv6 = false;
16578
- let consume = consumeHextets;
16579
- for (let i = 0; i < input.length; i++) {
16580
- const cursor = input[i];
16581
- if (cursor === "[" || cursor === "]") continue;
16582
- if (cursor === ":") {
16583
- if (endipv6Encountered === true) endIpv6 = true;
16584
- if (!consume(buffer, address, output)) break;
16585
- if (++tokenCount > 7) {
16586
- output.error = true;
16587
- break;
16588
- }
16589
- if (i > 0 && input[i - 1] === ":") endipv6Encountered = true;
16590
- address.push(":");
16591
- continue;
16592
- } else if (cursor === "%") {
16593
- if (!consume(buffer, address, output)) break;
16594
- consume = consumeIsZone;
16595
- } else {
16596
- buffer.push(cursor);
16726
+ * @returns {string|undefined}
16727
+ */
16728
+ function normalizeIPv6Address(input) {
16729
+ const compression = input.indexOf("::");
16730
+ if (compression !== -1 && input.indexOf("::", compression + 1) !== -1) return void 0;
16731
+ const left = compression === -1 ? input.split(":") : input.slice(0, compression).split(":");
16732
+ const right = compression === -1 ? [] : input.slice(compression + 2).split(":");
16733
+ if (compression !== -1) {
16734
+ if (left.length === 1 && left[0] === "") left.length = 0;
16735
+ if (right.length === 1 && right[0] === "") right.length = 0;
16736
+ }
16737
+ const parts = left.concat(right);
16738
+ let hextetCount = 0;
16739
+ for (let i = 0; i < parts.length; i++) {
16740
+ const part = parts[i];
16741
+ if (part === "") return void 0;
16742
+ if (part.indexOf(".") !== -1) {
16743
+ if (i !== parts.length - 1 || compression !== -1 && right.length === 0 || !isIPv4(part)) return void 0;
16744
+ hextetCount += 2;
16597
16745
  continue;
16598
16746
  }
16747
+ if (!isHextet(part)) return void 0;
16748
+ parts[i] = parseInt(part, 16).toString(16);
16749
+ hextetCount++;
16599
16750
  }
16600
- if (buffer.length) if (consume === consumeIsZone) output.zone = buffer.join("");
16601
- else if (endIpv6) address.push(buffer.join(""));
16602
- else address.push(stringArrayToHexStripped(buffer));
16603
- output.address = address.join("");
16604
- return output;
16751
+ if (compression === -1) {
16752
+ if (hextetCount !== 8) return void 0;
16753
+ return compressIPv6ZeroRun(parts);
16754
+ }
16755
+ if (hextetCount >= 8) return void 0;
16756
+ const expanded = parts.slice(0, left.length);
16757
+ for (let i = hextetCount; i < 8; i++) expanded.push("0");
16758
+ for (let i = left.length; i < parts.length; i++) expanded.push(parts[i]);
16759
+ return compressIPv6ZeroRun(expanded);
16605
16760
  }
16606
16761
  /**
16607
16762
  * @typedef {Object} NormalizeIPv6Result
16608
16763
  * @property {string} host - The normalized host.
16609
16764
  * @property {string} [escapedHost] - The escaped host.
16610
16765
  * @property {boolean} isIPV6 - Indicates if the host is an IPv6 address.
16766
+ * @property {boolean} [isIPVFuture] - Indicates if the host is an IPvFuture literal.
16767
+ * @property {boolean} [error] - Indicates if a bracketed IP literal is malformed.
16611
16768
  */
16612
16769
  /**
16770
+ * Validates and normalizes a bracketed IP literal. Raw zone separators remain
16771
+ * accepted for backwards compatibility, while encoded separators and zone
16772
+ * contents follow RFC 6874.
16773
+ *
16613
16774
  * @param {string} host
16614
16775
  * @returns {NormalizeIPv6Result}
16615
16776
  */
16616
16777
  function normalizeIPv6(host) {
16617
- if (findToken(host, ":") < 2) return {
16778
+ const bracketed = host[0] === "[" && host[host.length - 1] === "]";
16779
+ if ((host[0] === "[" || host[host.length - 1] === "]") && !bracketed) return {
16618
16780
  host,
16619
- isIPV6: false
16781
+ isIPV6: false,
16782
+ error: true
16620
16783
  };
16621
- const ipv6 = getIPV6(host);
16622
- if (!ipv6.error) {
16623
- let newHost = ipv6.address;
16624
- let escapedHost = ipv6.address;
16625
- if (ipv6.zone) {
16626
- newHost += "%" + ipv6.zone;
16627
- escapedHost += "%25" + ipv6.zone;
16628
- }
16784
+ let input = bracketed ? host.slice(1, -1) : host;
16785
+ if (bracketed && isIPvFuture(input)) {
16786
+ input = input.toLowerCase();
16629
16787
  return {
16630
- host: newHost,
16631
- isIPV6: true,
16632
- escapedHost
16788
+ host: `[${input}]`,
16789
+ escapedHost: input,
16790
+ isIPV6: false,
16791
+ isIPVFuture: true
16792
+ };
16793
+ }
16794
+ if (findToken(input, ":") < 2) return {
16795
+ host,
16796
+ isIPV6: false,
16797
+ error: bracketed
16798
+ };
16799
+ let zoneIdentifier = "";
16800
+ const zoneSeparator = input.indexOf("%");
16801
+ if (zoneSeparator !== -1) {
16802
+ const separatorLength = input.slice(zoneSeparator, zoneSeparator + 3).toLowerCase() === "%25" ? 3 : 1;
16803
+ zoneIdentifier = input.slice(zoneSeparator + separatorLength);
16804
+ if (!isZoneIdentifier(zoneIdentifier)) return {
16805
+ host,
16806
+ isIPV6: false,
16807
+ error: true
16633
16808
  };
16634
- } else return {
16809
+ input = input.slice(0, zoneSeparator);
16810
+ }
16811
+ const address = normalizeIPv6Address(input);
16812
+ if (address === void 0) return {
16635
16813
  host,
16636
- isIPV6: false
16814
+ isIPV6: false,
16815
+ error: true
16816
+ };
16817
+ return {
16818
+ host: address + (zoneIdentifier ? "%" + zoneIdentifier : ""),
16819
+ escapedHost: address + (zoneIdentifier ? "%25" + zoneIdentifier : ""),
16820
+ isIPV6: true
16637
16821
  };
16638
16822
  }
16639
16823
  /**
@@ -16721,19 +16905,264 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16721
16905
  return output.join("");
16722
16906
  }
16723
16907
  /**
16724
- * @param {import('../types/index').URIComponent} component
16725
- * @param {boolean} esc
16726
- * @returns {import('../types/index').URIComponent}
16908
+ * Re-escape RFC 3986 gen-delims that must not appear literally in the host.
16909
+ * After the URI regex parses, these characters cannot be literal in the host
16910
+ * field, so any that appear after decoding came from percent-encoding and
16911
+ * must be restored to prevent authority structure changes.
16912
+ *
16913
+ * @param {string} host
16914
+ * @param {boolean} isIP - true for IPv4/IPv6 hosts (skip colon re-escaping)
16915
+ * @returns {string}
16727
16916
  */
16728
- function normalizeComponentEncoding(component, esc) {
16729
- const func = esc !== true ? escape : unescape;
16730
- if (component.scheme !== void 0) component.scheme = func(component.scheme);
16731
- if (component.userinfo !== void 0) component.userinfo = func(component.userinfo);
16732
- if (component.host !== void 0) component.host = func(component.host);
16733
- if (component.path !== void 0) component.path = func(component.path);
16734
- if (component.query !== void 0) component.query = func(component.query);
16735
- if (component.fragment !== void 0) component.fragment = func(component.fragment);
16736
- return component;
16917
+ const HOST_DELIMS = {
16918
+ "@": "%40",
16919
+ "/": "%2F",
16920
+ "?": "%3F",
16921
+ "#": "%23",
16922
+ ":": "%3A"
16923
+ };
16924
+ const HOST_DELIM_RE = /[@/?#:]/g;
16925
+ const HOST_DELIM_NO_COLON_RE = /[@/?#]/g;
16926
+ function reescapeHostDelimiters(host, isIP) {
16927
+ const re = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE;
16928
+ re.lastIndex = 0;
16929
+ return host.replace(re, (ch) => HOST_DELIMS[ch]);
16930
+ }
16931
+ /**
16932
+ * Normalizes percent escapes and optionally decodes only unreserved ASCII bytes.
16933
+ * Reserved delimiters such as `%2F` stay escaped; `%2E` is unreserved.
16934
+ *
16935
+ * @param {string} input
16936
+ * @param {boolean} [decodeUnreserved=false]
16937
+ * @returns {string}
16938
+ */
16939
+ function normalizePercentEncoding(input, decodeUnreserved = false) {
16940
+ if (input.indexOf("%") === -1) return input;
16941
+ let output = "";
16942
+ for (let i = 0; i < input.length; i++) {
16943
+ if (input[i] === "%" && i + 2 < input.length) {
16944
+ const hex = input.slice(i + 1, i + 3);
16945
+ if (isHexPair(hex)) {
16946
+ const normalizedHex = hex.toUpperCase();
16947
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
16948
+ if (decodeUnreserved && isUnreserved(decoded)) output += decoded;
16949
+ else output += "%" + normalizedHex;
16950
+ i += 2;
16951
+ continue;
16952
+ }
16953
+ }
16954
+ output += input[i];
16955
+ }
16956
+ return output;
16957
+ }
16958
+ /**
16959
+ * Normalizes path data without turning reserved escapes into live path syntax.
16960
+ * Valid escapes are uppercased, raw unsafe characters are escaped, and only
16961
+ * unreserved bytes that are not `.` are decoded.
16962
+ *
16963
+ * @param {string} input
16964
+ * @returns {string}
16965
+ */
16966
+ function normalizePathEncoding(input) {
16967
+ let output = "";
16968
+ for (let i = 0; i < input.length; i++) {
16969
+ const ch = input[i];
16970
+ if (ch === "%" && i + 2 < input.length) {
16971
+ const hex = input.slice(i + 1, i + 3);
16972
+ if (isHexPair(hex)) {
16973
+ const normalizedHex = hex.toUpperCase();
16974
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
16975
+ if (decoded !== "." && isUnreserved(decoded)) output += decoded;
16976
+ else output += "%" + normalizedHex;
16977
+ i += 2;
16978
+ continue;
16979
+ }
16980
+ }
16981
+ if (isPathCharacter(ch)) output += ch;
16982
+ else {
16983
+ const code = input.charCodeAt(i);
16984
+ if (code < 128) output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
16985
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
16986
+ else if (code <= 56319 && i + 1 < input.length) {
16987
+ const low = input.charCodeAt(i + 1);
16988
+ if (low >= 56320 && low <= 57343) {
16989
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
16990
+ i++;
16991
+ } else output += percentEncodeNonAscii(65533);
16992
+ } else output += percentEncodeNonAscii(65533);
16993
+ }
16994
+ }
16995
+ return output;
16996
+ }
16997
+ /**
16998
+ * Serializes a path without rewriting reserved data. Raw RFC 3986 path
16999
+ * characters remain literal, valid escapes are preserved and uppercased, and
17000
+ * everything else is UTF-8 percent-encoded. In a path-noscheme, a colon in the
17001
+ * first segment must be escaped so the result cannot be parsed as a scheme.
17002
+ *
17003
+ * @param {string} input
17004
+ * @param {boolean} [pathNoScheme=false]
17005
+ * @returns {string}
17006
+ */
17007
+ function serializePathEncoding(input, pathNoScheme = false) {
17008
+ let output = "";
17009
+ let firstSegment = pathNoScheme && input[0] !== "/";
17010
+ for (let i = 0; i < input.length; i++) {
17011
+ const ch = input[i];
17012
+ if (ch === "%" && i + 2 < input.length) {
17013
+ const hex = input.slice(i + 1, i + 3);
17014
+ if (isHexPair(hex)) {
17015
+ output += "%" + hex.toUpperCase();
17016
+ i += 2;
17017
+ continue;
17018
+ }
17019
+ }
17020
+ if (ch === "/") firstSegment = false;
17021
+ if (isPathCharacter(ch) && (ch !== ":" || !firstSegment)) output += ch;
17022
+ else {
17023
+ const code = input.charCodeAt(i);
17024
+ if (code < 128) output += BYTE_HEX[code];
17025
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
17026
+ else if (code <= 56319 && i + 1 < input.length) {
17027
+ const low = input.charCodeAt(i + 1);
17028
+ if (low >= 56320 && low <= 57343) {
17029
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
17030
+ i++;
17031
+ } else output += percentEncodeNonAscii(65533);
17032
+ } else output += percentEncodeNonAscii(65533);
17033
+ }
17034
+ }
17035
+ return output;
17036
+ }
17037
+ /**
17038
+ * Percent-encodes a URI component using its RFC 3986 literal character set.
17039
+ * Existing valid escapes are preserved and normalized to uppercase hex.
17040
+ *
17041
+ * @param {string} input
17042
+ * @param {(value: string) => boolean} isAllowed
17043
+ * @returns {string}
17044
+ */
17045
+ function encodeComponent(input, isAllowed) {
17046
+ let output = "";
17047
+ for (let i = 0; i < input.length; i++) {
17048
+ const ch = input[i];
17049
+ if (ch === "%" && i + 2 < input.length) {
17050
+ const hex = input.slice(i + 1, i + 3);
17051
+ if (isHexPair(hex)) {
17052
+ output += "%" + hex.toUpperCase();
17053
+ i += 2;
17054
+ continue;
17055
+ }
17056
+ }
17057
+ if (isAllowed(ch)) output += ch;
17058
+ else {
17059
+ const code = input.charCodeAt(i);
17060
+ if (code < 128) output += BYTE_HEX[code];
17061
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
17062
+ else if (code <= 56319 && i + 1 < input.length) {
17063
+ const low = input.charCodeAt(i + 1);
17064
+ if (low >= 56320 && low <= 57343) {
17065
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
17066
+ i++;
17067
+ } else output += percentEncodeNonAscii(65533);
17068
+ } else output += percentEncodeNonAscii(65533);
17069
+ }
17070
+ }
17071
+ return output;
17072
+ }
17073
+ /**
17074
+ * Encodes userinfo while preserving its RFC 3986 §3.2.1 literal characters.
17075
+ * In particular, authority delimiters such as `@`, `/`, `?`, and `#` are data.
17076
+ *
17077
+ * @param {string} input
17078
+ * @returns {string}
17079
+ */
17080
+ function encodeUserinfo(input) {
17081
+ return encodeComponent(input, isUserinfoCharacter);
17082
+ }
17083
+ /**
17084
+ * Encodes query data using the RFC 3986 §3.4 grammar. A literal `#` must be
17085
+ * escaped because it would otherwise begin the fragment component.
17086
+ *
17087
+ * @param {string} input
17088
+ * @returns {string}
17089
+ */
17090
+ function encodeQuery(input) {
17091
+ return encodeComponent(input, isQueryFragmentCharacter);
17092
+ }
17093
+ /**
17094
+ * Encodes fragment data using the RFC 3986 §3.5 grammar.
17095
+ *
17096
+ * @param {string} input
17097
+ * @returns {string}
17098
+ */
17099
+ function encodeFragment(input) {
17100
+ return encodeComponent(input, isQueryFragmentCharacter);
17101
+ }
17102
+ function isEscapeSafe(cp) {
17103
+ return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
17104
+ }
17105
+ /**
17106
+ * Normalizes the percent-encoding of a query or fragment component.
17107
+ *
17108
+ * Like `normalizePathEncoding`, but uses the query/fragment character set
17109
+ * (which additionally allows `?`) and decodes `.` since it has no dot-segment
17110
+ * meaning outside of a path.
17111
+ *
17112
+ * @param {string} input
17113
+ * @returns {string}
17114
+ */
17115
+ function normalizeQueryFragmentEncoding(input) {
17116
+ let output = "";
17117
+ for (let i = 0; i < input.length; i++) {
17118
+ const ch = input[i];
17119
+ if (ch === "%" && i + 2 < input.length) {
17120
+ const hex = input.slice(i + 1, i + 3);
17121
+ if (isHexPair(hex)) {
17122
+ const normalizedHex = hex.toUpperCase();
17123
+ const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
17124
+ if (isUnreserved(decoded)) output += decoded;
17125
+ else output += "%" + normalizedHex;
17126
+ i += 2;
17127
+ continue;
17128
+ }
17129
+ }
17130
+ if (isQueryFragmentCharacter(ch)) output += ch;
17131
+ else {
17132
+ const code = input.charCodeAt(i);
17133
+ if (code < 128) output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
17134
+ else if (code < 55296 || code > 57343) output += percentEncodeNonAscii(code);
17135
+ else if (code <= 56319 && i + 1 < input.length) {
17136
+ const low = input.charCodeAt(i + 1);
17137
+ if (low >= 56320 && low <= 57343) {
17138
+ output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
17139
+ i++;
17140
+ } else output += percentEncodeNonAscii(65533);
17141
+ } else output += percentEncodeNonAscii(65533);
17142
+ }
17143
+ }
17144
+ return output;
17145
+ }
17146
+ /**
17147
+ * Escapes a component while preserving existing valid percent escapes.
17148
+ *
17149
+ * @param {string} input
17150
+ * @returns {string}
17151
+ */
17152
+ function escapePreservingEscapes(input) {
17153
+ let output = "";
17154
+ for (let i = 0; i < input.length; i++) {
17155
+ if (input[i] === "%" && i + 2 < input.length) {
17156
+ const hex = input.slice(i + 1, i + 3);
17157
+ if (isHexPair(hex)) {
17158
+ output += "%" + hex.toUpperCase();
17159
+ i += 2;
17160
+ continue;
17161
+ }
17162
+ }
17163
+ output += escape(input[i]);
17164
+ }
17165
+ return output;
16737
17166
  }
16738
17167
  /**
16739
17168
  * @param {import('../types/index').URIComponent} component
@@ -16742,28 +17171,42 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16742
17171
  function recomposeAuthority(component) {
16743
17172
  const uriTokens = [];
16744
17173
  if (component.userinfo !== void 0) {
16745
- uriTokens.push(component.userinfo);
17174
+ uriTokens.push(encodeUserinfo(component.userinfo));
16746
17175
  uriTokens.push("@");
16747
17176
  }
16748
17177
  if (component.host !== void 0) {
16749
- let host = unescape(component.host);
17178
+ let host = component.host;
16750
17179
  if (!isIPv4(host)) {
16751
- const ipV6res = normalizeIPv6(host);
16752
- if (ipV6res.isIPV6 === true) host = `[${ipV6res.escapedHost}]`;
16753
- else host = component.host;
17180
+ let ipV6res = normalizeIPv6(host);
17181
+ if (ipV6res.isIPV6 !== true && ipV6res.isIPVFuture !== true) {
17182
+ host = normalizePercentEncoding(host, true);
17183
+ ipV6res = normalizeIPv6(host);
17184
+ }
17185
+ if (ipV6res.isIPV6 === true || ipV6res.isIPVFuture === true) host = `[${ipV6res.escapedHost}]`;
17186
+ else host = reescapeHostDelimiters(host, false);
16754
17187
  }
16755
17188
  uriTokens.push(host);
16756
17189
  }
16757
17190
  if (typeof component.port === "number" || typeof component.port === "string") {
17191
+ const port = String(component.port);
17192
+ if (!isPort(port)) throw new TypeError("URI port is malformed.");
16758
17193
  uriTokens.push(":");
16759
- uriTokens.push(String(component.port));
17194
+ uriTokens.push(port);
16760
17195
  }
16761
17196
  return uriTokens.length ? uriTokens.join("") : void 0;
16762
17197
  }
16763
17198
  module.exports = {
16764
17199
  nonSimpleDomain,
16765
17200
  recomposeAuthority,
16766
- normalizeComponentEncoding,
17201
+ reescapeHostDelimiters,
17202
+ normalizePercentEncoding,
17203
+ normalizePathEncoding,
17204
+ serializePathEncoding,
17205
+ normalizeQueryFragmentEncoding,
17206
+ encodeUserinfo,
17207
+ encodeQuery,
17208
+ encodeFragment,
17209
+ escapePreservingEscapes,
16767
17210
  removeDotSegments,
16768
17211
  isIPv4,
16769
17212
  isUUID,
@@ -16773,7 +17216,7 @@ var require_utils = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16773
17216
  }));
16774
17217
  var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16775
17218
  const { isUUID } = require_utils();
16776
- const URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu;
17219
+ const URN_REG = /^([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-./:;=@]|%[\da-f]{2})+)$/iu;
16777
17220
  const supportedSchemeNames = [
16778
17221
  "http",
16779
17222
  "https",
@@ -16844,9 +17287,10 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16844
17287
  wsComponent.secure = void 0;
16845
17288
  }
16846
17289
  if (wsComponent.resourceName) {
16847
- const [path, query] = wsComponent.resourceName.split("?");
17290
+ const queryIndex = wsComponent.resourceName.indexOf("?");
17291
+ const path = queryIndex === -1 ? wsComponent.resourceName : wsComponent.resourceName.slice(0, queryIndex);
16848
17292
  wsComponent.path = path && path !== "/" ? path : void 0;
16849
- wsComponent.query = query;
17293
+ wsComponent.query = queryIndex === -1 ? void 0 : wsComponent.resourceName.slice(queryIndex + 1);
16850
17294
  wsComponent.resourceName = void 0;
16851
17295
  }
16852
17296
  wsComponent.fragment = void 0;
@@ -16859,7 +17303,7 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16859
17303
  return urnComponent;
16860
17304
  }
16861
17305
  const matches = urnComponent.path.match(URN_REG);
16862
- if (matches) {
17306
+ if (matches && matches[0] === urnComponent.path) {
16863
17307
  const scheme = options.scheme || urnComponent.scheme || "urn";
16864
17308
  urnComponent.nid = matches[1].toLowerCase();
16865
17309
  urnComponent.nss = matches[2];
@@ -16953,8 +17397,19 @@ var require_schemes = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16953
17397
  };
16954
17398
  }));
16955
17399
  var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16956
- const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
17400
+ const { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, serializePathEncoding, normalizeQueryFragmentEncoding, encodeQuery, encodeFragment, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils();
16957
17401
  const { SCHEMES, getSchemeHandler } = require_schemes();
17402
+ const VALID_SCHEME = /^[A-Za-z][A-Za-z0-9+.-]*$/u;
17403
+ const MALFORMED_SCHEME_ERROR = "URI scheme is malformed.";
17404
+ /**
17405
+ * @param {string} scheme
17406
+ * @returns {string}
17407
+ */
17408
+ function decodeValidScheme(scheme) {
17409
+ const decodedScheme = unescape(String(scheme));
17410
+ if (!VALID_SCHEME.test(decodedScheme)) throw new TypeError(MALFORMED_SCHEME_ERROR);
17411
+ return decodedScheme;
17412
+ }
16958
17413
  /**
16959
17414
  * @template {import('./types/index').URIComponent|string} T
16960
17415
  * @param {T} uri
@@ -16962,7 +17417,7 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16962
17417
  * @returns {T}
16963
17418
  */
16964
17419
  function normalize(uri, options) {
16965
- if (typeof uri === "string") uri = serialize(parse(uri, options), options);
17420
+ if (typeof uri === "string") uri = normalizeString(uri, options);
16966
17421
  else if (typeof uri === "object") uri = parse(serialize(uri, options), options);
16967
17422
  return uri;
16968
17423
  }
@@ -16974,7 +17429,16 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
16974
17429
  */
16975
17430
  function resolve(baseURI, relativeURI, options) {
16976
17431
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
16977
- const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
17432
+ const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed, malformedPercentEncoding: baseMalformedPercentEncoding, malformedSchemeSpecific: baseMalformedSchemeSpecific, malformedHost: baseMalformedHost, malformedScheme: baseMalformedScheme } = parseWithStatus(baseURI, schemelessOptions);
17433
+ const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed, malformedPercentEncoding: relativeMalformedPercentEncoding, malformedSchemeSpecific: relativeMalformedSchemeSpecific, malformedHost: relativeMalformedHost, malformedScheme: relativeMalformedScheme } = parseWithStatus(relativeURI, schemelessOptions);
17434
+ if (baseMalformed || relativeMalformed || baseMalformedPercentEncoding || relativeMalformedPercentEncoding || baseMalformedSchemeSpecific || relativeMalformedSchemeSpecific || baseMalformedHost || relativeMalformedHost || baseMalformedScheme || relativeMalformedScheme) throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
17435
+ const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
17436
+ const resolvedSchemeHandler = getSchemeHandler(options && options.scheme || resolved.scheme);
17437
+ const resolvedHost = resolved.host;
17438
+ const resolvedHostIsIP = resolvedHost !== void 0 && resolvedHost !== "" && (isIPv4(resolvedHost) || normalizeIPv6(resolvedHost).isIPV6);
17439
+ canonicalizeHost(resolved, options || {}, resolvedSchemeHandler, resolvedHostIsIP);
17440
+ const encodedASCIIHost = resolvedHost && resolvedHost.indexOf("%") !== -1 && !/\P{ASCII}/u.test(resolvedHost);
17441
+ if (resolved.error && !encodedASCIIHost) throw new Error(resolved.error);
16978
17442
  schemelessOptions.skipEscape = true;
16979
17443
  return serialize(resolved, schemelessOptions);
16980
17444
  }
@@ -17038,27 +17502,9 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17038
17502
  * @returns {boolean}
17039
17503
  */
17040
17504
  function equal(uriA, uriB, options) {
17041
- if (typeof uriA === "string") {
17042
- uriA = unescape(uriA);
17043
- uriA = serialize(normalizeComponentEncoding(parse(uriA, options), true), {
17044
- ...options,
17045
- skipEscape: true
17046
- });
17047
- } else if (typeof uriA === "object") uriA = serialize(normalizeComponentEncoding(uriA, true), {
17048
- ...options,
17049
- skipEscape: true
17050
- });
17051
- if (typeof uriB === "string") {
17052
- uriB = unescape(uriB);
17053
- uriB = serialize(normalizeComponentEncoding(parse(uriB, options), true), {
17054
- ...options,
17055
- skipEscape: true
17056
- });
17057
- } else if (typeof uriB === "object") uriB = serialize(normalizeComponentEncoding(uriB, true), {
17058
- ...options,
17059
- skipEscape: true
17060
- });
17061
- return uriA.toLowerCase() === uriB.toLowerCase();
17505
+ const normalizedA = normalizeComparableURI(uriA, options);
17506
+ const normalizedB = normalizeComparableURI(uriB, options);
17507
+ return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA === normalizedB;
17062
17508
  }
17063
17509
  /**
17064
17510
  * @param {Readonly<import('./types/index').URIComponent>} cmpts
@@ -17084,13 +17530,17 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17084
17530
  };
17085
17531
  const options = Object.assign({}, opts);
17086
17532
  const uriTokens = [];
17533
+ if (component.scheme) component.scheme = decodeValidScheme(component.scheme);
17087
17534
  const schemeHandler = getSchemeHandler(options.scheme || component.scheme);
17088
17535
  if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options);
17089
- if (component.path !== void 0) if (!options.skipEscape) {
17090
- component.path = escape(component.path);
17091
- if (component.scheme !== void 0) component.path = component.path.split("%3A").join(":");
17092
- } else component.path = unescape(component.path);
17093
- if (options.reference !== "suffix" && component.scheme) uriTokens.push(component.scheme, ":");
17536
+ const hasAuthority = component.userinfo !== void 0 || component.host !== void 0 || component.port !== void 0;
17537
+ const pathNoScheme = !options.skipEscape && component.scheme === void 0 && !hasAuthority;
17538
+ if (component.path !== void 0) if (!options.skipEscape) component.path = serializePathEncoding(component.path, pathNoScheme);
17539
+ else component.path = normalizePercentEncoding(component.path);
17540
+ if (options.reference !== "suffix" && component.scheme) {
17541
+ component.scheme = decodeValidScheme(component.scheme);
17542
+ uriTokens.push(component.scheme, ":");
17543
+ }
17094
17544
  const authority = recomposeAuthority(component);
17095
17545
  if (authority !== void 0) {
17096
17546
  if (options.reference !== "suffix") uriTokens.push("//");
@@ -17100,20 +17550,84 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17100
17550
  if (component.path !== void 0) {
17101
17551
  let s = component.path;
17102
17552
  if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) s = removeDotSegments(s);
17553
+ if (pathNoScheme) s = serializePathEncoding(s, true);
17103
17554
  if (authority === void 0 && s[0] === "/" && s[1] === "/") s = "/%2F" + s.slice(2);
17104
17555
  uriTokens.push(s);
17105
17556
  }
17106
- if (component.query !== void 0) uriTokens.push("?", component.query);
17107
- if (component.fragment !== void 0) uriTokens.push("#", component.fragment);
17557
+ if (component.query !== void 0) uriTokens.push("?", encodeQuery(component.query));
17558
+ if (component.fragment !== void 0) uriTokens.push("#", encodeFragment(component.fragment));
17108
17559
  return uriTokens.join("");
17109
17560
  }
17110
17561
  const URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
17562
+ const AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
17563
+ const AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
17564
+ /**
17565
+ * @param {import('./types/index').URIComponent} parsed
17566
+ * @param {RegExpMatchArray} matches
17567
+ * @returns {string|undefined}
17568
+ */
17569
+ function getParseError(parsed, matches) {
17570
+ if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") return "URI path must start with \"/\" when authority is present.";
17571
+ if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) return "URI port is malformed.";
17572
+ }
17573
+ /**
17574
+ * Checks percent syntax without decoding the represented octets. RFC 3986
17575
+ * percent-encoding is byte-oriented, so sequences such as `%FF` are valid even
17576
+ * though they are not independently valid UTF-8.
17577
+ *
17578
+ * @param {string|undefined} component
17579
+ * @returns {boolean}
17580
+ */
17581
+ function hasMalformedPercentEncoding(component) {
17582
+ if (component === void 0) return false;
17583
+ let percent = component.indexOf("%");
17584
+ while (percent !== -1) {
17585
+ if (percent + 2 >= component.length || !/^[\da-f]{2}$/iu.test(component.slice(percent + 1, percent + 3))) return true;
17586
+ percent = component.indexOf("%", percent + 3);
17587
+ }
17588
+ return false;
17589
+ }
17590
+ /**
17591
+ * Whether the host is a bracketed IP literal (RFC 3986 `IP-literal`).
17592
+ * An unterminated `[` is not a literal, so it must still be validated as a
17593
+ * reg-name instead of being waved through as an IP.
17594
+ *
17595
+ * @param {string} host
17596
+ * @returns {boolean}
17597
+ */
17598
+ function isIPLiteral(host) {
17599
+ return host[0] === "[" && host[host.length - 1] === "]";
17600
+ }
17601
+ /**
17602
+ * @param {RegExpMatchArray} matches
17603
+ * @returns {boolean}
17604
+ */
17605
+ function hasMalformedComponentPercentEncoding(matches) {
17606
+ const host = matches[4];
17607
+ return hasMalformedPercentEncoding(matches[3]) || host !== void 0 && !isIPLiteral(host) && hasMalformedPercentEncoding(host) || hasMalformedPercentEncoding(matches[6]) || hasMalformedPercentEncoding(matches[7]) || hasMalformedPercentEncoding(matches[8]);
17608
+ }
17609
+ /**
17610
+ * @param {import('./types/index').URIComponent} parsed
17611
+ * @param {import('./types/index').Options} options
17612
+ * @param {{ domainHost?: boolean, unicodeSupport?: boolean }|undefined} schemeHandler
17613
+ * @param {boolean} isIP
17614
+ * @returns {boolean} whether host conversion failed
17615
+ */
17616
+ function canonicalizeHost(parsed, options, schemeHandler, isIP) {
17617
+ if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport) && parsed.host && !isIPLiteral(parsed.host) && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) try {
17618
+ parsed.host = new URL("http://" + parsed.host).hostname;
17619
+ } catch (e) {
17620
+ parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
17621
+ return true;
17622
+ }
17623
+ return false;
17624
+ }
17111
17625
  /**
17112
17626
  * @param {string} uri
17113
17627
  * @param {import('./types/index').Options} [opts]
17114
- * @returns
17628
+ * @returns {{ parsed: import('./types/index').URIComponent, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
17115
17629
  */
17116
- function parse(uri, opts) {
17630
+ function parseWithStatus(uri, opts) {
17117
17631
  const options = Object.assign({}, opts);
17118
17632
  /** @type {import('./types/index').URIComponent} */
17119
17633
  const parsed = {
@@ -17125,9 +17639,34 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17125
17639
  query: void 0,
17126
17640
  fragment: void 0
17127
17641
  };
17642
+ let malformedAuthorityOrPort = false;
17643
+ let malformedPercentEncoding = false;
17644
+ let malformedSchemeSpecific = false;
17645
+ let malformedHost = false;
17646
+ let malformedIPLiteral = false;
17647
+ let malformedScheme = false;
17128
17648
  let isIP = false;
17129
17649
  if (options.reference === "suffix") if (options.scheme) uri = options.scheme + ":" + uri;
17130
17650
  else uri = "//" + uri;
17651
+ const authorityMatch = uri.match(AUTHORITY_PREFIX);
17652
+ if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) {
17653
+ parsed.error = "URI authority must not contain a literal backslash.";
17654
+ malformedAuthorityOrPort = true;
17655
+ }
17656
+ const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
17657
+ if (introducerMatch !== null) {
17658
+ const region = introducerMatch[1];
17659
+ const normalizedRegion = region.replace(/[\t\n\r]/g, "");
17660
+ if (normalizedRegion.length >= 2) {
17661
+ if (normalizedRegion.slice(0, 2) !== "//") {
17662
+ parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
17663
+ malformedAuthorityOrPort = true;
17664
+ } else if (region.length !== normalizedRegion.length) {
17665
+ parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
17666
+ malformedAuthorityOrPort = true;
17667
+ }
17668
+ }
17669
+ }
17131
17670
  const matches = uri.match(URI_PARSE);
17132
17671
  if (matches) {
17133
17672
  parsed.scheme = matches[1];
@@ -17137,11 +17676,33 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17137
17676
  parsed.path = matches[6] || "";
17138
17677
  parsed.query = matches[7];
17139
17678
  parsed.fragment = matches[8];
17679
+ if (parsed.scheme !== void 0) {
17680
+ const decodedScheme = unescape(parsed.scheme);
17681
+ if (VALID_SCHEME.test(decodedScheme)) parsed.scheme = decodedScheme.toLowerCase();
17682
+ else {
17683
+ parsed.error = parsed.error || MALFORMED_SCHEME_ERROR;
17684
+ malformedScheme = true;
17685
+ }
17686
+ }
17687
+ malformedPercentEncoding = hasMalformedComponentPercentEncoding(matches);
17688
+ if (malformedPercentEncoding) parsed.error = parsed.error || "URI contains malformed percent-encoding.";
17140
17689
  if (isNaN(parsed.port)) parsed.port = matches[5];
17690
+ const parseError = getParseError(parsed, matches);
17691
+ if (parseError !== void 0) {
17692
+ parsed.error = parsed.error || parseError;
17693
+ malformedAuthorityOrPort = true;
17694
+ }
17141
17695
  if (parsed.host) if (isIPv4(parsed.host) === false) {
17696
+ const bracketedIPLiteral = isIPLiteral(parsed.host);
17697
+ const hasIPLiteralBracket = parsed.host.indexOf("[") !== -1 || parsed.host.indexOf("]") !== -1;
17142
17698
  const ipv6result = normalizeIPv6(parsed.host);
17143
- parsed.host = ipv6result.host.toLowerCase();
17144
- isIP = ipv6result.isIPV6;
17699
+ isIP = ipv6result.isIPV6 || ipv6result.isIPVFuture === true;
17700
+ malformedIPLiteral = hasIPLiteralBracket && (!bracketedIPLiteral || ipv6result.error === true);
17701
+ parsed.host = isIP ? ipv6result.host : ipv6result.host.toLowerCase();
17702
+ if (malformedIPLiteral) {
17703
+ parsed.error = parsed.error || "URI host is malformed.";
17704
+ malformedAuthorityOrPort = true;
17705
+ }
17145
17706
  } else isIP = true;
17146
17707
  if (parsed.scheme === void 0 && parsed.userinfo === void 0 && parsed.host === void 0 && parsed.port === void 0 && parsed.query === void 0 && !parsed.path) parsed.reference = "same-document";
17147
17708
  else if (parsed.scheme === void 0) parsed.reference = "relative";
@@ -17149,24 +17710,78 @@ var require_fast_uri = /* @__PURE__ */ __commonJSMin(((exports, module) => {
17149
17710
  else parsed.reference = "uri";
17150
17711
  if (options.reference && options.reference !== "suffix" && options.reference !== parsed.reference) parsed.error = parsed.error || "URI is not a " + options.reference + " reference.";
17151
17712
  const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme);
17152
- if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) {
17153
- if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) try {
17154
- parsed.host = URL.domainToASCII(parsed.host.toLowerCase());
17155
- } catch (e) {
17156
- parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e;
17157
- }
17713
+ if (!malformedIPLiteral) malformedHost = canonicalizeHost(parsed, options, schemeHandler, isIP);
17714
+ if (uri.indexOf("%") !== -1 && parsed.host !== void 0 && !malformedIPLiteral) {
17715
+ let host = isIP ? parsed.host : normalizePercentEncoding(parsed.host, true);
17716
+ if (!isIP) host = normalizePercentEncoding(host.toLowerCase());
17717
+ parsed.host = reescapeHostDelimiters(host, isIP);
17158
17718
  }
17159
17719
  if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) {
17160
- if (uri.indexOf("%") !== -1) {
17161
- if (parsed.scheme !== void 0) parsed.scheme = unescape(parsed.scheme);
17162
- if (parsed.host !== void 0) parsed.host = unescape(parsed.host);
17163
- }
17164
- if (parsed.path) parsed.path = escape(unescape(parsed.path));
17165
- if (parsed.fragment) parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
17720
+ if (parsed.path) parsed.path = normalizePathEncoding(parsed.path);
17721
+ if (parsed.query) parsed.query = normalizeQueryFragmentEncoding(parsed.query);
17722
+ if (parsed.fragment) parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
17723
+ }
17724
+ if (schemeHandler && schemeHandler.parse) {
17725
+ schemeHandler.parse(parsed, options);
17726
+ if (schemeHandler === SCHEMES.urn && parsed.nid === void 0) malformedSchemeSpecific = true;
17166
17727
  }
17167
- if (schemeHandler && schemeHandler.parse) schemeHandler.parse(parsed, options);
17168
17728
  } else parsed.error = parsed.error || "URI can not be parsed.";
17169
- return parsed;
17729
+ return {
17730
+ parsed,
17731
+ malformedAuthorityOrPort,
17732
+ malformedPercentEncoding,
17733
+ malformedSchemeSpecific,
17734
+ malformedHost,
17735
+ malformedScheme
17736
+ };
17737
+ }
17738
+ /**
17739
+ * @param {string} uri
17740
+ * @param {import('./types/index').Options} [opts]
17741
+ * @returns
17742
+ */
17743
+ function parse(uri, opts) {
17744
+ return parseWithStatus(uri, opts).parsed;
17745
+ }
17746
+ /**
17747
+ * @param {string} uri
17748
+ * @param {import('./types/index').Options} [opts]
17749
+ * @returns {string}
17750
+ */
17751
+ function normalizeString(uri, opts) {
17752
+ return normalizeStringWithStatus(uri, opts).normalized;
17753
+ }
17754
+ /**
17755
+ * @param {string} uri
17756
+ * @param {import('./types/index').Options} [opts]
17757
+ * @returns {{ normalized: string, malformedAuthorityOrPort: boolean, malformedPercentEncoding: boolean, malformedSchemeSpecific: boolean, malformedHost: boolean, malformedScheme: boolean }}
17758
+ */
17759
+ function normalizeStringWithStatus(uri, opts) {
17760
+ const { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = parseWithStatus(uri, opts);
17761
+ return {
17762
+ normalized: malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? uri : serialize(parsed, opts),
17763
+ malformedAuthorityOrPort,
17764
+ malformedPercentEncoding,
17765
+ malformedSchemeSpecific,
17766
+ malformedHost,
17767
+ malformedScheme
17768
+ };
17769
+ }
17770
+ /**
17771
+ * @param {import ('./types/index').URIComponent|string} uri
17772
+ * @param {import('./types/index').Options} [opts]
17773
+ * @returns {string|undefined}
17774
+ */
17775
+ function normalizeComparableURI(uri, opts) {
17776
+ if (typeof uri !== "string" && typeof uri !== "object") return;
17777
+ let value;
17778
+ try {
17779
+ value = typeof uri === "string" ? uri : serialize(uri, opts);
17780
+ } catch {
17781
+ return;
17782
+ }
17783
+ const { normalized, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme } = normalizeStringWithStatus(value, opts);
17784
+ return malformedAuthorityOrPort || malformedPercentEncoding || malformedSchemeSpecific || malformedHost || malformedScheme ? void 0 : normalized;
17170
17785
  }
17171
17786
  const fastUri = {
17172
17787
  SCHEMES,
@@ -20955,7 +21570,7 @@ var AjvJsonSchemaValidator = class {
20955
21570
  }
20956
21571
  };
20957
21572
  import_ajv.Ajv;
20958
- // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/mcp-DIH4cS6P.mjs
21573
+ // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/mcp-oHn3ureC.mjs
20959
21574
  const COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
20960
21575
  /**
20961
21576
  * Checks if a schema is completable (has completion metadata).
@@ -20969,225 +21584,237 @@ function isCompletable(schema) {
20969
21584
  function getCompleter(schema) {
20970
21585
  return schema[COMPLETABLE_SYMBOL]?.complete;
20971
21586
  }
20972
- /**
20973
- * Whether a `subscriptions/listen` filter accepts a given change event.
20974
- *
20975
- * Pure: no I/O, no mutation. The filter governs ONLY the four
20976
- * subscription-gated change types — non-gated notifications never reach the
20977
- * bus and are not modeled here.
20978
- *
20979
- * `resource_updated` matches only when `resourceSubscriptions` is present and
20980
- * contains the event's URI exactly (per the spec: "for these resource URIs").
20981
- */
20982
- function listenFilterAccepts(filter, event) {
20983
- switch (event.kind) {
20984
- case "tools_list_changed": return filter.toolsListChanged === true;
20985
- case "prompts_list_changed": return filter.promptsListChanged === true;
20986
- case "resources_list_changed": return filter.resourcesListChanged === true;
20987
- case "resource_updated": return filter.resourceSubscriptions !== void 0 && filter.resourceSubscriptions.includes(event.uri);
20988
- }
21587
+ const MAX_TIMER_DELAY_MS = 2 ** 31 - 1;
21588
+ /** Arms an unref'd timer, or disables keep-alive for invalid delays. */
21589
+ function armSseKeepAlive(intervalMs, onTick) {
21590
+ if (!Number.isFinite(intervalMs) || intervalMs < 1) return;
21591
+ const timer = setInterval(onTick, Math.min(intervalMs, MAX_TIMER_DELAY_MS));
21592
+ timer.unref?.();
21593
+ return timer;
20989
21594
  }
21595
+ let readClassification;
20990
21596
  /**
20991
- * The honored subset of a requested filter: keeps only the fields the client
20992
- * explicitly opted in to (drops `false` and absent fields), narrowed against
20993
- * the server's declared capabilities when supplied. The serving entry sends
20994
- * this back in `notifications/subscriptions/acknowledged` so the ack reflects
20995
- * what the server can actually deliver.
21597
+ * The per-request micro-transport: a real, connected `Transport` whose whole
21598
+ * lifetime is one HTTP exchange. See the module documentation for the
21599
+ * response shapes it produces.
20996
21600
  *
20997
- * - `toolsListChanged` is honored only when `capabilities.tools.listChanged`
20998
- * is advertised; likewise `promptsListChanged` / `resourcesListChanged`.
20999
- * - `resourceSubscriptions` is honored only when
21000
- * `capabilities.resources.subscribe` is advertised.
21001
- *
21002
- * `capabilities` is optional on this pure helper for test convenience only —
21003
- * both wired routers REQUIRE capabilities at the call site (the HTTP router's
21004
- * `serve()` takes a required parameter; `StdioListenRouter.serve()` throws
21005
- * before `setServerCapabilities()` was called), so the fail-open
21006
- * `undefined → honor everything` branch is never reachable on a wired entry.
21007
- */
21008
- function honoredSubset(requested, capabilities) {
21009
- const honored = {};
21010
- const allow = (bit) => capabilities === void 0 || bit === true;
21011
- if (requested.toolsListChanged === true && allow(capabilities?.tools?.listChanged)) honored.toolsListChanged = true;
21012
- if (requested.promptsListChanged === true && allow(capabilities?.prompts?.listChanged)) honored.promptsListChanged = true;
21013
- if (requested.resourcesListChanged === true && allow(capabilities?.resources?.listChanged)) honored.resourcesListChanged = true;
21014
- if (requested.resourceSubscriptions !== void 0 && requested.resourceSubscriptions.length > 0 && allow(capabilities?.resources?.subscribe)) honored.resourceSubscriptions = [...requested.resourceSubscriptions];
21015
- return honored;
21016
- }
21017
- /** Default capacity guard: refuse a new subscription when this many are already open. */
21018
- const DEFAULT_MAX_SUBSCRIPTIONS = 1024;
21019
- /** Stamp the subscription id onto a notification's `_meta`. Non-mutating. */
21020
- function stampSubscriptionId(notification, subscriptionId) {
21021
- return {
21022
- method: notification.method,
21023
- params: {
21024
- ...notification.params,
21025
- _meta: {
21026
- ...notification.params?._meta,
21027
- [SUBSCRIPTION_ID_META_KEY]: subscriptionId
21028
- }
21029
- }
21030
- };
21031
- }
21032
- /**
21033
- * Read the requested filter off a `subscriptions/listen` request body.
21034
- * Returns the validated filter, or `undefined` when `params.notifications`
21035
- * is absent or fails the schema (the caller answers `-32602` — the spec
21036
- * marks `notifications` REQUIRED on the listen request).
21037
- */
21038
- function parseListenFilter(message) {
21039
- const outcome = codecForVersion(MODERN_WIRE_REVISION).validateRequest("subscriptions/listen", message);
21040
- return outcome.ok ? outcome.value.params?.notifications : void 0;
21041
- }
21042
- const CHANGE_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
21043
- "notifications/tools/list_changed",
21044
- "notifications/prompts/list_changed",
21045
- "notifications/resources/list_changed",
21046
- "notifications/resources/updated"
21047
- ]);
21048
- /**
21049
- * Per-connection listen state for the stdio entry. One instance is held by
21050
- * `serveStdio` for the connection lifetime; it routes inbound
21051
- * `subscriptions/listen` / `notifications/cancelled` and rewrites outbound
21052
- * change notifications onto the active subscriptions. No bus — the long-lived
21053
- * pinned instance's existing `send*ListChanged()` calls feed straight into
21054
- * `routeOutbound()`.
21601
+ * Connecting a `Server` binds the instance to the classified modern revision
21602
+ * when its `supportedProtocolVersions` lists it and the SDK implements it.
21055
21603
  */
21056
- var StdioListenRouter = class {
21057
- /** Active subscriptions, keyed by the listen request's JSON-RPC id verbatim. */
21058
- _subs = /* @__PURE__ */ new Map();
21059
- /**
21060
- * The serving instance's declared capabilities. Filled in by the entry
21061
- * once the modern instance is constructed (the router is created before
21062
- * the instance exists), so the acknowledged filter is narrowed against
21063
- * what the server can actually deliver.
21064
- */
21065
- _serverCapabilities;
21066
- /**
21067
- * The serving instance's identity, stamped onto the graceful-close
21068
- * results' `_meta` (the spec's `SubscriptionsListenResultMetaObject` extends
21069
- * `ResultMetaObject`). Handed over together with the capabilities.
21070
- */
21071
- _serverInfo;
21072
- constructor(_maxSubscriptions = DEFAULT_MAX_SUBSCRIPTIONS, serverCapabilities, serverInfo) {
21073
- this._maxSubscriptions = _maxSubscriptions;
21074
- this._serverCapabilities = serverCapabilities;
21075
- this._serverInfo = serverInfo;
21604
+ var PerRequestHTTPServerTransport = class {
21605
+ static {
21606
+ readClassification = (transport) => transport._classification;
21076
21607
  }
21077
- /**
21078
- * Record the serving instance's declared capabilities and identity once
21079
- * it has been constructed. Called by `serveStdio`'s connect path;
21080
- * subsequent `serve()` calls narrow the honored filter against the
21081
- * capabilities, and `teardownAll()` stamps the identity.
21082
- */
21083
- setServerCapabilities(capabilities, serverInfo) {
21084
- this._serverCapabilities = capabilities;
21085
- if (serverInfo !== void 0) this._serverInfo = serverInfo;
21608
+ onclose;
21609
+ onerror;
21610
+ onmessage;
21611
+ _classification;
21612
+ _responseMode;
21613
+ _started = false;
21614
+ _used = false;
21615
+ _closed = false;
21616
+ _terminalDelivered = false;
21617
+ /**
21618
+ * `true` only while the inbound message is being delivered synchronously
21619
+ * to the connected protocol layer. The pre-handler gates (the era
21620
+ * registry gate, the edge→instance handoff check, the missing-handler
21621
+ * rejection) answer inside this window; request handlers always run
21622
+ * after it (the protocol layer defers them to a microtask). An error
21623
+ * sent inside the window is therefore ladder-originated, and an error
21624
+ * sent after it is handler-produced.
21625
+ */
21626
+ _dispatchWindowOpen = false;
21627
+ _requestId;
21628
+ _deferredResponse;
21629
+ _sse;
21630
+ _abortCleanup;
21631
+ _keepAliveMs;
21632
+ constructor(options) {
21633
+ this._classification = options.classification;
21634
+ this._responseMode = options.responseMode ?? "auto";
21635
+ this._keepAliveMs = options.keepAliveMs ?? 15e3;
21086
21636
  }
21087
- /** Whether `id` is an active listen subscription on this connection. */
21088
- has(id) {
21089
- return this._subs.has(id);
21637
+ async start() {
21638
+ if (this._started) throw new Error("PerRequestHTTPServerTransport is already started");
21639
+ this._started = true;
21090
21640
  }
21091
21641
  /**
21092
- * Serve one inbound `subscriptions/listen` request: registers the
21093
- * subscription and returns the stamped acknowledged notification (or, on
21094
- * capacity / params rejection, the in-band JSON-RPC error response).
21642
+ * Serves the single exchange: delivers the classified message to the
21643
+ * connected server instance and resolves with the HTTP response.
21095
21644
  *
21096
- * @throws when called before {@linkcode setServerCapabilities} (or the
21097
- * constructor) has supplied the serving instance's capabilities. Honoring a
21098
- * filter without knowing the server's advertised capabilities would fail
21099
- * open (deliver unadvertised types); the entry guarantees capabilities are
21100
- * set before any listen request is routed here.
21101
- */
21102
- serve(message) {
21103
- if (this._serverCapabilities === void 0) throw new Error("StdioListenRouter.serve() called before setServerCapabilities(); refusing to honor a filter without capabilities");
21104
- if (this._subs.size >= this._maxSubscriptions) return {
21105
- jsonrpc: "2.0",
21106
- id: message.id,
21107
- error: {
21108
- code: -32603,
21109
- message: "Subscription limit reached"
21110
- }
21645
+ * Throws when called a second time (the transport is strictly
21646
+ * single-use), or before a server has been connected to the transport.
21647
+ * The returned promise rejects with a connection-closed error when the
21648
+ * transport is closed before a response was produced (for example because
21649
+ * the client disconnected).
21650
+ */
21651
+ async handleMessage(message, extra) {
21652
+ if (this._used) throw new Error("PerRequestHTTPServerTransport serves exactly one exchange; construct a new transport per request");
21653
+ if (!this._started || this.onmessage === void 0) throw new Error("PerRequestHTTPServerTransport is not connected: connect a server to this transport before handling a message");
21654
+ if (this._closed) throw new Error("PerRequestHTTPServerTransport is closed");
21655
+ this._used = true;
21656
+ const signal = extra?.request?.signal;
21657
+ if (signal?.aborted) {
21658
+ await this.close();
21659
+ throw new SdkError(SdkErrorCode.ConnectionClosed, "The request was aborted before it could be handled");
21660
+ }
21661
+ const messageExtra = {
21662
+ classification: this._classification,
21663
+ ...extra?.request !== void 0 && { request: extra.request },
21664
+ ...extra?.authInfo !== void 0 && { authInfo: extra.authInfo }
21111
21665
  };
21112
- const filter = parseListenFilter(message);
21113
- if (filter === void 0) return {
21114
- jsonrpc: "2.0",
21115
- id: message.id,
21116
- error: {
21117
- code: -32602,
21118
- message: "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter"
21666
+ if (isJSONRPCRequest(message)) {
21667
+ this._requestId = message.id;
21668
+ let resolve;
21669
+ let reject;
21670
+ const promise = new Promise((promiseResolve, promiseReject) => {
21671
+ resolve = promiseResolve;
21672
+ reject = promiseReject;
21673
+ });
21674
+ this._deferredResponse = {
21675
+ promise,
21676
+ resolve,
21677
+ reject,
21678
+ settled: false
21679
+ };
21680
+ if (signal !== void 0) {
21681
+ const onAbort = () => void this.close();
21682
+ signal.addEventListener("abort", onAbort, { once: true });
21683
+ this._abortCleanup = () => signal.removeEventListener("abort", onAbort);
21119
21684
  }
21120
- };
21121
- const honored = honoredSubset(filter, this._serverCapabilities);
21122
- this._subs.set(message.id, honored);
21123
- return stampSubscriptionId({
21124
- method: "notifications/subscriptions/acknowledged",
21125
- params: { notifications: honored }
21126
- }, message.id);
21685
+ this._dispatchWindowOpen = true;
21686
+ try {
21687
+ this.onmessage(message, messageExtra);
21688
+ } finally {
21689
+ this._dispatchWindowOpen = false;
21690
+ }
21691
+ if (this._responseMode === "sse" && !this._closed && !this._deferredResponse.settled) this.upgradeToSse();
21692
+ return promise;
21693
+ }
21694
+ this.onmessage(message, messageExtra);
21695
+ return new Response(null, { status: 202 });
21127
21696
  }
21128
- /**
21129
- * Tear down one subscription (inbound `notifications/cancelled`). Returns
21130
- * `true` when a subscription was removed. After this call NOTHING further
21131
- * is delivered for that subscription id (the post-cancel hardening).
21132
- */
21133
- cancel(id) {
21134
- return this._subs.delete(id);
21697
+ async send(message, options) {
21698
+ if (this._closed) return;
21699
+ const isResponse = isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message);
21700
+ const relatedId = isResponse ? message.id : options?.relatedRequestId;
21701
+ if (this._requestId === void 0 || relatedId === void 0 || relatedId !== this._requestId) {
21702
+ if (isResponse) this.onerror?.(/* @__PURE__ */ new Error(`Received a response for an unknown request id: ${String(message.id)}`));
21703
+ return;
21704
+ }
21705
+ if (isResponse) {
21706
+ if (this._terminalDelivered) return;
21707
+ this._terminalDelivered = true;
21708
+ const errorCode = isJSONRPCErrorResponse(message) ? message.error.code : void 0;
21709
+ const ladderStatus = errorCode !== void 0 && (this._dispatchWindowOpen || errorCode === ProtocolErrorCode.MissingRequiredClientCapability) ? LADDER_ERROR_HTTP_STATUS[errorCode] : void 0;
21710
+ if (ladderStatus !== void 0 && this._sse === void 0) {
21711
+ this.settleResponse(Response.json(message, {
21712
+ status: ladderStatus,
21713
+ headers: { "Content-Type": "application/json" }
21714
+ }));
21715
+ queueMicrotask(() => void this.close());
21716
+ return;
21717
+ }
21718
+ if (this._sse !== void 0 || this._responseMode === "sse") {
21719
+ if (this._sse === void 0) this.upgradeToSse();
21720
+ this.writeMessageFrame(message);
21721
+ this.finalizeStream();
21722
+ return;
21723
+ }
21724
+ this.settleResponse(Response.json(message, {
21725
+ status: 200,
21726
+ headers: { "Content-Type": "application/json" }
21727
+ }));
21728
+ queueMicrotask(() => void this.close());
21729
+ return;
21730
+ }
21731
+ if (this._responseMode === "json") return;
21732
+ if (this._sse === void 0) this.upgradeToSse();
21733
+ this.writeMessageFrame(message);
21135
21734
  }
21136
21735
  /**
21137
- * Route an outbound notification through the active subscriptions.
21138
- *
21139
- * - For a subscription-gated change notification, returns one stamped copy
21140
- * per subscription that opted in to it (an empty array means it is
21141
- * dropped — the modern era never delivers an un-requested change type).
21142
- * - For any other outbound message, returns `'passthrough'` (the entry
21143
- * forwards it as-is).
21736
+ * Writes an SSE comment frame (a keep-alive heartbeat). Dropped when the
21737
+ * exchange is not currently streaming.
21144
21738
  */
21145
- routeOutbound(message) {
21146
- if (!CHANGE_NOTIFICATION_METHODS.has(message.method)) return "passthrough";
21147
- const uriParam = message.params?.["uri"];
21148
- const uri = typeof uriParam === "string" ? uriParam : void 0;
21149
- const event = notificationToServerEvent(message.method, uri);
21150
- const out = [];
21151
- for (const [subscriptionId, filter] of this._subs) if (listenFilterAccepts(filter, event)) out.push(stampSubscriptionId({
21152
- method: message.method,
21153
- params: message.params ?? {}
21154
- }, subscriptionId));
21155
- return out;
21739
+ writeCommentFrame(comment) {
21740
+ if (this._closed || this._sse === void 0 || this._sse.closed) return;
21741
+ const frame = comment.split("\n").map((line) => `: ${line}`).join("\n");
21742
+ this.writeFrame(`${frame}\n\n`);
21156
21743
  }
21157
- /**
21158
- * Server-side graceful teardown of every active subscription: returns the
21159
- * empty `subscriptions/listen` JSON-RPC result for each subscription id —
21160
- * the spec's graceful-close signal, `_meta` carrying the subscription id
21161
- * and the serving instance's identity — for the entry to emit before
21162
- * closing the wire. Clears the set so nothing further is delivered.
21163
- */
21164
- teardownAll() {
21165
- const out = [];
21166
- for (const id of this._subs.keys()) out.push({
21167
- jsonrpc: "2.0",
21168
- id,
21169
- result: {
21170
- resultType: "complete",
21171
- _meta: {
21172
- [SUBSCRIPTION_ID_META_KEY]: id,
21173
- ...this._serverInfo !== void 0 && { ["io.modelcontextprotocol/serverInfo"]: this._serverInfo }
21174
- }
21744
+ async close() {
21745
+ if (this._closed) return;
21746
+ this._closed = true;
21747
+ this._abortCleanup?.();
21748
+ this._abortCleanup = void 0;
21749
+ if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
21750
+ if (this._sse !== void 0 && !this._sse.closed) {
21751
+ this._sse.closed = true;
21752
+ try {
21753
+ this._sse.controller.close();
21754
+ } catch {}
21755
+ }
21756
+ if (this._deferredResponse !== void 0 && !this._deferredResponse.settled) {
21757
+ this._deferredResponse.settled = true;
21758
+ this._deferredResponse.reject(new SdkError(SdkErrorCode.ConnectionClosed, "Connection closed before a response was produced"));
21759
+ }
21760
+ this.onclose?.();
21761
+ }
21762
+ settleResponse(response) {
21763
+ if (this._deferredResponse === void 0 || this._deferredResponse.settled) return;
21764
+ this._deferredResponse.settled = true;
21765
+ this._deferredResponse.resolve(response);
21766
+ }
21767
+ upgradeToSse() {
21768
+ let controller;
21769
+ const readable = new ReadableStream({
21770
+ start: (streamController) => {
21771
+ controller = streamController;
21772
+ },
21773
+ cancel: () => {
21774
+ this.close();
21175
21775
  }
21176
21776
  });
21177
- this._subs.clear();
21178
- return out;
21179
- }
21180
- };
21181
- function notificationToServerEvent(method, uri) {
21182
- switch (method) {
21183
- case "notifications/tools/list_changed": return { kind: "tools_list_changed" };
21184
- case "notifications/prompts/list_changed": return { kind: "prompts_list_changed" };
21185
- case "notifications/resources/list_changed": return { kind: "resources_list_changed" };
21186
- default: return {
21187
- kind: "resource_updated",
21188
- uri: uri ?? ""
21777
+ this._sse = {
21778
+ controller,
21779
+ encoder: new TextEncoder(),
21780
+ closed: false
21189
21781
  };
21782
+ this._sse.keepAliveTimer = armSseKeepAlive(this._keepAliveMs, () => this.writeCommentFrame("keepalive"));
21783
+ this.settleResponse(new Response(readable, {
21784
+ status: 200,
21785
+ headers: {
21786
+ "Content-Type": "text/event-stream",
21787
+ "Cache-Control": "no-cache, no-transform",
21788
+ Connection: "keep-alive",
21789
+ "X-Accel-Buffering": "no"
21790
+ }
21791
+ }));
21792
+ }
21793
+ finalizeStream() {
21794
+ if (this._sse?.keepAliveTimer !== void 0) clearInterval(this._sse.keepAliveTimer);
21795
+ if (this._sse !== void 0 && !this._sse.closed) {
21796
+ this._sse.closed = true;
21797
+ try {
21798
+ this._sse.controller.close();
21799
+ } catch {}
21800
+ }
21801
+ queueMicrotask(() => void this.close());
21190
21802
  }
21803
+ writeMessageFrame(message) {
21804
+ this.writeFrame(`event: message\ndata: ${JSON.stringify(message)}\n\n`);
21805
+ }
21806
+ writeFrame(frame) {
21807
+ if (this._sse === void 0 || this._sse.closed) return;
21808
+ try {
21809
+ this._sse.controller.enqueue(this._sse.encoder.encode(frame));
21810
+ } catch (error) {
21811
+ this.onerror?.(/* @__PURE__ */ new Error(`Failed to write to the response stream: ${error}`));
21812
+ }
21813
+ }
21814
+ };
21815
+ /** Package-internal: the classification a per-request transport serves; `undefined` for any other transport. Not public API. */
21816
+ function perRequestClassificationOf(transport) {
21817
+ return transport instanceof PerRequestHTTPServerTransport ? readClassification(transport) : void 0;
21191
21818
  }
21192
21819
  /** @internal */
21193
21820
  function supportsScopeChallengeResolver(transport) {
@@ -21396,6 +22023,7 @@ var Server = class extends Protocol {
21396
22023
  _requestStateVerify;
21397
22024
  _inputRequiredServing;
21398
22025
  _legacyShim;
22026
+ _boundFromTransport = false;
21399
22027
  /** Lazily-built legacy shim; the loop lives in legacyInputRequiredShim.ts behind a narrow host contract. */
21400
22028
  _legacyInputRequiredShim() {
21401
22029
  return this._legacyShim ??= new LegacyInputRequiredShim({
@@ -21435,6 +22063,15 @@ var Server = class extends Protocol {
21435
22063
  /** Attaches to the given transport; rejects while this instance is connected to another one. */
21436
22064
  async connect(transport) {
21437
22065
  if (this.transport !== void 0) throw new SdkError(SdkErrorCode.AlreadyConnected, "Use a separate Server instance per connection: this instance is already connected to a transport. To connect it to a new transport, call close() first.");
22066
+ if (this._boundFromTransport) {
22067
+ this._boundFromTransport = false;
22068
+ this._negotiatedProtocolVersion = void 0;
22069
+ }
22070
+ const revision = perRequestClassificationOf(transport)?.revision;
22071
+ if (revision !== void 0 && !this._servedModernEra() && SUPPORTED_MODERN_PROTOCOL_VERSIONS.includes(revision) && modernProtocolVersions(this._supportedProtocolVersions).includes(revision)) {
22072
+ this._negotiatedProtocolVersion = revision;
22073
+ this._boundFromTransport = true;
22074
+ }
21438
22075
  await super.connect(transport);
21439
22076
  }
21440
22077
  /**
@@ -21745,8 +22382,8 @@ var Server = class extends Protocol {
21745
22382
  * @deprecated Read client identity from the per-request handler context instead: on
21746
22383
  * 2026-07-28 (per-request envelope) requests `ctx.mcpReq.envelope` carries the client's
21747
22384
  * declared capabilities, while on 2025-era connections this accessor keeps returning the
21748
- * `initialize`-scoped value. The accessor remains functional — instances serving the
21749
- * 2026-07-28 era are backfilled per request from the validated envelope.
22385
+ * `initialize`-scoped value. The accessor remains functional where `createMcpHandler`
22386
+ * serves the 2026-07-28 era: it backfills it per request from the validated envelope.
21750
22387
  */
21751
22388
  getClientCapabilities() {
21752
22389
  return this._clientCapabilities;
@@ -21757,8 +22394,8 @@ var Server = class extends Protocol {
21757
22394
  * @deprecated Read client identity from the per-request handler context instead: on
21758
22395
  * 2026-07-28 (per-request envelope) requests `ctx.mcpReq.envelope` carries the client's
21759
22396
  * name and version, while on 2025-era connections this accessor keeps returning the
21760
- * `initialize`-scoped value. The accessor remains functional — instances serving the
21761
- * 2026-07-28 era are backfilled per request from the validated envelope.
22397
+ * `initialize`-scoped value. The accessor remains functional where `createMcpHandler`
22398
+ * serves the 2026-07-28 era: it backfills it per request from the validated envelope.
21762
22399
  */
21763
22400
  getClientVersion() {
21764
22401
  return this._clientVersion;
@@ -22351,8 +22988,14 @@ var McpServer = class {
22351
22988
  if (updates.uri) this._registeredResources[updates.uri] = registeredResource;
22352
22989
  }
22353
22990
  if (updates.name !== void 0) registeredResource.name = updates.name;
22354
- if (updates.title !== void 0) registeredResource.title = updates.title;
22355
22991
  if (updates.metadata !== void 0) registeredResource.metadata = updates.metadata;
22992
+ if (updates.title !== void 0) {
22993
+ registeredResource.title = updates.title;
22994
+ registeredResource.metadata = {
22995
+ ...registeredResource.metadata,
22996
+ title: updates.title
22997
+ };
22998
+ }
22356
22999
  if (updates.callback !== void 0) registeredResource.readCallback = updates.callback;
22357
23000
  if (updates.scopeChallenge !== void 0) registeredResource.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
22358
23001
  if (updates.enabled !== void 0) registeredResource.enabled = updates.enabled;
@@ -22378,9 +23021,15 @@ var McpServer = class {
22378
23021
  delete this._registeredResourceTemplates[name];
22379
23022
  if (updates.name) this._registeredResourceTemplates[updates.name] = registeredResourceTemplate;
22380
23023
  }
22381
- if (updates.title !== void 0) registeredResourceTemplate.title = updates.title;
22382
23024
  if (updates.template !== void 0) registeredResourceTemplate.resourceTemplate = updates.template;
22383
23025
  if (updates.metadata !== void 0) registeredResourceTemplate.metadata = updates.metadata;
23026
+ if (updates.title !== void 0) {
23027
+ registeredResourceTemplate.title = updates.title;
23028
+ registeredResourceTemplate.metadata = {
23029
+ ...registeredResourceTemplate.metadata,
23030
+ title: updates.title
23031
+ };
23032
+ }
22384
23033
  if (updates.callback !== void 0) registeredResourceTemplate.readCallback = updates.callback;
22385
23034
  if (updates.scopeChallenge !== void 0) registeredResourceTemplate.scopeChallenge = updates.scopeChallenge === null ? void 0 : updates.scopeChallenge;
22386
23035
  if (updates.enabled !== void 0) registeredResourceTemplate.enabled = updates.enabled;
@@ -22654,7 +23303,228 @@ function unwrapOptionalSchema(schema) {
22654
23303
  if (!isOptionalSchema(schema)) return schema;
22655
23304
  return schema.def?.innerType ?? schema;
22656
23305
  }
22657
- // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
23306
+ // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/listenRouter-Bartj63y.mjs
23307
+ /**
23308
+ * Whether a `subscriptions/listen` filter accepts a given change event.
23309
+ *
23310
+ * Pure: no I/O, no mutation. The filter governs ONLY the four
23311
+ * subscription-gated change types — non-gated notifications never reach the
23312
+ * bus and are not modeled here.
23313
+ *
23314
+ * `resource_updated` matches only when `resourceSubscriptions` is present and
23315
+ * contains the event's URI exactly (per the spec: "for these resource URIs").
23316
+ */
23317
+ function listenFilterAccepts(filter, event) {
23318
+ switch (event.kind) {
23319
+ case "tools_list_changed": return filter.toolsListChanged === true;
23320
+ case "prompts_list_changed": return filter.promptsListChanged === true;
23321
+ case "resources_list_changed": return filter.resourcesListChanged === true;
23322
+ case "resource_updated": return filter.resourceSubscriptions !== void 0 && filter.resourceSubscriptions.includes(event.uri);
23323
+ }
23324
+ }
23325
+ /**
23326
+ * The honored subset of a requested filter: keeps only the fields the client
23327
+ * explicitly opted in to (drops `false` and absent fields), narrowed against
23328
+ * the server's declared capabilities when supplied. The serving entry sends
23329
+ * this back in `notifications/subscriptions/acknowledged` so the ack reflects
23330
+ * what the server can actually deliver.
23331
+ *
23332
+ * - `toolsListChanged` is honored only when `capabilities.tools.listChanged`
23333
+ * is advertised; likewise `promptsListChanged` / `resourcesListChanged`.
23334
+ * - `resourceSubscriptions` is honored only when
23335
+ * `capabilities.resources.subscribe` is advertised.
23336
+ *
23337
+ * `capabilities` is optional on this pure helper for test convenience only —
23338
+ * both wired routers REQUIRE capabilities at the call site (the HTTP router's
23339
+ * `serve()` takes a required parameter; `StdioListenRouter.serve()` throws
23340
+ * before `setServerCapabilities()` was called), so the fail-open
23341
+ * `undefined → honor everything` branch is never reachable on a wired entry.
23342
+ */
23343
+ function honoredSubset(requested, capabilities) {
23344
+ const honored = {};
23345
+ const allow = (bit) => capabilities === void 0 || bit === true;
23346
+ if (requested.toolsListChanged === true && allow(capabilities?.tools?.listChanged)) honored.toolsListChanged = true;
23347
+ if (requested.promptsListChanged === true && allow(capabilities?.prompts?.listChanged)) honored.promptsListChanged = true;
23348
+ if (requested.resourcesListChanged === true && allow(capabilities?.resources?.listChanged)) honored.resourcesListChanged = true;
23349
+ if (requested.resourceSubscriptions !== void 0 && requested.resourceSubscriptions.length > 0 && allow(capabilities?.resources?.subscribe)) honored.resourceSubscriptions = [...requested.resourceSubscriptions];
23350
+ return honored;
23351
+ }
23352
+ /** Default capacity guard: refuse a new subscription when this many are already open. */
23353
+ const DEFAULT_MAX_SUBSCRIPTIONS = 1024;
23354
+ /** Stamp the subscription id onto a notification's `_meta`. Non-mutating. */
23355
+ function stampSubscriptionId(notification, subscriptionId) {
23356
+ return {
23357
+ method: notification.method,
23358
+ params: {
23359
+ ...notification.params,
23360
+ _meta: {
23361
+ ...notification.params?._meta,
23362
+ [SUBSCRIPTION_ID_META_KEY]: subscriptionId
23363
+ }
23364
+ }
23365
+ };
23366
+ }
23367
+ /**
23368
+ * Read the requested filter off a `subscriptions/listen` request body.
23369
+ * Returns the validated filter, or `undefined` when `params.notifications`
23370
+ * is absent or fails the schema (the caller answers `-32602` — the spec
23371
+ * marks `notifications` REQUIRED on the listen request).
23372
+ */
23373
+ function parseListenFilter(message) {
23374
+ const outcome = codecForVersion(MODERN_WIRE_REVISION).validateRequest("subscriptions/listen", message);
23375
+ return outcome.ok ? outcome.value.params?.notifications : void 0;
23376
+ }
23377
+ const CHANGE_NOTIFICATION_METHODS = /* @__PURE__ */ new Set([
23378
+ "notifications/tools/list_changed",
23379
+ "notifications/prompts/list_changed",
23380
+ "notifications/resources/list_changed",
23381
+ "notifications/resources/updated"
23382
+ ]);
23383
+ /**
23384
+ * Per-connection listen state for the stdio entry. One instance is held by
23385
+ * `serveStdio` for the connection lifetime; it routes inbound
23386
+ * `subscriptions/listen` / `notifications/cancelled` and rewrites outbound
23387
+ * change notifications onto the active subscriptions. No bus — the long-lived
23388
+ * pinned instance's existing `send*ListChanged()` calls feed straight into
23389
+ * `routeOutbound()`.
23390
+ */
23391
+ var StdioListenRouter = class {
23392
+ /** Active subscriptions, keyed by the listen request's JSON-RPC id verbatim. */
23393
+ _subs = /* @__PURE__ */ new Map();
23394
+ /**
23395
+ * The serving instance's declared capabilities. Filled in by the entry
23396
+ * once the modern instance is constructed (the router is created before
23397
+ * the instance exists), so the acknowledged filter is narrowed against
23398
+ * what the server can actually deliver.
23399
+ */
23400
+ _serverCapabilities;
23401
+ /**
23402
+ * The serving instance's identity, stamped onto the graceful-close
23403
+ * results' `_meta` (the spec's `SubscriptionsListenResultMetaObject` extends
23404
+ * `ResultMetaObject`). Handed over together with the capabilities.
23405
+ */
23406
+ _serverInfo;
23407
+ constructor(_maxSubscriptions = DEFAULT_MAX_SUBSCRIPTIONS, serverCapabilities, serverInfo) {
23408
+ this._maxSubscriptions = _maxSubscriptions;
23409
+ this._serverCapabilities = serverCapabilities;
23410
+ this._serverInfo = serverInfo;
23411
+ }
23412
+ /**
23413
+ * Record the serving instance's declared capabilities and identity once
23414
+ * it has been constructed. Called by `serveStdio`'s connect path;
23415
+ * subsequent `serve()` calls narrow the honored filter against the
23416
+ * capabilities, and `teardownAll()` stamps the identity.
23417
+ */
23418
+ setServerCapabilities(capabilities, serverInfo) {
23419
+ this._serverCapabilities = capabilities;
23420
+ if (serverInfo !== void 0) this._serverInfo = serverInfo;
23421
+ }
23422
+ /** Whether `id` is an active listen subscription on this connection. */
23423
+ has(id) {
23424
+ return this._subs.has(id);
23425
+ }
23426
+ /**
23427
+ * Serve one inbound `subscriptions/listen` request: registers the
23428
+ * subscription and returns the stamped acknowledged notification (or, on
23429
+ * capacity / params rejection, the in-band JSON-RPC error response).
23430
+ *
23431
+ * @throws when called before {@linkcode setServerCapabilities} (or the
23432
+ * constructor) has supplied the serving instance's capabilities. Honoring a
23433
+ * filter without knowing the server's advertised capabilities would fail
23434
+ * open (deliver unadvertised types); the entry guarantees capabilities are
23435
+ * set before any listen request is routed here.
23436
+ */
23437
+ serve(message) {
23438
+ if (this._serverCapabilities === void 0) throw new Error("StdioListenRouter.serve() called before setServerCapabilities(); refusing to honor a filter without capabilities");
23439
+ if (this._subs.size >= this._maxSubscriptions) return {
23440
+ jsonrpc: "2.0",
23441
+ id: message.id,
23442
+ error: {
23443
+ code: -32603,
23444
+ message: "Subscription limit reached"
23445
+ }
23446
+ };
23447
+ const filter = parseListenFilter(message);
23448
+ if (filter === void 0) return {
23449
+ jsonrpc: "2.0",
23450
+ id: message.id,
23451
+ error: {
23452
+ code: -32602,
23453
+ message: "Invalid params: 'notifications' is required and must be a valid SubscriptionFilter"
23454
+ }
23455
+ };
23456
+ const honored = honoredSubset(filter, this._serverCapabilities);
23457
+ this._subs.set(message.id, honored);
23458
+ return stampSubscriptionId({
23459
+ method: "notifications/subscriptions/acknowledged",
23460
+ params: { notifications: honored }
23461
+ }, message.id);
23462
+ }
23463
+ /**
23464
+ * Tear down one subscription (inbound `notifications/cancelled`). Returns
23465
+ * `true` when a subscription was removed. After this call NOTHING further
23466
+ * is delivered for that subscription id (the post-cancel hardening).
23467
+ */
23468
+ cancel(id) {
23469
+ return this._subs.delete(id);
23470
+ }
23471
+ /**
23472
+ * Route an outbound notification through the active subscriptions.
23473
+ *
23474
+ * - For a subscription-gated change notification, returns one stamped copy
23475
+ * per subscription that opted in to it (an empty array means it is
23476
+ * dropped — the modern era never delivers an un-requested change type).
23477
+ * - For any other outbound message, returns `'passthrough'` (the entry
23478
+ * forwards it as-is).
23479
+ */
23480
+ routeOutbound(message) {
23481
+ if (!CHANGE_NOTIFICATION_METHODS.has(message.method)) return "passthrough";
23482
+ const uriParam = message.params?.["uri"];
23483
+ const uri = typeof uriParam === "string" ? uriParam : void 0;
23484
+ const event = notificationToServerEvent(message.method, uri);
23485
+ const out = [];
23486
+ for (const [subscriptionId, filter] of this._subs) if (listenFilterAccepts(filter, event)) out.push(stampSubscriptionId({
23487
+ method: message.method,
23488
+ params: message.params ?? {}
23489
+ }, subscriptionId));
23490
+ return out;
23491
+ }
23492
+ /**
23493
+ * Server-side graceful teardown of every active subscription: returns the
23494
+ * empty `subscriptions/listen` JSON-RPC result for each subscription id —
23495
+ * the spec's graceful-close signal, `_meta` carrying the subscription id
23496
+ * and the serving instance's identity — for the entry to emit before
23497
+ * closing the wire. Clears the set so nothing further is delivered.
23498
+ */
23499
+ teardownAll() {
23500
+ const out = [];
23501
+ for (const id of this._subs.keys()) out.push({
23502
+ jsonrpc: "2.0",
23503
+ id,
23504
+ result: {
23505
+ resultType: "complete",
23506
+ _meta: {
23507
+ [SUBSCRIPTION_ID_META_KEY]: id,
23508
+ ...this._serverInfo !== void 0 && { ["io.modelcontextprotocol/serverInfo"]: this._serverInfo }
23509
+ }
23510
+ }
23511
+ });
23512
+ this._subs.clear();
23513
+ return out;
23514
+ }
23515
+ };
23516
+ function notificationToServerEvent(method, uri) {
23517
+ switch (method) {
23518
+ case "notifications/tools/list_changed": return { kind: "tools_list_changed" };
23519
+ case "notifications/prompts/list_changed": return { kind: "prompts_list_changed" };
23520
+ case "notifications/resources/list_changed": return { kind: "resources_list_changed" };
23521
+ default: return {
23522
+ kind: "resource_updated",
23523
+ uri: uri ?? ""
23524
+ };
23525
+ }
23526
+ }
23527
+ // ../../node_modules/.pnpm/@modelcontextprotocol+server@2.4.0/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
22658
23528
  /**
22659
23529
  * Marks a stdout `'error'` listener that a closed transport left attached purely to
22660
23530
  * swallow late write failures (see {@link StdioServerTransport.close}). A fresh
@@ -23233,7 +24103,7 @@ function toError(value) {
23233
24103
  return value instanceof Error ? value : new Error(String(value));
23234
24104
  }
23235
24105
  // package.json
23236
- var version = "0.26.1";
24106
+ var version = "0.26.2";
23237
24107
  // ../fountain/src/inline.ts
23238
24108
  const MARKERS$1 = /[*_\\]/;
23239
24109
  const isSpace$10 = (code) => code === void 0 || /\s/.test(String.fromCharCode(code));
@@ -39512,7 +40382,7 @@ export {};
39512
40382
  * with the code of the packages below inside it. Each one’s licence follows, in full.
39513
40383
  *
39514
40384
  * ----------------------------------------------------------------------
39515
- * @modelcontextprotocol/core 2.3.1 (Apache-2.0)
40385
+ * @modelcontextprotocol/core 2.4.0 (Apache-2.0)
39516
40386
  *
39517
40387
  * The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
39518
40388
  *
@@ -39732,7 +40602,7 @@ export {};
39732
40602
  * the full license text.
39733
40603
  *
39734
40604
  * ----------------------------------------------------------------------
39735
- * @modelcontextprotocol/server 2.3.1 (Apache-2.0)
40605
+ * @modelcontextprotocol/server 2.4.0 (Apache-2.0)
39736
40606
  *
39737
40607
  * The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
39738
40608
  *
@@ -40053,14 +40923,12 @@ export {};
40053
40923
  * SOFTWARE.
40054
40924
  *
40055
40925
  * ----------------------------------------------------------------------
40056
- * fast-uri 3.1.0 (BSD-3-Clause), carried inside @modelcontextprotocol/server
40926
+ * fast-uri 3.1.8 (BSD-3-Clause), carried inside @modelcontextprotocol/server
40057
40927
  *
40058
40928
  * Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae
40059
- * Copyright (c) 2021-present The Fastify team
40929
+ * Copyright (c) 2021-present The Fastify team <https://github.com/fastify/fastify#team>
40060
40930
  * All rights reserved.
40061
40931
  *
40062
- * The Fastify team members are listed at https://github.com/fastify/fastify#team.
40063
- *
40064
40932
  * Redistribution and use in source and binary forms, with or without
40065
40933
  * modification, are permitted provided that the following conditions are met:
40066
40934
  * * Redistributions of source code must retain the above copyright