#
tokens: 7300/50000 6/6 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

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

# Files

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

```
node_modules

```

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

```markdown
# OpenAPI Schema Model Context Protocol Server

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.

## Features

- Load any OpenAPI schema file (JSON or YAML) specified via command line argument
- Explore API paths, operations, parameters, and schemas
- View detailed request and response schemas
- Look up component definitions and examples
- Search across the entire API specification
- Get responses in YAML format for better LLM comprehension

## Usage

### Command Line

Run the MCP server with a specific schema file:

```bash
# Use the default openapi.yaml in current directory
npx -y mcp-openapi-schema

# Use a specific schema file (relative path)
npx -y mcp-openapi-schema ../petstore.json

# Use a specific schema file (absolute path)
npx -y mcp-openapi-schema /absolute/path/to/api-spec.yaml

# Show help
npx -y mcp-openapi-schema --help
```

### Claude Desktop Integration

To use this MCP server with Claude Desktop, edit your `claude_desktop_config.json` configuration file:

```json
{
  "mcpServers": {
    "OpenAPI Schema": {
      "command": "npx",
      "args": ["-y", "mcp-openapi-schema", "/ABSOLUTE/PATH/TO/openapi.yaml"]
    }
  }
}
```

Location of the configuration file:

- macOS/Linux: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `$env:AppData\Claude\claude_desktop_config.json`

### Claude Code Integration

To use this MCP server with Claude Code CLI, follow these steps:

1. **Add the OpenAPI Schema MCP server to Claude Code**

   ```bash
   # Basic syntax
   claude mcp add openapi-schema npx -y mcp-openapi-schema

   # Example with specific schema
   claude mcp add petstore-api npx -y mcp-openapi-schema ~/Projects/petstore.yaml
   ```

2. **Verify the MCP server is registered**

   ```bash
   # List all configured servers
   claude mcp list

   # Get details for your OpenAPI schema server
   claude mcp get openapi-schema
   ```

3. **Remove the server if needed**

   ```bash
   claude mcp remove openapi-schema
   ```

4. **Use the tool in Claude Code**

   Once configured, you can invoke the tool in your Claude Code session by asking questions about the OpenAPI schema.

**Tips:**

- Use the `-s` or `--scope` flag with `project` (default) or `global` to specify where the configuration is stored
- Add multiple MCP servers for different APIs with different names

## MCP Tools

The server provides the following tools for LLMs to interact with OpenAPI schemas:

- `list-endpoints`: Lists all API paths and their HTTP methods with summaries in a nested object structure
- `get-endpoint`: Gets detailed information about a specific endpoint including parameters and responses
- `get-request-body`: Gets the request body schema for a specific endpoint and method
- `get-response-schema`: Gets the response schema for a specific endpoint, method, and status code
- `get-path-parameters`: Gets the parameters for a specific path
- `list-components`: Lists all schema components (schemas, responses, parameters, etc.)
- `get-component`: Gets detailed definition for a specific component
- `list-security-schemes`: Lists all available security schemes
- `get-examples`: Gets examples for a specific component or endpoint
- `search-schema`: Searches across paths, operations, and schemas

## Examples

Example queries to try:

```
What endpoints are available in this API?
Show me the details for the POST /pets endpoint.
What parameters does the GET /pets/{petId} endpoint take?
What is the request body schema for creating a new pet?
What response will I get from the DELETE /pets/{petId} endpoint?
What schemas are defined in this API?
Show me the definition of the Pet schema.
What are the available security schemes for this API?
Are there any example responses for getting a pet by ID?
Search for anything related to "user" in this API.
```
```

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

```json
{
  "name": "mcp-openapi-schema",
  "version": "0.0.1",
  "description": "A Model Context Protocol server that exposes OpenAPI schema information to Large Language Models",
  "type": "module",
  "main": "index.mjs",
  "bin": {
    "mcp-openapi-schema": "index.mjs"
  },
  "scripts": {
    "start": "node index.mjs",
    "test": "node example-usage.mjs"
  },
  "keywords": [
    "openapi",
    "swagger",
    "mcp",
    "llm",
    "claude"
  ],
  "author": "Hannes Junnila",
  "license": "MIT",
  "dependencies": {
    "@apidevtools/swagger-parser": "^10.1.1",
    "@modelcontextprotocol/sdk": "^1.7.0",
    "js-yaml": "^4.1.0",
    "zod": "^3.24.2"
  }
}

```

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

