Muhammed-AbdelGhany/rest_api_mcp
login, token caching, 2FA/OTP support, Swagger spec fetch, and fuzzy endpoint search. Works with any REST API without writing auth code.
Ask AI about Muhammed-AbdelGhany/rest_api_mcp
Powered by Claude Β· Grounded in docs
I know everything about Muhammed-AbdelGhany/rest_api_mcp. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
rest-api-mcp
A Model Context Protocol (MCP) server for authenticated REST APIs.
Drop it into any project, point it at your API, and let AI agents call endpoints β with auto-login, 2FA support, Swagger spec fetch, and fuzzy endpoint search β all without writing a single line of auth code.
Table of Contents
- Features
- Installation
- Quick Start
- Configuration
- Tools
- Authentication Flows
- Multi-API Setup
- VS Code mcp.json Examples
- How It Works
- Environment Variables Reference
- Troubleshooting
Features
| Capability | Description |
|---|---|
| Auto-login | Logs in automatically before every request; re-logins when token expires |
| Token caching | 20-second TTL cache β survives rapid sequential calls |
| Auto-discovery | Finds the login endpoint by scanning the Swagger spec (no config needed) |
| Auto token detection | Tries 9 common token paths (data.access_token, accessToken, token, β¦) |
| AI-driven token detection | inspect_login tool exposes raw responses + heuristic suggestions so the AI can pick the exact token path |
| 2FA / OTP support | Two-step auth: login β verify-otp, session identifiers forwarded automatically |
| Custom session fields | Override hardcoded session candidates via verify_session_fields in request() |
| Extra login fields | source, userRole, channel, device_id β any field, via JSON env var |
| Fuzzy endpoint search | Find endpoints by keyword across path, summary, description, tags, operationId |
| Swagger spec fetch | Retrieve and inspect the full OpenAPI spec |
| SSL bypass | Optional for staging/dev environments with self-signed certs |
| Response truncation | Configurable size limit to keep responses in context |
Installation
Option A β Use via npx (recommended)
No installation needed. Add this to your project's .vscode/mcp.json and VS Code will download and run the package automatically:
{
"command": "npx",
"args": ["-y", "rest-api-mcp"]
}
This always uses the latest published version from npm. See VS Code mcp.json Examples for a full config.
Option B β Use locally (for development / offline)
git clone https://github.com/Muhammed-AbdelGhany/rest_api_mcp
cd rest_api_mcp
npm install && npm run build
Then point VS Code at the local build:
{
"command": "node",
"args": ["/path/to/rest_api_mcp/build/index.js"]
}
Quick Start
Add this to your project's .vscode/mcp.json:
{
"servers": {
"my-api": {
"command": "npx",
"args": ["-y", "rest-api-mcp"],
"env": {
"REST_BASE_URL": "https://api.example.com/api/v1",
"API_EMAIL": "user@example.com",
"API_PASSWORD": "yourpassword",
"API_SWAGGER_URL": "https://api.example.com/docs-json"
}
}
}
}
That's it. The agent can now:
- Search for endpoints by keyword
- Call any endpoint with automatic authentication
- Fetch the full OpenAPI spec for schema inspection
Configuration
All configuration is done via environment variables in mcp.json. No code changes required.
Minimum required
| Variable | Description |
|---|---|
REST_BASE_URL | Base URL of the API (no trailing slash) |
API_EMAIL | Login email |
API_PASSWORD | Login password |
Strongly recommended
| Variable | Description |
|---|---|
API_SWAGGER_URL | OpenAPI/Swagger JSON URL β enables fetch_spec, search_endpoints, and auto-login-endpoint discovery |
Optional
See Environment Variables Reference for the full list.
Tools
search_endpoints
Fuzzy-search the API spec by keyword. Returns matching endpoints with method, path, summary, tags, and required parameters. Use this before request when you don't know the exact path.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
query | string | β | Keywords to search for |
limit | number | β | Max results (default: 10) |
Example β Find order-related endpoints:
search_endpoints("orders list customer")
Response:
Found 6 match(es) for "orders list customer", showing top 5:
1. GET /customers/{id}/orders
Summary: List all orders for a customer
Tags: Orders, Customers
Required params: path:id
2. GET /orders
Summary: List orders with optional filters
Tags: Orders
Required params: query:status, query:page
3. POST /orders/search
Summary: Search orders by multiple criteria
Tags: Orders
Required params: body
...
describe_endpoint
Returns the full OpenAPI schema for a single endpoint: parameters, request body schema (with types, required flags, enums, examples), response schemas, and a generated example request body. Use this before request() when you need to know exactly what fields to include in the body or what response shape to expect.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
method | string | β | GET, POST, PUT, PATCH, DELETE |
endpoint | string | β | Path relative to REST_BASE_URL, e.g. /inspections |
Example β Inspect a POST endpoint before calling it:
describe_endpoint("POST", "/pharmacy/add-manager")
Response:
{
"method": "POST",
"path": "/pharmacy/add-manager",
"summary": "Add a manager to a pharmacy",
"parameters": [
{ "name": "pharmacyId", "in": "path", "required": true, "type": "string" }
],
"requestBody": {
"contentType": "application/json",
"schema": {
"type": "object",
"properties": {
"first_name": { "type": "string", "required": true },
"email": { "type": "string", "required": true },
"gender": { "type": "string", "enum": ["male", "female"], "required": true },
"start_date": { "type": "string", "format": "date-time", "required": true }
}
},
"example": {
"first_name": "First name",
"email": "manager@example.com",
"gender": "male",
"start_date": "2026-05-04T12:00:00Z"
}
},
"responses": {
"201": {
"description": "Manager added successfully",
"schema": { "type": "object", "properties": { "id": { "type": "number" } } },
"example": { "id": 42 }
}
}
}
The AI can now call request() with the exact body shape, no guessing required.
request
Make an authenticated API call. Handles login automatically β re-logins transparently if the token is expired.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
method | string | β | GET, POST, PUT, PATCH, DELETE |
endpoint | string | β | Path relative to REST_BASE_URL, e.g. /users/profile |
body | object | β | Request body for POST/PUT/PATCH |
headers | object | β | Extra headers to merge |
skip_auth | boolean | β | Set true to skip the Authorization header |
token_path | string | β | Dot-notation path to the token in the login/verify response (e.g. data.result.accessToken). Overrides auto-detection and is cached for re-logins. |
verify_session_fields | object | β | Map of verify-body field names β dot-notation paths in the step-1 login response. Example: {"sessionId": "data.result.sessionId"}. Overrides hardcoded candidates and is cached for re-logins. |
Response shape:
{
"status": 200,
"statusText": "OK",
"timing_ms": 312,
"login_data": { ... },
"response": { ... }
}
login_datacontains the full login response β useful for IDs likeuserId,orgId,tenantIdreturned at login that you need for subsequent requests.
Example β GET current user profile:
request("GET", "/users/me")
Example β POST with filters:
request("POST", "/orders/search", {
"status": "pending",
"from": "2025-01-01",
"limit": 20
})
Example β PATCH to update a resource:
request("PATCH", "/products/42", {
"price": 9.99,
"inStock": true
})
Example β Public endpoint (no auth):
request("GET", "/health", skip_auth=true)
Example β Custom token path (when auto-detection fails):
request("GET", "/orders", token_path="result.data.jwtToken")
Example β Custom 2FA session fields:
request("GET", "/orders",
token_path="data.result.accessToken",
verify_session_fields={"sessionId": "data.result.sessionId", "requestToken": "data.result.requestToken"}
)
inspect_login
Performs the login flow (and optional 2FA verify) and returns the raw server responses without extracting a token. Also returns heuristic suggestions for:
- Token paths (fields that look like JWTs or long auth strings)
- Session fields (fields that look like session identifiers for 2FA verify)
Use this when auto-detection fails so the AI can identify the correct token_path and verify_session_fields to pass to request().
No input required.
Example β when request() fails with "Could not find token":
inspect_login()
Response:
{
"step1": { "status": 200, "data": { "result": { "customJwt": "eyJ...", "sessionId": "abc" } } },
"step2": null,
"token_suggestions": [
{ "path": "result.customJwt", "value_preview": "eyJhbGciOiJIUzI1Ni...", "confidence": 4 }
],
"session_field_suggestions": [
{ "path": "result.sessionId", "key": "sessionId", "value_preview": "abc" }
],
"note": "Use token_path and verify_session_fields in your next request() call."
}
Then call request() with the AI-discovered path:
request("GET", "/orders", token_path="result.customJwt")
The server caches the AI-provided token_path and verify_session_fields so re-logins (after token expiry) use them automatically.
fetch_spec
Fetch the full OpenAPI/Swagger JSON spec for schema inspection, DTO discovery, or understanding available endpoints.
Input:
| Field | Type | Required | Description |
|---|---|---|---|
url | string | β | Override spec URL. Falls back to API_SWAGGER_URL env var |
Example:
fetch_spec()
Returns the raw OpenAPI JSON (truncated to REST_RESPONSE_SIZE_LIMIT if large).
Authentication Flows
Standard login
The most common case β email + password, token returned directly.
{
"REST_BASE_URL": "https://api.example.com/api/v1",
"API_EMAIL": "user@example.com",
"API_PASSWORD": "secret",
"API_SWAGGER_URL": "https://api.example.com/docs-json"
}
The server auto-discovers the login endpoint by scanning the Swagger spec for the first POST path containing "login". Override if needed:
"API_LOGIN_ENDPOINT": "/auth/sign-in"
Login with extra credentials
Some APIs require fields beyond email and password in the login request body β for example a role to specify what type of user is logging in, a source to indicate which client platform is making the request, a channel, a tenantId, etc.
Set API_LOGIN_CREDENTIALS to a JSON object string containing any extra fields you need. They are merged into the login POST body alongside email and password:
"API_LOGIN_CREDENTIALS": "{\"role\": \"admin\"}"
What gets sent to the login endpoint:
{
"email": "admin@acme.com",
"password": "secret",
"role": "admin"
}
Multiple extra fields work the same way:
"API_LOGIN_CREDENTIALS": "{\"role\": \"viewer\", \"source\": \"web\", \"tenantId\": \"acme\"}"
What gets sent:
{
"email": "viewer@acme.com",
"password": "secret",
"role": "viewer",
"source": "web",
"tenantId": "acme"
}
Note: The field names are entirely up to your API. Check its Swagger spec or docs to see what the login endpoint accepts.
Two-factor authentication (2FA)
Some APIs require a second verification step after the initial login β the server returns a one-time code to the user's email or phone, and you must submit it to a separate endpoint to receive the actual JWT.
Flow:
Step 1 β Login
POST /auth/login { email, password }
β 200: { session_token: "tmp_abc", message: "OTP sent to email" }
Step 2 β Verify OTP
POST /auth/verify-otp { email, otp: "482019", session_token: "tmp_abc" }
β 200: { accessToken: "eyJhbGci..." }
The three env vars that drive this:
"API_VERIFY_ENDPOINT": "/auth/verify-otp",
"API_OTP": "482019",
"API_LOGIN_CREDENTIALS": "{\"platform\": \"web\"}"
API_VERIFY_ENDPOINT β The path of the second step. When this is set, the server automatically performs both steps before attaching a token to your request.
API_OTP β The OTP value to submit. For staging environments this is usually a fixed test code provided by the API team. For production you'd need to retrieve the live code from your email and set it here.
Session carry-forward β Session identifiers returned by login step 1 (e.g. session_token, requestId, temp_token, nonce, transactionId) are automatically detected and forwarded to the verify endpoint. You do not need to configure this manually.
The full body sent to the verify endpoint looks like:
{
"email": "user@acme.com",
"otp": "482019",
"session_token": "tmp_abc" β auto-carried from step 1
}
API_VERIFY_CREDENTIALS β If your verify endpoint requires extra fields that aren't session identifiers or the OTP, add them here:
"API_VERIFY_CREDENTIALS": "{\"client_id\": \"web-app\"}"
What gets sent:
{
"email": "user@acme.com",
"otp": "482019",
"session_token": "tmp_abc",
"client_id": "web-app" β from API_VERIFY_CREDENTIALS
}
Multi-API Setup
Run multiple independent server instances β one per API β in the same mcp.json. Each instance runs its own auth session, token cache, and spec cache independently.
In this example, shop-api uses a simple role-based login and analytics-api uses 2FA:
{
"servers": {
"shop-api": {
"command": "npx",
"args": ["-y", "rest-api-mcp"],
"env": {
"REST_BASE_URL": "https://api.acme-shop.com/v1",
"API_EMAIL": "admin@acme-shop.com",
"API_PASSWORD": "s3cr3t",
"API_LOGIN_CREDENTIALS": "{\"role\": \"admin\"}",
"API_SWAGGER_URL": "https://api.acme-shop.com/docs-json"
}
},
"analytics-api": {
"command": "npx",
"args": ["-y", "rest-api-mcp"],
"env": {
"REST_BASE_URL": "https://analytics.acme.com/api/v2",
"API_EMAIL": "analyst@acme.com",
"API_PASSWORD": "s3cr3t",
"API_LOGIN_ENDPOINT": "/auth/sign-in",
"API_VERIFY_ENDPOINT": "/auth/verify-otp",
"API_OTP": "482019",
"API_SWAGGER_URL": "https://analytics.acme.com/openapi.json"
}
}
}
}
VS Code mcp.json Examples
Minimal
{
"servers": {
"my-api": {
"command": "npx",
"args": ["-y", "rest-api-mcp"],
"env": {
"REST_BASE_URL": "https://api.example.com/v1",
"API_EMAIL": "user@example.com",
"API_PASSWORD": "secret"
}
}
}
}
Full (all options)
{
"servers": {
"my-api": {
"command": "npx",
"args": ["-y", "rest-api-mcp"],
"env": {
"REST_BASE_URL": "https://api.example.com/v1",
"REST_ENABLE_SSL_VERIFY": "false",
"REST_RESPONSE_SIZE_LIMIT": "150000",
"API_EMAIL": "user@example.com",
"API_PASSWORD": "secret",
"API_LOGIN_ENDPOINT": "/auth/login",
"API_LOGIN_CREDENTIALS": "{\"source\":\"mobile\"}",
"API_VERIFY_ENDPOINT": "/auth/verify-otp",
"API_OTP": "123456",
"API_VERIFY_CREDENTIALS": "{\"device_id\":\"abc\"}",
"API_TOKEN_PATH": "data.access_token",
"API_SWAGGER_URL": "https://api.example.com/docs-json"
}
}
}
}
How It Works
Agent says: "show me pending orders"
β
βΌ
search_endpoints("orders pending list")
β Fetches Swagger spec, scores every endpoint by keyword match
β Returns: GET /orders β best match
βΌ
request("GET", "/orders?status=pending")
β
ββ Token cache valid? ββyesβββΊ attach Bearer token
β
ββ Cache expired/empty?
β
ββ Step 1: POST /auth/login {email, password, ...LOGIN_CREDENTIALS}
β βββ 200: {data: {access_token: "eyJ..."}}
β
ββ [if VERIFY_ENDPOINT set]
β Step 2: POST /auth/verify-otp {email, otp, ...session_tokens}
β βββ 200: {accessToken: "eyJ..."}
β
ββ Auto-detect token path from response (or use AI-provided token_path)
ββ Cache token for 20s
ββ attach Bearer token
β
βΌ
GET /orders?status=pending
Authorization: Bearer eyJ...
βββ 200: {total: 47, data: [{id: 1, status: "pending", ...}, ...]}
When auto-detection fails:
request("GET", "/orders") β "Could not find token"
β
βΌ
inspect_login()
β Returns raw login response + token/session suggestions
βΌ
request("GET", "/orders", token_path="data.result.jwt")
β Token path cached for future re-logins
βΌ
β
Success
Environment Variables Reference
| Variable | Required | Default | Description |
|---|---|---|---|
REST_BASE_URL | β | β | Base API URL, no trailing slash |
API_EMAIL | β * | β | Login email (*required for authenticated endpoints) |
API_PASSWORD | β * | β | Login password |
API_SWAGGER_URL | β | β | OpenAPI JSON URL for fetch_spec, search_endpoints, and login auto-discovery |
API_LOGIN_ENDPOINT | β | auto-discovered | Override login path, e.g. /auth/sign-in |
API_LOGIN_CREDENTIALS | β | β | JSON object of extra fields merged into the login POST body alongside email/password. Use for role, source, tenantId, etc. Example: {"role":"admin"} |
API_VERIFY_ENDPOINT | β | β | Path of the 2FA/OTP verify step. Setting this enables two-step auth. Example: /auth/verify-otp |
API_OTP | β | β | The OTP code to submit to API_VERIFY_ENDPOINT. On staging this is typically a fixed test code. |
API_VERIFY_CREDENTIALS | β | β | JSON object of extra fields merged into the verify POST body, beyond the auto-carried session identifiers and OTP. Example: {"client_id":"web-app"} |
API_TOKEN_PATH | β | auto-detected | Dot-path to token in login/verify response, e.g. data.access_token |
REST_ENABLE_SSL_VERIFY | β | true | Set false to skip TLS cert validation (dev/staging only) |
REST_RESPONSE_SIZE_LIMIT | β | 100000 | Max response characters before truncation |
Auto-detected token paths (tried in order):
data.access_token Β· access_token Β· data.token Β· token Β· data.accessToken Β· accessToken Β· data.data.access_token Β· result.access_token Β· result.token
If none match, use inspect_login() to discover the correct path and pass it via token_path.
Auto-forwarded session fields (2FA step 1 β step 2):
session_token Β· sessionToken Β· session Β· request_id Β· requestId Β· temp_token Β· tempToken Β· verification_token Β· verificationToken Β· challenge Β· nonce Β· transaction_id Β· transactionId
Override these via verify_session_fields when the API uses non-standard session field names.
Troubleshooting
Login failed: Could not find token
The login response uses an unusual token path. Use inspect_login() to see the raw response and heuristic suggestions, then pass the correct path to request():
inspect_login() β see suggestions
request("GET", "/orders", token_path="result.data.jwt")
Alternatively, set API_TOKEN_PATH explicitly in env:
"API_TOKEN_PATH": "result.data.jwt"
2FA verify fails with 401
The verify endpoint may need the OTP as a different field name. Use API_VERIFY_CREDENTIALS:
"API_VERIFY_CREDENTIALS": "{\"code\": \"123456\"}"
And leave API_OTP unset if the field name isn't otp.
search_endpoints returns no matches
- Make sure
API_SWAGGER_URLis set and reachable - Try broader keywords:
"inventory"instead of"getInventory" - The spec may be truncated β use
fetch_specto check
SSL errors on staging
"REST_ENABLE_SSL_VERIFY": "false"
Response truncated
Increase the limit:
"REST_RESPONSE_SIZE_LIMIT": "500000"
License
MIT
