f3router docs

English · 简体中文

Clients & coding agents

← Back to README · Documentation index · All supported agents

OpenAI-compatible clients

Any client that can target an OpenAI-compatible base URL can use f3router:

  • LangChain, LlamaIndex, official OpenAI SDKs: set base_url to http://localhost:3001/v1 and use the unified key from the dashboard.
  • Local GPU boxes: add custom OpenAI-compatible endpoints for Ollama, llama.cpp, LM Studio, vLLM, or an internal gateway.

Coding agents

Use the generator instead of hand-editing a client configuration:

export F3ROUTER_API_KEY=<unified-key>   # or pass --api-key on each command
npx f3router setup-claude --url http://localhost:3001 --dry-run
npx f3router setup-claude --url http://localhost:3001
npx f3router setup-dsh --url http://localhost:3001 --api-key <unified-key>

--dry-run prints a diff. Real writes merge with the existing configuration and create a timestamped backup first. --profile <name> creates a named Claude/Codex profile. The live /v1/models catalog supplies the model ids and context windows.

Agent Automated command Manual base URL Wire
Claude Code setup-claude or credential-free-on-disk launch http://localhost:3001 Anthropic Messages
Codex CLI setup-codex or launch-codex http://localhost:3001/v1 Responses (wire_api = "responses")
Cline setup-cline http://localhost:3001/v1 OpenAI Chat
Continue setup-continue http://localhost:3001/v1 OpenAI Chat / legacy Completions
Aider setup-aider http://localhost:3001/v1 OpenAI Chat
OpenCode setup-opencode http://localhost:3001/v1 OpenAI Chat
Goose setup-goose http://localhost:3001/v1 OpenAI Chat
Qwen Code setup-qwen http://localhost:3001/v1 OpenAI Chat (native Gemini also works)
Roo Code setup-roo http://localhost:3001/v1 OpenAI Chat
Kilo Code setup-kilo http://localhost:3001/v1 OpenAI Chat
Crush setup-crush http://localhost:3001/v1 OpenAI Chat
DeepSeek Harness setup-dsh http://localhost:3001/v1 OpenAI Chat (api: openai-completions)
MiMo Code setup-mimo http://localhost:3001/v1 OpenAI Chat
AtomCode setup-atomcode http://localhost:3001/v1 OpenAI Chat (type = "openai")
OpenClaw setup-openclaw http://localhost:3001/v1 OpenAI Chat (api: openai-completions)
Hermes Agent setup-hermes http://localhost:3001/v1 OpenAI Chat (provider: custom)
Pi setup-pi http://localhost:3001/v1 OpenAI Chat (api: openai-completions)
Reasonix setup-reasonix http://localhost:3001/v1 OpenAI Chat (kind = "openai")
QwenPaw Manual setup http://localhost:3001/v1 OpenAI Chat (chat.completions)
Cursor setup-cursor prints the guide public https://…/v1 OpenAI Chat
Anything else setup-generic prints a ready block http://localhost:3001/v1 OpenAI Chat

The root-vs-/v1 distinction matters: Claude Code expects the server root because it appends the Anthropic Messages path. OpenAI-compatible clients in this table—including Cline, Aider, Goose, Codex, Continue, OpenCode, Qwen, Roo, Kilo, Crush, MiMo Code, AtomCode, OpenClaw, Hermes Agent, Pi, Reasonix, QwenPaw, and DeepSeek Harness—expect their configured base URL to include /v1.

DeepSeek Harness (dsh)

DeepSeek Harness keeps every provider as a route under llm-pi-ai.providers in $DSH_HOME/settings.yaml (~/.dsh by default). setup-dsh adds a f3router route there — api: openai-completions, the gateway's /v1 base URL, and the live catalog as the route's models list, which DSH requires for a provider its installed catalog does not ship — and makes f3router/auto the default model. The key goes into $DSH_HOME/.env (mode 0600) as F3ROUTER_API_KEY, the environment layer DSH reads on its own, so nothing needs exporting. Both writes are structural merges: other routes, comments, and settings in the file are left as they were.

