fastmcp 4.24.4 → 4.25.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.
Files changed (39) hide show
  1. package/dist/FastMCP.cjs +3 -3
  2. package/dist/FastMCP.d.cts +2 -2
  3. package/dist/FastMCP.d.ts +2 -2
  4. package/dist/FastMCP.js +2 -2
  5. package/dist/{OAuthProvider-84BWWDBF.d.cts → OAuthProvider-BGtNEdC3.d.cts} +14 -0
  6. package/dist/{OAuthProvider-84BWWDBF.d.ts → OAuthProvider-BGtNEdC3.d.ts} +14 -0
  7. package/dist/auth/index.cjs +2 -2
  8. package/dist/auth/index.d.cts +2 -2
  9. package/dist/auth/index.d.ts +2 -2
  10. package/dist/auth/index.js +1 -1
  11. package/dist/bin/fastmcp.cjs +1 -0
  12. package/dist/bin/fastmcp.cjs.map +1 -1
  13. package/dist/bin/fastmcp.js +1 -0
  14. package/dist/bin/fastmcp.js.map +1 -1
  15. package/dist/{chunk-XPGMRMMN.cjs → chunk-AJE7ERFM.cjs} +14 -9
  16. package/dist/chunk-AJE7ERFM.cjs.map +1 -0
  17. package/dist/{chunk-BV4PWTT6.cjs → chunk-B7IXBMHV.cjs} +32 -1
  18. package/dist/chunk-B7IXBMHV.cjs.map +1 -0
  19. package/dist/{chunk-CAQLTSJ5.js → chunk-JFHOYU3S.js} +32 -1
  20. package/dist/chunk-JFHOYU3S.js.map +1 -0
  21. package/dist/{chunk-D2CVYAA5.js → chunk-PU3JOW7T.js} +11 -6
  22. package/dist/chunk-PU3JOW7T.js.map +1 -0
  23. package/dist/edge/index.cjs +18 -10
  24. package/dist/edge/index.cjs.map +1 -1
  25. package/dist/edge/index.js +18 -10
  26. package/dist/edge/index.js.map +1 -1
  27. package/dist/examples/custom-routes.cjs +3 -3
  28. package/dist/examples/custom-routes.js +2 -2
  29. package/dist/openapi/index.cjs +667 -556
  30. package/dist/openapi/index.cjs.map +1 -1
  31. package/dist/openapi/index.d.cts +8 -3
  32. package/dist/openapi/index.d.ts +8 -3
  33. package/dist/openapi/index.js +648 -537
  34. package/dist/openapi/index.js.map +1 -1
  35. package/package.json +1 -1
  36. package/dist/chunk-BV4PWTT6.cjs.map +0 -1
  37. package/dist/chunk-CAQLTSJ5.js.map +0 -1
  38. package/dist/chunk-D2CVYAA5.js.map +0 -1
  39. package/dist/chunk-XPGMRMMN.cjs.map +0 -1
@@ -2,630 +2,739 @@ import {
2
2
  FastMCP,
3
3
  UserError,
4
4
  jsonSchemaAdapter
5
- } from "../chunk-D2CVYAA5.js";
6
- import "../chunk-CAQLTSJ5.js";
5
+ } from "../chunk-PU3JOW7T.js";
6
+ import "../chunk-JFHOYU3S.js";
7
7
  import "../chunk-OKKFL2M4.js";
8
8
 
9
9
  // src/openapi/loadSpec.ts
10
10
  import SwaggerParser from "@apidevtools/swagger-parser";
