extract_openapi_endpoints
Extract specific endpoints from a large OpenAPI definition file to create a smaller, focused API specification. Input the path to the original file, list endpoints to keep, and save the condensed output for targeted use.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| endpointsToKeep | Yes | List of exact endpoint paths to keep (e.g., ['/users', '/users/{id}']). | |
| inputApiPath | Yes | Absolute path to the large input OpenAPI definition file. | |
| outputApiPath | Yes | Absolute path where the final, smaller bundled OpenAPI file should be saved. |
Implementation Reference
- src/index.ts:84-181 (handler)Handler function for 'extract_openapi_endpoints' tool. Creates temp dir, splits OpenAPI using redocly, parses YAML, filters specified endpoints, rewrites YAML, bundles to output path, cleans up temp dir.async ({ inputApiPath, endpointsToKeep, outputApiPath }, extra) => { let tempDir: string | undefined; try { // 1. Create a temporary directory tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openapi-extract-')); // Generic temp prefix console.error(`Created temp directory: ${tempDir}`); // 2. Split the input API into the temporary directory const splitCommand = `npx @redocly/cli@latest split "${inputApiPath}" --outDir="${tempDir}"`; console.error(`Executing split command: ${splitCommand}`); const { stdout: splitStdout, stderr: splitStderr } = await exec(splitCommand); if (splitStderr) { console.error("Split stderr:", splitStderr); } console.error("Split stdout:", splitStdout); // 3. Read the main openapi.yaml from the temporary directory const tempApiFilePath = path.join(tempDir, 'openapi.yaml'); // Default name used by split console.error(`Reading temporary API file: ${tempApiFilePath}`); const tempApiContent = await fs.readFile(tempApiFilePath, 'utf-8'); // 4. Parse the YAML const apiDoc: any = yaml.load(tempApiContent); if (!apiDoc || typeof apiDoc !== 'object' || !apiDoc.paths) { throw new Error('Invalid OpenAPI structure found after split.'); } // 5. Filter the paths console.error(`Filtering paths, keeping: ${endpointsToKeep.join(', ')}`); const originalPaths = apiDoc.paths; const filteredPaths: { [key: string]: any; } = {}; let pathsKept = 0; for (const pathKey in originalPaths) { if (endpointsToKeep.includes(pathKey)) { filteredPaths[pathKey] = originalPaths[pathKey]; pathsKept++; } } if (pathsKept === 0) { console.warn("Warning: No paths matched the endpointsToKeep list. The output file might be empty or invalid."); } else if (pathsKept < endpointsToKeep.length) { console.warn(`Warning: Only ${pathsKept} out of ${endpointsToKeep.length} specified endpoints were found in the original spec.`); } apiDoc.paths = filteredPaths; // 6. Write the modified YAML back const modifiedYamlContent = yaml.dump(apiDoc); console.error(`Writing modified API file back to: ${tempApiFilePath}`); await fs.writeFile(tempApiFilePath, modifiedYamlContent, 'utf-8'); // 7. Bundle the modified API to the final output path // Ensure the output directory exists const outputDir = path.dirname(outputApiPath); await fs.mkdir(outputDir, { recursive: true }); const bundleCommand = `npx @redocly/cli@latest bundle "${tempApiFilePath}" -o "${outputApiPath}"`; console.error(`Executing bundle command: ${bundleCommand}`); const { stdout: bundleStdout, stderr: bundleStderr } = await exec(bundleCommand); if (bundleStderr) { console.error("Bundle stderr:", bundleStderr); } console.error("Bundle stdout:", bundleStdout); return { content: [{ type: "text", text: `Successfully extracted endpoints and saved to ${outputApiPath}\n${bundleStdout}` }] }; } catch (error: any) { console.error("Extraction process failed:", error); const errorMessage = error.stderr || error.stdout || error.message || "Unknown error occurred during extraction"; return { content: [{ type: "text", text: `Error: ${errorMessage}` }], isError: true }; } finally { // 8. Clean up the temporary directory if (tempDir) { try { console.error(`Cleaning up temp directory: ${tempDir}`); await fs.rm(tempDir, { recursive: true, force: true }); } catch (cleanupError) { console.error(`Failed to clean up temp directory ${tempDir}:`, cleanupError); // Log cleanup error but don't let it mask the primary error } } } }
- src/index.ts:23-27 (schema)Zod schema defining input arguments for the extract_openapi_endpoints tool: inputApiPath, endpointsToKeep array, outputApiPath.const ExtractOpenApiEndpointsArgsSchema = z.object({ inputApiPath: z.string().describe("Absolute path to the large input OpenAPI definition file."), endpointsToKeep: z.array(z.string()).describe("List of exact endpoint paths to keep (e.g., ['/users', '/users/{id}'])."), outputApiPath: z.string().describe("Absolute path where the final, smaller bundled OpenAPI file should be saved.") });
- src/index.ts:80-182 (registration)Registration of the 'extract_openapi_endpoints' tool using server.tool(), providing name, input schema, and inline handler function.// Register the 'extract_openapi_endpoints' tool server.tool( "extract_openapi_endpoints", // Renamed tool ExtractOpenApiEndpointsArgsSchema.shape, // Use renamed schema variable async ({ inputApiPath, endpointsToKeep, outputApiPath }, extra) => { let tempDir: string | undefined; try { // 1. Create a temporary directory tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openapi-extract-')); // Generic temp prefix console.error(`Created temp directory: ${tempDir}`); // 2. Split the input API into the temporary directory const splitCommand = `npx @redocly/cli@latest split "${inputApiPath}" --outDir="${tempDir}"`; console.error(`Executing split command: ${splitCommand}`); const { stdout: splitStdout, stderr: splitStderr } = await exec(splitCommand); if (splitStderr) { console.error("Split stderr:", splitStderr); } console.error("Split stdout:", splitStdout); // 3. Read the main openapi.yaml from the temporary directory const tempApiFilePath = path.join(tempDir, 'openapi.yaml'); // Default name used by split console.error(`Reading temporary API file: ${tempApiFilePath}`); const tempApiContent = await fs.readFile(tempApiFilePath, 'utf-8'); // 4. Parse the YAML const apiDoc: any = yaml.load(tempApiContent); if (!apiDoc || typeof apiDoc !== 'object' || !apiDoc.paths) { throw new Error('Invalid OpenAPI structure found after split.'); } // 5. Filter the paths console.error(`Filtering paths, keeping: ${endpointsToKeep.join(', ')}`); const originalPaths = apiDoc.paths; const filteredPaths: { [key: string]: any; } = {}; let pathsKept = 0; for (const pathKey in originalPaths) { if (endpointsToKeep.includes(pathKey)) { filteredPaths[pathKey] = originalPaths[pathKey]; pathsKept++; } } if (pathsKept === 0) { console.warn("Warning: No paths matched the endpointsToKeep list. The output file might be empty or invalid."); } else if (pathsKept < endpointsToKeep.length) { console.warn(`Warning: Only ${pathsKept} out of ${endpointsToKeep.length} specified endpoints were found in the original spec.`); } apiDoc.paths = filteredPaths; // 6. Write the modified YAML back const modifiedYamlContent = yaml.dump(apiDoc); console.error(`Writing modified API file back to: ${tempApiFilePath}`); await fs.writeFile(tempApiFilePath, modifiedYamlContent, 'utf-8'); // 7. Bundle the modified API to the final output path // Ensure the output directory exists const outputDir = path.dirname(outputApiPath); await fs.mkdir(outputDir, { recursive: true }); const bundleCommand = `npx @redocly/cli@latest bundle "${tempApiFilePath}" -o "${outputApiPath}"`; console.error(`Executing bundle command: ${bundleCommand}`); const { stdout: bundleStdout, stderr: bundleStderr } = await exec(bundleCommand); if (bundleStderr) { console.error("Bundle stderr:", bundleStderr); } console.error("Bundle stdout:", bundleStdout); return { content: [{ type: "text", text: `Successfully extracted endpoints and saved to ${outputApiPath}\n${bundleStdout}` }] }; } catch (error: any) { console.error("Extraction process failed:", error); const errorMessage = error.stderr || error.stdout || error.message || "Unknown error occurred during extraction"; return { content: [{ type: "text", text: `Error: ${errorMessage}` }], isError: true }; } finally { // 8. Clean up the temporary directory if (tempDir) { try { console.error(`Cleaning up temp directory: ${tempDir}`); await fs.rm(tempDir, { recursive: true, force: true }); } catch (cleanupError) { console.error(`Failed to clean up temp directory ${tempDir}:`, cleanupError); // Log cleanup error but don't let it mask the primary error } } } } );