npx f3router setup-dsh --url http://localhost:3001 --api-key <unified-key>
npx @deepseek-ai/dsh web

Settings are hot-reloaded, so a running dsh uses the route on its next request. --profile <name> adds a second route (f3router-<name>) without changing the default model; --model <id> pins the default. Routes are declared text-only — add input: [text, image] to a model entry under f3router.models to send it images. DSH_HOME is honoured when set.

MiMo Code (mimo)

MiMo Code is an OpenCode derivative, so it takes any OpenAI-compatible endpoint as a custom entry under provider — an npm package to speak with (@ai-sdk/openai-compatible), the credentials in options, and a models map. setup-mimo writes that entry as f3router and names f3router/<model> as the default model.

The file is config.json in MiMo's own global config directory: $XDG_CONFIG_HOME/mimocode (~/.config/mimocode on macOS and Linux), or $MIMOCODE_HOME/config when MIMOCODE_HOME is set. That directory merges config.json, mimocode.json and mimocode.jsonc in that order, so the generated file is the weakest layer and anything you hand-write in mimocode.json still wins. The write itself is a structural merge, so other providers and settings already in config.json are left as they were.

npx f3router setup-mimo --url http://localhost:3001
export F3ROUTER_API_KEY=<unified-key>
mimo

The key is referenced as {env:F3ROUTER_API_KEY}, MiMo's own substitution syntax, so it stays out of the config file. There is no MIMOCODE_API_KEY or MIMOCODE_BASE_URL: MiMo's environment variables locate resources and toggle features, they are not a general fallback for config fields. Each model is declared with the limit.context and limit.output pair MiMo's schema requires, both taken from the live catalog.

AtomCode (atomcode)

AtomCode is AtomGit's terminal coding agent, written in Rust. It reads ~/.atomcode/config.toml, where a root default_provider key names one [providers.<id>] table and type = "openai" makes that table speak the OpenAI-compatible wire. setup-atomcode writes a [providers.f3router] table — base_url on the gateway's /v1, the unified key as api_key, the chosen model and its context_window from the live catalog — and sets default_provider to it. The write is a structural merge: the root key is placed above any existing tables, only the f3router table is replaced, and other [providers.*] tables and settings in the file are left as they were.

npx f3router setup-atomcode --url http://localhost:3001 --api-key <unified-key>
atomcode

AtomCode has no environment-variable fallback for the key, so it is written into the config file; the file is created with mode 0600 and a timestamped backup is taken before an existing one is changed. --model <id> pins the default model.

OpenClaw

OpenClaw is the always-on personal assistant that answers on WhatsApp, Telegram, Discord and the other channels its gateway connects to. It reads one JSON5 document, ~/.openclaw/openclaw.json, and every model endpoint is an entry under models.providers; a provider its bundled catalog does not know must spell out baseUrl, api and a non-empty models list. setup-openclaw writes a f3router provider there — api: openai-completions, the gateway's /v1 base URL, the live catalog as its models list — and makes f3router/auto the default model under agents.defaults.model.primary. The key is referenced as ${F3ROUTER_API_KEY}, OpenClaw's own substitution, and the value goes into ~/.openclaw/.env (mode 0600), the global env file OpenClaw loads on its own, so nothing needs exporting. Both writes are structural merges: other providers, fallbacks on the default model, and every other setting in the file are left as they were.

npx f3router setup-openclaw --url http://localhost:3001 --api-key <unified-key>
npm install -g openclaw@latest --allow-scripts=openclaw
openclaw agent exec --model f3router/auto "Say hello"   # no gateway daemon needed

A running openclaw gateway needs a restart to pick the change up. --profile <name> adds a second provider (f3router-<name>) without changing the default model; --model <id> pins the default. OpenClaw sends a bare SDK user agent to custom endpoints, so the provider carries a static User-Agent: openclaw header — that is what lights the "seen recently" badge on the Agents page. Models are declared text-only; add input: ["text", "image"] to a model entry under models.providers.f3router.models to send it images. OPENCLAW_CONFIG_PATH, OPENCLAW_STATE_DIR and OPENCLAW_HOME are honoured when set.

