Rest API

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.

Varun S
Varun S
15 min read
Building a REST API MCP Server | Give Claude Superpowers  Any REST API

REST API MCP Server

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.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

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

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:

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.

PriorityMethodBest For
1CLI argumentsQuick tests, one-off runs
2Environment variablesDocker, CI/CD pipelines
3.env fileLocal day-to-day development
4DefaultsNothing 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:

VariableRequiredDefaultDescription
REST_API_BASE_URLYes—REST API base URL
OPENAPI_SPEC_URLYes—OpenAPI/Swagger spec URL or local file path
AUTH_TYPENobearerAuth method: bearer, apikey, basic, none
API_ACCESS_TOKENNo—Bearer token for authentication
API_KEY_HEADERNoX-API-KeyHeader name when using API key auth
API_KEY_VALUENo—The API key value
BASIC_AUTH_USERNAMENo—Username for basic auth
BASIC_AUTH_PASSWORDNo—Password for basic auth
MCP_MODENostdiostdio or http
ENABLE_HTTP_ENDPOINTNotrueEnable HTTP health and metrics endpoints
MCP_SERVER_PORTNo8080HTTP server port
MCP_SERVER_HOSTNo0.0.0.0HTTP server host
LOG_LEVELNoINFOLogging verbosity
REQUEST_TIMEOUTNo30Request timeout in seconds
SCHEMA_CACHE_TTLNo3600Schema 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 $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

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} 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

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

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

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

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_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:

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.