API2AI - OpenAPI to MCP Server

🎯 Example MCP Server

NPM VersionGitHub DiscussionsPRs Welcome

API2AI: OpenAPI to MCP-Use Server

Generate production-ready MCP servers from any OpenAPI specification using the highly-used and convenient mcp-use framework (8k+ GitHub stars).

OpenAPI specs are easy to write and organize your code and have 100s of tools available such as the OpenAPI Builder web UI.

Features

  • 🚀 Modern Framework - Uses mcp-use for clean, maintainable code
  • 🔍 Built-in Inspector - Test tools immediately at /inspector
  • 📡 Multiple Transports - HTTP, SSE, and Streamable HTTP support
  • 🎨 UI Widgets - Compatible with ChatGPT Apps SDK and MCP-UI
  • 🔐 Auth Support - Bearer tokens, API keys, custom headers
  • ✨ Zod Schemas - Type-safe parameter validation
  • 🛡️ Security Hardening - Risk classification, policy enforcement, HTTP guardrails
  • 🐳 Production Ready - Docker, PM2, and Kubernetes ready

Quick Start

# Generate a server from the Petstore API
npx api2ai \
  https://petstore3.swagger.io/api/v3/openapi.json \
  ./petstore-mcp \
  --name petstore-api

# Install and run
cd petstore-mcp
npm install
npm start

Open http://localhost:3000/inspector to test your tools!

Usage

CLI

node generate-mcp-use-server.js <openapi-spec> [output-folder] [options]

Options:
  --name <name>            Server name (default: api-mcp-server)
  --base-url <url>         Override API base URL
  --port <port>            Server port (default: 3000)
  --allow-mutations        Enable POST/PUT/PATCH/DELETE tools by default
  --include-tags <tags>    Only include tools with these tags (comma-separated)
  --exclude-tags <tags>    Exclude tools with these tags (comma-separated)
  --approve-writes         Disable approval requirement for restricted tools
  --help                   Show help

Examples

# From remote URL
node generate-mcp-use-server.js \
  https://api.example.com/openapi.json \
  ./my-server \
  --name my-api

# From local file
node generate-mcp-use-server.js \
  ./specs/my-api.yaml \
  ./my-mcp-server \
  --port 8080

# With custom base URL
node generate-mcp-use-server.js \
  ./petstore.json \
  ./petstore \
  --base-url https://petstore.example.com/v3

# Include only read-only tools tagged "public"
node generate-mcp-use-server.js \
  ./api.json \
  ./readonly-server \
  --include-tags public \
  --exclude-tags admin,internal

# Enable writes (mutations) explicitly
node generate-mcp-use-server.js \
  ./api.json \
  ./full-server \
  --allow-mutations \
  --approve-writes

Programmatic Usage

import {
  generateMcpServer,
  extractTools,
  loadOpenApiSpec,
} from "./generate-mcp-use-server.js";

// Generate complete server
const result = await generateMcpServer(
  "https://api.example.com/openapi.json",
  "./output-folder",
  {
    serverName: "my-api",
    baseUrl: "https://api.example.com/v1",
    port: 3000,
    allowMutations: false, // block POST/PUT/PATCH/DELETE by default
    includeTags: ["public"], // only include tools tagged "public"
    excludeTags: ["admin"], // exclude tools tagged "admin"
  },
);

console.log(`Generated ${result.toolCount} tools`);

// Or just extract tools for custom processing
const spec = await loadOpenApiSpec("./my-spec.json");
const tools = extractTools(spec, {
  filterFn: (tool) => tool.riskLevel === "low", // only safe read-only tools
  excludeOperationIds: ["deleteUser"],
});

Security

The generator enforces a three-layer security model in every generated server.

Layer 1 — Generation-time risk classification

Every tool is classified during generation and the result is baked into src/tools-config.js:

Risk levelWhen assignedDefault behavior
lowGET, HEAD, OPTIONS with no dangerous keywordsEnabled, no approval required
mediumAny mutating method (POST, PUT, PATCH, DELETE)Blocked unless ALLOW_RESTRICTED_TOOLS=true
highAny operation matching admin, auth, billing, payments, tokens, secrets, user management patternsBlocked, approval required

