Library usage - ivo-toby/mcp-openapi-server GitHub Wiki

This section is for developers who want to use this package as a library to create custom MCP servers.

🚀 Using as a Library

Create dedicated MCP servers for specific APIs by importing and configuring the OpenAPIServer class. This approach is ideal for:

  • Custom Authentication: Implement complex authentication patterns with the AuthProvider interface
  • API-Specific Optimizations: Filter endpoints, customize error handling, and optimize for specific use cases
  • Distribution: Package your server as a standalone npm module for easy sharing
  • Integration: Embed the server in larger applications or add custom middleware

Basic Library Usage

import { OpenAPIServer } from "@ivotoby/openapi-mcp-server"
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"

const config = {
  name: "my-api-server",
  version: "1.0.0",
  apiBaseUrl: "https://api.example.com",
  openApiSpec: "https://api.example.com/openapi.json",
  specInputMethod: "url" as const,
  headers: {
    Authorization: "Bearer your-token",
    "X-API-Key": "your-api-key",
  },
  transportType: "stdio" as const,
  toolsMode: "all" as const, // Options: "all", "dynamic", "explicit"
}

const server = new OpenAPIServer(config)
const transport = new StdioServerTransport()
await server.start(transport)

Tool Loading Modes

The toolsMode configuration option controls which tools are loaded from your OpenAPI specification:

// Load all tools from the spec (default)
const config = {
  // ... other config
  toolsMode: "all" as const,
  // Optional: Apply filters to control which tools are loaded
  includeTools: ["GET::users", "POST::users"], // Only these tools
  includeTags: ["public"], // Only tools with these tags
  includeResources: ["users"], // Only tools under these resources
  includeOperations: ["get", "post"], // Only these HTTP methods
}

// Load only dynamic meta-tools for API exploration
const config = {
  // ... other config
  toolsMode: "dynamic" as const,
  // Provides: list-api-endpoints, get-api-endpoint-schema, invoke-api-endpoint
}

// Load only explicitly specified tools (ignores other filters)
const config = {
  // ... other config
  toolsMode: "explicit" as const,
  includeTools: ["GET::users", "POST::users"], // Only these exact tools
  // includeTags, includeResources, includeOperations are ignored in explicit mode
}

Advanced Authentication with AuthProvider

For APIs with token expiration, refresh requirements, or complex authentication:

import { OpenAPIServer, AuthProvider } from "@ivotoby/openapi-mcp-server"
import { AxiosError } from "axios"

class MyAuthProvider implements AuthProvider {
  async getAuthHeaders(): Promise<Record<string, string>> {
    // Called before each request - return fresh headers
    if (this.isTokenExpired()) {
      await this.refreshToken()
    }
    return { Authorization: `Bearer ${this.token}` }
  }

  async handleAuthError(error: AxiosError): Promise<boolean> {
    // Called on 401/403 errors - return true to retry
    if (error.response?.status === 401) {
      await this.refreshToken()
      return true // Retry the request
    }
    return false
  }
}

const authProvider = new MyAuthProvider()
const config = {
  // ... other config
  authProvider: authProvider, // Use AuthProvider instead of static headers
}

📁 See the examples/ directory for complete, runnable examples including:

  • Basic library usage with static authentication
  • AuthProvider implementations for different scenarios
  • Real-world Beatport API integration
  • Production-ready packaging patterns

🔐 Dynamic Authentication with AuthProvider

The AuthProvider interface enables sophisticated authentication scenarios that static headers cannot handle:

Key Features

  • Dynamic Headers: Fresh authentication headers for each request
  • Token Expiration Handling: Automatic detection and handling of expired tokens
  • Authentication Error Recovery: Retry logic for recoverable authentication failures
  • Custom Error Messages: Provide clear, actionable guidance to users

AuthProvider Interface

interface AuthProvider {
  /**
   * Get authentication headers for the current request
   * Called before each API request to get fresh headers
   */
  getAuthHeaders(): Promise<Record<string, string>>

  /**
   * Handle authentication errors from API responses
   * Called when the API returns 401 or 403 errors
   * Return true to retry the request, false otherwise
   */
  handleAuthError(error: AxiosError): Promise<boolean>
}

Common Patterns

Automatic Token Refresh

class RefreshableAuthProvider implements AuthProvider {
  async getAuthHeaders(): Promise<Record<string, string>> {
    if (this.isTokenExpired()) {
      await this.refreshToken()
    }
    return { Authorization: `Bearer ${this.accessToken}` }
  }

