Console

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 user

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

ParameterRequiredDescription
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": {...}}
ParameterRequiredDescription
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.

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

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

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

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

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

ValueModeAsset requirement
1Text-to-videoNo asset needed
2Image-to-video (first frame)image_urls provides a single start frame
3Image-to-video (first/last frame)image_urls provides [first frame, last frame] in that order
4Image-to-video (reference)image_urls provides one or more style/content reference images
5All referencesMix of image_urls/video_urls/audio_urls, referenced by position in text
14. get_video_generation — Poll a video task
ParameterRequiredDescription
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:

FieldTypeDescription
regionstringText model V2 tiering region, e.g. GLOBAL / NON_GLOBAL / us-central1. Null for image/video models
bandMinlongText model input token band lower bound (inclusive); null = unbounded
bandMaxlongText model input token band upper bound (inclusive); null = unbounded
resolutionstringImage/video resolution, e.g. 1080p / 4K. Null for text models
claritystringClarity level
unitstringBilling unit: token (text) / image (image) / video (video)
quantityintegerUnit quantity (e.g. per 1000 tokens, per 1 image)
descriptionstringPrice description
inputPricedecimalCaller's effective input unit price
outputPricedecimalCaller's effective output unit price
cachePricedecimalCache price (general models, legacy billing formula)
cacheReadPricedecimalCache read unit price (Bedrock Claude only)
cacheWritePricedecimalCache write unit price (Bedrock Claude only)
ratiodecimalBilling multiplier. 1 for FIXED mode; user/group multiplier for RATIO mode
modestringPricing mode: RATIO / FIXED
planIdstringMatched 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 typeunitEffective description fields
Texttokenregion / bandMin / bandMax
Imageimageresolution / clarity
Videovideoresolution / 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):

ScenarioBehavior
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 resolveEmpty array []; model still returned normally
Model has no price config at allEmpty array []
Price resolution throws an uncaught exceptionCaught 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 add without --header, or the key is wrong
  • If editing the JSON config directly, the headers field name is wrong (should be X-Basicrouter-Api-Key)
  • Running locally without the BASICROUTER_API_KEY environment 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_completion tool to call claude-sonnet-5 …"

Image/video generation stays pending forever

  • Poll image tasks with get_image_generation and video tasks with get_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_generation all consume account credit — check get_balance first 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's previous_response_id or maintain history yourself in messages.