Hermes Agent (hermes)

Hermes Agent is Nous Research's self-improving agent, in the terminal or behind Telegram, Discord and the other messengers its gateway serves. ~/.hermes/config.yaml is its single source of truth for the endpoint: OPENAI_BASE_URL is ignored for anything but api.openai.com and OPENAI_API_KEY is only sent to OpenAI hosts, so a custom endpoint has to be declared in the model block. setup-hermes writes that block — provider: custom, api_mode: chat_completions, the gateway's /v1 as base_url, the chosen model as default with its context_length from the live catalog — and references the key as api_key: "${F3ROUTER_API_KEY}", Hermes's own substitution, with the value in ~/.hermes/.env (mode 0600), which Hermes loads on its own. A fresh install ships model: "", the "not configured" sentinel; replacing it is exactly what hermes setup would do, and it is what lets a headless first run skip the wizard. Every other key in the file is left as it was.

npx f3router setup-hermes --url http://localhost:3001 --api-key <unified-key>
curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash -s -- --skip-setup
hermes -z "Say hello"

A running hermes gateway needs a restart to pick the change up. --profile <name> adds a providers.f3router-<name> entry instead — picked inside a chat with /model custom:f3router-<name>:auto — and leaves the default model alone; --model <id> pins the default. Hermes sends a bare SDK user agent to custom endpoints, so the block carries default_headers: { User-Agent: hermes-agent }, which is what the Agents page's "seen recently" badge keys on. HERMES_HOME is honoured when set.

Pi (pi)

Pi is a minimal terminal agent harness you extend with your own extensions, skills and prompt templates. Endpoints it does not ship are provider entries in ~/.pi/agent/models.json. setup-pi adds a f3router provider there — api: openai-completions, the gateway's /v1 as baseUrl, and every model in the live catalog with its context window, the chosen model first — then sets defaultProvider and defaultModel in ~/.pi/agent/settings.json so a fresh pi starts on it. Pi does not load a .env file of its own, so the key is written into models.json, which is created with mode 0600. Other providers and settings in both files are left as they were.

npx f3router setup-pi --url http://localhost:3001 --api-key <unified-key>
npm install -g @earendil-works/pi-coding-agent
pi -p "Say hello"

A running session reloads models.json when /model is opened. --profile <name> adds a f3router-<name> provider instead, picked with pi --model f3router-<name>/<model> or from /model, and leaves the default model alone; --model <id> pins the default. Models are declared text-only with reasoning: false. To send images to a vision model, add "image" to its input. Pi sends a bare SDK user agent to custom endpoints, so the provider carries headers: { "User-Agent": "pi-coding-agent" }, which is what the Agents page's "seen recently" badge keys on. PI_CODING_AGENT_DIR is honoured when set.

Reasonix (reasonix)

Reasonix is a DeepSeek-native terminal coding agent built around prefix-cache stability. Every endpoint is a [[providers]] entry in ~/.reasonix/config.toml (%APPDATA%\reasonix on Windows, $REASONIX_HOME when set). setup-reasonix adds one named f3router — kind = "openai", the gateway's /v1 as base_url, and every model in the live catalog, the chosen model first — and points default_model at f3router/<model>. The entry names its key with api_key_env = "F3ROUTER_API_KEY"; the value goes to ~/.reasonix/.env (0600), which Reasonix loads itself, so nothing needs exporting. Your other [[providers]] entries and settings are left as they were, and a rerun replaces only the f3router entry.

npx f3router setup-reasonix --url http://localhost:3001 --api-key <unified-key>
npm install -g reasonix
reasonix -p "Say hello"

Every catalog model is in /model as f3router/<id>. --profile <name> adds a f3router-<name> entry instead, picked with reasonix --model f3router-<name>/<model>, and leaves the default model alone; --model <id> pins the default. A zero price keeps Reasonix's cost readout at $0. Reasonix Studio only lists the providers in [desktop].provider_access when that list is set, so add "f3router" there (or under Settings → Model → Access). Reasonix sends Reasonix/<version> as its user agent, which is what the Agents page's "seen recently" badge keys on.