```
// This is a simple example of how to test the MCP OpenAPI Schema server
// using the official MCP SDK client
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { dirname, resolve } from "path";
import { fileURLToPath } from "url";

const __dirname = dirname(fileURLToPath(import.meta.url));

// Set up the MCP client to communicate with our server
const transport = new StdioClientTransport({
  command: "node",
  args: [
    resolve(__dirname, "index.mjs"),
    resolve(__dirname, "./sample-petstore.yaml")
  ],
});

const client = new Client({
  name: "openapi-schema-client",
  version: "1.0.0",
});

// Connect to the server
console.log("Connecting to MCP server...");
await client.connect(transport);
console.log("Connected to MCP server successfully!");

// Run example tool calls
try {
  // List endpoints
  console.log("\n--- LISTING ENDPOINTS ---");
  const endpoints = await client.callTool({
    name: "list-endpoints",
    arguments: {},
  });
  console.log(endpoints.content[0].text);

  // Get endpoint details
  console.log("\n--- GET ENDPOINT DETAILS ---");
  const endpointDetails = await client.callTool({
    name: "get-endpoint",
    arguments: {
      path: "/pets",
      method: "get",
    },
  });
  console.log(endpointDetails.content[0].text);

  // Get request body schema
  console.log("\n--- GET REQUEST BODY SCHEMA ---");
  const requestBody = await client.callTool({
    name: "get-request-body",
    arguments: {
      path: "/pets",
      method: "post",
    },
  });
  console.log(requestBody.content[0].text);

  // List components
  console.log("\n--- LIST COMPONENTS ---");
  const components = await client.callTool({
    name: "list-components",
    arguments: {},
  });
  console.log(components.content[0].text);

  // Get component schema
  console.log("\n--- GET COMPONENT SCHEMA ---");
  const component = await client.callTool({
    name: "get-component",
    arguments: {
      type: "schemas",
      name: "Pet",
    },
  });
  console.log(component.content[0].text);

  // Search schema
  console.log("\n--- SEARCH SCHEMA ---");
  const searchResults = await client.callTool({
    name: "search-schema",
    arguments: {
      pattern: "pet",
    },
  });
  console.log(searchResults.content[0].text);

  // Get path parameters
  console.log("\n--- GET PATH PARAMETERS ---");
  const parameters = await client.callTool({
    name: "get-path-parameters",
    arguments: {
      path: "/pets/{petId}",
      method: "get",
    },
  });
  console.log(parameters.content[0].text);

  // Get response schema
  console.log("\n--- GET RESPONSE SCHEMA ---");
  const response = await client.callTool({
    name: "get-response-schema",
    arguments: {
      path: "/pets/{petId}",
      method: "get",
      statusCode: "200",
    },
  });
  console.log(response.content[0].text);

  // List security schemes
  console.log("\n--- LIST SECURITY SCHEMES ---");
  const security = await client.callTool({
    name: "list-security-schemes",
    arguments: {},
  });
  console.log(security.content[0].text);
} catch (error) {
  console.error("Error during testing:", error);
} finally {
  // Close the connection
  await client.close();
  console.log("\nTests completed, disconnected from server.");
}

```

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

