#
tokens: 10136/50000 6/6 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── example-usage.mjs
├── index.mjs
├── package-lock.json
├── package.json
├── README.md
└── sample-petstore.yaml
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
1 | node_modules
2 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # OpenAPI Schema Model Context Protocol Server
  2 | 
  3 | A Model Context Protocol (MCP) server that exposes OpenAPI schema information to Large Language Models (LLMs) like Claude. This server allows an LLM to explore and understand OpenAPI specifications through a set of specialized tools.
  4 | 
  5 | ## Features
  6 | 
  7 | - Load any OpenAPI schema file (JSON or YAML) specified via command line argument
  8 | - Explore API paths, operations, parameters, and schemas
  9 | - View detailed request and response schemas
 10 | - Look up component definitions and examples
 11 | - Search across the entire API specification
 12 | - Get responses in YAML format for better LLM comprehension
 13 | 
 14 | ## Usage
 15 | 
 16 | ### Command Line
 17 | 
 18 | Run the MCP server with a specific schema file:
 19 | 
 20 | ```bash
 21 | # Use the default openapi.yaml in current directory
 22 | npx -y mcp-openapi-schema
 23 | 
 24 | # Use a specific schema file (relative path)
 25 | npx -y mcp-openapi-schema ../petstore.json
 26 | 
 27 | # Use a specific schema file (absolute path)
 28 | npx -y mcp-openapi-schema /absolute/path/to/api-spec.yaml
 29 | 
 30 | # Show help
 31 | npx -y mcp-openapi-schema --help
 32 | ```
 33 | 
 34 | ### Claude Desktop Integration
 35 | 
 36 | To use this MCP server with Claude Desktop, edit your `claude_desktop_config.json` configuration file:
 37 | 
 38 | ```json
 39 | {
 40 |   "mcpServers": {
 41 |     "OpenAPI Schema": {
 42 |       "command": "npx",
 43 |       "args": ["-y", "mcp-openapi-schema", "/ABSOLUTE/PATH/TO/openapi.yaml"]
 44 |     }
 45 |   }
 46 | }
 47 | ```
 48 | 
 49 | Location of the configuration file:
 50 | 
 51 | - macOS/Linux: `~/Library/Application Support/Claude/claude_desktop_config.json`
 52 | - Windows: `$env:AppData\Claude\claude_desktop_config.json`
 53 | 
 54 | ### Claude Code Integration
 55 | 
 56 | To use this MCP server with Claude Code CLI, follow these steps:
 57 | 
 58 | 1. **Add the OpenAPI Schema MCP server to Claude Code**
 59 | 
 60 |    ```bash
 61 |    # Basic syntax
 62 |    claude mcp add openapi-schema npx -y mcp-openapi-schema
 63 | 
 64 |    # Example with specific schema
 65 |    claude mcp add petstore-api npx -y mcp-openapi-schema ~/Projects/petstore.yaml
 66 |    ```
 67 | 
 68 | 2. **Verify the MCP server is registered**
 69 | 
 70 |    ```bash
 71 |    # List all configured servers
 72 |    claude mcp list
 73 | 
 74 |    # Get details for your OpenAPI schema server
 75 |    claude mcp get openapi-schema
 76 |    ```
 77 | 
 78 | 3. **Remove the server if needed**
 79 | 
 80 |    ```bash
 81 |    claude mcp remove openapi-schema
 82 |    ```
 83 | 
 84 | 4. **Use the tool in Claude Code**
 85 | 
 86 |    Once configured, you can invoke the tool in your Claude Code session by asking questions about the OpenAPI schema.
 87 | 
 88 | **Tips:**
 89 | 
 90 | - Use the `-s` or `--scope` flag with `project` (default) or `global` to specify where the configuration is stored
 91 | - Add multiple MCP servers for different APIs with different names
 92 | 
 93 | ## MCP Tools
 94 | 
 95 | The server provides the following tools for LLMs to interact with OpenAPI schemas:
 96 | 
 97 | - `list-endpoints`: Lists all API paths and their HTTP methods with summaries in a nested object structure
 98 | - `get-endpoint`: Gets detailed information about a specific endpoint including parameters and responses
 99 | - `get-request-body`: Gets the request body schema for a specific endpoint and method
