urai-router
OfficialIntegrates GitHub Models as a provider so models available through GitHub Models can be discovered and called through the router.
Routes requests to Google Gemini through its OpenAI-compatible endpoint using Google API keys registered in keys.txt.
Routes requests to NVIDIA's model API (NIM) using NVIDIA API keys managed by the router.
Connects to a local Ollama instance as an upstream provider, exposing locally hosted models through the unified endpoint and model catalogue.
Routes requests to OpenAI's API using OpenAI keys, exposing chat completions and models through the router's OpenAI-compatible endpoint.
Routes chat completion requests to Perplexity's API using Perplexity keys registered in keys.txt.
Click on "Deploy 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., "@urai-router list my available free LLM models and key health"
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.
Urai Router
One keys.txt → every LLM provider, one local endpoint, automatic key rotation.
Urai Router is a small, dependency-free Python gateway for people who keep several
LLM API keys. It reads a plain text file, auto-detects each provider, discovers the
models each key unlocks (including free OpenCode Zen models), rotates keys on
401, 403, 429 and 5xx, and exposes everything as:
an OpenAI-compatible HTTP API at
http://127.0.0.1:8787/v1an MCP stdio server for OpenCode, Kiro CLI and any MCP client
an Agent Skill (
SKILL.md) that teaches an agent how to use itgenerated OpenCode and Kiro configuration so both tools understand the provider and model list without manual editing
No database. No Node modules. No Docker. Python 3.9+ standard library only.
urai-router init # create ~/.config/urai-router/keys.txt
$EDITOR ~/.config/urai-router/keys.txt
urai-router setup all --start # OpenCode + Kiro + skill, start proxy
urai-router doctorWhy this exists (audit first)
The project started with an audit of existing open-source routers. The summary is
in docs/AUDIT.md. In short:
Project | Stars | License | What it is | Why not just use it |
62k | MIT-ish | Full AI gateway / SDK | Heavy, needs Postgres/Redis for teams; not a local MCP+skill | |
37k | MIT | Key redistribution server | Web UI + DB, aimed at resale, not CLI agents | |
51k | AGPL-3.0 | Fork of one-api | AGPL, DB, server-first | |
38k | MIT | Claude Code routing | Node/TS, Claude-shaped, not MCP-native | |
1.4k | GPL-3.0 | OpenCode Zen proxy | Go, GPL, Zen-only, no keys.txt/MCP config | |
983 | AGPL-3.0 | Multi-upstream proxy | AGPL, Go, no MCP/skill surface |
None of them combine all four requirements: plain keys.txt, free OpenCode
Zen discovery, MCP-native agent tools, and automatic OpenCode/Kiro config.
Urai Router is deliberately the small tool that does those four things.
Related MCP server: io.github.0xzr/freellmpool
Install
git clone https://github.com/etside/Urai-router.git
cd Urai-router
python3 -m pip install --user -e . # or: pipx install .
urai-router --versionFor a zero-install run: python3 -m urai_router --help from the repo.
keys.txt
Default search order:
--keys PATH$URAI_KEYS_FILE./keys.txt~/.config/urai-router/keys.txt~/.config/urai/keys.txt~/.urai/keys.txt~/urai-keys.txt~/keys.txt
# provider:key (provider prefix is optional when the key is recognizable)
opencode:sk-...
openrouter:sk-or-v1-...
groq:gsk_...
deepseek:sk-...
anthropic:sk-ant-...
google:AIza...
xai:xai-...
# labels and weights control rotation
opencode:sk-... # label=main weight=3
opencode:sk-... # label=backup weight=1#starts a comment;label=,weight=,@label,*weightare parsed.${ENV_VAR}is expanded.Duplicate keys are ignored.
An unlabelled key is auto-detected by prefix, then by a 1-token probe.
keys.txtis hot-reloaded when its mtime changes.The file is never sent anywhere and never written into logs;
keys listshows masked values only.
Keep it private:
chmod 600 ~/.config/urai-router/keys.txtProviders understood automatically
Registered out of the box:
opencode (Zen), openrouter, openai, deepseek, groq, anthropic,
google (Gemini OpenAI endpoint), xai, nvidia, moonshot, mistral,
together, fireworks, cerebras, huggingface, zai, minimax,
deepinfra, perplexity, github models, nebius, sambanova, cohere,
and local ollama / lmstudio / llamacpp.
Add any OpenAI-compatible provider in ~/.config/urai-router/config.json:
{
"providers": {
"my-gateway": {
"name": "My Gateway",
"base_url": "https://gateway.example.com/v1",
"api_style": "openai",
"default_models": ["my-model"]
}
}
}Then use my-gateway:key... in keys.txt.
OpenCode Zen free models
GET https://opencode.ai/zen/v1/models is public and returns pricing metadata.
Urai Router reads it, marks cost.text.input.price == 0 as free, and reads the
per-model api block so OpenAI-style models use /chat/completions while
Anthropic-style models use /messages (translated both ways, including tools
and SSE streaming).
You still need one Zen API key to call the models; the catalogue itself is public.
Free rows are time-limited by OpenCode, so run urai-router sync periodically.
Routing
Model reference syntax:
Reference | Meaning |
| free models first, then any usable model |
| free models only |
| explicit provider + model |
| OpenRouter model ids keep their slash |
| resolved across the catalogue when unambiguous |
alias | any key in |
Failover policy:
Upstream | Action |
| mark key invalid, rotate immediately |
| honour |
| short exponential cooldown, rotate |
| request or model problem — do not blame the key |
| try the model's alternative endpoint, then report |
Cooldowns and counters persist in
~/.local/state/urai-router/keys-state.json (no key material inside).
Use as an OpenAI-compatible API
urai-router serve # foreground: 127.0.0.1:8787
urai-router proxy start # background, writes pid/port + proxy.log
urai-router proxy status
urai-router proxy stopcurl -s http://127.0.0.1:8787/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"auto","messages":[{"role":"user","content":"say pong"}]}'from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="urai-local")
print(client.chat.completions.create(
model="auto",
messages=[{"role": "user", "content": "hello"}],
).choices[0].message.content)Endpoints:
GET /healthGET /v1/models(?free=1,?provider=opencode,?refresh=1)POST /v1/chat/completions(streaming supported)GET /v1/urai/stats·/v1/urai/keys(masked) ·/v1/urai/providersGET /v1/urai/explain?model=autoPOST /v1/urai/reload·/v1/urai/test
Bind to 127.0.0.1 by default. If you expose the port, set a token:
urai-router serve --token "$(openssl rand -hex 24)"OpenCode integration
urai-router setup opencode --set-defaultThis merges the following into ~/.config/opencode/opencode.jsonc (or .json),
preserving existing settings and writing a timestamped backup:
a custom provider
urai-routerusing@ai-sdk/openai-compatibleoptions.baseURL = http://127.0.0.1:8787/v1a
modelsmap generated from every usable/free model (context + output limits, ★ marks free rows)an
mcp.urai-routerlocal server entry
Then restart OpenCode. Models appear as
urai-router/opencode/deepseek-v4.1-flash-free, urai-router/auto, etc.
--set-default points model and small_model at the first free Zen model.
Refresh after adding keys or when Zen changes its free list:
urai-router syncProject-local config instead of global:
urai-router setup opencode --projectDry run and diff-friendly export:
urai-router setup opencode --dry-run
urai-router export opencodeKiro CLI integration
urai-router setup kiroWrites:
~/.kiro/settings/mcp.json→mcpServers.urai-router(stdio,urai-router mcp)~/.kiro/agents/urai-router.json→ an agent that includes the MCP server and theurai_*tools, with@urai-routerintools
Verify:
kiro-cli mcp list
kiro-cli chat --agent urai-routerKiro keeps its own model backend; the router is used through the MCP tools
(urai_chat, urai_models, urai_health, ...). That is intentional: Kiro does
not expose a custom OpenAI provider setting.
MCP server
urai-router mcp speaks JSON-RPC over stdio (newline-delimited, MCP 2025-06-18,
also compatible with 2025-03-26 / 2024-11-05).
Tools:
Tool | Purpose |
| chat completion through the router, key rotation included |
| merged catalogue, |
| masked health / cooldowns |
| keys + catalogue + proxy status |
| alias for health |
| show routing for a model |
| reload keys and catalogues |
| start the local HTTP proxy and return its URL |
| probe keys |
| generate OpenCode/Kiro/skill config ( |
Resources: urai://config, urai://keys, urai://providers, urai://models,
urai://stats.
MCP client snippet (OpenCode style):
{
"mcp": {
"urai-router": {
"type": "local",
"command": ["urai-router", "mcp"],
"enabled": true
}
}
}MCP client snippet (Kiro style):
{
"mcpServers": {
"urai-router": {
"command": "urai-router",
"args": ["mcp"],
"disabled": false,
"timeout": 30000
}
}
}Skill
urai-router setup skills copies the Agent Skill to:
~/.config/opencode/skills/urai-router/SKILL.md~/.agents/skills/urai-router/SKILL.md~/.claude/skills/urai-router/SKILL.md
Use --project to also write .opencode/, .agents/ and .claude/ variants.
The skill teaches an agent when to call urai_*, how to route free models, and
how to report failover without leaking keys.
CLI reference
urai-router init [--path PATH] [--force]
urai-router serve [--host H] [--port P] [--token T] [--no-detect] [--no-preload]
urai-router mcp
urai-router keys list|check|detect
urai-router models [--free] [--provider P] [--refresh] [--json]
urai-router setup [opencode|kiro|skills|all] [--project] [--dry-run] [--set-default] [--start]
urai-router sync [--set-default]
urai-router doctor [--json]
urai-router test [--model M] [--prompt TEXT] [--json]
urai-router explain [--model M]
urai-router export [opencode|kiro|env|mcp|providers]
urai-router proxy start|status|stop
urai-router versionGlobal flags: --keys, --config, --state-dir, -v.
Configuration
~/.config/urai-router/config.json (created on demand):
{
"host": "127.0.0.1",
"port": 8787,
"token": "",
"max_attempts": 8,
"default_model": "auto",
"prefer_free": true,
"catalog_ttl": 900,
"zen_ttl": 300,
"request_timeout": 120,
"cooldowns": {
"rate": 60,
"invalid": 1800,
"server": 20,
"network": 15,
"max": 3600
},
"aliases": {
"fast": "opencode/deepseek-v4.1-flash-free"
},
"providers": {},
"proxy": { "cors": true, "log_requests": true }
}State and cache:
~/.local/state/urai-router/keys-state.json— health, cooldowns (no secrets)~/.local/state/urai-router/proxy.json— pid, port, url~/.local/state/urai-router/proxy.log~/.cache/urai-router/catalog.json— model metadata cache
Architecture
keys.txt ──▶ KeyStore ──▶ Router ──▶ Provider adapter ──▶ upstream
│ │ │
│ ├─ model resolution ├─ OpenAI chat/completions
│ ├─ retry / cooldown └─ Anthropic messages (+SSE)
│ └─ free-first auto
▼
proxy (:8787/v1) MCP stdio OpenCode config Kiro config SKILL.mdurai_router/providers.py— registry, catalogue parsing, request buildingurai_router/keys.py— parsing, rotation, cooldowns, auto-detection, stateurai_router/router.py— model resolution, failover, streamingurai_router/proxy.py— HTTP server + background process managementurai_router/mcp_server.py— MCP tools/resources/promptsurai_router/config_writer.py— OpenCode/Kiro/skill writersurai_router/adapters.py— OpenAI ↔ Anthropic translation
Security
Keys are read from a local file and used only in
Authorization/x-api-keyheaders to the provider you configured.Logs and API responses only contain masked keys or labels.
keys-state.jsonandcatalog.jsonnever contain key material..gitignoreexcludeskeys.txt,.env, and state.The proxy binds to loopback by default; add
--tokenif you change that.doctorwarns when the keys file is group/world-readable.
Report vulnerabilities privately; see SECURITY.md.
GitHub Actions
CI (
.github/workflows/ci.yml): compile, unit tests against a local fake upstream, package build, zipapp smoke test. No network keys required.Release (
.github/workflows/release.yml): on av*tag, builds sdist, wheel andurai-router.pyz, then creates a GitHub release withGH_TOKEN(the token supplied by GitHub Actions). Manualworkflow_dispatchsupported.
Create the repo and push (what was used here):
gh repo create etside/Urai-router --public --source=. --remote=origin --pushTroubleshooting
Symptom | Fix |
| add keys, then |
models empty |
|
OpenCode shows no models |
|
all keys cooling | add keys or wait; check |
401 from every key |
|
Zen free list changed |
|
Kiro doesn't see MCP |
|
proxy not reachable |
|
License
MIT. See LICENSE.
Available Tools
10 toolsurai_chatB
Send an OpenAI-style chat completion through the automatic key router. Rotates keys on 401/403/429/5xx.
Inputs: messages (required), model, tools, system, +3 more.
urai_checkC
Probe one or all keys with a tiny request and report which ones are valid.
Inputs: all, provider.
urai_ensure_proxyB
Start the local OpenAI-compatible proxy in the background if needed and return its base URL.
Inputs: no named parameters.
urai_explainB
Explain how a model or alias would be routed, which providers qualify and how many keys are ready.
Inputs: model.
urai_healthA
Show router health, key file, catalogue freshness and proxy status.
Inputs: no named parameters.
urai_keysB
Show masked API keys, health, cooldowns and key counts per provider.
Inputs: no named parameters.
urai_modelsC
List the merged model catalogue from all configured providers, including free OpenCode Zen models.
Inputs: limit, refresh, provider, free_only.
urai_refreshC
Reload keys.txt and refresh provider model catalogues.
Inputs: force.
urai_setupC
Generate/merge OpenCode, Kiro and skill configuration. dry_run defaults to true.
Inputs: target, dry_run, project, set_default.
urai_statsA
Show routing statistics (same as health plus catalogue details).
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 10 tools
Most tools have distinct actions, but urai_health and urai_stats explicitly overlap, with stats described as health plus catalogue details. urai_check and urai_keys also partially overlap on key health/validity, which could cause misselection.
All tools use the same urai_ prefix and snake_case, with a mostly predictable split between action verbs and resource nouns. The mix of verb-style and noun-style names is minor but still readable.
Ten tools is well-scoped for a routing server covering chat, key checks, proxy startup, configuration, model listing, and diagnostics. Each tool maps to a plausible operational need.
The surface covers core routing, key inspection/refresh, proxy startup, model catalogue, setup, and diagnostics. Minor gaps exist around stopping the proxy or adding/removing keys directly, but agents can work around these by editing keys.txt or relying on existing operations.
Maintenance
Related MCP Connectors
Chat, image, video, and audio models from many vendors behind one remote MCP server and one key.
Connect MCP clients to 2,000+ AI models without managing provider API keys.
LLM cost optimization gateway, repo token-waste auditor & model arbitrage MCP server.
OpenAI-compatible LLM MCP (7 tools); chat via balance key or x402 USDC on Base
Related MCP Servers
- FlicenseAqualityBmaintenanceEnables LLM chat with routing and failover, balance checking, and model listing via MCP.3-
- FlicenseNot gradedqualityAmaintenanceExposes a unified pool of free LLM chat routes behind one OpenAI-compatible endpoint, letting MCP clients browse available providers and models, check which free tiers are usable right now, and send chat or agent requests with automatic failover. Works without credentials whenever an enabled keyless route is available.-
- FlicenseNot gradedqualityAmaintenanceEnables access to 178 pooled free-tier and keyless LLM chat routes through a single OpenAI-compatible endpoint, usable by coding agents, local proxies, and other MCP clients. It automatically selects only accessible enabled routes, fails over on rate limits or outages, and can start without credentials when a keyless provider is available.-
- FlicenseNot gradedqualityAmaintenancePublishes an MCP server that pools many free LLM providers — including keyless routes — behind one local OpenAI-compatible endpoint, letting coding agents and compatible clients discover, route to, and fail over across available chat models. It also exposes tools for status, model inventory, and capacity/health inspection.-