  async handleAuthError(error: AxiosError): Promise<boolean> {
    if (error.response?.status === 401) {
      await this.refreshToken()
      return true // Retry with fresh token
    }
    return false
  }
}

Manual Token Management (e.g., Beatport)

class ManualTokenAuthProvider implements AuthProvider {
  async getAuthHeaders(): Promise<Record<string, string>> {
    if (!this.token || this.isTokenExpired()) {
      throw new Error(
        "Token expired. Please get a new token from your browser:\n" +
          "1. Go to the API website and log in\n" +
          "2. Open browser dev tools (F12)\n" +
          "3. Copy the Authorization header from any API request\n" +
          "4. Update your token using updateToken()",
      )
    }
    return { Authorization: `Bearer ${this.token}` }
  }

  updateToken(token: string): void {
    this.token = token
    this.tokenExpiry = new Date(Date.now() + 3600000) // 1 hour
  }
}

API Key Authentication

class ApiKeyAuthProvider implements AuthProvider {
  constructor(private apiKey: string) {}

  async getAuthHeaders(): Promise<Record<string, string>> {
    return { "X-API-Key": this.apiKey }
  }

  async handleAuthError(error: AxiosError): Promise<boolean> {
    throw new Error("API key authentication failed. Please check your key.")
  }
}

📖 For detailed AuthProvider documentation and examples, see docs/auth-provider-guide.md

OpenAPI Schema Processing

Reference Resolution

This MCP server implements robust OpenAPI reference ($ref) resolution to ensure accurate representation of API schemas:

  • Parameter References: Fully resolves $ref pointers to parameter components in the OpenAPI spec
  • Schema References: Handles nested schema references within parameters and request bodies
  • Recursive References: Prevents infinite loops by detecting and handling circular references
  • Nested Properties: Preserves complex nested object and array structures with all their attributes

Query Parameter Handling

The server provides comprehensive support for OpenAPI query parameters, automatically handling them based on the HTTP method:

For GET-like methods (GET, DELETE, HEAD, OPTIONS):

  • Query parameters are sent as URL query strings via Axios params
  • Arrays are automatically converted to comma-separated strings (e.g., ["a", "b", "c"] becomes "a,b,c")
  • Boolean and numeric values are preserved as their respective types
  • Path parameters are correctly interpolated into the URL and removed from query parameters

For POST-like methods (POST, PUT, PATCH):

  • All parameters (including query parameters) are included in the request body
  • This follows REST conventions where POST requests typically use request bodies

Parameter Processing:

{
  "name": "limit",
  "in": "query",
  "schema": { "type": "integer" },
  "required": false
}

When you call a tool with query parameters:

// This GET request
{ "limit": 10, "tags": ["javascript", "typescript"] }

// Becomes this HTTP request
GET /api/search?limit=10&tags=javascript,typescript

Mixed Parameter Types:

{
  "parameters": [
    { "name": "userId", "in": "path", "required": true },
    { "name": "include", "in": "query", "required": false },
    { "name": "format", "in": "query", "required": false }
  ]
}
// Tool call parameters
{ "userId": "123", "include": "posts", "format": "json" }

// Results in HTTP request
GET /users/123/posts?include=posts&format=json

The server ensures that:

  • Path parameters are correctly substituted in URLs (not added to query string)
  • Query parameters are properly encoded and formatted
  • Parameter types are preserved (strings, numbers, booleans, arrays)
  • Undefined/null values are excluded from the request

Input Schema Composition

The server intelligently merges parameters and request bodies into a unified input schema for each tool:

  • Parameters + Request Body Merging: Combines path, query, and body parameters into a single schema
  • Collision Handling: Resolves naming conflicts by prefixing body properties that conflict with parameter names
  • Type Preservation: Maintains the original type information for all schema elements
  • Metadata Retention: Preserves descriptions, formats, defaults, enums, and other schema attributes

Complex Schema Support

The MCP server handles various OpenAPI schema complexities:

  • Primitive Type Bodies: Wraps non-object request bodies in a "body" property
  • Object Bodies: Flattens object properties into the tool's input schema
  • Array Bodies: Properly handles array schemas with their nested item definitions
  • Required Properties: Tracks and preserves which parameters and properties are required
⚠️ **GitHub.com Fallback** ⚠️