```yaml
openapi: 3.0.0
info:
  title: Petstore API
  version: 1.0.0
  description: A sample API for managing pets
servers:
  - url: https://petstore.example.com/api/v1
paths:
  /pets:
    get:
      summary: List all pets
      description: Returns all pets from the system
      operationId: listPets
      tags:
        - pets
      parameters:
        - name: limit
          in: query
          description: Maximum number of pets to return
          required: false
          schema:
            type: integer
            format: int32
            minimum: 1
            maximum: 100
            default: 20
      responses:
        '200':
          description: A paged array of pets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PetsResponse'
              example:
                pets:
                  - id: 1
                    name: "Fluffy"
                    status: "available"
                    category: "cat"
                  - id: 2
                    name: "Rex"
                    status: "pending"
                    category: "dog"
                total: 2
                limit: 20
                offset: 0
    post:
      summary: Create a pet
      description: Creates a new pet in the store
      operationId: createPet
      tags:
        - pets
      requestBody:
        description: Pet to add to the store
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewPet'
            example:
              name: "Fluffy"
              category: "cat"
      responses:
        '201':
          description: Pet created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
              example:
                id: 1
                name: "Fluffy"
                status: "available"
                category: "cat"
        '400':
          description: Invalid input
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                code: 400
                message: "Invalid input: name is required"
  /pets/{petId}:
    get:
      summary: Get a pet by ID
      description: Returns a single pet by ID
      operationId: getPetById
      tags:
        - pets
      parameters:
        - name: petId
          in: path
          description: ID of pet to return
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: Expected response to a valid request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Pet'
              example:
                id: 1
                name: "Fluffy"
                status: "available"
                category: "cat"
        '404':
          description: Pet not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              example:
                code: 404
                message: "Pet not found"
    delete:
      summary: Delete a pet
      description: Deletes a pet by ID
      operationId: deletePet
      tags:
        - pets
      parameters:
        - name: petId
          in: path
          description: ID of pet to delete
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '204':
          description: Pet deleted
        '404':
          description: Pet not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Pet:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
          format: int64
          description: Unique identifier for the pet
        name:
          type: string
          description: Name of the pet
        status:
          type: string
          description: Status of the pet
          enum:
            - available
            - pending
            - sold
          default: available
        category:
          type: string
          description: Category of the pet
    NewPet:
      type: object
      required:
        - name
      properties:
        name:
          type: string
          description: Name of the pet
        category:
          type: string
          description: Category of the pet
    PetsResponse:
      type: object
      required:
        - pets
        - total
      properties:
        pets:
          type: array
          items:
            $ref: '#/components/schemas/Pet'
        total:
          type: integer
          description: Total number of pets
        limit:
          type: integer
          description: Limit used in the request
        offset:
          type: integer
          description: Offset used in the request
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
          description: Error code
        message:
          type: string
          description: Error message
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key for authorization
security:
  - ApiKeyAuth: []
```

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

```
#!/usr/bin/env node
import SwaggerParser from "@apidevtools/swagger-parser";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import yaml from "js-yaml";
import { Console } from "node:console";
import { readFile } from "node:fs/promises";
import { resolve } from "node:path";
import { z } from "zod";

// Redirect console output to stderr to avoid interfering with MCP comms
globalThis.console = new Console(process.stderr);

const args = process.argv.slice(2);

if (args.includes("--help") || args.includes("-h")) {
  console.log(`
OpenAPI Schema Model Context Protocol Server

Usage: 
  node index.mjs [path/to/openapi.yaml]

Arguments:
  path/to/openapi.yaml  Path to the OpenAPI schema file (JSON or YAML) (optional)
                       If not provided, defaults to openapi.yaml

