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.
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
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)
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
}
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
The AuthProvider
interface enables sophisticated authentication scenarios that static headers cannot handle:
- 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
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>
}
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
}
}
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
}
}
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
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
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
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
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