100 | - `get-response-schema`: Gets the response schema for a specific endpoint, method, and status code
101 | - `get-path-parameters`: Gets the parameters for a specific path
102 | - `list-components`: Lists all schema components (schemas, responses, parameters, etc.)
103 | - `get-component`: Gets detailed definition for a specific component
104 | - `list-security-schemes`: Lists all available security schemes
105 | - `get-examples`: Gets examples for a specific component or endpoint
106 | - `search-schema`: Searches across paths, operations, and schemas
107 | 
108 | ## Examples
109 | 
110 | Example queries to try:
111 | 
112 | ```
113 | What endpoints are available in this API?
114 | Show me the details for the POST /pets endpoint.
115 | What parameters does the GET /pets/{petId} endpoint take?
116 | What is the request body schema for creating a new pet?
117 | What response will I get from the DELETE /pets/{petId} endpoint?
118 | What schemas are defined in this API?
119 | Show me the definition of the Pet schema.
120 | What are the available security schemes for this API?
121 | Are there any example responses for getting a pet by ID?
122 | Search for anything related to "user" in this API.
123 | ```
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-openapi-schema",
 3 |   "version": "0.0.1",
 4 |   "description": "A Model Context Protocol server that exposes OpenAPI schema information to Large Language Models",
 5 |   "type": "module",
 6 |   "main": "index.mjs",
 7 |   "bin": {
 8 |     "mcp-openapi-schema": "index.mjs"
 9 |   },