11
- async function loadSpec(spec) {
12
- const document = await SwaggerParser.bundle(
13
- spec
11
+
12
+ // src/openapi/schemas.ts
13
+ var SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
14
+ "$defs",
15
+ "definitions",
16
+ "dependentSchemas",
17
+ "patternProperties",
18
+ "properties"
19
+ ]);
20
+ var DATA_KEYS = /* @__PURE__ */ new Set([
21
+ "const",
22
+ "default",
23
+ "enum",
24
+ "example",
25
+ "examples"
26
+ ]);
27
+ var STRIP_KEYS = /* @__PURE__ */ new Set(["example", "examples"]);
28
+ function buildFlatSchema(route, sharedDefs) {
29
+ const byName = /* @__PURE__ */ new Map();
30
+ for (const param of route.parameters) {
31
+ const list = byName.get(param.name) ?? [];
32
+ list.push(param);
33
+ byName.set(param.name, list);
34
+ }
35
+ const {
36
+ bodyEncoding,
37
+ properties: bodyProperties,
38
+ unsupportedBodyContentType,
39
+ wholeBodyKey
40
+ } = extractBodyProperties(
41
+ route.method === "get" ? void 0 : route.requestBody,
42
+ sharedDefs
14
43
  );
15
- if (!document.openapi?.startsWith("3.")) {
16
- throw new Error(
17
- `fromOpenAPI only supports OpenAPI 3.x documents (found ${document.openapi ?? document.swagger ?? "an unrecognized version"}). Swagger 2.0 is not supported.`
18
- );
44
+ const properties = /* @__PURE__ */ Object.create(null);
45
+ const required = [];
46
+ const parameterMap = /* @__PURE__ */ Object.create(null);
47
+ const used = new Set(bodyProperties.keys());
48
+ for (const [name, occurrences] of byName) {
49
+ if (occurrences.length === 1 && !bodyProperties.has(name)) {
50
+ used.add(name);
51
+ }
52
+ }
53
+ const allocateAlias = (base) => {
54
+ let key = base;
55
+ let suffix = 1;
56
+ while (used.has(key)) {
57
+ suffix += 1;
58
+ key = `${base}_${suffix}`;
59
+ }
60
+ used.add(key);
61
+ return key;
62
+ };
63
+ for (const [name, occurrences] of byName) {
64
+ const collides = name === "__proto__" || occurrences.length > 1 || bodyProperties.has(name);
65
+ for (const param of occurrences) {
66
+ const base = collides ? `${name}__${param.in}` : name;
67
+ const key = collides ? allocateAlias(base) : name;
68
+ properties[key] = rewriteComponentRefs(
69
+ param.schema ?? { type: "string" }
70
+ );
71
+ parameterMap[key] = {
72
+ allowReserved: param.allowReserved,
73
+ explode: param.explode,
74
+ in: param.in,
75
+ name,
76
+ style: param.style
77
+ };
78
+ if (param.in === "path" || param.required) {
79
+ required.push(key);
80
+ }
81
+ }
82
+ }
83
+ const bodyKeys = [];
84
+ const requiredBodyKeys = [];
85
+ for (const [name, { required: isRequired, schema }] of bodyProperties) {
86
+ const key = name === "__proto__" ? allocateAlias(`${name}__body`) : name;
87
+ properties[key] = rewriteComponentRefs(schema);
88
+ parameterMap[key] = { in: "body", name };
89
+ bodyKeys.push(key);
90
+ if (isRequired) {
91
+ requiredBodyKeys.push(key);
92
+ }
93
+ }
94
+ if (route.requestBody?.required) {
95
+ required.push(...requiredBodyKeys);
96
+ }
97
+ const flatSchema = {
98
+ additionalProperties: false,
99
+ properties,
100
+ type: "object",
101
+ ...required.length > 0 ? { required } : {}
102
+ };
103
+ if (!route.requestBody?.required && requiredBodyKeys.length > 0) {
104
+ flatSchema.if = { anyOf: bodyKeys.map((key) => ({ required: [key] })) };
105
+ flatSchema.then = { required: requiredBodyKeys };
106
+ }
107
+ const usedDefs = sharedDefs && filterReferencedDefs(properties, sharedDefs);
108
+ if (usedDefs) {
109
+ flatSchema.$defs = usedDefs;
19
110
  }
20
111
  return {
21
- document,
22
- origin: typeof spec === "string" && isHttpUrl(spec) ? spec : void 0
112
+ bodyEncoding,
113
+ flatSchema,
114
+ parameterMap,
115
+ unsupportedBodyContentType,
116
+ wholeBodyKey
23
117
  };
24
118
  }
25
- function isHttpUrl(value) {
26
- return /^https?:\/\//i.test(value);
119
+ function buildSharedDefs(document) {
120
+ const schemas = document.components?.schemas;
121
+ if (!schemas || Object.keys(schemas).length === 0) {
122
+ return void 0;
123
+ }
124
+ return rewriteNode(schemas, "schemaMap");
27
125
  }
28
-
29
- // src/openapi/naming.ts
30
- var MAX_NAME_LENGTH = 56;
31
- var MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;
32
- function generateNames(routes, mcpNames) {
33
- const names = /* @__PURE__ */ new Map();
34
- const used = /* @__PURE__ */ new Set();
35
- for (const route of routes) {
36
- const base = slugify(baseNameFor(route, mcpNames));
37
- let candidate = base;
38
- let suffix = 1;
39
- while (used.has(candidate)) {
40
- suffix += 1;
41
- candidate = `${base}_${suffix}`;
42
- }
43
- used.add(candidate);
44
- names.set(route, candidate);
126
+ var SUCCESS_STATUS_PATTERN = /^2\d\d$/;
127
+ var MAX_OUTPUT_SCHEMA_DEFS = 50;
128
+ function buildOutputSchema(route, sharedDefs) {
129
+ const successEntry = Object.entries(route.responses ?? {}).find(
130
+ ([code]) => SUCCESS_STATUS_PATTERN.test(code)
131
+ );
132
+ const declaredSchema = findMediaType(
133
+ successEntry?.[1].content,
134
+ "application/json"
135
+ )?.schema;
136
+ if (!declaredSchema) {
137
+ return void 0;
45
138
  }
46
- return names;
139
+ const schema = resolveComponentRef(declaredSchema, sharedDefs);
140
+ const rewritten = rewriteComponentRefs(schema, true);
141
+ const { type } = rewritten;
142
+ const isObjectShaped = type === "object" || Array.isArray(type) && type.includes("object");
143
+ if (!isObjectShaped) {
144
+ return void 0;
145
+ }
146
+ const normalized = { ...rewritten, type: "object" };
147
+ const usedDefs = sharedDefs && filterReferencedDefs(normalized, sharedDefs);
148
+ if (usedDefs && Object.keys(usedDefs).length > MAX_OUTPUT_SCHEMA_DEFS) {
149
+ return void 0;
150
+ }
151
+ return {
152
+ ...normalized,
153
+ ...usedDefs ? {
154
+ $defs: rewriteNode(usedDefs, "schemaMap", true)
155
+ } : {}
156
+ };
47
157
  }
48
- function baseNameFor(route, mcpNames) {
49
- if (route.operationId) {
50
- const override = mcpNames && Object.hasOwn(mcpNames, route.operationId) ? mcpNames[route.operationId] : void 0;
51
- return override ?? route.operationId.split("__")[0];
158
+ function resolveComponentRef(schema, sharedDefs) {
159
+ const seen = /* @__PURE__ */ new Set();
160
+ let current = schema;
161
+ let ref = current.$ref;
162
+ while (typeof ref === "string") {
163
+ const name = componentSchemaName(ref);
164
+ const target = name === void 0 ? void 0 : sharedDefs?.[name];
165
+ if (name === void 0 || target === void 0 || seen.has(name)) {
166
+ break;
167
+ }
168
+ seen.add(name);
169
+ current = target;
170
+ ref = current.$ref;
52
171
  }
53
- return route.summary || `${route.method}_${route.path}`;
172
+ return current;
54
173
  }
55
- function slugify(value) {
56
- const slug = value.replace(/[^a-zA-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").slice(0, MAX_BASE_LENGTH);
57
- return slug || "operation";
174
+ function rewriteComponentRefs(value, stripExamples = false) {
175
+ return rewriteNode(value, "schema", stripExamples);
58
176
  }
59
-
60
- // src/openapi/requestBuilder.ts
61
- async function executeRequest(options) {
62
- const baseUrl = resolveBaseUrl(
63
- options.servers,
64
- options.origin,
65
- options.baseUrlOverride
66
- );
67
- const pathParams = /* @__PURE__ */ Object.create(null);
68
- const queryParts = [];
69
- const headers = new Headers(await resolveHeaders(options.headers));
70
- const bodyProps = /* @__PURE__ */ Object.create(null);
71
- for (const [key, value] of Object.entries(options.args)) {
72
- const mapping = options.parameterMap[key];
73
- if (!mapping || value === void 0) {
74
- continue;
177
+ function childMode(key) {
178
+ if (DATA_KEYS.has(key)) {
179
+ return "data";
180
+ }
181
+ return SCHEMA_MAP_KEYS.has(key) ? "schemaMap" : "schema";
182
+ }
183
+ function componentSchemaName(ref) {
184
+ for (const prefix of ["#/components/schemas/", "#/$defs/"]) {
185
+ if (ref.startsWith(prefix)) {
186
+ return ref.slice(prefix.length).replaceAll("~1", "/").replaceAll("~0", "~");
75
187
  }
76
- switch (mapping.in) {
77
- case "body":
78
- bodyProps[mapping.name] = value;
79
- break;
80
- case "cookie": {
81
- const pair = `${mapping.name}=${encodeCookieValue(mapping, value)}`;
82
- const existing = headers.get("cookie");
83
- headers.set("cookie", existing ? `${existing}; ${pair}` : pair);
84
- break;
85
- }
86
- case "header":
87
- headers.set(mapping.name, String(value));
88
- break;
89
- case "path":
90
- pathParams[mapping.name] = encodePathValue(
91
- value,
92
- mapping.style,
93
- mapping.explode
94
- );
95
- break;
96
- case "query": {
97
- const query = new URLSearchParams();
98
- appendQueryValue(
99
- query,
100
- mapping.name,
101
- mapping.style,
102
- value,
103
- mapping.explode
104
- );
105
- if (query.size > 0) {
106
- queryParts.push(serializeQuery(query, mapping.allowReserved));
107
- }
108
- break;
109
- }
188
+ }
189
+ return void 0;
190
+ }
191
+ function extractBodyProperties(requestBody, sharedDefs) {
192
+ const properties = /* @__PURE__ */ new Map();
193
+ const content = requestBody?.content;
194
+ if (!content) {
195
+ return { properties };
196
+ }
197
+ const json = findMediaType(content, "application/json");
198
+ const form = findMediaType(content, "application/x-www-form-urlencoded");
199
+ if (json === void 0 && form === void 0) {
200
+ const contentTypes = Object.keys(content);
201
+ return contentTypes.length > 0 ? { properties, unsupportedBodyContentType: contentTypes[0] } : { properties };
202
+ }
203
+ const bodyEncoding = json === void 0 ? "form" : "json";
204
+ const declaredSchema = bodyEncoding === "json" ? json?.schema : form?.schema;
205
+ if (!declaredSchema) {
206
+ return { bodyEncoding, properties };
207
+ }
208
+ const schema = bodyEncoding === "form" ? resolveComponentRef(declaredSchema, sharedDefs) : declaredSchema;
209
+ const schemaProperties = schema.properties;
210
+ if (schema.type === "object" && schemaProperties && (bodyEncoding === "form" || schema.additionalProperties === false || Object.keys(schemaProperties).length > 0)) {
211
+ const requiredNames = new Set(
212
+ schema.required ?? []
213
+ );
214
+ for (const [name, propertySchema] of Object.entries(schemaProperties)) {
215
+ properties.set(name, {
216
+ required: requiredNames.has(name),
217
+ schema: propertySchema
218
+ });
110
219
  }
220
+ return { bodyEncoding, properties };
111
221
  }
112
- let path = options.route.path;
113
- for (const [name, value] of Object.entries(pathParams)) {
114
- path = path.replaceAll(`{${name}}`, value);
222
+ if (bodyEncoding === "form") {
223
+ return {
224
+ properties,
225
+ unsupportedBodyContentType: "application/x-www-form-urlencoded"
226
+ };
115
227
  }
116
- assertNoSubstitutedDotSegments(options.route, path);
117
- const url = new URL(baseUrl.replace(/\/$/, "") + path);
118
- url.search = queryParts.join("&");
119
- let body;
120
- const requiredJsonBody = options.bodyEncoding === "json" && options.route.requestBody?.required === true && !options.wholeBodyKey;
121
- if (Object.keys(bodyProps).length > 0 || requiredJsonBody) {
122
- const payload = options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps;
123
- if (options.bodyEncoding === "form") {
124
- if (!headers.has("content-type")) {
125
- headers.set("content-type", "application/x-www-form-urlencoded");
126
- }
127
- body = encodeFormBody(payload);
128
- } else {
129
- if (!headers.has("content-type")) {
130
- headers.set("content-type", "application/json");
228
+ properties.set("body", {
229
+ required: requestBody?.required ?? false,
230
+ schema
231
+ });
232
+ return { bodyEncoding, properties, wholeBodyKey: "body" };
233
+ }
234
+ function filterReferencedDefs(node, allDefs) {
235
+ const referenced = /* @__PURE__ */ new Set();
236
+ const stack = [node];
237
+ while (stack.length > 0) {
238
+ const current = stack.pop();
239
+ if (Array.isArray(current)) {
240
+ stack.push(...current);
241
+ continue;
242
+ }
243
+ if (!current || typeof current !== "object") {
244
+ continue;
245
+ }
246
+ for (const [key, value] of Object.entries(
247
+ current
248
+ )) {
249
+ if (key === "$ref" && typeof value === "string" && value.startsWith("#/$defs/")) {
250
+ const [first = ""] = value.slice("#/$defs/".length).split("/");
251
+ const name = first.replaceAll("~1", "/").replaceAll("~0", "~");
252
+ if (allDefs[name] && !referenced.has(name)) {
253
+ referenced.add(name);
254
+ stack.push(allDefs[name]);
255
+ }
256
+ continue;
131
257
  }
132
- body = JSON.stringify(payload);
258
+ stack.push(value);
133
259
  }
134
260
  }
135
- const response = await options.fetchImpl(url.toString(), {
136
- body,
137
- headers,
138
- method: options.route.method.toUpperCase(),
139
- signal: options.signal
140
- });
141
- const text = await response.text();
142
- if (!response.ok) {
143
- throw new UserError(
144
- `${options.route.method.toUpperCase()} ${path} failed with ${response.status}: ${text.slice(0, 2e3)}`
145
- );
261
+ if (referenced.size === 0) {
262
+ return void 0;
146
263
  }
147
- const mediaType = response.headers.get("content-type")?.split(";")[0];
148
- if (mediaType?.toLowerCase().includes("json")) {
149
- try {
150
- const json = JSON.parse(text);
151
- return { json, text: JSON.stringify(json, null, 2) };
152
- } catch {
153
- return { text };
154
- }
264
+ return Object.fromEntries(
265
+ [...referenced].map((name) => [name, allDefs[name]])
266
+ );
267
+ }
268
+ function findMediaType(content, mediaType) {
269
+ if (!content) {
270
+ return void 0;
155
271
  }
156
- return { text };
272
+ const key = Object.hasOwn(content, mediaType) ? mediaType : Object.keys(content).find(
273
+ (candidate) => candidate.split(";")[0].trim().toLowerCase() === mediaType
274
+ );
275
+ return key === void 0 ? void 0 : content[key];
157
276
  }
158
- function resolveBaseUrl(servers, origin, overrideUrl) {
159
- if (overrideUrl) {
160
- return overrideUrl.replace(/\/$/, "");
277
+ function normalizeExclusiveBound(schema, exclusiveKey, boundKey) {
278
+ const { [exclusiveKey]: exclusive, ...rest } = schema;
279
+ if (typeof exclusive !== "boolean") {
280
+ return schema;
161
281
  }
162
- const server = servers?.[0];
163
- if (!server) {
164
- throw new Error(
165
- "The OpenAPI document has no `servers` entry. Pass `baseUrl` to fromOpenAPI() explicitly."
166
- );
282
+ if (!exclusive || typeof rest[boundKey] !== "number") {
283
+ return rest;
167
284
  }
168
- let url = server.url;
169
- for (const [name, variable] of Object.entries(server.variables ?? {})) {
170
- url = url.replaceAll(`{${name}}`, () => variable.default);
285
+ const { [boundKey]: bound, ...unbounded } = rest;
286
+ return { ...unbounded, [exclusiveKey]: bound };
287
+ }
288
+ function normalizeExclusiveBounds(schema) {
289
+ return normalizeExclusiveBound(
290
+ normalizeExclusiveBound(schema, "exclusiveMinimum", "minimum"),
291
+ "exclusiveMaximum",
292
+ "maximum"
293
+ );
294
+ }
295
+ function normalizeNullable(schema) {
296
+ if (!("nullable" in schema)) {
297
+ return schema;
171
298
  }
172
- try {
173
- return new URL(url).toString().replace(/\/$/, "");
174
- } catch {
175
- if (!origin) {
176
- throw new Error(
177
- `The OpenAPI document's servers[0].url ("${url}") is relative, and the spec was not loaded from an http(s) URL, so it cannot be resolved to an absolute address. Pass \`baseUrl\` to fromOpenAPI() explicitly.`
178
- );
179
- }
180
- return new URL(url, origin).toString().replace(/\/$/, "");
299
+ const { nullable, ...rest } = schema;
300
+ if (nullable !== true) {
301
+ return rest;
181
302
  }
182
- }
183
- function appendBracketPairs(params, key, value) {
184
- if (Array.isArray(value)) {
185
- for (const item of value) {
186
- if (item !== null && typeof item === "object") {
187
- appendBracketPairs(params, `${key}[]`, item);
188
- } else {
189
- params.append(key, String(item));
190
- }
191
- }
192
- return;
303
+ const { type } = rest;
304
+ if (typeof type === "string") {
305
+ return { ...rest, type: [type, "null"] };
193
306
  }
194
- if (value !== null && typeof value === "object") {
195
- for (const [subKey, subValue] of Object.entries(
196
- value
197
- )) {
198
- if (subValue !== void 0) {
199
- appendBracketPairs(params, `${key}[${subKey}]`, subValue);
200
- }
201
- }
202
- return;
307
+ if (Array.isArray(type)) {
308
+ return { ...rest, type: [.../* @__PURE__ */ new Set(["null", ...type])] };
203
309
  }
204
- params.append(key, String(value));
310
+ return rest;
205
311
  }
206
- function appendQueryValue(query, name, style, value, explode) {
207
- if (style === "deepObject") {
208
- appendBracketPairs(query, name, value);
209
- return;
210
- }
211
- if (style === "spaceDelimited" || style === "pipeDelimited") {
212
- const items = Array.isArray(value) ? value : [value];
213
- const separator = style === "spaceDelimited" ? " " : "|";
214
- query.append(name, items.map(String).join(separator));
215
- return;
312
+ function rewriteNode(value, mode, stripExamples = false) {
313
+ if (mode === "data") {
314
+ return value;
216
315
  }
217
- if ((style === void 0 || style === "form") && value !== null && typeof value === "object" && !Array.isArray(value)) {
218
- const entries = Object.entries(value);
219
- if (explode === false) {
220
- if (entries.length > 0) {
221
- query.append(
222
- name,
223
- entries.flatMap(([key, item]) => [key, String(item)]).join(",")
224
- );
225
- }
226
- } else {
227
- for (const [key, item] of entries) {
228
- query.append(key, String(item));
229
- }
230
- }
231
- return;
316
+ if (Array.isArray(value)) {
317
+ return value.map((item) => rewriteNode(item, "schema", stripExamples));
232
318
  }
233
- if ((style === void 0 || style === "form") && explode === false && Array.isArray(value) && value.length > 0) {
234
- query.append(name, value.map(String).join(","));
235
- return;
319
+ if (!value || typeof value !== "object") {
320
+ return value;
236
321
  }
237
- for (const item of Array.isArray(value) ? value : [value]) {
238
- query.append(name, String(item));
322
+ const entries = Object.entries(value).filter(
323
+ ([key]) => !stripExamples || mode !== "schema" || !STRIP_KEYS.has(key)
324
+ ).map(([key, entryValue]) => {
325
+ if (mode === "schemaMap") {
326
+ return [key, rewriteNode(entryValue, "schema", stripExamples)];
327
+ }
328
+ if (key === "$ref" && typeof entryValue === "string" && entryValue.startsWith("#/components/schemas/")) {
329
+ return [key, entryValue.replace("#/components/schemas/", "#/$defs/")];
330
+ }
331
+ return [key, rewriteNode(entryValue, childMode(key), stripExamples)];
332
+ });
333
+ const rewritten = Object.fromEntries(entries);
334
+ return mode === "schemaMap" ? rewritten : normalizeExclusiveBounds(normalizeNullable(rewritten));
335
+ }
336
+
337
+ // src/openapi/loadSpec.ts
338
+ async function loadSpec(spec) {
339
+ const document = await SwaggerParser.bundle(
340
+ spec
341
+ );
342
+ if (!document.openapi?.startsWith("3.")) {
343
+ throw new Error(
344
+ `fromOpenAPI only supports OpenAPI 3.x documents (found ${document.openapi ?? document.swagger ?? "an unrecognized version"}). Swagger 2.0 is not supported.`
345
+ );
239
346
  }
347
+ resolveBundledPointerRefs(document);
348
+ return {
349
+ document,
350
+ origin: typeof spec === "string" && isHttpUrl(spec) ? spec : void 0
351
+ };
240
352
  }
241
- function assertNoSubstitutedDotSegments(route, resolvedPath) {
242
- const templateSegments = new Set(route.path.split("/"));
243
- for (const segment of resolvedPath.split("/")) {
244
- if ((segment === "." || segment === "..") && !templateSegments.has(segment)) {
245
- throw new UserError(
246
- `${route.method.toUpperCase()} ${route.path}: path parameter values must not produce a "${segment}" path segment.`
353
+ function isHttpUrl(value) {
354
+ return /^https?:\/\//i.test(value);
355
+ }
356
+ function resolveBundledPointerRefs(document) {
357
+ const hoisted = /* @__PURE__ */ new Map();
358
+ let counter = 0;
359
+ const resolvePointer = (pointer) => {
360
+ let segments;
361
+ try {
362
+ segments = pointer.slice(2).split("/").map(
363
+ (segment) => decodeURIComponent(
364
+ segment.replaceAll("~1", "/").replaceAll("~0", "~")
365
+ )
247
366
  );
367
+ } catch {
368
+ return void 0;
248
369
  }
249
- }
250
- }
251
- function encodeCookieValue(mapping, value) {
252
- return (Array.isArray(value) ? value : [value]).map((item) => {
253
- const text = String(item);
254
- if (mapping.style !== "cookie") {
255
- return encodePathComponent(text);
370
+ let node = document;
371
+ for (const segment of segments) {
372
+ if (!node || typeof node !== "object" || !Object.hasOwn(node, segment)) {
373
+ return void 0;
374
+ }
375
+ node = node[segment];
256
376
  }
257
- if (/[;,]/.test(text)) {
258
- throw new UserError(
259
- `Cookie parameter "${mapping.name}" uses style "cookie" and its value contains ";" or ","`
260
- );
377
+ return node;
378
+ };
379
+ const hoist = (ref) => {
380
+ const existing = hoisted.get(ref);
381
+ if (existing !== void 0) {
382
+ return existing;
261
383
  }
262
- return text;
263
- }).join(",");
264
- }
265
- function encodeFormBody(payload) {
266
- const params = new URLSearchParams();
267
- if (payload && typeof payload === "object" && !Array.isArray(payload)) {
268
- for (const [key, value] of Object.entries(
269
- payload
270
- )) {
271
- if (value === void 0) {
272
- continue;
384
+ const target = resolvePointer(ref);
385
+ if (!target || typeof target !== "object") {
386
+ return void 0;
387
+ }
388
+ const schemas = (document.components ??= {}).schemas ??= {};
389
+ let name;
390
+ do {
391
+ name = `__bundle_${counter++}`;
392
+ } while (Object.hasOwn(schemas, name));
393
+ hoisted.set(ref, name);
394
+ schemas[name] = target;
395
+ walk(target, "schema");
396
+ return name;
397
+ };
398
+ const rewriteRef = (record) => {
399
+ const ref = record.$ref;
400
+ if (typeof ref !== "string" || !ref.startsWith("#/") || ref.startsWith("#/components/schemas/") || ref.startsWith("#/$defs/")) {
401
+ return;
402
+ }
403
+ const name = hoist(ref);
404
+ if (name !== void 0) {
405
+ record.$ref = `#/components/schemas/${name}`;
406
+ }
407
+ };
408
+ const childMode2 = (key, mode) => {
409
+ if (mode === "schemaMap") {
410
+ return "schema";
411
+ }
412
+ if (mode === "schema") {
413
+ return SCHEMA_MAP_KEYS.has(key) ? "schemaMap" : "schema";
414
+ }
415
+ if (key === "schema") {
416
+ return "schema";
417
+ }
418
+ if (mode === "document" && key === "components") {
419
+ return "components";
420
+ }
421
+ return mode === "components" && key === "schemas" ? "schemaMap" : "document";
422
+ };
423
+ const walk = (value, mode) => {
424
+ if (Array.isArray(value)) {
425
+ for (const item of value) {
426
+ walk(item, mode);
273
427
  }
274
- appendBracketPairs(params, key, value);
428
+ return;
275
429
  }
276
- }
277
- return params.toString();
278
- }
279
- function encodePathComponent(value) {
280
- return encodeURIComponent(value).replace(
281
- /[!'()*]/g,
282
- (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
283
- );
430
+ if (!value || typeof value !== "object") {
431
+ return;
432
+ }
433
+ const record = value;
434
+ for (const [key, entryValue] of Object.entries(record)) {
435
+ if (mode === "schema" && key === "$ref") {
436
+ rewriteRef(record);
437
+ } else if (mode !== "schema" || !DATA_KEYS.has(key)) {
438
+ walk(entryValue, childMode2(key, mode));
439
+ }
440
+ }
441
+ };
442
+ walk(document, "document");
284
443
  }
285
- function encodePathValue(value, style, explode) {
286
- if ((style === void 0 || style === "simple") && Array.isArray(value) && value.every((item) => item === null || typeof item !== "object")) {
287
- return value.map((item) => encodePathComponent(String(item))).join(",");
288
- }
289
- if ((style === void 0 || style === "simple") && value !== null && typeof value === "object" && !Array.isArray(value)) {
290
- const entries = Object.entries(value);
291
- if (entries.every(([, item]) => item === null || typeof item !== "object")) {
292
- return entries.map(([key, item]) => {
293
- const encodedKey = encodePathComponent(key);
294
- const encodedValue = encodePathComponent(String(item));
295
- return explode === true ? `${encodedKey}=${encodedValue}` : `${encodedKey},${encodedValue}`;
296
- }).join(",");
444
+
445
+ // src/openapi/naming.ts
446
+ var MAX_NAME_LENGTH = 56;
447
+ var MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;
448
+ function generateNames(routes, mcpNames) {
449
+ const names = /* @__PURE__ */ new Map();
450
+ const used = /* @__PURE__ */ new Set();
451
+ for (const route of routes) {
452
+ const base = slugify(baseNameFor(route, mcpNames));
453
+ let candidate = base;
454
+ let suffix = 1;
455
+ while (used.has(candidate)) {
456
+ suffix += 1;
457
+ candidate = `${base}_${suffix}`;
297
458
  }
459
+ used.add(candidate);
460
+ names.set(route, candidate);
298
461
  }
299
- return encodeURIComponent(String(value));
462
+ return names;
300
463
  }
301
- async function resolveHeaders(headers) {
302
- if (!headers) {
303
- return {};
464
+ function baseNameFor(route, mcpNames) {
465
+ if (route.operationId) {
466
+ const override = mcpNames && Object.hasOwn(mcpNames, route.operationId) ? mcpNames[route.operationId] : void 0;
467
+ return override ?? route.operationId.split("__")[0];
304
468
  }
305
- return typeof headers === "function" ? await headers() : { ...headers };
469
+ return route.summary || `${route.method}_${route.path}`;
306
470
  }
307
- function serializeQuery(query, allowReserved) {
308
- const encoded = query.toString();
309
- if (!allowReserved) {
310
- return encoded;
311
- }
312
- return encoded.split("&").map((pair) => {
313
- const [name, value] = pair.split("=");
314
- const expanded = value.replace(
315
- /\+|%25([\da-f]{2})|%(21|24|27|28|29|2c|2f|3a|3b|3f|40|7e)/gi,
316
- (match, triple) => triple ? `%${triple}` : match === "+" ? "%20" : decodeURIComponent(match)
317
- );
318
- return `${name}=${expanded}`;
319
- }).join("&");
471
+ function slugify(value) {
472
+ const slug = value.replace(/[^a-zA-Z0-9_]+/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "").slice(0, MAX_BASE_LENGTH);
473
+ return slug || "operation";
320
474
  }
321
475
 
322
- // src/openapi/schemas.ts
323
- var SCHEMA_MAP_KEYS = /* @__PURE__ */ new Set([
324
- "$defs",
325
- "definitions",
326
- "dependentSchemas",
327
- "patternProperties",
328
- "properties"
329
- ]);
330
- var DATA_KEYS = /* @__PURE__ */ new Set(["const", "default", "enum", "example", "examples"]);
331
- var STRIP_KEYS = /* @__PURE__ */ new Set(["example", "examples"]);
332
- function buildFlatSchema(route, sharedDefs) {
333
- const byName = /* @__PURE__ */ new Map();
334
- for (const param of route.parameters) {
335
- const list = byName.get(param.name) ?? [];
336
- list.push(param);
337
- byName.set(param.name, list);
338
- }
339
- const {
340
- bodyEncoding,
341
- properties: bodyProperties,
342
- unsupportedBodyContentType,
343
- wholeBodyKey
344
- } = extractBodyProperties(
345
- route.method === "get" ? void 0 : route.requestBody,
346
- sharedDefs
476
+ // src/openapi/requestBuilder.ts
477
+ async function executeRequest(options) {
478
+ const baseUrl = resolveBaseUrl(
479
+ options.servers,
480
+ options.origin,
481
+ options.baseUrlOverride
347
482
  );
348
- const properties = /* @__PURE__ */ Object.create(null);
349
- const required = [];
350
- const parameterMap = /* @__PURE__ */ Object.create(null);
351
- const used = new Set(bodyProperties.keys());
352
- for (const [name, occurrences] of byName) {
353
- if (occurrences.length === 1 && !bodyProperties.has(name)) {
354
- used.add(name);
355
- }
356
- }
357
- const allocateAlias = (base) => {
358
- let key = base;
359
- let suffix = 1;
360
- while (used.has(key)) {
361
- suffix += 1;
362
- key = `${base}_${suffix}`;
483
+ const pathParams = /* @__PURE__ */ Object.create(null);
484
+ const queryParts = [];
485
+ const headers = new Headers(
486
+ await resolveHeaders(options.headers, options.context)
487
+ );
488
+ const bodyProps = /* @__PURE__ */ Object.create(null);
489
+ for (const [key, value] of Object.entries(options.args)) {
490
+ const mapping = options.parameterMap[key];
491
+ if (!mapping || value === void 0) {
492
+ continue;
363
493
  }
364
- used.add(key);
365
- return key;
366
- };
367
- for (const [name, occurrences] of byName) {
368
- const collides = name === "__proto__" || occurrences.length > 1 || bodyProperties.has(name);
369
- for (const param of occurrences) {
370
- const base = collides ? `${name}__${param.in}` : name;
371
- const key = collides ? allocateAlias(base) : name;
372
- properties[key] = rewriteComponentRefs(
373
- param.schema ?? { type: "string" }
374
- );
375
- parameterMap[key] = {
376
- allowReserved: param.allowReserved,
377
- explode: param.explode,
378
- in: param.in,
379
- name,
380
- style: param.style
381
- };
382
- if (param.in === "path" || param.required) {
383
- required.push(key);
494
+ switch (mapping.in) {
495
+ case "body":
496
+ bodyProps[mapping.name] = value;
497
+ break;
498
+ case "cookie": {
499
+ const pair = `${mapping.name}=${encodeCookieValue(mapping, value)}`;
500
+ const existing = headers.get("cookie");
501
+ headers.set("cookie", existing ? `${existing}; ${pair}` : pair);
502
+ break;
503
+ }
504
+ case "header":
505
+ headers.set(mapping.name, String(value));
506
+ break;
507
+ case "path":
508
+ pathParams[mapping.name] = encodePathValue(
509
+ value,
510
+ mapping.style,
511
+ mapping.explode
512
+ );
513
+ break;
514
+ case "query": {
515
+ const query = new URLSearchParams();
516
+ appendQueryValue(
517
+ query,
518
+ mapping.name,
519
+ mapping.style,
520
+ value,
521
+ mapping.explode
522
+ );
523
+ if (query.size > 0) {
524
+ queryParts.push(serializeQuery(query, mapping.allowReserved));
525
+ }
526
+ break;
384
527
  }
385
528
  }
386
529
  }
387
- for (const [name, { required: isRequired, schema }] of bodyProperties) {
388
- const key = name === "__proto__" ? allocateAlias(`${name}__body`) : name;
389
- properties[key] = rewriteComponentRefs(schema);
390
- parameterMap[key] = { in: "body", name };
391
- if (isRequired) {
392
- required.push(key);
530
+ let path = options.route.path;
531
+ for (const [name, value] of Object.entries(pathParams)) {
532
+ path = path.replaceAll(`{${name}}`, value);
533
+ }
534
+ assertNoSubstitutedDotSegments(options.route, path);
535
+ const url = new URL(baseUrl.replace(/\/$/, "") + path);
536
+ url.search = queryParts.join("&");
537
+ let body;
538
+ const requiredJsonBody = options.bodyEncoding === "json" && options.route.requestBody?.required === true && !options.wholeBodyKey;
539
+ if (Object.keys(bodyProps).length > 0 || requiredJsonBody) {
540
+ const payload = options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps;
541
+ if (options.bodyEncoding === "form") {
542
+ if (!headers.has("content-type")) {
543
+ headers.set("content-type", "application/x-www-form-urlencoded");
544
+ }
545
+ body = encodeFormBody(payload);
546
+ } else {
547
+ if (!headers.has("content-type")) {
548
+ headers.set("content-type", "application/json");
549
+ }
550
+ body = JSON.stringify(payload);
393
551
  }
394
552
  }
395
- const flatSchema = {
396
- additionalProperties: false,
397
- properties,
398
- type: "object",
399
- ...required.length > 0 ? { required } : {}
400
- };
401
- const usedDefs = sharedDefs && filterReferencedDefs(properties, sharedDefs);
402
- if (usedDefs) {
403
- flatSchema.$defs = usedDefs;
553
+ const response = await options.fetchImpl(url.toString(), {
554
+ body,
555
+ headers,
556
+ method: options.route.method.toUpperCase(),
557
+ signal: options.signal
558
+ });
559
+ const text = await response.text();
560
+ if (!response.ok) {
561
+ throw new UserError(
562
+ `${options.route.method.toUpperCase()} ${path} failed with ${response.status}: ${text.slice(0, 2e3)}`
563
+ );
404
564
  }
405
- return {
406
- bodyEncoding,
407
- flatSchema,
408
- parameterMap,
409
- unsupportedBodyContentType,
410
- wholeBodyKey
411
- };
412
- }
413
- function buildSharedDefs(document) {
414
- const schemas = document.components?.schemas;
415
- if (!schemas || Object.keys(schemas).length === 0) {
416
- return void 0;
565
+ const mediaType = response.headers.get("content-type")?.split(";")[0];
566
+ if (mediaType?.toLowerCase().includes("json")) {
567
+ try {
568
+ const json = JSON.parse(text);
569
+ return { json, text: JSON.stringify(json, null, 2) };
570
+ } catch {
571
+ return { text };
572
+ }
417
573
  }
418
- return rewriteNode(schemas, "schemaMap");
574
+ return { text };
419
575
  }
420
- var SUCCESS_STATUS_PATTERN = /^2\d\d$/;
421
- var MAX_OUTPUT_SCHEMA_DEFS = 50;
422
- function buildOutputSchema(route, sharedDefs) {
423
- const successEntry = Object.entries(route.responses ?? {}).find(
424
- ([code]) => SUCCESS_STATUS_PATTERN.test(code)
425
- );
426
- const declaredSchema = findMediaType(
427
- successEntry?.[1].content,
428
- "application/json"
429
- )?.schema;
430
- if (!declaredSchema) {
431
- return void 0;
432
- }
433
- const schema = resolveComponentRef(declaredSchema, sharedDefs);
434
- const rewritten = rewriteComponentRefs(schema, true);
435
- const { type } = rewritten;
436
- const isObjectShaped = type === "object" || Array.isArray(type) && type.includes("object");
437
- if (!isObjectShaped) {
438
- return void 0;
576
+ function resolveBaseUrl(servers, origin, overrideUrl) {
577
+ if (overrideUrl) {
578
+ return overrideUrl.replace(/\/$/, "");
439
579
  }
440
- const normalized = { ...rewritten, type: "object" };
441
- const usedDefs = sharedDefs && filterReferencedDefs(normalized, sharedDefs);
442
- if (usedDefs && Object.keys(usedDefs).length > MAX_OUTPUT_SCHEMA_DEFS) {
443
- return void 0;
580
+ const server = servers?.[0];
581
+ if (!server) {
582
+ throw new Error(
583
+ "The OpenAPI document has no `servers` entry. Pass `baseUrl` to fromOpenAPI() explicitly."
584
+ );
444
585
  }
445
- return {
446
- ...normalized,
447
- ...usedDefs ? {
448
- $defs: rewriteNode(usedDefs, "schemaMap", true)
449
- } : {}
450
- };
451
- }
452
- function resolveComponentRef(schema, sharedDefs) {
453
- const seen = /* @__PURE__ */ new Set();
454
- let current = schema;
455
- let ref = current.$ref;
456
- while (typeof ref === "string") {
457
- const name = componentSchemaName(ref);
458
- const target = name === void 0 ? void 0 : sharedDefs?.[name];
459
- if (name === void 0 || target === void 0 || seen.has(name)) {
460
- break;
586
+ let url = server.url;
587
+ for (const [name, variable] of Object.entries(server.variables ?? {})) {
588
+ url = url.replaceAll(`{${name}}`, () => variable.default);
589
+ }
590
+ try {
591
+ return new URL(url).toString().replace(/\/$/, "");
592
+ } catch {
593
+ if (!origin) {
594
+ throw new Error(
595
+ `The OpenAPI document's servers[0].url ("${url}") is relative, and the spec was not loaded from an http(s) URL, so it cannot be resolved to an absolute address. Pass \`baseUrl\` to fromOpenAPI() explicitly.`
596
+ );
461
597
  }
462
- seen.add(name);
463
- current = target;
464
- ref = current.$ref;
598
+ return new URL(url, origin).toString().replace(/\/$/, "");
465
599
  }
466
- return current;
467
- }
468
- function rewriteComponentRefs(value, stripExamples = false) {
469
- return rewriteNode(value, "schema", stripExamples);
470
600
  }
471
- function childMode(key) {
472
- if (DATA_KEYS.has(key)) {
473
- return "data";
601
+ function appendBracketPairs(params, key, value) {
602
+ if (Array.isArray(value)) {
603
+ for (const item of value) {
604
+ if (item !== null && typeof item === "object") {
605
+ appendBracketPairs(params, `${key}[]`, item);
606
+ } else {
607
+ params.append(key, String(item));
608
+ }
609
+ }
610
+ return;
474
611
  }
475
- return SCHEMA_MAP_KEYS.has(key) ? "schemaMap" : "schema";
476
- }
477
- function componentSchemaName(ref) {
478
- for (const prefix of ["#/components/schemas/", "#/$defs/"]) {
479
- if (ref.startsWith(prefix)) {
480
- return ref.slice(prefix.length).replaceAll("~1", "/").replaceAll("~0", "~");
612
+ if (value !== null && typeof value === "object") {
613
+ for (const [subKey, subValue] of Object.entries(
614
+ value
615
+ )) {
616
+ if (subValue !== void 0) {
617
+ appendBracketPairs(params, `${key}[${subKey}]`, subValue);
618
+ }
481
619
  }
620
+ return;
482
621
  }
483
- return void 0;
622
+ params.append(key, String(value));
484
623
  }
485
- function extractBodyProperties(requestBody, sharedDefs) {
486
- const properties = /* @__PURE__ */ new Map();
487
- const content = requestBody?.content;
488
- if (!content) {
489
- return { properties };
490
- }
491
- const json = findMediaType(content, "application/json");
492
- const form = findMediaType(content, "application/x-www-form-urlencoded");
493
- if (json === void 0 && form === void 0) {
494
- const contentTypes = Object.keys(content);
495
- return contentTypes.length > 0 ? { properties, unsupportedBodyContentType: contentTypes[0] } : { properties };
624
+ function appendQueryValue(query, name, style, value, explode) {
625
+ if (style === "deepObject") {
626
+ appendBracketPairs(query, name, value);
627
+ return;
496
628
  }
497
- const bodyEncoding = json === void 0 ? "form" : "json";
498
- const declaredSchema = bodyEncoding === "json" ? json?.schema : form?.schema;
499
- if (!declaredSchema) {
500
- return { bodyEncoding, properties };
629
+ if (style === "spaceDelimited" || style === "pipeDelimited") {
630
+ const items = Array.isArray(value) ? value : [value];
631
+ const separator = style === "spaceDelimited" ? " " : "|";
632
+ query.append(name, items.map(String).join(separator));
633
+ return;
501
634
  }
502
- const schema = bodyEncoding === "form" ? resolveComponentRef(declaredSchema, sharedDefs) : declaredSchema;
503
- const schemaProperties = schema.properties;
504
- if (schema.type === "object" && schemaProperties && (bodyEncoding === "form" || schema.additionalProperties === false || Object.keys(schemaProperties).length > 0)) {
505
- const requiredNames = new Set(
506
- schema.required ?? []
507
- );
508
- for (const [name, propertySchema] of Object.entries(schemaProperties)) {
509
- properties.set(name, {
510
- required: requiredNames.has(name),
511
- schema: propertySchema
512
- });
635
+ if ((style === void 0 || style === "form") && value !== null && typeof value === "object" && !Array.isArray(value)) {
636
+ const entries = Object.entries(value);
637
+ if (explode === false) {
638
+ if (entries.length > 0) {
639
+ query.append(
640
+ name,
641
+ entries.flatMap(([key, item]) => [key, String(item)]).join(",")
642
+ );
643
+ }
644
+ } else {
645
+ for (const [key, item] of entries) {
646
+ query.append(key, String(item));
647
+ }
513
648
  }
514
- return { bodyEncoding, properties };
649
+ return;
515
650
  }
516
- if (bodyEncoding === "form") {
517
- return {
518
- properties,
519
- unsupportedBodyContentType: "application/x-www-form-urlencoded"
520
- };
651
+ if ((style === void 0 || style === "form") && explode === false && Array.isArray(value) && value.length > 0) {
652
+ query.append(name, value.map(String).join(","));
653
+ return;
654
+ }
655
+ for (const item of Array.isArray(value) ? value : [value]) {
656
+ query.append(name, String(item));
521
657
  }
522
- properties.set("body", {
523
- required: requestBody?.required ?? false,
524
- schema
525
- });
526
- return { bodyEncoding, properties, wholeBodyKey: "body" };
527
658
  }
528
- function filterReferencedDefs(node, allDefs) {
529
- const referenced = /* @__PURE__ */ new Set();
530
- const stack = [node];
531
- while (stack.length > 0) {
532
- const current = stack.pop();
533
- if (Array.isArray(current)) {
534
- stack.push(...current);
535
- continue;
659
+ function assertNoSubstitutedDotSegments(route, resolvedPath) {
660
+ const templateSegments = new Set(route.path.split("/"));
661
+ for (const segment of resolvedPath.split("/")) {
662
+ if ((segment === "." || segment === "..") && !templateSegments.has(segment)) {
663
+ throw new UserError(
664
+ `${route.method.toUpperCase()} ${route.path}: path parameter values must not produce a "${segment}" path segment.`
665
+ );
536
666
  }
537
- if (!current || typeof current !== "object") {
538
- continue;
667
+ }
668
+ }
669
+ function encodeCookieValue(mapping, value) {
670
+ return (Array.isArray(value) ? value : [value]).map((item) => {
671
+ const text = String(item);
672
+ if (mapping.style !== "cookie") {
673
+ return encodePathComponent(text);
539
674
  }
675
+ if (/[;,]/.test(text)) {
676
+ throw new UserError(
677
+ `Cookie parameter "${mapping.name}" uses style "cookie" and its value contains ";" or ","`
678
+ );
679
+ }
680
+ return text;
681
+ }).join(",");
682
+ }
683
+ function encodeFormBody(payload) {
684
+ const params = new URLSearchParams();
685
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
540
686
  for (const [key, value] of Object.entries(
541
- current
687
+ payload
542
688
  )) {
543
- if (key === "$ref" && typeof value === "string" && value.startsWith("#/$defs/")) {
544
- const [first = ""] = value.slice("#/$defs/".length).split("/");
545
- const name = first.replaceAll("~1", "/").replaceAll("~0", "~");
546
- if (allDefs[name] && !referenced.has(name)) {
547
- referenced.add(name);
548
- stack.push(allDefs[name]);
549
- }
689
+ if (value === void 0) {
550
690
  continue;
551
691
  }
552
- stack.push(value);
692
+ appendBracketPairs(params, key, value);
553
693
  }
554
694
  }
555
- if (referenced.size === 0) {
556
- return void 0;
557
- }
558
- return Object.fromEntries(
559
- [...referenced].map((name) => [name, allDefs[name]])
560
- );
695
+ return params.toString();
561
696
  }
562
- function findMediaType(content, mediaType) {
563
- if (!content) {
564
- return void 0;
565
- }
566
- const key = Object.hasOwn(content, mediaType) ? mediaType : Object.keys(content).find(
567
- (candidate) => candidate.split(";")[0].trim().toLowerCase() === mediaType
697
+ function encodePathComponent(value) {
698
+ return encodeURIComponent(value).replace(
699
+ /[!'()*]/g,
700
+ (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
568
701
  );
569
- return key === void 0 ? void 0 : content[key];
570
702
  }
571
- function normalizeExclusiveBound(schema, exclusiveKey, boundKey) {
572
- const { [exclusiveKey]: exclusive, ...rest } = schema;
573
- if (typeof exclusive !== "boolean") {
574
- return schema;
703
+ function encodePathValue(value, style, explode) {
704
+ if ((style === void 0 || style === "simple") && Array.isArray(value) && value.every((item) => item === null || typeof item !== "object")) {
705
+ return value.map((item) => encodePathComponent(String(item))).join(",");
575
706
  }
576
- if (!exclusive || typeof rest[boundKey] !== "number") {
577
- return rest;
707
+ if ((style === void 0 || style === "simple") && value !== null && typeof value === "object" && !Array.isArray(value)) {
708
+ const entries = Object.entries(value);
709
+ if (entries.every(([, item]) => item === null || typeof item !== "object")) {
710
+ return entries.map(([key, item]) => {
711
+ const encodedKey = encodePathComponent(key);
712
+ const encodedValue = encodePathComponent(String(item));
713
+ return explode === true ? `${encodedKey}=${encodedValue}` : `${encodedKey},${encodedValue}`;
714
+ }).join(",");
715
+ }
578
716
  }
579
- const { [boundKey]: bound, ...unbounded } = rest;
580
- return { ...unbounded, [exclusiveKey]: bound };
581
- }
582
- function normalizeExclusiveBounds(schema) {
583
- return normalizeExclusiveBound(
584
- normalizeExclusiveBound(schema, "exclusiveMinimum", "minimum"),
585
- "exclusiveMaximum",
586
- "maximum"
587
- );
717
+ return encodeURIComponent(String(value));
588
718
  }
589
- function normalizeNullable(schema) {
590
- if (!("nullable" in schema)) {
591
- return schema;
592
- }
593
- const { nullable, ...rest } = schema;
594
- if (nullable !== true) {
595
- return rest;
596
- }
597
- const { type } = rest;
598
- if (typeof type === "string") {
599
- return { ...rest, type: [type, "null"] };
600
- }
601
- if (Array.isArray(type)) {
602
- return { ...rest, type: [.../* @__PURE__ */ new Set(["null", ...type])] };
719
+ async function resolveHeaders(headers, context) {
720
+ if (!headers) {
721
+ return {};
603
722
  }
604
- return rest;
723
+ return typeof headers === "function" ? await headers(context) : { ...headers };
605
724
  }
606
- function rewriteNode(value, mode, stripExamples = false) {
607
- if (mode === "data") {
608
- return value;
609
- }
610
- if (Array.isArray(value)) {
611
- return value.map((item) => rewriteNode(item, "schema", stripExamples));
612
- }
613
- if (!value || typeof value !== "object") {
614
- return value;
725
+ function serializeQuery(query, allowReserved) {
726
+ const encoded = query.toString();
727
+ if (!allowReserved) {
728
+ return encoded;
615
729
  }
616
- const entries = Object.entries(value).filter(
617
- ([key]) => !stripExamples || mode !== "schema" || !STRIP_KEYS.has(key)
618
- ).map(([key, entryValue]) => {
619
- if (mode === "schemaMap") {
620
- return [key, rewriteNode(entryValue, "schema", stripExamples)];
621
- }
622
- if (key === "$ref" && typeof entryValue === "string" && entryValue.startsWith("#/components/schemas/")) {
623
- return [key, entryValue.replace("#/components/schemas/", "#/$defs/")];
624
- }
625
- return [key, rewriteNode(entryValue, childMode(key), stripExamples)];
626
- });
627
- const rewritten = Object.fromEntries(entries);
628
- return mode === "schemaMap" ? rewritten : normalizeExclusiveBounds(normalizeNullable(rewritten));
730
+ return encoded.split("&").map((pair) => {
731
+ const [name, value] = pair.split("=");
732
+ const expanded = value.replace(
733
+ /\+|%25([\da-f]{2})|%(21|24|27|28|29|2c|2f|3a|3b|3f|40|7e)/gi,
734
+ (match, triple) => triple ? `%${triple}` : match === "+" ? "%20" : decodeURIComponent(match)
735
+ );
736
+ return `${name}=${expanded}`;
737
+ }).join("&");
629
738
  }
630
739
 
631
740
  // src/openapi/resourceMapping.ts
@@ -857,12 +966,13 @@ async function fromOpenAPI(options) {
857
966
  const outputSchema = outputSchemaJson ? jsonSchemaAdapter(outputSchemaJson) : void 0;
858
967
  server.addTool({
859
968
  description: route.summary ?? `${route.method.toUpperCase()} ${route.path}`,
860
- execute: async (args, { signal }) => {
969
+ execute: async (args, context) => {
861
970
  const result = await executeRequest({
862
971
  ...execOptions,
863
972
  args,
864
973
  bodyEncoding,
865
- signal,
974
+ context,
975
+ signal: context.signal,
866
976
  wholeBodyKey
867
977
  });
868
978
  return resolveToolResult(result, outputSchema);
@@ -887,8 +997,8 @@ function registerResource(server, route, name, parameterMap, requiredKeys, execO
887
997
  if (mapping.kind === "resource") {
888
998
  server.addResource({
889
999
  description,
890
- load: async () => wrapAsResourceResult(
891
- await executeRequest({ ...execOptions, args: {} })
1000
+ load: async (_auth, context) => wrapAsResourceResult(
1001
+ await executeRequest({ ...execOptions, args: {}, context })
892
1002
  ),
893
1003
  name,
894
1004
  uri: mapping.uri
@@ -898,10 +1008,11 @@ function registerResource(server, route, name, parameterMap, requiredKeys, execO
898
1008
  server.addResourceTemplate({
899
1009
  arguments: mapping.args,
900
1010
  description,
901
- load: async (args) => wrapAsResourceResult(
1011
+ load: async (args, _auth, context) => wrapAsResourceResult(
902
1012
  await executeRequest({
903
1013
  ...execOptions,
904
- args
1014
+ args,
1015
+ context
905
1016
  })
906
1017
  ),
907
1018
  name,