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.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
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.
This server cannot be deployed
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.-