From d943231672eb4e0f61894c5d09a0684670405acb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 Aug 2025 04:33:51 +0000 Subject: [PATCH] Implement BYO OpenAPIV3.Document support in getToolsFromOpenApi Co-authored-by: harsha-iiiv <31560965+harsha-iiiv@users.noreply.github.com> --- PROGRAMMATIC_API.md | 13 ++++++++++++- src/api.ts | 35 +++++++++++++++++++++++++++++------ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/PROGRAMMATIC_API.md b/PROGRAMMATIC_API.md index 3e16400..39684c1 100644 --- a/PROGRAMMATIC_API.md +++ b/PROGRAMMATIC_API.md @@ -22,7 +22,7 @@ This function extracts an array of tools from an OpenAPI specification. **Parameters:** -- `specPathOrUrl`: Path to a local OpenAPI spec file or URL to a remote spec +- `specPathOrUrl`: Path to a local OpenAPI spec file, URL to a remote spec, or a pre-parsed OpenAPIV3.Document - `options`: (Optional) Configuration options **Options:** @@ -62,6 +62,17 @@ for (const tool of filteredTools) { console.log(` Method: ${tool.method.toUpperCase()} ${tool.pathTemplate}`); console.log(` OperationId: ${tool.operationId}`); } + +// Using a pre-parsed OpenAPI document (helpful when @apidevtools/swagger-parser fails) +import { parse } from '@readme/openapi-parser'; +import { OpenAPIV3 } from 'openapi-types'; + +const api = await parse( + 'https://problematic-api-spec.com/openapi.json', + { dereference: { circular: true } } +); + +const tools = await getToolsFromOpenApi(api); ``` ## Tool Definition Structure diff --git a/src/api.ts b/src/api.ts index 7194000..a8b3bff 100644 --- a/src/api.ts +++ b/src/api.ts @@ -9,6 +9,16 @@ import { extractToolsFromApi } from './parser/extract-tools.js'; import { McpToolDefinition } from './types/index.js'; import { determineBaseUrl } from './utils/url.js'; +/** + * Type guard to check if the input is an OpenAPI document + * + * @param spec Input that could be a string or OpenAPIV3.Document + * @returns True if input is an OpenAPIV3.Document, false otherwise + */ +function isOpenApiDocument(spec: string | OpenAPIV3.Document): spec is OpenAPIV3.Document { + return typeof spec === 'object' && spec !== null && 'openapi' in spec; +} + /** * Options for generating the MCP tools */ @@ -29,19 +39,32 @@ export interface GetToolsOptions { /** * Get a list of tools from an OpenAPI specification * - * @param specPathOrUrl Path or URL to the OpenAPI specification + * @param specPathOrUrl Path or URL to the OpenAPI specification, or a pre-parsed OpenAPI document * @param options Options for generating the tools * @returns Promise that resolves to an array of tool definitions */ export async function getToolsFromOpenApi( - specPathOrUrl: string, + specPathOrUrl: string | OpenAPIV3.Document, options: GetToolsOptions = {} ): Promise { try { - // Parse the OpenAPI spec - const api = options.dereference - ? ((await SwaggerParser.dereference(specPathOrUrl)) as OpenAPIV3.Document) - : ((await SwaggerParser.parse(specPathOrUrl)) as OpenAPIV3.Document); + // Parse the OpenAPI spec or use the provided document + let api: OpenAPIV3.Document; + + if (isOpenApiDocument(specPathOrUrl)) { + // Input is already a parsed OpenAPI document + api = specPathOrUrl; + + // If dereference option is requested, apply it to the document + if (options.dereference) { + api = (await SwaggerParser.dereference(api)) as OpenAPIV3.Document; + } + } else { + // Input is a string path or URL, parse it + api = options.dereference + ? ((await SwaggerParser.dereference(specPathOrUrl)) as OpenAPIV3.Document) + : ((await SwaggerParser.parse(specPathOrUrl)) as OpenAPIV3.Document); + } // Extract tools from the API const allTools = extractToolsFromApi(api);