QwenPaw

QwenPaw supports custom providers that use the OpenAI chat.completions API. Start f3router, create or copy a unified key from its dashboard, then open Settings → Models in the QwenPaw Console:

  1. Under Providers, choose Add Provider.
  2. Give it a Provider ID such as f3router and a Provider Name such as f3router, and set the API compatibility mode to OpenAI chat.completions.
  3. Open the new provider's settings and set Base URL to http://localhost:3001/v1 and API Key to your f3router unified key.
  4. On the provider's models page, add a model. Use auto as the Model ID to let f3router route the request, or copy a current id from GET http://localhost:3001/v1/models.
  5. Save, then pick the model as the default (or per chat) and send a test message.

If QwenPaw runs in a container, localhost refers to that container rather than the host running f3router. Use a hostname or host-gateway address that the QwenPaw container can reach, while keeping the /v1 suffix. Do not put the unified key in a URL, screenshot, or issue report. See QwenPaw's official model configuration guide for the current Console field names.

Native Gemini clients

Gemini CLI and Gemini-lineage clients can speak Google's wire format directly:

export GOOGLE_GEMINI_BASE_URL=http://localhost:3001
export GEMINI_API_KEY=f3router-your-unified-key
gemini

The native surface implements GET /v1beta/models, model metadata, generateContent, streamGenerateContent (including ?alt=sse), and countTokens. Authentication accepts x-goog-api-key, Bearer, or Gemini's ?key= fallback. Prefer the header: query credentials leak into history and proxy logs.

The Keys → Agents tab maps Gemini Pro, Flash, and Flash-Lite family names to Auto or a pinned catalog model.

Ollama clients

Ollama emulation is off by default. Enable one of these modes on Keys → Agents:

  • open-loopback: no key on this machine only. The socket peer must be 127.0.0.1/::1; enabling desktop LAN access does not widen it. Docker note: inside a container the socket peer is the Docker bridge IP, not loopback, so this mode refuses even host-local traffic through a published port — use key-required for Docker deployments.
  • key-required: clients must send Authorization: Bearer <unified-key>.

The exact endpoints are /api/tags, /api/chat, /api/generate, /api/show, /api/version, /api/embed, and legacy /api/embeddings. Streaming uses newline-delimited JSON, not SSE. Point Zed, JetBrains AI Assistant, or another Ollama-capable client at http://localhost:3001.

Headerless clients

If a client cannot set headers, create a separately revocable token on Keys → Agents and use:

http://localhost:3001/v1/t/<token>/chat/completions
http://localhost:3001/v1/t/<token>/responses
http://localhost:3001/v1/t/<token>/models

The same prefix exposes /api/chat and /api/tags. Never put the unified API key in a URL. URL tokens have independent hashes and immediate revocation because URLs routinely leak into shell history, reverse-proxy logs, and telemetry.

MCP server

On top of the OpenAI-compatible API, the router is an MCP server: agents can ask a f3router model to perform a text task and introspect the gateway mid-session (usable models and the params each one honors, provider health, usage and cache stats, routing strategy).

