├── .gitignore ├── Dockerfile ├── LICENSE ├── README-v1.md ├── README.md ├── index.js ├── openapi-mcp.drawio.png ├── package.json └── smithery.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | old -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | FROM node:lts-alpine 3 | 4 | WORKDIR /app 5 | 6 | # Copy package.json and package-lock.json if available 7 | COPY package*.json ./ 8 | 9 | # Install dependencies (skip scripts so that postinstall does not mess up file permissions if any) 10 | RUN npm install --ignore-scripts 11 | 12 | # Copy the rest of the project files 13 | COPY . . 14 | 15 | # Make sure the entrypoint is executable 16 | RUN chmod +x index.js 17 | 18 | # Expose necessary ports if any (MCP works on stdio so none required) 19 | 20 | # Run the server 21 | CMD ["node", "index.js", "run"] 22 | -------------------------------------------------------------------------------- /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-v1.md: -------------------------------------------------------------------------------- 1 | # OpenAPI MCP Server 2 | 3 | [![smithery badge](https://smithery.ai/badge/openapi-mcp-server)](https://smithery.ai/server/openapi-mcp-server) 4 | [![coverage](https://img.shields.io/badge/coverage-83%25-yellowgreen.svg)](https://github.com/snaggle-ai/openapi-mcp-server) 5 | 6 | > **Talk to any OpenAPI (v3.1) compliant API through Claude Desktop!** 7 | 8 | This tool creates a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server that acts as a proxy for any API that has an OpenAPI v3.1 specification. This allows you to use Claude Desktop to easily interact with both local and remote server APIs. 9 | 10 | If you're having trouble with Claude crashing or specs not working put them through our [spec cleaner app](https://open-api-spec-cleaner.replit.app/) this tidies up some open api schemas to help them be LLM-readable. 11 | 12 | ## What does it do? 13 | 14 | This proxy automatically converts OpenAPI endpoints into Claude tools, allowing Claude to: 15 | 16 | 1. Discover available API endpoints and understand their purpose 17 | 2. Know what parameters are required and their types 18 | 3. Make API calls on your behalf 19 | 4. Handle the responses appropriately 20 | 21 | For example, if you have a Petstore API with this endpoint: 22 | 23 | ```yaml 24 | /pets/{petId}: 25 | get: 26 | operationId: getPetById 27 | summary: Returns a pet by ID 28 | parameters: 29 | - name: petId 30 | in: path 31 | description: ID of pet to return 32 | required: true 33 | schema: 34 | type: integer 35 | ``` 36 | 37 | Claude will see this as a tool it can use: 38 | 39 | ![Example of Claude seeing the getPetById tool](./examples/petstore_tools_in_claude.png) 40 | 41 | You can then ask Claude natural questions like: 42 | - "Can you fetch the details for pet ID 123?" 43 | - "What's the status of my pet with ID 456?" 44 | 45 | Claude will understand the context and make the appropriate API calls. 46 | 47 | ## File Upload Support 48 | 49 | The proxy supports file uploads for APIs that accept multipart/form-data. When an endpoint accepts file uploads (indicated by `type: string, format: binary` in the OpenAPI spec), you can provide local file paths and the proxy will handle reading and uploading the files. 50 | 51 | ### Example Use Cases 52 | 53 | 1. **Profile Picture Upload** 54 | ```yaml 55 | /users/{userId}/avatar: 56 | post: 57 | summary: Upload a user's profile picture 58 | requestBody: 59 | content: 60 | multipart/form-data: 61 | schema: 62 | type: object 63 | properties: 64 | avatar: 65 | type: string 66 | format: binary 67 | description: Profile picture (JPEG/PNG) 68 | cropInfo: 69 | type: object 70 | properties: 71 | x: { type: number } 72 | y: { type: number } 73 | width: { type: number } 74 | height: { type: number } 75 | ``` 76 | 77 | You can ask Claude: 78 | - "Upload my new profile picture from ~/Pictures/profile.jpg" 79 | - "Update my avatar with ~/Downloads/photo.png and crop it to 200x200" 80 | 81 | 2. **Document Processing** 82 | ```yaml 83 | /documents: 84 | post: 85 | summary: Upload documents for processing 86 | requestBody: 87 | content: 88 | multipart/form-data: 89 | schema: 90 | type: object 91 | properties: 92 | document: 93 | type: string 94 | format: binary 95 | description: PDF or Word document 96 | language: 97 | type: string 98 | enum: [en, es, fr] 99 | description: Document language 100 | processOCR: 101 | type: boolean 102 | description: Whether to extract text using OCR 103 | ``` 104 | 105 | Natural language commands: 106 | - "Process the document at ~/Documents/contract.pdf in English with OCR enabled" 107 | - "Upload ~/Downloads/report.docx and set the language to French" 108 | 109 | 3. **Batch File Upload** 110 | ```yaml 111 | /batch-upload: 112 | post: 113 | summary: Upload multiple files in one request 114 | requestBody: 115 | content: 116 | multipart/form-data: 117 | schema: 118 | type: object 119 | properties: 120 | files: 121 | type: array 122 | items: 123 | type: string 124 | format: binary 125 | tags: 126 | type: array 127 | items: 128 | type: string 129 | ``` 130 | 131 | You can say: 132 | - "Upload these three files: ~/data1.csv, ~/data2.csv, and ~/data3.csv with tags 'monthly-report'" 133 | - "Process the files in ~/exports/ with tags 'raw-data', 'june-2023'" 134 | 135 | ### Important Considerations 136 | 137 | 1. **Security** 138 | - File paths are resolved relative to the current working directory 139 | - Access is restricted to files the user has permission to read 140 | - Sensitive files (like ~/.ssh/id_rsa) require explicit user confirmation 141 | - File contents are only read when making the actual API request 142 | 143 | 2. **Performance** 144 | - Large files are streamed directly from disk to the API 145 | - Memory usage is optimized for large files 146 | - Progress reporting is available for large uploads 147 | 148 | 3. **Limitations** 149 | - Maximum file size is determined by the target API 150 | - Only local files are supported (no remote URLs) 151 | - Some file types may be restricted by the API 152 | 153 | ## Getting Started 154 | 155 | 1. **Configure Claude Desktop:** 156 | Add this to your `claude_desktop_config.json`: 157 | ```json 158 | { 159 | "mcpServers": { 160 | "petstore-api": { 161 | "command": "npx", 162 | "args": ["openapi-mcp-server", "/abs/path/to/petstore-openapi.json"] 163 | } 164 | } 165 | } 166 | ``` 167 | 168 | 2. **Restart Claude Desktop** and start interacting with your API! 169 | 170 | ## Examples 171 | 172 | This repository includes a complete example of a Petstore API server that you can use to test the OpenAPI MCP Server. The example server implements a basic CRUD API for managing pets, making it perfect for learning how to use this tool. 173 | 174 | See [examples/README.md](examples/README.md) for instructions on running the example server. 175 | 176 | ### CLI Tool 177 | The repository includes a command-line tool for testing OpenAPI endpoints: 178 | 179 | ```bash 180 | # List all available methods 181 | pnpm tsx examples/cli/openapi-client.ts http://localhost:3000/openapi.json list 182 | 183 | # Call a specific method 184 | pnpm tsx examples/cli/openapi-client.ts http://localhost:3000/openapi.json call API-getPetById '{"id": 1}' 185 | ``` 186 | 187 | The CLI tool demonstrates how to use both the `OpenAPIToMCPConverter` and `HttpClient` classes to interact with OpenAPI-compliant servers programmatically. 188 | 189 | ## Use Cases 190 | 191 | 1. **Local Development** 192 | - Test your APIs through natural conversation 193 | - Debug endpoints without writing code 194 | - Explore API capabilities interactively 195 | 196 | 2. **API Integration** 197 | - Quickly test third-party APIs 198 | - Prototype integrations before writing code 199 | - Learn new APIs through conversation 200 | 201 | 3. **Documentation** 202 | - Ask questions about API endpoints 203 | - Get examples of how to use endpoints 204 | - Understand error conditions 205 | 206 | ## Limitations 207 | 208 | - Currently supports OpenAPI v3.1 specs only 209 | - Response handling is optimized for JSON/text responses 210 | - File uploads support local files only (no remote URLs) 211 | - Streaming responses not yet implemented 212 | 213 | ## Development 214 | 215 | Outstanding tasks are listed in [TODO.md](TODO.md). 216 | 217 | Basics: 218 | ```bash 219 | # Install dependencies 220 | pnpm install 221 | 222 | # Run tests 223 | pnpm test 224 | 225 | # Build the project 226 | pnpm build 227 | 228 | # Link the project to your global node_modules so that npx works 229 | npm link 230 | 231 | # Now start claude desktop to use 232 | 233 | # After making changes run build again before restarting claude desktop 234 | pnpm build 235 | 236 | # Now restart claude desktop to run with latest changes 237 | ``` 238 | 239 | ## Using OpenAPIToMCPConverter Programmatically 240 | 241 | If you want to convert OpenAPI specs to MCP tools programmatically, you can use the `OpenAPIToMCPConverter` class: 242 | 243 | ```typescript 244 | import { OpenAPIToMCPConverter } from 'openapi-mcp-server' 245 | 246 | // Initialize the converter with your OpenAPI spec 247 | const converter = new OpenAPIToMCPConverter(openApiSpec) 248 | 249 | // Convert to OpenAI tools format 250 | const openAiTools = await converter.convertToOpenAITools() 251 | 252 | // Convert to Anthropic tools format 253 | const anthropicTools = await converter.convertToAnthropicTools() 254 | 255 | // Convert to MCP tools format 256 | const { tools, openApiLookup } = converter.convertToMCPTools() 257 | ``` 258 | 259 | The converter supports multiple tool formats, making it easy to integrate with different LLM providers. The converted tools maintain all the type information and descriptions from your OpenAPI spec, ensuring accurate parameter validation and helpful documentation. 260 | 261 | ## Making API Calls Programmatically 262 | 263 | You can use the `HttpClient` class to make API calls directly without going through an LLM: 264 | 265 | ```typescript 266 | import { HttpClient, OpenAPIToMCPConverter } from 'openapi-mcp-server' 267 | 268 | // Initialize the converter and client 269 | const converter = new OpenAPIToMCPConverter(openApiSpec) 270 | const httpClient = new HttpClient({ baseUrl: 'https://api.example.com' }, openApiSpec) 271 | 272 | // Get operation details from the converter 273 | const { openApiLookup } = converter.convertToMCPTools() 274 | const operation = openApiLookup['API-getPetById'] 275 | 276 | // Make the API call 277 | const response = await httpClient.executeOperation(operation, { 278 | petId: 123 279 | }) 280 | ``` 281 | 282 | The `HttpClient` handles: 283 | - Parameter validation 284 | - URL path parameter substitution 285 | - Query string formatting 286 | - Request body formatting 287 | - File uploads (multipart/form-data) 288 | - Error handling 289 | 290 | 291 | ## :hammer: Tools 292 | 293 | Developed with 294 | 295 | ### [cursor-tools](https://github.com/eastlondoner/cursor-tools) 296 | Cursor-tools are tools that power up AI code generation. Cursor-tools works with any AI agent that can execute commands including Cursor Agent, Cline & Aider. 297 | 298 | :link: [Build with AI: Smarter, faster, and better with **cursor-tools**](https://github.com/eastlondoner/cursor-tools) 299 | 300 | 301 | ## Sponsors 302 | 303 | ### [Vinta.app](https://vinta.app) 304 | **Optimise your Vinted accounting** with real-time analytics, inventory management, and tax compliance tools. 305 | 306 | :link: [Start scaling your Vinted business today](https://vinta.app) 307 | 308 | --- 309 | 310 | ### [Resoled.it](https://resoled.it) 311 | **Automate your Vinted reselling business** with advanced tools like autobuy, custom snipers, and one-click relisting. 312 | 313 | :link: [Take Vinted reselling to the next level](https://resoled.it) 314 | 315 | 316 | ## License 317 | 318 | MIT 319 | 320 | --- 321 | 322 | Built with ❤️ for making APIs more accessible through natural language. 323 | -------------------------------------------------------------------------------- /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=README-v1.md&excludePathPatterns=*.yaml)](https://uithub.com/janwilmake/openapi-mcp-server?excludePathPatterns=README-v1.md&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 | > [!IMPORTANT] 19 | > OpenAPI MCP has found a [new owner](https://github.com/janwilmake) and has been migrated from v1.2 to v2, which works different to the previous version. You can still access any version prior to v2.0.0 and their README is [here](README-v1.md) 20 | > 21 | > OpenAPI MCP v2 is a Work In Progress and focuses on exploration and providing context about APIs. It **does not** allow executing the endpoints as tools directly, as authentication isn't a solved problem with MCP yet. However, it's great for codegen! 22 | > 23 | > Expect bugs. Open To Contributers, [DM](https://x.com/janwilmake) 24 | 25 | ## Features 26 | 27 | - Get an overview of any OpenAPI specification 28 | - Retrieve details about specific API operations 29 | - Support for both JSON and YAML formats 30 | - Tested with Claude Desktop and Cursor 31 | 32 | ## Installation 33 | 34 | ### Installing via Smithery 35 | 36 | To install openapi-mcp-server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@janwilmake/openapi-mcp-server): 37 | 38 | ```bash 39 | npx -y @smithery/cli install @janwilmake/openapi-mcp-server --client claude 40 | ``` 41 | 42 | ### Installing via npx 43 | 44 | Run and follow instructions: 45 | 46 | ```bash 47 | npx openapi-mcp-server@latest init 48 | ``` 49 | 50 | ## Usage in Claude 51 | 52 | Once installed, you can ask Claude to: 53 | 54 | - "Find information about the Stripe API" 55 | - "Explain how to use the GitHub API's repository endpoints" 56 | 57 | Claude will use the MCP server to: 58 | 59 | 1. First get an overview of the requested API 60 | 2. Then retrieve specific operation details as needed 61 | 62 | ## Requirements 63 | 64 | - Node.js >= 16.17.0 65 | - Claude Desktop, Cursor, or any other MCP client. 66 | 67 | ## License 68 | 69 | MIT 70 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import dotenv from "dotenv"; 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 { fetch } from "undici"; 10 | import { exec as execCallback } from "child_process"; 11 | import { promisify } from "util"; 12 | import chalk from "chalk"; 13 | import * as os from "node:os"; 14 | import * as path from "node:path"; 15 | import * as fs from "node:fs"; 16 | import { fileURLToPath } from "url"; 17 | 18 | // Configuration 19 | dotenv.config(); 20 | const __filename = fileURLToPath(import.meta.url); 21 | const __dirname = path.dirname(__filename); 22 | const execAsync = promisify(execCallback); 23 | const version = process.env.npm_package_version || "0.1.0"; 24 | const debug = process.env.DEBUG === "true"; 25 | 26 | // Utility functions 27 | function createDialog(lines) { 28 | const maxLineWidth = Math.max(...lines.map((line) => line.length), 60); 29 | const border = chalk.gray("-".repeat(maxLineWidth)); 30 | return [border, ...lines, border, ""].join("\n"); 31 | } 32 | 33 | function isDirectory(dirPath) { 34 | try { 35 | return fs.statSync(dirPath).isDirectory(); 36 | } catch (error) { 37 | return false; 38 | } 39 | } 40 | 41 | function log(...args) { 42 | if (debug) { 43 | const msg = `[DEBUG ${new Date().toISOString()}] ${args.join(" ")}\n`; 44 | process.stderr.write(msg); 45 | } 46 | } 47 | 48 | async function findNodePath() { 49 | try { 50 | return process.execPath; 51 | } catch (error) { 52 | try { 53 | const cmd = process.platform === "win32" ? "where" : "which"; 54 | const { stdout } = await execAsync(`${cmd} node`); 55 | return stdout.toString().trim().split("\n")[0]; 56 | } catch (err) { 57 | return "node"; // Fallback 58 | } 59 | } 60 | } 61 | 62 | // Tool handlers 63 | const HANDLERS = { 64 | getApiOverview: async (request) => { 65 | const { id } = request.params.arguments; 66 | 67 | log("Executing getApiOverview for API:", id); 68 | 69 | try { 70 | // Fetch from oapis.org/overview endpoint 71 | const url = `https://oapis.org/overview/${encodeURIComponent(id)}`; 72 | log("SLOP API request URL:", url); 73 | 74 | const response = await fetch(url); 75 | if (!response.ok) { 76 | const error = await response.text(); 77 | throw new Error(`SLOP API error: ${error}`); 78 | } 79 | 80 | // Get response 81 | let responseContent = await response.text(); 82 | 83 | const tooBig = responseContent.length > 250000; 84 | 85 | if (tooBig) { 86 | throw new Error( 87 | `The SLOP found is too large to process with this MCP. Please try a different OpenAPI.`, 88 | ); 89 | } 90 | return { 91 | content: [{ type: "text", text: responseContent }], 92 | metadata: {}, 93 | }; 94 | } catch (error) { 95 | log("Error handling SLOP API overview request:", error); 96 | return { 97 | content: [ 98 | { 99 | type: "text", 100 | text: `Error: ${ 101 | error instanceof Error ? error.message : String(error) 102 | }`, 103 | }, 104 | ], 105 | metadata: {}, 106 | isError: true, 107 | }; 108 | } 109 | }, 110 | 111 | getApiOperation: async (request) => { 112 | const { id, operationIdOrRoute } = request.params.arguments; 113 | 114 | log( 115 | "Executing getApiOperation for API:", 116 | id, 117 | "Operation:", 118 | operationIdOrRoute, 119 | ); 120 | 121 | try { 122 | // Fetch from oapis.org/openapi endpoint instead of summary 123 | const url = `https://oapis.org/openapi/${encodeURIComponent( 124 | id, 125 | )}/${operationIdOrRoute}`; 126 | log("SLOP API request URL:", url); 127 | 128 | const response = await fetch(url); 129 | if (!response.ok) { 130 | const error = await response.text(); 131 | throw new Error(`SLOP API error: ${error}`); 132 | } 133 | 134 | return { 135 | content: [{ type: "text", text: await response.text() }], 136 | metadata: {}, 137 | }; 138 | } catch (error) { 139 | log("Error handling SLOP API operation request:", error); 140 | return { 141 | content: [ 142 | { 143 | type: "text", 144 | text: `Error: ${ 145 | error instanceof Error ? error.message : String(error) 146 | }`, 147 | }, 148 | ], 149 | metadata: {}, 150 | isError: true, 151 | }; 152 | } 153 | }, 154 | }; 155 | 156 | // Initialize the SLOP MCP server 157 | export async function init() { 158 | console.log( 159 | createDialog([ 160 | `👋 Welcome to ${chalk.yellow("mcp-server-slop")} v${version}!`, 161 | `💁‍♀️ This ${chalk.green( 162 | "'init'", 163 | )} process will install the SLOP MCP Server into Claude Desktop`, 164 | ` enabling Claude to search and analyze OpenAPI specifications.`, 165 | `🧡 Let's get started.`, 166 | ]), 167 | ); 168 | 169 | console.log(`${chalk.yellow("Step 1:")} Checking for Claude Desktop...`); 170 | 171 | const claudeConfigPath = path.join( 172 | os.homedir(), 173 | "Library", 174 | "Application Support", 175 | "Claude", 176 | "claude_desktop_config.json", 177 | ); 178 | 179 | const nodePath = await findNodePath(); 180 | const config = { 181 | command: nodePath, 182 | args: [__filename, "run"], 183 | }; 184 | 185 | console.log( 186 | `Looking for existing config in: ${chalk.yellow( 187 | path.dirname(claudeConfigPath), 188 | )}`, 189 | ); 190 | const configDirExists = isDirectory(path.dirname(claudeConfigPath)); 191 | 192 | if (configDirExists) { 193 | const existingConfig = fs.existsSync(claudeConfigPath) 194 | ? JSON.parse(fs.readFileSync(claudeConfigPath, "utf8")) 195 | : { mcpServers: {} }; 196 | 197 | if ("slop" in (existingConfig?.mcpServers || {})) { 198 | console.log( 199 | `${chalk.green( 200 | "Note:", 201 | )} Replacing existing SLOP MCP config:\n${chalk.gray( 202 | JSON.stringify(existingConfig.mcpServers.slop), 203 | )}`, 204 | ); 205 | } 206 | 207 | const newConfig = { 208 | ...existingConfig, 209 | mcpServers: { 210 | ...existingConfig.mcpServers, 211 | slop: config, 212 | }, 213 | }; 214 | 215 | fs.writeFileSync(claudeConfigPath, JSON.stringify(newConfig, null, 2)); 216 | 217 | console.log( 218 | `${chalk.yellow( 219 | "mcp-server-slop", 220 | )} configured & added to Claude Desktop!`, 221 | ); 222 | console.log(`Wrote config to ${chalk.yellow(claudeConfigPath)}`); 223 | console.log( 224 | chalk.blue( 225 | `Try asking Claude to "search for an OpenAPI specification" to get started!`, 226 | ), 227 | ); 228 | } else { 229 | const fullConfig = { mcpServers: { slop: config } }; 230 | console.log( 231 | `Couldn't detect Claude Desktop config at ${claudeConfigPath}.\nTo add the SLOP MCP server manually, add the following config to your ${chalk.yellow( 232 | "MCP config-file", 233 | )}:\n\n${JSON.stringify(fullConfig, null, 2)}`, 234 | ); 235 | } 236 | } 237 | 238 | // Start the MCP server 239 | async function main() { 240 | log("Starting SLOP MCP server..."); 241 | 242 | try { 243 | const server = new Server( 244 | { name: "slop", version: "1.0.0" }, 245 | { capabilities: { tools: {} } }, 246 | ); 247 | 248 | // Handle list tools request 249 | server.setRequestHandler(ListToolsRequestSchema, async () => { 250 | log("Received list tools request"); 251 | 252 | const openpaiIds = await fetch("https://openapisearch.com/").then((res) => 253 | res.text(), 254 | ); 255 | 256 | // Define the tool schemas 257 | const GET_API_OVERVIEW_TOOL = { 258 | name: "getApiOverview", 259 | description: `Get an overview of an OpenAPI specification. This should be the first step when working with any API.\n\n${openpaiIds}`, 260 | inputSchema: { 261 | type: "object", 262 | properties: { 263 | id: { 264 | type: "string", 265 | description: 266 | "API identifier, can be a known ID from openapisearch.com or a URL leading to a raw OpenAPI file", 267 | }, 268 | }, 269 | required: ["id"], 270 | }, 271 | }; 272 | 273 | const GET_API_OPERATION_TOOL = { 274 | name: "getApiOperation", 275 | description: `Get details about a specific operation from an OpenAPI specification. Use this after getting an overview.\n\n${openpaiIds}`, 276 | inputSchema: { 277 | type: "object", 278 | properties: { 279 | id: { 280 | type: "string", 281 | description: 282 | "API identifier, can be a known ID from openapisearch.com or a URL leading to a raw OpenAPI file", 283 | }, 284 | operationIdOrRoute: { 285 | type: "string", 286 | description: "Operation ID or route path to retrieve", 287 | }, 288 | }, 289 | required: ["id", "operationIdOrRoute"], 290 | }, 291 | }; 292 | 293 | return { tools: [GET_API_OVERVIEW_TOOL, GET_API_OPERATION_TOOL] }; 294 | }); 295 | 296 | // Handle tool calls 297 | server.setRequestHandler(CallToolRequestSchema, async (request) => { 298 | const toolName = request.params.name; 299 | log("Received tool call:", toolName); 300 | 301 | try { 302 | const handler = HANDLERS[toolName]; 303 | if (!handler) { 304 | throw new Error(`Unknown tool: ${toolName}`); 305 | } 306 | return await handler(request); 307 | } catch (error) { 308 | log("Error handling tool call:", error); 309 | return { 310 | toolResult: { 311 | content: [ 312 | { 313 | type: "text", 314 | text: `Error: ${ 315 | error instanceof Error ? error.message : String(error) 316 | }`, 317 | }, 318 | ], 319 | isError: true, 320 | }, 321 | }; 322 | } 323 | }); 324 | 325 | // Connect to transport 326 | const transport = new StdioServerTransport(); 327 | log("Created transport"); 328 | await server.connect(transport); 329 | log("Server connected and running"); 330 | } catch (error) { 331 | log("Fatal error:", error); 332 | process.exit(1); 333 | } 334 | } 335 | 336 | // Handle process events 337 | process.on("uncaughtException", (error) => { 338 | log("Uncaught exception:", error); 339 | }); 340 | 341 | process.on("unhandledRejection", (error) => { 342 | log("Unhandled rejection:", error); 343 | }); 344 | 345 | // Command line handling 346 | const [cmd, ...args] = process.argv.slice(2); 347 | if (cmd === "init") { 348 | init() 349 | .then(() => { 350 | console.log("Initialization complete!"); 351 | }) 352 | .catch((error) => { 353 | console.error("Error during initialization:", error); 354 | process.exit(1); 355 | }); 356 | } else if (cmd === "run") { 357 | main().catch((error) => { 358 | console.error("Error starting server:", error); 359 | process.exit(1); 360 | }); 361 | } else { 362 | console.error(`Unknown command: ${cmd}. Expected 'init' or 'run'.`); 363 | process.exit(1); 364 | } 365 | -------------------------------------------------------------------------------- /openapi-mcp.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janwilmake/openapi-mcp-server/885750cad29335e29d2b697524befc2ba5d4bc37/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.0.3", 10 | "license": "MIT", 11 | "type": "module", 12 | "bin": { 13 | "slop-mcp": "index.js", 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 | "postinstall": "chmod +x index.js" 24 | }, 25 | "dependencies": { 26 | "@modelcontextprotocol/sdk": "^0.6.0", 27 | "chalk": "^5.3.0", 28 | "dotenv": "^16.4.5", 29 | "undici": "^5.28.4" 30 | }, 31 | "engines": { 32 | "node": ">=16.17.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | {} 8 | commandFunction: 9 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio. 10 | |- 11 | (config) => ({ command: 'node', args: ['index.js', 'run'] }) 12 | exampleConfig: {} 13 | --------------------------------------------------------------------------------