fastmcp 4.18.0 → 4.19.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.
@@ -28,7 +28,7 @@ function isHttpUrl(value) {
28
28
  // src/openapi/naming.ts
29
29
  var MAX_NAME_LENGTH = 56;
30
30
  var MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;
31
- function generateToolNames(routes, mcpNames) {
31
+ function generateNames(routes, mcpNames) {
32
32
  const names = /* @__PURE__ */ new Map();
33
33
  const used = /* @__PURE__ */ new Set();
34
34
  for (const route of routes) {
@@ -104,12 +104,18 @@ async function executeRequest(options) {
104
104
  url.search = query.toString();
105
105
  let body;
106
106
  if (Object.keys(bodyProps).length > 0) {
107
- if (!headers.has("content-type")) {
108
- headers.set("content-type", "application/json");
107
+ const payload = options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps;
108
+ if (options.bodyEncoding === "form") {
109
+ if (!headers.has("content-type")) {
110
+ headers.set("content-type", "application/x-www-form-urlencoded");
111
+ }
112
+ body = encodeFormBody(payload);
113
+ } else {
114
+ if (!headers.has("content-type")) {
115
+ headers.set("content-type", "application/json");
116
+ }
117
+ body = JSON.stringify(payload);
109
118
  }
110
- body = JSON.stringify(
111
- options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps
112
- );
113
119
  }
114
120
  const response = await options.fetchImpl(url.toString(), {
115
121
  body,
@@ -156,6 +162,25 @@ function resolveBaseUrl(servers, origin, overrideUrl) {
156
162
  return new URL(url, origin).toString().replace(/\/$/, "");
157
163
  }
158
164
  }
165
+ function encodeFormBody(payload) {
166
+ const params = new URLSearchParams();
167
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
168
+ for (const [key, value] of Object.entries(
169
+ payload
170
+ )) {
171
+ if (value === void 0) {
172
+ continue;
173
+ }
174
+ for (const item of Array.isArray(value) ? value : [value]) {
175
+ params.append(
176
+ key,
177
+ item !== null && typeof item === "object" ? JSON.stringify(item) : String(item)
178
+ );
179
+ }
180
+ }
181
+ }
182
+ return params.toString();
183
+ }
159
184
  async function resolveHeaders(headers) {
160
185
  if (!headers) {
161
186
  return {};
@@ -163,6 +188,38 @@ async function resolveHeaders(headers) {
163
188
  return typeof headers === "function" ? await headers() : { ...headers };
164
189
  }
165
190
 
191
+ // src/openapi/resourceMapping.ts
192
+ function buildResourceMapping(route, name, parameterMap, requiredKeys) {
193
+ const entries = Object.entries(parameterMap);
194
+ if (entries.length === 0) {
195
+ return { kind: "resource", uri: `openapi://${name}${route.path}` };
196
+ }
197
+ const required = new Set(_nullishCoalesce(requiredKeys, () => ( [])));
198
+ const args = [];
199
+ const queryKeys = [];
200
+ let path = route.path;
201
+ for (const [flatKey, mapping] of entries) {
202
+ if (mapping.in === "path") {
203
+ if (flatKey !== mapping.name) {
204
+ path = path.replace(`{${mapping.name}}`, `{${flatKey}}`);
205
+ }
206
+ } else {
207
+ queryKeys.push(flatKey);
208
+ }
209
+ args.push({ name: flatKey, required: required.has(flatKey) });
210
+ }
211
+ const uriTemplate = `openapi://${name}${path}` + (queryKeys.length > 0 ? `{?${queryKeys.join(",")}}` : "");
212
+ return { args, kind: "template", uriTemplate };
213
+ }
214
+ function isEligibleForResource(route) {
215
+ return route.parameters.every((param) => {
216
+ if (param.in === "header" || param.in === "cookie") {
217
+ return false;
218
+ }
219
+ return _optionalChain([param, 'access', _11 => _11.schema, 'optionalAccess', _12 => _12.type]) !== "array";
220
+ });
221
+ }
222
+
166
223
  // src/openapi/routes.ts
167
224
  var HTTP_METHODS = ["get", "put", "post", "delete", "patch"];
168
225
  function extractRoutes(document) {
@@ -217,7 +274,7 @@ function resolveRef(document, value) {
217
274
  );
218
275
  let node = document;
219
276
  for (const segment of segments) {
220
- node = _optionalChain([node, 'optionalAccess', _11 => _11[segment]]);
277
+ node = _optionalChain([node, 'optionalAccess', _13 => _13[segment]]);
221
278
  }
222
279
  return node;
223
280
  }
@@ -238,8 +295,14 @@ function buildFlatSchema(route, sharedDefs) {
238
295
  list.push(param);
239
296
  byName.set(param.name, list);
240
297
  }
241
- const { properties: bodyProperties, wholeBodyKey } = extractBodyProperties(
242
- route.method === "get" ? void 0 : route.requestBody
298
+ const {
299
+ bodyEncoding,
300
+ properties: bodyProperties,
301
+ unsupportedBodyContentType,
302
+ wholeBodyKey
303
+ } = extractBodyProperties(
304
+ route.method === "get" ? void 0 : route.requestBody,
305
+ sharedDefs
243
306
  );
244
307
  const properties = {};
245
308
  const required = [];
@@ -274,10 +337,16 @@ function buildFlatSchema(route, sharedDefs) {
274
337
  if (usedDefs) {
275
338
  flatSchema.$defs = usedDefs;
276
339
  }
277
- return { flatSchema, parameterMap, wholeBodyKey };
340
+ return {
341
+ bodyEncoding,
342
+ flatSchema,
343
+ parameterMap,
344
+ unsupportedBodyContentType,
345
+ wholeBodyKey
346
+ };
278
347
  }
279
348
  function buildSharedDefs(document) {
280
- const schemas = _optionalChain([document, 'access', _12 => _12.components, 'optionalAccess', _13 => _13.schemas]);
349
+ const schemas = _optionalChain([document, 'access', _14 => _14.components, 'optionalAccess', _15 => _15.schemas]);
281
350
  if (!schemas || Object.keys(schemas).length === 0) {
282
351
  return void 0;
283
352
  }
@@ -292,12 +361,32 @@ function childMode(key) {
292
361
  }
293
362
  return SCHEMA_MAP_KEYS.has(key) ? "schemaMap" : "schema";
294
363
  }
295
- function extractBodyProperties(requestBody) {
364
+ function componentSchemaName(ref) {
365
+ for (const prefix of ["#/components/schemas/", "#/$defs/"]) {
366
+ if (ref.startsWith(prefix)) {
367
+ return ref.slice(prefix.length);
368
+ }
369
+ }
370
+ return void 0;
371
+ }
372
+ function extractBodyProperties(requestBody, sharedDefs) {
296
373
  const properties = /* @__PURE__ */ new Map();
297
- const schema = _optionalChain([requestBody, 'optionalAccess', _14 => _14.content, 'optionalAccess', _15 => _15["application/json"], 'optionalAccess', _16 => _16.schema]);
298
- if (!schema) {
374
+ const content = _optionalChain([requestBody, 'optionalAccess', _16 => _16.content]);
375
+ if (!content) {
299
376
  return { properties };
300
377
  }
378
+ const hasJson = "application/json" in content;
379
+ const hasForm = "application/x-www-form-urlencoded" in content;
380
+ if (!hasJson && !hasForm) {
381
+ const contentTypes = Object.keys(content);
382
+ return contentTypes.length > 0 ? { properties, unsupportedBodyContentType: contentTypes[0] } : { properties };
383
+ }
384
+ const bodyEncoding = hasJson ? "json" : "form";
385
+ const declaredSchema = hasJson ? _optionalChain([content, 'access', _17 => _17["application/json"], 'optionalAccess', _18 => _18.schema]) : _optionalChain([content, 'access', _19 => _19["application/x-www-form-urlencoded"], 'optionalAccess', _20 => _20.schema]);
386
+ if (!declaredSchema) {
387
+ return { bodyEncoding, properties };
388
+ }
389
+ const schema = bodyEncoding === "form" ? resolveComponentRef(declaredSchema, sharedDefs) : declaredSchema;
301
390
  const schemaProperties = schema.properties;
302
391
  if (schema.type === "object" && schemaProperties) {
303
392
  const requiredNames = new Set(
@@ -309,13 +398,19 @@ function extractBodyProperties(requestBody) {
309
398
  schema: propertySchema
310
399
  });
311
400
  }
312
- return { properties };
401
+ return { bodyEncoding, properties };
402
+ }
403
+ if (bodyEncoding === "form") {
404
+ return {
405
+ properties,
406
+ unsupportedBodyContentType: "application/x-www-form-urlencoded"
407
+ };
313
408
  }
314
409
  properties.set("body", {
315
- required: _nullishCoalesce(_optionalChain([requestBody, 'optionalAccess', _17 => _17.required]), () => ( false)),
410
+ required: _nullishCoalesce(_optionalChain([requestBody, 'optionalAccess', _21 => _21.required]), () => ( false)),
316
411
  schema
317
412
  });
318
- return { properties, wholeBodyKey: "body" };
413
+ return { bodyEncoding, properties, wholeBodyKey: "body" };
319
414
  }
320
415
  function filterReferencedDefs(node, allDefs) {
321
416
  const referenced = /* @__PURE__ */ new Set();
@@ -366,6 +461,22 @@ function normalizeNullable(schema) {
366
461
  }
367
462
  return rest;
368
463
  }
464
+ function resolveComponentRef(schema, sharedDefs) {
465
+ const seen = /* @__PURE__ */ new Set();
466
+ let current = schema;
467
+ let ref = current.$ref;
468
+ while (typeof ref === "string") {
469
+ const name = componentSchemaName(ref);
470
+ const target = name === void 0 ? void 0 : _optionalChain([sharedDefs, 'optionalAccess', _22 => _22[name]]);
471
+ if (name === void 0 || target === void 0 || seen.has(name)) {
472
+ break;
473
+ }
474
+ seen.add(name);
475
+ current = target;
476
+ ref = current.$ref;
477
+ }
478
+ return current;
479
+ }
369
480
  function rewriteNode(value, mode) {
370
481
  if (mode === "data") {
371
482
  return value;
@@ -442,40 +553,109 @@ async function fromOpenAPI(options) {
442
553
  const { document, origin } = await loadSpec(options.spec);
443
554
  const routes = extractRoutes(document);
444
555
  const selected = selectRoutes(routes, options);
445
- const names = generateToolNames(selected, options.mcpNames);
556
+ const names = generateNames(selected, options.mcpNames);
446
557
  const sharedDefs = buildSharedDefs(document);
447
558
  const server = _nullishCoalesce(options.server, () => ( new (0, _chunkSYZRGVQXcjs.FastMCP)({
448
- name: _nullishCoalesce(_nullishCoalesce(options.name, () => ( _optionalChain([document, 'access', _18 => _18.info, 'optionalAccess', _19 => _19.title]))), () => ( "OpenAPI Server")),
559
+ name: _nullishCoalesce(_nullishCoalesce(options.name, () => ( _optionalChain([document, 'access', _23 => _23.info, 'optionalAccess', _24 => _24.title]))), () => ( "OpenAPI Server")),
449
560
  version: _nullishCoalesce(options.version, () => ( "1.0.0"))
450
561
  })));
562
+ const skippedOperations = [];
451
563
  for (const route of selected) {
452
564
  const name = names.get(route);
453
565
  if (!name) {
454
566
  continue;
455
567
  }
456
- const { flatSchema, parameterMap, wholeBodyKey } = buildFlatSchema(
568
+ const {
569
+ bodyEncoding,
570
+ flatSchema,
571
+ parameterMap,
572
+ unsupportedBodyContentType,
573
+ wholeBodyKey
574
+ } = buildFlatSchema(route, sharedDefs);
575
+ const execOptions = {
576
+ baseUrlOverride: options.baseUrl,
577
+ fetchImpl: _nullishCoalesce(options.fetch, () => ( fetch)),
578
+ headers: options.headers,
579
+ origin,
580
+ parameterMap,
457
581
  route,
458
- sharedDefs
459
- );
582
+ servers: document.servers
583
+ };
584
+ if (options.resources && route.method === "get" && isEligibleForResource(route)) {
585
+ registerResource(
586
+ server,
587
+ route,
588
+ name,
589
+ parameterMap,
590
+ flatSchema.required,
591
+ execOptions
592
+ );
593
+ continue;
594
+ }
595
+ if (unsupportedBodyContentType) {
596
+ skippedOperations.push({
597
+ contentType: unsupportedBodyContentType,
598
+ method: route.method,
599
+ path: route.path
600
+ });
601
+ continue;
602
+ }
460
603
  server.addTool({
461
604
  description: _nullishCoalesce(route.summary, () => ( `${route.method.toUpperCase()} ${route.path}`)),
462
605
  execute: async (args) => executeRequest({
606
+ ...execOptions,
463
607
  args,
464
- baseUrlOverride: options.baseUrl,
465
- fetchImpl: _nullishCoalesce(options.fetch, () => ( fetch)),
466
- headers: options.headers,
467
- origin,
468
- parameterMap,
469
- route,
470
- servers: document.servers,
608
+ bodyEncoding,
471
609
  wholeBodyKey
472
610
  }),
473
611
  name,
474
612
  parameters: _chunkSYZRGVQXcjs.jsonSchemaAdapter.call(void 0, flatSchema)
475
613
  });
476
614
  }
615
+ if (skippedOperations.length > 0) {
616
+ console.warn(
617
+ `fromOpenAPI: skipped ${skippedOperations.length} operation(s) whose request body can't be turned into tool parameters (supported: application/json, or application/x-www-form-urlencoded with a flat object schema): ` + skippedOperations.map(
618
+ (op) => `${op.method.toUpperCase()} ${op.path} (${op.contentType})`
619
+ ).join(", ")
620
+ );
621
+ }
477
622
  return server;
478
623
  }
624
+ function registerResource(server, route, name, parameterMap, requiredKeys, execOptions) {
625
+ const mapping = buildResourceMapping(route, name, parameterMap, requiredKeys);
626
+ const description = _nullishCoalesce(route.summary, () => ( `GET ${route.path}`));
627
+ if (mapping.kind === "resource") {
628
+ server.addResource({
629
+ description,
630
+ load: async () => wrapAsResourceResult(
631
+ await executeRequest({ ...execOptions, args: {} })
632
+ ),
633
+ name,
634
+ uri: mapping.uri
635
+ });
636
+ return;
637
+ }
638
+ server.addResourceTemplate({
639
+ arguments: mapping.args,
640
+ description,
641
+ load: async (args) => wrapAsResourceResult(
642
+ await executeRequest({
643
+ ...execOptions,
644
+ args
645
+ })
646
+ ),
647
+ name,
648
+ uriTemplate: mapping.uriTemplate
649
+ });
650
+ }
651
+ function wrapAsResourceResult(text) {
652
+ try {
653
+ JSON.parse(text);
654
+ return { mimeType: "application/json", text };
655
+ } catch (e3) {
656
+ return { mimeType: "text/plain", text };
657
+ }
658
+ }
479
659
 
480
660
 
481
661
  exports.fromOpenAPI = fromOpenAPI;
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/fastmcp/fastmcp/dist/openapi/index.cjs","../../src/openapi/loadSpec.ts","../../src/openapi/naming.ts","../../src/openapi/requestBuilder.ts","../../src/openapi/routes.ts","../../src/openapi/schemas.ts","../../src/openapi/selection.ts","../../src/openapi/fromOpenAPI.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fastmcp/fastmcp/dist/openapi/index.cjs","sourcesContent":[null,"import SwaggerParser from \"@apidevtools/swagger-parser\";\n\nimport type { BundledOpenApiDocument } from \"./types.js\";\n\nexport interface LoadedSpec {\n document: BundledOpenApiDocument;\n /**\n * The spec's own URL, when it was loaded from one. Used to resolve a\n * relative `servers[0].url` against the document's origin.\n */\n origin?: string;\n}\n\n/**\n * Loads and bundles an OpenAPI document, resolving local *and* external\n * `$ref`s (relative paths, absolute URLs, `other.yaml#/fragment`).\n *\n * `spec` is handed to swagger-parser as-is — a URL, file path, or object —\n * rather than being fetched and re-parsed here first. External refs resolve\n * relative to whatever document they were found in, so pre-fetching the\n * entry document and passing its parsed text as an object would resolve\n * every external ref against the wrong base (or none at all).\n */\nexport async function loadSpec(\n spec: Record<string, unknown> | string,\n): Promise<LoadedSpec> {\n const document = (await SwaggerParser.bundle(\n spec as never,\n )) as unknown as BundledOpenApiDocument;\n\n if (!document.openapi?.startsWith(\"3.\")) {\n throw new Error(\n `fromOpenAPI only supports OpenAPI 3.x documents (found ${\n document.openapi ?? document.swagger ?? \"an unrecognized version\"\n }). Swagger 2.0 is not supported.`,\n );\n }\n\n return {\n document,\n origin: typeof spec === \"string\" && isHttpUrl(spec) ? spec : undefined,\n };\n}\n\nfunction isHttpUrl(value: string): boolean {\n return value.startsWith(\"http://\") || value.startsWith(\"https://\");\n}\n","import type { HttpRoute } from \"./types.js\";\n\nconst MAX_NAME_LENGTH = 56;\n// Reserves room for a \"_<n>\" collision suffix so the final name never\n// exceeds MAX_NAME_LENGTH, however many collisions it takes.\nconst MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;\n\n/**\n * Generates a unique tool name per route.\n *\n * Ports the Python implementation's naming rule\n * (`server/providers/openapi/provider.py:_generate_default_name`): prefer\n * `mcpNames[operationId]`, then `operationId` (FastAPI-style `__` suffixes\n * stripped), falling back to `summary` or `{method}_{path}`; slugified and\n * capped at 56 characters, with `_2`, `_3`, ... appended on collision.\n *\n * Uniqueness is checked against the final (post-suffix) name, not just the\n * base — otherwise a spec whose own operationIds already look auto-suffixed\n * (e.g. both \"foo\" and \"foo_2\" present) could produce two identically-named\n * tools, one of which `FastMCP.addTool` would silently drop.\n */\nexport function generateToolNames(\n routes: HttpRoute[],\n mcpNames: Record<string, string> | undefined,\n): Map<HttpRoute, string> {\n const names = new Map<HttpRoute, string>();\n const used = new Set<string>();\n\n for (const route of routes) {\n const base = slugify(baseNameFor(route, mcpNames));\n let candidate = base;\n let suffix = 1;\n\n while (used.has(candidate)) {\n suffix += 1;\n candidate = `${base}_${suffix}`;\n }\n\n used.add(candidate);\n names.set(route, candidate);\n }\n\n return names;\n}\n\nfunction baseNameFor(\n route: HttpRoute,\n mcpNames: Record<string, string> | undefined,\n): string {\n if (route.operationId) {\n return mcpNames?.[route.operationId] ?? route.operationId.split(\"__\")[0];\n }\n\n return route.summary || `${route.method}_${route.path}`;\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .replace(/[^a-zA-Z0-9_]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\")\n .slice(0, MAX_BASE_LENGTH);\n\n return slug || \"operation\";\n}\n","import type { ParameterMapping } from \"./schemas.js\";\nimport type { FromOpenAPIOptions, HttpRoute, OpenApiServer } from \"./types.js\";\n\nimport { UserError } from \"../FastMCP.js\";\n\nexport interface ExecuteRequestOptions {\n args: Record<string, unknown>;\n baseUrlOverride?: string;\n fetchImpl: typeof fetch;\n headers?: FromOpenAPIOptions[\"headers\"];\n origin?: string;\n parameterMap: Record<string, ParameterMapping>;\n route: HttpRoute;\n servers: OpenApiServer[] | undefined;\n wholeBodyKey?: string;\n}\n\nexport async function executeRequest(\n options: ExecuteRequestOptions,\n): Promise<string> {\n const baseUrl = resolveBaseUrl(\n options.servers,\n options.origin,\n options.baseUrlOverride,\n );\n\n const pathParams: Record<string, string> = {};\n const query = new URLSearchParams();\n // A plain object keys headers case-sensitively, so a caller-supplied\n // header (e.g. \"Content-Type\") wouldn't be recognized as the same header\n // as one this function sets internally (e.g. \"content-type\") — `Headers`\n // normalizes casing, so `.set()` correctly overrides rather than\n // combining into a comma-joined, malformed value.\n const headers = new Headers(await resolveHeaders(options.headers));\n const bodyProps: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(options.args)) {\n const mapping = options.parameterMap[key];\n\n if (!mapping || value === undefined) {\n continue;\n }\n\n switch (mapping.in) {\n case \"body\":\n bodyProps[mapping.name] = value;\n break;\n case \"cookie\": {\n const existing = headers.get(\"cookie\");\n headers.set(\n \"cookie\",\n existing\n ? `${existing}; ${mapping.name}=${String(value)}`\n : `${mapping.name}=${String(value)}`,\n );\n break;\n }\n case \"header\":\n headers.set(mapping.name, String(value));\n break;\n case \"path\":\n pathParams[mapping.name] = String(value);\n break;\n case \"query\":\n for (const item of Array.isArray(value) ? value : [value]) {\n query.append(mapping.name, String(item));\n }\n break;\n }\n }\n\n let path = options.route.path;\n\n for (const [name, value] of Object.entries(pathParams)) {\n path = path.replace(`{${name}}`, encodeURIComponent(value));\n }\n\n const url = new URL(baseUrl.replace(/\\/$/, \"\") + path);\n url.search = query.toString();\n\n let body: string | undefined;\n\n if (Object.keys(bodyProps).length > 0) {\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n body = JSON.stringify(\n options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps,\n );\n }\n\n const response = await options.fetchImpl(url.toString(), {\n body,\n headers,\n method: options.route.method.toUpperCase(),\n });\n\n const text = await response.text();\n\n if (!response.ok) {\n throw new UserError(\n `${options.route.method.toUpperCase()} ${path} failed with ${response.status}: ${text.slice(0, 2000)}`,\n );\n }\n\n if (response.headers.get(\"content-type\")?.includes(\"json\")) {\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n }\n\n return text;\n}\n\n/**\n * Resolves `servers[0].url` the way a real HTTP client needs it resolved,\n * not just the way a schema validator would accept it: a relative URL (e.g.\n * Petstore's own `\"/api/v3\"`) is joined against the document's own origin,\n * not passed through verbatim.\n */\nexport function resolveBaseUrl(\n servers: OpenApiServer[] | undefined,\n origin: string | undefined,\n overrideUrl: string | undefined,\n): string {\n if (overrideUrl) {\n return overrideUrl.replace(/\\/$/, \"\");\n }\n\n const server = servers?.[0];\n\n if (!server) {\n throw new Error(\n \"The OpenAPI document has no `servers` entry. Pass `baseUrl` to fromOpenAPI() explicitly.\",\n );\n }\n\n let url = server.url;\n\n for (const [name, variable] of Object.entries(server.variables ?? {})) {\n url = url.replaceAll(`{${name}}`, variable.default);\n }\n\n try {\n return new URL(url).toString().replace(/\\/$/, \"\");\n } catch {\n if (!origin) {\n throw new Error(\n `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.`,\n );\n }\n\n return new URL(url, origin).toString().replace(/\\/$/, \"\");\n }\n}\n\nasync function resolveHeaders(\n headers: FromOpenAPIOptions[\"headers\"],\n): Promise<Record<string, string>> {\n if (!headers) {\n return {};\n }\n\n return typeof headers === \"function\" ? await headers() : { ...headers };\n}\n","import type {\n BundledOpenApiDocument,\n HttpMethod,\n HttpRoute,\n OpenApiParameter,\n OpenApiParameterRef,\n OpenApiRequestBody,\n} from \"./types.js\";\n\nconst HTTP_METHODS: HttpMethod[] = [\"get\", \"put\", \"post\", \"delete\", \"patch\"];\n\n/**\n * Walks a bundled document's `paths` into a flat list of routes, resolving\n * any structural (non-schema) `$ref`s on parameters and request bodies —\n * e.g. `#/components/parameters/Limit` — against the same document.\n *\n * Bundling (see `loadSpec.ts`) guarantees every remaining `$ref` here is\n * local, so a plain JSON-pointer lookup is enough.\n */\nexport function extractRoutes(document: BundledOpenApiDocument): HttpRoute[] {\n const routes: HttpRoute[] = [];\n\n for (const [path, pathItem] of Object.entries(document.paths ?? {})) {\n const pathLevelParams = (pathItem.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n for (const method of HTTP_METHODS) {\n const operation = pathItem[method];\n\n if (!operation) {\n continue;\n }\n\n const operationParams = (operation.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n routes.push({\n deprecated: operation.deprecated ?? false,\n method,\n operationId: operation.operationId,\n parameters: mergeParameters(pathLevelParams, operationParams),\n path,\n requestBody: operation.requestBody\n ? resolveRef<OpenApiRequestBody>(document, operation.requestBody)\n : undefined,\n summary: operation.summary,\n tags: operation.tags ?? [],\n });\n }\n }\n\n return routes;\n}\n\nfunction mergeParameters(\n pathLevel: OpenApiParameter[],\n operationLevel: OpenApiParameter[],\n): OpenApiParameter[] {\n const overridden = new Set(\n operationLevel.map((param) => `${param.in}:${param.name}`),\n );\n\n return [\n ...pathLevel.filter(\n (param) => !overridden.has(`${param.in}:${param.name}`),\n ),\n ...operationLevel,\n ];\n}\n\nfunction resolveRef<TValue>(\n document: BundledOpenApiDocument,\n value: OpenApiParameterRef | TValue,\n): TValue {\n if (!value || typeof value !== \"object\" || !(\"$ref\" in value)) {\n return value;\n }\n\n const pointer = value.$ref;\n\n if (!pointer.startsWith(\"#/\")) {\n // Bundling should have already turned every external ref into a local\n // one — if this fires, swagger-parser's output shape has changed.\n throw new Error(`Unexpected external $ref after bundling: ${pointer}`);\n }\n\n // swagger-parser synthesizes these pointers as URI fragments (e.g. a path\n // like \"/pets/{petId}\" becomes \"~1pets~1%7BpetId%7D\"), so each segment\n // needs its \"~1\"/\"~0\" escapes undone *and* percent-decoding, in that order.\n const segments = pointer\n .slice(2)\n .split(\"/\")\n .map((segment) =>\n decodeURIComponent(segment.replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\")),\n );\n\n let node: unknown = document;\n\n for (const segment of segments) {\n node = (node as Record<string, unknown> | undefined)?.[segment];\n }\n\n return node as TValue;\n}\n","import type { JsonSchemaObject } from \"../jsonSchemaAdapter.js\";\nimport type {\n BundledOpenApiDocument,\n HttpRoute,\n OpenApiParameter,\n OpenApiRequestBody,\n OpenApiSchema,\n ParameterLocation,\n} from \"./types.js\";\n\n/**\n * Keys whose values are name-to-schema maps: their child keys are\n * author-chosen names rather than JSON Schema keywords.\n */\nconst SCHEMA_MAP_KEYS = new Set([\n \"$defs\",\n \"definitions\",\n \"dependentSchemas\",\n \"patternProperties\",\n \"properties\",\n]);\n\n/**\n * Keys whose values are arbitrary instance data rather than schemas. A\n * sample payload may well contain a \"$ref\" or \"nullable\" key of its own.\n */\nconst DATA_KEYS = new Set([\"const\", \"default\", \"enum\", \"example\", \"examples\"]);\n\nexport interface FlatSchemaResult {\n flatSchema: JsonSchemaObject;\n parameterMap: Record<string, ParameterMapping>;\n /**\n * Set when the request body's schema is not a flat object (e.g. an array,\n * or a bare non-object `$ref`) — the whole body is exposed as a single\n * property under this key, rather than flattened into individual\n * properties.\n */\n wholeBodyKey?: string;\n}\n\nexport interface ParameterMapping {\n in: \"body\" | ParameterLocation;\n name: string;\n}\n\ntype WalkMode = \"data\" | \"schema\" | \"schemaMap\";\n\n/**\n * Flattens a route's path/query/header/cookie parameters and request body\n * into a single tool input schema.\n *\n * Collision precedence ports the Python implementation's rule\n * (`utilities/openapi/schemas.py:_combine_schemas_and_map_params`): a name\n * that collides across path/query/header/cookie gets suffixed\n * `{name}__{location}`; a request body property with a colliding name always\n * keeps its bare name.\n *\n * `GET` never contributes a request body: `fetch` (and the Fetch spec in\n * general) rejects a body on a GET request, so a tool built from a spec's\n * (legal, if unusual) `GET` + `requestBody` operation would be permanently\n * broken. The request body is simply not flattened into the schema for such\n * a route, rather than surfacing a schema that can never actually be called.\n */\nexport function buildFlatSchema(\n route: HttpRoute,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): FlatSchemaResult {\n const byName = new Map<string, OpenApiParameter[]>();\n\n for (const param of route.parameters) {\n const list = byName.get(param.name) ?? [];\n list.push(param);\n byName.set(param.name, list);\n }\n\n const { properties: bodyProperties, wholeBodyKey } = extractBodyProperties(\n route.method === \"get\" ? undefined : route.requestBody,\n );\n\n const properties: Record<string, OpenApiSchema> = {};\n const required: string[] = [];\n const parameterMap: Record<string, ParameterMapping> = {};\n\n for (const [name, occurrences] of byName) {\n const collides = occurrences.length > 1 || bodyProperties.has(name);\n\n for (const param of occurrences) {\n const key = collides ? `${name}__${param.in}` : name;\n\n properties[key] = rewriteComponentRefs(\n param.schema ?? { type: \"string\" },\n );\n parameterMap[key] = { in: param.in, name };\n\n if (param.in === \"path\" || param.required) {\n required.push(key);\n }\n }\n }\n\n for (const [name, { required: isRequired, schema }] of bodyProperties) {\n properties[name] = rewriteComponentRefs(schema);\n parameterMap[name] = { in: \"body\", name };\n\n if (isRequired) {\n required.push(name);\n }\n }\n\n const flatSchema: JsonSchemaObject = {\n additionalProperties: false,\n properties,\n type: \"object\",\n ...(required.length > 0 ? { required } : {}),\n };\n\n // Only the definitions this tool's own schema actually (transitively)\n // references — embedding the whole document's components.schemas into\n // every single tool would multiply the tools/list payload size by the\n // tool count for no benefit.\n const usedDefs = sharedDefs && filterReferencedDefs(properties, sharedDefs);\n\n if (usedDefs) {\n flatSchema.$defs = usedDefs;\n }\n\n return { flatSchema, parameterMap, wholeBodyKey };\n}\n\nexport function buildSharedDefs(\n document: BundledOpenApiDocument,\n): Record<string, OpenApiSchema> | undefined {\n const schemas = document.components?.schemas;\n\n if (!schemas || Object.keys(schemas).length === 0) {\n return undefined;\n }\n\n return rewriteNode(schemas, \"schemaMap\") as Record<string, OpenApiSchema>;\n}\n\n/**\n * Rewrites `$ref`s pointing at `#/components/schemas/...` to `#/$defs/...`,\n * so a per-tool schema that contains one can be handed to AJV standalone,\n * alongside a `$defs` object built from the document's `components.schemas`\n * (see `buildSharedDefs`). Ports the equivalent rewrite from the Python\n * implementation (`utilities/openapi/schemas.py:_replace_ref_with_defs`).\n *\n * Also normalizes OpenAPI 3.0's `nullable` keyword (see `normalizeNullable`),\n * since real specs carry both.\n */\nexport function rewriteComponentRefs<TValue>(value: TValue): TValue {\n return rewriteNode(value, \"schema\") as TValue;\n}\n\nfunction childMode(key: string): WalkMode {\n if (DATA_KEYS.has(key)) {\n return \"data\";\n }\n\n return SCHEMA_MAP_KEYS.has(key) ? \"schemaMap\" : \"schema\";\n}\n\nfunction extractBodyProperties(requestBody: OpenApiRequestBody | undefined): {\n properties: Map<string, { required: boolean; schema: OpenApiSchema }>;\n wholeBodyKey?: string;\n} {\n const properties = new Map<\n string,\n { required: boolean; schema: OpenApiSchema }\n >();\n\n const schema = requestBody?.content?.[\"application/json\"]?.schema;\n\n if (!schema) {\n return { properties };\n }\n\n const schemaProperties = schema.properties as\n | Record<string, OpenApiSchema>\n | undefined;\n\n if (schema.type === \"object\" && schemaProperties) {\n const requiredNames = new Set(\n (schema.required as string[] | undefined) ?? [],\n );\n\n for (const [name, propertySchema] of Object.entries(schemaProperties)) {\n properties.set(name, {\n required: requiredNames.has(name),\n schema: propertySchema,\n });\n }\n\n return { properties };\n }\n\n // Non-object body (array, bare $ref to a scalar/array, etc.) — expose the\n // whole thing as a single \"body\" property rather than flattening it.\n properties.set(\"body\", {\n required: requestBody?.required ?? false,\n schema,\n });\n\n return { properties, wholeBodyKey: \"body\" };\n}\n\n/**\n * Walks a schema fragment for `#/$defs/Name` refs and returns just those\n * definitions (transitively — a referenced def may itself reference\n * others), or `undefined` if none are referenced.\n */\nfunction filterReferencedDefs(\n node: unknown,\n allDefs: Record<string, OpenApiSchema>,\n): Record<string, OpenApiSchema> | undefined {\n const referenced = new Set<string>();\n const stack: unknown[] = [node];\n\n while (stack.length > 0) {\n const current = stack.pop();\n\n if (Array.isArray(current)) {\n stack.push(...current);\n continue;\n }\n\n if (!current || typeof current !== \"object\") {\n continue;\n }\n\n for (const [key, value] of Object.entries(\n current as Record<string, unknown>,\n )) {\n if (\n key === \"$ref\" &&\n typeof value === \"string\" &&\n value.startsWith(\"#/$defs/\")\n ) {\n const name = value.slice(\"#/$defs/\".length);\n\n if (allDefs[name] && !referenced.has(name)) {\n referenced.add(name);\n stack.push(allDefs[name]);\n }\n\n continue;\n }\n\n stack.push(value);\n }\n }\n\n if (referenced.size === 0) {\n return undefined;\n }\n\n return Object.fromEntries(\n [...referenced].map((name) => [name, allDefs[name]]),\n );\n}\n\n/**\n * OpenAPI 3.0's `nullable` keyword only makes sense alongside a sibling\n * `type`, which it widens (`nullable: true` + `type: \"string\"` means\n * \"string or null\") — but it is not itself standard JSON Schema. AJV\n * recognizes the keyword and throws ('\"nullable\" cannot be used without\n * \"type\"') if it finds one with no `type` on the same node, which real\n * specs do produce (e.g. `nullable` sibling to `oneOf`/`allOf`/`$ref`\n * instead of `type`, as in Box's API). Folded into `type` where there is\n * one to widen, dropped otherwise.\n */\nfunction normalizeNullable(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n if (!(\"nullable\" in schema)) {\n return schema;\n }\n\n const { nullable, type, ...rest } = schema;\n\n if (nullable !== true) {\n return rest;\n }\n\n if (typeof type === \"string\") {\n return { ...rest, type: [type, \"null\"] };\n }\n\n if (Array.isArray(type)) {\n return { ...rest, type: [...new Set([\"null\", ...type])] };\n }\n\n return rest;\n}\n\n/**\n * Walks a schema fragment, distinguishing the three kinds of node it can\n * reach — because only one of them is a schema whose keys are JSON Schema\n * keywords:\n *\n * - `\"schema\"` — a schema object. `$ref`/`nullable` here are keywords.\n * - `\"schemaMap\"` — a name-to-schema map (`properties`, `$defs`, ...). Its\n * keys are author-chosen names, so a property literally named `nullable`\n * or `$ref` is a field, not a keyword, and must survive untouched.\n * - `\"data\"` — arbitrary values (`default`, `enum`, `example`, ...). Not\n * schemas at all; passed through verbatim.\n *\n * Walking every node as a schema (as this originally did) silently deletes\n * a property named `nullable` from the generated tool schema, since\n * `normalizeNullable` cannot tell the keyword from a same-named field.\n */\nfunction rewriteNode(value: unknown, mode: WalkMode): unknown {\n if (mode === \"data\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => rewriteNode(item, \"schema\"));\n }\n\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n const entries = Object.entries(value as Record<string, unknown>).map(\n ([key, entryValue]): [string, unknown] => {\n if (mode === \"schemaMap\") {\n return [key, rewriteNode(entryValue, \"schema\")];\n }\n\n if (\n key === \"$ref\" &&\n typeof entryValue === \"string\" &&\n entryValue.startsWith(\"#/components/schemas/\")\n ) {\n return [key, entryValue.replace(\"#/components/schemas/\", \"#/$defs/\")];\n }\n\n return [key, rewriteNode(entryValue, childMode(key))];\n },\n );\n\n const rewritten = Object.fromEntries(entries) as Record<string, unknown>;\n\n return mode === \"schemaMap\" ? rewritten : normalizeNullable(rewritten);\n}\n","import type {\n FromOpenAPIOptions,\n HttpMethod,\n HttpRoute,\n OperationSummary,\n} from \"./types.js\";\n\n/**\n * If neither `include`/`exclude` nor `maxTools` was given, and a spec still\n * produces more operations than this, `selectRoutes` throws rather than\n * silently generating a wall of tools most clients can't usefully work with.\n */\nexport const DEFAULT_MAX_OPERATIONS = 40;\n\nconst METHOD_PRIORITY: Record<HttpMethod, number> = {\n delete: 4,\n get: 0,\n patch: 3,\n post: 1,\n put: 2,\n};\n\n/**\n * Filters and orders routes into the set that becomes tools.\n *\n * Deprecated operations are excluded by default. Ordering is deterministic\n * (method priority GET→POST→PUT→PATCH→DELETE, then path) so that, combined\n * with `maxTools`, truncation is legible rather than arbitrary.\n */\nexport function selectRoutes(\n routes: HttpRoute[],\n options: Pick<FromOpenAPIOptions, \"exclude\" | \"include\" | \"maxTools\">,\n): HttpRoute[] {\n let selected = routes.filter((route) => !route.deprecated);\n\n if (options.include) {\n const include = options.include;\n selected = selected.filter((route) => include(toSummary(route)));\n }\n\n if (options.exclude) {\n const exclude = options.exclude;\n selected = selected.filter((route) => !exclude(toSummary(route)));\n }\n\n selected = [...selected].sort((a, b) => {\n const byMethod = METHOD_PRIORITY[a.method] - METHOD_PRIORITY[b.method];\n return byMethod !== 0 ? byMethod : a.path.localeCompare(b.path);\n });\n\n const noSelectionGiven = !options.include && !options.exclude;\n\n if (\n noSelectionGiven &&\n options.maxTools === undefined &&\n selected.length > DEFAULT_MAX_OPERATIONS\n ) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds the default limit of ${DEFAULT_MAX_OPERATIONS}. ` +\n \"This is a deliberate stop, not a bug: turning every operation in a large spec into a tool produces a tool list most MCP clients can't use well. \" +\n \"Pass `include`/`exclude` to choose the operations you actually want, or `maxTools` to raise this limit explicitly.\",\n );\n }\n\n if (options.maxTools !== undefined && selected.length > options.maxTools) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds maxTools (${options.maxTools}). ` +\n \"Narrow the spec with `include`/`exclude`, or raise `maxTools`.\",\n );\n }\n\n return selected;\n}\n\nfunction toSummary(route: HttpRoute): OperationSummary {\n return {\n deprecated: route.deprecated,\n method: route.method,\n operationId: route.operationId,\n path: route.path,\n tags: route.tags,\n };\n}\n","import type { FromOpenAPIOptions } from \"./types.js\";\n\nimport { FastMCP } from \"../FastMCP.js\";\nimport { jsonSchemaAdapter } from \"../jsonSchemaAdapter.js\";\nimport { loadSpec } from \"./loadSpec.js\";\nimport { generateToolNames } from \"./naming.js\";\nimport { executeRequest } from \"./requestBuilder.js\";\nimport { extractRoutes } from \"./routes.js\";\nimport { buildFlatSchema, buildSharedDefs } from \"./schemas.js\";\nimport { selectRoutes } from \"./selection.js\";\n\n/**\n * Converts an OpenAPI 3.x document into an MCP server, one tool per\n * operation.\n *\n * See docs/openapi.md for the full option reference and known limitations.\n */\nexport async function fromOpenAPI(\n options: FromOpenAPIOptions,\n): Promise<FastMCP> {\n const { document, origin } = await loadSpec(options.spec);\n const routes = extractRoutes(document);\n const selected = selectRoutes(routes, options);\n const names = generateToolNames(selected, options.mcpNames);\n const sharedDefs = buildSharedDefs(document);\n\n const server =\n options.server ??\n new FastMCP({\n name: options.name ?? document.info?.title ?? \"OpenAPI Server\",\n version: options.version ?? \"1.0.0\",\n });\n\n for (const route of selected) {\n const name = names.get(route);\n\n if (!name) {\n continue;\n }\n\n const { flatSchema, parameterMap, wholeBodyKey } = buildFlatSchema(\n route,\n sharedDefs,\n );\n\n server.addTool({\n description:\n route.summary ?? `${route.method.toUpperCase()} ${route.path}`,\n execute: async (args) =>\n executeRequest({\n args: args as Record<string, unknown>,\n baseUrlOverride: options.baseUrl,\n fetchImpl: options.fetch ?? fetch,\n headers: options.headers,\n origin,\n parameterMap,\n route,\n servers: document.servers,\n wholeBodyKey,\n }),\n name,\n parameters: jsonSchemaAdapter(flatSchema),\n });\n }\n\n return server;\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/fastmcp/fastmcp/dist/openapi/index.cjs","../../src/openapi/loadSpec.ts","../../src/openapi/naming.ts","../../src/openapi/requestBuilder.ts","../../src/openapi/resourceMapping.ts","../../src/openapi/routes.ts","../../src/openapi/schemas.ts","../../src/openapi/selection.ts","../../src/openapi/fromOpenAPI.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fastmcp/fastmcp/dist/openapi/index.cjs","sourcesContent":[null,"import SwaggerParser from \"@apidevtools/swagger-parser\";\n\nimport type { BundledOpenApiDocument } from \"./types.js\";\n\nexport interface LoadedSpec {\n document: BundledOpenApiDocument;\n /**\n * The spec's own URL, when it was loaded from one. Used to resolve a\n * relative `servers[0].url` against the document's origin.\n */\n origin?: string;\n}\n\n/**\n * Loads and bundles an OpenAPI document, resolving local *and* external\n * `$ref`s (relative paths, absolute URLs, `other.yaml#/fragment`).\n *\n * `spec` is handed to swagger-parser as-is — a URL, file path, or object —\n * rather than being fetched and re-parsed here first. External refs resolve\n * relative to whatever document they were found in, so pre-fetching the\n * entry document and passing its parsed text as an object would resolve\n * every external ref against the wrong base (or none at all).\n */\nexport async function loadSpec(\n spec: Record<string, unknown> | string,\n): Promise<LoadedSpec> {\n const document = (await SwaggerParser.bundle(\n spec as never,\n )) as unknown as BundledOpenApiDocument;\n\n if (!document.openapi?.startsWith(\"3.\")) {\n throw new Error(\n `fromOpenAPI only supports OpenAPI 3.x documents (found ${\n document.openapi ?? document.swagger ?? \"an unrecognized version\"\n }). Swagger 2.0 is not supported.`,\n );\n }\n\n return {\n document,\n origin: typeof spec === \"string\" && isHttpUrl(spec) ? spec : undefined,\n };\n}\n\nfunction isHttpUrl(value: string): boolean {\n return value.startsWith(\"http://\") || value.startsWith(\"https://\");\n}\n","import type { HttpRoute } from \"./types.js\";\n\nconst MAX_NAME_LENGTH = 56;\n// Reserves room for a \"_<n>\" collision suffix so the final name never\n// exceeds MAX_NAME_LENGTH, however many collisions it takes.\nconst MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;\n\n/**\n * Generates a unique name per route — used both for tools and, when\n * `resources: true`, for the resources/resource templates a `GET` route\n * maps to instead. One pass over the whole selected set keeps names unique\n * regardless of which destination a route ends up at.\n *\n * Ports the Python implementation's naming rule\n * (`server/providers/openapi/provider.py:_generate_default_name`): prefer\n * `mcpNames[operationId]`, then `operationId` (FastAPI-style `__` suffixes\n * stripped), falling back to `summary` or `{method}_{path}`; slugified and\n * capped at 56 characters, with `_2`, `_3`, ... appended on collision.\n *\n * Uniqueness is checked against the final (post-suffix) name, not just the\n * base — otherwise a spec whose own operationIds already look auto-suffixed\n * (e.g. both \"foo\" and \"foo_2\" present) could produce two identically-named\n * tools, one of which `FastMCP.addTool` would silently drop.\n */\nexport function generateNames(\n routes: HttpRoute[],\n mcpNames: Record<string, string> | undefined,\n): Map<HttpRoute, string> {\n const names = new Map<HttpRoute, string>();\n const used = new Set<string>();\n\n for (const route of routes) {\n const base = slugify(baseNameFor(route, mcpNames));\n let candidate = base;\n let suffix = 1;\n\n while (used.has(candidate)) {\n suffix += 1;\n candidate = `${base}_${suffix}`;\n }\n\n used.add(candidate);\n names.set(route, candidate);\n }\n\n return names;\n}\n\nfunction baseNameFor(\n route: HttpRoute,\n mcpNames: Record<string, string> | undefined,\n): string {\n if (route.operationId) {\n return mcpNames?.[route.operationId] ?? route.operationId.split(\"__\")[0];\n }\n\n return route.summary || `${route.method}_${route.path}`;\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .replace(/[^a-zA-Z0-9_]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\")\n .slice(0, MAX_BASE_LENGTH);\n\n return slug || \"operation\";\n}\n","import type { ParameterMapping } from \"./schemas.js\";\nimport type { FromOpenAPIOptions, HttpRoute, OpenApiServer } from \"./types.js\";\n\nimport { UserError } from \"../FastMCP.js\";\n\nexport interface ExecuteRequestOptions {\n args: Record<string, unknown>;\n baseUrlOverride?: string;\n /** How to serialize a request body, if `args` contains any body-mapped values. */\n bodyEncoding?: \"form\" | \"json\";\n fetchImpl: typeof fetch;\n headers?: FromOpenAPIOptions[\"headers\"];\n origin?: string;\n parameterMap: Record<string, ParameterMapping>;\n route: HttpRoute;\n servers: OpenApiServer[] | undefined;\n wholeBodyKey?: string;\n}\n\nexport async function executeRequest(\n options: ExecuteRequestOptions,\n): Promise<string> {\n const baseUrl = resolveBaseUrl(\n options.servers,\n options.origin,\n options.baseUrlOverride,\n );\n\n const pathParams: Record<string, string> = {};\n const query = new URLSearchParams();\n // A plain object keys headers case-sensitively, so a caller-supplied\n // header (e.g. \"Content-Type\") wouldn't be recognized as the same header\n // as one this function sets internally (e.g. \"content-type\") — `Headers`\n // normalizes casing, so `.set()` correctly overrides rather than\n // combining into a comma-joined, malformed value.\n const headers = new Headers(await resolveHeaders(options.headers));\n const bodyProps: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(options.args)) {\n const mapping = options.parameterMap[key];\n\n if (!mapping || value === undefined) {\n continue;\n }\n\n switch (mapping.in) {\n case \"body\":\n bodyProps[mapping.name] = value;\n break;\n case \"cookie\": {\n const existing = headers.get(\"cookie\");\n headers.set(\n \"cookie\",\n existing\n ? `${existing}; ${mapping.name}=${String(value)}`\n : `${mapping.name}=${String(value)}`,\n );\n break;\n }\n case \"header\":\n headers.set(mapping.name, String(value));\n break;\n case \"path\":\n pathParams[mapping.name] = String(value);\n break;\n case \"query\":\n for (const item of Array.isArray(value) ? value : [value]) {\n query.append(mapping.name, String(item));\n }\n break;\n }\n }\n\n let path = options.route.path;\n\n for (const [name, value] of Object.entries(pathParams)) {\n path = path.replace(`{${name}}`, encodeURIComponent(value));\n }\n\n const url = new URL(baseUrl.replace(/\\/$/, \"\") + path);\n url.search = query.toString();\n\n let body: string | undefined;\n\n if (Object.keys(bodyProps).length > 0) {\n const payload = options.wholeBodyKey\n ? bodyProps[options.wholeBodyKey]\n : bodyProps;\n\n if (options.bodyEncoding === \"form\") {\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/x-www-form-urlencoded\");\n }\n\n body = encodeFormBody(payload);\n } else {\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n body = JSON.stringify(payload);\n }\n }\n\n const response = await options.fetchImpl(url.toString(), {\n body,\n headers,\n method: options.route.method.toUpperCase(),\n });\n\n const text = await response.text();\n\n if (!response.ok) {\n throw new UserError(\n `${options.route.method.toUpperCase()} ${path} failed with ${response.status}: ${text.slice(0, 2000)}`,\n );\n }\n\n if (response.headers.get(\"content-type\")?.includes(\"json\")) {\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n }\n\n return text;\n}\n\n/**\n * Resolves `servers[0].url` the way a real HTTP client needs it resolved,\n * not just the way a schema validator would accept it: a relative URL (e.g.\n * Petstore's own `\"/api/v3\"`) is joined against the document's own origin,\n * not passed through verbatim.\n */\nexport function resolveBaseUrl(\n servers: OpenApiServer[] | undefined,\n origin: string | undefined,\n overrideUrl: string | undefined,\n): string {\n if (overrideUrl) {\n return overrideUrl.replace(/\\/$/, \"\");\n }\n\n const server = servers?.[0];\n\n if (!server) {\n throw new Error(\n \"The OpenAPI document has no `servers` entry. Pass `baseUrl` to fromOpenAPI() explicitly.\",\n );\n }\n\n let url = server.url;\n\n for (const [name, variable] of Object.entries(server.variables ?? {})) {\n url = url.replaceAll(`{${name}}`, variable.default);\n }\n\n try {\n return new URL(url).toString().replace(/\\/$/, \"\");\n } catch {\n if (!origin) {\n throw new Error(\n `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.`,\n );\n }\n\n return new URL(url, origin).toString().replace(/\\/$/, \"\");\n }\n}\n\n/**\n * Serializes a flattened body payload as `application/x-www-form-urlencoded`.\n * Array values become repeated keys, matching the existing query-parameter\n * convention. A nested object/array *value* is JSON-stringified into a\n * single form value rather than expanded with bracket notation (e.g.\n * Stripe's own `metadata[key]=value` style) — correct for the flat scalar\n * properties that make up the overwhelming majority of real form-encoded\n * APIs (Stripe, Twilio), not a full form-encoding implementation.\n * `URLSearchParams` handles percent-encoding for free.\n */\nfunction encodeFormBody(payload: unknown): string {\n const params = new URLSearchParams();\n\n if (payload && typeof payload === \"object\" && !Array.isArray(payload)) {\n for (const [key, value] of Object.entries(\n payload as Record<string, unknown>,\n )) {\n if (value === undefined) {\n continue;\n }\n\n for (const item of Array.isArray(value) ? value : [value]) {\n params.append(\n key,\n item !== null && typeof item === \"object\"\n ? JSON.stringify(item)\n : String(item),\n );\n }\n }\n }\n\n return params.toString();\n}\n\nasync function resolveHeaders(\n headers: FromOpenAPIOptions[\"headers\"],\n): Promise<Record<string, string>> {\n if (!headers) {\n return {};\n }\n\n return typeof headers === \"function\" ? await headers() : { ...headers };\n}\n","import type { ParameterMapping } from \"./schemas.js\";\nimport type { HttpRoute } from \"./types.js\";\n\nexport type ResourceMapping =\n | {\n args: { name: string; required: boolean }[];\n kind: \"template\";\n uriTemplate: string;\n }\n | { kind: \"resource\"; uri: string };\n\n/**\n * Builds a static resource URI, or a resource template (URI + `arguments`),\n * for an eligible `GET` route — reusing the `parameterMap` and `required`\n * list `buildFlatSchema` already produced for it (path-always-required,\n * collision-suffixed flat keys) rather than re-deriving parameter\n * flattening from scratch.\n *\n * OpenAPI's `{petId}` path-parameter syntax is already valid RFC 6570 simple\n * string expansion, so the route's own path is reused verbatim except where\n * a flat key was collision-suffixed. Query parameters are appended as an\n * RFC 6570 query-expansion segment (`{?a,b}`), which `uri-templates`\n * (already a FastMCP dependency — see its own resource-template dispatch in\n * FastMCP.ts) parses and fills the same way it does path variables.\n */\nexport function buildResourceMapping(\n route: HttpRoute,\n name: string,\n parameterMap: Record<string, ParameterMapping>,\n requiredKeys: string[] | undefined,\n): ResourceMapping {\n const entries = Object.entries(parameterMap);\n\n if (entries.length === 0) {\n return { kind: \"resource\", uri: `openapi://${name}${route.path}` };\n }\n\n const required = new Set(requiredKeys ?? []);\n const args: { name: string; required: boolean }[] = [];\n const queryKeys: string[] = [];\n let path = route.path;\n\n for (const [flatKey, mapping] of entries) {\n if (mapping.in === \"path\") {\n if (flatKey !== mapping.name) {\n path = path.replace(`{${mapping.name}}`, `{${flatKey}}`);\n }\n } else {\n queryKeys.push(flatKey);\n }\n\n args.push({ name: flatKey, required: required.has(flatKey) });\n }\n\n const uriTemplate =\n `openapi://${name}${path}` +\n (queryKeys.length > 0 ? `{?${queryKeys.join(\",\")}}` : \"\");\n\n return { args, kind: \"template\", uriTemplate };\n}\n\n/**\n * Whether a `GET` route can become an MCP resource/resource template\n * instead of a tool, when `resources: true` is passed to `fromOpenAPI`. See\n * docs/openapi.md \"GET → resources\" for the reasoning behind each carve-out:\n *\n * - `header`/`cookie` parameters can't be expressed in a resource URI, and\n * MCP resource reads have no per-call side channel for them.\n * - An array-typed path/query parameter can't be represented consistently\n * between OpenAPI's query serialization (repeated keys) and RFC 6570's\n * array representation (comma-joined or `*`-exploded).\n *\n * A route failing either check falls through to the existing tool path —\n * this only ever *removes* operations from the tool list in favor of a\n * resource, never breaks one.\n */\nexport function isEligibleForResource(route: HttpRoute): boolean {\n return route.parameters.every((param) => {\n if (param.in === \"header\" || param.in === \"cookie\") {\n return false;\n }\n\n return param.schema?.type !== \"array\";\n });\n}\n","import type {\n BundledOpenApiDocument,\n HttpMethod,\n HttpRoute,\n OpenApiParameter,\n OpenApiParameterRef,\n OpenApiRequestBody,\n} from \"./types.js\";\n\nconst HTTP_METHODS: HttpMethod[] = [\"get\", \"put\", \"post\", \"delete\", \"patch\"];\n\n/**\n * Walks a bundled document's `paths` into a flat list of routes, resolving\n * any structural (non-schema) `$ref`s on parameters and request bodies —\n * e.g. `#/components/parameters/Limit` — against the same document.\n *\n * Bundling (see `loadSpec.ts`) guarantees every remaining `$ref` here is\n * local, so a plain JSON-pointer lookup is enough.\n */\nexport function extractRoutes(document: BundledOpenApiDocument): HttpRoute[] {\n const routes: HttpRoute[] = [];\n\n for (const [path, pathItem] of Object.entries(document.paths ?? {})) {\n const pathLevelParams = (pathItem.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n for (const method of HTTP_METHODS) {\n const operation = pathItem[method];\n\n if (!operation) {\n continue;\n }\n\n const operationParams = (operation.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n routes.push({\n deprecated: operation.deprecated ?? false,\n method,\n operationId: operation.operationId,\n parameters: mergeParameters(pathLevelParams, operationParams),\n path,\n requestBody: operation.requestBody\n ? resolveRef<OpenApiRequestBody>(document, operation.requestBody)\n : undefined,\n summary: operation.summary,\n tags: operation.tags ?? [],\n });\n }\n }\n\n return routes;\n}\n\nfunction mergeParameters(\n pathLevel: OpenApiParameter[],\n operationLevel: OpenApiParameter[],\n): OpenApiParameter[] {\n const overridden = new Set(\n operationLevel.map((param) => `${param.in}:${param.name}`),\n );\n\n return [\n ...pathLevel.filter(\n (param) => !overridden.has(`${param.in}:${param.name}`),\n ),\n ...operationLevel,\n ];\n}\n\nfunction resolveRef<TValue>(\n document: BundledOpenApiDocument,\n value: OpenApiParameterRef | TValue,\n): TValue {\n if (!value || typeof value !== \"object\" || !(\"$ref\" in value)) {\n return value;\n }\n\n const pointer = value.$ref;\n\n if (!pointer.startsWith(\"#/\")) {\n // Bundling should have already turned every external ref into a local\n // one — if this fires, swagger-parser's output shape has changed.\n throw new Error(`Unexpected external $ref after bundling: ${pointer}`);\n }\n\n // swagger-parser synthesizes these pointers as URI fragments (e.g. a path\n // like \"/pets/{petId}\" becomes \"~1pets~1%7BpetId%7D\"), so each segment\n // needs its \"~1\"/\"~0\" escapes undone *and* percent-decoding, in that order.\n const segments = pointer\n .slice(2)\n .split(\"/\")\n .map((segment) =>\n decodeURIComponent(segment.replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\")),\n );\n\n let node: unknown = document;\n\n for (const segment of segments) {\n node = (node as Record<string, unknown> | undefined)?.[segment];\n }\n\n return node as TValue;\n}\n","import type { JsonSchemaObject } from \"../jsonSchemaAdapter.js\";\nimport type {\n BundledOpenApiDocument,\n HttpRoute,\n OpenApiParameter,\n OpenApiRequestBody,\n OpenApiSchema,\n ParameterLocation,\n} from \"./types.js\";\n\n/**\n * Keys whose values are name-to-schema maps: their child keys are\n * author-chosen names rather than JSON Schema keywords.\n */\nconst SCHEMA_MAP_KEYS = new Set([\n \"$defs\",\n \"definitions\",\n \"dependentSchemas\",\n \"patternProperties\",\n \"properties\",\n]);\n\n/**\n * Keys whose values are arbitrary instance data rather than schemas. A\n * sample payload may well contain a \"$ref\" or \"nullable\" key of its own.\n */\nconst DATA_KEYS = new Set([\"const\", \"default\", \"enum\", \"example\", \"examples\"]);\n\n/**\n * Request body content types this module knows how to flatten and encode.\n * `application/json` wins when a route declares both.\n */\nexport const SUPPORTED_BODY_CONTENT_TYPES = [\n \"application/json\",\n \"application/x-www-form-urlencoded\",\n] as const;\n\nexport interface FlatSchemaResult {\n /** How the request body (if any properties were extracted) must be serialized. */\n bodyEncoding?: \"form\" | \"json\";\n flatSchema: JsonSchemaObject;\n parameterMap: Record<string, ParameterMapping>;\n /**\n * Set when `route.requestBody` declares a body this module can't carry:\n * either only in content type(s) it doesn't support (e.g.\n * `multipart/form-data`, `application/json-patch+json`,\n * `application/octet-stream`), or as `application/x-www-form-urlencoded`\n * with a schema that isn't a flat object, which form encoding can't\n * represent. Holds the content type the body was declared in. The caller\n * should not turn this route into a tool with a payload it can never\n * carry — see `fromOpenAPI.ts`.\n */\n unsupportedBodyContentType?: string;\n /**\n * Set when the request body's schema is not a flat object (e.g. an array,\n * or a bare non-object `$ref`) — the whole body is exposed as a single\n * property under this key, rather than flattened into individual\n * properties.\n */\n wholeBodyKey?: string;\n}\n\nexport interface ParameterMapping {\n in: \"body\" | ParameterLocation;\n name: string;\n}\n\ntype WalkMode = \"data\" | \"schema\" | \"schemaMap\";\n\n/**\n * Flattens a route's path/query/header/cookie parameters and request body\n * into a single tool input schema.\n *\n * Collision precedence ports the Python implementation's rule\n * (`utilities/openapi/schemas.py:_combine_schemas_and_map_params`): a name\n * that collides across path/query/header/cookie gets suffixed\n * `{name}__{location}`; a request body property with a colliding name always\n * keeps its bare name.\n *\n * `GET` never contributes a request body: `fetch` (and the Fetch spec in\n * general) rejects a body on a GET request, so a tool built from a spec's\n * (legal, if unusual) `GET` + `requestBody` operation would be permanently\n * broken. The request body is simply not flattened into the schema for such\n * a route, rather than surfacing a schema that can never actually be called.\n */\nexport function buildFlatSchema(\n route: HttpRoute,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): FlatSchemaResult {\n const byName = new Map<string, OpenApiParameter[]>();\n\n for (const param of route.parameters) {\n const list = byName.get(param.name) ?? [];\n list.push(param);\n byName.set(param.name, list);\n }\n\n const {\n bodyEncoding,\n properties: bodyProperties,\n unsupportedBodyContentType,\n wholeBodyKey,\n } = extractBodyProperties(\n route.method === \"get\" ? undefined : route.requestBody,\n sharedDefs,\n );\n\n const properties: Record<string, OpenApiSchema> = {};\n const required: string[] = [];\n const parameterMap: Record<string, ParameterMapping> = {};\n\n for (const [name, occurrences] of byName) {\n const collides = occurrences.length > 1 || bodyProperties.has(name);\n\n for (const param of occurrences) {\n const key = collides ? `${name}__${param.in}` : name;\n\n properties[key] = rewriteComponentRefs(\n param.schema ?? { type: \"string\" },\n );\n parameterMap[key] = { in: param.in, name };\n\n if (param.in === \"path\" || param.required) {\n required.push(key);\n }\n }\n }\n\n for (const [name, { required: isRequired, schema }] of bodyProperties) {\n properties[name] = rewriteComponentRefs(schema);\n parameterMap[name] = { in: \"body\", name };\n\n if (isRequired) {\n required.push(name);\n }\n }\n\n const flatSchema: JsonSchemaObject = {\n additionalProperties: false,\n properties,\n type: \"object\",\n ...(required.length > 0 ? { required } : {}),\n };\n\n // Only the definitions this tool's own schema actually (transitively)\n // references — embedding the whole document's components.schemas into\n // every single tool would multiply the tools/list payload size by the\n // tool count for no benefit.\n const usedDefs = sharedDefs && filterReferencedDefs(properties, sharedDefs);\n\n if (usedDefs) {\n flatSchema.$defs = usedDefs;\n }\n\n return {\n bodyEncoding,\n flatSchema,\n parameterMap,\n unsupportedBodyContentType,\n wholeBodyKey,\n };\n}\n\nexport function buildSharedDefs(\n document: BundledOpenApiDocument,\n): Record<string, OpenApiSchema> | undefined {\n const schemas = document.components?.schemas;\n\n if (!schemas || Object.keys(schemas).length === 0) {\n return undefined;\n }\n\n return rewriteNode(schemas, \"schemaMap\") as Record<string, OpenApiSchema>;\n}\n\n/**\n * Rewrites `$ref`s pointing at `#/components/schemas/...` to `#/$defs/...`,\n * so a per-tool schema that contains one can be handed to AJV standalone,\n * alongside a `$defs` object built from the document's `components.schemas`\n * (see `buildSharedDefs`). Ports the equivalent rewrite from the Python\n * implementation (`utilities/openapi/schemas.py:_replace_ref_with_defs`).\n *\n * Also normalizes OpenAPI 3.0's `nullable` keyword (see `normalizeNullable`),\n * since real specs carry both.\n */\nexport function rewriteComponentRefs<TValue>(value: TValue): TValue {\n return rewriteNode(value, \"schema\") as TValue;\n}\n\nfunction childMode(key: string): WalkMode {\n if (DATA_KEYS.has(key)) {\n return \"data\";\n }\n\n return SCHEMA_MAP_KEYS.has(key) ? \"schemaMap\" : \"schema\";\n}\n\nfunction componentSchemaName(ref: string): string | undefined {\n for (const prefix of [\"#/components/schemas/\", \"#/$defs/\"]) {\n if (ref.startsWith(prefix)) {\n return ref.slice(prefix.length);\n }\n }\n\n return undefined;\n}\n\n/**\n * Picks the request body's content type and flattens its schema.\n *\n * `application/json` wins if a route declares both it and\n * `application/x-www-form-urlencoded` (a fixed preference, not declaration\n * order — the latter isn't a reliable signal). A route whose body is only\n * declared under a content type this module doesn't handle at all (e.g.\n * `multipart/form-data`) reports `unsupportedBodyContentType` instead of\n * silently returning an empty property map — that emptiness is exactly what\n * a real Stripe/Twilio operation (both form-urlencoded-only) looked like\n * before this function read anything but JSON, and it produced a tool with\n * no way to carry its actual payload. A bare `content: {}` (no content\n * types at all) still means \"no body,\" not \"unsupported\" — and so does a\n * supported content type with no `schema` at all (a legal, if unusual,\n * \"any JSON body\" declaration): the content type itself is fine, there's\n * just nothing to flatten.\n *\n * A form-urlencoded body is very often declared as a bare `$ref` to a\n * component schema rather than inline — FastAPI emits\n * `#/components/schemas/Body_<operation>` for every form endpoint, and\n * Box's OAuth token/refresh/revoke operations do the same. The document is\n * bundled, not dereferenced (see `loadSpec.ts`), so that `$ref` is resolved\n * here against `sharedDefs` before deciding whether the body is a flat\n * object. Only the form path needs this: a JSON body that isn't a flat\n * object falls back to a single whole-body property, which a `$ref`\n * satisfies as-is.\n *\n * A non-object body (array, `$ref` to a scalar/array, etc.) can't be\n * form-urlencoded at all — form encoding is inherently flat key/value pairs\n * — so that combination is also reported as unsupported, rather than\n * `encodeFormBody` (requestBuilder.ts) silently sending an empty body for\n * data it has no way to represent.\n */\nfunction extractBodyProperties(\n requestBody: OpenApiRequestBody | undefined,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): {\n bodyEncoding?: \"form\" | \"json\";\n properties: Map<string, { required: boolean; schema: OpenApiSchema }>;\n unsupportedBodyContentType?: string;\n wholeBodyKey?: string;\n} {\n const properties = new Map<\n string,\n { required: boolean; schema: OpenApiSchema }\n >();\n\n const content = requestBody?.content;\n\n if (!content) {\n return { properties };\n }\n\n const hasJson = \"application/json\" in content;\n const hasForm = \"application/x-www-form-urlencoded\" in content;\n\n if (!hasJson && !hasForm) {\n const contentTypes = Object.keys(content);\n\n return contentTypes.length > 0\n ? { properties, unsupportedBodyContentType: contentTypes[0] }\n : { properties };\n }\n\n const bodyEncoding: \"form\" | \"json\" = hasJson ? \"json\" : \"form\";\n const declaredSchema = hasJson\n ? content[\"application/json\"]?.schema\n : content[\"application/x-www-form-urlencoded\"]?.schema;\n\n if (!declaredSchema) {\n // The content type is declared and supported; it just has no schema\n // (an unconstrained body) — nothing to flatten, but not unsupported.\n return { bodyEncoding, properties };\n }\n\n const schema =\n bodyEncoding === \"form\"\n ? resolveComponentRef(declaredSchema, sharedDefs)\n : declaredSchema;\n\n const schemaProperties = schema.properties as\n | Record<string, OpenApiSchema>\n | undefined;\n\n if (schema.type === \"object\" && schemaProperties) {\n const requiredNames = new Set(\n (schema.required as string[] | undefined) ?? [],\n );\n\n for (const [name, propertySchema] of Object.entries(schemaProperties)) {\n properties.set(name, {\n required: requiredNames.has(name),\n schema: propertySchema,\n });\n }\n\n return { bodyEncoding, properties };\n }\n\n if (bodyEncoding === \"form\") {\n return {\n properties,\n unsupportedBodyContentType: \"application/x-www-form-urlencoded\",\n };\n }\n\n // Non-object JSON body (array, bare $ref to a scalar/array, etc.) — expose\n // the whole thing as a single \"body\" property rather than flattening it.\n properties.set(\"body\", {\n required: requestBody?.required ?? false,\n schema,\n });\n\n return { bodyEncoding, properties, wholeBodyKey: \"body\" };\n}\n\n/**\n * Walks a schema fragment for `#/$defs/Name` refs and returns just those\n * definitions (transitively — a referenced def may itself reference\n * others), or `undefined` if none are referenced.\n */\nfunction filterReferencedDefs(\n node: unknown,\n allDefs: Record<string, OpenApiSchema>,\n): Record<string, OpenApiSchema> | undefined {\n const referenced = new Set<string>();\n const stack: unknown[] = [node];\n\n while (stack.length > 0) {\n const current = stack.pop();\n\n if (Array.isArray(current)) {\n stack.push(...current);\n continue;\n }\n\n if (!current || typeof current !== \"object\") {\n continue;\n }\n\n for (const [key, value] of Object.entries(\n current as Record<string, unknown>,\n )) {\n if (\n key === \"$ref\" &&\n typeof value === \"string\" &&\n value.startsWith(\"#/$defs/\")\n ) {\n const name = value.slice(\"#/$defs/\".length);\n\n if (allDefs[name] && !referenced.has(name)) {\n referenced.add(name);\n stack.push(allDefs[name]);\n }\n\n continue;\n }\n\n stack.push(value);\n }\n }\n\n if (referenced.size === 0) {\n return undefined;\n }\n\n return Object.fromEntries(\n [...referenced].map((name) => [name, allDefs[name]]),\n );\n}\n\n/**\n * OpenAPI 3.0's `nullable` keyword only makes sense alongside a sibling\n * `type`, which it widens (`nullable: true` + `type: \"string\"` means\n * \"string or null\") — but it is not itself standard JSON Schema. AJV\n * recognizes the keyword and throws ('\"nullable\" cannot be used without\n * \"type\"') if it finds one with no `type` on the same node, which real\n * specs do produce (e.g. `nullable` sibling to `oneOf`/`allOf`/`$ref`\n * instead of `type`, as in Box's API). Folded into `type` where there is\n * one to widen, dropped otherwise.\n */\nfunction normalizeNullable(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n if (!(\"nullable\" in schema)) {\n return schema;\n }\n\n const { nullable, type, ...rest } = schema;\n\n if (nullable !== true) {\n return rest;\n }\n\n if (typeof type === \"string\") {\n return { ...rest, type: [type, \"null\"] };\n }\n\n if (Array.isArray(type)) {\n return { ...rest, type: [...new Set([\"null\", ...type])] };\n }\n\n return rest;\n}\n\n/**\n * Follows a bare `$ref` into `components.schemas` — or its rewritten\n * `#/$defs/` form, which is what `sharedDefs` entries themselves carry —\n * until it reaches a concrete schema. A dangling or cyclic reference is\n * returned as-is rather than failing the whole conversion.\n */\nfunction resolveComponentRef(\n schema: OpenApiSchema,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): OpenApiSchema {\n const seen = new Set<string>();\n let current = schema;\n let ref = current.$ref;\n\n while (typeof ref === \"string\") {\n const name = componentSchemaName(ref);\n const target = name === undefined ? undefined : sharedDefs?.[name];\n\n if (name === undefined || target === undefined || seen.has(name)) {\n break;\n }\n\n seen.add(name);\n current = target;\n ref = current.$ref;\n }\n\n return current;\n}\n\n/**\n * Walks a schema fragment, distinguishing the three kinds of node it can\n * reach — because only one of them is a schema whose keys are JSON Schema\n * keywords:\n *\n * - `\"schema\"` — a schema object. `$ref`/`nullable` here are keywords.\n * - `\"schemaMap\"` — a name-to-schema map (`properties`, `$defs`, ...). Its\n * keys are author-chosen names, so a property literally named `nullable`\n * or `$ref` is a field, not a keyword, and must survive untouched.\n * - `\"data\"` — arbitrary values (`default`, `enum`, `example`, ...). Not\n * schemas at all; passed through verbatim.\n *\n * Walking every node as a schema (as this originally did) silently deletes\n * a property named `nullable` from the generated tool schema, since\n * `normalizeNullable` cannot tell the keyword from a same-named field.\n */\nfunction rewriteNode(value: unknown, mode: WalkMode): unknown {\n if (mode === \"data\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => rewriteNode(item, \"schema\"));\n }\n\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n const entries = Object.entries(value as Record<string, unknown>).map(\n ([key, entryValue]): [string, unknown] => {\n if (mode === \"schemaMap\") {\n return [key, rewriteNode(entryValue, \"schema\")];\n }\n\n if (\n key === \"$ref\" &&\n typeof entryValue === \"string\" &&\n entryValue.startsWith(\"#/components/schemas/\")\n ) {\n return [key, entryValue.replace(\"#/components/schemas/\", \"#/$defs/\")];\n }\n\n return [key, rewriteNode(entryValue, childMode(key))];\n },\n );\n\n const rewritten = Object.fromEntries(entries) as Record<string, unknown>;\n\n return mode === \"schemaMap\" ? rewritten : normalizeNullable(rewritten);\n}\n","import type {\n FromOpenAPIOptions,\n HttpMethod,\n HttpRoute,\n OperationSummary,\n} from \"./types.js\";\n\n/**\n * If neither `include`/`exclude` nor `maxTools` was given, and a spec still\n * produces more operations than this, `selectRoutes` throws rather than\n * silently generating a wall of tools most clients can't usefully work with.\n */\nexport const DEFAULT_MAX_OPERATIONS = 40;\n\nconst METHOD_PRIORITY: Record<HttpMethod, number> = {\n delete: 4,\n get: 0,\n patch: 3,\n post: 1,\n put: 2,\n};\n\n/**\n * Filters and orders routes into the set that becomes tools.\n *\n * Deprecated operations are excluded by default. Ordering is deterministic\n * (method priority GET→POST→PUT→PATCH→DELETE, then path) so that, combined\n * with `maxTools`, truncation is legible rather than arbitrary.\n */\nexport function selectRoutes(\n routes: HttpRoute[],\n options: Pick<FromOpenAPIOptions, \"exclude\" | \"include\" | \"maxTools\">,\n): HttpRoute[] {\n let selected = routes.filter((route) => !route.deprecated);\n\n if (options.include) {\n const include = options.include;\n selected = selected.filter((route) => include(toSummary(route)));\n }\n\n if (options.exclude) {\n const exclude = options.exclude;\n selected = selected.filter((route) => !exclude(toSummary(route)));\n }\n\n selected = [...selected].sort((a, b) => {\n const byMethod = METHOD_PRIORITY[a.method] - METHOD_PRIORITY[b.method];\n return byMethod !== 0 ? byMethod : a.path.localeCompare(b.path);\n });\n\n const noSelectionGiven = !options.include && !options.exclude;\n\n if (\n noSelectionGiven &&\n options.maxTools === undefined &&\n selected.length > DEFAULT_MAX_OPERATIONS\n ) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds the default limit of ${DEFAULT_MAX_OPERATIONS}. ` +\n \"This is a deliberate stop, not a bug: turning every operation in a large spec into a tool produces a tool list most MCP clients can't use well. \" +\n \"Pass `include`/`exclude` to choose the operations you actually want, or `maxTools` to raise this limit explicitly.\",\n );\n }\n\n if (options.maxTools !== undefined && selected.length > options.maxTools) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds maxTools (${options.maxTools}). ` +\n \"Narrow the spec with `include`/`exclude`, or raise `maxTools`.\",\n );\n }\n\n return selected;\n}\n\nfunction toSummary(route: HttpRoute): OperationSummary {\n return {\n deprecated: route.deprecated,\n method: route.method,\n operationId: route.operationId,\n path: route.path,\n tags: route.tags,\n };\n}\n","import type { ResourceResult } from \"../FastMCP.js\";\nimport type { ParameterMapping } from \"./schemas.js\";\nimport type { HttpRoute } from \"./types.js\";\nimport type { FromOpenAPIOptions } from \"./types.js\";\n\nimport { FastMCP } from \"../FastMCP.js\";\nimport { jsonSchemaAdapter } from \"../jsonSchemaAdapter.js\";\nimport { loadSpec } from \"./loadSpec.js\";\nimport { generateNames } from \"./naming.js\";\nimport { executeRequest } from \"./requestBuilder.js\";\nimport {\n buildResourceMapping,\n isEligibleForResource,\n} from \"./resourceMapping.js\";\nimport { extractRoutes } from \"./routes.js\";\nimport { buildFlatSchema, buildSharedDefs } from \"./schemas.js\";\nimport { selectRoutes } from \"./selection.js\";\n\n/**\n * Converts an OpenAPI 3.x document into an MCP server, one tool (or, with\n * `resources: true`, resource/resource template for an eligible `GET`) per\n * operation.\n *\n * See docs/openapi.md for the full option reference and known limitations.\n */\nexport async function fromOpenAPI(\n options: FromOpenAPIOptions,\n): Promise<FastMCP> {\n const { document, origin } = await loadSpec(options.spec);\n const routes = extractRoutes(document);\n const selected = selectRoutes(routes, options);\n const names = generateNames(selected, options.mcpNames);\n const sharedDefs = buildSharedDefs(document);\n\n const server =\n options.server ??\n new FastMCP({\n name: options.name ?? document.info?.title ?? \"OpenAPI Server\",\n version: options.version ?? \"1.0.0\",\n });\n\n const skippedOperations: {\n contentType: string;\n method: string;\n path: string;\n }[] = [];\n\n for (const route of selected) {\n const name = names.get(route);\n\n if (!name) {\n continue;\n }\n\n const {\n bodyEncoding,\n flatSchema,\n parameterMap,\n unsupportedBodyContentType,\n wholeBodyKey,\n } = buildFlatSchema(route, sharedDefs);\n\n const execOptions = {\n baseUrlOverride: options.baseUrl,\n fetchImpl: options.fetch ?? fetch,\n headers: options.headers,\n origin,\n parameterMap,\n route,\n servers: document.servers,\n };\n\n if (\n options.resources &&\n route.method === \"get\" &&\n isEligibleForResource(route)\n ) {\n registerResource(\n server,\n route,\n name,\n parameterMap,\n flatSchema.required,\n execOptions,\n );\n continue;\n }\n\n if (unsupportedBodyContentType) {\n skippedOperations.push({\n contentType: unsupportedBodyContentType,\n method: route.method,\n path: route.path,\n });\n continue;\n }\n\n server.addTool({\n description:\n route.summary ?? `${route.method.toUpperCase()} ${route.path}`,\n execute: async (args) =>\n executeRequest({\n ...execOptions,\n args: args as Record<string, unknown>,\n bodyEncoding,\n wholeBodyKey,\n }),\n name,\n parameters: jsonSchemaAdapter(flatSchema),\n });\n }\n\n if (skippedOperations.length > 0) {\n console.warn(\n \"fromOpenAPI: skipped \" +\n `${skippedOperations.length} operation(s) whose request body can't be turned into tool parameters ` +\n \"(supported: application/json, or application/x-www-form-urlencoded with a flat object schema): \" +\n skippedOperations\n .map(\n (op) => `${op.method.toUpperCase()} ${op.path} (${op.contentType})`,\n )\n .join(\", \"),\n );\n }\n\n return server;\n}\n\nfunction registerResource(\n server: FastMCP,\n route: HttpRoute,\n name: string,\n parameterMap: Record<string, ParameterMapping>,\n requiredKeys: string[] | undefined,\n execOptions: Omit<\n Parameters<typeof executeRequest>[0],\n \"args\" | \"bodyEncoding\" | \"wholeBodyKey\"\n >,\n): void {\n const mapping = buildResourceMapping(route, name, parameterMap, requiredKeys);\n const description = route.summary ?? `GET ${route.path}`;\n\n if (mapping.kind === \"resource\") {\n server.addResource({\n description,\n load: async () =>\n wrapAsResourceResult(\n await executeRequest({ ...execOptions, args: {} }),\n ),\n name,\n uri: mapping.uri,\n });\n\n return;\n }\n\n server.addResourceTemplate({\n arguments: mapping.args,\n description,\n load: async (args) =>\n wrapAsResourceResult(\n await executeRequest({\n ...execOptions,\n args: args as Record<string, unknown>,\n }),\n ),\n name,\n uriTemplate: mapping.uriTemplate,\n });\n}\n\nfunction wrapAsResourceResult(text: string): ResourceResult {\n try {\n JSON.parse(text);\n return { mimeType: \"application/json\", text };\n } catch {\n return { mimeType: \"text/plain\", text };\n }\n}\n"]}
@@ -73,6 +73,15 @@ interface FromOpenAPIOptions {
73
73
  * @default the spec's `info.title`, or "OpenAPI Server"
74
74
  */
75
75
  name?: string;
76
+ /**
77
+ * When `true`, an eligible `GET` operation becomes an MCP resource (no
78
+ * path/query parameters) or resource template (path and/or query
79
+ * parameters) instead of a tool. A `GET` with any `header`/`cookie`
80
+ * parameter, or any array-typed path/query parameter, stays a tool
81
+ * regardless — see docs/openapi.md "GET → resources".
82
+ * @default false
83
+ */
84
+ resources?: boolean;
76
85
  /**
77
86
  * An existing `FastMCP` server to register the generated tools onto,
78
87
  * instead of creating a new one.
@@ -161,7 +170,8 @@ type RawPathItem = {
161
170
  } & Partial<Record<HttpMethod, RawOperation>>;
162
171
 
163
172
  /**
164
- * Converts an OpenAPI 3.x document into an MCP server, one tool per
173
+ * Converts an OpenAPI 3.x document into an MCP server, one tool (or, with
174
+ * `resources: true`, resource/resource template for an eligible `GET`) per
165
175
  * operation.
166
176
  *
167
177
  * See docs/openapi.md for the full option reference and known limitations.
@@ -73,6 +73,15 @@ interface FromOpenAPIOptions {
73
73
  * @default the spec's `info.title`, or "OpenAPI Server"
74
74
  */
75
75
  name?: string;
76
+ /**
77
+ * When `true`, an eligible `GET` operation becomes an MCP resource (no
78
+ * path/query parameters) or resource template (path and/or query
79
+ * parameters) instead of a tool. A `GET` with any `header`/`cookie`
80
+ * parameter, or any array-typed path/query parameter, stays a tool
81
+ * regardless — see docs/openapi.md "GET → resources".
82
+ * @default false
83
+ */
84
+ resources?: boolean;
76
85
  /**
77
86
  * An existing `FastMCP` server to register the generated tools onto,
78
87
  * instead of creating a new one.
@@ -161,7 +170,8 @@ type RawPathItem = {
161
170
  } & Partial<Record<HttpMethod, RawOperation>>;
162
171
 
163
172
  /**
164
- * Converts an OpenAPI 3.x document into an MCP server, one tool per
173
+ * Converts an OpenAPI 3.x document into an MCP server, one tool (or, with
174
+ * `resources: true`, resource/resource template for an eligible `GET`) per
165
175
  * operation.
166
176
  *
167
177
  * See docs/openapi.md for the full option reference and known limitations.
@@ -28,7 +28,7 @@ function isHttpUrl(value) {
28
28
  // src/openapi/naming.ts
29
29
  var MAX_NAME_LENGTH = 56;
30
30
  var MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;
31
- function generateToolNames(routes, mcpNames) {
31
+ function generateNames(routes, mcpNames) {
32
32
  const names = /* @__PURE__ */ new Map();
33
33
  const used = /* @__PURE__ */ new Set();
34
34
  for (const route of routes) {
@@ -104,12 +104,18 @@ async function executeRequest(options) {
104
104
  url.search = query.toString();
105
105
  let body;
106
106
  if (Object.keys(bodyProps).length > 0) {
107
- if (!headers.has("content-type")) {
108
- headers.set("content-type", "application/json");
107
+ const payload = options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps;
108
+ if (options.bodyEncoding === "form") {
109
+ if (!headers.has("content-type")) {
110
+ headers.set("content-type", "application/x-www-form-urlencoded");
111
+ }
112
+ body = encodeFormBody(payload);
113
+ } else {
114
+ if (!headers.has("content-type")) {
115
+ headers.set("content-type", "application/json");
116
+ }
117
+ body = JSON.stringify(payload);
109
118
  }
110
- body = JSON.stringify(
111
- options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps
112
- );
113
119
  }
114
120
  const response = await options.fetchImpl(url.toString(), {
115
121
  body,
@@ -156,6 +162,25 @@ function resolveBaseUrl(servers, origin, overrideUrl) {
156
162
  return new URL(url, origin).toString().replace(/\/$/, "");
157
163
  }
158
164
  }
165
+ function encodeFormBody(payload) {
166
+ const params = new URLSearchParams();
167
+ if (payload && typeof payload === "object" && !Array.isArray(payload)) {
168
+ for (const [key, value] of Object.entries(
169
+ payload
170
+ )) {
171
+ if (value === void 0) {
172
+ continue;
173
+ }
174
+ for (const item of Array.isArray(value) ? value : [value]) {
175
+ params.append(
176
+ key,
177
+ item !== null && typeof item === "object" ? JSON.stringify(item) : String(item)
178
+ );
179
+ }
180
+ }
181
+ }
182
+ return params.toString();
183
+ }
159
184
  async function resolveHeaders(headers) {
160
185
  if (!headers) {
161
186
  return {};
@@ -163,6 +188,38 @@ async function resolveHeaders(headers) {
163
188
  return typeof headers === "function" ? await headers() : { ...headers };
164
189
  }
165
190
 
191
+ // src/openapi/resourceMapping.ts
192
+ function buildResourceMapping(route, name, parameterMap, requiredKeys) {
193
+ const entries = Object.entries(parameterMap);
194
+ if (entries.length === 0) {
195
+ return { kind: "resource", uri: `openapi://${name}${route.path}` };
196
+ }
197
+ const required = new Set(requiredKeys ?? []);
198
+ const args = [];
199
+ const queryKeys = [];
200
+ let path = route.path;
201
+ for (const [flatKey, mapping] of entries) {
202
+ if (mapping.in === "path") {
203
+ if (flatKey !== mapping.name) {
204
+ path = path.replace(`{${mapping.name}}`, `{${flatKey}}`);
205
+ }
206
+ } else {
207
+ queryKeys.push(flatKey);
208
+ }
209
+ args.push({ name: flatKey, required: required.has(flatKey) });
210
+ }
211
+ const uriTemplate = `openapi://${name}${path}` + (queryKeys.length > 0 ? `{?${queryKeys.join(",")}}` : "");
212
+ return { args, kind: "template", uriTemplate };
213
+ }
214
+ function isEligibleForResource(route) {
215
+ return route.parameters.every((param) => {
216
+ if (param.in === "header" || param.in === "cookie") {
217
+ return false;
218
+ }
219
+ return param.schema?.type !== "array";
220
+ });
221
+ }
222
+
166
223
  // src/openapi/routes.ts
167
224
  var HTTP_METHODS = ["get", "put", "post", "delete", "patch"];
168
225
  function extractRoutes(document) {
@@ -238,8 +295,14 @@ function buildFlatSchema(route, sharedDefs) {
238
295
  list.push(param);
239
296
  byName.set(param.name, list);
240
297
  }
241
- const { properties: bodyProperties, wholeBodyKey } = extractBodyProperties(
242
- route.method === "get" ? void 0 : route.requestBody
298
+ const {
299
+ bodyEncoding,
300
+ properties: bodyProperties,
301
+ unsupportedBodyContentType,
302
+ wholeBodyKey
303
+ } = extractBodyProperties(
304
+ route.method === "get" ? void 0 : route.requestBody,
305
+ sharedDefs
243
306
  );
244
307
  const properties = {};
245
308
  const required = [];
@@ -274,7 +337,13 @@ function buildFlatSchema(route, sharedDefs) {
274
337
  if (usedDefs) {
275
338
  flatSchema.$defs = usedDefs;
276
339
  }
277
- return { flatSchema, parameterMap, wholeBodyKey };
340
+ return {
341
+ bodyEncoding,
342
+ flatSchema,
343
+ parameterMap,
344
+ unsupportedBodyContentType,
345
+ wholeBodyKey
346
+ };
278
347
  }
279
348
  function buildSharedDefs(document) {
280
349
  const schemas = document.components?.schemas;
@@ -292,12 +361,32 @@ function childMode(key) {
292
361
  }
293
362
  return SCHEMA_MAP_KEYS.has(key) ? "schemaMap" : "schema";
294
363
  }
295
- function extractBodyProperties(requestBody) {
364
+ function componentSchemaName(ref) {
365
+ for (const prefix of ["#/components/schemas/", "#/$defs/"]) {
366
+ if (ref.startsWith(prefix)) {
367
+ return ref.slice(prefix.length);
368
+ }
369
+ }
370
+ return void 0;
371
+ }
372
+ function extractBodyProperties(requestBody, sharedDefs) {
296
373
  const properties = /* @__PURE__ */ new Map();
297
- const schema = requestBody?.content?.["application/json"]?.schema;
298
- if (!schema) {
374
+ const content = requestBody?.content;
375
+ if (!content) {
299
376
  return { properties };
300
377
  }
378
+ const hasJson = "application/json" in content;
379
+ const hasForm = "application/x-www-form-urlencoded" in content;
380
+ if (!hasJson && !hasForm) {
381
+ const contentTypes = Object.keys(content);
382
+ return contentTypes.length > 0 ? { properties, unsupportedBodyContentType: contentTypes[0] } : { properties };
383
+ }
384
+ const bodyEncoding = hasJson ? "json" : "form";
385
+ const declaredSchema = hasJson ? content["application/json"]?.schema : content["application/x-www-form-urlencoded"]?.schema;
386
+ if (!declaredSchema) {
387
+ return { bodyEncoding, properties };
388
+ }
389
+ const schema = bodyEncoding === "form" ? resolveComponentRef(declaredSchema, sharedDefs) : declaredSchema;
301
390
  const schemaProperties = schema.properties;
302
391
  if (schema.type === "object" && schemaProperties) {
303
392
  const requiredNames = new Set(
@@ -309,13 +398,19 @@ function extractBodyProperties(requestBody) {
309
398
  schema: propertySchema
310
399
  });
311
400
  }
312
- return { properties };
401
+ return { bodyEncoding, properties };
402
+ }
403
+ if (bodyEncoding === "form") {
404
+ return {
405
+ properties,
406
+ unsupportedBodyContentType: "application/x-www-form-urlencoded"
407
+ };
313
408
  }
314
409
  properties.set("body", {
315
410
  required: requestBody?.required ?? false,
316
411
  schema
317
412
  });
318
- return { properties, wholeBodyKey: "body" };
413
+ return { bodyEncoding, properties, wholeBodyKey: "body" };
319
414
  }
320
415
  function filterReferencedDefs(node, allDefs) {
321
416
  const referenced = /* @__PURE__ */ new Set();
@@ -366,6 +461,22 @@ function normalizeNullable(schema) {
366
461
  }
367
462
  return rest;
368
463
  }
464
+ function resolveComponentRef(schema, sharedDefs) {
465
+ const seen = /* @__PURE__ */ new Set();
466
+ let current = schema;
467
+ let ref = current.$ref;
468
+ while (typeof ref === "string") {
469
+ const name = componentSchemaName(ref);
470
+ const target = name === void 0 ? void 0 : sharedDefs?.[name];
471
+ if (name === void 0 || target === void 0 || seen.has(name)) {
472
+ break;
473
+ }
474
+ seen.add(name);
475
+ current = target;
476
+ ref = current.$ref;
477
+ }
478
+ return current;
479
+ }
369
480
  function rewriteNode(value, mode) {
370
481
  if (mode === "data") {
371
482
  return value;
@@ -442,40 +553,109 @@ async function fromOpenAPI(options) {
442
553
  const { document, origin } = await loadSpec(options.spec);
443
554
  const routes = extractRoutes(document);
444
555
  const selected = selectRoutes(routes, options);
445
- const names = generateToolNames(selected, options.mcpNames);
556
+ const names = generateNames(selected, options.mcpNames);
446
557
  const sharedDefs = buildSharedDefs(document);
447
558
  const server = options.server ?? new FastMCP({
448
559
  name: options.name ?? document.info?.title ?? "OpenAPI Server",
449
560
  version: options.version ?? "1.0.0"
450
561
  });
562
+ const skippedOperations = [];
451
563
  for (const route of selected) {
452
564
  const name = names.get(route);
453
565
  if (!name) {
454
566
  continue;
455
567
  }
456
- const { flatSchema, parameterMap, wholeBodyKey } = buildFlatSchema(
568
+ const {
569
+ bodyEncoding,
570
+ flatSchema,
571
+ parameterMap,
572
+ unsupportedBodyContentType,
573
+ wholeBodyKey
574
+ } = buildFlatSchema(route, sharedDefs);
575
+ const execOptions = {
576
+ baseUrlOverride: options.baseUrl,
577
+ fetchImpl: options.fetch ?? fetch,
578
+ headers: options.headers,
579
+ origin,
580
+ parameterMap,
457
581
  route,
458
- sharedDefs
459
- );
582
+ servers: document.servers
583
+ };
584
+ if (options.resources && route.method === "get" && isEligibleForResource(route)) {
585
+ registerResource(
586
+ server,
587
+ route,
588
+ name,
589
+ parameterMap,
590
+ flatSchema.required,
591
+ execOptions
592
+ );
593
+ continue;
594
+ }
595
+ if (unsupportedBodyContentType) {
596
+ skippedOperations.push({
597
+ contentType: unsupportedBodyContentType,
598
+ method: route.method,
599
+ path: route.path
600
+ });
601
+ continue;
602
+ }
460
603
  server.addTool({
461
604
  description: route.summary ?? `${route.method.toUpperCase()} ${route.path}`,
462
605
  execute: async (args) => executeRequest({
606
+ ...execOptions,
463
607
  args,
464
- baseUrlOverride: options.baseUrl,
465
- fetchImpl: options.fetch ?? fetch,
466
- headers: options.headers,
467
- origin,
468
- parameterMap,
469
- route,
470
- servers: document.servers,
608
+ bodyEncoding,
471
609
  wholeBodyKey
472
610
  }),
473
611
  name,
474
612
  parameters: jsonSchemaAdapter(flatSchema)
475
613
  });
476
614
  }
615
+ if (skippedOperations.length > 0) {
616
+ console.warn(
617
+ `fromOpenAPI: skipped ${skippedOperations.length} operation(s) whose request body can't be turned into tool parameters (supported: application/json, or application/x-www-form-urlencoded with a flat object schema): ` + skippedOperations.map(
618
+ (op) => `${op.method.toUpperCase()} ${op.path} (${op.contentType})`
619
+ ).join(", ")
620
+ );
621
+ }
477
622
  return server;
478
623
  }
624
+ function registerResource(server, route, name, parameterMap, requiredKeys, execOptions) {
625
+ const mapping = buildResourceMapping(route, name, parameterMap, requiredKeys);
626
+ const description = route.summary ?? `GET ${route.path}`;
627
+ if (mapping.kind === "resource") {
628
+ server.addResource({
629
+ description,
630
+ load: async () => wrapAsResourceResult(
631
+ await executeRequest({ ...execOptions, args: {} })
632
+ ),
633
+ name,
634
+ uri: mapping.uri
635
+ });
636
+ return;
637
+ }
638
+ server.addResourceTemplate({
639
+ arguments: mapping.args,
640
+ description,
641
+ load: async (args) => wrapAsResourceResult(
642
+ await executeRequest({
643
+ ...execOptions,
644
+ args
645
+ })
646
+ ),
647
+ name,
648
+ uriTemplate: mapping.uriTemplate
649
+ });
650
+ }
651
+ function wrapAsResourceResult(text) {
652
+ try {
653
+ JSON.parse(text);
654
+ return { mimeType: "application/json", text };
655
+ } catch {
656
+ return { mimeType: "text/plain", text };
657
+ }
658
+ }
479
659
  export {
480
660
  fromOpenAPI
481
661
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/openapi/loadSpec.ts","../../src/openapi/naming.ts","../../src/openapi/requestBuilder.ts","../../src/openapi/routes.ts","../../src/openapi/schemas.ts","../../src/openapi/selection.ts","../../src/openapi/fromOpenAPI.ts"],"sourcesContent":["import SwaggerParser from \"@apidevtools/swagger-parser\";\n\nimport type { BundledOpenApiDocument } from \"./types.js\";\n\nexport interface LoadedSpec {\n document: BundledOpenApiDocument;\n /**\n * The spec's own URL, when it was loaded from one. Used to resolve a\n * relative `servers[0].url` against the document's origin.\n */\n origin?: string;\n}\n\n/**\n * Loads and bundles an OpenAPI document, resolving local *and* external\n * `$ref`s (relative paths, absolute URLs, `other.yaml#/fragment`).\n *\n * `spec` is handed to swagger-parser as-is — a URL, file path, or object —\n * rather than being fetched and re-parsed here first. External refs resolve\n * relative to whatever document they were found in, so pre-fetching the\n * entry document and passing its parsed text as an object would resolve\n * every external ref against the wrong base (or none at all).\n */\nexport async function loadSpec(\n spec: Record<string, unknown> | string,\n): Promise<LoadedSpec> {\n const document = (await SwaggerParser.bundle(\n spec as never,\n )) as unknown as BundledOpenApiDocument;\n\n if (!document.openapi?.startsWith(\"3.\")) {\n throw new Error(\n `fromOpenAPI only supports OpenAPI 3.x documents (found ${\n document.openapi ?? document.swagger ?? \"an unrecognized version\"\n }). Swagger 2.0 is not supported.`,\n );\n }\n\n return {\n document,\n origin: typeof spec === \"string\" && isHttpUrl(spec) ? spec : undefined,\n };\n}\n\nfunction isHttpUrl(value: string): boolean {\n return value.startsWith(\"http://\") || value.startsWith(\"https://\");\n}\n","import type { HttpRoute } from \"./types.js\";\n\nconst MAX_NAME_LENGTH = 56;\n// Reserves room for a \"_<n>\" collision suffix so the final name never\n// exceeds MAX_NAME_LENGTH, however many collisions it takes.\nconst MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;\n\n/**\n * Generates a unique tool name per route.\n *\n * Ports the Python implementation's naming rule\n * (`server/providers/openapi/provider.py:_generate_default_name`): prefer\n * `mcpNames[operationId]`, then `operationId` (FastAPI-style `__` suffixes\n * stripped), falling back to `summary` or `{method}_{path}`; slugified and\n * capped at 56 characters, with `_2`, `_3`, ... appended on collision.\n *\n * Uniqueness is checked against the final (post-suffix) name, not just the\n * base — otherwise a spec whose own operationIds already look auto-suffixed\n * (e.g. both \"foo\" and \"foo_2\" present) could produce two identically-named\n * tools, one of which `FastMCP.addTool` would silently drop.\n */\nexport function generateToolNames(\n routes: HttpRoute[],\n mcpNames: Record<string, string> | undefined,\n): Map<HttpRoute, string> {\n const names = new Map<HttpRoute, string>();\n const used = new Set<string>();\n\n for (const route of routes) {\n const base = slugify(baseNameFor(route, mcpNames));\n let candidate = base;\n let suffix = 1;\n\n while (used.has(candidate)) {\n suffix += 1;\n candidate = `${base}_${suffix}`;\n }\n\n used.add(candidate);\n names.set(route, candidate);\n }\n\n return names;\n}\n\nfunction baseNameFor(\n route: HttpRoute,\n mcpNames: Record<string, string> | undefined,\n): string {\n if (route.operationId) {\n return mcpNames?.[route.operationId] ?? route.operationId.split(\"__\")[0];\n }\n\n return route.summary || `${route.method}_${route.path}`;\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .replace(/[^a-zA-Z0-9_]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\")\n .slice(0, MAX_BASE_LENGTH);\n\n return slug || \"operation\";\n}\n","import type { ParameterMapping } from \"./schemas.js\";\nimport type { FromOpenAPIOptions, HttpRoute, OpenApiServer } from \"./types.js\";\n\nimport { UserError } from \"../FastMCP.js\";\n\nexport interface ExecuteRequestOptions {\n args: Record<string, unknown>;\n baseUrlOverride?: string;\n fetchImpl: typeof fetch;\n headers?: FromOpenAPIOptions[\"headers\"];\n origin?: string;\n parameterMap: Record<string, ParameterMapping>;\n route: HttpRoute;\n servers: OpenApiServer[] | undefined;\n wholeBodyKey?: string;\n}\n\nexport async function executeRequest(\n options: ExecuteRequestOptions,\n): Promise<string> {\n const baseUrl = resolveBaseUrl(\n options.servers,\n options.origin,\n options.baseUrlOverride,\n );\n\n const pathParams: Record<string, string> = {};\n const query = new URLSearchParams();\n // A plain object keys headers case-sensitively, so a caller-supplied\n // header (e.g. \"Content-Type\") wouldn't be recognized as the same header\n // as one this function sets internally (e.g. \"content-type\") — `Headers`\n // normalizes casing, so `.set()` correctly overrides rather than\n // combining into a comma-joined, malformed value.\n const headers = new Headers(await resolveHeaders(options.headers));\n const bodyProps: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(options.args)) {\n const mapping = options.parameterMap[key];\n\n if (!mapping || value === undefined) {\n continue;\n }\n\n switch (mapping.in) {\n case \"body\":\n bodyProps[mapping.name] = value;\n break;\n case \"cookie\": {\n const existing = headers.get(\"cookie\");\n headers.set(\n \"cookie\",\n existing\n ? `${existing}; ${mapping.name}=${String(value)}`\n : `${mapping.name}=${String(value)}`,\n );\n break;\n }\n case \"header\":\n headers.set(mapping.name, String(value));\n break;\n case \"path\":\n pathParams[mapping.name] = String(value);\n break;\n case \"query\":\n for (const item of Array.isArray(value) ? value : [value]) {\n query.append(mapping.name, String(item));\n }\n break;\n }\n }\n\n let path = options.route.path;\n\n for (const [name, value] of Object.entries(pathParams)) {\n path = path.replace(`{${name}}`, encodeURIComponent(value));\n }\n\n const url = new URL(baseUrl.replace(/\\/$/, \"\") + path);\n url.search = query.toString();\n\n let body: string | undefined;\n\n if (Object.keys(bodyProps).length > 0) {\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n body = JSON.stringify(\n options.wholeBodyKey ? bodyProps[options.wholeBodyKey] : bodyProps,\n );\n }\n\n const response = await options.fetchImpl(url.toString(), {\n body,\n headers,\n method: options.route.method.toUpperCase(),\n });\n\n const text = await response.text();\n\n if (!response.ok) {\n throw new UserError(\n `${options.route.method.toUpperCase()} ${path} failed with ${response.status}: ${text.slice(0, 2000)}`,\n );\n }\n\n if (response.headers.get(\"content-type\")?.includes(\"json\")) {\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n }\n\n return text;\n}\n\n/**\n * Resolves `servers[0].url` the way a real HTTP client needs it resolved,\n * not just the way a schema validator would accept it: a relative URL (e.g.\n * Petstore's own `\"/api/v3\"`) is joined against the document's own origin,\n * not passed through verbatim.\n */\nexport function resolveBaseUrl(\n servers: OpenApiServer[] | undefined,\n origin: string | undefined,\n overrideUrl: string | undefined,\n): string {\n if (overrideUrl) {\n return overrideUrl.replace(/\\/$/, \"\");\n }\n\n const server = servers?.[0];\n\n if (!server) {\n throw new Error(\n \"The OpenAPI document has no `servers` entry. Pass `baseUrl` to fromOpenAPI() explicitly.\",\n );\n }\n\n let url = server.url;\n\n for (const [name, variable] of Object.entries(server.variables ?? {})) {\n url = url.replaceAll(`{${name}}`, variable.default);\n }\n\n try {\n return new URL(url).toString().replace(/\\/$/, \"\");\n } catch {\n if (!origin) {\n throw new Error(\n `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.`,\n );\n }\n\n return new URL(url, origin).toString().replace(/\\/$/, \"\");\n }\n}\n\nasync function resolveHeaders(\n headers: FromOpenAPIOptions[\"headers\"],\n): Promise<Record<string, string>> {\n if (!headers) {\n return {};\n }\n\n return typeof headers === \"function\" ? await headers() : { ...headers };\n}\n","import type {\n BundledOpenApiDocument,\n HttpMethod,\n HttpRoute,\n OpenApiParameter,\n OpenApiParameterRef,\n OpenApiRequestBody,\n} from \"./types.js\";\n\nconst HTTP_METHODS: HttpMethod[] = [\"get\", \"put\", \"post\", \"delete\", \"patch\"];\n\n/**\n * Walks a bundled document's `paths` into a flat list of routes, resolving\n * any structural (non-schema) `$ref`s on parameters and request bodies —\n * e.g. `#/components/parameters/Limit` — against the same document.\n *\n * Bundling (see `loadSpec.ts`) guarantees every remaining `$ref` here is\n * local, so a plain JSON-pointer lookup is enough.\n */\nexport function extractRoutes(document: BundledOpenApiDocument): HttpRoute[] {\n const routes: HttpRoute[] = [];\n\n for (const [path, pathItem] of Object.entries(document.paths ?? {})) {\n const pathLevelParams = (pathItem.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n for (const method of HTTP_METHODS) {\n const operation = pathItem[method];\n\n if (!operation) {\n continue;\n }\n\n const operationParams = (operation.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n routes.push({\n deprecated: operation.deprecated ?? false,\n method,\n operationId: operation.operationId,\n parameters: mergeParameters(pathLevelParams, operationParams),\n path,\n requestBody: operation.requestBody\n ? resolveRef<OpenApiRequestBody>(document, operation.requestBody)\n : undefined,\n summary: operation.summary,\n tags: operation.tags ?? [],\n });\n }\n }\n\n return routes;\n}\n\nfunction mergeParameters(\n pathLevel: OpenApiParameter[],\n operationLevel: OpenApiParameter[],\n): OpenApiParameter[] {\n const overridden = new Set(\n operationLevel.map((param) => `${param.in}:${param.name}`),\n );\n\n return [\n ...pathLevel.filter(\n (param) => !overridden.has(`${param.in}:${param.name}`),\n ),\n ...operationLevel,\n ];\n}\n\nfunction resolveRef<TValue>(\n document: BundledOpenApiDocument,\n value: OpenApiParameterRef | TValue,\n): TValue {\n if (!value || typeof value !== \"object\" || !(\"$ref\" in value)) {\n return value;\n }\n\n const pointer = value.$ref;\n\n if (!pointer.startsWith(\"#/\")) {\n // Bundling should have already turned every external ref into a local\n // one — if this fires, swagger-parser's output shape has changed.\n throw new Error(`Unexpected external $ref after bundling: ${pointer}`);\n }\n\n // swagger-parser synthesizes these pointers as URI fragments (e.g. a path\n // like \"/pets/{petId}\" becomes \"~1pets~1%7BpetId%7D\"), so each segment\n // needs its \"~1\"/\"~0\" escapes undone *and* percent-decoding, in that order.\n const segments = pointer\n .slice(2)\n .split(\"/\")\n .map((segment) =>\n decodeURIComponent(segment.replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\")),\n );\n\n let node: unknown = document;\n\n for (const segment of segments) {\n node = (node as Record<string, unknown> | undefined)?.[segment];\n }\n\n return node as TValue;\n}\n","import type { JsonSchemaObject } from \"../jsonSchemaAdapter.js\";\nimport type {\n BundledOpenApiDocument,\n HttpRoute,\n OpenApiParameter,\n OpenApiRequestBody,\n OpenApiSchema,\n ParameterLocation,\n} from \"./types.js\";\n\n/**\n * Keys whose values are name-to-schema maps: their child keys are\n * author-chosen names rather than JSON Schema keywords.\n */\nconst SCHEMA_MAP_KEYS = new Set([\n \"$defs\",\n \"definitions\",\n \"dependentSchemas\",\n \"patternProperties\",\n \"properties\",\n]);\n\n/**\n * Keys whose values are arbitrary instance data rather than schemas. A\n * sample payload may well contain a \"$ref\" or \"nullable\" key of its own.\n */\nconst DATA_KEYS = new Set([\"const\", \"default\", \"enum\", \"example\", \"examples\"]);\n\nexport interface FlatSchemaResult {\n flatSchema: JsonSchemaObject;\n parameterMap: Record<string, ParameterMapping>;\n /**\n * Set when the request body's schema is not a flat object (e.g. an array,\n * or a bare non-object `$ref`) — the whole body is exposed as a single\n * property under this key, rather than flattened into individual\n * properties.\n */\n wholeBodyKey?: string;\n}\n\nexport interface ParameterMapping {\n in: \"body\" | ParameterLocation;\n name: string;\n}\n\ntype WalkMode = \"data\" | \"schema\" | \"schemaMap\";\n\n/**\n * Flattens a route's path/query/header/cookie parameters and request body\n * into a single tool input schema.\n *\n * Collision precedence ports the Python implementation's rule\n * (`utilities/openapi/schemas.py:_combine_schemas_and_map_params`): a name\n * that collides across path/query/header/cookie gets suffixed\n * `{name}__{location}`; a request body property with a colliding name always\n * keeps its bare name.\n *\n * `GET` never contributes a request body: `fetch` (and the Fetch spec in\n * general) rejects a body on a GET request, so a tool built from a spec's\n * (legal, if unusual) `GET` + `requestBody` operation would be permanently\n * broken. The request body is simply not flattened into the schema for such\n * a route, rather than surfacing a schema that can never actually be called.\n */\nexport function buildFlatSchema(\n route: HttpRoute,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): FlatSchemaResult {\n const byName = new Map<string, OpenApiParameter[]>();\n\n for (const param of route.parameters) {\n const list = byName.get(param.name) ?? [];\n list.push(param);\n byName.set(param.name, list);\n }\n\n const { properties: bodyProperties, wholeBodyKey } = extractBodyProperties(\n route.method === \"get\" ? undefined : route.requestBody,\n );\n\n const properties: Record<string, OpenApiSchema> = {};\n const required: string[] = [];\n const parameterMap: Record<string, ParameterMapping> = {};\n\n for (const [name, occurrences] of byName) {\n const collides = occurrences.length > 1 || bodyProperties.has(name);\n\n for (const param of occurrences) {\n const key = collides ? `${name}__${param.in}` : name;\n\n properties[key] = rewriteComponentRefs(\n param.schema ?? { type: \"string\" },\n );\n parameterMap[key] = { in: param.in, name };\n\n if (param.in === \"path\" || param.required) {\n required.push(key);\n }\n }\n }\n\n for (const [name, { required: isRequired, schema }] of bodyProperties) {\n properties[name] = rewriteComponentRefs(schema);\n parameterMap[name] = { in: \"body\", name };\n\n if (isRequired) {\n required.push(name);\n }\n }\n\n const flatSchema: JsonSchemaObject = {\n additionalProperties: false,\n properties,\n type: \"object\",\n ...(required.length > 0 ? { required } : {}),\n };\n\n // Only the definitions this tool's own schema actually (transitively)\n // references — embedding the whole document's components.schemas into\n // every single tool would multiply the tools/list payload size by the\n // tool count for no benefit.\n const usedDefs = sharedDefs && filterReferencedDefs(properties, sharedDefs);\n\n if (usedDefs) {\n flatSchema.$defs = usedDefs;\n }\n\n return { flatSchema, parameterMap, wholeBodyKey };\n}\n\nexport function buildSharedDefs(\n document: BundledOpenApiDocument,\n): Record<string, OpenApiSchema> | undefined {\n const schemas = document.components?.schemas;\n\n if (!schemas || Object.keys(schemas).length === 0) {\n return undefined;\n }\n\n return rewriteNode(schemas, \"schemaMap\") as Record<string, OpenApiSchema>;\n}\n\n/**\n * Rewrites `$ref`s pointing at `#/components/schemas/...` to `#/$defs/...`,\n * so a per-tool schema that contains one can be handed to AJV standalone,\n * alongside a `$defs` object built from the document's `components.schemas`\n * (see `buildSharedDefs`). Ports the equivalent rewrite from the Python\n * implementation (`utilities/openapi/schemas.py:_replace_ref_with_defs`).\n *\n * Also normalizes OpenAPI 3.0's `nullable` keyword (see `normalizeNullable`),\n * since real specs carry both.\n */\nexport function rewriteComponentRefs<TValue>(value: TValue): TValue {\n return rewriteNode(value, \"schema\") as TValue;\n}\n\nfunction childMode(key: string): WalkMode {\n if (DATA_KEYS.has(key)) {\n return \"data\";\n }\n\n return SCHEMA_MAP_KEYS.has(key) ? \"schemaMap\" : \"schema\";\n}\n\nfunction extractBodyProperties(requestBody: OpenApiRequestBody | undefined): {\n properties: Map<string, { required: boolean; schema: OpenApiSchema }>;\n wholeBodyKey?: string;\n} {\n const properties = new Map<\n string,\n { required: boolean; schema: OpenApiSchema }\n >();\n\n const schema = requestBody?.content?.[\"application/json\"]?.schema;\n\n if (!schema) {\n return { properties };\n }\n\n const schemaProperties = schema.properties as\n | Record<string, OpenApiSchema>\n | undefined;\n\n if (schema.type === \"object\" && schemaProperties) {\n const requiredNames = new Set(\n (schema.required as string[] | undefined) ?? [],\n );\n\n for (const [name, propertySchema] of Object.entries(schemaProperties)) {\n properties.set(name, {\n required: requiredNames.has(name),\n schema: propertySchema,\n });\n }\n\n return { properties };\n }\n\n // Non-object body (array, bare $ref to a scalar/array, etc.) — expose the\n // whole thing as a single \"body\" property rather than flattening it.\n properties.set(\"body\", {\n required: requestBody?.required ?? false,\n schema,\n });\n\n return { properties, wholeBodyKey: \"body\" };\n}\n\n/**\n * Walks a schema fragment for `#/$defs/Name` refs and returns just those\n * definitions (transitively — a referenced def may itself reference\n * others), or `undefined` if none are referenced.\n */\nfunction filterReferencedDefs(\n node: unknown,\n allDefs: Record<string, OpenApiSchema>,\n): Record<string, OpenApiSchema> | undefined {\n const referenced = new Set<string>();\n const stack: unknown[] = [node];\n\n while (stack.length > 0) {\n const current = stack.pop();\n\n if (Array.isArray(current)) {\n stack.push(...current);\n continue;\n }\n\n if (!current || typeof current !== \"object\") {\n continue;\n }\n\n for (const [key, value] of Object.entries(\n current as Record<string, unknown>,\n )) {\n if (\n key === \"$ref\" &&\n typeof value === \"string\" &&\n value.startsWith(\"#/$defs/\")\n ) {\n const name = value.slice(\"#/$defs/\".length);\n\n if (allDefs[name] && !referenced.has(name)) {\n referenced.add(name);\n stack.push(allDefs[name]);\n }\n\n continue;\n }\n\n stack.push(value);\n }\n }\n\n if (referenced.size === 0) {\n return undefined;\n }\n\n return Object.fromEntries(\n [...referenced].map((name) => [name, allDefs[name]]),\n );\n}\n\n/**\n * OpenAPI 3.0's `nullable` keyword only makes sense alongside a sibling\n * `type`, which it widens (`nullable: true` + `type: \"string\"` means\n * \"string or null\") — but it is not itself standard JSON Schema. AJV\n * recognizes the keyword and throws ('\"nullable\" cannot be used without\n * \"type\"') if it finds one with no `type` on the same node, which real\n * specs do produce (e.g. `nullable` sibling to `oneOf`/`allOf`/`$ref`\n * instead of `type`, as in Box's API). Folded into `type` where there is\n * one to widen, dropped otherwise.\n */\nfunction normalizeNullable(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n if (!(\"nullable\" in schema)) {\n return schema;\n }\n\n const { nullable, type, ...rest } = schema;\n\n if (nullable !== true) {\n return rest;\n }\n\n if (typeof type === \"string\") {\n return { ...rest, type: [type, \"null\"] };\n }\n\n if (Array.isArray(type)) {\n return { ...rest, type: [...new Set([\"null\", ...type])] };\n }\n\n return rest;\n}\n\n/**\n * Walks a schema fragment, distinguishing the three kinds of node it can\n * reach — because only one of them is a schema whose keys are JSON Schema\n * keywords:\n *\n * - `\"schema\"` — a schema object. `$ref`/`nullable` here are keywords.\n * - `\"schemaMap\"` — a name-to-schema map (`properties`, `$defs`, ...). Its\n * keys are author-chosen names, so a property literally named `nullable`\n * or `$ref` is a field, not a keyword, and must survive untouched.\n * - `\"data\"` — arbitrary values (`default`, `enum`, `example`, ...). Not\n * schemas at all; passed through verbatim.\n *\n * Walking every node as a schema (as this originally did) silently deletes\n * a property named `nullable` from the generated tool schema, since\n * `normalizeNullable` cannot tell the keyword from a same-named field.\n */\nfunction rewriteNode(value: unknown, mode: WalkMode): unknown {\n if (mode === \"data\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => rewriteNode(item, \"schema\"));\n }\n\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n const entries = Object.entries(value as Record<string, unknown>).map(\n ([key, entryValue]): [string, unknown] => {\n if (mode === \"schemaMap\") {\n return [key, rewriteNode(entryValue, \"schema\")];\n }\n\n if (\n key === \"$ref\" &&\n typeof entryValue === \"string\" &&\n entryValue.startsWith(\"#/components/schemas/\")\n ) {\n return [key, entryValue.replace(\"#/components/schemas/\", \"#/$defs/\")];\n }\n\n return [key, rewriteNode(entryValue, childMode(key))];\n },\n );\n\n const rewritten = Object.fromEntries(entries) as Record<string, unknown>;\n\n return mode === \"schemaMap\" ? rewritten : normalizeNullable(rewritten);\n}\n","import type {\n FromOpenAPIOptions,\n HttpMethod,\n HttpRoute,\n OperationSummary,\n} from \"./types.js\";\n\n/**\n * If neither `include`/`exclude` nor `maxTools` was given, and a spec still\n * produces more operations than this, `selectRoutes` throws rather than\n * silently generating a wall of tools most clients can't usefully work with.\n */\nexport const DEFAULT_MAX_OPERATIONS = 40;\n\nconst METHOD_PRIORITY: Record<HttpMethod, number> = {\n delete: 4,\n get: 0,\n patch: 3,\n post: 1,\n put: 2,\n};\n\n/**\n * Filters and orders routes into the set that becomes tools.\n *\n * Deprecated operations are excluded by default. Ordering is deterministic\n * (method priority GET→POST→PUT→PATCH→DELETE, then path) so that, combined\n * with `maxTools`, truncation is legible rather than arbitrary.\n */\nexport function selectRoutes(\n routes: HttpRoute[],\n options: Pick<FromOpenAPIOptions, \"exclude\" | \"include\" | \"maxTools\">,\n): HttpRoute[] {\n let selected = routes.filter((route) => !route.deprecated);\n\n if (options.include) {\n const include = options.include;\n selected = selected.filter((route) => include(toSummary(route)));\n }\n\n if (options.exclude) {\n const exclude = options.exclude;\n selected = selected.filter((route) => !exclude(toSummary(route)));\n }\n\n selected = [...selected].sort((a, b) => {\n const byMethod = METHOD_PRIORITY[a.method] - METHOD_PRIORITY[b.method];\n return byMethod !== 0 ? byMethod : a.path.localeCompare(b.path);\n });\n\n const noSelectionGiven = !options.include && !options.exclude;\n\n if (\n noSelectionGiven &&\n options.maxTools === undefined &&\n selected.length > DEFAULT_MAX_OPERATIONS\n ) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds the default limit of ${DEFAULT_MAX_OPERATIONS}. ` +\n \"This is a deliberate stop, not a bug: turning every operation in a large spec into a tool produces a tool list most MCP clients can't use well. \" +\n \"Pass `include`/`exclude` to choose the operations you actually want, or `maxTools` to raise this limit explicitly.\",\n );\n }\n\n if (options.maxTools !== undefined && selected.length > options.maxTools) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds maxTools (${options.maxTools}). ` +\n \"Narrow the spec with `include`/`exclude`, or raise `maxTools`.\",\n );\n }\n\n return selected;\n}\n\nfunction toSummary(route: HttpRoute): OperationSummary {\n return {\n deprecated: route.deprecated,\n method: route.method,\n operationId: route.operationId,\n path: route.path,\n tags: route.tags,\n };\n}\n","import type { FromOpenAPIOptions } from \"./types.js\";\n\nimport { FastMCP } from \"../FastMCP.js\";\nimport { jsonSchemaAdapter } from \"../jsonSchemaAdapter.js\";\nimport { loadSpec } from \"./loadSpec.js\";\nimport { generateToolNames } from \"./naming.js\";\nimport { executeRequest } from \"./requestBuilder.js\";\nimport { extractRoutes } from \"./routes.js\";\nimport { buildFlatSchema, buildSharedDefs } from \"./schemas.js\";\nimport { selectRoutes } from \"./selection.js\";\n\n/**\n * Converts an OpenAPI 3.x document into an MCP server, one tool per\n * operation.\n *\n * See docs/openapi.md for the full option reference and known limitations.\n */\nexport async function fromOpenAPI(\n options: FromOpenAPIOptions,\n): Promise<FastMCP> {\n const { document, origin } = await loadSpec(options.spec);\n const routes = extractRoutes(document);\n const selected = selectRoutes(routes, options);\n const names = generateToolNames(selected, options.mcpNames);\n const sharedDefs = buildSharedDefs(document);\n\n const server =\n options.server ??\n new FastMCP({\n name: options.name ?? document.info?.title ?? \"OpenAPI Server\",\n version: options.version ?? \"1.0.0\",\n });\n\n for (const route of selected) {\n const name = names.get(route);\n\n if (!name) {\n continue;\n }\n\n const { flatSchema, parameterMap, wholeBodyKey } = buildFlatSchema(\n route,\n sharedDefs,\n );\n\n server.addTool({\n description:\n route.summary ?? `${route.method.toUpperCase()} ${route.path}`,\n execute: async (args) =>\n executeRequest({\n args: args as Record<string, unknown>,\n baseUrlOverride: options.baseUrl,\n fetchImpl: options.fetch ?? fetch,\n headers: options.headers,\n origin,\n parameterMap,\n route,\n servers: document.servers,\n wholeBodyKey,\n }),\n name,\n parameters: jsonSchemaAdapter(flatSchema),\n });\n }\n\n return server;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/openapi/loadSpec.ts","../../src/openapi/naming.ts","../../src/openapi/requestBuilder.ts","../../src/openapi/resourceMapping.ts","../../src/openapi/routes.ts","../../src/openapi/schemas.ts","../../src/openapi/selection.ts","../../src/openapi/fromOpenAPI.ts"],"sourcesContent":["import SwaggerParser from \"@apidevtools/swagger-parser\";\n\nimport type { BundledOpenApiDocument } from \"./types.js\";\n\nexport interface LoadedSpec {\n document: BundledOpenApiDocument;\n /**\n * The spec's own URL, when it was loaded from one. Used to resolve a\n * relative `servers[0].url` against the document's origin.\n */\n origin?: string;\n}\n\n/**\n * Loads and bundles an OpenAPI document, resolving local *and* external\n * `$ref`s (relative paths, absolute URLs, `other.yaml#/fragment`).\n *\n * `spec` is handed to swagger-parser as-is — a URL, file path, or object —\n * rather than being fetched and re-parsed here first. External refs resolve\n * relative to whatever document they were found in, so pre-fetching the\n * entry document and passing its parsed text as an object would resolve\n * every external ref against the wrong base (or none at all).\n */\nexport async function loadSpec(\n spec: Record<string, unknown> | string,\n): Promise<LoadedSpec> {\n const document = (await SwaggerParser.bundle(\n spec as never,\n )) as unknown as BundledOpenApiDocument;\n\n if (!document.openapi?.startsWith(\"3.\")) {\n throw new Error(\n `fromOpenAPI only supports OpenAPI 3.x documents (found ${\n document.openapi ?? document.swagger ?? \"an unrecognized version\"\n }). Swagger 2.0 is not supported.`,\n );\n }\n\n return {\n document,\n origin: typeof spec === \"string\" && isHttpUrl(spec) ? spec : undefined,\n };\n}\n\nfunction isHttpUrl(value: string): boolean {\n return value.startsWith(\"http://\") || value.startsWith(\"https://\");\n}\n","import type { HttpRoute } from \"./types.js\";\n\nconst MAX_NAME_LENGTH = 56;\n// Reserves room for a \"_<n>\" collision suffix so the final name never\n// exceeds MAX_NAME_LENGTH, however many collisions it takes.\nconst MAX_BASE_LENGTH = MAX_NAME_LENGTH - 5;\n\n/**\n * Generates a unique name per route — used both for tools and, when\n * `resources: true`, for the resources/resource templates a `GET` route\n * maps to instead. One pass over the whole selected set keeps names unique\n * regardless of which destination a route ends up at.\n *\n * Ports the Python implementation's naming rule\n * (`server/providers/openapi/provider.py:_generate_default_name`): prefer\n * `mcpNames[operationId]`, then `operationId` (FastAPI-style `__` suffixes\n * stripped), falling back to `summary` or `{method}_{path}`; slugified and\n * capped at 56 characters, with `_2`, `_3`, ... appended on collision.\n *\n * Uniqueness is checked against the final (post-suffix) name, not just the\n * base — otherwise a spec whose own operationIds already look auto-suffixed\n * (e.g. both \"foo\" and \"foo_2\" present) could produce two identically-named\n * tools, one of which `FastMCP.addTool` would silently drop.\n */\nexport function generateNames(\n routes: HttpRoute[],\n mcpNames: Record<string, string> | undefined,\n): Map<HttpRoute, string> {\n const names = new Map<HttpRoute, string>();\n const used = new Set<string>();\n\n for (const route of routes) {\n const base = slugify(baseNameFor(route, mcpNames));\n let candidate = base;\n let suffix = 1;\n\n while (used.has(candidate)) {\n suffix += 1;\n candidate = `${base}_${suffix}`;\n }\n\n used.add(candidate);\n names.set(route, candidate);\n }\n\n return names;\n}\n\nfunction baseNameFor(\n route: HttpRoute,\n mcpNames: Record<string, string> | undefined,\n): string {\n if (route.operationId) {\n return mcpNames?.[route.operationId] ?? route.operationId.split(\"__\")[0];\n }\n\n return route.summary || `${route.method}_${route.path}`;\n}\n\nfunction slugify(value: string): string {\n const slug = value\n .replace(/[^a-zA-Z0-9_]+/g, \"_\")\n .replace(/_+/g, \"_\")\n .replace(/^_|_$/g, \"\")\n .slice(0, MAX_BASE_LENGTH);\n\n return slug || \"operation\";\n}\n","import type { ParameterMapping } from \"./schemas.js\";\nimport type { FromOpenAPIOptions, HttpRoute, OpenApiServer } from \"./types.js\";\n\nimport { UserError } from \"../FastMCP.js\";\n\nexport interface ExecuteRequestOptions {\n args: Record<string, unknown>;\n baseUrlOverride?: string;\n /** How to serialize a request body, if `args` contains any body-mapped values. */\n bodyEncoding?: \"form\" | \"json\";\n fetchImpl: typeof fetch;\n headers?: FromOpenAPIOptions[\"headers\"];\n origin?: string;\n parameterMap: Record<string, ParameterMapping>;\n route: HttpRoute;\n servers: OpenApiServer[] | undefined;\n wholeBodyKey?: string;\n}\n\nexport async function executeRequest(\n options: ExecuteRequestOptions,\n): Promise<string> {\n const baseUrl = resolveBaseUrl(\n options.servers,\n options.origin,\n options.baseUrlOverride,\n );\n\n const pathParams: Record<string, string> = {};\n const query = new URLSearchParams();\n // A plain object keys headers case-sensitively, so a caller-supplied\n // header (e.g. \"Content-Type\") wouldn't be recognized as the same header\n // as one this function sets internally (e.g. \"content-type\") — `Headers`\n // normalizes casing, so `.set()` correctly overrides rather than\n // combining into a comma-joined, malformed value.\n const headers = new Headers(await resolveHeaders(options.headers));\n const bodyProps: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(options.args)) {\n const mapping = options.parameterMap[key];\n\n if (!mapping || value === undefined) {\n continue;\n }\n\n switch (mapping.in) {\n case \"body\":\n bodyProps[mapping.name] = value;\n break;\n case \"cookie\": {\n const existing = headers.get(\"cookie\");\n headers.set(\n \"cookie\",\n existing\n ? `${existing}; ${mapping.name}=${String(value)}`\n : `${mapping.name}=${String(value)}`,\n );\n break;\n }\n case \"header\":\n headers.set(mapping.name, String(value));\n break;\n case \"path\":\n pathParams[mapping.name] = String(value);\n break;\n case \"query\":\n for (const item of Array.isArray(value) ? value : [value]) {\n query.append(mapping.name, String(item));\n }\n break;\n }\n }\n\n let path = options.route.path;\n\n for (const [name, value] of Object.entries(pathParams)) {\n path = path.replace(`{${name}}`, encodeURIComponent(value));\n }\n\n const url = new URL(baseUrl.replace(/\\/$/, \"\") + path);\n url.search = query.toString();\n\n let body: string | undefined;\n\n if (Object.keys(bodyProps).length > 0) {\n const payload = options.wholeBodyKey\n ? bodyProps[options.wholeBodyKey]\n : bodyProps;\n\n if (options.bodyEncoding === \"form\") {\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/x-www-form-urlencoded\");\n }\n\n body = encodeFormBody(payload);\n } else {\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n\n body = JSON.stringify(payload);\n }\n }\n\n const response = await options.fetchImpl(url.toString(), {\n body,\n headers,\n method: options.route.method.toUpperCase(),\n });\n\n const text = await response.text();\n\n if (!response.ok) {\n throw new UserError(\n `${options.route.method.toUpperCase()} ${path} failed with ${response.status}: ${text.slice(0, 2000)}`,\n );\n }\n\n if (response.headers.get(\"content-type\")?.includes(\"json\")) {\n try {\n return JSON.stringify(JSON.parse(text), null, 2);\n } catch {\n return text;\n }\n }\n\n return text;\n}\n\n/**\n * Resolves `servers[0].url` the way a real HTTP client needs it resolved,\n * not just the way a schema validator would accept it: a relative URL (e.g.\n * Petstore's own `\"/api/v3\"`) is joined against the document's own origin,\n * not passed through verbatim.\n */\nexport function resolveBaseUrl(\n servers: OpenApiServer[] | undefined,\n origin: string | undefined,\n overrideUrl: string | undefined,\n): string {\n if (overrideUrl) {\n return overrideUrl.replace(/\\/$/, \"\");\n }\n\n const server = servers?.[0];\n\n if (!server) {\n throw new Error(\n \"The OpenAPI document has no `servers` entry. Pass `baseUrl` to fromOpenAPI() explicitly.\",\n );\n }\n\n let url = server.url;\n\n for (const [name, variable] of Object.entries(server.variables ?? {})) {\n url = url.replaceAll(`{${name}}`, variable.default);\n }\n\n try {\n return new URL(url).toString().replace(/\\/$/, \"\");\n } catch {\n if (!origin) {\n throw new Error(\n `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.`,\n );\n }\n\n return new URL(url, origin).toString().replace(/\\/$/, \"\");\n }\n}\n\n/**\n * Serializes a flattened body payload as `application/x-www-form-urlencoded`.\n * Array values become repeated keys, matching the existing query-parameter\n * convention. A nested object/array *value* is JSON-stringified into a\n * single form value rather than expanded with bracket notation (e.g.\n * Stripe's own `metadata[key]=value` style) — correct for the flat scalar\n * properties that make up the overwhelming majority of real form-encoded\n * APIs (Stripe, Twilio), not a full form-encoding implementation.\n * `URLSearchParams` handles percent-encoding for free.\n */\nfunction encodeFormBody(payload: unknown): string {\n const params = new URLSearchParams();\n\n if (payload && typeof payload === \"object\" && !Array.isArray(payload)) {\n for (const [key, value] of Object.entries(\n payload as Record<string, unknown>,\n )) {\n if (value === undefined) {\n continue;\n }\n\n for (const item of Array.isArray(value) ? value : [value]) {\n params.append(\n key,\n item !== null && typeof item === \"object\"\n ? JSON.stringify(item)\n : String(item),\n );\n }\n }\n }\n\n return params.toString();\n}\n\nasync function resolveHeaders(\n headers: FromOpenAPIOptions[\"headers\"],\n): Promise<Record<string, string>> {\n if (!headers) {\n return {};\n }\n\n return typeof headers === \"function\" ? await headers() : { ...headers };\n}\n","import type { ParameterMapping } from \"./schemas.js\";\nimport type { HttpRoute } from \"./types.js\";\n\nexport type ResourceMapping =\n | {\n args: { name: string; required: boolean }[];\n kind: \"template\";\n uriTemplate: string;\n }\n | { kind: \"resource\"; uri: string };\n\n/**\n * Builds a static resource URI, or a resource template (URI + `arguments`),\n * for an eligible `GET` route — reusing the `parameterMap` and `required`\n * list `buildFlatSchema` already produced for it (path-always-required,\n * collision-suffixed flat keys) rather than re-deriving parameter\n * flattening from scratch.\n *\n * OpenAPI's `{petId}` path-parameter syntax is already valid RFC 6570 simple\n * string expansion, so the route's own path is reused verbatim except where\n * a flat key was collision-suffixed. Query parameters are appended as an\n * RFC 6570 query-expansion segment (`{?a,b}`), which `uri-templates`\n * (already a FastMCP dependency — see its own resource-template dispatch in\n * FastMCP.ts) parses and fills the same way it does path variables.\n */\nexport function buildResourceMapping(\n route: HttpRoute,\n name: string,\n parameterMap: Record<string, ParameterMapping>,\n requiredKeys: string[] | undefined,\n): ResourceMapping {\n const entries = Object.entries(parameterMap);\n\n if (entries.length === 0) {\n return { kind: \"resource\", uri: `openapi://${name}${route.path}` };\n }\n\n const required = new Set(requiredKeys ?? []);\n const args: { name: string; required: boolean }[] = [];\n const queryKeys: string[] = [];\n let path = route.path;\n\n for (const [flatKey, mapping] of entries) {\n if (mapping.in === \"path\") {\n if (flatKey !== mapping.name) {\n path = path.replace(`{${mapping.name}}`, `{${flatKey}}`);\n }\n } else {\n queryKeys.push(flatKey);\n }\n\n args.push({ name: flatKey, required: required.has(flatKey) });\n }\n\n const uriTemplate =\n `openapi://${name}${path}` +\n (queryKeys.length > 0 ? `{?${queryKeys.join(\",\")}}` : \"\");\n\n return { args, kind: \"template\", uriTemplate };\n}\n\n/**\n * Whether a `GET` route can become an MCP resource/resource template\n * instead of a tool, when `resources: true` is passed to `fromOpenAPI`. See\n * docs/openapi.md \"GET → resources\" for the reasoning behind each carve-out:\n *\n * - `header`/`cookie` parameters can't be expressed in a resource URI, and\n * MCP resource reads have no per-call side channel for them.\n * - An array-typed path/query parameter can't be represented consistently\n * between OpenAPI's query serialization (repeated keys) and RFC 6570's\n * array representation (comma-joined or `*`-exploded).\n *\n * A route failing either check falls through to the existing tool path —\n * this only ever *removes* operations from the tool list in favor of a\n * resource, never breaks one.\n */\nexport function isEligibleForResource(route: HttpRoute): boolean {\n return route.parameters.every((param) => {\n if (param.in === \"header\" || param.in === \"cookie\") {\n return false;\n }\n\n return param.schema?.type !== \"array\";\n });\n}\n","import type {\n BundledOpenApiDocument,\n HttpMethod,\n HttpRoute,\n OpenApiParameter,\n OpenApiParameterRef,\n OpenApiRequestBody,\n} from \"./types.js\";\n\nconst HTTP_METHODS: HttpMethod[] = [\"get\", \"put\", \"post\", \"delete\", \"patch\"];\n\n/**\n * Walks a bundled document's `paths` into a flat list of routes, resolving\n * any structural (non-schema) `$ref`s on parameters and request bodies —\n * e.g. `#/components/parameters/Limit` — against the same document.\n *\n * Bundling (see `loadSpec.ts`) guarantees every remaining `$ref` here is\n * local, so a plain JSON-pointer lookup is enough.\n */\nexport function extractRoutes(document: BundledOpenApiDocument): HttpRoute[] {\n const routes: HttpRoute[] = [];\n\n for (const [path, pathItem] of Object.entries(document.paths ?? {})) {\n const pathLevelParams = (pathItem.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n for (const method of HTTP_METHODS) {\n const operation = pathItem[method];\n\n if (!operation) {\n continue;\n }\n\n const operationParams = (operation.parameters ?? []).map((param) =>\n resolveRef<OpenApiParameter>(document, param),\n );\n\n routes.push({\n deprecated: operation.deprecated ?? false,\n method,\n operationId: operation.operationId,\n parameters: mergeParameters(pathLevelParams, operationParams),\n path,\n requestBody: operation.requestBody\n ? resolveRef<OpenApiRequestBody>(document, operation.requestBody)\n : undefined,\n summary: operation.summary,\n tags: operation.tags ?? [],\n });\n }\n }\n\n return routes;\n}\n\nfunction mergeParameters(\n pathLevel: OpenApiParameter[],\n operationLevel: OpenApiParameter[],\n): OpenApiParameter[] {\n const overridden = new Set(\n operationLevel.map((param) => `${param.in}:${param.name}`),\n );\n\n return [\n ...pathLevel.filter(\n (param) => !overridden.has(`${param.in}:${param.name}`),\n ),\n ...operationLevel,\n ];\n}\n\nfunction resolveRef<TValue>(\n document: BundledOpenApiDocument,\n value: OpenApiParameterRef | TValue,\n): TValue {\n if (!value || typeof value !== \"object\" || !(\"$ref\" in value)) {\n return value;\n }\n\n const pointer = value.$ref;\n\n if (!pointer.startsWith(\"#/\")) {\n // Bundling should have already turned every external ref into a local\n // one — if this fires, swagger-parser's output shape has changed.\n throw new Error(`Unexpected external $ref after bundling: ${pointer}`);\n }\n\n // swagger-parser synthesizes these pointers as URI fragments (e.g. a path\n // like \"/pets/{petId}\" becomes \"~1pets~1%7BpetId%7D\"), so each segment\n // needs its \"~1\"/\"~0\" escapes undone *and* percent-decoding, in that order.\n const segments = pointer\n .slice(2)\n .split(\"/\")\n .map((segment) =>\n decodeURIComponent(segment.replaceAll(\"~1\", \"/\").replaceAll(\"~0\", \"~\")),\n );\n\n let node: unknown = document;\n\n for (const segment of segments) {\n node = (node as Record<string, unknown> | undefined)?.[segment];\n }\n\n return node as TValue;\n}\n","import type { JsonSchemaObject } from \"../jsonSchemaAdapter.js\";\nimport type {\n BundledOpenApiDocument,\n HttpRoute,\n OpenApiParameter,\n OpenApiRequestBody,\n OpenApiSchema,\n ParameterLocation,\n} from \"./types.js\";\n\n/**\n * Keys whose values are name-to-schema maps: their child keys are\n * author-chosen names rather than JSON Schema keywords.\n */\nconst SCHEMA_MAP_KEYS = new Set([\n \"$defs\",\n \"definitions\",\n \"dependentSchemas\",\n \"patternProperties\",\n \"properties\",\n]);\n\n/**\n * Keys whose values are arbitrary instance data rather than schemas. A\n * sample payload may well contain a \"$ref\" or \"nullable\" key of its own.\n */\nconst DATA_KEYS = new Set([\"const\", \"default\", \"enum\", \"example\", \"examples\"]);\n\n/**\n * Request body content types this module knows how to flatten and encode.\n * `application/json` wins when a route declares both.\n */\nexport const SUPPORTED_BODY_CONTENT_TYPES = [\n \"application/json\",\n \"application/x-www-form-urlencoded\",\n] as const;\n\nexport interface FlatSchemaResult {\n /** How the request body (if any properties were extracted) must be serialized. */\n bodyEncoding?: \"form\" | \"json\";\n flatSchema: JsonSchemaObject;\n parameterMap: Record<string, ParameterMapping>;\n /**\n * Set when `route.requestBody` declares a body this module can't carry:\n * either only in content type(s) it doesn't support (e.g.\n * `multipart/form-data`, `application/json-patch+json`,\n * `application/octet-stream`), or as `application/x-www-form-urlencoded`\n * with a schema that isn't a flat object, which form encoding can't\n * represent. Holds the content type the body was declared in. The caller\n * should not turn this route into a tool with a payload it can never\n * carry — see `fromOpenAPI.ts`.\n */\n unsupportedBodyContentType?: string;\n /**\n * Set when the request body's schema is not a flat object (e.g. an array,\n * or a bare non-object `$ref`) — the whole body is exposed as a single\n * property under this key, rather than flattened into individual\n * properties.\n */\n wholeBodyKey?: string;\n}\n\nexport interface ParameterMapping {\n in: \"body\" | ParameterLocation;\n name: string;\n}\n\ntype WalkMode = \"data\" | \"schema\" | \"schemaMap\";\n\n/**\n * Flattens a route's path/query/header/cookie parameters and request body\n * into a single tool input schema.\n *\n * Collision precedence ports the Python implementation's rule\n * (`utilities/openapi/schemas.py:_combine_schemas_and_map_params`): a name\n * that collides across path/query/header/cookie gets suffixed\n * `{name}__{location}`; a request body property with a colliding name always\n * keeps its bare name.\n *\n * `GET` never contributes a request body: `fetch` (and the Fetch spec in\n * general) rejects a body on a GET request, so a tool built from a spec's\n * (legal, if unusual) `GET` + `requestBody` operation would be permanently\n * broken. The request body is simply not flattened into the schema for such\n * a route, rather than surfacing a schema that can never actually be called.\n */\nexport function buildFlatSchema(\n route: HttpRoute,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): FlatSchemaResult {\n const byName = new Map<string, OpenApiParameter[]>();\n\n for (const param of route.parameters) {\n const list = byName.get(param.name) ?? [];\n list.push(param);\n byName.set(param.name, list);\n }\n\n const {\n bodyEncoding,\n properties: bodyProperties,\n unsupportedBodyContentType,\n wholeBodyKey,\n } = extractBodyProperties(\n route.method === \"get\" ? undefined : route.requestBody,\n sharedDefs,\n );\n\n const properties: Record<string, OpenApiSchema> = {};\n const required: string[] = [];\n const parameterMap: Record<string, ParameterMapping> = {};\n\n for (const [name, occurrences] of byName) {\n const collides = occurrences.length > 1 || bodyProperties.has(name);\n\n for (const param of occurrences) {\n const key = collides ? `${name}__${param.in}` : name;\n\n properties[key] = rewriteComponentRefs(\n param.schema ?? { type: \"string\" },\n );\n parameterMap[key] = { in: param.in, name };\n\n if (param.in === \"path\" || param.required) {\n required.push(key);\n }\n }\n }\n\n for (const [name, { required: isRequired, schema }] of bodyProperties) {\n properties[name] = rewriteComponentRefs(schema);\n parameterMap[name] = { in: \"body\", name };\n\n if (isRequired) {\n required.push(name);\n }\n }\n\n const flatSchema: JsonSchemaObject = {\n additionalProperties: false,\n properties,\n type: \"object\",\n ...(required.length > 0 ? { required } : {}),\n };\n\n // Only the definitions this tool's own schema actually (transitively)\n // references — embedding the whole document's components.schemas into\n // every single tool would multiply the tools/list payload size by the\n // tool count for no benefit.\n const usedDefs = sharedDefs && filterReferencedDefs(properties, sharedDefs);\n\n if (usedDefs) {\n flatSchema.$defs = usedDefs;\n }\n\n return {\n bodyEncoding,\n flatSchema,\n parameterMap,\n unsupportedBodyContentType,\n wholeBodyKey,\n };\n}\n\nexport function buildSharedDefs(\n document: BundledOpenApiDocument,\n): Record<string, OpenApiSchema> | undefined {\n const schemas = document.components?.schemas;\n\n if (!schemas || Object.keys(schemas).length === 0) {\n return undefined;\n }\n\n return rewriteNode(schemas, \"schemaMap\") as Record<string, OpenApiSchema>;\n}\n\n/**\n * Rewrites `$ref`s pointing at `#/components/schemas/...` to `#/$defs/...`,\n * so a per-tool schema that contains one can be handed to AJV standalone,\n * alongside a `$defs` object built from the document's `components.schemas`\n * (see `buildSharedDefs`). Ports the equivalent rewrite from the Python\n * implementation (`utilities/openapi/schemas.py:_replace_ref_with_defs`).\n *\n * Also normalizes OpenAPI 3.0's `nullable` keyword (see `normalizeNullable`),\n * since real specs carry both.\n */\nexport function rewriteComponentRefs<TValue>(value: TValue): TValue {\n return rewriteNode(value, \"schema\") as TValue;\n}\n\nfunction childMode(key: string): WalkMode {\n if (DATA_KEYS.has(key)) {\n return \"data\";\n }\n\n return SCHEMA_MAP_KEYS.has(key) ? \"schemaMap\" : \"schema\";\n}\n\nfunction componentSchemaName(ref: string): string | undefined {\n for (const prefix of [\"#/components/schemas/\", \"#/$defs/\"]) {\n if (ref.startsWith(prefix)) {\n return ref.slice(prefix.length);\n }\n }\n\n return undefined;\n}\n\n/**\n * Picks the request body's content type and flattens its schema.\n *\n * `application/json` wins if a route declares both it and\n * `application/x-www-form-urlencoded` (a fixed preference, not declaration\n * order — the latter isn't a reliable signal). A route whose body is only\n * declared under a content type this module doesn't handle at all (e.g.\n * `multipart/form-data`) reports `unsupportedBodyContentType` instead of\n * silently returning an empty property map — that emptiness is exactly what\n * a real Stripe/Twilio operation (both form-urlencoded-only) looked like\n * before this function read anything but JSON, and it produced a tool with\n * no way to carry its actual payload. A bare `content: {}` (no content\n * types at all) still means \"no body,\" not \"unsupported\" — and so does a\n * supported content type with no `schema` at all (a legal, if unusual,\n * \"any JSON body\" declaration): the content type itself is fine, there's\n * just nothing to flatten.\n *\n * A form-urlencoded body is very often declared as a bare `$ref` to a\n * component schema rather than inline — FastAPI emits\n * `#/components/schemas/Body_<operation>` for every form endpoint, and\n * Box's OAuth token/refresh/revoke operations do the same. The document is\n * bundled, not dereferenced (see `loadSpec.ts`), so that `$ref` is resolved\n * here against `sharedDefs` before deciding whether the body is a flat\n * object. Only the form path needs this: a JSON body that isn't a flat\n * object falls back to a single whole-body property, which a `$ref`\n * satisfies as-is.\n *\n * A non-object body (array, `$ref` to a scalar/array, etc.) can't be\n * form-urlencoded at all — form encoding is inherently flat key/value pairs\n * — so that combination is also reported as unsupported, rather than\n * `encodeFormBody` (requestBuilder.ts) silently sending an empty body for\n * data it has no way to represent.\n */\nfunction extractBodyProperties(\n requestBody: OpenApiRequestBody | undefined,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): {\n bodyEncoding?: \"form\" | \"json\";\n properties: Map<string, { required: boolean; schema: OpenApiSchema }>;\n unsupportedBodyContentType?: string;\n wholeBodyKey?: string;\n} {\n const properties = new Map<\n string,\n { required: boolean; schema: OpenApiSchema }\n >();\n\n const content = requestBody?.content;\n\n if (!content) {\n return { properties };\n }\n\n const hasJson = \"application/json\" in content;\n const hasForm = \"application/x-www-form-urlencoded\" in content;\n\n if (!hasJson && !hasForm) {\n const contentTypes = Object.keys(content);\n\n return contentTypes.length > 0\n ? { properties, unsupportedBodyContentType: contentTypes[0] }\n : { properties };\n }\n\n const bodyEncoding: \"form\" | \"json\" = hasJson ? \"json\" : \"form\";\n const declaredSchema = hasJson\n ? content[\"application/json\"]?.schema\n : content[\"application/x-www-form-urlencoded\"]?.schema;\n\n if (!declaredSchema) {\n // The content type is declared and supported; it just has no schema\n // (an unconstrained body) — nothing to flatten, but not unsupported.\n return { bodyEncoding, properties };\n }\n\n const schema =\n bodyEncoding === \"form\"\n ? resolveComponentRef(declaredSchema, sharedDefs)\n : declaredSchema;\n\n const schemaProperties = schema.properties as\n | Record<string, OpenApiSchema>\n | undefined;\n\n if (schema.type === \"object\" && schemaProperties) {\n const requiredNames = new Set(\n (schema.required as string[] | undefined) ?? [],\n );\n\n for (const [name, propertySchema] of Object.entries(schemaProperties)) {\n properties.set(name, {\n required: requiredNames.has(name),\n schema: propertySchema,\n });\n }\n\n return { bodyEncoding, properties };\n }\n\n if (bodyEncoding === \"form\") {\n return {\n properties,\n unsupportedBodyContentType: \"application/x-www-form-urlencoded\",\n };\n }\n\n // Non-object JSON body (array, bare $ref to a scalar/array, etc.) — expose\n // the whole thing as a single \"body\" property rather than flattening it.\n properties.set(\"body\", {\n required: requestBody?.required ?? false,\n schema,\n });\n\n return { bodyEncoding, properties, wholeBodyKey: \"body\" };\n}\n\n/**\n * Walks a schema fragment for `#/$defs/Name` refs and returns just those\n * definitions (transitively — a referenced def may itself reference\n * others), or `undefined` if none are referenced.\n */\nfunction filterReferencedDefs(\n node: unknown,\n allDefs: Record<string, OpenApiSchema>,\n): Record<string, OpenApiSchema> | undefined {\n const referenced = new Set<string>();\n const stack: unknown[] = [node];\n\n while (stack.length > 0) {\n const current = stack.pop();\n\n if (Array.isArray(current)) {\n stack.push(...current);\n continue;\n }\n\n if (!current || typeof current !== \"object\") {\n continue;\n }\n\n for (const [key, value] of Object.entries(\n current as Record<string, unknown>,\n )) {\n if (\n key === \"$ref\" &&\n typeof value === \"string\" &&\n value.startsWith(\"#/$defs/\")\n ) {\n const name = value.slice(\"#/$defs/\".length);\n\n if (allDefs[name] && !referenced.has(name)) {\n referenced.add(name);\n stack.push(allDefs[name]);\n }\n\n continue;\n }\n\n stack.push(value);\n }\n }\n\n if (referenced.size === 0) {\n return undefined;\n }\n\n return Object.fromEntries(\n [...referenced].map((name) => [name, allDefs[name]]),\n );\n}\n\n/**\n * OpenAPI 3.0's `nullable` keyword only makes sense alongside a sibling\n * `type`, which it widens (`nullable: true` + `type: \"string\"` means\n * \"string or null\") — but it is not itself standard JSON Schema. AJV\n * recognizes the keyword and throws ('\"nullable\" cannot be used without\n * \"type\"') if it finds one with no `type` on the same node, which real\n * specs do produce (e.g. `nullable` sibling to `oneOf`/`allOf`/`$ref`\n * instead of `type`, as in Box's API). Folded into `type` where there is\n * one to widen, dropped otherwise.\n */\nfunction normalizeNullable(\n schema: Record<string, unknown>,\n): Record<string, unknown> {\n if (!(\"nullable\" in schema)) {\n return schema;\n }\n\n const { nullable, type, ...rest } = schema;\n\n if (nullable !== true) {\n return rest;\n }\n\n if (typeof type === \"string\") {\n return { ...rest, type: [type, \"null\"] };\n }\n\n if (Array.isArray(type)) {\n return { ...rest, type: [...new Set([\"null\", ...type])] };\n }\n\n return rest;\n}\n\n/**\n * Follows a bare `$ref` into `components.schemas` — or its rewritten\n * `#/$defs/` form, which is what `sharedDefs` entries themselves carry —\n * until it reaches a concrete schema. A dangling or cyclic reference is\n * returned as-is rather than failing the whole conversion.\n */\nfunction resolveComponentRef(\n schema: OpenApiSchema,\n sharedDefs: Record<string, OpenApiSchema> | undefined,\n): OpenApiSchema {\n const seen = new Set<string>();\n let current = schema;\n let ref = current.$ref;\n\n while (typeof ref === \"string\") {\n const name = componentSchemaName(ref);\n const target = name === undefined ? undefined : sharedDefs?.[name];\n\n if (name === undefined || target === undefined || seen.has(name)) {\n break;\n }\n\n seen.add(name);\n current = target;\n ref = current.$ref;\n }\n\n return current;\n}\n\n/**\n * Walks a schema fragment, distinguishing the three kinds of node it can\n * reach — because only one of them is a schema whose keys are JSON Schema\n * keywords:\n *\n * - `\"schema\"` — a schema object. `$ref`/`nullable` here are keywords.\n * - `\"schemaMap\"` — a name-to-schema map (`properties`, `$defs`, ...). Its\n * keys are author-chosen names, so a property literally named `nullable`\n * or `$ref` is a field, not a keyword, and must survive untouched.\n * - `\"data\"` — arbitrary values (`default`, `enum`, `example`, ...). Not\n * schemas at all; passed through verbatim.\n *\n * Walking every node as a schema (as this originally did) silently deletes\n * a property named `nullable` from the generated tool schema, since\n * `normalizeNullable` cannot tell the keyword from a same-named field.\n */\nfunction rewriteNode(value: unknown, mode: WalkMode): unknown {\n if (mode === \"data\") {\n return value;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => rewriteNode(item, \"schema\"));\n }\n\n if (!value || typeof value !== \"object\") {\n return value;\n }\n\n const entries = Object.entries(value as Record<string, unknown>).map(\n ([key, entryValue]): [string, unknown] => {\n if (mode === \"schemaMap\") {\n return [key, rewriteNode(entryValue, \"schema\")];\n }\n\n if (\n key === \"$ref\" &&\n typeof entryValue === \"string\" &&\n entryValue.startsWith(\"#/components/schemas/\")\n ) {\n return [key, entryValue.replace(\"#/components/schemas/\", \"#/$defs/\")];\n }\n\n return [key, rewriteNode(entryValue, childMode(key))];\n },\n );\n\n const rewritten = Object.fromEntries(entries) as Record<string, unknown>;\n\n return mode === \"schemaMap\" ? rewritten : normalizeNullable(rewritten);\n}\n","import type {\n FromOpenAPIOptions,\n HttpMethod,\n HttpRoute,\n OperationSummary,\n} from \"./types.js\";\n\n/**\n * If neither `include`/`exclude` nor `maxTools` was given, and a spec still\n * produces more operations than this, `selectRoutes` throws rather than\n * silently generating a wall of tools most clients can't usefully work with.\n */\nexport const DEFAULT_MAX_OPERATIONS = 40;\n\nconst METHOD_PRIORITY: Record<HttpMethod, number> = {\n delete: 4,\n get: 0,\n patch: 3,\n post: 1,\n put: 2,\n};\n\n/**\n * Filters and orders routes into the set that becomes tools.\n *\n * Deprecated operations are excluded by default. Ordering is deterministic\n * (method priority GET→POST→PUT→PATCH→DELETE, then path) so that, combined\n * with `maxTools`, truncation is legible rather than arbitrary.\n */\nexport function selectRoutes(\n routes: HttpRoute[],\n options: Pick<FromOpenAPIOptions, \"exclude\" | \"include\" | \"maxTools\">,\n): HttpRoute[] {\n let selected = routes.filter((route) => !route.deprecated);\n\n if (options.include) {\n const include = options.include;\n selected = selected.filter((route) => include(toSummary(route)));\n }\n\n if (options.exclude) {\n const exclude = options.exclude;\n selected = selected.filter((route) => !exclude(toSummary(route)));\n }\n\n selected = [...selected].sort((a, b) => {\n const byMethod = METHOD_PRIORITY[a.method] - METHOD_PRIORITY[b.method];\n return byMethod !== 0 ? byMethod : a.path.localeCompare(b.path);\n });\n\n const noSelectionGiven = !options.include && !options.exclude;\n\n if (\n noSelectionGiven &&\n options.maxTools === undefined &&\n selected.length > DEFAULT_MAX_OPERATIONS\n ) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds the default limit of ${DEFAULT_MAX_OPERATIONS}. ` +\n \"This is a deliberate stop, not a bug: turning every operation in a large spec into a tool produces a tool list most MCP clients can't use well. \" +\n \"Pass `include`/`exclude` to choose the operations you actually want, or `maxTools` to raise this limit explicitly.\",\n );\n }\n\n if (options.maxTools !== undefined && selected.length > options.maxTools) {\n throw new Error(\n `fromOpenAPI found ${selected.length} operations, which exceeds maxTools (${options.maxTools}). ` +\n \"Narrow the spec with `include`/`exclude`, or raise `maxTools`.\",\n );\n }\n\n return selected;\n}\n\nfunction toSummary(route: HttpRoute): OperationSummary {\n return {\n deprecated: route.deprecated,\n method: route.method,\n operationId: route.operationId,\n path: route.path,\n tags: route.tags,\n };\n}\n","import type { ResourceResult } from \"../FastMCP.js\";\nimport type { ParameterMapping } from \"./schemas.js\";\nimport type { HttpRoute } from \"./types.js\";\nimport type { FromOpenAPIOptions } from \"./types.js\";\n\nimport { FastMCP } from \"../FastMCP.js\";\nimport { jsonSchemaAdapter } from \"../jsonSchemaAdapter.js\";\nimport { loadSpec } from \"./loadSpec.js\";\nimport { generateNames } from \"./naming.js\";\nimport { executeRequest } from \"./requestBuilder.js\";\nimport {\n buildResourceMapping,\n isEligibleForResource,\n} from \"./resourceMapping.js\";\nimport { extractRoutes } from \"./routes.js\";\nimport { buildFlatSchema, buildSharedDefs } from \"./schemas.js\";\nimport { selectRoutes } from \"./selection.js\";\n\n/**\n * Converts an OpenAPI 3.x document into an MCP server, one tool (or, with\n * `resources: true`, resource/resource template for an eligible `GET`) per\n * operation.\n *\n * See docs/openapi.md for the full option reference and known limitations.\n */\nexport async function fromOpenAPI(\n options: FromOpenAPIOptions,\n): Promise<FastMCP> {\n const { document, origin } = await loadSpec(options.spec);\n const routes = extractRoutes(document);\n const selected = selectRoutes(routes, options);\n const names = generateNames(selected, options.mcpNames);\n const sharedDefs = buildSharedDefs(document);\n\n const server =\n options.server ??\n new FastMCP({\n name: options.name ?? document.info?.title ?? \"OpenAPI Server\",\n version: options.version ?? \"1.0.0\",\n });\n\n const skippedOperations: {\n contentType: string;\n method: string;\n path: string;\n }[] = [];\n\n for (const route of selected) {\n const name = names.get(route);\n\n if (!name) {\n continue;\n }\n\n const {\n bodyEncoding,\n flatSchema,\n parameterMap,\n unsupportedBodyContentType,\n wholeBodyKey,\n } = buildFlatSchema(route, sharedDefs);\n\n const execOptions = {\n baseUrlOverride: options.baseUrl,\n fetchImpl: options.fetch ?? fetch,\n headers: options.headers,\n origin,\n parameterMap,\n route,\n servers: document.servers,\n };\n\n if (\n options.resources &&\n route.method === \"get\" &&\n isEligibleForResource(route)\n ) {\n registerResource(\n server,\n route,\n name,\n parameterMap,\n flatSchema.required,\n execOptions,\n );\n continue;\n }\n\n if (unsupportedBodyContentType) {\n skippedOperations.push({\n contentType: unsupportedBodyContentType,\n method: route.method,\n path: route.path,\n });\n continue;\n }\n\n server.addTool({\n description:\n route.summary ?? `${route.method.toUpperCase()} ${route.path}`,\n execute: async (args) =>\n executeRequest({\n ...execOptions,\n args: args as Record<string, unknown>,\n bodyEncoding,\n wholeBodyKey,\n }),\n name,\n parameters: jsonSchemaAdapter(flatSchema),\n });\n }\n\n if (skippedOperations.length > 0) {\n console.warn(\n \"fromOpenAPI: skipped \" +\n `${skippedOperations.length} operation(s) whose request body can't be turned into tool parameters ` +\n \"(supported: application/json, or application/x-www-form-urlencoded with a flat object schema): \" +\n skippedOperations\n .map(\n (op) => `${op.method.toUpperCase()} ${op.path} (${op.contentType})`,\n )\n .join(\", \"),\n );\n }\n\n return server;\n}\n\nfunction registerResource(\n server: FastMCP,\n route: HttpRoute,\n name: string,\n parameterMap: Record<string, ParameterMapping>,\n requiredKeys: string[] | undefined,\n execOptions: Omit<\n Parameters<typeof executeRequest>[0],\n \"args\" | \"bodyEncoding\" | \"wholeBodyKey\"\n >,\n): void {\n const mapping = buildResourceMapping(route, name, parameterMap, requiredKeys);\n const description = route.summary ?? `GET ${route.path}`;\n\n if (mapping.kind === \"resource\") {\n server.addResource({\n description,\n load: async () =>\n wrapAsResourceResult(\n await executeRequest({ ...execOptions, args: {} }),\n ),\n name,\n uri: mapping.uri,\n });\n\n return;\n }\n\n server.addResourceTemplate({\n arguments: mapping.args,\n description,\n load: async (args) =>\n wrapAsResourceResult(\n await executeRequest({\n ...execOptions,\n args: args as Record<string, unknown>,\n }),\n ),\n name,\n uriTemplate: mapping.uriTemplate,\n });\n}\n\nfunction wrapAsResourceResult(text: string): ResourceResult {\n try {\n JSON.parse(text);\n return { mimeType: \"application/json\", text };\n } catch {\n return { mimeType: \"text/plain\", text };\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fastmcp",
3
- "version": "4.18.0",
3
+ "version": "4.19.0",
4
4
  "main": "dist/FastMCP.js",
5
5
  "scripts": {
6
6
  "build": "tsup",