The MCP surface is a setting rather than an always-on endpoint (#925). Fresh installs start with it off; installs that already had provider keys configured when they upgraded keep it on, so an existing Claude Code or Cline session does not break on upgrade. Toggle it on the Keys page under Agent compatibility, or from the API:

curl -X PUT http://localhost:3001/api/settings/enable-mcp \
  -H "Authorization: Bearer <dashboard-token>" -H "Content-Type: application/json" \
  -d '{"enabled": true}'

While it is off, every verb on /mcp answers 403 with a JSON-RPC error saying so.

For Claude Code:

claude mcp add --transport http f3router http://localhost:3001/mcp \
  --header "Authorization: Bearer f3router-your-unified-key"

Any MCP client that speaks Streamable HTTP works the same way: point it at /mcp with the unified key as a Bearer token.

ChatGPT (private Secure MCP Tunnel)

ask_f3router makes f3router directly callable from a ChatGPT conversation. It uses the same /v1/chat/completions implementation as every OpenAI-compatible client, so model routing, fallback, response caching, prompt compression, token accounting, execution ids, and redacted provider errors stay consistent. The result includes the answer, requested and served model, usage, route, fallback trail, cache/compression state, and execution id.

The three relevant settings are deliberately separate:

Setting Where it lives Example
Base URL mcp.server_urls[].url in tunnel-client http://127.0.0.1:3001/mcp
API key F3ROUTER_MCP_AUTHORIZATION environment variable Bearer f3router-...
Default model f3router MCP_INFERENCE_DEFAULT_MODEL auto, a model id, or auto:<profile>
  1. Enable the MCP surface as shown above. Optionally set the default model before starting f3router:

    MCP_INFERENCE_DEFAULT_MODEL=auto
    MCP_INFERENCE_TIMEOUT_MS=120000
    
  2. Copy examples/chatgpt/tunnel-client.yaml.example outside the repository or into an ignored local file, replace its non-secret tunnel_id, and provide both secrets only through the process environment. The f3router variable contains the complete HTTP header value, including Bearer :

    $env:CONTROL_PLANE_API_KEY = '<OpenAI tunnel runtime key>'
    $env:F3ROUTER_MCP_AUTHORIZATION = 'Bearer <f3router unified key>'
    tunnel-client doctor --profile-file .\tunnel-client.yaml --explain
    tunnel-client run --profile-file .\tunnel-client.yaml
    
    export CONTROL_PLANE_API_KEY='<OpenAI tunnel runtime key>'
    export F3ROUTER_MCP_AUTHORIZATION='Bearer <f3router unified key>'
    tunnel-client doctor --profile-file ./tunnel-client.yaml --explain
    tunnel-client run --profile-file ./tunnel-client.yaml
    
  3. In ChatGPT, enable Settings → Security and login → Developer mode. Open the Plugins directory, create a developer-mode plugin, choose Tunnel, and select the tunnel id. Review the discovered tools before saving. Start with healthcheck; use ask_f3router for inference and list_models when selecting an explicit model.

The tunnel's static MCP headers are sent only to the configured f3router origin. Do not put either API key in the YAML file, MCP URL, screenshots, logs, or commits. A public plugin deployment is a different security model and needs a stable public HTTPS endpoint plus the authentication required by ChatGPT; the private tunnel above is the local-first path.

f3router is local-first and single-user by design. Your provider keys stay in your SQLite database, encrypted at rest, and requests go from your machine to the upstream providers you enabled.

VS Code ghost-text autocomplete (Continue)

f3router exposes /v1/completions for editor autocomplete clients that send legacy OpenAI prompt/suffix requests. Example Continue config:

models:
  - name: f3router Autocomplete
    provider: openai
    model: auto
    apiBase: http://localhost:3001/v1
    apiKey: f3router-your-unified-key
    useLegacyCompletionsEndpoint: true
    roles:
      - autocomplete

Context Handoff

When f3router falls over to a different model mid-conversation (quota, rate limit, cooldown), the new model has no idea it is picking up someone else's task. Context handoff adds a single compact system message to the outbound request that tells the new model exactly that:

f3router context handoff:
You are taking over an ongoing conversation from another model (groq:llama-3 → google:gemini-flash).
Continue the user's task using the conversation context already provided in this request.
Do not restart the task, re-ask already answered setup questions, or discard prior tool results.
Respect the user's latest message as the highest-priority instruction.

Recent session summary:
User: …
Assistant: …

Enable it in .env:

F3ROUTER_CONTEXT_HANDOFF=on_model_switch

How it works:

  • Messages per session are stored in memory (TTL: 3 hours).
  • Only injected when the selected model changes for a given session key.
  • Not injected on the first request, on same-model continuations, or if a handoff message is already present.
  • Session key: X-Session-Id header if present, otherwise SHA-1 of the first user message (same as sticky sessions).
  • Storage is in-memory only. Nothing is written to disk or logged.

Important: Context Handoff improves continuity for conversations routed through f3router. It cannot recover provider-internal hidden state or messages that were never sent to the proxy.