Examples:
  node index.mjs # Uses default openapi.yaml
  node index.mjs ../petstore.json # Uses petstore OpenAPI spec
  node index.mjs /absolute/path/to/api-schema.yaml
  `);
  process.exit(0);
}

const schemaArg = args[0];

const loadSchema = async () => {
  // Default to openapi.yaml if no argument provided
  const schemaPath = resolve(schemaArg ?? "openapi.yaml");

  try {
    // Parse and validate the OpenAPI document
    return await SwaggerParser.validate(schemaPath, { validate: { schema: false } });
  } catch (error) {
    console.error(`Error loading schema: ${error.message}`);
    process.exit(1);
  }
};

const openApiDoc = await loadSchema();

// Extract schema name from file path or from the OpenAPI info
const schemaName =
  openApiDoc.info?.title ||
  (schemaArg
    ? schemaArg
        .split("/")
        .pop()
        .replace(/\.(yaml|json)$/i, "")
    : "openapi");

const server = new McpServer({
  name: `OpenAPI Schema: ${schemaName}`,
  version: "1.0.0",
  description: `Provides OpenAPI schema information for ${schemaName} (${openApiDoc.info?.version || "unknown version"})`,
});

// Helper to convert objects to YAML for better readability
const toYaml = (obj) => yaml.dump(obj, { lineWidth: 100, noRefs: true });

// List all API paths and operations
server.tool(
  "list-endpoints",
  "Lists all API paths and their HTTP methods with summaries, organized by path",
  () => {
    const pathMap = {};

    for (const [path, pathItem] of Object.entries(openApiDoc.paths || {})) {
      // Get all HTTP methods for this path
      const methods = Object.keys(pathItem).filter((key) =>
        ["get", "post", "put", "delete", "patch", "options", "head"].includes(key.toLowerCase()),
      );

      // Create a methods object for this path
      pathMap[path] = {};

      // Add each method with its summary
      for (const method of methods) {
        const operation = pathItem[method];
        pathMap[path][method.toUpperCase()] = operation.summary || "No summary";
      }
    }

    return {
      content: [
        {
          type: "text",
          text: toYaml(pathMap),
        },
      ],
    };
  },
);

// Get details for a specific endpoint
server.tool(
  "get-endpoint",
  "Gets detailed information about a specific API endpoint",
  { path: z.string(), method: z.string() },
  ({ path, method }) => {
    const pathItem = openApiDoc.paths?.[path];
    if (!pathItem) {
      return { content: [{ type: "text", text: `Path ${path} not found` }] };
    }

    const operation = pathItem[method.toLowerCase()];
    if (!operation) {
      return { content: [{ type: "text", text: `Method ${method} not found for path ${path}` }] };
    }

    // Extract relevant information
    const endpoint = {
      path,
      method: method.toUpperCase(),
      summary: operation.summary,
      description: operation.description,
      tags: operation.tags,
      parameters: operation.parameters,
      requestBody: operation.requestBody,
      responses: operation.responses,
      security: operation.security,
      deprecated: operation.deprecated,
    };

    return {
      content: [
        {
          type: "text",
          text: toYaml(endpoint),
        },
      ],
    };
  },
);

// Get request body schema for a specific endpoint
server.tool(
  "get-request-body",
  "Gets the request body schema for a specific endpoint",
  { path: z.string(), method: z.string() },
  ({ path, method }) => {
    const pathItem = openApiDoc.paths?.[path];
    if (!pathItem) {
      return { content: [{ type: "text", text: `Path ${path} not found` }] };
    }

    const operation = pathItem[method.toLowerCase()];
    if (!operation) {
      return { content: [{ type: "text", text: `Method ${method} not found for path ${path}` }] };
    }

    const requestBody = operation.requestBody;
    if (!requestBody) {
      return { content: [{ type: "text", text: `No request body defined for ${method} ${path}` }] };
    }

    return {
      content: [
        {
          type: "text",
          text: toYaml(requestBody),
        },
      ],
    };
  },
);

// Get response schema for a specific endpoint and status code
server.tool(
  "get-response-schema",
  "Gets the response schema for a specific endpoint, method, and status code",
  {
    path: z.string(),
    method: z.string(),
    statusCode: z.string().default("200"),
  },
  ({ path, method, statusCode }) => {
    const pathItem = openApiDoc.paths?.[path];
    if (!pathItem) {
      return { content: [{ type: "text", text: `Path ${path} not found` }] };
    }

    const operation = pathItem[method.toLowerCase()];
    if (!operation) {
      return { content: [{ type: "text", text: `Method ${method} not found for path ${path}` }] };
    }

    const responses = operation.responses;
    if (!responses) {
      return { content: [{ type: "text", text: `No responses defined for ${method} ${path}` }] };
    }

    const response = responses[statusCode] || responses.default;
    if (!response) {
      return {
        content: [
          {
            type: "text",
            text: `No response for status code ${statusCode} (or default) found for ${method} ${path}.\nAvailable status codes: ${Object.keys(responses).join(", ")}`,
          },
        ],
      };
    }

    return {
      content: [
        {
          type: "text",
          text: toYaml(response),
        },
      ],
    };
  },
);

// Get parameters for a specific path
server.tool(
  "get-path-parameters",
  "Gets the parameters for a specific path",
  { path: z.string(), method: z.string().optional() },
  ({ path, method }) => {
    const pathItem = openApiDoc.paths?.[path];
    if (!pathItem) {
      return { content: [{ type: "text", text: `Path ${path} not found` }] };
    }

    let parameters = [...(pathItem.parameters || [])];

    // If method is specified, add method-specific parameters
    if (method) {
      const operation = pathItem[method.toLowerCase()];
      if (operation && operation.parameters) {
        parameters = [...parameters, ...operation.parameters];
      }
    }

    if (parameters.length === 0) {
      return {
        content: [
          {
            type: "text",
            text: `No parameters found for ${method ? `${method.toUpperCase()} ` : ""}${path}`,
          },
        ],
      };
    }

    return {
      content: [
        {
          type: "text",
          text: toYaml(parameters),
        },
      ],
    };
  },
);

// List all components
server.tool(
  "list-components",
  "Lists all schema components (schemas, parameters, responses, etc.)",
  () => {
    const components = openApiDoc.components || {};
    const result = {};

    // For each component type, list the keys
    for (const [type, items] of Object.entries(components)) {
      if (items && typeof items === "object") {
        result[type] = Object.keys(items);
      }
    }

    return {
      content: [
        {
          type: "text",
          text: toYaml(result),
        },
      ],
    };
  },
);

// Get a specific component
server.tool(
  "get-component",
  "Gets detailed definition for a specific component",
  {
    type: z.string().describe("Component type (e.g., schemas, parameters, responses)"),
    name: z.string().describe("Component name"),
  },
  ({ type, name }) => {
    const components = openApiDoc.components || {};
    const componentType = components[type];

    if (!componentType) {
      return {
        content: [
          {
            type: "text",
            text: `Component type '${type}' not found. Available types: ${Object.keys(components).join(", ")}`,
          },
        ],
      };
    }

    const component = componentType[name];
    if (!component) {
      return {
        content: [
          {
            type: "text",
            text: `Component '${name}' not found in '${type}'. Available components: ${Object.keys(componentType).join(", ")}`,
          },
        ],
      };
    }

    return {
      content: [
        {
          type: "text",
          text: toYaml(component),
        },
      ],
    };
  },
);

// List security schemes
server.tool("list-security-schemes", "Lists all available security schemes", () => {
  const securitySchemes = openApiDoc.components?.securitySchemes || {};
  const result = {};

  for (const [name, scheme] of Object.entries(securitySchemes)) {
    result[name] = {
      type: scheme.type,
      description: scheme.description,
      ...(scheme.type === "oauth2" ? { flows: Object.keys(scheme.flows || {}) } : {}),
      ...(scheme.type === "apiKey" ? { in: scheme.in, name: scheme.name } : {}),
      ...(scheme.type === "http" ? { scheme: scheme.scheme } : {}),
    };
  }

  if (Object.keys(result).length === 0) {
    return { content: [{ type: "text", text: "No security schemes defined in this API" }] };
  }

  return {
    content: [
      {
        type: "text",
        text: toYaml(result),
      },
    ],
  };
});

// Get examples
server.tool(
  "get-examples",
  "Gets examples for a specific component or endpoint",
  {
    type: z.enum(["request", "response", "component"]).describe("Type of example to retrieve"),
    path: z.string().optional().describe("API path (required for request/response examples)"),
    method: z.string().optional().describe("HTTP method (required for request/response examples)"),
    statusCode: z.string().optional().describe("Status code (for response examples)"),
    componentType: z
      .string()
      .optional()
      .describe("Component type (required for component examples)"),
    componentName: z
      .string()
      .optional()
      .describe("Component name (required for component examples)"),
  },
  ({ type, path, method, statusCode, componentType, componentName }) => {
    if (type === "request") {
      if (!path || !method) {
        return {
          content: [{ type: "text", text: "Path and method are required for request examples" }],
        };
      }

      const operation = openApiDoc.paths?.[path]?.[method.toLowerCase()];
      if (!operation) {
        return {
          content: [{ type: "text", text: `Operation ${method.toUpperCase()} ${path} not found` }],
        };
      }

      if (!operation.requestBody?.content) {
        return {
          content: [
            { type: "text", text: `No request body defined for ${method.toUpperCase()} ${path}` },
          ],
        };
      }

      const examples = {};
      for (const [contentType, content] of Object.entries(operation.requestBody.content)) {
        if (content.examples) {
          examples[contentType] = content.examples;
        } else if (content.example) {
          examples[contentType] = { default: { value: content.example } };
        }
      }

      if (Object.keys(examples).length === 0) {
        return {
          content: [
            { type: "text", text: `No examples found for ${method.toUpperCase()} ${path} request` },
          ],
        };
      }

      return {
        content: [
          {
            type: "text",
            text: toYaml(examples),
          },
        ],
      };
    } else if (type === "response") {
      if (!path || !method) {
        return {
          content: [{ type: "text", text: "Path and method are required for response examples" }],
        };
      }

      const operation = openApiDoc.paths?.[path]?.[method.toLowerCase()];
      if (!operation) {
        return {
          content: [{ type: "text", text: `Operation ${method.toUpperCase()} ${path} not found` }],
        };
      }

      if (!operation.responses) {
        return {
          content: [
            { type: "text", text: `No responses defined for ${method.toUpperCase()} ${path}` },
          ],
        };
      }

      const responseObj = statusCode
        ? operation.responses[statusCode]
        : Object.values(operation.responses)[0];
      if (!responseObj) {
        return {
          content: [
            {
              type: "text",
              text: `Response ${statusCode} not found for ${method.toUpperCase()} ${path}. Available: ${Object.keys(operation.responses).join(", ")}`,
            },
          ],
        };
      }

      if (!responseObj.content) {
        return { content: [{ type: "text", text: `No content defined in response` }] };
      }

      const examples = {};
      for (const [contentType, content] of Object.entries(responseObj.content)) {
        if (content.examples) {
          examples[contentType] = content.examples;
        } else if (content.example) {
          examples[contentType] = { default: { value: content.example } };
        }
      }

      if (Object.keys(examples).length === 0) {
        return {
          content: [
            {
              type: "text",
              text: `No examples found for ${method.toUpperCase()} ${path} response${statusCode ? ` ${statusCode}` : ""}`,
            },
          ],
        };
      }

      return {
        content: [
          {
            type: "text",
            text: toYaml(examples),
          },
        ],
      };
    } else if (type === "component") {
      if (!componentType || !componentName) {
        return {
          content: [
            { type: "text", text: "Component type and name are required for component examples" },
          ],
        };
      }

      const component = openApiDoc.components?.[componentType]?.[componentName];
      if (!component) {
        return {
          content: [
            { type: "text", text: `Component ${componentType}.${componentName} not found` },
          ],
        };
      }

      const examples =
        component.examples || (component.example ? { default: component.example } : null);
      if (!examples) {
        return {
          content: [
            {
              type: "text",
              text: `No examples found for component ${componentType}.${componentName}`,
            },
          ],
        };
      }

      return {
        content: [
          {
            type: "text",
            text: toYaml(examples),
          },
        ],
      };
    }
  },
);

// Search across the API specification
server.tool(
  "search-schema",
  "Searches across paths, operations, and schemas",
  { pattern: z.string().describe("Search pattern (case-insensitive)") },
  ({ pattern }) => {
    const searchRegex = new RegExp(pattern, "i");
    const results = {
      paths: [],
      operations: [],
      components: [],
      parameters: [],
      securitySchemes: [],
    };

    // Search paths
    for (const path of Object.keys(openApiDoc.paths || {})) {
      if (searchRegex.test(path)) {
        results.paths.push(path);
      }

      // Search operations within paths
      const pathItem = openApiDoc.paths[path];
      for (const method of ["get", "post", "put", "delete", "patch", "options", "head"]) {
        const operation = pathItem[method];
        if (!operation) continue;

        if (
          searchRegex.test(operation.summary || "") ||
          searchRegex.test(operation.description || "") ||
          (operation.tags && operation.tags.some((tag) => searchRegex.test(tag)))
        ) {
          results.operations.push(`${method.toUpperCase()} ${path}`);
        }

        // Search parameters
        for (const param of operation.parameters || []) {
          if (searchRegex.test(param.name || "") || searchRegex.test(param.description || "")) {
            results.parameters.push(`${param.name} (${method.toUpperCase()} ${path})`);
          }
        }
      }
    }

    // Search components
    const components = openApiDoc.components || {};
    for (const [type, typeObj] of Object.entries(components)) {
      if (!typeObj || typeof typeObj !== "object") continue;

      for (const [name, component] of Object.entries(typeObj)) {
        if (searchRegex.test(name) || searchRegex.test(component.description || "")) {
          results.components.push(`${type}.${name}`);
        }
      }
    }

    // Search security schemes
    for (const [name, scheme] of Object.entries(components.securitySchemes || {})) {
      if (searchRegex.test(name) || searchRegex.test(scheme.description || "")) {
        results.securitySchemes.push(name);
      }
    }

    // Clean up empty arrays
    for (const key of Object.keys(results)) {
      if (results[key].length === 0) {
        delete results[key];
      }
    }

    if (Object.keys(results).length === 0) {
      return { content: [{ type: "text", text: `No matches found for "${pattern}"` }] };
    }

    return {
      content: [
        {
          type: "text",
          text: toYaml(results),
        },
      ],
    };
  },
);

const transport = new StdioServerTransport();
await server.connect(transport);

```