<img src={require('./img/rest1.png').default} alt="REST API MCP Server" width="900" height="450" /> <br/> Here's a scenario most developers know well: your team has a solid REST API, a detailed OpenAPI spec, and now you want Claude to actually *use* that API — not just talk about it. Maybe you want Claude to create users, fetch orders, or trigger deployments directly from a conversation. The problem? Getting Claude to call your API usually means writing a custom tool for every endpoint. For APIs with 50+ routes, that is a painful amount of boilerplate. The **REST API MCP Server** solves this in a completely different way — it reads your OpenAPI/Swagger spec and **automatically turns every endpoint into an MCP tool**. Point it at your spec URL, hit run, and Claude is ready to work with your entire API. Zero manual wiring. Official MCP Documentation: https://modelcontextprotocol.io/docs --- ## Why This Approach Makes Sense The standard way to connect Claude to an API is to write individual tool definitions by hand. That works fine for three or four endpoints. Once your API grows, though, that approach becomes a maintenance nightmare. Every time a new route is added, someone has to remember to update the tool list. The REST API MCP Server flips the script: - It reads your existing OpenAPI/Swagger spec — the one you probably already maintain - It auto-generates a typed MCP tool for every single endpoint at startup - It stays in sync with your API automatically, since it re-reads the spec on each run - It handles authentication, query parameters, path parameters, and request bodies without any extra configuration - It works for both local development (via Claude Desktop) and production deployments (via Docker or Kubernetes) If your API has an OpenAPI spec, you are 90% of the way there before writing a single line of extra code. --- ## Step 1: Understanding What the Server Is Made Of The server is split into three focused components. Getting to know each one makes the whole system much easier to reason about. ### The REST Client ```python class RestClient: def __init__(self, base_url: str, auth_config: Optional[Dict] = None): self.base_url = base_url.rstrip('/') self.auth_config = auth_config or {'type': 'none'} self.session = requests.Session() self._configure_auth() ``` **What it does:** - Creates a `requests.Session` and wires up authentication headers once at startup - Supports four auth modes out of the box: Bearer token, API key header, HTTP Basic, and no auth - Builds the full URL by joining `base_url` with the endpoint path - Returns parsed JSON automatically — and falls back to returning raw text with status code if the response is not JSON **Why a session?** - A `requests.Session` reuses the TCP connection across calls. For APIs with lots of back-to-back requests, this avoids re-establishing the handshake every time — a small but meaningful performance win. --- ### The Schema Manager ```python class SchemaManager: def load_schema(self, spec_url: str) -> bool: ... def get_all_endpoints(self) -> List[Dict]: ... def get_endpoint_info(self, method, path) -> Optional[Dict]: ... def search_endpoints(self, method, search_term, tag) -> List[Dict]: ... ``` **What it does:** - Loads your OpenAPI spec from a remote URL or a local file — both JSON and YAML are handled automatically - Detects whether you are using OpenAPI 3.x or Swagger 2.0 and adjusts parsing accordingly - Walks every path and HTTP method in the spec, building a clean endpoint cache - Resolves `$ref` references inline so nested schemas are usable - Extracts path parameters, query parameters, request body schemas, and response descriptions for each endpoint **When you do not need to think about it:** - This runs automatically on startup. By the time Claude gets its first tool call, the entire API surface is already loaded and cached. --- ### The MCP Server ```python class RestApiMCPServer: async def initialize(self) -> bool: ... def _generate_tools(self): ... async def handle_tool_call(self, tool_name, arguments) -> dict: ... async def run(self): ... ``` **What it does:** - Orchestrates the whole startup sequence: loading config, setting up auth, parsing the spec, and generating tools - Converts every endpoint in the cache into a properly typed MCP tool definition - Routes incoming tool calls — figuring out which endpoint a given tool name maps to, building the URL, injecting parameters, and executing the request - Runs in `stdio` mode for Claude Desktop or `http` mode for containerized deployments - Adds five built-in utility tools for exploring the API without knowing it in advance --- ## Step 2: Installation Getting started takes about thirty seconds: ```bash pip install nife-restapi-mcp-server ``` You will need Python 3.11 or newer. The main dependencies are `requests` for HTTP, `aiohttp` for the optional HTTP server, `pyyaml` for YAML spec support, and `python-dotenv` for `.env` file loading. --- ## Step 3: Configuration — Four Ways to Set It Up <img src={require('./img/rest3.png').default} alt="REST API MCP Configuration Options" width="900" height="450" /> </br> The server is designed to fit into whatever workflow you already have. There are four ways to configure it, and they stack in priority order — whichever method is highest on the list wins if you set the same variable multiple ways. | Priority | Method | Best For | |----------|--------|----------| | 1 | CLI arguments | Quick tests, one-off runs | | 2 | Environment variables | Docker, CI/CD pipelines | | 3 | `.env` file | Local day-to-day development | | 4 | Defaults | Nothing required | ### Method 1 — CLI Arguments The most direct way. Great for testing a new API before committing to a full config: ```bash restapi-mcp-server \ --base-url https://api.example.com \ --spec-url https://api.example.com/openapi.json \ --token mytoken ``` Every available flag: ``` --base-url URL REST API base URL (required if not set via env) --spec-url URL OpenAPI/Swagger spec URL or local file path --token TOKEN Bearer token for authentication --auth-type TYPE Auth method: bearer, apikey, basic, none (default: bearer) --mode stdio|http Server mode (default: stdio) --port PORT HTTP port for http mode (default: 8080) --host HOST HTTP host for http mode (default: 0.0.0.0) --log-level LEVEL DEBUG, INFO, WARNING, or ERROR (default: INFO) --env-file PATH Path to a custom .env file ``` ### Method 2 — Environment Variables Ideal for Docker or any environment where you want config outside the codebase: ```bash export REST_API_BASE_URL=https://api.example.com export OPENAPI_SPEC_URL=https://api.example.com/openapi.json export API_ACCESS_TOKEN=your_token_here export MCP_MODE=stdio restapi-mcp-server ``` ### Method 3 — `.env` File The most comfortable option for local development — create a `.env` in your working directory and forget about it: ```env REST_API_BASE_URL=https://api.example.com OPENAPI_SPEC_URL=https://api.example.com/openapi.json API_ACCESS_TOKEN=your_token_here AUTH_TYPE=bearer MCP_MODE=stdio LOG_LEVEL=INFO ``` Then just run: ```bash restapi-mcp-server ``` ### Method 4 — Claude Desktop Config (the most common production setup) No `.env` file needed at all. Pass everything through the `env` block in `claude_desktop_config.json`: ```json { "mcpServers": { "restapi": { "command": "restapi-mcp-server", "env": { "REST_API_BASE_URL": "https://api.example.com", "OPENAPI_SPEC_URL": "https://api.example.com/openapi.json", "API_ACCESS_TOKEN": "your_token_here", "MCP_MODE": "stdio", "ENABLE_HTTP_ENDPOINT": "false" } } } } ``` Claude Desktop injects these values into the process environment when it starts the server. No files to manage, no secrets in your working directory. --- ## Step 4: Environment Variable Reference Here is the complete list of variables the server understands: | Variable | Required | Default | Description | |----------|----------|---------|-------------| | `REST_API_BASE_URL` | Yes | — | REST API base URL | | `OPENAPI_SPEC_URL` | Yes | — | OpenAPI/Swagger spec URL or local file path | | `AUTH_TYPE` | No | `bearer` | Auth method: `bearer`, `apikey`, `basic`, `none` | | `API_ACCESS_TOKEN` | No | — | Bearer token for authentication | | `API_KEY_HEADER` | No | `X-API-Key` | Header name when using API key auth | | `API_KEY_VALUE` | No | — | The API key value | | `BASIC_AUTH_USERNAME` | No | — | Username for basic auth | | `BASIC_AUTH_PASSWORD` | No | — | Password for basic auth | | `MCP_MODE` | No | `stdio` | `stdio` or `http` | | `ENABLE_HTTP_ENDPOINT` | No | `true` | Enable HTTP health and metrics endpoints | | `MCP_SERVER_PORT` | No | `8080` | HTTP server port | | `MCP_SERVER_HOST` | No | `0.0.0.0` | HTTP server host | | `LOG_LEVEL` | No | `INFO` | Logging verbosity | | `REQUEST_TIMEOUT` | No | `30` | Request timeout in seconds | | `SCHEMA_CACHE_TTL` | No | `3600` | Schema cache TTL in seconds | --- ## Step 5: Authentication — Pick What Fits Your API One of the more practical things about this server is that it handles four common authentication patterns without requiring any custom code. You just set the variables and move on. ### Bearer Token The default and most common pattern for modern APIs: ```env AUTH_TYPE=bearer API_ACCESS_TOKEN=your_token ``` The server attaches `Authorization: Bearer <token>` to every outgoing request. ### API Key Header For APIs that use a custom header instead of Bearer: ```env AUTH_TYPE=apikey API_KEY_HEADER=X-API-Key API_KEY_VALUE=your_key ``` The header name is configurable — so whether your API uses `X-API-Key`, `X-Auth-Token`, or something else, it works. ### HTTP Basic Authentication For APIs that still use username and password: ```env AUTH_TYPE=basic BASIC_AUTH_USERNAME=username BASIC_AUTH_PASSWORD=password ``` Credentials are Base64-encoded and sent in the `Authorization: Basic` header automatically. ### No Authentication For internal APIs or open endpoints: ```env AUTH_TYPE=none ``` --- ## Step 6: How the Spec Gets Parsed When the server starts, the `SchemaManager.load_schema()` method fires and does a lot of work in a short amount of time. First, it figures out where the spec lives: ```python if spec_url.startswith('http://') or spec_url.startswith('https://'): response = requests.get(spec_url, timeout=30) self.schema = yaml.safe_load(response.text) # or response.json() else: with open(spec_url, 'r', encoding='utf-8') as f: self.schema = yaml.safe_load(f) # or json.load(f) ``` Then it validates that the spec is either OpenAPI 3.x or Swagger 2.0. Then it walks every `path` and every HTTP method inside that path, building a clean endpoint record for each one: ```python for path, path_item in paths.items(): for method in ['get', 'post', 'put', 'delete', 'patch', 'head', 'options']: if method not in path_item: continue endpoint = { 'method': method, 'path': path, 'description': operation.get('summary', '...'), 'path_params': [...], 'query_params': [...], 'request_body': {...}, 'responses': {...} } self.endpoints_cache.append(endpoint) ``` **What it does:** - Separates path parameters (like `/users/{id}`) from query parameters (like `?page=2`) - Marks required vs optional fields based on what the spec says - Handles `$ref` resolution so nested schema components are properly expanded - Stores all response codes and their descriptions for documentation purposes By the time this finishes, the server has a complete picture of every operation your API supports. --- ## Step 7: Dynamic Tool Generation <img src={require('./img/rest2.png').default} alt="How REST API MCP Server Works" width="900"ight="450" /> </br> This is where the magic happens. Once the endpoints are parsed, the server converts each one into a typed MCP tool: ```python for endpoint in endpoints: tool_name = self._create_tool_name(endpoint['method'], endpoint['path']) properties = {} required = [] for param in endpoint.get('path_params', []): properties[param['name']] = { 'type': self._convert_type(param.get('type', 'string')), 'description': param.get('description', f"Path parameter: {param['name']}") } if param.get('required', True): required.append(param['name']) for param in endpoint.get('query_params', []): properties[param['name']] = { 'type': self._convert_type(param.get('type', 'string')), 'description': param.get('description', f"Query parameter: {param['name']}") } ``` **What it does:** - Creates a readable tool name from the HTTP method and path — for example, `GET /users/{id}` becomes `get_users_by_id` - Maps OpenAPI types (`integer`, `boolean`, etc.) to JSON Schema types - Marks path parameters as required and query parameters as optional by default, following the spec - Adds a `body` field for POST, PUT, and PATCH endpoints that have a request body **Example tool names generated:** - `GET /users` → `get_users` - `POST /users` → `post_users` - `GET /users/{id}` → `get_users_by_id` - `DELETE /users/{id}` → `delete_users_by_id` - `PATCH /products/{productId}/inventory` → `patch_products_inventory_by_productId` --- <img src={require('./img/rest4.png').default} alt="Dynamic MCP Tool Generation" width="900" height="450" /> </br> ## Step 8: Built-in Utility Tools Beyond your API's own endpoints, the server ships with five utility tools that make working with unfamiliar APIs much easier: - `list_available_endpoints` — lists all endpoints, with optional filtering by HTTP method or path search term - `get_endpoint_info` — returns the full details for a specific `method + path` combination: parameters, request body schema, response codes, everything - `get_schema_info` — gives an overview of the API (title, version, total endpoint count) or deep-dives into a specific model from the spec - `execute_custom_request` — lets you fire any HTTP request with full control over method, path, query params, body, and extra headers - `health_check` — reports server status, whether the schema is loaded, and how many tools and endpoints are available **A practical example of how these get used:** You drop Claude into a project it has never seen. You ask it to "check what endpoints are available." Claude calls `list_available_endpoints`, sees a `POST /invoices` route, calls `get_endpoint_info` to understand what fields the body needs, and then calls `post_invoices` to actually create one — all without you writing a single line of tool code. --- ## Step 9: Operating Modes ### stdio Mode — for Claude Desktop and Local Development ```bash restapi-mcp-server \ --base-url https://api.example.com \ --spec-url https://api.example.com/openapi.json \ --mode stdio ``` **What it does:** - Communicates over `stdin` and `stdout` using the MCP JSON-RPC protocol - No HTTP server is started — the process is entirely driven by the MCP client - This is the default mode and the right choice for Claude Desktop, Cursor, and similar tools ### HTTP Mode — for Docker and Production ```bash restapi-mcp-server \ --base-url https://api.example.com \ --spec-url https://api.example.com/openapi.json \ --mode http \ --port 8080 ``` **What it does:** - Starts an `aiohttp` server and keeps the process alive indefinitely - Exposes six HTTP endpoints for operational visibility: ``` GET / → server info and available endpoints GET /health → health status and schema load state GET /metrics → uptime, tool counts, endpoint counts GET /schema → OpenAPI schema summary GET /tools → all generated tools with type labels GET /api/endpoints → all API endpoints with method and description ``` --- ## Step 10: Docker Deployment ```bash docker build -t restapi-mcp-server . docker run -p 8080:8080 \ -e REST_API_BASE_URL=https://api.example.com \ -e OPENAPI_SPEC_URL=https://api.example.com/openapi.json \ -e API_ACCESS_TOKEN=your_token \ -e MCP_MODE=http \ restapi-mcp-server ``` Or with Docker Compose (the easier approach for multi-service setups): ```bash cp .env.example .env # fill in your values docker-compose up ``` ⚠️ Important: Always set `MCP_MODE=http` in Docker. In `stdio` mode, the server waits for stdin input that never comes in a detached container — and exits immediately. --- ## Real Scenario Here is what a full end-to-end session actually looks like: ```bash restapi-mcp-server \ --base-url https://myapp.com/api \ --spec-url https://myapp.com/api/openapi.json \ --mode stdio ``` The server boots and parses the spec: ``` ✓ OpenAPI schema loaded successfully - API Title: My Application API - API Version: v2.3.1 - 87 endpoints - 34 schemas ✓ Server ready with 92 tools! ``` Claude opens the conversation and calls `list_available_endpoints` with `method: "POST"`. It finds `POST /orders`, calls `get_endpoint_info` to check what fields are needed, then fires: ```python # Claude calls: post_orders { "body": { "customerId": "cust_abc123", "items": [{"productId": "prod_xyz", "quantity": 2}], "shippingAddress": "123 Main St" } } ``` The `RestClient` builds the request, attaches the Bearer token, hits `POST https://myapp.com/api/orders`, and returns the response — all transparently. --- ## Best Practices - Keep `SCHEMA_CACHE_TTL` set to avoid re-parsing the spec on every request in long-running deployments - Use `list_available_endpoints` with a method filter first — it is faster than browsing a long unfiltered list - Use `execute_custom_request` for complex calls that need extra headers or non-standard formatting - Always use HTTP mode in Docker — stdio mode will not work in a detached container - If your spec uses `$ref` heavily, make sure the spec URL returns the fully resolved document, not just fragments - Check `health_check` first when debugging — it confirms whether the schema loaded successfully --- ## Troubleshooting **`[ERROR] REST_API_BASE_URL not configured`** The base URL is missing. Fix it with: ```bash restapi-mcp-server --base-url https://your-api.com ``` **`[ERROR] OPENAPI_SPEC_URL not configured`** The spec URL is missing. Fix it with: ```bash restapi-mcp-server --spec-url https://your-api.com/openapi.json ``` **Schema not loading** Three things to check: confirm the spec URL is reachable from where the server is running, verify the response is valid JSON or YAML, and make sure the document declares either `openapi: "3.x.x"` or `swagger: "2.0"` at the top level. **Docker container exits immediately** You are running in stdio mode inside Docker. Switch to HTTP mode: ```bash docker run -e MCP_MODE=http -e REST_API_BASE_URL=... -e OPENAPI_SPEC_URL=... restapi-mcp-server ``` **Port already in use** Change the port: ```bash restapi-mcp-server --mode http --port 9090 ``` --- ## Conclusion REST APIs are how most of the world's data and functionality is exposed. The REST API MCP Server makes that entire surface area available to Claude — not just the endpoints you remembered to write tools for, but every single route in your spec, automatically, the moment the server starts. There is something genuinely satisfying about pointing the server at a new API, watching it parse the spec in a few seconds, and then having Claude immediately explore, query, and interact with it — all driven by the OpenAPI documentation your team already wrote. If your team maintains an OpenAPI spec (and in 2026, most do), this server turns that spec into a fully functional AI tool layer at zero extra cost. Learn more about the MCP protocol: https://modelcontextprotocol.io