├── .gitignore ├── LICENSE ├── README.md ├── index.js ├── openapi-mcp.drawio.png ├── package.json └── smithery.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | old 4 | .smithery -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Snaggle.ai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenAPI MCP Server 2 | 3 | [![smithery badge](https://smithery.ai/badge/@janwilmake/openapi-mcp-server)](https://smithery.ai/server/@janwilmake/openapi-mcp-server) [![janwilmake/openapi-mcp-server context](https://badge.forgithub.com/janwilmake/openapi-mcp-server?excludePathPatterns=*.yaml)](https://uithub.com/janwilmake/openapi-mcp-server?excludePathPatterns=*.yaml) 4 | 5 | A Model Context Protocol (MCP) server for Claude/Cursor that enables searching and exploring OpenAPI specifications through oapis.org. 6 | 7 | - Demo: https://x.com/janwilmake/status/1903497808134496583 8 | - HN Thread: https://news.ycombinator.com/item?id=43447278 9 | - OpenAPISearch: https://github.com/janwilmake/openapisearch 10 | - OAPIS: https://github.com/janwilmake/oapis 11 | 12 | The MCP works by applying a 3 step process : 13 | 14 | 1. It figures out the openapi identifier you need 15 | 2. It requests a summary of that in simple language 16 | 3. It determines which endpoints you need, and checks out how exactly they work (again, in simple language) 17 | 18 | ## Features 19 | 20 | - Get an overview of any OpenAPI specification 21 | - Retrieve details about specific API operations 22 | - Support for both JSON and YAML formats 23 | - Tested with Claude Desktop and Cursor 24 | 25 | | Summary | Prompt it | 26 | | --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 27 | | Basic understanding of the OpenAPI MCP Server | [![](https://b.lmpify.com/overview)](https://lmpify.com?q=https%3A%2F%2Fuuithub.com%2Fjanwilmake%2Fopenapi-mcp-server%2Ftree%2Fmain%3FpathPatterns%3DREADME.md%26pathPatterns%3Dopenapi-mcp.drawio.png%0A%0ACan%20you%20explain%20what%20OpenAPI%20MCP%20Server%20does%20and%20how%20I%20can%20use%20it%20with%20Claude%20Desktop%3F) | 28 | | Core implementation details of the MCP server | [![](https://b.lmpify.com/implementation)](https://lmpify.com?q=https%3A%2F%2Fuuithub.com%2Fjanwilmake%2Fopenapi-mcp-server%2Ftree%2Fmain%3FpathPatterns%3Dindex.js%26pathPatterns%3Dpackage.json%0A%0AHow%20does%20the%20OpenAPI%20MCP%20Server%20handle%20API%20requests%3F%20Can%20you%20explain%20the%20tool%20handlers%3F) | 29 | | How to extend or contribute to the project | [![](https://b.lmpify.com/extend)](https://lmpify.com?q=https%3A%2F%2Fuuithub.com%2Fjanwilmake%2Fopenapi-mcp-server%2Ftree%2Fmain%3FpathPatterns%3Dindex.js%26pathPatterns%3Dpackage.json%26pathPatterns%3DREADME.md%0A%0AI'd%20like%20to%20add%20support%20for%20a%20new%20feature%20to%20the%20OpenAPI%20MCP%20Server.%20Where%20should%20I%20start%3F) | 30 | 31 | ## Installation 32 | 33 | ### Installing via Smithery 34 | 35 | Our hosted smithery URL is https://smithery.ai/server/@janwilmake/openapi-mcp-server 36 | 37 | To install openapi-mcp-server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@janwilmake/openapi-mcp-server): 38 | 39 | ```bash 40 | npx -y @smithery/cli install @janwilmake/openapi-mcp-server --client claude 41 | ``` 42 | 43 | For other clients, see the [smithery page](https://smithery.ai/server/@janwilmake/openapi-mcp-server) for instructions. 44 | 45 | ### Installing using stdio 46 | 47 | ``` 48 | { 49 | "mcpServers": { 50 | "openapi-mcp-server": { 51 | "command": "node", 52 | "args": ["/absolute/path/to/openapi-mcp-server/index.js"], 53 | "env": { 54 | "DEBUG": "true" 55 | } 56 | } 57 | } 58 | } 59 | ``` 60 | 61 | ## Usage in Claude 62 | 63 | Once installed, you can ask Claude to: 64 | 65 | - "Find information about the Stripe API" 66 | - "Explain how to use the GitHub API's repository endpoints" 67 | 68 | Claude will use the MCP server to: 69 | 70 | 1. First get an overview of the requested API 71 | 2. Then retrieve specific operation details as needed 72 | 73 | ## Requirements 74 | 75 | - Node.js >= 16.17.0 76 | - Claude Desktop, Cursor, or any other MCP client. 77 | 78 | ## License 79 | 80 | MIT 81 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js"; 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 5 | import { 6 | CallToolRequestSchema, 7 | ListToolsRequestSchema, 8 | } from "@modelcontextprotocol/sdk/types.js"; 9 | import { z } from "zod"; 10 | 11 | /** 12 | * Configuration schema for the OpenAPI MCP Server 13 | */ 14 | export const configSchema = z.object({ 15 | debug: z.boolean().default(false).describe("Enable debug logging"), 16 | }); 17 | 18 | /** 19 | * Utility function for debug logging 20 | * @param {boolean} debug - Whether debug mode is enabled 21 | * @param {...any} args - Arguments to log 22 | */ 23 | function log(debug, ...args) { 24 | if (debug) { 25 | const msg = `[DEBUG ${new Date().toISOString()}] ${args.join(" ")}\n`; 26 | process.stderr.write(msg); 27 | } 28 | } 29 | 30 | /** 31 | * Tool handlers for the MCP server 32 | */ 33 | const HANDLERS = { 34 | /** 35 | * Get an overview of an OpenAPI specification 36 | * @param {Object} request - The tool request 37 | * @param {Object} config - Server configuration 38 | * @returns {Promise} Tool response 39 | */ 40 | getApiOverview: async (request, config) => { 41 | const { id } = request.params.arguments; 42 | 43 | log(config.debug, "Executing getApiOverview for API:", id); 44 | 45 | try { 46 | // Fetch from oapis.org/overview endpoint 47 | const url = `https://oapis.org/overview/${encodeURIComponent(id)}`; 48 | log(config.debug, "API request URL:", url); 49 | 50 | const response = await fetch(url); 51 | if (!response.ok) { 52 | const error = await response.text(); 53 | throw new Error(`API error: ${error}`); 54 | } 55 | 56 | // Get response 57 | let responseContent = await response.text(); 58 | 59 | const tooBig = responseContent.length > 250000; 60 | 61 | if (tooBig) { 62 | throw new Error( 63 | `The OpenAPI specification is too large to process with this MCP. Please try a different OpenAPI.` 64 | ); 65 | } 66 | 67 | return { 68 | content: [{ type: "text", text: responseContent }], 69 | metadata: {}, 70 | }; 71 | } catch (error) { 72 | log(config.debug, "Error handling API overview request:", error); 73 | return { 74 | content: [ 75 | { 76 | type: "text", 77 | text: `Error: ${ 78 | error instanceof Error ? error.message : String(error) 79 | }`, 80 | }, 81 | ], 82 | metadata: {}, 83 | isError: true, 84 | }; 85 | } 86 | }, 87 | 88 | /** 89 | * Get details about a specific API operation 90 | * @param {Object} request - The tool request 91 | * @param {Object} config - Server configuration 92 | * @returns {Promise} Tool response 93 | */ 94 | getApiOperation: async (request, config) => { 95 | const { id, operationIdOrRoute } = request.params.arguments; 96 | 97 | log( 98 | config.debug, 99 | "Executing getApiOperation for API:", 100 | id, 101 | "Operation:", 102 | operationIdOrRoute 103 | ); 104 | 105 | try { 106 | // Fetch from oapis.org/openapi endpoint 107 | const url = `https://oapis.org/openapi/${encodeURIComponent( 108 | id 109 | )}/${operationIdOrRoute}`; 110 | log(config.debug, "API request URL:", url); 111 | 112 | const response = await fetch(url); 113 | if (!response.ok) { 114 | const error = await response.text(); 115 | throw new Error(`API error: ${error}`); 116 | } 117 | 118 | return { 119 | content: [{ type: "text", text: await response.text() }], 120 | metadata: {}, 121 | }; 122 | } catch (error) { 123 | log(config.debug, "Error handling API operation request:", error); 124 | return { 125 | content: [ 126 | { 127 | type: "text", 128 | text: `Error: ${ 129 | error instanceof Error ? error.message : String(error) 130 | }`, 131 | }, 132 | ], 133 | metadata: {}, 134 | isError: true, 135 | }; 136 | } 137 | }, 138 | }; 139 | 140 | /** 141 | * Create and configure the MCP server 142 | * @param {Object} options - Server creation options 143 | * @param {Object} options.config - Server configuration 144 | * @returns {Object} Configured MCP server 145 | */ 146 | export default function createServer({ config }) { 147 | log(config.debug, "Starting OpenAPI MCP server with config:", config); 148 | 149 | const server = new Server( 150 | { name: "openapi-mcp-server", version: "2.1.0" }, 151 | { capabilities: { tools: {} } } 152 | ); 153 | 154 | // Handle list tools request 155 | server.setRequestHandler(ListToolsRequestSchema, async () => { 156 | log(config.debug, "Received list tools request"); 157 | 158 | const openapiIds = await fetch("https://openapisearch.com/").then((res) => 159 | res.text() 160 | ); 161 | 162 | // Define the tool schemas 163 | const GET_API_OVERVIEW_TOOL = { 164 | name: "getApiOverview", 165 | description: `Get an overview of an OpenAPI specification. This should be the first step when working with any API.\n\n${openapiIds}`, 166 | inputSchema: { 167 | type: "object", 168 | properties: { 169 | id: { 170 | type: "string", 171 | description: 172 | "API identifier, can be a known ID from openapisearch.com or a URL leading to a raw OpenAPI file", 173 | }, 174 | }, 175 | required: ["id"], 176 | }, 177 | }; 178 | 179 | const GET_API_OPERATION_TOOL = { 180 | name: "getApiOperation", 181 | description: `Get details about a specific operation from an OpenAPI specification. Use this after getting an overview.\n\n${openapiIds}`, 182 | inputSchema: { 183 | type: "object", 184 | properties: { 185 | id: { 186 | type: "string", 187 | description: 188 | "API identifier, can be a known ID from openapisearch.com or a URL leading to a raw OpenAPI file", 189 | }, 190 | operationIdOrRoute: { 191 | type: "string", 192 | description: "Operation ID or route path to retrieve", 193 | }, 194 | }, 195 | required: ["id", "operationIdOrRoute"], 196 | }, 197 | }; 198 | 199 | return { tools: [GET_API_OVERVIEW_TOOL, GET_API_OPERATION_TOOL] }; 200 | }); 201 | 202 | // Handle tool calls 203 | server.setRequestHandler(CallToolRequestSchema, async (request) => { 204 | const toolName = request.params.name; 205 | log(config.debug, "Received tool call:", toolName); 206 | 207 | try { 208 | const handler = HANDLERS[toolName]; 209 | if (!handler) { 210 | throw new Error(`Unknown tool: ${toolName}`); 211 | } 212 | return await handler(request, config); 213 | } catch (error) { 214 | log(config.debug, "Error handling tool call:", error); 215 | return { 216 | toolResult: { 217 | content: [ 218 | { 219 | type: "text", 220 | text: `Error: ${ 221 | error instanceof Error ? error.message : String(error) 222 | }`, 223 | }, 224 | ], 225 | isError: true, 226 | }, 227 | }; 228 | } 229 | }); 230 | 231 | return server; 232 | } 233 | 234 | /** 235 | * Main function for STDIO compatibility 236 | * Runs the server with STDIO transport when executed directly 237 | */ 238 | async function main() { 239 | // Get debug setting from environment variable 240 | const debug = process.env.DEBUG === "true"; 241 | 242 | // Create server with configuration 243 | const server = createServer({ 244 | config: { debug }, 245 | }); 246 | 247 | const transport = new StdioServerTransport(); 248 | await server.connect(transport); 249 | console.error("OpenAPI MCP Server running in stdio mode"); 250 | } 251 | 252 | // Handle process events 253 | process.on("uncaughtException", (error) => { 254 | console.error("Uncaught exception:", error); 255 | }); 256 | 257 | process.on("unhandledRejection", (error) => { 258 | console.error("Unhandled rejection:", error); 259 | }); 260 | 261 | // Run server when executed directly (for STDIO compatibility) 262 | main().catch((error) => { 263 | console.error("Error starting server:", error); 264 | process.exit(1); 265 | }); 266 | -------------------------------------------------------------------------------- /openapi-mcp.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janwilmake/openapi-mcp-server/df55b320f65269d866a541a9c7a487b8d404ebd1/openapi-mcp.drawio.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openapi-mcp-server", 3 | "keywords": [ 4 | "openapi", 5 | "mcp", 6 | "server", 7 | "proxy" 8 | ], 9 | "version": "2.1.0", 10 | "license": "MIT", 11 | "type": "module", 12 | "module": "index.js", 13 | "bin": { 14 | "openapi-mcp-server": "index.js" 15 | }, 16 | "description": "MCP server for interacting with openapisearch.com API", 17 | "main": "index.js", 18 | "files": [ 19 | "index.js", 20 | "README.md" 21 | ], 22 | "scripts": { 23 | "dev": "node index.js", 24 | "dev-smithery": "npx @smithery/cli dev" 25 | }, 26 | "dependencies": { 27 | "@modelcontextprotocol/sdk": "^1.17.5", 28 | "zod": "^3.25.76" 29 | }, 30 | "engines": { 31 | "node": ">=18.0.0" 32 | }, 33 | "devDependencies": { 34 | "tsx": "^4.20.5" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | runtime: "javascript" 4 | --------------------------------------------------------------------------------