OpenRouter MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OpenRouter MCP ServerFind the cheapest model for summarizing long documents."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
OpenRouter MCP Server
Remote MCP server (streamable HTTP, stateless JSON) that lets AI agents delegate tasks to cheaper models via the OpenRouter API, querying the catalog and live prices, with a cost policy configurable through a .env file.
What it does
Live catalog: queries OpenRouter's
GET /api/v1/models(with a 5-minute cache) and exposes prices in USD per million tokens, context window, and tool-calling support.Explicit delegation: the agent picks the model by looking at prices and delegates the task.
Automatic price-based delegation: the server picks the model based on a tier (
economy/balanced/quality) using configurable price bands.Policy via
.env: max price caps, allowed/blocked model lists, default model, preferred providers.Real cost: every delegation returns tokens used and estimated cost in USD.
No truncated answers: the server sizes the output budget itself and resumes answers that hit the token limit, so the calling agent never has to guess
max_tokens(see Output budget).
Related MCP server: whichmodel-mcp
Installation
npm install
cp .env.example .env # edit and set your OPENROUTER_API_KEY
npm run build
npm start # listens on http://localhost:3000/mcpFor development with auto-reload: npm run dev.
Docker
A multi-arch image (linux/amd64, linux/arm64) is built automatically by GitHub Actions and published to GHCR:
ghcr.io/vmhq/openrouter-mcp-serverAvailable tags: latest (main branch), vX.Y.Z / X.Y (releases), main, and sha-<commit>.
Docker Compose
services:
openrouter-mcp:
image: ghcr.io/vmhq/openrouter-mcp-server:latest
container_name: openrouter-mcp
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
volumes:
# Persists OAuth state (registered clients, token hashes)
- ./data:/app/data
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3docker compose up -dNote: the container runs as the unprivileged
nodeuser. Make sure the mounted./datadirectory is writable by UID 1000 (chown -R 1000:1000 ./data), otherwise OAuth state cannot be persisted.
Example .env
# ============================================================
# OpenRouter MCP Server - environment variables
# Copy this file to .env and fill in the values.
# ============================================================
# --- Required ---
# Your OpenRouter API key (https://openrouter.ai/keys)
OPENROUTER_API_KEY=sk-or-v1-...
# --- HTTP server ---
# Port where the MCP endpoint is exposed (http://host:PORT/mcp)
PORT=3000
# Optional token protecting the remote server. If set, MCP clients must send
# "Authorization: Bearer <token>". Strongly recommended if the server is
# reachable outside localhost.
MCP_AUTH_TOKEN=
# --- Interactive OAuth with PocketID (for AI agents like Claude) ---
# Public URL of this server (e.g. https://mcp.example.com). Required so the
# OAuth metadata and callback point to the right URL behind a reverse proxy.
MCP_PUBLIC_URL=
# When all three POCKETID_* variables are set, the /oauth/authorize flow
# delegates the human login to your PocketID instance (passkey).
# In PocketID: create an OIDC client and register this callback:
# <MCP_PUBLIC_URL>/oauth/callback
# Restrict who can sign in with the OIDC client's allowed groups in PocketID.
# The static MCP_AUTH_TOKEN keeps working in parallel for machine-to-machine
# access.
POCKETID_ISSUER=
POCKETID_CLIENT_ID=
POCKETID_CLIENT_SECRET=
# Optional OIDC scopes (space-separated). Default: "openid profile email".
# POCKETID_SCOPES=openid profile email
# Path of the file where OAuth state is persisted (registered clients, codes
# and token hashes). Default: ./data/oauth-state.json
# MCP_OAUTH_STATE_PATH=./data/oauth-state.json
# OAuth access token lifetime, in seconds. Default: 2592000 (30 days).
# MCP_OAUTH_TOKEN_TTL_S=2592000
# --- Optional OpenRouter attribution (rankings) ---
APP_URL=
APP_TITLE=OpenRouter MCP Server
# --- Delegation policy ---
# Default model when the agent doesn't specify one in openrouter_delegate_task
DEFAULT_MODEL=
# Price caps (USD per million tokens). Models above them are rejected with an
# explanatory error. Empty = no limit.
MAX_PROMPT_PRICE_PER_M=
MAX_COMPLETION_PRICE_PER_M=
# Comma-separated control lists. Accept exact ids ("openai/gpt-4.1-mini") or
# provider prefixes ("openai/"). Empty ALLOWED_MODELS = all allowed (except
# the blocked ones).
ALLOWED_MODELS=
BLOCKED_MODELS=
# Allow free models (price 0)? They usually have strict rate limits.
ALLOW_FREE_MODELS=true
# Preferred providers for automatic selection (openrouter_auto_delegate)
PREFERRED_PROVIDERS=openai,anthropic,google,meta-llama,mistralai,deepseek,qwen,x-ai,amazon
# "Blended" price caps (70% prompt + 30% completion, USD/M tokens) for each
# tier of the automatic selection.
TIER_ECONOMY_MAX_PRICE=0.5
TIER_BALANCED_MAX_PRICE=3
TIER_QUALITY_MAX_PRICE=15
# Model catalog cache, in seconds
MODELS_CACHE_TTL_SECONDS=300
# --- Output budget (truncated-answer control) ---
# The calling agent should not have to guess max_tokens: the server derives a
# budget from each model's context window and per-request output cap, and
# resumes answers that get cut off. These knobs bound that behaviour.
# Completion budget used when the agent does not pass max_tokens.
DEFAULT_MAX_TOKENS=4096
# Floor applied on reasoning models, whose budget is consumed by internal
# chain-of-thought before any visible text is produced.
REASONING_MIN_MAX_TOKENS=2000
# Hard ceiling for a single delegation, summed across auto-continuations.
# This is the real cost guard: it bounds how much a runaway answer can spend.
MAX_OUTPUT_TOKENS=32000
# How many times an answer cut off by the token limit may be auto-resumed.
# Set to 0 to disable auto-continuation server-wide.
MAX_CONTINUATIONS=3
# Max characters returned inline in one tool result. Longer answers are
# returned as a first page plus a response_id for openrouter_fetch_response.
MAX_RESPONSE_CHARS=25000Environment variables
See .env.example — the main ones:
Variable | Description |
| Required. Your key from https://openrouter.ai/keys |
| HTTP port (default 3000) |
| If set, clients must send |
| Public URL of the server (e.g. |
| Enable interactive OAuth login by delegating authentication to your PocketID instance (see below) |
| Model used by |
| Price ceiling (USD/M tokens); more expensive models are rejected |
| Comma-separated lists: exact ids or prefixes ( |
| Allow free models (default |
| Combined price ceilings (0.7·input + 0.3·output) for each tier of the automatic selection |
| Completion budget when the agent doesn't pass |
| Budget floor for reasoning models (default 2000) |
| Ceiling for one delegation across all continuations (default 32000) — the real cost guard |
| How many times a cut-off answer is auto-resumed (default 3; |
| Inline size limit of a tool result before paging kicks in (default 25000) |
Exposed tools
Tool | Description |
| Lists models with live prices; filters by text, price, context, tool-calling; sort by price/context/recency; paginated |
| Full detail of a model + whether the |
| Delegates a task to a specific model; returns response, tokens, and estimated cost |
| The server picks the model by price tier ( |
| Reads the remaining pages of a delegated answer too large to return inline |
| Usage and limits of the configured API key |
Typical agent flow: openrouter_list_models (or directly openrouter_auto_delegate with the economy tier) → delegate the task → use the response, knowing how much it cost.
Important: the delegated model does not see the agent's conversation; the task (task) must be self-contained, with all the necessary context.
Connecting an agent
Claude Code:
claude mcp add --transport http openrouter http://localhost:3000/mcpWith an auth token:
claude mcp add --transport http openrouter http://YOUR_HOST:3000/mcp --header "Authorization: Bearer YOUR_TOKEN"Any MCP client: point it at the POST /mcp endpoint with the "streamable HTTP" transport. There's a GET /health endpoint for monitoring.
claude.ai (remote connector): requires a public HTTPS URL — deploy the server on a VPS behind a reverse proxy (Caddy/nginx) or use a tunnel (e.g. cloudflared tunnel). With OAuth enabled (see below), add the connector pointing to https://YOUR_HOST/mcp and leave the advanced OAuth Client ID/Secret fields empty: the server publishes OAuth metadata and supports Dynamic Client Registration, so Claude registers itself and obtains its token automatically when you click Authorize.
OAuth with PocketID
The server implements full OAuth 2.1 for AI agents (Claude, Cursor, …): it acts as the authorization server towards MCP clients (RFC 7591 Dynamic Client Registration + PKCE S256 + issuing its own tokens, with RFC 8414/9728 metadata) and delegates the human login to your PocketID instance via OIDC (passkey).
Flow: the MCP client receives a 401 with WWW-Authenticate → discovers the metadata at /.well-known/oauth-protected-resource → registers at /oauth/register → opens /oauth/authorize in the browser → the user signs in to PocketID with their passkey → PocketID returns to /oauth/callback → the server issues its own code and the client exchanges it at /oauth/token for an access token (30 days by default).
Setup:
In PocketID, create a new OIDC client.
Register the callback:
<MCP_PUBLIC_URL>/oauth/callback.Restrict who can sign in using the OIDC client's allowed groups in PocketID.
Copy the Client ID and Client Secret into
POCKETID_CLIENT_ID/POCKETID_CLIENT_SECRET, and set the PocketID base URL inPOCKETID_ISSUER.Set
MCP_PUBLIC_URLto the server's public HTTPS URL.
If the POCKETID_* variables are not set, the interactive /oauth/authorize flow shows an error; the static MCP_AUTH_TOKEN bearer keeps working in parallel for machine-to-machine access (curl, Codex, etc.).
OAuth state (registered clients, one-time codes, and SHA-256 hashes of the tokens — never the plaintext tokens) is persisted to ./data/oauth-state.json (configurable via MCP_OAUTH_STATE_PATH). If the connector fails after a restart with wiped state, remove it in Claude and add it again so it re-registers.
Output budget (no more truncated answers)
Picking max_tokens is the calling agent's most common failure mode: too low and the answer is cut off mid-sentence, and on reasoning models the whole budget goes to hidden chain-of-thought so the answer comes back empty. The server takes that decision away from the agent.
The budget is derived, not guessed. If the call omits
max_tokens, the server starts fromDEFAULT_MAX_TOKENSand clamps it to what the model actually accepts: the provider's per-request output cap (top_provider.max_completion_tokens) and the room left in the context window after the prompt. A prompt that leaves no room for an answer is reported as such instead of returning a stub.Reasoning models get a floor. On a model that supports
reasoning, a budget belowREASONING_MIN_MAX_TOKENSis raised automatically — that is what makes a "write one line" delegation come back empty.Cut-off answers are resumed. If the model stops with
finish_reason: "length", the server feeds the partial answer back, asks it to continue exactly where it stopped, and stitches the pieces together — up toMAX_CONTINUATIONSrounds andMAX_OUTPUT_TOKENSin total. Passauto_continue: falseto opt out per call.An empty reasoning answer is retried, once, bigger. If the first attempt produced only hidden tokens, the server retries with a much larger budget before giving up.
Whatever happens is reported. The result always carries
truncated,continuationsand human-readablenotes(e.g. "raised max_tokens from 300 to 2000"), so a still-incomplete answer is never mistaken for a complete one.Large answers are paged, not clipped. An answer over
MAX_RESPONSE_CHARSis returned as a first page plus aresponse_id; the agent pulls the rest withopenrouter_fetch_responseinstead of having its client clip the tool result. Stored answers live in memory for 30 minutes.
max_tokens remains available as a deliberate cost/length cap — it is just no longer something the agent has to get right.
The answer itself is returned as plain text with a compact metadata footer, rather than JSON-escaped inside the payload; the full structured data is still available in structuredContent.
Development
npm run dev # tsx watch
npm run typecheck # tsc over src/ and test/
npm test # typecheck + node:test suite
npm run build # emit dist/How openrouter_auto_delegate picks a model
Filters the catalog by the
.envpolicy and the call's requirements (require_tools,min_context, text output).Computes the combined price per model:
0.7·input_price + 0.3·output_price(USD/M tokens).Depending on the tier, searches within its price band (falling back to the neighboring band if empty):
economy(≤ $0.5/M by default): the cheapest.balanced($0.5–$3/M): the cheapest in the middle band.quality($3–$15/M): the highest-priced within the ceiling (price as a proxy for capability, without reaching flagship models).
Prefers providers from
PREFERRED_PROVIDERS, and reports in the response the chosen model, the reasoning, and the discarded alternatives.
Security
The OpenRouter API key lives only in the server's
.env; it is never exposed to agents.The
.envfile is in.gitignore.If the port is reachable from outside, set
MCP_AUTH_TOKENand serve behind HTTPS.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityAmaintenanceRoutes your AI tasks to the best available model across 20+ providers — automatically selecting based on task type, budget, and subscription pressure. Supports text, image, video, and audio with built-in cost optimization and fallback chains.6071MIT
- AlicenseNot gradedqualityDmaintenanceA model routing advisor for autonomous agents — get cost-optimised LLM recommendations via MCP.10MIT
- AlicenseBqualityDmaintenanceRoute prompts intelligently across Claude, Gemini, and GPT-4o, automatically picking the best model for every task while minimizing token cost.518MIT
- AlicenseNot gradedqualityCmaintenanceProvides live LLM pricing data from OpenRouter, enabling agents to search models, get pricing, estimate costs, and compare models.6MIT
Related MCP Connectors
SaaS intelligence for AI agents. 5 unified tools cover 1,000+ services with 91-96% token savings.
See, price, and control every tool call your AI agents make: policy checks, cost, and audit tools.
Human-as-a-Service for AI agents. Delegate tasks that need a real human, get results via API.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/vmhq/openrouter-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server