MCP
BasicRouter MCP Guide
Wraps BasicRouter (an OpenAI-compatible LLM gateway) as an MCP server so your AI tools can call BasicRouter's chat, model, billing, image, and video endpoints directly.
Features
- 🤖 Multi-API chat: supports OpenAI Chat Completions, Anthropic Messages, and OpenAI Responses compatible endpoints
- 🖼️ Multimodal generation: besides text chat, supports image and video generation (text-to-, image-to-, first/last frame, reference asset) async tasks
- 🔍 Model & account lookup: list available models, model details, account balance, usage/billing details, and top-up transactions
- 🔑 Key passed via header: each call reads the API key from the request header, so one deployment can be shared by multiple accounts — the server never persists or caches any key
Quickstart
Use it in Claude Code (recommended). Add -s user to register it at user scope (available in all your projects).
Step 1: Add the connection
claude mcp add --transport http basicrouter https://api.basicrouter.ai/mcp \
--header "X-Basicrouter-Api-Key: <your BasicRouter API key>" \
-s userStep 2: Verify the connection
claude mcp list # should show ✓ Connected
claude mcp get basicrouter # should show Scope: User config (available in all your projects)Step 3: Start using it
You can ask Claude things like:
- "Use BasicRouter to call claude-sonnet-5 and write a poem about autumn"
- "List the models available on my BasicRouter account"
- "Check my BasicRouter balance and recent usage"
- "Use BasicRouter to generate a cyberpunk city-at-night image"
Claude will automatically call the matching MCP tool and return the result.
Using it with Claude Desktop
Add this to the mcpServers section:
{
"mcpServers": {
"basicrouter": {
"type": "http",
"url": "https://api.basicrouter.ai/mcp",
"headers": {
"X-Basicrouter-Api-Key": "<your BasicRouter API key>"
}
}
}
}Using it with Codex CLI
Recommended: use env_http_headers to read the key from an environment variable
Export the environment variable in your shell first:
export BASICROUTER_API_KEY=<your BasicRouter API key>Then in ~/.codex/config.toml:
[mcp_servers.basicrouter]
url = "https://api.basicrouter.ai/mcp"
env_http_headers = { "X-Basicrouter-Api-Key" = "BASICROUTER_API_KEY" }Alternative: use http_headers to write the key directly into the config (handy if you don't want to manage a separate env var, but note the key is then stored in plain text in the config file):
[mcp_servers.basicrouter]
url = "https://api.basicrouter.ai/mcp"
http_headers = { "X-Basicrouter-Api-Key" = "<your BasicRouter API key>" }If adding it through the Codex settings UI:
- Type: HTTP / Streamable HTTP
- Name: basicrouter
- URL:
https://api.basicrouter.ai/mcp - Header name:
X-Basicrouter-Api-Key - Header value: your BasicRouter API key
Verify the connection
After starting Codex CLI, the /mcp command lists configured MCP servers and their connection status — just confirm it loaded correctly. The usage pattern matches Claude: Codex automatically picks the right tool.
If your config file already has other MCP servers, append this one at the same level. Fully quit and reopen Claude Desktop after editing.
Tools
This MCP server provides 14 tools, grouped into five categories:
1. Chat
1. chat_completion — Chat Completions
Sends a single chat request and returns the model's full reply (no streaming). Beyond plain text, it also accepts images and video for multimodal understanding (model must support the corresponding vision/video capability — check input_modalities via list_models first) — change the message content from a string to an array of content blocks mixing text with image_url / video_url.
| Parameter | Required | Description |
|---|---|---|
| model | ✅ | Model ID, e.g. "claude-sonnet-5" — check list_models first |
| messages | ✅ | Message list, each shaped like {"role": "user"|"assistant"|"system", "content": "..."}; for multimodal input, content is an array of content blocks (see Multimodal input below) |
| temperature | ❌ | Sampling temperature — higher is more random |
| max_tokens | ❌ | Maximum tokens to generate |
Multimodal input (text / image / video)
Image input (URL):
{"role": "user", "content": [
{"type": "text", "text": "What's in this image?"},
{"type": "image_url", "image_url": {"url": "https://example.com/image.jpg"}}
]}Image input (Base64):
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,<BASE64_STRING>"}}Supported formats: PNG, JPEG, GIF (first frame only), WebP. Single image size limit 20MB.
Note: Base64-encoded large images produce very long strings and may fail due to an oversized request body; prefer passing images by URL.
The detail parameter (optional, set on the image_url object) controls image processing fidelity: "auto" (default — model decides by image size) / "low" (512x512 thumbnail, fast and cheap, good for simple classification) / "high" (full resolution, good for reading small text / fine detail). A single message's content array may hold multiple image_url blocks to pass several images.
Video input (URL):
{"role": "user", "content": [
{"type": "text", "text": "Describe what happens in this video."},
{"type": "video_url", "video_url": {"url": "https://example.com/video.mp4"}}
]}Video input (Base64):
{"type": "video_url", "video_url": {"url": "data:video/mp4;base64,<BASE64_STRING>"}}Models supporting video input on BasicRouter include select models from the Qwen, Doubao (dola-seed), Kimi, and MiniMax series — check input_modalities returned by list_models. The official OpenAI Chat Completions standard does not natively support video, but the BasicRouter gateway supports it via the
{"type": "video_url", "video_url": {"url": "..."}}extension format, consistent with the video_url format used by OpenRouter, NVIDIA NIM, vLLM, and other platforms.
2. create_message — Anthropic Messages compatible
Calls the Anthropic Messages compatible endpoint (no streaming). Content blocks support text, image, tool_use, tool_result, thinking, redacted_thinking. Images are passed via content blocks:
{"type": "image", "source": {...}}| Parameter | Required | Description |
|---|---|---|
| model | ✅ | Model ID, e.g. "claude-sonnet-4.6" |
| messages | ✅ | Message list; content can be a string or an array of content blocks (text/image/tool_use/tool_result/thinking, etc.) |
| max_tokens | ✅ | Maximum output tokens |
| system | ❌ | System prompt, a string or [{"type": "text", "text": "..."}] |
| temperature / top_p / top_k | ❌ | Sampling parameters |
| tools | ❌ | Tool definitions, each shaped like {"name", "description", "input_schema", ...} |
| tool_choice | ❌ | Tool selection strategy |
| thinking | ❌ | Extended thinking configuration |
| stop_sequences | ❌ | Custom stop sequences |
| metadata | ❌ | Additional metadata |
| anthropic_beta | ❌ | Beta feature identifier for the anthropic-beta header |
Multimodal input (text / image / video)
Image input supports three source types:
1. Base64-encoded (note: the data field is a plain Base64 string, without the data: prefix); media_type supports image/jpeg, image/png, image/gif, image/webp:
{"type": "image", "source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "<BASE64_STRING>"
}}2. URL reference:
{"type": "image", "source": {
"type": "url",
"url": "https://example.com/image.jpg"
}}3. File ID (first upload the image via the Files API with purpose="vision" to get a file_id):
{"type": "image", "source": {
"type": "file",
"file_id": "<file_id>"
}}A single message's content array may hold multiple image blocks to pass several images.
Note: Base64-encoded large images produce very long strings and may fail due to an oversized request body; prefer passing images by URL.
Video input: the Anthropic Messages API does not support video files. For video understanding, first extract keyframes with ffmpeg or similar, then pass each frame as an image content block. For models that support video input on BasicRouter (check input_modalities via list_models), use the chat_completion or create_response interface for native video support.
3. create_response — OpenAI Responses compatible
Uses input instead of messages, instructions instead of a system message, and text.format instead of response_format. Beyond plain text, it also accepts images and video for multimodal understanding (model must support the corresponding vision/video capability — check input_modalities via list_models) — set input to an array of message objects whose content is an array of content blocks mixing text with images/video.
| Parameter | Required | Description |
|---|---|---|
| model | ✅ | Model ID, e.g. "glm-5.2" |
| input | ✅ | A plain string (as one user message) or an array of message objects (used for multimodal input, see below) |
| instructions | ❌ | System prompt |
| max_output_tokens | ❌ | Maximum output tokens |
| temperature / top_p | ❌ | Sampling parameters |
| tools | ❌ | Tool definitions, top-level shape {"type", "name", "description", "parameters"} |
| tool_choice | ❌ | "auto" / "none" / "required" or {"type", "name"} |
| text | ❌ | Output format config, e.g. {"format": {"type": "text" | "json_object" | "json_schema", ...}} |
| previous_response_id | ❌ | Previous response ID for multi-turn conversations |
| parallel_tool_calls | ❌ | Whether to allow parallel tool calls |
| metadata | ❌ | Additional metadata |
Multimodal input (text / image / video)
Note: the Responses API uses content block types input_text / input_image / input_video (not the text / image_url / video_url of Chat Completions), and image_url / video_url are bare strings, not nested objects.
Image input (URL):
[{"role": "user", "content": [
{"type": "input_text", "text": "What's in this image?"},
{"type": "input_image", "image_url": "https://example.com/image.jpg"}
]}]Image input (Base64):
{"type": "input_image", "image_url": "data:image/jpeg;base64,<BASE64_STRING>"}Supported formats: PNG, JPEG, GIF (first frame only), WebP. Single image size limit 20MB.
Note: Base64-encoded large images produce very long strings and may fail due to an oversized request body; prefer passing images by URL.
Image input (File ID):
{"type": "input_image", "file_id": "<file_id>"}The file_id is obtained by uploading the image via the Files API with purpose="vision".
The detail parameter (optional, set on the input_image object) controls image processing fidelity: "auto" (default) / "low" (512x512 thumbnail, fast and cheap) / "high" (full resolution, for small text / fine detail) / "original" (original resolution, only some newer models). A single message's content array may hold multiple input_image blocks to pass several images.
Video input (URL):
[{"role": "user", "content": [
{"type": "input_text", "text": "Describe what happens in this video."},
{"type": "input_video", "video_url": "https://example.com/video.mp4"}
]}]Video input (Base64):
{"type": "input_video", "video_url": "data:video/mp4;base64,<BASE64_STRING>"}Video input (File ID):
{"type": "input_video", "file_id": "<file_id>"}Models supporting video input on BasicRouter include select models from the Qwen, Doubao (dola-seed), Kimi, and MiniMax series — check input_modalities returned by list_models. The official OpenAI Responses API standard does not natively support video, but the BasicRouter gateway supports it via the
{"type": "input_video", "video_url": "..."}extension format, consistent with the input_video format used by BytePlus/Volcengine and other platforms.
2. Models & account
4. list_models — List available models
Returns the models available to the current account, with vendor, modality, capability, and pricing metadata. No parameters. Each list entry includes a price_tiers price tier list — the caller's effective billing price, matching actual charges (see Price tier field reference).
5. get_model — Get a single model's details
| Parameter | Required | Description |
|---|---|---|
| model | ✅ | Model ID, e.g. "gpt-5.5" — returns a clear error if it doesn't exist |
The response includes a price_tiers price tier list (see Price tier field reference); returns 404 (no price fields) when the model doesn't exist.
6. get_balance — Check account balance
Returns the current account's usage and balance. No parameters.
3. Billing
7. list_usage — Model usage/billing details
Paginated model usage billing details, sorted by order creation time descending. Only regular usage charges — no top-ups or adjustments.
| Parameter | Required | Description |
|---|---|---|
| page | ❌ | Page number, starting at 1, default 1 |
| size | ❌ | Page size, default 20 |
| start_time | ❌ | Start time, format "yyyy-MM-ddTHH:mm:ss" (e.g. "2026-07-01T00:00:00") |
| end_time | ❌ | End time, same format |
8. list_transactions — Top-up/package transactions
Paginated paid top-up / package purchase transactions, sorted by creation time descending.
| Parameter | Required | Description |
|---|---|---|
| page | ❌ | Page number, starting at 1, default 1 |
| size | ❌ | Page size, default 20 |
| start_time | ❌ | Start time, format "yyyy-MM-dd HH:mm:ss" (note: a space, not "T", between date and time) |
| end_time | ❌ | End time, same format |
4. Image generation
9. list_image_models — List image models
Returns supported image generation models, with resolution, aspect ratio, max image count (maxCount), and max reference image count (fileMax). No parameters. Check this before generating images — you can only pass values it publishes. Each list entry includes a priceTiers price tier list (see Price tier field reference).
10. create_image_generation — Submit an image generation task
Submits asynchronously and immediately returns a taskId; consumes account credit.
| Parameter | Required | Description |
|---|---|---|
| text | ✅ | Image generation prompt |
| model | ✅ | Model name, from the id returned by list_image_models |
| count | ❌ | Number of images to generate |
| resolution | ❌ | Resolution, from list_image_models' resolutions |
| ratio | ❌ | Aspect ratio, from list_image_models' ratios |
| image_urls | ❌ | Reference image URLs (image-to-image), count capped at that model's fileMax |
| callback_url | ❌ | Webhook called on completion; omit to poll instead |
11. get_image_generation — Poll an image task
| Parameter | Required | Description |
|---|---|---|
| task_id | ✅ | The taskId returned by create_image_generation |
Returns: status is pending / success / failed; images is a JSON string array of image URLs; text carries any extra model output (e.g. Gemini multimodal output).
5. Video generation
12. list_video_models — List video models
Returns supported video generation models, with allowedVideoTypes, duration range (videoDurationMin/Max), resolution, aspect ratio, and reference asset limit (fileMax). No parameters. Check this before generating video. Each list entry includes a priceTiers price tier list (see Price tier field reference).
13. create_video_generation — Submit a video generation task
Submits asynchronously and immediately returns a taskId; consumes account credit.
| Parameter | Required | Description |
|---|---|---|
| text | ✅ | Video generation prompt; when video_type=5, reference assets with "@Image N"/"@Video N"/"@Audio N" |
| model | ✅ | Model name, from the id returned by list_video_models |
| video_type | ✅ | Generation mode code (1-5, see below) — only values in that model's allowedVideoTypes are valid |
| image_urls | ❌ | Image asset URLs; meaning depends on video_type |
| video_urls | ❌ | Video asset URLs, only for video_type=5 |
| audio_urls | ❌ | Audio asset URLs, only for video_type=5 |
| resolution / ratio | ❌ | Resolution / aspect ratio, from list_video_models |
| duration | ❌ | Video duration in seconds, must be within videoDurationMin/Max |
| callback_url | ❌ | Webhook called on completion; omit to poll instead |
Meaning of video_type:
| Value | Mode | Asset requirement |
|---|---|---|
| 1 | Text-to-video | No asset needed |
| 2 | Image-to-video (first frame) | image_urls provides a single start frame |
| 3 | Image-to-video (first/last frame) | image_urls provides [first frame, last frame] in that order |
| 4 | Image-to-video (reference) | image_urls provides one or more style/content reference images |
| 5 | All references | Mix of image_urls/video_urls/audio_urls, referenced by position in text |
14. get_video_generation — Poll a video task
| Parameter | Required | Description |
|---|---|---|
| task_id | ✅ | The taskId returned by create_video_generation |
Price tier field reference
The outputs of these 4 model-query tools — list_models, get_model, list_image_models, list_video_models — return the caller's (API Key user's) effective billing price, which matches actual charges exactly, and include all price tiers for the model.
Naming difference: list_models / get_model follow OpenAI snake_case (price_tiers); list_image_models / list_video_models follow camelCase (priceTiers). Both have the same structure.
price_tiers / priceTiers is an array; each element is a price tier:
| Field | Type | Description |
|---|---|---|
region | string | Text model V2 tiering region, e.g. GLOBAL / NON_GLOBAL / us-central1. Null for image/video models |
bandMin | long | Text model input token band lower bound (inclusive); null = unbounded |
bandMax | long | Text model input token band upper bound (inclusive); null = unbounded |
resolution | string | Image/video resolution, e.g. 1080p / 4K. Null for text models |
clarity | string | Clarity level |
unit | string | Billing unit: token (text) / image (image) / video (video) |
quantity | integer | Unit quantity (e.g. per 1000 tokens, per 1 image) |
description | string | Price description |
inputPrice | decimal | Caller's effective input unit price |
outputPrice | decimal | Caller's effective output unit price |
cachePrice | decimal | Cache price (general models, legacy billing formula) |
cacheReadPrice | decimal | Cache read unit price (Bedrock Claude only) |
cacheWritePrice | decimal | Cache write unit price (Bedrock Claude only) |
ratio | decimal | Billing multiplier. 1 for FIXED mode; user/group multiplier for RATIO mode |
mode | string | Pricing mode: RATIO / FIXED |
planId | string | Matched pricing plan id; may be null |
The three model types share one structure; only the description fields differ, and non-applicable fields are null:
| Model type | unit | Effective description fields |
|---|---|---|
| Text | token | region / bandMin / bandMax |
| Image | image | resolution / clarity |
| Video | video | resolution / clarity |
Pricing meaning: the returned inputPrice / outputPrice / cacheReadPrice / cacheWritePrice, etc. are the final billing unit prices for the current API Key user, resolved through the price chain (chain) → guidance → base price, and match actual charges. The unit price a caller sees varies by their pricing chain (reseller / distributor / enterprise / user-level config).
Actual charge = usage ÷ quantity × unit price × ratio. For per-second video billing, actual charge = duration × outputPrice × ratio.
Edge cases & error handling (a read-only endpoint never returns 500 due to price config):
| Scenario | Behavior |
|---|---|
| A single tier fails to resolve (pricing policy denies access, missing config) | Skip that tier, log a warn, continue resolving the rest |
| All tiers for a model fail to resolve | Empty array []; model still returned normally |
| Model has no price config at all | Empty array [] |
| Price resolution throws an uncaught exception | Caught overall, returns empty array; endpoint still 200 |
Response example (list_models, text model):
{
"object": "list",
"data": [
{
"id": "gpt-4o",
"object": "model",
"display_name": "gpt-4o",
"context_length": 128000,
"description": "...",
"price_tiers": [
{
"region": "GLOBAL",
"bandMin": null,
"bandMax": null,
"resolution": null,
"clarity": null,
"unit": "token",
"quantity": 1000,
"description": "per 1K tokens",
"inputPrice": 0.0025,
"outputPrice": 0.01,
"cachePrice": 0.00125,
"cacheReadPrice": 0,
"cacheWritePrice": 0,
"ratio": 1,
"mode": "RATIO",
"planId": null
}
]
}
]
}Response example (list_image_models, image model; priceTiers has the same structure, unit is image):
{
"object": "list",
"data": [
{
"id": "dall-e-3",
"object": "image_model",
"displayName": "dall-e-3",
"resolutions": ["1024x1024", "1792x1024", "1024x1792"],
"priceTiers": [
{
"region": null,
"bandMin": null,
"bandMax": null,
"resolution": "1024x1024",
"clarity": "standard",
"unit": "image",
"quantity": 1,
"description": "per image",
"inputPrice": 0,
"outputPrice": 0.04,
"cachePrice": 0,
"cacheReadPrice": 0,
"cacheWritePrice": 0,
"ratio": 1,
"mode": "RATIO",
"planId": null
}
]
}
]
}Examples
Example 1: a plain chat request
Ask: "Use BasicRouter to call claude-sonnet-5 and ask what Python's GIL is"
The AI will call chat_completion with:
{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "What is Python's GIL?"}]
}Example 2: tool use via the Anthropic Messages endpoint
Ask: "Use BasicRouter's Messages endpoint and let the model decide whether to check the weather"
The AI will call create_message, passing a tools definition and tool_choice; the response will include a tool_use content block.
Example 3: list available models
Ask: "List the models on my BasicRouter account"
The AI will call list_models, returning IDs, vendors, modalities, pricing, and more for every available model.
Example 4: check balance and usage
Ask: "Check my BasicRouter usage for this month"
The AI will call get_balance for the balance, then list_usage (with start_time scoped to this month) for the usage breakdown.
Example 5: generate an image
Ask: "Use BasicRouter to generate a cyberpunk city-at-night image"
The AI will first call list_image_models to check specs, then create_image_generation to get a taskId, then poll get_image_generation for the image URL.
Example 6: text-to-video
Ask: "Use BasicRouter to generate a 5-second video of a space nebula"
The AI will first call list_video_models to check specs, then create_video_generation (video_type=1, duration=5), and after getting a taskId, poll get_video_generation.
FAQ
Tool call fails with "missing BasicRouter API Key"
- You ran
claude mcp addwithout--header, or the key is wrong - If editing the JSON config directly, the
headersfield name is wrong (should beX-Basicrouter-Api-Key) - Running locally without the
BASICROUTER_API_KEYenvironment variable set
Tool call fails with "BasicRouter returned 401 unauthorized"
The API key itself is wrong or expired — regenerate it in the BasicRouter console.
Claude isn't calling BasicRouter tools — what now?
- Confirm the connection shows ✓ Connected via
claude mcp list - Check the connection status:
claude mcp get basicrouter - Try being explicit: "Use BasicRouter's
chat_completiontool to callclaude-sonnet-5…"
Image/video generation stays pending forever
- Poll image tasks with
get_image_generationand video tasks withget_video_generation— note that a running video task's status is "running", not "pending" - Generation takes time, usually a few to tens of seconds, longer for video
- If you passed a
callback_url, the task will call back on completion — no need to poll
list_image_models / list_video_models errors
Neither tool consumes credit itself — an error usually points to a key or network issue. First confirm list_models works normally.
Notes
- Header name:MCP passes the key via
X-Basicrouter-Api-Key. - Credit consumption:
chat_completion/create_message/create_response/create_image_generation/create_video_generationall consume account credit — checkget_balancefirst if you want to know the balance beforehand. - Check specs before generating:Image/video generation parameters like model, resolution, ratio, count, duration, and video_type must only use values published by
list_image_models/list_video_models, otherwise the call errors out. - No streaming:None of the three chat endpoints support streaming — each returns the full result at once.
- Stateless design:Every request is independent and no session is stored; for multi-turn conversations use
create_response'sprevious_response_idor maintain history yourself inmessages.
