mcp-from-openapi 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -30,12 +30,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ArazzoError: () => ArazzoError,
33
34
  BLOCKED_HOSTNAMES: () => BLOCKED_HOSTNAMES,
34
35
  BUILTIN_FORMAT_RESOLVERS: () => BUILTIN_FORMAT_RESOLVERS,
36
+ CODECALL_RESERVED_NAMESPACES: () => CODECALL_RESERVED_NAMESPACES,
35
37
  GenerationError: () => GenerationError,
36
38
  LoadError: () => LoadError,
37
39
  OpenAPIToolError: () => OpenAPIToolError,
38
40
  OpenAPIToolGenerator: () => OpenAPIToolGenerator,
41
+ OverlayError: () => OverlayError,
39
42
  ParameterResolver: () => ParameterResolver,
40
43
  ParseError: () => ParseError,
41
44
  RequestBuildError: () => RequestBuildError,
@@ -46,7 +49,9 @@ __export(index_exports, {
46
49
  SsrfError: () => SsrfError,
47
50
  ValidationError: () => ValidationError,
48
51
  Validator: () => Validator,
52
+ analyzeToolSet: () => analyzeToolSet,
49
53
  applyClientTarget: () => applyClientTarget,
54
+ applyOverlay: () => applyOverlay,
50
55
  assertUrlSafe: () => assertUrlSafe,
51
56
  buildHttpRequest: () => buildHttpRequest,
52
57
  collapseNestedUnions: () => collapseNestedUnions,
@@ -55,20 +60,28 @@ __export(index_exports, {
55
60
  decodeIpv4MappedIpv6: () => decodeIpv4MappedIpv6,
56
61
  defaultLookup: () => defaultLookup,
57
62
  demoteFormats: () => demoteFormats,
63
+ deriveSecurityElicitations: () => deriveSecurityElicitations,
64
+ dottedNaming: () => dottedNaming,
65
+ emitToolTypeScript: () => emitToolTypeScript,
58
66
  enforceClosedObjects: () => enforceClosedObjects,
59
67
  ensureArrayItems: () => ensureArrayItems,
68
+ estimateToolTokens: () => estimateToolTokens,
60
69
  extractExtensionOverrides: () => extractExtensionOverrides,
70
+ fromArazzo: () => fromArazzo,
61
71
  inferAnnotationsFromMethod: () => inferAnnotationsFromMethod,
62
72
  inlineLocalRefs: () => inlineLocalRefs,
63
73
  isBlockedAddress: () => isBlockedAddress,
64
74
  isBlockedHostname: () => isBlockedHostname,
65
75
  isReferenceObject: () => isReferenceObject,
76
+ lintDocument: () => lintDocument,
66
77
  normalizeSsrfOptions: () => normalizeSsrfOptions,
78
+ parseRuntimeExpression: () => parseRuntimeExpression,
67
79
  requireAllProperties: () => requireAllProperties,
68
80
  resolveExtensionEnabled: () => resolveExtensionEnabled,
69
81
  resolveSchemaFormats: () => resolveSchemaFormats,
70
82
  safeFetch: () => safeFetch,
71
83
  toJsonSchema: () => toJsonSchema,
84
+ toPascalIdentifier: () => toPascalIdentifier,
72
85
  toSdkTool: () => toSdkTool
73
86
  });
74
87
  module.exports = __toCommonJS(index_exports);
@@ -81,9 +94,24 @@ function isReferenceObject(obj) {
81
94
  return obj && typeof obj === "object" && "$ref" in obj;
82
95
  }
83
96
  function toJsonSchema(schema) {
97
+ return convertSchema(schema, /* @__PURE__ */ new Set());
98
+ }
99
+ function convertSchema(schema, stack) {
84
100
  if (isReferenceObject(schema)) {
85
101
  return { $ref: schema.$ref };
86
102
  }
103
+ if (stack.has(schema)) {
104
+ return {};
105
+ }
106
+ stack.add(schema);
107
+ try {
108
+ return convertSchemaInner(schema, stack);
109
+ } finally {
110
+ stack.delete(schema);
111
+ }
112
+ }
113
+ function convertSchemaInner(schema, stack) {
114
+ const recurse = (value) => convertSchema(value, stack);
87
115
  const { exclusiveMaximum, exclusiveMinimum, maximum, minimum, ...rest } = schema;
88
116
  const { nullable, example, ...cleanRest } = rest;
89
117
  const result = { ...cleanRest };
@@ -135,34 +163,34 @@ function toJsonSchema(schema) {
135
163
  if (result["properties"] && typeof result["properties"] === "object") {
136
164
  const props = {};
137
165
  for (const [key, value] of Object.entries(result["properties"])) {
138
- props[key] = toJsonSchema(value);
166
+ props[key] = recurse(value);
139
167
  }
140
168
  result["properties"] = props;
141
169
  }
142
170
  if (result["items"]) {
143
171
  if (Array.isArray(result["items"])) {
144
- result["items"] = result["items"].map(toJsonSchema);
172
+ result["items"] = result["items"].map(recurse);
145
173
  } else {
146
- result["items"] = toJsonSchema(result["items"]);
174
+ result["items"] = recurse(result["items"]);
147
175
  }
148
176
  }
149
177
  if (result["additionalProperties"] && typeof result["additionalProperties"] === "object") {
150
- result["additionalProperties"] = toJsonSchema(result["additionalProperties"]);
178
+ result["additionalProperties"] = recurse(result["additionalProperties"]);
151
179
  }
152
180
  for (const key of ["allOf", "anyOf", "oneOf"]) {
153
181
  if (result[key] && Array.isArray(result[key])) {
154
- result[key] = result[key].map(toJsonSchema);
182
+ result[key] = result[key].map(recurse);
155
183
  }
156
184
  }
157
185
  if (result["not"]) {
158
- result["not"] = toJsonSchema(result["not"]);
186
+ result["not"] = recurse(result["not"]);
159
187
  }
160
188
  for (const key of ["patternProperties", "$defs", "definitions", "dependentSchemas"]) {
161
189
  const value = result[key];
162
190
  if (value && typeof value === "object" && !Array.isArray(value)) {
163
191
  const mapped = {};
164
192
  for (const [name, sub] of Object.entries(value)) {
165
- mapped[name] = toJsonSchema(sub);
193
+ mapped[name] = recurse(sub);
166
194
  }
167
195
  result[key] = mapped;
168
196
  }
@@ -179,11 +207,11 @@ function toJsonSchema(schema) {
179
207
  ]) {
180
208
  const value = result[key];
181
209
  if (value && typeof value === "object") {
182
- result[key] = toJsonSchema(value);
210
+ result[key] = recurse(value);
183
211
  }
184
212
  }
185
213
  if (Array.isArray(result["prefixItems"])) {
186
- result["prefixItems"] = result["prefixItems"].map(toJsonSchema);
214
+ result["prefixItems"] = result["prefixItems"].map(recurse);
187
215
  }
188
216
  if (wrapNullable) {
189
217
  const wrapper = {};
@@ -204,8 +232,11 @@ var ParameterResolver = class {
204
232
  namingStrategy;
205
233
  includeExamples;
206
234
  constructor(namingStrategy, options) {
207
- this.namingStrategy = namingStrategy ?? {
208
- conflictResolver: this.defaultConflictResolver
235
+ this.namingStrategy = {
236
+ ...namingStrategy,
237
+ // Bind a supplied resolver to its own strategy object so class-based
238
+ // strategies keep their `this` (we invoke it off a spread clone).
239
+ conflictResolver: namingStrategy?.conflictResolver ? namingStrategy.conflictResolver.bind(namingStrategy) : this.defaultConflictResolver
209
240
  };
210
241
  this.includeExamples = options?.includeExamples ?? false;
211
242
  }
@@ -387,6 +418,9 @@ var ParameterResolver = class {
387
418
  schema["deprecated"] = true;
388
419
  }
389
420
  schema["x-parameter-location"] = param.location;
421
+ if (param.location === "header") {
422
+ schema["x-mcp-header"] = param.name;
423
+ }
390
424
  if (param.style) {
391
425
  schema["x-parameter-style"] = param.style;
392
426
  }
@@ -481,6 +515,9 @@ var ParameterResolver = class {
481
515
  });
482
516
  const schemeInInput = includeInInput === true || Array.isArray(includeInInput) && includeInInput.includes(scheme);
483
517
  if (schemeInInput) {
518
+ if (paramLocation === "header") {
519
+ schema["x-mcp-header"] = headerKey;
520
+ }
484
521
  properties[inputKey] = schema;
485
522
  required.push(inputKey);
486
523
  }
@@ -1005,6 +1042,96 @@ var SchemaBuilder = class {
1005
1042
  }
1006
1043
  return copy;
1007
1044
  }
1045
+ // Copy-on-walk over every structural keyword (same key groups as
1046
+ // truncateDepth): `visit` transforms each node top-down and must return a
1047
+ // new node when it changes anything.
1048
+ static walkCopy(node, visit, seen = /* @__PURE__ */ new Map()) {
1049
+ if (!node || typeof node !== "object") return node;
1050
+ const existing = seen.get(node);
1051
+ if (existing) return existing;
1052
+ const copy = visit({ ...node });
1053
+ seen.set(node, copy);
1054
+ for (const key of this.TRUNCATE_MAP_KEYS) {
1055
+ const value = copy[key];
1056
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
1057
+ const mapped = {};
1058
+ for (const [name, sub] of Object.entries(value)) {
1059
+ mapped[name] = this.walkCopy(sub, visit, seen);
1060
+ }
1061
+ copy[key] = mapped;
1062
+ }
1063
+ }
1064
+ for (const key of this.TRUNCATE_SCHEMA_KEYS) {
1065
+ const value = copy[key];
1066
+ if (Array.isArray(value)) {
1067
+ copy[key] = value.map((item) => this.walkCopy(item, visit, seen));
1068
+ } else if (value !== null && typeof value === "object") {
1069
+ copy[key] = this.walkCopy(value, visit, seen);
1070
+ }
1071
+ }
1072
+ for (const key of this.TRUNCATE_LIST_KEYS) {
1073
+ const value = copy[key];
1074
+ if (Array.isArray(value)) {
1075
+ copy[key] = value.map((member) => this.walkCopy(member, visit, seen));
1076
+ }
1077
+ }
1078
+ return copy;
1079
+ }
1080
+ /**
1081
+ * Limit every object node to its first `max` properties (declaration
1082
+ * order). Dropped properties are pruned from `required` and counted in a
1083
+ * note appended to the node's description.
1084
+ */
1085
+ static limitProperties(schema, max) {
1086
+ const bound = Number.isFinite(max) ? Math.max(1, Math.floor(max)) : Number.MAX_SAFE_INTEGER;
1087
+ return this.walkCopy(schema, (node) => {
1088
+ const properties = node.properties;
1089
+ if (!properties || typeof properties !== "object") return node;
1090
+ const entries = Object.entries(properties);
1091
+ if (entries.length <= bound) return node;
1092
+ const kept = entries.slice(0, bound);
1093
+ const keptNames = new Set(kept.map(([name]) => name));
1094
+ const dropped = entries.length - bound;
1095
+ const note = `[${dropped} additional propert${dropped === 1 ? "y" : "ies"} omitted: exceeds maxProperties]`;
1096
+ const next = { ...node, properties: Object.fromEntries(kept) };
1097
+ if (Array.isArray(node.required)) {
1098
+ const required = node.required.filter((name) => keptNames.has(String(name)));
1099
+ if (required.length > 0) {
1100
+ next.required = required;
1101
+ } else {
1102
+ delete next.required;
1103
+ }
1104
+ }
1105
+ next.description = node.description ? `${node.description} ${note}` : note;
1106
+ return next;
1107
+ });
1108
+ }
1109
+ /**
1110
+ * Cap every description in the schema tree to `maxLength` characters,
1111
+ * truncating with an ellipsis.
1112
+ */
1113
+ static capDescriptions(schema, maxLength) {
1114
+ const bound = Number.isFinite(maxLength) ? Math.max(1, Math.floor(maxLength)) : Number.MAX_SAFE_INTEGER;
1115
+ return this.walkCopy(schema, (node) => {
1116
+ if (typeof node.description === "string" && node.description.length > bound) {
1117
+ return { ...node, description: `${node.description.slice(0, bound - 1)}\u2026` };
1118
+ }
1119
+ return node;
1120
+ });
1121
+ }
1122
+ /**
1123
+ * Remove every `examples` array from the schema tree (a token-budget
1124
+ * trimming step — validation keywords are untouched).
1125
+ */
1126
+ static stripExamples(schema) {
1127
+ return this.walkCopy(schema, (node) => {
1128
+ if ("examples" in node) {
1129
+ const { examples: _examples, ...rest } = node;
1130
+ return rest;
1131
+ }
1132
+ return node;
1133
+ });
1134
+ }
1008
1135
  /**
1009
1136
  * Simplify schema by removing unnecessary fields
1010
1137
  */
@@ -1063,9 +1190,63 @@ function mergeOverrides(base, layer) {
1063
1190
  ...layer.description !== void 0 && { description: layer.description },
1064
1191
  ...(base.annotations || layer.annotations) && {
1065
1192
  annotations: { ...base.annotations, ...layer.annotations }
1066
- }
1193
+ },
1194
+ ...(base.meta || layer.meta) && { meta: { ...base.meta, ...layer.meta } },
1195
+ ...layer.icons !== void 0 && { icons: layer.icons }
1067
1196
  };
1068
1197
  }
1198
+ function cleanseMeta(node, seen) {
1199
+ if (!node || typeof node !== "object") {
1200
+ return node;
1201
+ }
1202
+ if (seen.has(node)) {
1203
+ return void 0;
1204
+ }
1205
+ seen.add(node);
1206
+ try {
1207
+ if (Array.isArray(node)) {
1208
+ return node.map((item) => cleanseMeta(item, seen));
1209
+ }
1210
+ const out = {};
1211
+ for (const [key, value] of Object.entries(node)) {
1212
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
1213
+ out[key] = cleanseMeta(value, seen);
1214
+ }
1215
+ return out;
1216
+ } finally {
1217
+ seen.delete(node);
1218
+ }
1219
+ }
1220
+ function sanitizeMeta(value) {
1221
+ if (value && typeof value === "object" && !Array.isArray(value)) {
1222
+ return cleanseMeta(value, /* @__PURE__ */ new Set());
1223
+ }
1224
+ return void 0;
1225
+ }
1226
+ function isAllowedIconSrc(src) {
1227
+ const lower = src.toLowerCase();
1228
+ return lower.startsWith("https:") || lower.startsWith("data:");
1229
+ }
1230
+ function sanitizeIcons(value) {
1231
+ if (!Array.isArray(value)) {
1232
+ return void 0;
1233
+ }
1234
+ const icons = [];
1235
+ for (const entry of value) {
1236
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
1237
+ const raw = entry;
1238
+ if (typeof raw["src"] !== "string" || !isAllowedIconSrc(raw["src"])) continue;
1239
+ const icon = { src: raw["src"] };
1240
+ if (typeof raw["mimeType"] === "string") {
1241
+ icon.mimeType = raw["mimeType"];
1242
+ }
1243
+ if (Array.isArray(raw["sizes"]) && raw["sizes"].every((s) => typeof s === "string")) {
1244
+ icon.sizes = [...raw["sizes"]];
1245
+ }
1246
+ icons.push(icon);
1247
+ }
1248
+ return icons.length > 0 ? icons : void 0;
1249
+ }
1069
1250
  function readXMcp(node) {
1070
1251
  return node["x-mcp"];
1071
1252
  }
@@ -1114,16 +1295,24 @@ function extractExtensionOverrides(operation) {
1114
1295
  name: typeof ext["name"] === "string" ? ext["name"] : void 0,
1115
1296
  title: typeof ext["title"] === "string" ? ext["title"] : void 0,
1116
1297
  description: typeof ext["description"] === "string" ? ext["description"] : void 0,
1117
- annotations: pickAnnotations(ext["annotations"])
1298
+ annotations: pickAnnotations(ext["annotations"]),
1299
+ meta: sanitizeMeta(ext["meta"]),
1300
+ icons: sanitizeIcons(ext["icons"])
1118
1301
  });
1119
1302
  }
1120
1303
  const frontmcp = op["x-frontmcp"];
1121
- if (frontmcp && typeof frontmcp === "object" && frontmcp.annotations) {
1122
- const annotations = pickAnnotations(frontmcp.annotations);
1123
- result = mergeOverrides(result, {
1124
- annotations,
1125
- title: typeof frontmcp.annotations.title === "string" ? frontmcp.annotations.title : void 0
1126
- });
1304
+ if (frontmcp && typeof frontmcp === "object") {
1305
+ const layer = {
1306
+ meta: sanitizeMeta(frontmcp.meta),
1307
+ icons: sanitizeIcons(frontmcp.icons)
1308
+ };
1309
+ if (frontmcp.annotations) {
1310
+ layer.annotations = pickAnnotations(frontmcp.annotations);
1311
+ if (typeof frontmcp.annotations.title === "string") {
1312
+ layer.title = frontmcp.annotations.title;
1313
+ }
1314
+ }
1315
+ result = mergeOverrides(result, layer);
1127
1316
  }
1128
1317
  return result;
1129
1318
  }
@@ -1445,6 +1634,564 @@ function applyClientTarget(schema, target) {
1445
1634
  return result;
1446
1635
  }
1447
1636
 
