Model Context Protocol
MCP on any model
Route MCP tool servers and chat completions through one endpoint. Add mcp_servers to any /v1/chat/completions request, keep the OpenAI-compatible response shape. No SDK swap, no separate MCP client.
Why this matters
Only some providers can call MCP servers themselves, each through its own API shape. Leanroute runs the tool loop at the gateway instead, so the model never has to know MCP exists. That means you can point an MCP-backed agent at GPT, Gemini, DeepSeek or Groq through the standard chat-completions API, and compare them on the same tools, same prompt, same bill.
How it works
When a request carries mcp_servers, the gateway does the whole agent loop before it answers you:
- Connects to each MCP server and calls tools/list to discover what it offers.
- Namespaces each tool as
serverName__toolNameso two servers exposingsearchcan coexist in one request. - Injects them as ordinary function-tool declarations, merged with any
toolsyou declared yourself. - Dispatches to whichever model you asked for.
- Intercepts the model's tool calls, executes them against the right MCP server, and feeds the results back for another turn.
- Repeats until the model stops asking for tools, then returns one OpenAI-shape response.
Because MCP tools become plain function declarations on the way out, any provider with function calling works. There is no per-provider MCP integration to wait for.
Quickstart
Add an mcp_servers array to any non-streaming request. Note the model below is openai/*, not anthropic/*.
curl https://api.leanroute.dev/v1/chat/completions \
-H "Authorization: Bearer $LEANROUTE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.6-luna",
"messages": [
{ "role": "user", "content": "What upcoming meetings do I have this week?" }
],
"mcp_servers": [
{
"type": "url",
"url": "https://example-server.modelcontextprotocol.io/mcp",
"name": "calendar",
"authorization_token": "eyJhbGciOi..."
}
],
"max_tokens": 2048
}'Response is OpenAI-shape. The assistant.content string already incorporates every tool result, and usage is the sum across all turns of the loop, so the number you are billed on is the number you see.
Budget for the whole loop, not one turn
A tool-using request costs several model calls. Set max_tokens generously. On reasoning models (gpt-5*, o1*, o3*) it is a total budget that includes hidden reasoning tokens, and tool selection consumes a lot of them. Too low a budget and the model runs out before it produces any visible answer.
Trust model — per-org allowlist
Why we require an allowlist
An MCP server can execute tools on your behalf. Unlike a bad model (recoverable — retry with another), a bad MCP server can exfiltrate data, execute shell commands, or write to databases via whatever tools it exposes. The gateway refuses mcp_servers URLs that aren't on your org's explicit list.
Before your first request with mcp_servers, add each server URL at /dashboard/mcp. Attempts to reference an unlisted URL return 403 mcp_server_not_allowlisted with the offending URLs echoed back so you know exactly what to add.
x-gateway-mcp-allowlist: N confirms how many of your servers matched the allowlist. It should equal the number you sent.

Server configuration
Each entry in mcp_servers takes:
type— currently only"url".url— MCP server endpoint. Must behttps://and must speak the Streamable HTTP transport. The legacy HTTP+SSE transport is deprecated in the MCP spec and is not supported; localstdioservers cannot work here at all, since a hosted gateway has no way to spawn a process on your machine.name— unique identifier for this server within the request. Used as the namespace prefix on every tool it exposes, so pick something short.authorization_token— Bearer token if the server requires it. Sent to your server only; Leanroute never logs it and never puts it in a response header.
Response headers
The loop is fully observable from the response:
x-gateway-mcp-mode: proxy— the gateway ran the loop.x-gateway-mcp-allowlist: N— servers that passed the allowlist check.x-gateway-mcp-tools-discovered: N— tools found across all servers.x-gateway-mcp-tool-calls: N— tools actually executed.x-gateway-mcp-iterations: N— model round trips. 1 means the model answered without calling anything.x-gateway-mcp-iteration-cap: true— only when the loop hit its 10-iteration ceiling, which usually means the model got stuck retrying a failing tool.x-gateway-mcp-server-failures: N— servers that could not be reached. The run continues with whatever the others offered rather than failing outright.
Provider support
Any model with function calling. MCP tools are converted to ordinary function declarations before dispatch, so there is nothing provider-specific to support.
Billing, spend caps and credit
Every model call inside the loop is billed and logged as its own request, so a request that took 3 iterations costs 3 model calls. Tool execution itself is free; the MCP server is yours.
Spend caps and the prepaid balance are checked once, when the request arrives. They are not re-checked between iterations. A single MCP request can therefore make up to 10 model calls after passing the check, and can take an org slightly past its daily or monthly cap (or its credit balance) before the next request is refused. With typical models that is a few cents. If you run premium models with long contexts close to a cap, leave headroom of roughly 10 calls, or lower the cap by that amount.
Errors
Streaming is the one restriction. Intercepting tool calls mid-stream is not implemented yet, so mcp_servers with stream: true is refused on every provider rather than silently dropping the field:
{
"error": {
"message": "mcp_servers is not supported with stream:true. Set stream:false ...",
"code": "mcp_streaming_unsupported"
}
}Fix: set stream: false. The gateway runs the tool loop and hands back the finished answer, which is what most agent code wants from a tool turn anyway.
Arbitrage + MCP
Requests with mcp_servers are excluded from same-tier cost arbitrage, and from automatic failover. Swapping providers partway through an agent run would hand a half-finished tool conversation to a model that never saw the earlier turns. Your explicit model choice is what runs.
What's not supported yet
- Streaming — see Errors above. Requires intercepting tool calls mid-stream, which is real work we have deferred until someone needs it.
- Per-tool allowlist or denylist — every tool a server exposes is offered to the model. Allowlisting is at the server level, via the dashboard.
- Legacy HTTP+SSE and local
stdiotransports — Streamable HTTP only. - MCP prompts and resources — tools only. Prompts and resources stay client-side.
Pricing
Billed like any other request: upstream input plus output tokens at the model's catalog rate. The difference with a tool loop is that it makes several model calls, and each tool result becomes input tokens on the following turn. The usage object we return is the aggregate across every turn, so it always matches what you are charged.
See also: API reference · Response headers · Model catalog · MCP specification