Use --allow-mutations at generation time to promote medium-risk tools to enabled-by-default, or override at runtime with env vars.

Layer 2 — Runtime policy enforcement

checkToolPolicy() runs before every outbound API call, reading env vars at call-time so you can change policy without regenerating:

ALLOW_RESTRICTED_TOOLS=true    # unlock medium/high-risk tools
REQUIRE_APPROVALS=false        # bypass per-call approval gate

Layer 3 — HTTP hardening

The generated HTTP client enforces these on every request:

  • Timeouts — configurable via REQUEST_TIMEOUT_MS (default 30 s)
  • Response size cap — configurable via MAX_RESPONSE_BYTES (default 10 MB)
  • No redirects — redirect: 'error' prevents host-pivot attacks
  • Credential header protection — tool arguments cannot override Authorization, Cookie, X-API-Key, or other credential headers; env-configured auth always wins
  • Host allowlist — ALLOWED_API_HOSTS restricts outbound calls to specific hostnames

Inspector note: The built-in inspector at /inspector exposes all registered tools. In production, restrict access using a reverse proxy or firewall rule.

Generated Output

my-mcp-server/
├── .env              # Environment config (gitignored)
├── .env.example      # Example environment file
├── .gitignore
├── package.json
├── README.md         # Generated documentation
└── src/
    ├── index.js        # Main server with tool registrations
    ├── http-client.js  # Hardened HTTP client
    ├── tools-config.js # Tool configurations with risk metadata
    └── policy.js       # Runtime security policy

Generated Server Features

Built-in Endpoints

EndpointDescription
GET /inspectorInteractive tool testing UI
POST /mcpMCP protocol endpoint
GET /sseServer-Sent Events endpoint
GET /healthHealth check

Environment Variables

VariableDescriptionDefault
PORTServer port3000
NODE_ENVdevelopment / productiondevelopment
API_BASE_URLBase URL for API callsFrom spec
API_KEYBearer token auth—
API_AUTH_HEADERCustom header (Name:value)—
MCP_URLPublic URL for widgets—
ALLOWED_ORIGINSCORS origins (production)—
ALLOW_RESTRICTED_TOOLSAllow medium/high-risk toolsfalse
REQUIRE_APPROVALSRequire approval for restricted toolstrue
ALLOWED_API_HOSTSComma-separated allowed API hostnames(spec's host)
REQUEST_TIMEOUT_MSOutbound request timeout in ms30000
MAX_RESPONSE_BYTESMaximum response body size in bytes10485760

Connect to Claude Desktop

{
  "mcpServers": {
    "my-api": {
      "url": "http://localhost:3000/mcp"
    }
  }
}

Connect to ChatGPT

The generated server supports the OpenAI Apps SDK out of the box.

Advanced Options

Filter by risk level

const result = await generateMcpServer(specUrl, outputDir, {
  filterFn: (tool) => tool.riskLevel === "low",
});

Filter Tools by Method

const result = await generateMcpServer(specUrl, outputDir, {
  filterFn: (tool) => ["get", "post"].includes(tool.method),
});

Exclude Dangerous Operations

const result = await generateMcpServer(specUrl, outputDir, {
  excludeOperationIds: ["deleteUser", "deleteAllData", "adminReset"],
});

Filter by Path Pattern

const result = await generateMcpServer(specUrl, outputDir, {
  filterFn: (tool) => tool.pathTemplate.startsWith("/api/v2/"),
});

Combine Filters

const result = await generateMcpServer(specUrl, outputDir, {
  excludeOperationIds: ["deleteUser"],
  allowMutations: false,
  filterFn: (tool) =>
    tool.riskLevel === "low" && tool.pathTemplate.includes("/public/"),
});

Comparison with Raw MCP SDK

FeatureThis GeneratorRaw SDK
Code needed~50 lines~200+ lines
Inspector✅ Built-in❌ Manual
UI Widgets✅ Supported❌ Manual
Zod validation✅ Generated❌ Manual
Authentication✅ Configured❌ Manual
Risk classification✅ Automatic❌ Manual
Runtime policy✅ Generated❌ Manual
HTTP hardening✅ Built-in❌ Manual
Production ready✅ Yes⚠️ Requires work

Last updated on

On this page