1637
+ // src/errors.ts
1638
+ var OpenAPIToolError = class extends Error {
1639
+ context;
1640
+ constructor(message, context) {
1641
+ super(message);
1642
+ this.name = this.constructor.name;
1643
+ this.context = context;
1644
+ if (Error.captureStackTrace) {
1645
+ Error.captureStackTrace(this, this.constructor);
1646
+ }
1647
+ }
1648
+ };
1649
+ var LoadError = class extends OpenAPIToolError {
1650
+ constructor(message, context) {
1651
+ super(message, context);
1652
+ }
1653
+ };
1654
+ var SsrfError = class extends LoadError {
1655
+ constructor(message, context) {
1656
+ super(message, context);
1657
+ }
1658
+ };
1659
+ var ParseError = class extends OpenAPIToolError {
1660
+ constructor(message, context) {
1661
+ super(message, context);
1662
+ }
1663
+ };
1664
+ var ValidationError = class extends OpenAPIToolError {
1665
+ errors;
1666
+ constructor(message, context) {
1667
+ super(message, context);
1668
+ this.errors = context?.["errors"];
1669
+ }
1670
+ };
1671
+ var GenerationError = class extends OpenAPIToolError {
1672
+ constructor(message, context) {
1673
+ super(message, context);
1674
+ }
1675
+ };
1676
+ var OverlayError = class extends OpenAPIToolError {
1677
+ constructor(message, context) {
1678
+ super(message, context);
1679
+ }
1680
+ };
1681
+ var RequestBuildError = class extends OpenAPIToolError {
1682
+ constructor(message, context) {
1683
+ super(message, context);
1684
+ }
1685
+ };
1686
+ var ArazzoError = class extends OpenAPIToolError {
1687
+ path;
1688
+ constructor(message, context) {
1689
+ super(message, context);
1690
+ this.path = context?.["path"];
1691
+ }
1692
+ };
1693
+ var SchemaError = class extends OpenAPIToolError {
1694
+ constructor(message, context) {
1695
+ super(message, context);
1696
+ }
1697
+ };
1698
+
1699
+ // src/overlay.ts
1700
+ function parsePath(path) {
1701
+ if (typeof path !== "string" || !path.startsWith("$")) {
1702
+ throw new OverlayError(`Overlay target must be a JSONPath starting with '$'; received '${String(path)}'`, {
1703
+ target: path
1704
+ });
1705
+ }
1706
+ const segments = [];
1707
+ let rest = path.slice(1);
1708
+ while (rest.length > 0) {
1709
+ let recursive = false;
1710
+ if (rest.startsWith("..")) {
1711
+ recursive = true;
1712
+ rest = rest.slice(2);
1713
+ const bare = rest.match(/^([A-Za-z_][\w-]*)/);
1714
+ if (bare) {
1715
+ segments.push({ kind: "child", name: bare[1], recursive });
1716
+ rest = rest.slice(bare[0].length);
1717
+ continue;
1718
+ }
1719
+ } else if (rest.startsWith(".")) {
1720
+ rest = rest.slice(1);
1721
+ if (rest.startsWith("*")) {
1722
+ segments.push({ kind: "wildcard", recursive });
1723
+ rest = rest.slice(1);
1724
+ continue;
1725
+ }
1726
+ const bare = rest.match(/^([A-Za-z_][\w-]*)/);
1727
+ if (bare) {
1728
+ segments.push({ kind: "child", name: bare[1], recursive });
1729
+ rest = rest.slice(bare[0].length);
1730
+ continue;
1731
+ }
1732
+ throw new OverlayError(`Invalid JSONPath segment after '.' in '${path}'`, { target: path });
1733
+ }
1734
+ if (!rest.startsWith("[")) {
1735
+ throw new OverlayError(`Invalid JSONPath segment at '${rest}' in '${path}'`, { target: path });
1736
+ }
1737
+ const bracket = matchBracket(rest, path);
1738
+ const inner = bracket.inner.trim();
1739
+ rest = bracket.rest;
1740
+ if (inner === "*") {
1741
+ segments.push({ kind: "wildcard", recursive });
1742
+ } else if (/^-?\d+$/.test(inner)) {
1743
+ segments.push({ kind: "index", index: parseInt(inner, 10), recursive });
1744
+ } else if (/^'.*'$/.test(inner) || /^".*"$/.test(inner)) {
1745
+ segments.push({ kind: "child", name: inner.slice(1, -1), recursive });
1746
+ } else if (inner.startsWith("?(") && inner.endsWith(")")) {
1747
+ segments.push(parseFilter(inner.slice(2, -1).trim(), path, recursive));
1748
+ } else {
1749
+ throw new OverlayError(`Unsupported JSONPath selector '[${inner}]' in '${path}'`, { target: path });
1750
+ }
1751
+ }
1752
+ return segments;
1753
+ }
1754
+ function matchBracket(input, fullPath) {
1755
+ let quote = null;
1756
+ let depth = 0;
1757
+ for (let i = 1; i < input.length; i++) {
1758
+ const char = input[i];
1759
+ if (quote) {
1760
+ if (char === quote) quote = null;
1761
+ } else if (char === "'" || char === '"') {
1762
+ quote = char;
1763
+ } else if (char === "[") {
1764
+ depth++;
1765
+ } else if (char === "]") {
1766
+ if (depth === 0) {
1767
+ return { inner: input.slice(1, i), rest: input.slice(i + 1) };
1768
+ }
1769
+ depth--;
1770
+ }
1771
+ }
1772
+ throw new OverlayError(`Unterminated '[' selector in '${fullPath}'`, { target: fullPath });
1773
+ }
1774
+ function parseFilter(expr, path, recursive) {
1775
+ const match = expr.match(/^@(?:\.([A-Za-z_][\w-]*)|\['([^']*)'\]|\["([^"]*)"\])\s*(?:(==|!=)\s*(.+))?$/);
1776
+ if (!match) {
1777
+ throw new OverlayError(`Unsupported filter expression '?(${expr})' in '${path}'`, { target: path });
1778
+ }
1779
+ const field = match[1] ?? match[2] ?? match[3];
1780
+ const op = match[4];
1781
+ if (!op) {
1782
+ return { kind: "filter", field, op: "exists", recursive };
1783
+ }
1784
+ const raw = match[5].trim();
1785
+ let literal;
1786
+ if (/^'.*'$/.test(raw) || /^".*"$/.test(raw)) {
1787
+ literal = raw.slice(1, -1);
1788
+ } else if (/^-?\d+(\.\d+)?$/.test(raw)) {
1789
+ literal = parseFloat(raw);
1790
+ } else if (raw === "true" || raw === "false") {
1791
+ literal = raw === "true";
1792
+ } else {
1793
+ throw new OverlayError(`Unsupported filter literal '${raw}' in '${path}'`, { target: path });
1794
+ }
1795
+ return { kind: "filter", field, op, literal, recursive };
1796
+ }
1797
+ function isContainer(value) {
1798
+ return value !== null && typeof value === "object";
1799
+ }
1800
+ var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1801
+ function descendants(match) {
1802
+ const result = [];
1803
+ const walk = (node) => {
1804
+ if (!isContainer(node)) return;
1805
+ if (Array.isArray(node)) {
1806
+ node.forEach((item, index) => {
1807
+ result.push({ parent: node, key: index, value: item });
1808
+ walk(item);
1809
+ });
1810
+ } else {
1811
+ for (const [key, value] of Object.entries(node)) {
1812
+ result.push({ parent: node, key, value });
1813
+ walk(value);
1814
+ }
1815
+ }
1816
+ };
1817
+ walk(match.value);
1818
+ return result;
1819
+ }
1820
+ function dedupeMatches(matches) {
1821
+ const seen = /* @__PURE__ */ new Map();
1822
+ const result = [];
1823
+ for (const match of matches) {
1824
+ let keys = seen.get(match.parent);
1825
+ if (!keys) {
1826
+ keys = /* @__PURE__ */ new Set();
1827
+ seen.set(match.parent, keys);
1828
+ }
1829
+ if (keys.has(match.key)) continue;
1830
+ keys.add(match.key);
1831
+ result.push(match);
1832
+ }
1833
+ return result;
1834
+ }
1835
+ function applySegment(matches, segment) {
1836
+ const scope = segment.recursive ? matches.flatMap((m) => [m, ...descendants(m)]) : matches;
1837
+ const next = [];
1838
+ for (const match of scope) {
1839
+ const node = match.value;
1840
+ switch (segment.kind) {
1841
+ case "child": {
1842
+ if (isContainer(node) && !Array.isArray(node) && !UNSAFE_KEYS.has(segment.name) && Object.prototype.hasOwnProperty.call(node, segment.name)) {
1843
+ next.push({ parent: node, key: segment.name, value: node[segment.name] });
1844
+ }
1845
+ break;
1846
+ }
1847
+ case "wildcard": {
1848
+ if (Array.isArray(node)) {
1849
+ node.forEach((item, index) => next.push({ parent: node, key: index, value: item }));
1850
+ } else if (isContainer(node)) {
1851
+ for (const [key, value] of Object.entries(node)) {
1852
+ next.push({ parent: node, key, value });
1853
+ }
1854
+ }
1855
+ break;
1856
+ }
1857
+ case "index": {
1858
+ if (Array.isArray(node)) {
1859
+ const index = segment.index < 0 ? node.length + segment.index : segment.index;
1860
+ if (index >= 0 && index < node.length) {
1861
+ next.push({ parent: node, key: index, value: node[index] });
1862
+ }
1863
+ }
1864
+ break;
1865
+ }
1866
+ case "filter": {
1867
+ const members = Array.isArray(node) ? node.map((item, index) => ({ parent: node, key: index, value: item })) : isContainer(node) ? Object.entries(node).map(([key, value]) => ({ parent: node, key, value })) : [];
1868
+ for (const member of members) {
1869
+ if (!isContainer(member.value) || Array.isArray(member.value)) continue;
1870
+ const fieldValue = member.value[segment.field];
1871
+ const keep = segment.op === "exists" ? fieldValue !== void 0 : segment.op === "==" ? fieldValue === segment.literal : fieldValue !== segment.literal;
1872
+ if (keep) next.push(member);
1873
+ }
1874
+ break;
1875
+ }
1876
+ }
1877
+ }
1878
+ return next;
1879
+ }
1880
+ function deepMerge(target, update) {
1881
+ for (const [key, value] of Object.entries(update)) {
1882
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
1883
+ const existing = target[key];
1884
+ if (isContainer(value) && !Array.isArray(value) && isContainer(existing) && !Array.isArray(existing)) {
1885
+ deepMerge(existing, value);
1886
+ } else {
1887
+ target[key] = value;
1888
+ }
1889
+ }
1890
+ }
1891
+ function applyOverlay(document, overlay) {
1892
+ if (!overlay || typeof overlay !== "object" || !Array.isArray(overlay.actions)) {
1893
+ throw new OverlayError("Overlay document must have an actions array", {});
1894
+ }
1895
+ const result = JSON.parse(JSON.stringify(document));
1896
+ for (const [index, action] of overlay.actions.entries()) {
1897
+ if (!action || typeof action !== "object" || typeof action.target !== "string") {
1898
+ throw new OverlayError(`Overlay action #${index} must have a string target`, { index });
1899
+ }
1900
+ if (action.update === void 0 && action.remove !== true) {
1901
+ throw new OverlayError(`Overlay action #${index} needs 'update' or 'remove: true'`, {
1902
+ index,
1903
+ target: action.target
1904
+ });
1905
+ }
1906
+ const segments = parsePath(action.target);
1907
+ let matches = [{ parent: null, key: null, value: result }];
1908
+ for (const segment of segments) {
1909
+ matches = dedupeMatches(applySegment(matches, segment));
1910
+ }
1911
+ if (action.remove === true) {
1912
+ const arrayRemovals = /* @__PURE__ */ new Map();
1913
+ for (const match of matches) {
1914
+ if (match.parent === null) {
1915
+ throw new OverlayError("Overlay cannot remove the document root", { target: action.target });
1916
+ }
1917
+ if (Array.isArray(match.parent)) {
1918
+ const indices = arrayRemovals.get(match.parent) ?? [];
1919
+ indices.push(match.key);
1920
+ arrayRemovals.set(match.parent, indices);
1921
+ } else {
1922
+ delete match.parent[match.key];
1923
+ }
1924
+ }
1925
+ for (const [parent, indices] of arrayRemovals) {
1926
+ for (const index2 of indices.sort((a, b) => b - a)) {
1927
+ parent.splice(index2, 1);
1928
+ }
1929
+ }
1930
+ continue;
1931
+ }
1932
+ for (const match of matches) {
1933
+ const node = match.value;
1934
+ if (Array.isArray(node)) {
1935
+ node.push(action.update);
1936
+ } else if (isContainer(node) && isContainer(action.update) && !Array.isArray(action.update)) {
1937
+ deepMerge(node, action.update);
1938
+ } else {
1939
+ if (match.parent === null) {
1940
+ throw new OverlayError("Overlay cannot replace the document root with a non-object", {
1941
+ target: action.target
1942
+ });
1943
+ }
1944
+ match.parent[match.key] = action.update;
1945
+ }
1946
+ }
1947
+ }
1948
+ return result;
1949
+ }
1950
+
1951
+ // src/lint.ts
1952
+ var METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
1953
+ var PAGINATION_PARAM = /^(page|limit|offset|cursor|per_page|pagesize|page_size|after|before)$/i;
1954
+ var DEEP_SCHEMA_THRESHOLD = 8;
1955
+ var WIDE_SCHEMA_THRESHOLD = 30;
1956
+ function measureSchema(node, seen = /* @__PURE__ */ new Map()) {
1957
+ if (node === null || typeof node !== "object") {
1958
+ return { depth: 0, widestObject: 0, hasArray: false };
1959
+ }
1960
+ if (seen.has(node)) {
1961
+ return seen.get(node) ?? { depth: 0, widestObject: 0, hasArray: false };
1962
+ }
1963
+ seen.set(node, null);
1964
+ const record = node;
1965
+ let childDepth = 0;
1966
+ let widestObject = 0;
1967
+ let hasArray = record["type"] === "array" || Array.isArray(record["type"]) && record["type"].includes("array");
1968
+ const visit = (child) => {
1969
+ const shape2 = measureSchema(child, seen);
1970
+ childDepth = Math.max(childDepth, shape2.depth);
1971
+ widestObject = Math.max(widestObject, shape2.widestObject);
1972
+ hasArray = hasArray || shape2.hasArray;
1973
+ };
1974
+ const properties = record["properties"];
1975
+ if (properties && typeof properties === "object") {
1976
+ widestObject = Math.max(widestObject, Object.keys(properties).length);
1977
+ for (const child of Object.values(properties)) visit(child);
1978
+ }
1979
+ for (const key of ["items", "additionalProperties", "not", "contentSchema"]) {
1980
+ const value = record[key];
1981
+ if (value && typeof value === "object" && !Array.isArray(value)) visit(value);
1982
+ if (Array.isArray(value)) value.forEach(visit);
1983
+ }
1984
+ for (const key of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
1985
+ const value = record[key];
1986
+ if (Array.isArray(value)) value.forEach(visit);
1987
+ }
1988
+ const shape = { depth: childDepth + 1, widestObject, hasArray };
1989
+ seen.set(node, shape);
1990
+ return shape;
1991
+ }
1992
+ function schemaHasExample(node, seen = /* @__PURE__ */ new Set()) {
1993
+ if (node === null || typeof node !== "object" || seen.has(node)) return false;
1994
+ seen.add(node);
1995
+ const record = node;
1996
+ if (record["example"] !== void 0 || record["examples"] !== void 0) return true;
1997
+ const properties = record["properties"];
1998
+ if (properties && typeof properties === "object") {
1999
+ if (Object.values(properties).some((child) => schemaHasExample(child, seen))) return true;
2000
+ }
2001
+ for (const key of ["items", "additionalProperties", "not", "contentSchema"]) {
2002
+ const value = record[key];
2003
+ if (value && typeof value === "object" && !Array.isArray(value) && schemaHasExample(value, seen)) return true;
2004
+ if (Array.isArray(value) && value.some((item) => schemaHasExample(item, seen))) return true;
2005
+ }
2006
+ for (const key of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
2007
+ const value = record[key];
2008
+ if (Array.isArray(value) && value.some((member) => schemaHasExample(member, seen))) return true;
2009
+ }
2010
+ return false;
2011
+ }
2012
+ function hasAnyExample(content) {
2013
+ if (!content) return false;
2014
+ return Object.values(content).some((media) => {
2015
+ if (!media || typeof media !== "object") return false;
2016
+ const record = media;
2017
+ if (record["example"] !== void 0 || record["examples"] !== void 0) return true;
2018
+ return schemaHasExample(record["schema"]);
2019
+ });
2020
+ }
2021
+ function lintDocument(document) {
2022
+ const findings = [];
2023
+ const operationIds = /* @__PURE__ */ new Map();
2024
+ const paths = document.paths ?? {};
2025
+ for (const [pathStr, pathItem] of Object.entries(paths).sort(([a], [b]) => a < b ? -1 : 1)) {
2026
+ if (!pathItem || "$ref" in pathItem) continue;
2027
+ const pathLevelParameters = (pathItem["parameters"] ?? []).filter(
2028
+ (param) => !isReferenceObject(param)
2029
+ );
2030
+ for (const method of METHODS) {
2031
+ const operation = pathItem[method];
2032
+ if (!operation) continue;
2033
+ const label = `${method.toUpperCase()} ${pathStr}`;
2034
+ if (!operation.operationId) {
2035
+ findings.push({
2036
+ severity: "warning",
2037
+ code: "missing-operation-id",
2038
+ message: "Operation has no operationId; the tool name will be generated from the method and path.",
2039
+ path: label,
2040
+ hint: "Add a short, action-oriented operationId (it becomes the tool name)."
2041
+ });
2042
+ } else {
2043
+ const existing = operationIds.get(operation.operationId) ?? [];
2044
+ existing.push(label);
2045
+ operationIds.set(operation.operationId, existing);
2046
+ if (operation.operationId.length > 64) {
2047
+ findings.push({
2048
+ severity: "info",
2049
+ code: "long-operation-id",
2050
+ message: `operationId '${operation.operationId.slice(0, 40)}\u2026' exceeds 64 characters and will be truncated with a hash suffix.`,
2051
+ path: label,
2052
+ hint: "Shorten the operationId below 64 characters to keep tool names readable."
2053
+ });
2054
+ }
2055
+ }
2056
+ const prose = `${operation.summary ?? ""} ${operation.description ?? ""}`.trim();
2057
+ if (prose.length === 0) {
2058
+ findings.push({
2059
+ severity: "warning",
2060
+ code: "missing-description",
2061
+ message: "Operation has neither summary nor description; the model only sees the method and path.",
2062
+ path: label,
2063
+ hint: "Describe WHEN to use this operation and what it returns (or patch it in with an overlay)."
2064
+ });
2065
+ } else if (prose.length < 20) {
2066
+ findings.push({
2067
+ severity: "info",
2068
+ code: "vague-description",
2069
+ message: `Operation description is only ${prose.length} characters \u2014 likely too vague for reliable tool selection.`,
2070
+ path: label,
2071
+ hint: "Expand the description with the use case and key parameters."
2072
+ });
2073
+ }
2074
+ const parameters = [
2075
+ ...pathLevelParameters,
2076
+ ...(operation.parameters ?? []).filter((param) => !isReferenceObject(param))
2077
+ ];
2078
+ const undescribed = parameters.filter((param) => !param.description).map((param) => param.name);
2079
+ if (undescribed.length > 0) {
2080
+ findings.push({
2081
+ severity: "info",
2082
+ code: "missing-parameter-description",
2083
+ message: `Parameter(s) without description: ${undescribed.join(", ")}.`,
2084
+ path: label,
2085
+ hint: "Describe each parameter \u2014 models mis-fill undocumented arguments."
2086
+ });
2087
+ }
2088
+ const responses = operation.responses ?? {};
2089
+ const successCodes = Object.keys(responses).filter((code) => /^2(\d\d|XX)$/i.test(code));
2090
+ if (successCodes.length === 0 && !responses["default"]) {
2091
+ findings.push({
2092
+ severity: "warning",
2093
+ code: "missing-success-response",
2094
+ message: "Operation declares no 2xx or default response; no output schema can be generated.",
2095
+ path: label,
2096
+ hint: "Add the success response with its schema."
2097
+ });
2098
+ }
2099
+ let responseShape = { depth: 0, widestObject: 0, hasArray: false };
2100
+ for (const code of [...successCodes, "default"]) {
2101
+ const response = responses[code];
2102
+ if (!response || typeof response !== "object" || isReferenceObject(response)) continue;
2103
+ const content = response["content"];
2104
+ if (!content) continue;
2105
+ for (const media of Object.values(content)) {
2106
+ const schema = media && typeof media === "object" ? media["schema"] : void 0;
2107
+ const shape = measureSchema(schema);
2108
+ responseShape = {
2109
+ depth: Math.max(responseShape.depth, shape.depth),
2110
+ widestObject: Math.max(responseShape.widestObject, shape.widestObject),
2111
+ hasArray: responseShape.hasArray || shape.hasArray
2112
+ };
2113
+ }
2114
+ }
2115
+ if (method === "get" && responseShape.hasArray) {
2116
+ const hasPagination = parameters.some((param) => param.in === "query" && PAGINATION_PARAM.test(param.name));
2117
+ if (!hasPagination) {
2118
+ findings.push({
2119
+ severity: "warning",
2120
+ code: "unpaginated-list",
2121
+ message: "GET returns an array but declares no pagination parameter \u2014 responses can blow past client result limits (Claude Code caps tool results at 25K tokens).",
2122
+ path: label,
2123
+ hint: "Add limit/cursor/page parameters, or shape responses at the server."
2124
+ });
2125
+ }
2126
+ }
2127
+ const body = operation.requestBody;
2128
+ const bodyContent = body && !isReferenceObject(body) ? body.content : void 0;
2129
+ let requestShape = { depth: 0, widestObject: 0, hasArray: false };
2130
+ for (const media of Object.values(bodyContent ?? {})) {
2131
+ const schema = media && typeof media === "object" ? media["schema"] : void 0;
2132
+ const shape = measureSchema(schema);
2133
+ requestShape = {
2134
+ depth: Math.max(requestShape.depth, shape.depth),
2135
+ widestObject: Math.max(requestShape.widestObject, shape.widestObject),
2136
+ hasArray: requestShape.hasArray || shape.hasArray
2137
+ };
2138
+ }
2139
+ const maxDepth = Math.max(requestShape.depth, responseShape.depth);
2140
+ if (maxDepth > DEEP_SCHEMA_THRESHOLD) {
2141
+ findings.push({
2142
+ severity: "warning",
2143
+ code: "deep-schema",
2144
+ message: `Schema nesting reaches depth ${maxDepth} (threshold ${DEEP_SCHEMA_THRESHOLD}) \u2014 deep schemas cost tokens and reduce accuracy.`,
2145
+ path: label,
2146
+ hint: "Flatten the schema, or bound generation with maxSchemaDepth."
2147
+ });
2148
+ }
2149
+ const maxWidth = Math.max(requestShape.widestObject, responseShape.widestObject);
2150
+ if (maxWidth > WIDE_SCHEMA_THRESHOLD) {
2151
+ findings.push({
2152
+ severity: "info",
2153
+ code: "wide-schema",
2154
+ message: `An object schema declares ${maxWidth} properties (threshold ${WIDE_SCHEMA_THRESHOLD}).`,
2155
+ path: label,
2156
+ hint: "Split the payload, or bound generation with maxProperties."
2157
+ });
2158
+ }
2159
+ if (bodyContent && !hasAnyExample(bodyContent)) {
2160
+ findings.push({
2161
+ severity: "info",
2162
+ code: "missing-request-example",
2163
+ message: "Request body has no example \u2014 examples measurably improve complex-parameter accuracy.",
2164
+ path: label,
2165
+ hint: "Add a media-type example (and enable includeExamples), or patch one in with an overlay."
2166
+ });
2167
+ }
2168
+ }
2169
+ }
2170
+ for (const [operationId, labels] of operationIds) {
2171
+ if (labels.length > 1) {
2172
+ findings.push({
2173
+ severity: "error",
2174
+ code: "duplicate-operation-id",
2175
+ message: `operationId '${operationId}' is used by ${labels.length} operations: ${labels.join(", ")}.`,
2176
+ path: labels[0],
2177
+ hint: "Make operationIds unique \u2014 duplicates force hash-suffixed tool names."
2178
+ });
2179
+ }
2180
+ }
2181
+ const rank = { error: 0, warning: 1, info: 2 };
2182
+ findings.sort(
2183
+ (a, b) => rank[a.severity] - rank[b.severity] || (a.path < b.path ? -1 : a.path > b.path ? 1 : 0) || (a.code < b.code ? -1 : 1)
2184
+ );
2185
+ return {
2186
+ findings,
2187
+ counts: {
2188
+ error: findings.filter((f) => f.severity === "error").length,
2189
+ warning: findings.filter((f) => f.severity === "warning").length,
2190
+ info: findings.filter((f) => f.severity === "info").length
2191
+ }
2192
+ };
2193
+ }
2194
+
1448
2195
  // src/validator.ts
1449
2196
  var Validator = class {
1450
2197
  /**
@@ -1495,7 +2242,8 @@ var Validator = class {
1495
2242
  code: "NO_PATHS"
1496
2243
  });
1497
2244
  } else {
1498
- this.validatePaths(document.paths, errors, warnings);
2245
+ const componentParameters = document.components?.parameters ?? {};
2246
+ this.validatePaths(document.paths, componentParameters, errors, warnings);
1499
2247
  }
1500
2248
  if (!document.servers || document.servers.length === 0) {
1501
2249
  warnings.push({
@@ -1526,7 +2274,18 @@ var Validator = class {
1526
2274
  /**
1527
2275
  * Validate paths
1528
2276
  */
1529
- validatePaths(paths, errors, warnings) {
2277
+ /**
2278
+ * Resolve a local `#/components/parameters/<name>` reference (JSON Pointer
2279
+ * tokens decoded). Returns undefined for external or dangling references.
2280
+ */
2281
+ resolveParameterRef(param, componentParameters) {
2282
+ if (!param || typeof param !== "object" || !("$ref" in param)) return param;
2283
+ const match = /^#\/components\/parameters\/(.+)$/.exec(String(param.$ref));
2284
+ if (!match) return void 0;
2285
+ const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
2286
+ return componentParameters[name];
2287
+ }
2288
+ validatePaths(paths, componentParameters, errors, warnings) {
1530
2289
  for (const [path, pathItem] of Object.entries(paths)) {
1531
2290
  if (!pathItem) continue;
1532
2291
  if (!path.startsWith("/")) {
@@ -1538,11 +2297,15 @@ var Validator = class {
1538
2297
  }
1539
2298
  const methods = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
1540
2299
  let hasOperations = false;
2300
+ const pathLevelParameters = Array.isArray(pathItem.parameters) ? pathItem.parameters : [];
2301
+ if (pathLevelParameters.length > 0) {
2302
+ this.validateParameters(pathLevelParameters, `/paths/${path}/parameters`, errors, warnings);
2303
+ }
1541
2304
  for (const method of methods) {
1542
2305
  const operation = pathItem[method];
1543
2306
  if (operation) {
1544
2307
  hasOperations = true;
1545
- this.validateOperation(operation, path, method, errors, warnings);
2308
+ this.validateOperation(operation, path, method, errors, warnings, pathLevelParameters, componentParameters);
1546
2309
  }
1547
2310
  }
1548
2311
  if (!hasOperations && !pathItem.$ref) {
@@ -1557,7 +2320,7 @@ var Validator = class {
1557
2320
  /**
1558
2321
  * Validate an operation
1559
2322
  */
1560
- validateOperation(operation, path, method, errors, warnings) {
2323
+ validateOperation(operation, path, method, errors, warnings, pathLevelParameters = [], componentParameters = {}) {
1561
2324
  const basePath = `/paths/${path}/${method}`;
1562
2325
  if (!operation.operationId) {
1563
2326
  warnings.push({
@@ -1574,14 +2337,18 @@ var Validator = class {
1574
2337
  });
1575
2338
  }
1576
2339
  if (operation.parameters) {
1577
- this.validateParameters(operation.parameters, path, method, errors, warnings);
2340
+ this.validateParameters(operation.parameters, `${basePath}/parameters`, errors, warnings);
1578
2341
  }
1579
- const pathParams = path.match(/\{([^}]+)\}/g)?.map((p) => p.slice(1, -1)) ?? [];
2342
+ const allParameters = [...pathLevelParameters, ...operation.parameters ?? []].map(
2343
+ (p) => this.resolveParameterRef(p, componentParameters)
2344
+ );
2345
+ const hasUnresolvableRefs = allParameters.some((p) => p === void 0);
2346
+ const pathParams = path.match(/\{([^{}]+)\}/g)?.map((p) => p.slice(1, -1)) ?? [];
1580
2347
  const definedPathParams = new Set(
1581
- operation.parameters?.filter((p) => p.in === "path").map((p) => p.name) ?? []
2348
+ allParameters.filter((p) => p && p.in === "path").map((p) => p.name)
1582
2349
  );
1583
2350
  for (const param of pathParams) {
1584
- if (!definedPathParams.has(param)) {
2351
+ if (!hasUnresolvableRefs && !definedPathParams.has(param)) {
1585
2352
  errors.push({
1586
2353
  message: `Path parameter '${param}' not defined in parameters: ${method.toUpperCase()} ${path}`,
1587
2354
  path: `${basePath}/parameters`,
@@ -1593,11 +2360,13 @@ var Validator = class {
1593
2360
  /**
1594
2361
  * Validate parameters
1595
2362
  */
1596
- validateParameters(parameters, path, method, errors, warnings) {
1597
- const basePath = `/paths/${path}/${method}/parameters`;
2363
+ validateParameters(parameters, basePath, errors, warnings) {
1598
2364
  for (let i = 0; i < parameters.length; i++) {
1599
2365
  const param = parameters[i];
1600
2366
  const paramPath = `${basePath}/${i}`;
2367
+ if (param && typeof param === "object" && "$ref" in param) {
2368
+ continue;
2369
+ }
1601
2370
  if (!param.name) {
1602
2371
  errors.push({
1603
2372
  message: "Parameter missing name",
@@ -1636,56 +2405,6 @@ var Validator = class {
1636
2405
  }
1637
2406
  };
1638
2407
 
1639
- // src/errors.ts
1640
- var OpenAPIToolError = class extends Error {
1641
- context;
1642
- constructor(message, context) {
1643
- super(message);
1644
- this.name = this.constructor.name;
1645
- this.context = context;
1646
- if (Error.captureStackTrace) {
1647
- Error.captureStackTrace(this, this.constructor);
1648
- }
1649
- }
1650
- };
1651
- var LoadError = class extends OpenAPIToolError {
1652
- constructor(message, context) {
1653
- super(message, context);
1654
- }
1655
- };
1656
- var SsrfError = class extends LoadError {
1657
- constructor(message, context) {
1658
- super(message, context);
1659
- }
1660
- };
1661
- var ParseError = class extends OpenAPIToolError {
1662
- constructor(message, context) {
1663
- super(message, context);
1664
- }
1665
- };
1666
- var ValidationError = class extends OpenAPIToolError {
1667
- errors;
1668
- constructor(message, context) {
1669
- super(message, context);
1670
- this.errors = context?.["errors"];
1671
- }
1672
- };
1673
- var GenerationError = class extends OpenAPIToolError {
1674
- constructor(message, context) {
1675
- super(message, context);
1676
- }
1677
- };
1678
- var RequestBuildError = class extends OpenAPIToolError {
1679
- constructor(message, context) {
1680
- super(message, context);
1681
- }
1682
- };
1683
- var SchemaError = class extends OpenAPIToolError {
1684
- constructor(message, context) {
1685
- super(message, context);
1686
- }
1687
- };
1688
-
1689
2408
  // src/format-resolver.ts
1690
2409
  var BUILTIN_FORMAT_RESOLVERS = {
1691
2410
  // String formats
@@ -1795,6 +2514,376 @@ function resolveSchemaFormats(schema, resolvers) {
1795
2514
  return result;
1796
2515
  }
1797
2516
 
2517
+ // src/type-signature.ts
2518
+ var DEFAULT_MAX_DEPTH = 8;
2519
+ var IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
2520
+ function toPascalIdentifier(toolName) {
2521
+ const segments = toolName.split(/[^A-Za-z0-9]+/).filter((s) => s.length > 0);
2522
+ const joined = segments.map((s) => s[0].toUpperCase() + s.slice(1)).join("");
2523
+ if (joined === "") {
2524
+ return "Tool";
2525
+ }
2526
+ return /^[0-9]/.test(joined) ? `T${joined}` : joined;
2527
+ }
2528
+ function lowerFirst(name) {
2529
+ return name[0].toLowerCase() + name.slice(1);
2530
+ }
2531
+ function isSchemaRecord(value) {
2532
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2533
+ }
2534
+ function isNullSchema(value) {
2535
+ return isSchemaRecord(value) && value["type"] === "null";
2536
+ }
2537
+ function paren(expr) {
2538
+ return expr.includes(" | ") || expr.includes(" & ") ? `(${expr})` : expr;
2539
+ }
2540
+ function dedupe(parts) {
2541
+ return [...new Set(parts)];
2542
+ }
2543
+ function quoteKey(name) {
2544
+ return IDENTIFIER.test(name) ? name : JSON.stringify(name);
2545
+ }
2546
+ function literalOf(value) {
2547
+ if (value === null) {
2548
+ return "null";
2549
+ }
2550
+ const t = typeof value;
2551
+ if (t === "number") {
2552
+ return Number.isFinite(value) ? JSON.stringify(value) : "number";
2553
+ }
2554
+ if (t === "string" || t === "boolean") {
2555
+ return JSON.stringify(value);
2556
+ }
2557
+ return "unknown";
2558
+ }
2559
+ var RESERVED_WORDS = /* @__PURE__ */ new Set([
2560
+ "break",
2561
+ "case",
2562
+ "catch",
2563
+ "class",
2564
+ "const",
2565
+ "continue",
2566
+ "debugger",
2567
+ "default",
2568
+ "delete",
2569
+ "do",
2570
+ "else",
2571
+ "enum",
2572
+ "export",
2573
+ "extends",
2574
+ "false",
2575
+ "finally",
2576
+ "for",
2577
+ "function",
2578
+ "if",
2579
+ "import",
2580
+ "in",
2581
+ "instanceof",
2582
+ "new",
2583
+ "null",
2584
+ "return",
2585
+ "super",
2586
+ "switch",
2587
+ "this",
2588
+ "throw",
2589
+ "true",
2590
+ "try",
2591
+ "typeof",
2592
+ "var",
2593
+ "void",
2594
+ "while",
2595
+ "with",
2596
+ "implements",
2597
+ "interface",
2598
+ "let",
2599
+ "package",
2600
+ "private",
2601
+ "protected",
2602
+ "public",
2603
+ "static",
2604
+ "yield",
2605
+ "await"
2606
+ ]);
2607
+ function escapeJsdoc(text) {
2608
+ return text.replace(/\*\//g, "*\\/");
2609
+ }
2610
+ function jsdocLines(prop) {
2611
+ if (!isSchemaRecord(prop)) {
2612
+ return [];
2613
+ }
2614
+ const lines = [];
2615
+ const description = prop["description"];
2616
+ if (typeof description === "string" && description !== "") {
2617
+ lines.push(...escapeJsdoc(description).split("\n"));
2618
+ }
2619
+ const format = prop["format"];
2620
+ if (typeof format === "string" && format !== "") {
2621
+ lines.push(`@format ${escapeJsdoc(format)}`);
2622
+ }
2623
+ if ("default" in prop && !(typeof prop["default"] === "number" && !Number.isFinite(prop["default"]))) {
2624
+ const rendered = JSON.stringify(prop["default"]);
2625
+ if (rendered !== void 0) {
2626
+ lines.push(`@default ${escapeJsdoc(rendered)}`);
2627
+ }
2628
+ }
2629
+ if (prop["deprecated"] === true) {
2630
+ lines.push("@deprecated");
2631
+ }
2632
+ return lines;
2633
+ }
2634
+ function renderJsdoc(lines, indent) {
2635
+ if (lines.length === 1) {
2636
+ return `${indent}/** ${lines[0]} */
2637
+ `;
2638
+ }
2639
+ return `${indent}/**
2640
+ ${lines.map((l) => `${indent} * ${l}`).join("\n")}
2641
+ ${indent} */
2642
+ `;
2643
+ }
2644
+ function hasObjectShape(r) {
2645
+ return r["type"] === "object" || r["type"] === void 0 && (r["properties"] !== void 0 || r["additionalProperties"] !== void 0 || r["patternProperties"] !== void 0);
2646
+ }
2647
+ function typeExpr(schema, ctx, depth, indent) {
2648
+ if (schema === true) {
2649
+ return "unknown";
2650
+ }
2651
+ if (schema === false) {
2652
+ return "never";
2653
+ }
2654
+ if (!isSchemaRecord(schema)) {
2655
+ return "unknown";
2656
+ }
2657
+ if (ctx.stack.has(schema)) {
2658
+ return "unknown";
2659
+ }
2660
+ if (depth >= ctx.maxDepth) {
2661
+ return "unknown";
2662
+ }
2663
+ if (schema["$ref"] !== void 0) {
2664
+ return "unknown";
2665
+ }
2666
+ ctx.stack.add(schema);
2667
+ try {
2668
+ return typeExprInner(schema, ctx, depth, indent);
2669
+ } finally {
2670
+ ctx.stack.delete(schema);
2671
+ }
2672
+ }
2673
+ function typeExprInner(r, ctx, depth, indent) {
2674
+ if ("const" in r) {
2675
+ const rendered = literalOf(r["const"]);
2676
+ if (rendered !== "unknown") {
2677
+ return rendered;
2678
+ }
2679
+ }
2680
+ const enumMembers = r["enum"];
2681
+ if (Array.isArray(enumMembers)) {
2682
+ if (enumMembers.length === 0) {
2683
+ return "unknown";
2684
+ }
2685
+ return dedupe(enumMembers.map(literalOf)).join(" | ");
2686
+ }
2687
+ const anyOf = r["anyOf"];
2688
+ if (Array.isArray(anyOf) && anyOf.length === 2) {
2689
+ const nullIdx = anyOf.findIndex(isNullSchema);
2690
+ if (nullIdx >= 0 && !isNullSchema(anyOf[1 - nullIdx])) {
2691
+ return `${paren(typeExpr(anyOf[1 - nullIdx], ctx, depth + 1, indent))} | null`;
2692
+ }
2693
+ }
2694
+ const allOf = r["allOf"];
2695
+ if (Array.isArray(allOf)) {
2696
+ const parts = allOf.map((m) => paren(typeExpr(m, ctx, depth + 1, indent)));
2697
+ if (r["properties"] !== void 0) {
2698
+ parts.push(paren(objectExpr(r, ctx, depth, indent)));
2699
+ }
2700
+ return parts.length === 0 ? "unknown" : dedupe(parts).join(" & ");
2701
+ }
2702
+ const union = Array.isArray(r["oneOf"]) ? r["oneOf"] : Array.isArray(anyOf) ? anyOf : void 0;
2703
+ if (union) {
2704
+ if (union.length === 0) {
2705
+ return "unknown";
2706
+ }
2707
+ return dedupe(union.map((m) => typeExpr(m, ctx, depth + 1, indent))).join(" | ");
2708
+ }
2709
+ const type = r["type"];
2710
+ if (Array.isArray(type)) {
2711
+ const parts = type.filter((t) => typeof t === "string").map((t) => typeExpr({ ...r, type: t }, ctx, depth, indent));
2712
+ return parts.length === 0 ? "unknown" : dedupe(parts).join(" | ");
2713
+ }
2714
+ switch (type) {
2715
+ case "string":
2716
+ return "string";
2717
+ case "number":
2718
+ case "integer":
2719
+ return "number";
2720
+ case "boolean":
2721
+ return "boolean";
2722
+ case "null":
2723
+ return "null";
2724
+ case "array":
2725
+ return arrayExpr(r, ctx, depth, indent);
2726
+ default:
2727
+ if (hasObjectShape(r)) {
2728
+ return objectExpr(r, ctx, depth, indent);
2729
+ }
2730
+ return "unknown";
2731
+ }
2732
+ }
2733
+ function arrayExpr(r, ctx, depth, indent) {
2734
+ const items = r["items"];
2735
+ const prefix = Array.isArray(r["prefixItems"]) ? r["prefixItems"] : Array.isArray(items) ? items : void 0;
2736
+ if (prefix) {
2737
+ const parts = prefix.map((m) => typeExpr(m, ctx, depth + 1, indent));
2738
+ let rest = "";
2739
+ if (Array.isArray(r["prefixItems"]) && items !== void 0 && !Array.isArray(items)) {
2740
+ rest = `, ...${paren(typeExpr(items, ctx, depth + 1, indent))}[]`;
2741
+ }
2742
+ return `[${parts.join(", ")}${rest}]`;
2743
+ }
2744
+ if (items === void 0) {
2745
+ return "unknown[]";
2746
+ }
2747
+ return `${paren(typeExpr(items, ctx, depth + 1, indent))}[]`;
2748
+ }
2749
+ function objectExpr(r, ctx, depth, indent) {
2750
+ const properties = isSchemaRecord(r["properties"]) ? r["properties"] : {};
2751
+ const entries = Object.entries(properties);
2752
+ const required = new Set(Array.isArray(r["required"]) ? r["required"] : []);
2753
+ const extraTypes = [];
2754
+ const ap = r["additionalProperties"];
2755
+ if (ap === true) {
2756
+ extraTypes.push("unknown");
2757
+ } else if (isSchemaRecord(ap)) {
2758
+ extraTypes.push(typeExpr(ap, ctx, depth + 1, indent));
2759
+ }
2760
+ const patternProps = r["patternProperties"];
2761
+ if (isSchemaRecord(patternProps)) {
2762
+ for (const value of Object.values(patternProps)) {
2763
+ extraTypes.push(typeExpr(value, ctx, depth + 1, indent));
2764
+ }
2765
+ }
2766
+ const extra = extraTypes.length > 0 ? dedupe(extraTypes).join(" | ") : void 0;
2767
+ if (entries.length === 0) {
2768
+ if (extra !== void 0) {
2769
+ return `Record<string, ${extra}>`;
2770
+ }
2771
+ return ap === false ? "Record<string, never>" : "Record<string, unknown>";
2772
+ }
2773
+ const suffix = extra !== void 0 ? ` & Record<string, ${extra}>` : "";
2774
+ if (ctx.mode === "compact") {
2775
+ const members = entries.map(
2776
+ ([key, prop]) => `${quoteKey(key)}${required.has(key) ? "" : "?"}: ${typeExpr(prop, ctx, depth + 1, indent)}`
2777
+ );
2778
+ return `{ ${members.join("; ")} }${suffix}`;
2779
+ }
2780
+ const inner = indent + " ";
2781
+ let body = "{\n";
2782
+ for (const [key, prop] of entries) {
2783
+ const doc = jsdocLines(prop);
2784
+ if (doc.length > 0) {
2785
+ body += renderJsdoc(doc, inner);
2786
+ }
2787
+ body += `${inner}${quoteKey(key)}${required.has(key) ? "" : "?"}: ${typeExpr(prop, ctx, depth + 1, inner)};
2788
+ `;
2789
+ }
2790
+ body += `${indent}}`;
2791
+ return `${body}${suffix}`;
2792
+ }
2793
+ function isPlainObjectBody(schema) {
2794
+ if (!isSchemaRecord(schema) || schema["$ref"] !== void 0) {
2795
+ return false;
2796
+ }
2797
+ if ("const" in schema && literalOf(schema["const"]) !== "unknown" || Array.isArray(schema["enum"])) {
2798
+ return false;
2799
+ }
2800
+ if (Array.isArray(schema["allOf"]) || Array.isArray(schema["oneOf"]) || Array.isArray(schema["anyOf"])) {
2801
+ return false;
2802
+ }
2803
+ if (Array.isArray(schema["type"]) || !hasObjectShape(schema)) {
2804
+ return false;
2805
+ }
2806
+ const properties = isSchemaRecord(schema["properties"]) ? schema["properties"] : {};
2807
+ if (Object.keys(properties).length === 0) {
2808
+ return false;
2809
+ }
2810
+ const ap = schema["additionalProperties"];
2811
+ if (ap === true || isSchemaRecord(ap) || isSchemaRecord(schema["patternProperties"])) {
2812
+ return false;
2813
+ }
2814
+ return true;
2815
+ }
2816
+ function namedRoot(name, schema, ctx) {
2817
+ const expr = typeExpr(schema, ctx, 0, "");
2818
+ return isPlainObjectBody(schema) ? `interface ${name} ${expr}` : `type ${name} = ${expr};`;
2819
+ }
2820
+ function paramList(inputSchema, typeText) {
2821
+ if (inputSchema === true) {
2822
+ return `(input?: ${typeText})`;
2823
+ }
2824
+ if (!isSchemaRecord(inputSchema)) {
2825
+ return "()";
2826
+ }
2827
+ const properties = isSchemaRecord(inputSchema["properties"]) ? inputSchema["properties"] : {};
2828
+ const keys = Object.keys(properties);
2829
+ if (keys.length === 0) {
2830
+ const ap = inputSchema["additionalProperties"];
2831
+ const hasExtra = ap === true || isSchemaRecord(ap) || isSchemaRecord(inputSchema["patternProperties"]);
2832
+ const objectish = inputSchema["type"] === "object" || inputSchema["type"] === void 0;
2833
+ const composed = Array.isArray(inputSchema["allOf"]) || Array.isArray(inputSchema["oneOf"]) || Array.isArray(inputSchema["anyOf"]) || Array.isArray(inputSchema["enum"]) || "const" in inputSchema;
2834
+ return objectish && !hasExtra && !composed ? "()" : `(input: ${typeText})`;
2835
+ }
2836
+ const required = new Set(Array.isArray(inputSchema["required"]) ? inputSchema["required"] : []);
2837
+ const allOptional = keys.every((k) => !required.has(k));
2838
+ return allOptional ? `(input?: ${typeText})` : `(input: ${typeText})`;
2839
+ }
2840
+ function outputVariantsDeclaration(name, variants, ctx) {
2841
+ const lines = variants.map((member) => {
2842
+ let comment = "";
2843
+ if (isSchemaRecord(member)) {
2844
+ const status = member["x-status-code"];
2845
+ if (typeof status === "number" || typeof status === "string") {
2846
+ const contentType = member["x-content-type"];
2847
+ const ct = typeof contentType === "string" ? ` (${escapeJsdoc(contentType)})` : "";
2848
+ comment = `/** status ${escapeJsdoc(String(status))}${ct} */ `;
2849
+ }
2850
+ }
2851
+ return ` | ${comment}${typeExpr(member, ctx, 1, " ")}`;
2852
+ });
2853
+ return `type ${name} =
2854
+ ${lines.join("\n")};`;
2855
+ }
2856
+ function emitToolTypeScript(toolName, description, inputSchema, outputSchema, options = {}) {
2857
+ const maxDepth = typeof options.maxDepth === "number" && Number.isFinite(options.maxDepth) ? Math.max(1, Math.floor(options.maxDepth)) : DEFAULT_MAX_DEPTH;
2858
+ const compact = { mode: "compact", maxDepth, stack: /* @__PURE__ */ new Set() };
2859
+ const pretty = { mode: "pretty", maxDepth, stack: /* @__PURE__ */ new Set() };
2860
+ const inputCompact = typeExpr(inputSchema, compact, 0, "");
2861
+ const outputCompact = outputSchema === void 0 ? "unknown" : typeExpr(outputSchema, compact, 0, "");
2862
+ const signature = `${paramList(inputSchema, inputCompact)} => Promise<${outputCompact}>`;
2863
+ const base = toPascalIdentifier(toolName);
2864
+ const inputName = `${base}Input`;
2865
+ const outputName = `${base}Output`;
2866
+ const blocks = [];
2867
+ if (typeof description === "string" && description !== "") {
2868
+ blocks.push(renderJsdoc(escapeJsdoc(description).split("\n"), "").trimEnd());
2869
+ }
2870
+ blocks.push(namedRoot(inputName, inputSchema, pretty));
2871
+ const outputUnion = isSchemaRecord(outputSchema) && Array.isArray(outputSchema["oneOf"]) ? outputSchema["oneOf"] : void 0;
2872
+ if (outputSchema === void 0) {
2873
+ blocks.push(`type ${outputName} = unknown;`);
2874
+ } else if (outputUnion && outputUnion.some((m) => isSchemaRecord(m) && m["x-status-code"] !== void 0)) {
2875
+ blocks.push(outputVariantsDeclaration(outputName, outputUnion, pretty));
2876
+ } else {
2877
+ blocks.push(namedRoot(outputName, outputSchema, pretty));
2878
+ }
2879
+ let fnName = lowerFirst(base);
2880
+ if (RESERVED_WORDS.has(fnName)) {
2881
+ fnName = `${fnName}_`;
2882
+ }
2883
+ blocks.push(`declare function ${fnName}${paramList(inputSchema, inputName)}: Promise<${outputName}>;`);
2884
+ return { signature, declaration: blocks.join("\n\n") };
2885
+ }
2886
+
1798
2887
  // src/ssrf.ts
1799
2888
  var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set([
1800
2889
  "localhost",
@@ -2084,6 +3173,103 @@ function applySecureDefaults(options) {
2084
3173
  }
2085
3174
  };
2086
3175
  }
3176
+ function hasUnboundedArray(node, seen = /* @__PURE__ */ new Set()) {
3177
+ if (node === null || typeof node !== "object" || seen.has(node)) return false;
3178
+ seen.add(node);
3179
+ const record = node;
3180
+ const type = record["type"];
3181
+ const isArray = type === "array" || Array.isArray(type) && type.includes("array");
3182
+ if (isArray && record["maxItems"] === void 0) return true;
3183
+ const children = [];
3184
+ const properties = record["properties"];
3185
+ if (properties && typeof properties === "object") children.push(...Object.values(properties));
3186
+ for (const key of ["items", "additionalProperties", "contentSchema"]) {
3187
+ const value = record[key];
3188
+ if (Array.isArray(value)) children.push(...value);
3189
+ else if (value && typeof value === "object") children.push(value);
3190
+ }
3191
+ for (const key of ["allOf", "anyOf", "oneOf", "prefixItems"]) {
3192
+ if (Array.isArray(record[key])) children.push(...record[key]);
3193
+ }
3194
+ return children.some((child) => hasUnboundedArray(child, seen));
3195
+ }
3196
+ function detectResponseHints(outputSchema, mapper) {
3197
+ const paginationParams = [
3198
+ ...new Set(mapper.filter((m) => m.type === "query" && !m.security && PAGINATION_PARAM.test(m.key)).map((m) => m.key))
3199
+ ];
3200
+ const unboundedArray = outputSchema !== void 0 && hasUnboundedArray(outputSchema);
3201
+ if (!unboundedArray && paginationParams.length === 0) return void 0;
3202
+ return {
3203
+ ...unboundedArray && { unboundedArray: true },
3204
+ ...paginationParams.length > 0 && { paginationParams },
3205
+ ...unboundedArray && paginationParams.length === 0 && { largeResponseRisk: true }
3206
+ };
3207
+ }
3208
+ function composeDescription(operation, method, pathStr, strategy) {
3209
+ const fallback = `${method.toUpperCase()} ${pathStr}`;
3210
+ const summary = operation.summary?.trim();
3211
+ const description = operation.description?.trim();
3212
+ switch (strategy) {
3213
+ case "descriptionOnly":
3214
+ return description || summary || fallback;
3215
+ case "combined":
3216
+ if (summary && description && summary !== description) {
3217
+ return `${summary}
3218
+
3219
+ ${description}`;
3220
+ }
3221
+ return summary || description || fallback;
3222
+ case "full": {
3223
+ const parts = [];
3224
+ if (summary) parts.push(summary);
3225
+ if (description && description !== summary) parts.push(description);
3226
+ if (operation.operationId) parts.push(`Operation: ${operation.operationId}`);
3227
+ parts.push(fallback);
3228
+ return parts.join("\n\n");
3229
+ }
3230
+ default:
3231
+ return summary || description || fallback;
3232
+ }
3233
+ }
3234
+ function propertyNames(schema, cap = 8) {
3235
+ const properties = schema["properties"];
3236
+ if (!properties || typeof properties !== "object") return "";
3237
+ const names = Object.keys(properties);
3238
+ const listed = names.slice(0, cap).join(", ");
3239
+ return names.length > cap ? `${listed}, \u2026` : listed;
3240
+ }
3241
+ function summarizeOutputSchema(schema) {
3242
+ const record = schema;
3243
+ const variants = record["oneOf"];
3244
+ if (Array.isArray(variants) && variants.length > 0) {
3245
+ const first = variants[0];
3246
+ const firstSummary = first && typeof first === "object" ? summarizeOutputSchema(first) : void 0;
3247
+ return firstSummary ? `${firstSummary} (${variants.length} response variants)` : void 0;
3248
+ }
3249
+ const type = record["type"];
3250
+ if (type === "object" || type === void 0 && record["properties"]) {
3251
+ const names = propertyNames(record);
3252
+ return names ? `object with fields: ${names}` : "object";
3253
+ }
3254
+ if (type === "array") {
3255
+ const items = record["items"];
3256
+ if (items && typeof items === "object" && !Array.isArray(items)) {
3257
+ const itemRecord = items;
3258
+ if (itemRecord["type"] === "object" || itemRecord["properties"]) {
3259
+ const names = propertyNames(itemRecord);
3260
+ return names ? `array of objects with fields: ${names}` : "array of objects";
3261
+ }
3262
+ if (typeof itemRecord["type"] === "string") {
3263
+ return `array of ${itemRecord["type"]}`;
3264
+ }
3265
+ }
3266
+ return "array";
3267
+ }
3268
+ if (typeof type === "string" && type !== "null") {
3269
+ return type;
3270
+ }
3271
+ return void 0;
3272
+ }
2087
3273
  function globToRegExp(glob) {
2088
3274
  let pattern = "^";
2089
3275
  for (let i = 0; i < glob.length; i++) {
@@ -2106,6 +3292,32 @@ function globToRegExp(glob) {
2106
3292
  function matchesAnyGlob(path, globs) {
2107
3293
  return globs.some((glob) => globToRegExp(glob).test(path));
2108
3294
  }
3295
+ function iconsFromInfoLogo(info) {
3296
+ if (!info || typeof info !== "object") {
3297
+ return void 0;
3298
+ }
3299
+ const logo = info["x-logo"];
3300
+ let src;
3301
+ if (typeof logo === "string") {
3302
+ src = logo;
3303
+ } else if (logo && typeof logo === "object" && !Array.isArray(logo)) {
3304
+ const url = logo["url"];
3305
+ if (typeof url === "string") {
3306
+ src = url;
3307
+ }
3308
+ }
3309
+ if (src !== void 0 && isAllowedIconSrc(src)) {
3310
+ return [{ src }];
3311
+ }
3312
+ return void 0;
3313
+ }
3314
+ function trimUnderscores(value) {
3315
+ let start = 0;
3316
+ let end = value.length;
3317
+ while (start < end && value[start] === "_") start++;
3318
+ while (end > start && value[end - 1] === "_") end--;
3319
+ return value.slice(start, end);
3320
+ }
2109
3321
  function fnv1aHex(input) {
2110
3322
  let hash = 2166136261;
2111
3323
  for (let i = 0; i < input.length; i++) {
@@ -2116,7 +3328,7 @@ function fnv1aHex(input) {
2116
3328
  }
2117
3329
  function normalizeToolName(raw, maxLength, fallbackSeed) {
2118
3330
  let hashSeed = raw;
2119
- let name = raw.replace(/[^A-Za-z0-9_.-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
3331
+ let name = trimUnderscores(raw.replace(/[^A-Za-z0-9_.-]/g, "_").replace(/_+/g, "_"));
2120
3332
  if (name.length === 0) {
2121
3333
  hashSeed = fallbackSeed;
2122
3334
  name = `tool_${fnv1aHex(fallbackSeed)}`;
@@ -2149,8 +3361,15 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2149
3361
  validate: options.validate ?? true,
2150
3362
  followRedirects: options.followRedirects ?? true,
2151
3363
  refResolution: options.refResolution ?? {},
2152
- secureDefaults: options.secureDefaults ?? false
3364
+ secureDefaults: options.secureDefaults ?? false,
3365
+ overlays: options.overlays
2153
3366
  };
3367
+ if (this.options.overlays) {
3368
+ const overlays = Array.isArray(this.options.overlays) ? this.options.overlays : [this.options.overlays];
3369
+ for (const overlay of overlays) {
3370
+ this.document = applyOverlay(this.document, overlay);
3371
+ }
3372
+ }
2154
3373
  }
2155
3374
  /**
2156
3375
  * Create generator from a URL
@@ -2180,7 +3399,7 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2180
3399
  }
2181
3400
  return new _OpenAPIToolGenerator(document, options);
2182
3401
  } catch (error) {
2183
- if (error instanceof LoadError) {
3402
+ if (error instanceof LoadError || error instanceof OverlayError) {
2184
3403
  throw error;
2185
3404
  }
2186
3405
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -2213,6 +3432,9 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2213
3432
  }
2214
3433
  return new _OpenAPIToolGenerator(document, options);
2215
3434
  } catch (error) {
3435
+ if (error instanceof OverlayError) {
3436
+ throw error;
3437
+ }
2216
3438
  const errorMessage = error instanceof Error ? error.message : String(error);
2217
3439
  throw new LoadError(`Failed to load OpenAPI spec from file: ${errorMessage}`, {
2218
3440
  filePath,
@@ -2228,6 +3450,9 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2228
3450
  const document = yaml.parse(yamlString);
2229
3451
  return new _OpenAPIToolGenerator(document, options);
2230
3452
  } catch (error) {
3453
+ if (error instanceof OverlayError) {
3454
+ throw error;
3455
+ }
2231
3456
  const errorMessage = error instanceof Error ? error.message : String(error);
2232
3457
  throw new ParseError(`Failed to parse YAML: ${errorMessage}`, {
2233
3458
  originalError: error
@@ -2254,6 +3479,16 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2254
3479
  const validator = new Validator();
2255
3480
  return validator.validate(this.document);
2256
3481
  }
3482
+ /**
3483
+ * Lint the loaded document for agent-readiness (missing operationIds,
3484
+ * vague descriptions, unpaginated lists, oversized schemas, ...). Runs
3485
+ * after overlays and dereferencing so findings reflect what tools would
3486
+ * actually be generated from.
3487
+ */
3488
+ async lint() {
3489
+ await this.initialize(false);
3490
+ return lintDocument(this.getDocument());
3491
+ }
2257
3492
  // NOTE: internal/private-address blocking + IPv4-mapped-IPv6 decoding now live
2258
3493
  // in `ssrf.ts` (`isBlockedHostname` / `isBlockedAddress` / `decodeIpv4MappedIpv6`),
2259
3494
  // shared by the spec-URL fetch (`fromURL`) and the `$ref` resolver below, and
@@ -2391,7 +3626,7 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2391
3626
  /**
2392
3627
  * Initialize the generator (dereference if needed, then validate)
2393
3628
  */
2394
- async initialize() {
3629
+ async initialize(runValidation = this.options.validate) {
2395
3630
  if (this.options.dereference && !this.dereferencedDocument) {
2396
3631
  const cloned = JSON.parse(JSON.stringify(this.document));
2397
3632
  if (!_OpenAPIToolGenerator.hasExternalRefs(cloned)) {
@@ -2409,7 +3644,7 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2409
3644
  }
2410
3645
  }
2411
3646
  }
2412
- if (this.options.validate) {
3647
+ if (runValidation) {
2413
3648
  const validator = new Validator();
2414
3649
  const documentToValidate = this.dereferencedDocument ?? this.document;
2415
3650
  const result = await validator.validate(documentToValidate);
@@ -2458,6 +3693,14 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2458
3693
  attempts++;
2459
3694
  }
2460
3695
  tool = { ...tool, name: deduped };
3696
+ if (tool.metadata.typescript) {
3697
+ tool.metadata = {
3698
+ ...tool.metadata,
3699
+ typescript: emitToolTypeScript(deduped, tool.description, tool.inputSchema, tool.outputSchema, {
3700
+ maxDepth: Math.max(1, options.maxSchemaDepth ?? 10)
3701
+ })
3702
+ };
3703
+ }
2461
3704
  }
2462
3705
  usedNames.add(tool.name);
2463
3706
  tools.push(tool);
@@ -2506,8 +3749,14 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2506
3749
  const responseBuilder = new ResponseBuilder(options);
2507
3750
  const outputSchema = responseBuilder.build(operation.responses);
2508
3751
  const overrides = extractExtensionOverrides(operation);
2509
- const name = this.generateToolName(pathStr, method, overrides.name ?? operation.operationId, options);
2510
- const description = overrides.description ?? (operation.summary || operation.description || `${method.toUpperCase()} ${pathStr}`);
3752
+ const name = this.generateToolName(
3753
+ pathStr,
3754
+ method,
3755
+ overrides.name ?? operation.operationId,
3756
+ options,
3757
+ operation
3758
+ );
3759
+ const description = overrides.description ?? composeDescription(operation, method, pathStr, options.descriptionStrategy ?? "summaryOnly");
2511
3760
  const title = overrides.title ?? operation.summary;
2512
3761
  const inferred = options.inferAnnotations !== false ? inferAnnotationsFromMethod(method.toLowerCase()) : void 0;
2513
3762
  const annotations = inferred || overrides.annotations ? { ...inferred, ...overrides.annotations } : void 0;
@@ -2524,17 +3773,95 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2524
3773
  if (resolvedOutputSchema) {
2525
3774
  resolvedOutputSchema = SchemaBuilder.truncateDepth(resolvedOutputSchema, maxSchemaDepth);
2526
3775
  }
3776
+ const applyTrim = (schema, isInputRoot) => {
3777
+ let trimmed = schema;
3778
+ if (options.stripExamples) trimmed = SchemaBuilder.stripExamples(trimmed);
3779
+ if (options.maxDescriptionLength !== void 0) {
3780
+ trimmed = SchemaBuilder.capDescriptions(trimmed, options.maxDescriptionLength);
3781
+ }
3782
+ if (options.maxProperties !== void 0) {
3783
+ if (isInputRoot) {
3784
+ const properties = trimmed.properties;
3785
+ if (properties && typeof properties === "object") {
3786
+ const limited = {};
3787
+ for (const [key, value] of Object.entries(properties)) {
3788
+ limited[key] = SchemaBuilder.limitProperties(value, options.maxProperties);
3789
+ }
3790
+ trimmed = { ...trimmed, properties: limited };
3791
+ }
3792
+ } else {
3793
+ trimmed = SchemaBuilder.limitProperties(trimmed, options.maxProperties);
3794
+ }
3795
+ }
3796
+ return trimmed;
3797
+ };
3798
+ if (options.stripExamples || options.maxProperties !== void 0 || options.maxDescriptionLength !== void 0) {
3799
+ resolvedInputSchema = applyTrim(resolvedInputSchema, true);
3800
+ if (resolvedOutputSchema) {
3801
+ resolvedOutputSchema = applyTrim(resolvedOutputSchema, false);
3802
+ }
3803
+ }
2527
3804
  if (options.target) {
2528
3805
  resolvedInputSchema = applyClientTarget(resolvedInputSchema, options.target);
2529
3806
  if (resolvedOutputSchema) {
2530
3807
  resolvedOutputSchema = applyClientTarget(resolvedOutputSchema, options.target);
2531
3808
  }
2532
3809
  }
3810
+ const responseHints = detectResponseHints(resolvedOutputSchema, mapper);
3811
+ if (responseHints) {
3812
+ metadata.responseHints = responseHints;
3813
+ }
3814
+ let finalDescription = description;
3815
+ if (options.appendResponseSummary && resolvedOutputSchema) {
3816
+ const summary = summarizeOutputSchema(resolvedOutputSchema);
3817
+ if (summary) {
3818
+ finalDescription = `${finalDescription}
3819
+
3820
+ Returns: ${summary}`;
3821
+ }
3822
+ }
3823
+ if (options.emitTypeSignatures) {
3824
+ metadata.typescript = emitToolTypeScript(name, finalDescription, resolvedInputSchema, resolvedOutputSchema, {
3825
+ // Print at least as deep as the schemas were truncated, so the
3826
+ // emitted types never collapse levels the schema still carries.
3827
+ maxDepth: Math.max(1, options.maxSchemaDepth ?? 10)
3828
+ });
3829
+ }
3830
+ let toolMeta;
3831
+ if (overrides.meta) {
3832
+ toolMeta = {};
3833
+ for (const [key, value] of Object.entries(overrides.meta)) {
3834
+ if (!key.startsWith("dev.agentfront.openapi/")) {
3835
+ toolMeta[key] = value;
3836
+ }
3837
+ }
3838
+ }
3839
+ if (options.emitMeta) {
3840
+ const info = document.info;
3841
+ toolMeta = {
3842
+ ...toolMeta,
3843
+ "dev.agentfront.openapi/operation": {
3844
+ path: pathStr,
3845
+ method,
3846
+ ...operation.operationId !== void 0 && { operationId: operation.operationId },
3847
+ ...operation.tags && { tags: [...operation.tags] },
3848
+ ...operation.deprecated !== void 0 && { deprecated: operation.deprecated },
3849
+ ...typeof info?.["title"] === "string" && { specTitle: info["title"] },
3850
+ ...typeof info?.["version"] === "string" && { specVersion: info["version"] }
3851
+ }
3852
+ };
3853
+ }
3854
+ if (toolMeta && Object.keys(toolMeta).length === 0) {
3855
+ toolMeta = void 0;
3856
+ }
3857
+ const icons = overrides.icons ?? (options.inheritDocumentIcons ? iconsFromInfoLogo(document.info) : void 0);
2533
3858
  return {
2534
3859
  name,
2535
3860
  ...title !== void 0 && { title },
2536
- description,
3861
+ description: finalDescription,
2537
3862
  ...annotations && { annotations },
3863
+ ...toolMeta && { _meta: toolMeta },
3864
+ ...icons && { icons },
2538
3865
  inputSchema: resolvedInputSchema,
2539
3866
  outputSchema: resolvedOutputSchema,
2540
3867
  mapper,
@@ -2602,14 +3929,16 @@ var OpenAPIToolGenerator = class _OpenAPIToolGenerator {
2602
3929
  /**
2603
3930
  * Generate a tool name
2604
3931
  */
2605
- generateToolName(path, method, operationId, options = {}) {
3932
+ generateToolName(path, method, operationId, options = {}, operation) {
2606
3933
  let rawName;
2607
3934
  if (options.namingStrategy?.toolNameGenerator) {
2608
- rawName = options.namingStrategy.toolNameGenerator(path, method, operationId);
3935
+ rawName = options.namingStrategy.toolNameGenerator(path, method, operationId, operation);
2609
3936
  } else if (operationId) {
2610
3937
  rawName = operationId;
2611
3938
  } else {
2612
- const sanitized = path.replace(/\{([^}]+)\}/g, "By_$1").replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
3939
+ const sanitized = trimUnderscores(
3940
+ path.replace(/\{([^{}]+)\}/g, "By_$1").replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_")
3941
+ );
2613
3942
  rawName = `${method}_${sanitized}`;
2614
3943
  }
2615
3944
  return normalizeToolName(
@@ -2824,7 +4153,7 @@ var SecurityResolver = class {
2824
4153
  resolveDigestAuth(context) {
2825
4154
  const digest = context.digest;
2826
4155
  if (!digest) return void 0;
2827
- const quoted = (v) => String(v).replace(/[\r\n]/g, "").replace(/"/g, '\\"');
4156
+ const quoted = (v) => String(v).replace(/[\r\n]/g, "").replace(/\\/g, "\\\\").replace(/"/g, '\\"');
2828
4157
  const token = (v) => String(v).replace(/[\r\n",]/g, "");
2829
4158
  const parts = [
2830
4159
  `username="${quoted(digest.username)}"`,
@@ -2941,6 +4270,1213 @@ function createSecurityContext(auth) {
2941
4270
  };
2942
4271
  }
2943
4272
 
4273
+ // src/naming-presets.ts
4274
+ var CODECALL_RESERVED_NAMESPACES = [
4275
+ "console",
4276
+ "Math",
4277
+ "JSON",
4278
+ "Object",
4279
+ "Promise",
4280
+ "Array",
4281
+ "String",
4282
+ "Number",
4283
+ "Boolean",
4284
+ "Date",
4285
+ "RegExp",
4286
+ "Error",
4287
+ "Symbol",
4288
+ "Map",
4289
+ "Set",
4290
+ "WeakMap",
4291
+ "WeakSet",
4292
+ "globalThis",
4293
+ "global",
4294
+ "window",
4295
+ "self",
4296
+ "undefined",
4297
+ "null",
4298
+ "true",
4299
+ "false",
4300
+ "NaN",
4301
+ "Infinity",
4302
+ "callTool",
4303
+ "getTool",
4304
+ "mcpLog",
4305
+ "mcpNotify"
4306
+ ];
4307
+ function sanitizeIdentifier(value) {
4308
+ if (value === void 0) {
4309
+ return "";
4310
+ }
4311
+ let out = value.replace(/[^A-Za-z0-9_]+/g, "_").replace(/_+/g, "_");
4312
+ let start = 0;
4313
+ let end = out.length;
4314
+ while (start < end && out[start] === "_") start++;
4315
+ while (end > start && out[end - 1] === "_") end--;
4316
+ out = out.slice(start, end);
4317
+ if (out === "") {
4318
+ return "";
4319
+ }
4320
+ return /^[0-9]/.test(out) ? `_${out}` : out;
4321
+ }
4322
+ function firstPathSegment(path) {
4323
+ for (const segment of path.split("/")) {
4324
+ if (segment !== "" && !segment.startsWith("{")) {
4325
+ return sanitizeIdentifier(segment);
4326
+ }
4327
+ }
4328
+ return "";
4329
+ }
4330
+ function pathMethodHalf(method, path, ns) {
4331
+ const segments = path.split("/").filter((s) => s !== "").map((s) => {
4332
+ const templated = s.replace(/\{([^{}]+)\}/g, "by_$1");
4333
+ return sanitizeIdentifier(templated);
4334
+ }).filter((s) => s !== "");
4335
+ if (segments.length > 0 && segments[0] === ns) {
4336
+ segments.shift();
4337
+ }
4338
+ const joined = segments.join("_");
4339
+ return joined === "" ? method : `${method}_${joined}`;
4340
+ }
4341
+ function dottedNaming(options = {}) {
4342
+ const namespaceFrom = options.namespaceFrom ?? "tag";
4343
+ const reserved = /* @__PURE__ */ new Set([...CODECALL_RESERVED_NAMESPACES, ...options.reservedNamespaces ?? []]);
4344
+ return {
4345
+ toolNameGenerator: (path, method, operationId, operation) => {
4346
+ let ns = "";
4347
+ if (namespaceFrom === "tag") {
4348
+ ns = sanitizeIdentifier(operation?.tags?.[0]);
4349
+ }
4350
+ if (ns === "") {
4351
+ ns = firstPathSegment(path);
4352
+ }
4353
+ if (ns === "") {
4354
+ ns = "api";
4355
+ }
4356
+ if (ns.startsWith("_")) {
4357
+ ns = `n${ns.slice(1)}`;
4358
+ }
4359
+ if (reserved.has(ns)) {
4360
+ ns = `${ns}_`;
4361
+ }
4362
+ const methodHalf = sanitizeIdentifier(operationId) || pathMethodHalf(method, path, ns);
4363
+ return `${ns}.${methodHalf}`;
4364
+ }
4365
+ };
4366
+ }
4367
+
4368
+ // src/elicitation.ts
4369
+ function buildElicitation(source) {
4370
+ const { scheme, type } = source;
4371
+ if (type === "http") {
4372
+ const httpScheme = (source.httpScheme ?? "bearer").toLowerCase();
4373
+ if (httpScheme === "basic" || httpScheme === "digest") {
4374
+ return {
4375
+ scheme,
4376
+ message: `Provide HTTP ${httpScheme} credentials for "${scheme}".`,
4377
+ requestedSchema: {
4378
+ type: "object",
4379
+ properties: {
4380
+ username: { type: "string", title: "Username" },
4381
+ password: { type: "string", title: "Password", description: "Handled as a secret \u2014 never logged." }
4382
+ },
4383
+ required: ["username", "password"]
4384
+ }
4385
+ };
4386
+ }
4387
+ const format = source.bearerFormat ? ` (${source.bearerFormat})` : "";
4388
+ return {
4389
+ scheme,
4390
+ message: `Provide the ${httpScheme} token for "${scheme}".`,
4391
+ requestedSchema: {
4392
+ type: "object",
4393
+ properties: {
4394
+ token: { type: "string", title: "Token", description: `HTTP ${httpScheme} authentication token${format}.` }
4395
+ },
4396
+ required: ["token"]
4397
+ }
4398
+ };
4399
+ }
4400
+ if (type === "apiKey") {
4401
+ const keyName = source.apiKeyName ?? scheme;
4402
+ const location = source.apiKeyIn ?? "header";
4403
+ return {
4404
+ scheme,
4405
+ message: `Provide the API key for "${scheme}".`,
4406
+ requestedSchema: {
4407
+ type: "object",
4408
+ properties: {
4409
+ apiKey: { type: "string", title: "API key", description: `API key "${keyName}" sent via ${location}.` }
4410
+ },
4411
+ required: ["apiKey"]
4412
+ }
4413
+ };
4414
+ }
4415
+ if (type === "oauth2" || type === "openIdConnect") {
4416
+ const scopes = source.scopes && source.scopes.length > 0 ? ` Scopes: ${source.scopes.join(", ")}.` : "";
4417
+ return {
4418
+ scheme,
4419
+ message: `Provide an OAuth2 access token for "${scheme}".${scopes}`,
4420
+ requestedSchema: {
4421
+ type: "object",
4422
+ properties: {
4423
+ accessToken: { type: "string", title: "Access token", description: `OAuth2 access token.${scopes}` }
4424
+ },
4425
+ required: ["accessToken"]
4426
+ }
4427
+ };
4428
+ }
4429
+ return void 0;
4430
+ }
4431
+ function deriveSecurityElicitations(tool) {
4432
+ const sources = [];
4433
+ const seen = /* @__PURE__ */ new Set();
4434
+ for (const entry of tool.mapper) {
4435
+ const security = entry.security;
4436
+ if (security && !seen.has(security.scheme)) {
4437
+ seen.add(security.scheme);
4438
+ sources.push(security);
4439
+ }
4440
+ }
4441
+ if (sources.length === 0 && tool.metadata.security) {
4442
+ for (const requirement of tool.metadata.security) {
4443
+ if (!seen.has(requirement.scheme)) {
4444
+ seen.add(requirement.scheme);
4445
+ sources.push({
4446
+ scheme: requirement.scheme,
4447
+ type: requirement.type,
4448
+ httpScheme: requirement.httpScheme,
4449
+ bearerFormat: requirement.bearerFormat,
4450
+ scopes: requirement.scopes,
4451
+ apiKeyName: requirement.name,
4452
+ apiKeyIn: requirement.in
4453
+ });
4454
+ }
4455
+ }
4456
+ }
4457
+ const result = [];
4458
+ for (const source of sources) {
4459
+ const elicitation = buildElicitation(source);
4460
+ if (elicitation) {
4461
+ result.push(elicitation);
4462
+ }
4463
+ }
4464
+ return result;
4465
+ }
4466
+
4467
+ // src/arazzo-expressions.ts
4468
+ var EXACT_ROOTS = {
4469
+ $url: "url",
4470
+ $method: "method",
4471
+ $statusCode: "statusCode"
4472
+ };
4473
+ var DOTTED_ROOTS = {
4474
+ $inputs: "inputs",
4475
+ $outputs: "outputs",
4476
+ $steps: "steps",
4477
+ $workflows: "workflows",
4478
+ $sourceDescriptions: "sourceDescriptions",
4479
+ $components: "components"
4480
+ };
4481
+ var KNOWN_ROOT = /^\$(?:(?:url|method|statusCode)$|(?:request|response|message)\.|(?:inputs|outputs|steps|workflows|sourceDescriptions|components)\.)/;
4482
+ function fail(message, docPath, expression) {
4483
+ throw new ArazzoError(message, { path: docPath, expression });
4484
+ }
4485
+ var TOKEN = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
4486
+ function parseSourceRef(prefix, rest, raw, docPath) {
4487
+ if (rest.startsWith("header.")) {
4488
+ const name = rest.slice("header.".length);
4489
+ if (name === "" || !TOKEN.test(name)) {
4490
+ fail(`Invalid header name in runtime expression "${raw}"`, docPath, raw);
4491
+ }
4492
+ return { type: prefix, raw, path: [], source: "header", name };
4493
+ }
4494
+ if (rest.startsWith("query.") || rest.startsWith("path.")) {
4495
+ const source = rest.startsWith("query.") ? "query" : "path";
4496
+ const name = rest.slice(source.length + 1);
4497
+ if (name === "") {
4498
+ fail(`Empty ${source} parameter name in runtime expression "${raw}"`, docPath, raw);
4499
+ }
4500
+ return { type: prefix, raw, path: [], source, name };
4501
+ }
4502
+ if (rest === "body" || rest.startsWith("body#")) {
4503
+ const node = { type: prefix, raw, path: [], source: "body" };
4504
+ if (rest.startsWith("body#")) {
4505
+ const pointer = rest.slice("body#".length);
4506
+ if (pointer !== "" && !pointer.startsWith("/")) {
4507
+ fail(`JSON Pointer in "${raw}" must be empty or start with "/"`, docPath, raw);
4508
+ }
4509
+ node.pointer = pointer;
4510
+ }
4511
+ return node;
4512
+ }
4513
+ fail(`Invalid $${prefix} reference "${raw}" \u2014 expected header.<name>, query.<name>, path.<name>, or body[#<pointer>]`, docPath, raw);
4514
+ }
4515
+ function parseRuntimeExpression(raw, docPath = "") {
4516
+ const exact = EXACT_ROOTS[raw];
4517
+ if (exact) {
4518
+ return { type: exact, raw, path: [] };
4519
+ }
4520
+ for (const key of Object.keys(EXACT_ROOTS)) {
4521
+ if (raw.startsWith(key) && raw !== key) {
4522
+ fail(`Unexpected characters after "${key}" in runtime expression "${raw}"`, docPath, raw);
4523
+ }
4524
+ }
4525
+ for (const prefix of ["request", "response", "message"]) {
4526
+ if (raw.startsWith(`$${prefix}.`)) {
4527
+ return parseSourceRef(prefix, raw.slice(prefix.length + 2), raw, docPath);
4528
+ }
4529
+ }
4530
+ const dot = raw.indexOf(".");
4531
+ const rootToken = dot === -1 ? raw : raw.slice(0, dot);
4532
+ const root = DOTTED_ROOTS[rootToken];
4533
+ if (root) {
4534
+ const rest = dot === -1 ? "" : raw.slice(dot + 1);
4535
+ if (rest === "") {
4536
+ fail(`Runtime expression "${raw}" is missing a name after "${rootToken}."`, docPath, raw);
4537
+ }
4538
+ const path = rest.split(".");
4539
+ if (path.some((segment) => segment === "" || /\s/.test(segment))) {
4540
+ fail(`Runtime expression "${raw}" contains an empty or whitespace path segment`, docPath, raw);
4541
+ }
4542
+ return { type: root, raw, path };
4543
+ }
4544
+ fail(`Invalid runtime expression "${raw}"`, docPath, raw);
4545
+ }
4546
+ function parseExpressionValue(value, docPath = "") {
4547
+ if (typeof value !== "string") {
4548
+ return { kind: "literal", value };
4549
+ }
4550
+ if (value.startsWith("$")) {
4551
+ if (KNOWN_ROOT.test(value)) {
4552
+ return { kind: "expression", expression: parseRuntimeExpression(value, docPath) };
4553
+ }
4554
+ return { kind: "literal", value };
4555
+ }
4556
+ if (!value.includes("{$")) {
4557
+ return { kind: "literal", value };
4558
+ }
4559
+ const parts = [];
4560
+ let cursor = 0;
4561
+ while (cursor < value.length) {
4562
+ const open = value.indexOf("{$", cursor);
4563
+ if (open === -1) {
4564
+ parts.push(value.slice(cursor));
4565
+ break;
4566
+ }
4567
+ if (open > cursor) {
4568
+ parts.push(value.slice(cursor, open));
4569
+ }
4570
+ const close = value.indexOf("}", open);
4571
+ if (close === -1) {
4572
+ fail(`Unterminated "{$" template expression in "${value}"`, docPath, value);
4573
+ }
4574
+ parts.push(parseRuntimeExpression(value.slice(open + 1, close), docPath));
4575
+ cursor = close + 1;
4576
+ }
4577
+ return { kind: "template", raw: value, parts };
4578
+ }
4579
+ function escapePointerSegment(segment) {
4580
+ return segment.replace(/~/g, "~0").replace(/\//g, "~1");
4581
+ }
4582
+ function collectPayloadExpressions(payload, docPath = "") {
4583
+ const found = [];
4584
+ const seen = /* @__PURE__ */ new Set();
4585
+ const visit = (node, pointer) => {
4586
+ if (typeof node === "string") {
4587
+ const value = parseExpressionValue(node, docPath);
4588
+ if (value.kind !== "literal") {
4589
+ found.push({ pointer, value });
4590
+ }
4591
+ return;
4592
+ }
4593
+ if (!node || typeof node !== "object") {
4594
+ return;
4595
+ }
4596
+ if (seen.has(node)) {
4597
+ return;
4598
+ }
4599
+ seen.add(node);
4600
+ if (Array.isArray(node)) {
4601
+ node.forEach((item, index) => visit(item, `${pointer}/${index}`));
4602
+ return;
4603
+ }
4604
+ for (const [key, value] of Object.entries(node)) {
4605
+ visit(value, `${pointer}/${escapePointerSegment(key)}`);
4606
+ }
4607
+ };
4608
+ visit(payload, "");
4609
+ return found;
4610
+ }
4611
+
4612
+ // src/arazzo.ts
4613
+ var yaml2 = __toESM(require("yaml"));
4614
+ var ID_PATTERN = /^[A-Za-z0-9_-]+$/;
4615
+ var OUTPUT_KEY_PATTERN = /^[a-zA-Z0-9.\-_]+$/;
4616
+ var VERSION_PATTERN = /^1\.0\.\d+$/;
4617
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "trace"];
4618
+ var PARAMETER_LOCATIONS = ["path", "query", "header", "cookie"];
4619
+ var OUTPUT_DERIVATION_MAX_DEPTH = 8;
4620
+ function err(message, path, extra) {
4621
+ throw new ArazzoError(message, { path, ...extra });
4622
+ }
4623
+ function toPlainJson(value) {
4624
+ try {
4625
+ return JSON.parse(JSON.stringify(value));
4626
+ } catch (error) {
4627
+ const message = error instanceof Error ? error.message : String(error);
4628
+ throw new ArazzoError(`Arazzo document must be JSON-serializable (acyclic, bounded depth): ${message}`, {
4629
+ path: ""
4630
+ });
4631
+ }
4632
+ }
4633
+ function parseArazzoInput(input) {
4634
+ if (typeof input === "string") {
4635
+ let parsed;
4636
+ try {
4637
+ parsed = yaml2.parse(input);
4638
+ } catch (error) {
4639
+ const message = error instanceof Error ? error.message : String(error);
4640
+ throw new ArazzoError(`Failed to parse Arazzo document: ${message}`, { path: "" });
4641
+ }
4642
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4643
+ err("Arazzo document must be an object", "");
4644
+ }
4645
+ return toPlainJson(parsed);
4646
+ }
4647
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
4648
+ err("Arazzo document must be an object", "");
4649
+ }
4650
+ return toPlainJson(input);
4651
+ }
4652
+ function validateCriteria(criteria, path) {
4653
+ if (criteria === void 0) return;
4654
+ if (!Array.isArray(criteria)) {
4655
+ err("successCriteria/criteria must be an array", path);
4656
+ }
4657
+ criteria.forEach((criterion, index) => {
4658
+ const cPath = `${path}/${index}`;
4659
+ if (!criterion || typeof criterion !== "object") {
4660
+ err("Criterion must be an object", cPath);
4661
+ }
4662
+ if (typeof criterion.condition !== "string" || criterion.condition === "") {
4663
+ err('Criterion requires a non-empty string "condition"', cPath);
4664
+ }
4665
+ const type = criterion.type;
4666
+ let effectiveType;
4667
+ if (type !== void 0) {
4668
+ if (typeof type === "string") {
4669
+ if (!["simple", "regex", "jsonpath", "xpath"].includes(type)) {
4670
+ err(`Unknown criterion type "${type}"`, cPath);
4671
+ }
4672
+ effectiveType = type;
4673
+ } else if (type && typeof type === "object") {
4674
+ if (type.type !== "jsonpath" && type.type !== "xpath" || typeof type.version !== "string") {
4675
+ err('Criterion Expression Type Object requires "type" (jsonpath|xpath) and "version"', cPath);
4676
+ }
4677
+ effectiveType = type.type;
4678
+ } else {
4679
+ err('Criterion "type" must be a string or a Criterion Expression Type Object', cPath);
4680
+ }
4681
+ }
4682
+ if (criterion.context !== void 0 && typeof criterion.context !== "string") {
4683
+ err('Criterion "context" must be a runtime expression string', cPath);
4684
+ }
4685
+ if (effectiveType !== void 0 && effectiveType !== "simple" && criterion.context === void 0) {
4686
+ err(`Criterion of type "${effectiveType}" requires a "context" expression`, cPath);
4687
+ }
4688
+ });
4689
+ }
4690
+ function validateActions(actions, kind, path) {
4691
+ if (actions === void 0) return;
4692
+ if (!Array.isArray(actions)) {
4693
+ err("Actions must be an array", path);
4694
+ }
4695
+ actions.forEach((action, index) => {
4696
+ const aPath = `${path}/${index}`;
4697
+ if (!action || typeof action !== "object") {
4698
+ err("Action must be an object", aPath);
4699
+ }
4700
+ if ("reference" in action) {
4701
+ return;
4702
+ }
4703
+ validateActionObject(action, kind, aPath);
4704
+ });
4705
+ }
4706
+ function validateActionObject(action, kind, aPath) {
4707
+ const act = action;
4708
+ if (typeof act.name !== "string" || act.name === "") {
4709
+ err('Action requires a non-empty string "name"', aPath);
4710
+ }
4711
+ const allowed = kind === "success" ? ["end", "goto"] : ["end", "retry", "goto"];
4712
+ if (!allowed.includes(act.type)) {
4713
+ err(`Invalid ${kind}-action type "${String(act.type)}" (allowed: ${allowed.join(", ")})`, aPath);
4714
+ }
4715
+ const targets = [act.workflowId, act.stepId].filter((t) => t !== void 0).length;
4716
+ if (act.type === "goto" && targets !== 1) {
4717
+ err('A "goto" action requires exactly one of "workflowId" or "stepId"', aPath);
4718
+ }
4719
+ if (act.type === "end" && targets !== 0) {
4720
+ err('An "end" action must not specify "workflowId" or "stepId"', aPath);
4721
+ }
4722
+ if (act.retryAfter !== void 0 && (typeof act.retryAfter !== "number" || act.retryAfter < 0)) {
4723
+ err('"retryAfter" must be a non-negative number', aPath);
4724
+ }
4725
+ if (act.retryLimit !== void 0 && (typeof act.retryLimit !== "number" || !Number.isInteger(act.retryLimit) || act.retryLimit < 0)) {
4726
+ err('"retryLimit" must be a non-negative integer', aPath);
4727
+ }
4728
+ validateCriteria(act.criteria, `${aPath}/criteria`);
4729
+ }
4730
+ function validateParameters(parameters, requireIn, path) {
4731
+ if (parameters === void 0) return;
4732
+ if (!Array.isArray(parameters)) {
4733
+ err("Parameters must be an array", path);
4734
+ }
4735
+ const seen = /* @__PURE__ */ new Set();
4736
+ parameters.forEach((parameter, index) => {
4737
+ const pPath = `${path}/${index}`;
4738
+ if (!parameter || typeof parameter !== "object") {
4739
+ err("Parameter must be an object", pPath);
4740
+ }
4741
+ if ("reference" in parameter) {
4742
+ return;
4743
+ }
4744
+ validateParameterObject(parameter, requireIn, pPath);
4745
+ const param = parameter;
4746
+ const key = `${param.name} ${param.in ?? ""}`;
4747
+ if (seen.has(key)) {
4748
+ err(`Duplicate parameter "${param.name}"${param.in ? ` (in: ${param.in})` : ""}`, pPath);
4749
+ }
4750
+ seen.add(key);
4751
+ });
4752
+ }
4753
+ function validateParameterObject(param, requireIn, pPath) {
4754
+ if (typeof param.name !== "string" || param.name === "") {
4755
+ err('Parameter requires a non-empty string "name"', pPath);
4756
+ }
4757
+ const paramName = param.name;
4758
+ if (!("value" in param)) {
4759
+ err(`Parameter "${paramName}" requires a "value"`, pPath);
4760
+ }
4761
+ if (param.in !== void 0 && !PARAMETER_LOCATIONS.includes(param.in)) {
4762
+ err(`Invalid parameter location "${String(param.in)}"`, pPath);
4763
+ }
4764
+ if (requireIn === true && param.in === void 0) {
4765
+ err(`Parameter "${param.name}" on an operation step requires "in"`, pPath);
4766
+ }
4767
+ if (requireIn === false && param.in !== void 0) {
4768
+ err(`Parameter "${param.name}" on a workflowId step must not specify "in"`, pPath);
4769
+ }
4770
+ }
4771
+ function validateOutputs(outputs, path) {
4772
+ if (outputs === void 0) return;
4773
+ if (!outputs || typeof outputs !== "object" || Array.isArray(outputs)) {
4774
+ err('"outputs" must be an object of name \u2192 runtime expression', path);
4775
+ }
4776
+ for (const [key, value] of Object.entries(outputs)) {
4777
+ if (!OUTPUT_KEY_PATTERN.test(key)) {
4778
+ err(`Invalid output name "${key}"`, `${path}/${key}`);
4779
+ }
4780
+ if (typeof value !== "string") {
4781
+ err(`Output "${key}" must be a runtime expression string`, `${path}/${key}`);
4782
+ }
4783
+ }
4784
+ }
4785
+ function validateDocument(doc) {
4786
+ if (typeof doc.arazzo !== "string" || !VERSION_PATTERN.test(doc.arazzo)) {
4787
+ err(`Unsupported arazzo version "${String(doc.arazzo)}" (expected 1.0.x)`, "/arazzo");
4788
+ }
4789
+ if (!doc.info || typeof doc.info !== "object" || typeof doc.info.title !== "string" || typeof doc.info.version !== "string") {
4790
+ err('"info" requires string "title" and "version"', "/info");
4791
+ }
4792
+ if (!Array.isArray(doc.sourceDescriptions) || doc.sourceDescriptions.length === 0) {
4793
+ err('"sourceDescriptions" must be a non-empty array', "/sourceDescriptions");
4794
+ }
4795
+ const sourceNames = /* @__PURE__ */ new Set();
4796
+ doc.sourceDescriptions.forEach((source, index) => {
4797
+ const sPath = `/sourceDescriptions/${index}`;
4798
+ if (!source || typeof source !== "object" || typeof source.name !== "string" || !ID_PATTERN.test(source.name)) {
4799
+ err('Source description requires a "name" matching [A-Za-z0-9_-]+', sPath);
4800
+ }
4801
+ if (typeof source.url !== "string" || source.url === "") {
4802
+ err(`Source "${source.name}" requires a string "url"`, sPath);
4803
+ }
4804
+ if (source.type !== void 0 && source.type !== "openapi" && source.type !== "arazzo") {
4805
+ err(`Source "${source.name}" has invalid type "${String(source.type)}"`, sPath);
4806
+ }
4807
+ if (sourceNames.has(source.name)) {
4808
+ err(`Duplicate source description name "${source.name}"`, sPath);
4809
+ }
4810
+ sourceNames.add(source.name);
4811
+ });
4812
+ if (!Array.isArray(doc.workflows) || doc.workflows.length === 0) {
4813
+ err('"workflows" must be a non-empty array', "/workflows");
4814
+ }
4815
+ const workflowIds = /* @__PURE__ */ new Set();
4816
+ doc.workflows.forEach((workflow, wIndex) => {
4817
+ const wPath = `/workflows/${wIndex}`;
4818
+ if (!workflow || typeof workflow !== "object" || typeof workflow.workflowId !== "string" || !ID_PATTERN.test(workflow.workflowId)) {
4819
+ err('Workflow requires a "workflowId" matching [A-Za-z0-9_-]+', wPath);
4820
+ }
4821
+ if (workflowIds.has(workflow.workflowId)) {
4822
+ err(`Duplicate workflowId "${workflow.workflowId}"`, wPath);
4823
+ }
4824
+ workflowIds.add(workflow.workflowId);
4825
+ if (!Array.isArray(workflow.steps) || workflow.steps.length === 0) {
4826
+ err(`Workflow "${workflow.workflowId}" requires a non-empty "steps" array`, `${wPath}/steps`);
4827
+ }
4828
+ validateParameters(workflow.parameters, void 0, `${wPath}/parameters`);
4829
+ validateActions(workflow.successActions, "success", `${wPath}/successActions`);
4830
+ validateActions(workflow.failureActions, "failure", `${wPath}/failureActions`);
4831
+ validateOutputs(workflow.outputs, `${wPath}/outputs`);
4832
+ const stepIds = /* @__PURE__ */ new Set();
4833
+ workflow.steps.forEach((step, sIndex) => {
4834
+ const sPath = `${wPath}/steps/${sIndex}`;
4835
+ if (!step || typeof step !== "object" || typeof step.stepId !== "string" || !ID_PATTERN.test(step.stepId)) {
4836
+ err('Step requires a "stepId" matching [A-Za-z0-9_-]+', sPath);
4837
+ }
4838
+ if (stepIds.has(step.stepId)) {
4839
+ err(`Duplicate stepId "${step.stepId}" in workflow "${workflow.workflowId}"`, sPath);
4840
+ }
4841
+ stepIds.add(step.stepId);
4842
+ const kinds = [step.operationId, step.operationPath, step.workflowId].filter((k) => k !== void 0).length;
4843
+ if (kinds !== 1) {
4844
+ err(`Step "${step.stepId}" requires exactly one of "operationId", "operationPath", or "workflowId"`, sPath);
4845
+ }
4846
+ validateParameters(step.parameters, step.workflowId !== void 0 ? false : true, `${sPath}/parameters`);
4847
+ validateCriteria(step.successCriteria, `${sPath}/successCriteria`);
4848
+ validateActions(step.onSuccess, "success", `${sPath}/onSuccess`);
4849
+ validateActions(step.onFailure, "failure", `${sPath}/onFailure`);
4850
+ validateOutputs(step.outputs, `${sPath}/outputs`);
4851
+ });
4852
+ });
4853
+ }
4854
+ function ownComponent(group, name) {
4855
+ if (!group || !Object.prototype.hasOwnProperty.call(group, name)) {
4856
+ return void 0;
4857
+ }
4858
+ const value = group[name];
4859
+ return value !== null && typeof value === "object" ? value : void 0;
4860
+ }
4861
+ function resolveReusable(entry, components, expectedGroup, path) {
4862
+ if (!entry || typeof entry !== "object" || !("reference" in entry)) {
4863
+ return entry;
4864
+ }
4865
+ const reusable = entry;
4866
+ if (typeof reusable.reference !== "string") {
4867
+ err('Reusable Object "reference" must be a string', path);
4868
+ }
4869
+ const ast = parseRuntimeExpression(reusable.reference, path);
4870
+ if (ast.type !== "components" || ast.path.length < 2 || ast.path[0] !== expectedGroup) {
4871
+ err(`Reference "${reusable.reference}" must point at $components.${expectedGroup}.<name>`, path);
4872
+ }
4873
+ const name = ast.path.slice(1).join(".");
4874
+ const target = ownComponent(components?.[expectedGroup], name);
4875
+ if (!target) {
4876
+ err(`Unknown reference "$components.${expectedGroup}.${name}"`, path);
4877
+ }
4878
+ const resolved = JSON.parse(JSON.stringify(target));
4879
+ if (expectedGroup === "parameters" && "value" in reusable) {
4880
+ resolved.value = reusable.value;
4881
+ }
4882
+ return resolved;
4883
+ }
4884
+ function resolveInputRefs(node, components, path, seen) {
4885
+ if (Array.isArray(node)) {
4886
+ return node.map((item) => resolveInputRefs(item, components, path, seen));
4887
+ }
4888
+ if (!node || typeof node !== "object") {
4889
+ return node;
4890
+ }
4891
+ const record = node;
4892
+ const ref = record["$ref"];
4893
+ if (typeof ref === "string") {
4894
+ const prefix = "#/components/inputs/";
4895
+ if (!ref.startsWith(prefix)) {
4896
+ err(`Unsupported $ref "${ref}" in workflow inputs (only ${prefix}<name> is resolvable)`, path);
4897
+ }
4898
+ const name = ref.slice(prefix.length);
4899
+ const target = ownComponent(components?.inputs, name);
4900
+ if (!target) {
4901
+ err(`Unknown workflow inputs reference "${ref}"`, path);
4902
+ }
4903
+ if (seen.has(name)) {
4904
+ err(`Cyclic workflow inputs reference "${ref}"`, path);
4905
+ }
4906
+ seen.add(name);
4907
+ const resolved = resolveInputRefs(target, components, path, seen);
4908
+ seen.delete(name);
4909
+ return resolved;
4910
+ }
4911
+ const out = {};
4912
+ for (const [key, value] of Object.entries(record)) {
4913
+ out[key] = resolveInputRefs(value, components, path, seen);
4914
+ }
4915
+ return out;
4916
+ }
4917
+ async function prepareSources(doc, options) {
4918
+ const declared = new Map(doc.sourceDescriptions.map((s) => [s.name, s]));
4919
+ const generators = /* @__PURE__ */ new Map();
4920
+ const sourceTypes = /* @__PURE__ */ new Map();
4921
+ for (const [name, source] of Object.entries(options.sources ?? {})) {
4922
+ if (!declared.has(name)) {
4923
+ err(`options.sources contains "${name}", which is not a declared source description`, "/sourceDescriptions", {
4924
+ declared: [...declared.keys()]
4925
+ });
4926
+ }
4927
+ if (source instanceof OpenAPIToolGenerator) {
4928
+ generators.set(name, source);
4929
+ } else {
4930
+ generators.set(name, await OpenAPIToolGenerator.fromJSON(source, options.loadOptions));
4931
+ }
4932
+ }
4933
+ for (const [name, source] of declared) {
4934
+ sourceTypes.set(name, source.type ?? "openapi");
4935
+ }
4936
+ const operationIndex = /* @__PURE__ */ new Map();
4937
+ for (const [name, generator] of generators) {
4938
+ const document = generator.getDocument();
4939
+ for (const [pathStr, pathItem] of Object.entries(document.paths ?? {})) {
4940
+ if (!pathItem || typeof pathItem !== "object") continue;
4941
+ for (const method of HTTP_METHODS) {
4942
+ const operation = pathItem[method];
4943
+ if (!operation || typeof operation !== "object") continue;
4944
+ const operationId = operation["operationId"];
4945
+ if (typeof operationId !== "string") continue;
4946
+ const hits = operationIndex.get(operationId) ?? [];
4947
+ hits.push({ source: name, path: pathStr, method });
4948
+ operationIndex.set(operationId, hits);
4949
+ }
4950
+ }
4951
+ }
4952
+ return { generators, operationIndex, sourceTypes };
4953
+ }
4954
+ function requireGenerator(ctx, source, path) {
4955
+ if (ctx.sourceTypes.get(source) === "arazzo") {
4956
+ err(`Source "${source}" has type "arazzo" \u2014 nested Arazzo sources are not supported`, path);
4957
+ }
4958
+ const generator = ctx.generators.get(source);
4959
+ if (!generator) {
4960
+ err(`No document supplied for source "${source}" (add it to options.sources)`, path, {
4961
+ supplied: [...ctx.generators.keys()]
4962
+ });
4963
+ }
4964
+ return generator;
4965
+ }
4966
+ function parseOperationPath(value, path) {
4967
+ if (!value.startsWith("{")) {
4968
+ err(`operationPath "${value}" must start with a "{$sourceDescriptions...}" expression`, path);
4969
+ }
4970
+ const close = value.indexOf("}");
4971
+ if (close === -1) {
4972
+ err(`operationPath "${value}" is missing "}"`, path);
4973
+ }
4974
+ const ast = parseRuntimeExpression(value.slice(1, close), path);
4975
+ if (ast.type !== "sourceDescriptions" || ast.path.length !== 2 || ast.path[1] !== "url") {
4976
+ err(`operationPath "${value}" must reference $sourceDescriptions.<name>.url`, path);
4977
+ }
4978
+ const source = ast.path[0];
4979
+ const rest = value.slice(close + 1);
4980
+ if (!rest.startsWith("#/")) {
4981
+ err(`operationPath "${value}" requires a "#/paths/..." JSON Pointer after the source expression`, path);
4982
+ }
4983
+ const segments = rest.slice(2).split("/").map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~"));
4984
+ if (segments.length !== 3 || segments[0] !== "paths") {
4985
+ err(`operationPath pointer in "${value}" must have the shape #/paths/<path>/<method>`, path);
4986
+ }
4987
+ const method = segments[2].toLowerCase();
4988
+ if (!HTTP_METHODS.includes(method)) {
4989
+ err(`operationPath "${value}" ends in unknown HTTP method "${segments[2]}"`, path);
4990
+ }
4991
+ return { source, path: segments[1], method };
4992
+ }
4993
+ function resolveOperationRef(step, ctx, path) {
4994
+ if (step.operationPath !== void 0) {
4995
+ return parseOperationPath(step.operationPath, path);
4996
+ }
4997
+ const ref = step.operationId;
4998
+ if (ref.startsWith("$")) {
4999
+ const ast = parseRuntimeExpression(ref, path);
5000
+ if (ast.type !== "sourceDescriptions" || ast.path.length < 2) {
5001
+ err(`operationId expression "${ref}" must be $sourceDescriptions.<name>.<operationId>`, path);
5002
+ }
5003
+ const source = ast.path[0];
5004
+ const operationId = ast.path.slice(1).join(".");
5005
+ const hits2 = (ctx.operationIndex.get(operationId) ?? []).filter((h) => h.source === source);
5006
+ if (hits2.length === 0) {
5007
+ requireGenerator(ctx, source, path);
5008
+ err(`operationId "${operationId}" not found in source "${source}"`, path);
5009
+ }
5010
+ if (hits2.length > 1) {
5011
+ err(`operationId "${operationId}" is duplicated inside source "${source}"`, path, { hits: hits2 });
5012
+ }
5013
+ return { ...hits2[0], operationId };
5014
+ }
5015
+ const hits = ctx.operationIndex.get(ref) ?? [];
5016
+ if (hits.length === 0) {
5017
+ err(`operationId "${ref}" not found in any supplied source (${[...ctx.generators.keys()].join(", ") || "none"})`, path);
5018
+ }
5019
+ if (hits.length > 1) {
5020
+ err(
5021
+ `operationId "${ref}" is ambiguous across sources (${hits.map((h) => h.source).join(", ")}) \u2014 pin it with $sourceDescriptions.<name>.${ref}`,
5022
+ path,
5023
+ { hits }
5024
+ );
5025
+ }
5026
+ return { ...hits[0], operationId: ref };
5027
+ }
5028
+ function checkCycles(edges, kind) {
5029
+ const state = /* @__PURE__ */ new Map();
5030
+ for (const start of edges.keys()) {
5031
+ if (state.get(start) === "done") continue;
5032
+ const stack = [{ node: start, next: 0 }];
5033
+ state.set(start, "visiting");
5034
+ while (stack.length > 0) {
5035
+ const frame = stack[stack.length - 1];
5036
+ const targets = edges.get(frame.node) ?? [];
5037
+ if (frame.next >= targets.length) {
5038
+ state.set(frame.node, "done");
5039
+ stack.pop();
5040
+ continue;
5041
+ }
5042
+ const target = targets[frame.next++];
5043
+ const targetState = state.get(target);
5044
+ if (targetState === "visiting") {
5045
+ const cycle = [...stack.map((f) => f.node), target];
5046
+ err(`Cyclic ${kind}: ${cycle.slice(cycle.indexOf(target)).join(" -> ")}`, "/workflows");
5047
+ }
5048
+ if (targetState !== "done") {
5049
+ state.set(target, "visiting");
5050
+ stack.push({ node: target, next: 0 });
5051
+ }
5052
+ }
5053
+ }
5054
+ }
5055
+ function toCriterionIR(criterion, path) {
5056
+ const ir = {
5057
+ condition: criterion.condition,
5058
+ type: "simple"
5059
+ };
5060
+ if (criterion.context !== void 0) {
5061
+ ir.context = parseRuntimeExpression(criterion.context, path);
5062
+ }
5063
+ if (typeof criterion.type === "string") {
5064
+ ir.type = criterion.type;
5065
+ } else if (criterion.type) {
5066
+ ir.type = criterion.type.type;
5067
+ ir.version = criterion.type.version;
5068
+ }
5069
+ return ir;
5070
+ }
5071
+ function toActionIR(action, kind, path) {
5072
+ const failure = action;
5073
+ return {
5074
+ name: action.name,
5075
+ kind,
5076
+ type: action.type,
5077
+ ...action.workflowId !== void 0 && { workflowId: action.workflowId },
5078
+ ...action.stepId !== void 0 && { stepId: action.stepId },
5079
+ ...failure.retryAfter !== void 0 && { retryAfter: failure.retryAfter },
5080
+ ...failure.retryLimit !== void 0 && { retryLimit: failure.retryLimit },
5081
+ ...action.criteria && { criteria: action.criteria.map((c, i) => toCriterionIR(c, `${path}/criteria/${i}`)) }
5082
+ };
5083
+ }
5084
+ function resolveActions(actions, kind, components, path) {
5085
+ const group = kind === "success" ? "successActions" : "failureActions";
5086
+ return actions.map((action, index) => {
5087
+ const aPath = `${path}/${index}`;
5088
+ const concrete = resolveReusable(action, components, group, aPath);
5089
+ validateActionObject(concrete, kind, aPath);
5090
+ return toActionIR(concrete, kind, aPath);
5091
+ });
5092
+ }
5093
+ function resolveParameters(parameters, components, requireIn, path) {
5094
+ const seen = /* @__PURE__ */ new Set();
5095
+ return parameters.map((parameter, index) => {
5096
+ const pPath = `${path}/${index}`;
5097
+ const concrete = resolveReusable(parameter, components, "parameters", pPath);
5098
+ validateParameterObject(concrete, requireIn, pPath);
5099
+ const key = `${concrete.name} ${concrete.in ?? ""}`;
5100
+ if (seen.has(key)) {
5101
+ err(`Duplicate parameter "${concrete.name}"${concrete.in ? ` (in: ${concrete.in})` : ""}`, pPath);
5102
+ }
5103
+ seen.add(key);
5104
+ return {
5105
+ name: concrete.name,
5106
+ ...concrete.in !== void 0 && { in: concrete.in },
5107
+ value: parseExpressionValue(concrete.value, pPath)
5108
+ };
5109
+ });
5110
+ }
5111
+ function parseOutputs(outputs, path) {
5112
+ if (!outputs) return void 0;
5113
+ const parsed = {};
5114
+ for (const [name, expression] of Object.entries(outputs)) {
5115
+ parsed[name] = parseRuntimeExpression(expression, `${path}/${name}`);
5116
+ }
5117
+ return parsed;
5118
+ }
5119
+ async function resolveStepOperation(ref, ctx, docPath) {
5120
+ const key = `${ref.source} ${ref.method} ${ref.path}`;
5121
+ let cached = ctx.operationCache.get(key);
5122
+ if (!cached) {
5123
+ const generator = requireGenerator(ctx.sources, ref.source, docPath);
5124
+ cached = generator.generateTool(ref.path, ref.method, ctx.generateOptions).catch((error) => {
5125
+ const message = error instanceof Error ? error.message : String(error);
5126
+ throw new ArazzoError(
5127
+ `Failed to resolve ${ref.method.toUpperCase()} ${ref.path} from source "${ref.source}": ${message}`,
5128
+ { path: docPath, source: ref.source }
5129
+ );
5130
+ });
5131
+ ctx.operationCache.set(key, cached);
5132
+ }
5133
+ return cached;
5134
+ }
5135
+ async function buildStepIR(step, ctx, path) {
5136
+ const components = ctx.doc.components;
5137
+ const base = {
5138
+ stepId: step.stepId,
5139
+ ...step.description !== void 0 && { description: step.description },
5140
+ ...step.parameters && {
5141
+ parameters: resolveParameters(
5142
+ step.parameters,
5143
+ components,
5144
+ step.workflowId !== void 0 ? false : true,
5145
+ `${path}/parameters`
5146
+ )
5147
+ },
5148
+ ...step.successCriteria && {
5149
+ successCriteria: step.successCriteria.map((c, i) => toCriterionIR(c, `${path}/successCriteria/${i}`))
5150
+ },
5151
+ ...step.onSuccess && { onSuccess: resolveActions(step.onSuccess, "success", components, `${path}/onSuccess`) },
5152
+ ...step.onFailure && { onFailure: resolveActions(step.onFailure, "failure", components, `${path}/onFailure`) },
5153
+ ...step.outputs && { outputs: parseOutputs(step.outputs, `${path}/outputs`) }
5154
+ };
5155
+ if (step.workflowId !== void 0) {
5156
+ if (step.requestBody !== void 0) {
5157
+ err(`Step "${step.stepId}" invokes a workflow and must not declare a requestBody`, `${path}/requestBody`);
5158
+ }
5159
+ if (step.workflowId.startsWith("$")) {
5160
+ err(`Step "${step.stepId}" invokes a workflow in another Arazzo document \u2014 nested Arazzo sources are not supported`, path);
5161
+ }
5162
+ if (!ctx.workflowIds.has(step.workflowId)) {
5163
+ err(`Step "${step.stepId}" references unknown workflow "${step.workflowId}"`, path);
5164
+ }
5165
+ const ir2 = { kind: "workflow", workflowId: step.workflowId, ...base };
5166
+ return ir2;
5167
+ }
5168
+ const ref = resolveOperationRef(step, ctx.sources, path);
5169
+ const tool = await resolveStepOperation(ref, ctx, path);
5170
+ const operation = {
5171
+ inputSchema: tool.inputSchema,
5172
+ outputSchema: tool.outputSchema,
5173
+ mapper: tool.mapper,
5174
+ ...tool.metadata.security && { security: tool.metadata.security },
5175
+ ...tool.metadata.servers && { servers: tool.metadata.servers }
5176
+ };
5177
+ let requestBody;
5178
+ if (step.requestBody !== void 0) {
5179
+ if (!step.requestBody || typeof step.requestBody !== "object") {
5180
+ err(`Step "${step.stepId}" requestBody must be an object`, `${path}/requestBody`);
5181
+ }
5182
+ requestBody = {
5183
+ ...step.requestBody.contentType !== void 0 && { contentType: step.requestBody.contentType },
5184
+ ...step.requestBody.payload !== void 0 && { payload: step.requestBody.payload }
5185
+ };
5186
+ const expressions = collectPayloadExpressions(step.requestBody.payload, `${path}/requestBody/payload`);
5187
+ if (expressions.length > 0) {
5188
+ requestBody.payloadExpressions = expressions;
5189
+ }
5190
+ if (step.requestBody.replacements !== void 0) {
5191
+ if (!Array.isArray(step.requestBody.replacements)) {
5192
+ err(`Step "${step.stepId}" requestBody.replacements must be an array`, `${path}/requestBody/replacements`);
5193
+ }
5194
+ requestBody.replacements = step.requestBody.replacements.map((replacement, index) => {
5195
+ const rPath = `${path}/requestBody/replacements/${index}`;
5196
+ if (!replacement || typeof replacement !== "object" || typeof replacement.target !== "string") {
5197
+ err('Replacement requires a string "target"', rPath);
5198
+ }
5199
+ return { target: replacement.target, value: parseExpressionValue(replacement.value, rPath) };
5200
+ });
5201
+ }
5202
+ }
5203
+ const ir = {
5204
+ kind: "operation",
5205
+ source: ref.source,
5206
+ path: ref.path,
5207
+ method: ref.method,
5208
+ ...ref.operationId !== void 0 && { operationId: ref.operationId },
5209
+ operation,
5210
+ ...requestBody && { requestBody },
5211
+ ...base
5212
+ };
5213
+ return ir;
5214
+ }
5215
+ function isRecord(value) {
5216
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5217
+ }
5218
+ function walkPointer(schema, pointer) {
5219
+ if (pointer === void 0 || pointer === "") {
5220
+ return schema;
5221
+ }
5222
+ let node = schema;
5223
+ for (const rawSegment of pointer.slice(1).split("/")) {
5224
+ const segment = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
5225
+ if (!isRecord(node)) return void 0;
5226
+ const properties = node["properties"];
5227
+ if (isRecord(properties) && properties[segment] !== void 0) {
5228
+ node = properties[segment];
5229
+ continue;
5230
+ }
5231
+ if (/^\d+$/.test(segment) && node["items"] !== void 0 && !Array.isArray(node["items"])) {
5232
+ node = node["items"];
5233
+ continue;
5234
+ }
5235
+ return void 0;
5236
+ }
5237
+ return node;
5238
+ }
5239
+ function primaryResponseSchema(outputSchema) {
5240
+ if (isRecord(outputSchema) && Array.isArray(outputSchema["oneOf"])) {
5241
+ const variants = outputSchema["oneOf"];
5242
+ if (variants.length > 0 && variants.every((v) => isRecord(v) && v["x-status-code"] !== void 0)) {
5243
+ return variants[0];
5244
+ }
5245
+ }
5246
+ return outputSchema;
5247
+ }
5248
+ function deriveOutputSchema(ast, steps, inputSchema, depth, stepContext) {
5249
+ if (depth >= OUTPUT_DERIVATION_MAX_DEPTH) {
5250
+ return {};
5251
+ }
5252
+ if (ast.type === "statusCode") {
5253
+ return { type: "number" };
5254
+ }
5255
+ if (ast.type === "url" || ast.type === "method") {
5256
+ return { type: "string" };
5257
+ }
5258
+ if (ast.type === "response") {
5259
+ if (ast.source !== "body") {
5260
+ return { type: "string" };
5261
+ }
5262
+ if (!stepContext) {
5263
+ return {};
5264
+ }
5265
+ const body = primaryResponseSchema(stepContext.operation.outputSchema);
5266
+ const target = walkPointer(body, ast.pointer);
5267
+ return isRecord(target) ? target : {};
5268
+ }
5269
+ if (ast.type === "inputs") {
5270
+ const properties = isRecord(inputSchema) ? inputSchema["properties"] : void 0;
5271
+ const target = isRecord(properties) ? properties[ast.path.join(".")] : void 0;
5272
+ return isRecord(target) ? target : {};
5273
+ }
5274
+ if (ast.type === "steps" && ast.path.length >= 3 && ast.path[1] === "outputs") {
5275
+ const step = steps.get(ast.path[0]);
5276
+ if (step?.kind === "operation") {
5277
+ const stepOutput = step.outputs?.[ast.path.slice(2).join(".")];
5278
+ if (stepOutput) {
5279
+ return deriveOutputSchema(stepOutput, steps, inputSchema, depth + 1, step);
5280
+ }
5281
+ }
5282
+ return {};
5283
+ }
5284
+ return {};
5285
+ }
5286
+ function deriveOutputsSchema(outputs, steps, inputSchema) {
5287
+ if (!outputs) {
5288
+ return void 0;
5289
+ }
5290
+ const stepMap = new Map(steps.map((s) => [s.stepId, s]));
5291
+ const properties = {};
5292
+ for (const [name, ast] of Object.entries(outputs)) {
5293
+ const derived = deriveOutputSchema(ast, stepMap, inputSchema, 0);
5294
+ const copied = JSON.parse(JSON.stringify(derived));
5295
+ properties[name] = { ...copied, description: `Arazzo output: ${ast.raw}` };
5296
+ }
5297
+ return { type: "object", properties };
5298
+ }
5299
+ function applySchemaPipeline(schema, options, isInputRoot) {
5300
+ const formatResolvers = {
5301
+ ...options.resolveFormats ? BUILTIN_FORMAT_RESOLVERS : {},
5302
+ ...options.formatResolvers
5303
+ };
5304
+ let resolved = Object.keys(formatResolvers).length > 0 ? resolveSchemaFormats(schema, formatResolvers) : schema;
5305
+ resolved = SchemaBuilder.truncateDepth(resolved, Math.max(1, options.maxSchemaDepth ?? 10));
5306
+ if (options.stripExamples) resolved = SchemaBuilder.stripExamples(resolved);
5307
+ if (options.maxDescriptionLength !== void 0) {
5308
+ resolved = SchemaBuilder.capDescriptions(resolved, options.maxDescriptionLength);
5309
+ }
5310
+ if (options.maxProperties !== void 0) {
5311
+ if (isInputRoot) {
5312
+ const properties = resolved.properties;
5313
+ if (properties && typeof properties === "object") {
5314
+ const limited = {};
5315
+ for (const [key, value] of Object.entries(properties)) {
5316
+ limited[key] = SchemaBuilder.limitProperties(value, options.maxProperties);
5317
+ }
5318
+ resolved = { ...resolved, properties: limited };
5319
+ }
5320
+ } else {
5321
+ resolved = SchemaBuilder.limitProperties(resolved, options.maxProperties);
5322
+ }
5323
+ }
5324
+ if (options.target) {
5325
+ resolved = applyClientTarget(resolved, options.target);
5326
+ }
5327
+ return resolved;
5328
+ }
5329
+ function buildWorkflowTool(workflow, stepIRs, ctx, wPath) {
5330
+ const options = ctx.generateOptions;
5331
+ let inputSchema;
5332
+ let rawInputSchema;
5333
+ if (workflow.inputs !== void 0) {
5334
+ const resolved = resolveInputRefs(workflow.inputs, ctx.doc.components, `${wPath}/inputs`, /* @__PURE__ */ new Set());
5335
+ rawInputSchema = toJsonSchema(resolved);
5336
+ inputSchema = applySchemaPipeline(rawInputSchema, options, true);
5337
+ } else {
5338
+ inputSchema = { type: "object", properties: {} };
5339
+ }
5340
+ const derivedOutput = deriveOutputsSchema(parseOutputs(workflow.outputs, `${wPath}/outputs`), stepIRs, rawInputSchema);
5341
+ const outputSchema = derivedOutput ? applySchemaPipeline(derivedOutput, options, false) : void 0;
5342
+ const name = normalizeToolName(workflow.workflowId, options.maxToolNameLength ?? 64, workflow.workflowId);
5343
+ const description = workflow.summary && workflow.description ? `${workflow.summary}
5344
+
5345
+ ${workflow.description}` : workflow.summary ?? workflow.description ?? `Arazzo workflow: ${workflow.workflowId}`;
5346
+ const operationSteps = stepIRs.filter((s) => s.kind === "operation");
5347
+ const allReadOnly = operationSteps.length === stepIRs.length && operationSteps.every((s) => inferAnnotationsFromMethod(s.method).readOnlyHint === true);
5348
+ const security = [];
5349
+ const seenSecurity = /* @__PURE__ */ new Set();
5350
+ for (const step of operationSteps) {
5351
+ for (const requirement of step.operation.security ?? []) {
5352
+ const key = JSON.stringify(requirement);
5353
+ if (!seenSecurity.has(key)) {
5354
+ seenSecurity.add(key);
5355
+ security.push(requirement);
5356
+ }
5357
+ }
5358
+ }
5359
+ const ir = {
5360
+ arazzoVersion: ctx.doc.arazzo,
5361
+ workflowId: workflow.workflowId,
5362
+ ...workflow.summary !== void 0 && { summary: workflow.summary },
5363
+ ...workflow.description !== void 0 && { description: workflow.description },
5364
+ ...rawInputSchema !== void 0 && { inputSchema: rawInputSchema },
5365
+ ...workflow.dependsOn && { dependsOn: workflow.dependsOn },
5366
+ ...workflow.parameters && {
5367
+ parameters: resolveParameters(workflow.parameters, ctx.doc.components, void 0, `${wPath}/parameters`)
5368
+ },
5369
+ steps: stepIRs,
5370
+ ...workflow.successActions && {
5371
+ successActions: resolveActions(workflow.successActions, "success", ctx.doc.components, `${wPath}/successActions`)
5372
+ },
5373
+ ...workflow.failureActions && {
5374
+ failureActions: resolveActions(workflow.failureActions, "failure", ctx.doc.components, `${wPath}/failureActions`)
5375
+ },
5376
+ ...workflow.outputs && { outputs: parseOutputs(workflow.outputs, `${wPath}/outputs`) }
5377
+ };
5378
+ const tool = {
5379
+ name,
5380
+ ...workflow.summary !== void 0 && { title: workflow.summary },
5381
+ description,
5382
+ ...allReadOnly && {
5383
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }
5384
+ },
5385
+ inputSchema,
5386
+ outputSchema,
5387
+ // A workflow tool has no single HTTP shape — each step's mapper lives at
5388
+ // metadata.workflow.steps[*].operation.mapper
5389
+ mapper: [],
5390
+ metadata: {
5391
+ path: `arazzo:${workflow.workflowId}`,
5392
+ method: "post",
5393
+ operationId: workflow.workflowId,
5394
+ ...workflow.summary !== void 0 && { operationSummary: workflow.summary },
5395
+ ...workflow.description !== void 0 && { operationDescription: workflow.description },
5396
+ ...security.length > 0 && { security },
5397
+ workflow: ir
5398
+ }
5399
+ };
5400
+ if (options.emitTypeSignatures) {
5401
+ tool.metadata.typescript = emitToolTypeScript(name, description, inputSchema, outputSchema, {
5402
+ maxDepth: Math.max(1, options.maxSchemaDepth ?? 10)
5403
+ });
5404
+ }
5405
+ return tool;
5406
+ }
5407
+ async function fromArazzo(document, options) {
5408
+ const doc = parseArazzoInput(document);
5409
+ validateDocument(doc);
5410
+ const sources = await prepareSources(doc, options);
5411
+ const workflowIds = new Set(doc.workflows.map((w) => w.workflowId));
5412
+ const dependsEdges = /* @__PURE__ */ new Map();
5413
+ const nestedEdges = /* @__PURE__ */ new Map();
5414
+ const declaredSources = new Set(doc.sourceDescriptions.map((s) => s.name));
5415
+ doc.workflows.forEach((workflow, index) => {
5416
+ if (workflow.dependsOn !== void 0 && !Array.isArray(workflow.dependsOn)) {
5417
+ err(`Workflow "${workflow.workflowId}" dependsOn must be an array of workflowIds`, `/workflows/${index}/dependsOn`);
5418
+ }
5419
+ const localTargets = [];
5420
+ for (const target of workflow.dependsOn ?? []) {
5421
+ if (typeof target !== "string") {
5422
+ err(`Workflow "${workflow.workflowId}" dependsOn entries must be strings`, `/workflows/${index}/dependsOn`);
5423
+ }
5424
+ if (target.startsWith("$")) {
5425
+ const ast = parseRuntimeExpression(target, `/workflows/${index}/dependsOn`);
5426
+ if (ast.type !== "sourceDescriptions" || ast.path.length < 2 || !declaredSources.has(ast.path[0])) {
5427
+ err(
5428
+ `Workflow "${workflow.workflowId}" dependsOn "${target}" must reference a declared source ($sourceDescriptions.<name>.<workflowId>)`,
5429
+ `/workflows/${index}/dependsOn`
5430
+ );
5431
+ }
5432
+ continue;
5433
+ }
5434
+ if (!workflowIds.has(target)) {
5435
+ err(`Workflow "${workflow.workflowId}" dependsOn unknown workflow "${target}"`, `/workflows/${index}/dependsOn`);
5436
+ }
5437
+ localTargets.push(target);
5438
+ }
5439
+ dependsEdges.set(workflow.workflowId, localTargets);
5440
+ nestedEdges.set(
5441
+ workflow.workflowId,
5442
+ workflow.steps.filter((s) => s.workflowId !== void 0 && !s.workflowId.startsWith("$")).map((s) => s.workflowId)
5443
+ );
5444
+ });
5445
+ checkCycles(dependsEdges, "dependsOn chain");
5446
+ checkCycles(nestedEdges, "workflow invocation");
5447
+ const ctx = {
5448
+ doc,
5449
+ sources,
5450
+ generateOptions: options.generateOptions ?? {},
5451
+ workflowIds,
5452
+ operationCache: /* @__PURE__ */ new Map()
5453
+ };
5454
+ const tools = [];
5455
+ const usedNames = /* @__PURE__ */ new Set();
5456
+ for (let wIndex = 0; wIndex < doc.workflows.length; wIndex++) {
5457
+ const workflow = doc.workflows[wIndex];
5458
+ const wPath = `/workflows/${wIndex}`;
5459
+ const stepIRs = [];
5460
+ for (let sIndex = 0; sIndex < workflow.steps.length; sIndex++) {
5461
+ stepIRs.push(await buildStepIR(workflow.steps[sIndex], ctx, `${wPath}/steps/${sIndex}`));
5462
+ }
5463
+ let tool = buildWorkflowTool(workflow, stepIRs, ctx, wPath);
5464
+ if (usedNames.has(tool.name)) {
5465
+ const maxLength = ctx.generateOptions.maxToolNameLength ?? 64;
5466
+ let seed = workflow.workflowId;
5467
+ let deduped = normalizeToolName(`${tool.name}_${fnv1aHex(seed)}`, maxLength, seed);
5468
+ while (usedNames.has(deduped)) {
5469
+ seed += "#";
5470
+ deduped = normalizeToolName(`${tool.name}_${fnv1aHex(seed)}`, maxLength, seed);
5471
+ }
5472
+ tool = { ...tool, name: deduped };
5473
+ }
5474
+ usedNames.add(tool.name);
5475
+ tools.push(tool);
5476
+ }
5477
+ return tools;
5478
+ }
5479
+
2944
5480
  // src/request-builder.ts
2945
5481
  var RESERVED_DECODE = {
2946
5482
  "%3A": ":",
@@ -3177,7 +5713,7 @@ function buildHttpRequest(tool, input, options = {}) {
3177
5713
  case "body":
3178
5714
  hasBody = true;
3179
5715
  contentType = contentType ?? mapper.serialization?.contentType ?? "application/json";
3180
- if (mapper.serialization?.binary) binaryBody = true;
5716
+ if (mapper.serialization?.binary && mapper.wholeBody) binaryBody = true;
3181
5717
  if (mapper.wholeBody) {
3182
5718
  rawBody = value;
3183
5719
  } else {
@@ -3274,25 +5810,67 @@ function buildHttpRequest(tool, input, options = {}) {
3274
5810
  // src/sdk.ts
3275
5811
  function toSdkTool(tool, wrapper) {
3276
5812
  const wrapSchema = wrapper?.fromJsonSchema ?? ((schema) => schema);
5813
+ const outputSchema = tool.outputSchema !== void 0 && tool.outputSchema["type"] === "object" ? tool.outputSchema : void 0;
3277
5814
  return [
3278
5815
  tool.name,
3279
5816
  {
3280
5817
  ...tool.title !== void 0 && { title: tool.title },
3281
5818
  description: tool.description,
3282
5819
  inputSchema: wrapSchema(tool.inputSchema),
3283
- ...tool.outputSchema !== void 0 && { outputSchema: wrapSchema(tool.outputSchema) },
5820
+ ...outputSchema !== void 0 && { outputSchema: wrapSchema(outputSchema) },
3284
5821
  ...tool.annotations !== void 0 && { annotations: tool.annotations }
3285
5822
  }
3286
5823
  ];
3287
5824
  }
5825
+
5826
+ // src/token-report.ts
5827
+ function estimateToolTokens(tool) {
5828
+ const advertised = {
5829
+ name: tool.name,
5830
+ ...tool.title !== void 0 && { title: tool.title },
5831
+ description: tool.description,
5832
+ ...tool.annotations !== void 0 && { annotations: tool.annotations },
5833
+ inputSchema: tool.inputSchema,
5834
+ ...tool.outputSchema !== void 0 && { outputSchema: tool.outputSchema }
5835
+ };
5836
+ return Math.ceil(JSON.stringify(advertised).length / 4);
5837
+ }
5838
+ function analyzeToolSet(tools, options = {}) {
5839
+ const tokenBudget = options.tokenBudget ?? 1e4;
5840
+ const maxRecommendedTools = options.maxRecommendedTools ?? 40;
5841
+ const perToolWarning = options.perToolWarning ?? 2e3;
5842
+ const perTool = tools.map((tool) => ({ name: tool.name, tokens: estimateToolTokens(tool) })).sort((a, b) => b.tokens - a.tokens || (a.name < b.name ? -1 : 1));
5843
+ const estimatedTokens = perTool.reduce((sum, entry) => sum + entry.tokens, 0);
5844
+ const warnings = [];
5845
+ if (tools.length > maxRecommendedTools) {
5846
+ warnings.push(
5847
+ `${tools.length} tools exceeds the ~${maxRecommendedTools}-tool range where model selection accuracy degrades \u2014 curate with filters (tags, paths, readOnlyOnly) or split into focused servers.`
5848
+ );
5849
+ }
5850
+ if (estimatedTokens > tokenBudget) {
5851
+ warnings.push(
5852
+ `Estimated ${estimatedTokens} tokens of tool definitions exceeds the ${tokenBudget}-token budget \u2014 trim schemas (maxSchemaDepth, maxProperties) or reduce the tool count.`
5853
+ );
5854
+ }
5855
+ const heavy = perTool.filter((entry) => entry.tokens > perToolWarning);
5856
+ if (heavy.length > 0) {
5857
+ warnings.push(
5858
+ `${heavy.length} tool(s) exceed ${perToolWarning} tokens each (${heavy.slice(0, 3).map((entry) => `${entry.name}: ~${entry.tokens}`).join(", ")}${heavy.length > 3 ? ", \u2026" : ""}) \u2014 consider schema trimming for these.`
5859
+ );
5860
+ }
5861
+ return { toolCount: tools.length, estimatedTokens, perTool, warnings };
5862
+ }
3288
5863
  // Annotate the CommonJS export names for ESM import in node:
3289
5864
  0 && (module.exports = {
5865
+ ArazzoError,
3290
5866
  BLOCKED_HOSTNAMES,
3291
5867
  BUILTIN_FORMAT_RESOLVERS,
5868
+ CODECALL_RESERVED_NAMESPACES,
3292
5869
  GenerationError,
3293
5870
  LoadError,
3294
5871
  OpenAPIToolError,
3295
5872
  OpenAPIToolGenerator,
5873
+ OverlayError,
3296
5874
  ParameterResolver,
3297
5875
  ParseError,
3298
5876
  RequestBuildError,
@@ -3303,7 +5881,9 @@ function toSdkTool(tool, wrapper) {
3303
5881
  SsrfError,
3304
5882
  ValidationError,
3305
5883
  Validator,
5884
+ analyzeToolSet,
3306
5885
  applyClientTarget,
5886
+ applyOverlay,
3307
5887
  assertUrlSafe,
3308
5888
  buildHttpRequest,
3309
5889
  collapseNestedUnions,
@@ -3312,19 +5892,27 @@ function toSdkTool(tool, wrapper) {
3312
5892
  decodeIpv4MappedIpv6,
3313
5893
  defaultLookup,
3314
5894
  demoteFormats,
5895
+ deriveSecurityElicitations,
5896
+ dottedNaming,
5897
+ emitToolTypeScript,
3315
5898
  enforceClosedObjects,
3316
5899
  ensureArrayItems,
5900
+ estimateToolTokens,
3317
5901
  extractExtensionOverrides,
5902
+ fromArazzo,
3318
5903
  inferAnnotationsFromMethod,
3319
5904
  inlineLocalRefs,
3320
5905
  isBlockedAddress,
3321
5906
  isBlockedHostname,
3322
5907
  isReferenceObject,
5908
+ lintDocument,
3323
5909
  normalizeSsrfOptions,
5910
+ parseRuntimeExpression,
3324
5911
  requireAllProperties,
3325
5912
  resolveExtensionEnabled,
3326
5913
  resolveSchemaFormats,
3327
5914
  safeFetch,
3328
5915
  toJsonSchema,
5916
+ toPascalIdentifier,
3329
5917
  toSdkTool
3330
5918
  });