Building a REST API MCP Server | Give Claude Superpowers Any REST API
REST API MCP server that reads your OpenAPI/Swagger spec and auto-generates every endpoint as an AI-callable tool — no hardcoding.

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
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.Sessionand 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_urlwith 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.Sessionreuses 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
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
$refreferences 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
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
stdiomode for Claude Desktop orhttpmode 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:
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
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:
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:
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:
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:
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:
{
"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:
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:
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:
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:
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:
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:
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
$refresolution 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
This is where the magic happens. Once the endpoints are parsed, the server converts each one into a typed MCP tool:
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}becomesget_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
bodyfield for POST, PUT, and PATCH endpoints that have a request body
Example tool names generated:
GET /users→get_usersPOST /users→post_usersGET /users/{id}→get_users_by_idDELETE /users/{id}→delete_users_by_idPATCH /products/{productId}/inventory→patch_products_inventory_by_productId
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 termget_endpoint_info— returns the full details for a specificmethod + pathcombination: parameters, request body schema, response codes, everythingget_schema_info— gives an overview of the API (title, version, total endpoint count) or deep-dives into a specific model from the specexecute_custom_request— lets you fire any HTTP request with full control over method, path, query params, body, and extra headershealth_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
restapi-mcp-server \
--base-url https://api.example.com \
--spec-url https://api.example.com/openapi.json \
--mode stdio
What it does:
- Communicates over
stdinandstdoutusing 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
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
aiohttpserver 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
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):
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:
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:
# 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_TTLset to avoid re-parsing the spec on every request in long-running deployments - Use
list_available_endpointswith a method filter first — it is faster than browsing a long unfiltered list - Use
execute_custom_requestfor 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
$refheavily, make sure the spec URL returns the fully resolved document, not just fragments - Check
health_checkfirst 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:
restapi-mcp-server --base-url https://your-api.com
[ERROR] OPENAPI_SPEC_URL not configured
The spec URL is missing. Fix it with:
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:
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:
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
From the team at Nife
Deploy your next app in minutes, not days.
Nife is a multi-cloud edge platform that lets you deploy, scale, and manage applications globally without the infrastructure headache.