10 |   "scripts": {
11 |     "start": "node index.mjs",
12 |     "test": "node example-usage.mjs"
13 |   },
14 |   "keywords": [
15 |     "openapi",
16 |     "swagger",
17 |     "mcp",
18 |     "llm",
19 |     "claude"
20 |   ],
21 |   "author": "Hannes Junnila",
22 |   "license": "MIT",
23 |   "dependencies": {
24 |     "@apidevtools/swagger-parser": "^10.1.1",
25 |     "@modelcontextprotocol/sdk": "^1.7.0",
26 |     "js-yaml": "^4.1.0",
27 |     "zod": "^3.24.2"
28 |   }
29 | }
30 | 
```

--------------------------------------------------------------------------------
/example-usage.mjs:
--------------------------------------------------------------------------------

```
  1 | // This is a simple example of how to test the MCP OpenAPI Schema server
  2 | // using the official MCP SDK client
  3 | import { Client } from "@modelcontextprotocol/sdk/client/index.js";
  4 | import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
  5 | import { dirname, resolve } from "path";
  6 | import { fileURLToPath } from "url";
  7 | 
  8 | const __dirname = dirname(fileURLToPath(import.meta.url));
  9 | 
 10 | // Set up the MCP client to communicate with our server
 11 | const transport = new StdioClientTransport({
 12 |   command: "node",
 13 |   args: [
 14 |     resolve(__dirname, "index.mjs"),
 15 |     resolve(__dirname, "./sample-petstore.yaml")
 16 |   ],
 17 | });
 18 | 
 19 | const client = new Client({
 20 |   name: "openapi-schema-client",
 21 |   version: "1.0.0",
 22 | });
 23 | 
 24 | // Connect to the server
 25 | console.log("Connecting to MCP server...");
 26 | await client.connect(transport);
 27 | console.log("Connected to MCP server successfully!");
 28 | 
 29 | // Run example tool calls
 30 | try {
 31 |   // List endpoints
 32 |   console.log("\n--- LISTING ENDPOINTS ---");
 33 |   const endpoints = await client.callTool({
 34 |     name: "list-endpoints",
 35 |     arguments: {},
 36 |   });
 37 |   console.log(endpoints.content[0].text);
 38 | 
 39 |   // Get endpoint details
 40 |   console.log("\n--- GET ENDPOINT DETAILS ---");
 41 |   const endpointDetails = await client.callTool({
 42 |     name: "get-endpoint",
 43 |     arguments: {
 44 |       path: "/pets",
 45 |       method: "get",
 46 |     },
 47 |   });
 48 |   console.log(endpointDetails.content[0].text);
 49 | 
 50 |   // Get request body schema
 51 |   console.log("\n--- GET REQUEST BODY SCHEMA ---");
 52 |   const requestBody = await client.callTool({
 53 |     name: "get-request-body",
 54 |     arguments: {
 55 |       path: "/pets",
 56 |       method: "post",
 57 |     },
 58 |   });
 59 |   console.log(requestBody.content[0].text);
 60 | 
 61 |   // List components
 62 |   console.log("\n--- LIST COMPONENTS ---");
 63 |   const components = await client.callTool({
 64 |     name: "list-components",
 65 |     arguments: {},
 66 |   });
 67 |   console.log(components.content[0].text);
 68 | 
 69 |   // Get component schema
 70 |   console.log("\n--- GET COMPONENT SCHEMA ---");
 71 |   const component = await client.callTool({
 72 |     name: "get-component",
 73 |     arguments: {
 74 |       type: "schemas",
 75 |       name: "Pet",
 76 |     },
 77 |   });
 78 |   console.log(component.content[0].text);
 79 | 
 80 |   // Search schema
 81 |   console.log("\n--- SEARCH SCHEMA ---");
 82 |   const searchResults = await client.callTool({
 83 |     name: "search-schema",
 84 |     arguments: {
 85 |       pattern: "pet",
 86 |     },
 87 |   });
 88 |   console.log(searchResults.content[0].text);
 89 | 
 90 |   // Get path parameters
 91 |   console.log("\n--- GET PATH PARAMETERS ---");
 92 |   const parameters = await client.callTool({
 93 |     name: "get-path-parameters",
 94 |     arguments: {
 95 |       path: "/pets/{petId}",
 96 |       method: "get",
 97 |     },
 98 |   });
 99 |   console.log(parameters.content[0].text);
100 | 
101 |   // Get response schema
102 |   console.log("\n--- GET RESPONSE SCHEMA ---");
103 |   const response = await client.callTool({
104 |     name: "get-response-schema",
105 |     arguments: {
106 |       path: "/pets/{petId}",
107 |       method: "get",
108 |       statusCode: "200",
109 |     },
110 |   });
111 |   console.log(response.content[0].text);
112 | 
113 |   // List security schemes
114 |   console.log("\n--- LIST SECURITY SCHEMES ---");
115 |   const security = await client.callTool({
116 |     name: "list-security-schemes",
117 |     arguments: {},
118 |   });
119 |   console.log(security.content[0].text);
120 | } catch (error) {
121 |   console.error("Error during testing:", error);
122 | } finally {
123 |   // Close the connection
124 |   await client.close();
125 |   console.log("\nTests completed, disconnected from server.");
126 | }
127 | 
```

--------------------------------------------------------------------------------
/sample-petstore.yaml:
--------------------------------------------------------------------------------

```yaml
  1 | openapi: 3.0.0
  2 | info:
  3 |   title: Petstore API
  4 |   version: 1.0.0
  5 |   description: A sample API for managing pets
  6 | servers:
  7 |   - url: https://petstore.example.com/api/v1
  8 | paths:
  9 |   /pets:
 10 |     get:
 11 |       summary: List all pets
 12 |       description: Returns all pets from the system
 13 |       operationId: listPets
 14 |       tags:
 15 |         - pets
 16 |       parameters:
 17 |         - name: limit
 18 |           in: query
 19 |           description: Maximum number of pets to return
 20 |           required: false
 21 |           schema:
 22 |             type: integer
 23 |             format: int32
 24 |             minimum: 1
 25 |             maximum: 100
 26 |             default: 20
 27 |       responses:
 28 |         '200':
 29 |           description: A paged array of pets
 30 |           content:
 31 |             application/json:
 32 |               schema:
 33 |                 $ref: '#/components/schemas/PetsResponse'
 34 |               example:
 35 |                 pets:
 36 |                   - id: 1
 37 |                     name: "Fluffy"
 38 |                     status: "available"
 39 |                     category: "cat"
 40 |                   - id: 2
 41 |                     name: "Rex"
 42 |                     status: "pending"
 43 |                     category: "dog"
 44 |                 total: 2
 45 |                 limit: 20
 46 |                 offset: 0
 47 |     post:
 48 |       summary: Create a pet
 49 |       description: Creates a new pet in the store
 50 |       operationId: createPet
 51 |       tags:
 52 |         - pets
 53 |       requestBody:
 54 |         description: Pet to add to the store
 55 |         required: true
 56 |         content:
 57 |           application/json:
 58 |             schema:
 59 |               $ref: '#/components/schemas/NewPet'
 60 |             example:
 61 |               name: "Fluffy"
 62 |               category: "cat"
 63 |       responses:
 64 |         '201':
 65 |           description: Pet created
 66 |           content:
 67 |             application/json:
 68 |               schema:
 69 |                 $ref: '#/components/schemas/Pet'
 70 |               example:
 71 |                 id: 1
 72 |                 name: "Fluffy"
 73 |                 status: "available"
 74 |                 category: "cat"
 75 |         '400':
 76 |           description: Invalid input
 77 |           content:
 78 |             application/json:
 79 |               schema:
 80 |                 $ref: '#/components/schemas/Error'
 81 |               example:
 82 |                 code: 400
 83 |                 message: "Invalid input: name is required"
 84 |   /pets/{petId}:
 85 |     get:
 86 |       summary: Get a pet by ID
 87 |       description: Returns a single pet by ID
 88 |       operationId: getPetById
 89 |       tags:
 90 |         - pets
 91 |       parameters:
 92 |         - name: petId
 93 |           in: path
 94 |           description: ID of pet to return
 95 |           required: true
 96 |           schema:
 97 |             type: integer
 98 |             format: int64
 99 |       responses:
