Skip to main content
Glama
auto-browse

Unbundle OpenAPI Specs MCP

by auto-browse

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
NameRequiredDescriptionDefault
endpointsToKeepYesList of exact endpoint paths to keep (e.g., ['/users', '/users/{id}']).
inputApiPathYesAbsolute path to the large input OpenAPI definition file.
outputApiPathYesAbsolute path where the final, smaller bundled OpenAPI file should be saved.

Implementation Reference

  • 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
                }
            }
        }
    }
  • 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
                    }
                }
            }
        }
    );
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Tool has no description.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness1/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Tool has no description.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Tool has no description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Tool has no description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose1/5

Does the description clearly state what the tool does and how it differs from similar tools?

Tool has no description.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Tool has no description.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/auto-browse/unbundle_openapi_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server