100 |         '200':
101 |           description: Expected response to a valid request
102 |           content:
103 |             application/json:
104 |               schema:
105 |                 $ref: '#/components/schemas/Pet'
106 |               example:
107 |                 id: 1
108 |                 name: "Fluffy"
109 |                 status: "available"
110 |                 category: "cat"
111 |         '404':
112 |           description: Pet not found
113 |           content:
114 |             application/json:
115 |               schema:
116 |                 $ref: '#/components/schemas/Error'
117 |               example:
118 |                 code: 404
119 |                 message: "Pet not found"
120 |     delete:
121 |       summary: Delete a pet
122 |       description: Deletes a pet by ID
123 |       operationId: deletePet
124 |       tags:
125 |         - pets
126 |       parameters:
127 |         - name: petId
128 |           in: path
129 |           description: ID of pet to delete
130 |           required: true
131 |           schema:
132 |             type: integer
133 |             format: int64
134 |       responses:
135 |         '204':
136 |           description: Pet deleted
137 |         '404':
138 |           description: Pet not found
139 |           content:
140 |             application/json:
141 |               schema:
142 |                 $ref: '#/components/schemas/Error'
143 | components:
144 |   schemas:
145 |     Pet:
146 |       type: object
147 |       required:
148 |         - id
149 |         - name
150 |       properties:
151 |         id:
152 |           type: integer
153 |           format: int64
154 |           description: Unique identifier for the pet
155 |         name:
156 |           type: string
157 |           description: Name of the pet
158 |         status:
159 |           type: string
160 |           description: Status of the pet
161 |           enum:
162 |             - available
163 |             - pending
164 |             - sold
165 |           default: available
166 |         category:
167 |           type: string
168 |           description: Category of the pet
169 |     NewPet:
170 |       type: object
171 |       required:
172 |         - name
173 |       properties:
174 |         name:
175 |           type: string
176 |           description: Name of the pet
177 |         category:
178 |           type: string
179 |           description: Category of the pet
180 |     PetsResponse:
181 |       type: object
182 |       required:
183 |         - pets
184 |         - total
185 |       properties:
186 |         pets:
187 |           type: array
188 |           items:
189 |             $ref: '#/components/schemas/Pet'
190 |         total:
191 |           type: integer
192 |           description: Total number of pets
193 |         limit:
194 |           type: integer
195 |           description: Limit used in the request
196 |         offset:
197 |           type: integer
198 |           description: Offset used in the request
199 |     Error:
200 |       type: object
201 |       required:
202 |         - code
203 |         - message
204 |       properties:
205 |         code:
206 |           type: integer
207 |           format: int32
208 |           description: Error code
209 |         message:
210 |           type: string
211 |           description: Error message
212 |   securitySchemes:
213 |     ApiKeyAuth:
214 |       type: apiKey
215 |       in: header
216 |       name: X-API-Key
217 |       description: API key for authorization
218 | security:
219 |   - ApiKeyAuth: []
```

--------------------------------------------------------------------------------
/index.mjs:
--------------------------------------------------------------------------------

```
  1 | #!/usr/bin/env node
  2 | import SwaggerParser from "@apidevtools/swagger-parser";
  3 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import yaml from "js-yaml";
  6 | import { Console } from "node:console";
  7 | import { readFile } from "node:fs/promises";
  8 | import { resolve } from "node:path";
  9 | import { z } from "zod";
 10 | 
 11 | // Redirect console output to stderr to avoid interfering with MCP comms
 12 | globalThis.console = new Console(process.stderr);
 13 | 
 14 | const args = process.argv.slice(2);
 15 | 
 16 | if (args.includes("--help") || args.includes("-h")) {
 17 |   console.log(`
 18 | OpenAPI Schema Model Context Protocol Server
 19 | 
 20 | Usage: 
 21 |   node index.mjs [path/to/openapi.yaml]
 22 | 
 23 | Arguments:
 24 |   path/to/openapi.yaml  Path to the OpenAPI schema file (JSON or YAML) (optional)
 25 |                        If not provided, defaults to openapi.yaml
 26 | 
 27 | Examples:
 28 |   node index.mjs # Uses default openapi.yaml
 29 |   node index.mjs ../petstore.json # Uses petstore OpenAPI spec
 30 |   node index.mjs /absolute/path/to/api-schema.yaml
 31 |   `);
 32 |   process.exit(0);
 33 | }
 34 | 
 35 | const schemaArg = args[0];
 36 | 
 37 | const loadSchema = async () => {
 38 |   // Default to openapi.yaml if no argument provided
 39 |   const schemaPath = resolve(schemaArg ?? "openapi.yaml");
 40 | 
 41 |   try {
 42 |     // Parse and validate the OpenAPI document
 43 |     return await SwaggerParser.validate(schemaPath, { validate: { schema: false } });
 44 |   } catch (error) {
 45 |     console.error(`Error loading schema: ${error.message}`);
 46 |     process.exit(1);
 47 |   }
 48 | };
 49 | 
 50 | const openApiDoc = await loadSchema();
 51 | 
 52 | // Extract schema name from file path or from the OpenAPI info
 53 | const schemaName =
 54 |   openApiDoc.info?.title ||
 55 |   (schemaArg
 56 |     ? schemaArg
 57 |         .split("/")
 58 |         .pop()
 59 |         .replace(/\.(yaml|json)$/i, "")
 60 |     : "openapi");
 61 | 
 62 | const server = new McpServer({
 63 |   name: `OpenAPI Schema: ${schemaName}`,
 64 |   version: "1.0.0",
 65 |   description: `Provides OpenAPI schema information for ${schemaName} (${openApiDoc.info?.version || "unknown version"})`,
 66 | });
 67 | 
 68 | // Helper to convert objects to YAML for better readability
 69 | const toYaml = (obj) => yaml.dump(obj, { lineWidth: 100, noRefs: true });
 70 | 
 71 | // List all API paths and operations
 72 | server.tool(
 73 |   "list-endpoints",
 74 |   "Lists all API paths and their HTTP methods with summaries, organized by path",
 75 |   () => {
 76 |     const pathMap = {};
 77 | 
 78 |     for (const [path, pathItem] of Object.entries(openApiDoc.paths || {})) {
 79 |       // Get all HTTP methods for this path
 80 |       const methods = Object.keys(pathItem).filter((key) =>
 81 |         ["get", "post", "put", "delete", "patch", "options", "head"].includes(key.toLowerCase()),
 82 |       );
 83 | 
 84 |       // Create a methods object for this path
 85 |       pathMap[path] = {};
 86 | 
 87 |       // Add each method with its summary
 88 |       for (const method of methods) {
 89 |         const operation = pathItem[method];
 90 |         pathMap[path][method.toUpperCase()] = operation.summary || "No summary";
 91 |       }
 92 |     }
 93 | 
 94 |     return {
 95 |       content: [
 96 |         {
 97 |           type: "text",
 98 |           text: toYaml(pathMap),
 99 |         },
100 |       ],
101 |     };
102 |   },
103 | );
104 | 
105 | // Get details for a specific endpoint
106 | server.tool(
107 |   "get-endpoint",
108 |   "Gets detailed information about a specific API endpoint",
109 |   { path: z.string(), method: z.string() },
110 |   ({ path, method }) => {
111 |     const pathItem = openApiDoc.paths?.[path];
112 |     if (!pathItem) {
113 |       return { content: [{ type: "text", text: `Path ${path} not found` }] };
114 |     }
115 | 
116 |     const operation = pathItem[method.toLowerCase()];
117 |     if (!operation) {
118 |       return { content: [{ type: "text", text: `Method ${method} not found for path ${path}` }] };
119 |     }
120 | 
121 |     // Extract relevant information
122 |     const endpoint = {
123 |       path,
124 |       method: method.toUpperCase(),
125 |       summary: operation.summary,
126 |       description: operation.description,
127 |       tags: operation.tags,
128 |       parameters: operation.parameters,
129 |       requestBody: operation.requestBody,
130 |       responses: operation.responses,
131 |       security: operation.security,
132 |       deprecated: operation.deprecated,
133 |     };
134 | 
135 |     return {
136 |       content: [
137 |         {
138 |           type: "text",
139 |           text: toYaml(endpoint),
140 |         },
141 |       ],
142 |     };
143 |   },
144 | );
145 | 
146 | // Get request body schema for a specific endpoint
147 | server.tool(
148 |   "get-request-body",
149 |   "Gets the request body schema for a specific endpoint",
150 |   { path: z.string(), method: z.string() },
151 |   ({ path, method }) => {
152 |     const pathItem = openApiDoc.paths?.[path];
153 |     if (!pathItem) {
154 |       return { content: [{ type: "text", text: `Path ${path} not found` }] };
155 |     }
156 | 
157 |     const operation = pathItem[method.toLowerCase()];
158 |     if (!operation) {
159 |       return { content: [{ type: "text", text: `Method ${method} not found for path ${path}` }] };
160 |     }
161 | 
162 |     const requestBody = operation.requestBody;
163 |     if (!requestBody) {
164 |       return { content: [{ type: "text", text: `No request body defined for ${method} ${path}` }] };
165 |     }
166 | 
167 |     return {
168 |       content: [
169 |         {
170 |           type: "text",
171 |           text: toYaml(requestBody),
172 |         },
173 |       ],
174 |     };
175 |   },
176 | );
177 | 
178 | // Get response schema for a specific endpoint and status code
179 | server.tool(
180 |   "get-response-schema",
181 |   "Gets the response schema for a specific endpoint, method, and status code",
182 |   {
183 |     path: z.string(),
184 |     method: z.string(),
185 |     statusCode: z.string().default("200"),
186 |   },
187 |   ({ path, method, statusCode }) => {
188 |     const pathItem = openApiDoc.paths?.[path];
189 |     if (!pathItem) {
190 |       return { content: [{ type: "text", text: `Path ${path} not found` }] };
191 |     }
192 | 
193 |     const operation = pathItem[method.toLowerCase()];
194 |     if (!operation) {
195 |       return { content: [{ type: "text", text: `Method ${method} not found for path ${path}` }] };
196 |     }
197 | 
198 |     const responses = operation.responses;
199 |     if (!responses) {
200 |       return { content: [{ type: "text", text: `No responses defined for ${method} ${path}` }] };
201 |     }
202 | 
203 |     const response = responses[statusCode] || responses.default;
204 |     if (!response) {
205 |       return {
206 |         content: [
207 |           {
208 |             type: "text",
209 |             text: `No response for status code ${statusCode} (or default) found for ${method} ${path}.\nAvailable status codes: ${Object.keys(responses).join(", ")}`,
210 |           },
211 |         ],
212 |       };
213 |     }
214 | 
215 |     return {
216 |       content: [
217 |         {
218 |           type: "text",
219 |           text: toYaml(response),
220 |         },
221 |       ],
222 |     };
223 |   },
224 | );
225 | 
226 | // Get parameters for a specific path
227 | server.tool(
228 |   "get-path-parameters",
229 |   "Gets the parameters for a specific path",
230 |   { path: z.string(), method: z.string().optional() },
231 |   ({ path, method }) => {
232 |     const pathItem = openApiDoc.paths?.[path];
233 |     if (!pathItem) {
234 |       return { content: [{ type: "text", text: `Path ${path} not found` }] };
235 |     }
236 | 
237 |     let parameters = [...(pathItem.parameters || [])];
238 | 
239 |     // If method is specified, add method-specific parameters
240 |     if (method) {
241 |       const operation = pathItem[method.toLowerCase()];
242 |       if (operation && operation.parameters) {
243 |         parameters = [...parameters, ...operation.parameters];
244 |       }
245 |     }
246 | 
247 |     if (parameters.length === 0) {
248 |       return {
249 |         content: [
250 |           {
251 |             type: "text",
252 |             text: `No parameters found for ${method ? `${method.toUpperCase()} ` : ""}${path}`,
253 |           },
254 |         ],
255 |       };
256 |     }
257 | 
258 |     return {
259 |       content: [
260 |         {
261 |           type: "text",
262 |           text: toYaml(parameters),
263 |         },
264 |       ],
265 |     };
266 |   },
267 | );
268 | 
269 | // List all components
270 | server.tool(
271 |   "list-components",
272 |   "Lists all schema components (schemas, parameters, responses, etc.)",
273 |   () => {
274 |     const components = openApiDoc.components || {};
275 |     const result = {};
276 | 
277 |     // For each component type, list the keys
278 |     for (const [type, items] of Object.entries(components)) {
279 |       if (items && typeof items === "object") {
280 |         result[type] = Object.keys(items);
281 |       }
282 |     }
283 | 
284 |     return {
285 |       content: [
286 |         {
287 |           type: "text",
288 |           text: toYaml(result),
289 |         },
290 |       ],
291 |     };
292 |   },
293 | );
294 | 
295 | // Get a specific component
296 | server.tool(
297 |   "get-component",
298 |   "Gets detailed definition for a specific component",
299 |   {
300 |     type: z.string().describe("Component type (e.g., schemas, parameters, responses)"),
301 |     name: z.string().describe("Component name"),
302 |   },
303 |   ({ type, name }) => {
304 |     const components = openApiDoc.components || {};
305 |     const componentType = components[type];
306 | 
307 |     if (!componentType) {
308 |       return {
309 |         content: [
310 |           {
311 |             type: "text",
312 |             text: `Component type '${type}' not found. Available types: ${Object.keys(components).join(", ")}`,
313 |           },
314 |         ],
315 |       };
316 |     }
317 | 
318 |     const component = componentType[name];
319 |     if (!component) {
320 |       return {
321 |         content: [
322 |           {
323 |             type: "text",
324 |             text: `Component '${name}' not found in '${type}'. Available components: ${Object.keys(componentType).join(", ")}`,
325 |           },
326 |         ],
327 |       };
328 |     }
329 | 
330 |     return {
331 |       content: [
332 |         {
333 |           type: "text",
334 |           text: toYaml(component),
335 |         },
336 |       ],
337 |     };
338 |   },
339 | );
340 | 
341 | // List security schemes
342 | server.tool("list-security-schemes", "Lists all available security schemes", () => {
343 |   const securitySchemes = openApiDoc.components?.securitySchemes || {};
344 |   const result = {};
345 | 
346 |   for (const [name, scheme] of Object.entries(securitySchemes)) {
347 |     result[name] = {
348 |       type: scheme.type,
349 |       description: scheme.description,
350 |       ...(scheme.type === "oauth2" ? { flows: Object.keys(scheme.flows || {}) } : {}),
351 |       ...(scheme.type === "apiKey" ? { in: scheme.in, name: scheme.name } : {}),
352 |       ...(scheme.type === "http" ? { scheme: scheme.scheme } : {}),
353 |     };
354 |   }
355 | 
356 |   if (Object.keys(result).length === 0) {
357 |     return { content: [{ type: "text", text: "No security schemes defined in this API" }] };
358 |   }
359 | 
360 |   return {
361 |     content: [
362 |       {
363 |         type: "text",
364 |         text: toYaml(result),
365 |       },
366 |     ],
367 |   };
368 | });
369 | 
370 | // Get examples
371 | server.tool(
372 |   "get-examples",
373 |   "Gets examples for a specific component or endpoint",
374 |   {
375 |     type: z.enum(["request", "response", "component"]).describe("Type of example to retrieve"),
376 |     path: z.string().optional().describe("API path (required for request/response examples)"),
377 |     method: z.string().optional().describe("HTTP method (required for request/response examples)"),
378 |     statusCode: z.string().optional().describe("Status code (for response examples)"),
379 |     componentType: z
380 |       .string()
381 |       .optional()
382 |       .describe("Component type (required for component examples)"),
383 |     componentName: z
384 |       .string()
385 |       .optional()
386 |       .describe("Component name (required for component examples)"),
387 |   },
388 |   ({ type, path, method, statusCode, componentType, componentName }) => {
389 |     if (type === "request") {
390 |       if (!path || !method) {
391 |         return {
392 |           content: [{ type: "text", text: "Path and method are required for request examples" }],
393 |         };
394 |       }
395 | 
396 |       const operation = openApiDoc.paths?.[path]?.[method.toLowerCase()];
397 |       if (!operation) {
398 |         return {
399 |           content: [{ type: "text", text: `Operation ${method.toUpperCase()} ${path} not found` }],
400 |         };
401 |       }
402 | 
403 |       if (!operation.requestBody?.content) {
404 |         return {
405 |           content: [
406 |             { type: "text", text: `No request body defined for ${method.toUpperCase()} ${path}` },
407 |           ],
408 |         };
409 |       }
410 | 
411 |       const examples = {};
412 |       for (const [contentType, content] of Object.entries(operation.requestBody.content)) {
413 |         if (content.examples) {
414 |           examples[contentType] = content.examples;
415 |         } else if (content.example) {
416 |           examples[contentType] = { default: { value: content.example } };
417 |         }
418 |       }
419 | 
420 |       if (Object.keys(examples).length === 0) {
421 |         return {
422 |           content: [
423 |             { type: "text", text: `No examples found for ${method.toUpperCase()} ${path} request` },
424 |           ],
425 |         };
426 |       }
427 | 
428 |       return {
429 |         content: [
430 |           {
431 |             type: "text",
432 |             text: toYaml(examples),
433 |           },
434 |         ],
435 |       };
436 |     } else if (type === "response") {
437 |       if (!path || !method) {
438 |         return {
439 |           content: [{ type: "text", text: "Path and method are required for response examples" }],
440 |         };
441 |       }
442 | 
443 |       const operation = openApiDoc.paths?.[path]?.[method.toLowerCase()];
444 |       if (!operation) {
445 |         return {
446 |           content: [{ type: "text", text: `Operation ${method.toUpperCase()} ${path} not found` }],
447 |         };
448 |       }
449 | 
450 |       if (!operation.responses) {
451 |         return {
452 |           content: [
453 |             { type: "text", text: `No responses defined for ${method.toUpperCase()} ${path}` },
454 |           ],
455 |         };
456 |       }
457 | 
458 |       const responseObj = statusCode
459 |         ? operation.responses[statusCode]
460 |         : Object.values(operation.responses)[0];
461 |       if (!responseObj) {
462 |         return {
463 |           content: [
464 |             {
465 |               type: "text",
466 |               text: `Response ${statusCode} not found for ${method.toUpperCase()} ${path}. Available: ${Object.keys(operation.responses).join(", ")}`,
467 |             },
468 |           ],
469 |         };
470 |       }
471 | 
472 |       if (!responseObj.content) {
473 |         return { content: [{ type: "text", text: `No content defined in response` }] };
474 |       }
475 | 
476 |       const examples = {};
477 |       for (const [contentType, content] of Object.entries(responseObj.content)) {
478 |         if (content.examples) {
479 |           examples[contentType] = content.examples;
480 |         } else if (content.example) {
481 |           examples[contentType] = { default: { value: content.example } };
482 |         }
483 |       }
484 | 
485 |       if (Object.keys(examples).length === 0) {
486 |         return {
487 |           content: [
488 |             {
489 |               type: "text",
490 |               text: `No examples found for ${method.toUpperCase()} ${path} response${statusCode ? ` ${statusCode}` : ""}`,
491 |             },
492 |           ],
493 |         };
494 |       }
495 | 
496 |       return {
497 |         content: [
498 |           {
499 |             type: "text",
500 |             text: toYaml(examples),
501 |           },
502 |         ],
503 |       };
504 |     } else if (type === "component") {
505 |       if (!componentType || !componentName) {
506 |         return {
507 |           content: [
508 |             { type: "text", text: "Component type and name are required for component examples" },
509 |           ],
510 |         };
511 |       }
512 | 
513 |       const component = openApiDoc.components?.[componentType]?.[componentName];
514 |       if (!component) {
515 |         return {
516 |           content: [
517 |             { type: "text", text: `Component ${componentType}.${componentName} not found` },
518 |           ],
519 |         };
520 |       }
521 | 
522 |       const examples =
523 |         component.examples || (component.example ? { default: component.example } : null);
524 |       if (!examples) {
525 |         return {
526 |           content: [
527 |             {
528 |               type: "text",
529 |               text: `No examples found for component ${componentType}.${componentName}`,
530 |             },
531 |           ],
532 |         };
533 |       }
534 | 
535 |       return {
536 |         content: [
537 |           {
538 |             type: "text",
539 |             text: toYaml(examples),
540 |           },
541 |         ],
542 |       };
543 |     }
544 |   },
545 | );
546 | 
547 | // Search across the API specification
548 | server.tool(
549 |   "search-schema",
550 |   "Searches across paths, operations, and schemas",
551 |   { pattern: z.string().describe("Search pattern (case-insensitive)") },
552 |   ({ pattern }) => {
553 |     const searchRegex = new RegExp(pattern, "i");
554 |     const results = {
555 |       paths: [],
556 |       operations: [],
557 |       components: [],
558 |       parameters: [],
559 |       securitySchemes: [],
560 |     };
561 | 
562 |     // Search paths
563 |     for (const path of Object.keys(openApiDoc.paths || {})) {
564 |       if (searchRegex.test(path)) {
565 |         results.paths.push(path);
566 |       }
567 | 
568 |       // Search operations within paths
569 |       const pathItem = openApiDoc.paths[path];
570 |       for (const method of ["get", "post", "put", "delete", "patch", "options", "head"]) {
571 |         const operation = pathItem[method];
572 |         if (!operation) continue;
573 | 
574 |         if (
575 |           searchRegex.test(operation.summary || "") ||
576 |           searchRegex.test(operation.description || "") ||
577 |           (operation.tags && operation.tags.some((tag) => searchRegex.test(tag)))
578 |         ) {
579 |           results.operations.push(`${method.toUpperCase()} ${path}`);
580 |         }
581 | 
582 |         // Search parameters
583 |         for (const param of operation.parameters || []) {
584 |           if (searchRegex.test(param.name || "") || searchRegex.test(param.description || "")) {
585 |             results.parameters.push(`${param.name} (${method.toUpperCase()} ${path})`);
586 |           }
587 |         }
588 |       }
589 |     }
590 | 
591 |     // Search components
592 |     const components = openApiDoc.components || {};
593 |     for (const [type, typeObj] of Object.entries(components)) {
594 |       if (!typeObj || typeof typeObj !== "object") continue;
595 | 
596 |       for (const [name, component] of Object.entries(typeObj)) {
597 |         if (searchRegex.test(name) || searchRegex.test(component.description || "")) {
598 |           results.components.push(`${type}.${name}`);
599 |         }
600 |       }
601 |     }
602 | 
603 |     // Search security schemes
604 |     for (const [name, scheme] of Object.entries(components.securitySchemes || {})) {
605 |       if (searchRegex.test(name) || searchRegex.test(scheme.description || "")) {
606 |         results.securitySchemes.push(name);
607 |       }
608 |     }
609 | 
610 |     // Clean up empty arrays
611 |     for (const key of Object.keys(results)) {
612 |       if (results[key].length === 0) {
613 |         delete results[key];
614 |       }
615 |     }
616 | 
617 |     if (Object.keys(results).length === 0) {
618 |       return { content: [{ type: "text", text: `No matches found for "${pattern}"` }] };
619 |     }
620 | 
621 |     return {
622 |       content: [
623 |         {
624 |           type: "text",
625 |           text: toYaml(results),
626 |         },
627 |       ],
628 |     };
629 |   },
630 | );
631 | 
632 | const transport = new StdioServerTransport();
633 | await server.connect(transport);
634 | 
```