@caeliq/claude-code-router 2.1.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,12 +1,16 @@
1
- ![](blog/images/claude-code-router-img.png)
1
+ ![Claude Code Router — adaptive model routing, tool use, and thinking](blog/images/claude-code-router-img.png)
2
2
 
3
3
  [![](https://img.shields.io/github/license/musistudio/claude-code-router)](https://github.com/musistudio/claude-code-router/blob/main/LICENSE)
4
4
 
5
+ Claude Code Router is an adaptive LLM gateway for Claude Code. It routes each request to the most suitable model and provider while preserving tool calls, streaming, extended thinking, and multi-turn context across different APIs.
6
+
5
7
  ## ✨ Features
6
8
 
7
- - **Model Routing**: Route requests to different models based on your needs (e.g., background tasks, thinking, long context).
9
+ - **Adaptive Model Routing**: Route requests by scenario, including background tasks, thinking, long context, web search, and image workflows.
10
+ - **Tool Use & Thinking**: Preserve tool calls, tool results, and reasoning content across providers with different API formats.
8
11
  - **Multi-Provider Support**: Supports various model providers like OpenRouter, DeepSeek, Ollama, Gemini, Antigravity, Volcengine, SiliconFlow, Codex, Claude subscription, Qwen, Chrome On-Device, and Cursor (SDK).
9
12
  - **Request/Response Transformation**: Customize requests and responses for different providers using transformers.
13
+ - **Native Client Protocols**: Accept Anthropic Messages, OpenAI Chat Completions, and OpenAI Responses requests through the same router and fallback pipeline.
10
14
  - **Dynamic Model Switching**: Switch models on-the-fly within Claude Code using the `/model` command.
11
15
  - **CLI Model Management**: Manage models and providers directly from the terminal with `ccr model`.
12
16
  - **GitHub Actions Integration**: Trigger Claude Code tasks in your GitHub workflows.
@@ -41,20 +45,23 @@ This fork is based on [claude-code-router](https://github.com/musistudio/claude-
41
45
  #### Prerequisites
42
46
 
43
47
  Before you begin, ensure you have the following installed on your system:
44
- - **Docker & Docker Compose** (Recommended): The primary way to run the router. See [Docker Install Guide](https://docs.docker.com/get-docker/).
45
- - **Node.js** (Optional): Required to run from source, publish packages, or use the **Chrome On-Device** bridge. This fork requires **Node.js ≥ 22.13.0** (needed by `@cursor/sdk`). See [Node.js Download](https://nodejs.org/).
48
+ - **Docker** (Recommended): The primary way to run the router via the published image. See [Docker Install Guide](https://docs.docker.com/get-docker/).
49
+ - **Node.js** (Optional): Required to run from source, publish packages, or use the **Chrome On-Device** bridge. This fork requires **Node.js ≥ 22.19.0** (needed by `undici`). See [Node.js Download](https://nodejs.org/).
46
50
  - **Claude Code**: See the [official quickstart guide](https://code.claude.com/docs/en/quickstart) for installation instructions.
47
51
 
48
52
  #### Quick Start with Docker
49
53
 
50
- The fastest way to launch Claude Code Router is using Docker Compose:
54
+ The fastest way to launch Claude Code Router is the published Docker image:
51
55
 
52
56
  ```shell
53
- cd packages/server
54
- docker compose up --build -d
57
+ mkdir -p ~/.claude-code-router
58
+ docker run -d --name ccr \
59
+ -p 3456:3456 \
60
+ -v ~/.claude-code-router:/root/.claude-code-router \
61
+ ghcr.io/oakimov/claude-code-router:latest
55
62
  ```
56
63
 
57
- The Compose setup builds the server and UI into the `ccr` container, exposes the proxy on `http://localhost:3456`, and mounts configuration from `packages/server/ccr-config` to `/root/.claude-code-router` inside the container.
64
+ The image ships the server and UI, exposes the proxy on `http://localhost:3456`, and mounts `~/.claude-code-router` as the config directory (`/root/.claude-code-router` inside the container). Set `"HOST": "0.0.0.0"` in your `config.json` so the port mapping can reach the server. After changing the config, restart with `docker restart ccr`; view logs with `docker logs -f ccr`.
58
65
 
59
66
  ### 2. Configuration
60
67
 
@@ -68,8 +75,9 @@ The `config.json` file has several key sections:
68
75
  - **Logging Systems**: The Claude Code Router uses two separate logging systems:
69
76
  - **Server-level logs**: HTTP requests, API calls, and server events are logged using pino in the `~/.claude-code-router/logs/` directory with filenames like `ccr-*.log`
70
77
  - **Application-level logs**: Routing decisions and business logic events are logged in `~/.claude-code-router/claude-code-router.log`
71
- - **`APIKEY`** (optional): You can set a secret key to authenticate requests. When set, clients must provide this key in the `Authorization` header (e.g., `Bearer your-secret-key`) or the `x-api-key` header. Example: `"APIKEY": "your-secret-key"`.
78
+ - **`APIKEY`** (optional): You can set a secret key to authenticate requests. API clients can provide it in the `Authorization` header (e.g., `Bearer your-secret-key`) or the `x-api-key` header. The web UI exchanges it for an opaque `HttpOnly`, same-site session cookie and never stores the key in browser storage. UI sessions are kept in memory and require login again after CCR restarts. Example: `"APIKEY": "your-secret-key"`.
72
79
  - **`HOST`** (optional): You can set the host address for the server. If `APIKEY` is not set, the host will be forced to `127.0.0.1` for security reasons to prevent unauthorized access. Example: `"HOST": "0.0.0.0"`.
80
+ - **Rate limiting**: Every route has a default limit of 1000 requests per minute. The shared default is defined by `RATE_LIMIT_CONFIG` in `packages/shared/src/constants.ts`; change it there to update all route limits.
73
81
  - **`NON_INTERACTIVE_MODE`** (optional): When set to `true`, enables compatibility with non-interactive environments like GitHub Actions, Docker containers, or other CI/CD systems. This sets appropriate environment variables (`CI=true`, `FORCE_COLOR=0`, etc.) and configures stdin handling to prevent the process from hanging in automated environments. Example: `"NON_INTERACTIVE_MODE": true`.
74
82
 
75
83
  - **`Providers`**: Used to configure different model providers.
@@ -97,125 +105,34 @@ Claude Code Router supports environment variable interpolation for secure API ke
97
105
 
98
106
  This allows you to keep sensitive API keys in environment variables instead of hardcoding them in configuration files. The interpolation works recursively through nested objects and arrays.
99
107
 
100
- Here is a comprehensive example:
108
+ Here is a minimal configuration:
101
109
 
102
110
  ```json
103
111
  {
104
112
  "APIKEY": "your-secret-key",
105
- "PROXY_URL": "http://127.0.0.1:7890",
106
- "LOG": true,
107
- "API_TIMEOUT_MS": 600000,
108
- "NON_INTERACTIVE_MODE": false,
109
113
  "Providers": [
110
114
  {
111
115
  "name": "openrouter",
112
116
  "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
113
- "api_key": "sk-xxx",
114
- "models": [
115
- "google/gemini-2.5-pro-preview",
116
- "anthropic/claude-sonnet-4",
117
- "anthropic/claude-3.5-sonnet",
118
- "anthropic/claude-3.7-sonnet:thinking"
119
- ],
117
+ "api_key": "$OPENROUTER_API_KEY",
118
+ "models": ["anthropic/claude-sonnet-4"],
120
119
  "transformer": {
121
120
  "use": ["openrouter"]
122
121
  }
123
- },
124
- {
125
- "name": "deepseek",
126
- "api_base_url": "https://api.deepseek.com/chat/completions",
127
- "api_key": "sk-xxx",
128
- "models": ["deepseek-chat", "deepseek-reasoner"],
129
- "transformer": {
130
- "use": ["deepseek"],
131
- "deepseek-chat": {
132
- "use": ["tooluse"]
133
- }
134
- }
135
- },
136
- {
137
- "name": "ollama",
138
- "api_base_url": "http://localhost:11434/v1/chat/completions",
139
- "api_key": "ollama",
140
- "models": ["qwen2.5-coder:latest"]
141
- },
142
- {
143
- "name": "gemini",
144
- "api_base_url": "https://generativelanguage.googleapis.com/v1beta/models/",
145
- "api_key": "sk-xxx",
146
- "models": ["gemini-2.5-flash", "gemini-2.5-pro", "gemma-4-31b-it"],
147
- "transformer": {
148
- "use": ["gemini"]
149
- }
150
- },
151
- {
152
- "name": "volcengine",
153
- "api_base_url": "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
154
- "api_key": "sk-xxx",
155
- "models": ["deepseek-v3-250324", "deepseek-r1-250528"],
156
- "transformer": {
157
- "use": ["deepseek"]
158
- }
159
- },
160
- {
161
- "name": "modelscope",
162
- "api_base_url": "https://api-inference.modelscope.cn/v1/chat/completions",
163
- "api_key": "",
164
- "models": ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507"],
165
- "transformer": {
166
- "use": [
167
- [
168
- "maxtoken",
169
- {
170
- "max_tokens": 65536
171
- }
172
- ],
173
- "enhancetool"
174
- ],
175
- "Qwen/Qwen3-235B-A22B-Thinking-2507": {
176
- "use": ["reasoning"]
177
- }
178
- }
179
- },
180
- {
181
- "name": "dashscope",
182
- "api_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
183
- "api_key": "",
184
- "models": ["qwen3-coder-plus"],
185
- "transformer": {
186
- "use": [
187
- [
188
- "maxtoken",
189
- {
190
- "max_tokens": 65536
191
- }
192
- ],
193
- "enhancetool"
194
- ]
195
- }
196
- },
197
- {
198
- "name": "aihubmix",
199
- "api_base_url": "https://aihubmix.com/v1/chat/completions",
200
- "api_key": "sk-",
201
- "models": [
202
- "glm-4.5",
203
- "claude-opus-4-20250514",
204
- "gemini-2.5-pro"
205
- ]
206
122
  }
207
123
  ],
208
124
  "Router": {
209
- "default": "deepseek,deepseek-chat",
210
- "background": "ollama,qwen2.5-coder:latest",
211
- "think": "deepseek,deepseek-reasoner",
212
- "longContext": "openrouter,google/gemini-2.5-pro-preview",
213
- "longContextThreshold": 60000,
214
- "webSearch": "gemini,gemini-2.5-flash"
125
+ "default": "openrouter,anthropic/claude-sonnet-4"
215
126
  }
216
127
  }
217
128
  ```
218
129
 
130
+ > **See also**: The complete config reference, per-provider examples, and routing
131
+ > options are in `docs/docs/server/config/basic.md`,
132
+ > `docs/docs/server/config/providers.md`,
133
+ > `docs/docs/server/config/transformers.md`, and
134
+ > `docs/docs/server/config/routing.md`.
135
+
219
136
  #### Adding a New Provider
220
137
 
221
138
  If you want to add a new provider and automatically discover its models, follow these steps:
@@ -278,6 +195,19 @@ You can also configure Claude Code to always use the router by editing its `sett
278
195
 
279
196
  This approach lets you run `claude` directly without needing `ccr code`.
280
197
 
198
+ #### Via OpenAI-compatible clients
199
+
200
+ OpenAI SDK clients can use CCR without an Anthropic compatibility layer:
201
+
202
+ ```shell
203
+ export OPENAI_BASE_URL=http://127.0.0.1:3456/v1
204
+ export OPENAI_API_KEY=your-router-api-key
205
+ ```
206
+
207
+ CCR accepts Chat Completions at `/v1/chat/completions` (alias `/chat/completions`) and Responses at `/v1/responses` (alias `/responses`). Send a model as `provider,model` to select a destination explicitly, or send a bare model and configure `Router.default`. Both JSON and SSE responses are converted back to the protocol used by the client.
208
+
209
+ The compatibility layer supports ordinary text, images, function tools/results, reasoning effort, and usage reporting. Stateful Responses features such as `store: true`, `previous_response_id`, conversations, background mode, provider file IDs, and unsupported hosted tools return an explicit 400 error instead of being silently discarded.
210
+
281
211
  > **Note**: After modifying the configuration file, you need to restart the service for the changes to take effect:
282
212
  >
283
213
  > ```shell
@@ -294,7 +224,7 @@ ccr ui
294
224
 
295
225
  This will open a web-based interface where you can easily view and edit your `config.json` file.
296
226
 
297
- ![UI](/blog/images/ui.png)
227
+ ![UI](blog/images/ui.png)
298
228
 
299
229
  ### 5. CLI Model Management
300
230
 
@@ -305,24 +235,9 @@ ccr model
305
235
  ```
306
236
  ![](blog/images/models.gif)
307
237
 
308
- This command provides an interactive interface to:
238
+ `ccr model` lets you view configured models, switch models per scenario, add models, and create providers with transformer configuration — all with validation and prompts.
309
239
 
310
- - View current configuration:
311
- - See all configured models (default, background, think, longContext, webSearch, image)
312
- - Switch models: Quickly change which model is used for each router type
313
- - Add new models: Add models to existing providers
314
- - Create new providers: Set up complete provider configurations including:
315
- - Provider name and API endpoint
316
- - API key
317
- - Available models
318
- - Transformer configuration with support for:
319
- - Multiple transformers (openrouter, deepseek, gemini, etc.)
320
- - Transformer options (e.g., maxtoken with custom limits)
321
- - Provider-specific routing (e.g., OpenRouter provider preferences)
322
-
323
- The CLI tool validates all inputs and provides helpful prompts to guide you through the configuration process, making it easy to manage complex setups without editing JSON files manually.
324
-
325
- For non-interactive model discovery, you can also test provider access and list remote models directly:
240
+ For non-interactive model discovery from any provider with a model-list endpoint:
326
241
 
327
242
  ```shell
328
243
  ccr model get claude
@@ -330,44 +245,11 @@ ccr model get gemini
330
245
  ccr model get openai
331
246
  ```
332
247
 
333
- This command:
334
- - Calls the provider's model-list endpoint using the configured API key
335
- - Prints the remote models returned by the provider
336
- - Prompts to append missing models and remove configured models that the API no longer returns
248
+ `ccr model get <provider>` fetches remote models, then prompts to append missing ones and remove configured ones the API no longer returns. Built-in endpoint support exists for `anthropic`/`claude`, `gemini`, `openai`, `codex`, and `cursor`. Other providers can use `models_api_url` plus a `models_response_format` (`listPath`, `idPath`, `stripPrefix`) to parse custom JSON responses.
337
249
 
338
- Built-in endpoint support is included for `anthropic`/`claude`, `gemini`, `openai`, `codex`, and `cursor`. For Claude subscription providers using `claude-auth`, discovery reads `~/.claude-code-router/claude_auth.json` and sends the required Anthropic OAuth beta headers; the provider `api_key` is only a placeholder in that mode. For other providers, you can configure `models_api_url` and a custom `models_response_format` to handle different JSON response structures.
339
-
340
- For the `codex` provider, model discovery sends the current Codex CLI `client_version` because the ChatGPT backend can gate newly released Codex model slugs by client version. CCR defaults to the latest stable version known at release time; override it with `codex_client_version` on the provider or `CCR_CODEX_CLIENT_VERSION` when testing a newer Codex CLI rollout. Runtime Codex requests are handled separately by the core Codex transformer, which spoofs the Codex CLI request version and identity headers without depending on CCR's CLI package.
341
-
342
- The `models_response_format` object supports:
343
- - `listPath`: JSON path to the array of models (e.g., `"data"`, `"models"`, or `""` for root array)
344
- - `idPath`: Field name within each model object to use as ID (e.g., `"id"`, `"name"`, `"slug"`)
345
- - `stripPrefix`: Optional prefix to remove from model IDs (e.g., `"models/"`)
346
-
347
- Example:
348
-
349
- ```json
350
- {
351
- "name": "together.ai",
352
- "api_base_url": "https://api.together.ai/v1/chat/completions",
353
- "models_api_url": "https://api.together.ai/v1/models",
354
- "api_key": "$TOGETHERAI_API_KEY",
355
- "models": [],
356
- "models_response_format": {
357
- "listPath": "",
358
- "idPath": "id"
359
- }
360
- }
361
- ```
362
-
363
- You can also override these settings via CLI flags for testing:
364
- ```shell
365
- ccr model get my-provider --list-path data --id-path id --strip-prefix "v1/"
366
- ```
367
-
368
- If the provider returns model changes, `ccr model get <provider>` can append missing entries and remove unavailable configured entries, each behind a separate confirmation prompt.
369
-
370
- > **Note**: After syncing models into `config.json`, restart the service with `ccr restart` so the updated provider list is picked up by the running server.
250
+ > **See also**: `docs/docs/server/guides/model-discovery.md` and `docs/docs/cli/commands/model-get.md`.
251
+ >
252
+ > **Note**: After syncing models into `config.json`, restart the service with `ccr restart`.
371
253
 
372
254
  #### Antigravity Authentication
373
255
 
@@ -403,9 +285,7 @@ Example provider:
403
285
  }
404
286
  ```
405
287
 
406
- Why those Gemini options:
407
- - **`cachedContent: false`** — Antigravity has no Google `cachedContents` resource. The Gemini default is `true`; leaving it on causes 404s.
408
- - **`thoughtSignatureFallback: "skip"`** — explicit form of the default. When a tool call is replayed without a cached `thoughtSignature`, CCR stamps Google's `skip_thought_signature_validator` sentinel so the gateway does not 400. Only change this to `"none"` if your endpoint rejects that sentinel.
288
+ `cachedContent: false` is required — Antigravity has no Google `cachedContents` resource, and leaving the Gemini default (`true`) causes 404s. The `thoughtSignatureFallback` option is covered in `docs/docs/server/config/transformers.md`.
409
289
 
410
290
  > **Note**: Keep the CCR server running during auth. Using Antigravity IDE OAuth client credentials from a non-IDE client may violate Google's terms.
411
291
 
@@ -413,248 +293,74 @@ Why those Gemini options:
413
293
 
414
294
  The Codex provider supports two authentication modes:
415
295
 
416
- - **OAuth** via `ccr codex-auth`
417
- - **PAT** via `api_key: "at-..."`
418
-
419
- ##### OAuth mode
420
-
421
- Before using Codex models with OAuth, authenticate with your OpenAI account:
422
-
423
- ```shell
424
- ccr codex-auth
425
- ```
426
-
427
- This command:
428
- 1. Opens your browser to the OpenAI OAuth authorization page
429
- 2. After you sign in, the OAuth callback is handled by the running CCR server
430
- 3. Tokens are stored in `~/.claude-code-router/codex_auth.json`
431
- 4. The CLI and server independently refresh tokens five minutes before expiry
432
-
433
- CCR derives the selected ChatGPT workspace and FedRAMP routing state from the
434
- OAuth ID token. Runtime requests and `ccr model get codex` both send the same
435
- Codex bearer, account, and routing headers. Refreshes use an atomic credential
436
- file and a cross-process lock so a separately running CLI and server cannot
437
- reuse the same rotating refresh token. A runtime OAuth 401 performs one guarded
438
- credential reload/refresh retry.
439
-
440
- > **Note**: The server must be running for `ccr codex-auth` to work, as it hosts the OAuth callback endpoint.
441
-
442
- **Running with Docker**:
296
+ - **OAuth** via `ccr codex-auth` — browser flow handled by the CCR server callback on port `1455` (Docker maps `1455:3456`); tokens stored in `~/.claude-code-router/codex_auth.json` and auto-refreshed.
297
+ - **PAT** via a literal `api_key: "at-..."` (or an env var containing an `at-` token) — skips `ccr codex-auth`.
443
298
 
444
- The OAuth callback uses port `1455`, which is mapped to the CCR server port in `docker-compose.yml` (`"1455:3456"`). When running in Docker:
445
-
446
- ```shell
447
- docker exec -it claude-code-router ccr codex-auth
448
- ```
299
+ An `at-` value is always treated as a PAT and never silently falls back to OAuth; any other placeholder selects OAuth tokens.
449
300
 
450
- The CLI prints a URL to open in your host browser. After signing in, the browser redirects to `http://localhost:1455/auth/callback`, which Docker forwards to the container. Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
451
-
452
- ##### PAT mode
453
-
454
- If your provider `api_key` starts with `at-`, CCR treats it as a Codex Personal Access Token and uses it directly. In PAT mode, you do **not** run `ccr codex-auth`.
455
-
456
- ```json
457
- {
458
- "name": "codex",
459
- "api_base_url": "https://chatgpt.com/backend-api/codex",
460
- "api_key": "at-your-personal-access-token",
461
- "models": ["gpt-5.4"],
462
- "transformer": {
463
- "use": ["codex"]
464
- }
465
- }
466
- ```
467
-
468
- CCR resolves the PAT's account, user, plan, and FedRAMP metadata through
469
- OpenAI's `/whoami` endpoint before calling the Codex backend. Both runtime
470
- requests and `ccr model get codex` then send `Authorization`,
471
- `ChatGPT-Account-ID`, and `X-OpenAI-Fedramp` when required. Metadata requests
472
- are deduplicated and cached briefly by the server.
473
-
474
- Auth mode is explicit: an `at-` value is always treated as a PAT. An invalid or
475
- revoked PAT fails as PAT authentication and is never silently replaced with
476
- OAuth. Any non-PAT placeholder selects OAuth tokens from
477
- `~/.claude-code-router/codex_auth.json`.
478
-
479
- > **See also**: Full Codex setup and troubleshooting are documented in `docs/docs/server/guides/codex.md`.
301
+ > **See also**: Full Codex setup, both auth modes, provider config, and troubleshooting are in `docs/docs/server/guides/codex.md`.
480
302
 
481
303
  #### Cursor Provider Authentication
482
304
 
483
- The Cursor provider uses the official `@cursor/sdk` (no browser OAuth CLI). Auth resolve order:
484
-
485
- 1. Provider `api_key` starting with `crsr_` (Cursor dashboard API key)
486
- 2. Otherwise `CURSOR_API_KEY` from the environment
487
-
488
- Example provider:
489
-
490
- ```json
491
- {
492
- "name": "cursor",
493
- "api_base_url": "https://cursor.com",
494
- "api_key": "$CURSOR_API_KEY",
495
- "models": ["composer-2"],
496
- "transformer": {
497
- "use": [
498
- [
499
- "cursor-sdk",
500
- {
501
- "cursorMode": "bridge"
502
- }
503
- ]
504
- ]
505
- }
506
- }
507
- ```
508
-
509
- - **bridge** (default): Claude Code hosts tools; Cursor built-ins are denied in an isolated workspace under `~/.claude-code-router/cursor-sdk-workspaces/`
510
- - Cursor builds its harness prompt server-side from the SDK workspace root, so bridge mode grounds the model in the host environment instead: the project root and platform from the incoming `<env>` block are injected through the workspace `AGENTS.md`, the head and tail of the agent prompt, and the built-in denial message. Host tool calls whose arguments reference the isolated workspace are intercepted, answered with a correction rather than forwarded to Claude Code, and counted in the session metrics. Isolated workspaces are removed with their session and swept when orphaned.
511
- - Discover models with `ccr model get cursor` (lists via `Cursor.models.list`, not REST `/models`)
512
- - Docker Compose passes `CURSOR_API_KEY` into the container when set; local Cursor sandboxing is forced off in Docker
513
- - Cursor prompt caching is native to the held-open SDK agent session. CCR reports per-request token estimates to Claude Code and maps SDK cache-read deltas back as bounded Anthropic cache-read usage.
514
- - Cursor thinking is forwarded from both SDK stream `thinking` messages and token-level `Agent.send({ onDelta })` `thinking-delta` updates, then closed with a synthetic signature so Claude Code can render it as Anthropic extended thinking. Claude Code 2.1.89+ hides interactive thinking summaries by default; enable `"showThinkingSummaries": true` in the settings file passed to Claude Code to display them. This is a client rendering setting: CCR still transports the thinking block when it is disabled.
515
- - Cursor turns are coordinated per Claude conversation. Identical overlapping retries share one bounded response producer (and recent completed result), so only one consumer reads the Cursor iterator and only one `agent.send` is submitted. Stopping the last subscriber awaits bounded run/iterator retirement before re-entry. Pure, exactly matched tool results resume a live parked run; a rejected result plus meaningful replacement text, an unmatched/dead run, or divergent transcript retires the old agent and replays the full conversation. Idle agents receive slim follow-ups only when the incoming transcript is exactly the committed host-visible transcript plus one supported user message; larger suffixes are fully replayed. CCR does not use `local.force` to bypass Cursor's active-run guard.
305
+ The Cursor provider runs models in-process via the official `@cursor/sdk` (no browser OAuth CLI). Auth resolves from the provider `api_key` starting with `crsr_`, then `CURSOR_API_KEY`. Default **bridge** mode keeps Claude Code as the tool host; Cursor built-ins are denied in an isolated workspace. Discover models with `ccr model get cursor`.
516
306
 
517
- > **See also**: Full Cursor setup is documented in `docs/docs/server/guides/cursor.md`.
307
+ > **See also**: Full Cursor setup, bridge/plan/agent modes, and configuration are in `docs/docs/server/guides/cursor.md`.
518
308
 
519
309
  #### Claude Subscription Authentication
520
310
 
521
- The Claude subscription provider uses OAuth to authenticate with Anthropic's API using your Claude Pro or Max subscription. Before using Claude models this way, you must authenticate:
311
+ Route Claude Code through your Claude Pro or Max subscription via OAuth:
522
312
 
523
313
  ```shell
524
314
  ccr claude-auth
525
315
  ```
526
316
 
527
- This command:
528
- 1. Opens your browser to the Claude OAuth authorization page
529
- 2. After you sign in, the OAuth callback is handled by the running CCR server on port `1455`
530
- 3. Tokens are stored in `~/.claude-code-router/claude_auth.json`
531
- 4. The `claude-auth` transformer automatically refreshes tokens when they expire
532
-
533
- > **Note**: The server must be running for `ccr claude-auth` to work, as it hosts the OAuth callback endpoint on port 1455.
534
-
535
- **Running with Docker**:
536
-
537
- The OAuth callback uses port `1455`, which is mapped to the CCR server port in `docker-compose.yml` (`"1455:3456"`). When running in Docker:
538
-
539
- ```shell
540
- docker exec -it claude-code-router ccr claude-auth
541
- ```
542
-
543
- The CLI prints a URL to open in your host browser. After signing in, the browser redirects to `http://localhost:1455/callback`, which Docker forwards to the container. Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
544
-
545
- A Claude subscription provider requires the `claude-auth` + `Anthropic` transformer chain:
546
-
547
- ```json
548
- {
549
- "name": "claude-subscription",
550
- "api_base_url": "https://api.anthropic.com",
551
- "api_key": "no-key",
552
- "models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
553
- "transformer": {
554
- "use": ["claude-auth", "Anthropic"]
555
- }
556
- }
557
- ```
558
-
559
- > **See also**: Full Claude subscription setup is documented in `docs/docs/server/guides/claude-auth.md`.
317
+ The OAuth flow is handled by the CCR server callback on port `1455` (Docker maps `1455:3456`); tokens are stored in `~/.claude-code-router/claude_auth.json` and auto-refreshed. A Claude subscription provider requires the `claude-auth` + `Anthropic` transformer chain.
560
318
 
561
- **Custom Transformers:**
319
+ > **See also**: Full setup, the transformer chain, client classification, and billing/identity details are in `docs/docs/server/guides/claude-auth.md`.
562
320
 
563
321
  #### Qwen Provider Authentication
564
322
 
565
- The Qwen provider uses a single JWT to authenticate with the Qwen Chat backend. Before using Qwen models, you must save a token to your local CCR config:
323
+ Authenticate with Qwen Chat by saving a JWT from `chat.qwen.ai` localStorage:
566
324
 
567
325
  ```shell
568
326
  ccr qwen-auth
569
327
  ```
570
328
 
571
- This command:
572
- 1. Prints a URL (`http://127.0.0.1:<port>/qwen/auth`) for the in-browser auth page
573
- 2. The page offers two options:
574
- - **Bookmarklet (recommended)**: drag the "Get Qwen Token" link to your bookmarks bar, then click it on the signed-in Qwen page. The token is sent back to CCR automatically.
575
- - **Manual paste**: sign in at `chat.qwen.ai`, open dev tools (F12) → Console, run `copy(localStorage.getItem('token'))`, paste the JWT into the form and submit.
576
- 3. The token is validated against `qwen.aikit.club/v1/validate` and saved to `~/.claude-code-router/qwen_auth.json` (mode 0600)
577
- 4. The `qwen-auth` transformer automatically refreshes the token when it nears expiry (within 6 hours)
578
-
579
- > **Note**: The server must be running for `ccr qwen-auth` to work, as it hosts the auth form at `/qwen/auth`. Unlike the Codex flow, no OAuth callback is required — the token is pasted directly into the form.
580
-
581
- **Running with Docker**:
582
-
583
- The Qwen auth page is served on the regular CCR port (no separate callback port). When running in Docker:
584
-
585
- ```shell
586
- docker exec -it claude-code-router ccr qwen-auth
587
- ```
588
-
589
- The CLI prints a URL to open in your host browser (`http://localhost:3456/qwen/auth`, which Docker forwards to the container). Tokens persist across container restarts via the volume-mounted `./ccr-config` directory.
329
+ The CCR server hosts an auth page at `/qwen/auth` offering a bookmarklet or manual paste. The token is validated, saved to `~/.claude-code-router/qwen_auth.json`, and auto-refreshed. The Qwen provider requires the `qwen-auth` + `reasoning` + `OpenAI` transformer chain.
590
330
 
591
- **Custom host/port for the bookmarklet**: The bookmarklet's redirect target is hardcoded into the JS because it runs in the Qwen page's context (with no knowledge of CCR's address). By default it points to `http://127.0.0.1:3456`. If your CCR server is on a different host or port, set the `QWEN_AUTH_REDIRECT` env var before starting the server, e.g. `QWEN_AUTH_REDIRECT=http://192.168.1.10:8080` — the bookmarklet will then redirect to that address.
331
+ > **See also**: Full Qwen setup and provider config are in `docs/docs/server/guides/qwen.md`.
592
332
 
593
333
  #### Chrome On-Device Bridge
594
334
 
595
- The `chrome-on-device` transformer requires a bridge process running on the host to communicate with Chrome's Gemini Nano model:
335
+ Use Chrome's built-in Gemini Nano (~4GB local model) with zero API cost via a host-side bridge:
596
336
 
597
337
  ```bash
598
- # Start the bridge (default: port 3457, CDP port 9222)
599
- ccr chrome-bridge
600
-
601
- # Custom ports
338
+ ccr chrome-bridge # default: port 3457, CDP 9222
602
339
  ccr chrome-bridge --port 3457 --cdp 9222
603
340
  ```
604
341
 
605
- The bridge:
606
- 1. Checks if Chrome is running with remote debugging enabled (port 9222)
607
- 2. If not, launches Chrome with the required flags (`--remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug-profile`)
608
- 3. Connects to Chrome via Puppeteer/CDP with a 5-minute protocol timeout to handle slow model inference
609
- 4. Loads a page that accesses the Prompt API (`window.LanguageModel`) and maintains a persistent `LanguageModel` session across all requests — conversation history is carried forward naturally within the session, not rebuilt per request
610
- 5. Replaces Claude Code's system prompt with a minimal tool-focused one (5 core tools), using `responseConstraint` (JSON Schema) to force the model to emit structured JSON with `{text, tool_calls[]}` fields
611
- 6. Exposes an OpenAI-compatible HTTP API on `0.0.0.0:3457`:
612
- - `GET /v1/models` — returns available models with live context usage
613
- - `GET /v1/models/{model_name}` — returns individual model info (display_name, max_input_tokens, capabilities)
614
- - `POST /v1/chat/completions` — chat completions with streaming and non-streaming support
615
- - `GET /health` — health check
342
+ The bridge connects to Chrome's Prompt API over CDP, maintains persistent model sessions, and exposes an OpenAI-compatible API (`/v1/chat/completions`) on `127.0.0.1:3457`. It replaces Claude Code's system prompt with a minimal tool-focused one and uses `responseConstraint` (JSON Schema) to force structured `{text, tool_calls[]}` output.
616
343
 
617
- **Prerequisites**: Chrome flags must be enabled (see Chrome On-Device Provider Configuration section). The model (~4GB) must be downloaded.
344
+ **Prerequisites**: enable `chrome://flags/#optimization-guide-on-device-model` and `chrome://flags/#prompt-api-for-gemini-nano-multimodal-input`, restart Chrome, and let the ~4GB model download.
618
345
 
619
- > **Note for Docker**: The bridge runs on the Docker **host**, not inside the container. Set the provider host to `http://host.docker.internal:3457` in your `config.json`.
346
+ > **Note for Docker**: The bridge runs on the Docker **host**, not inside the container set the provider host to `http://host.docker.internal:3457`. Full setup, provider config, features, and limitations are in `docs/docs/server/guides/chrome-on-device.md`.
620
347
 
621
348
  ### 6. Presets Management
622
349
 
623
- Presets allow you to save, share, and reuse configurations easily. You can export your current configuration as a preset and install presets from files or URLs.
350
+ Save, share, and reuse configurations:
624
351
 
625
352
  ```shell
626
- # Export current configuration as a preset
627
- ccr preset export my-preset
628
-
629
- # Export with metadata
353
+ ccr preset export my-preset # export current config as a preset
630
354
  ccr preset export my-preset --description "My OpenAI config" --author "Your Name" --tags "openai,production"
631
-
632
- # Install a preset from local directory
633
- ccr preset install /path/to/preset
634
-
635
- # List all installed presets
355
+ ccr preset install /path/to/preset # install from a directory
636
356
  ccr preset list
637
-
638
- # Show preset information
639
357
  ccr preset info my-preset
640
-
641
- # Delete a preset
642
358
  ccr preset delete my-preset
643
359
  ```
644
360
 
645
- **Preset Features:**
646
- - **Export**: Save your current configuration as a preset directory (with manifest.json)
647
- - **Install**: Install presets from local directories
648
- - **Sensitive Data Handling**: API keys and other sensitive data are automatically sanitized during export (marked as `{{field}}` placeholders)
649
- - **Dynamic Configuration**: Presets can include input schemas for collecting required information during installation
650
- - **Version Control**: Each preset includes version metadata for tracking updates
361
+ Presets store your configuration (plus metadata) as a directory with `manifest.json`. Sensitive fields are sanitized to `{{field}}` placeholders on export, and presets can include input schemas to collect required values (e.g. API keys) at install time.
651
362
 
652
- **Preset File Structure:**
653
- ```
654
- ~/.claude-code-router/presets/
655
- ├── my-preset/
656
- │ └── manifest.json # Contains configuration and metadata
657
- ```
363
+ > **See also**: `docs/docs/cli/commands/preset.md`.
658
364
 
659
365
  ### 7. Activate Command (Environment Variables Setup)
660
366
 
@@ -682,299 +388,24 @@ The `activate` command sets the following environment variables:
682
388
 
683
389
  > **Note**: Make sure the Claude Code Router service is running (`ccr start`) before using the activated environment variables. The environment variables are only valid for the current shell session. To make them persistent, you can add `eval "$(ccr activate)"` to your shell configuration file (e.g., `~/.zshrc` or `~/.bashrc`).
684
390
 
685
- #### Providers
686
-
687
- The `Providers` array is where you define the different model providers you want to use. Each provider object requires:
391
+ #### Providers and Transformers
688
392
 
689
- - `name`: A unique name for the provider.
690
- - `api_base_url`: The full API endpoint for chat completions.
691
- - `api_key`: Your API key for the provider.
692
- - `models`: A list of model names available from this provider.
693
- - `transformer` (optional): Specifies transformers to process requests and responses.
393
+ The `Providers` array defines each provider: `name`, `api_base_url`, `api_key`, `models`, and an optional `transformer` object. The `transformer.use` list applies transformers globally (all models) or per model key, and some transformers accept options via a nested `[name, options]` array.
694
394
 
695
- #### Transformers
696
-
697
- Transformers allow you to modify the request and response payloads to ensure compatibility with different provider APIs.
698
-
699
- - **Global Transformer**: Apply a transformer to all models from a provider. In this example, the `openrouter` transformer is applied to all models under the `openrouter` provider.
700
- ```json
701
- {
702
- "name": "openrouter",
703
- "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
704
- "api_key": "sk-xxx",
705
- "models": [
706
- "google/gemini-2.5-pro-preview",
707
- "anthropic/claude-sonnet-4",
708
- "anthropic/claude-3.5-sonnet"
709
- ],
710
- "transformer": { "use": ["openrouter"] }
711
- }
712
- ```
713
- - **Model-Specific Transformer**: Apply a transformer to a specific model. In this example, the `deepseek` transformer is applied to all models, and an additional `tooluse` transformer is applied only to the `deepseek-chat` model.
714
-
715
- ```json
716
- {
717
- "name": "deepseek",
718
- "api_base_url": "https://api.deepseek.com/chat/completions",
719
- "api_key": "sk-xxx",
720
- "models": ["deepseek-chat", "deepseek-reasoner"],
721
- "transformer": {
722
- "use": ["deepseek"],
723
- "deepseek-chat": { "use": ["tooluse"] }
724
- }
725
- }
726
- ```
727
-
728
- - **Passing Options to a Transformer**: Some transformers, like `maxtoken`, accept options. To pass options, use a nested array where the first element is the transformer name and the second is an options object.
729
- ```json
730
- {
731
- "name": "siliconflow",
732
- "api_base_url": "https://api.siliconflow.cn/v1/chat/completions",
733
- "api_key": "sk-xxx",
734
- "models": ["moonshotai/Kimi-K2-Instruct"],
735
- "transformer": {
736
- "use": [
737
- [
738
- "maxtoken",
739
- {
740
- "max_tokens": 16384
741
- }
742
- ]
743
- ]
744
- }
745
- }
746
- ```
395
+ > **See also**: Provider schema and per-provider examples are in `docs/docs/server/config/providers.md`; the transformer reference and option passing are in `docs/docs/server/config/transformers.md`.
747
396
 
748
397
  **Available Built-in Transformers:**
749
398
 
750
- - `Anthropic`:If you use only the `Anthropic` transformer, it will preserve the original request and response parameters(you can use it to connect directly to an Anthropic endpoint).
751
- - `deepseek`: Adapts requests/responses for DeepSeek API.
752
- - `gemini`: Adapts requests/responses for Gemini API (also the dialect stage used with Antigravity; same options apply to `vertex-gemini`). Claude Code's effort setting (sent as `output_config.effort`) drives thinking depth: `thinkingLevel` for Gemini 3+ (`low`/`high` on Gemini 3 Pro, plus `medium` on later Pro minors, plus `minimal` on Flash/Lite) or `thinkingBudget` for Gemini 2.5 and Claude-via-Antigravity never both (the API rejects that mix). Efforts outside a family's range round up (`medium` on Gemini 3 Pro, `xhigh`/`max` anywhere → `high`), and the configured model id is never rewritten (a tier-pinned `gemini-3-pro-low` keeps talking to `gemini-3-pro-low`). Pass options as `["gemini", { ... }]`:
753
- - **`cachedContent`** (boolean, default `true`): whether CCR may use Google's separate **`cachedContents` HTTP resource** to store/reuse prompt prefixes on the public Gemini API. This is Gemini server-side context cache — **not** Anthropic `cache_control` and **not** Claude Code's local prompt cache. Leave `true` for normal Gemini; set **`false` for Antigravity** (and any gateway without `cachedContents`) or you get 404s.
754
- - **`thoughtSignatureFallback`** (`"skip"` \| `"none"`, default `"skip"`): what to do when a replayed tool call has no cached Gemini `thoughtSignature` (Claude Code's Anthropic `tool_use` cannot carry that field, so CCR caches signatures by tool-call id and restores them; a miss otherwise 400s). `"skip"` means stamp Google's documented sentinel `skip_thought_signature_validator` on the **first** `functionCall` of the step — the value name refers to that sentinel, **not** “disable the fallback.” Leave `"skip"` for Gemini/Antigravity; set `"none"` only if the endpoint rejects the sentinel (some Vertex). Real cached signatures are always preferred; the sentinel is a last resort.
755
- - `mistral`: Adapts requests/responses for Mistral API.
756
- - `openrouter`: Adapts requests/responses for OpenRouter API. It can also accept a `provider` routing parameter to specify which underlying providers OpenRouter should use. For more details, refer to the [OpenRouter documentation](https://openrouter.ai/docs/features/provider-routing). See an example below:
757
- ```json
758
- "transformer": {
759
- "use": ["openrouter"],
760
- "moonshotai/kimi-k2": {
761
- "use": [
762
- [
763
- "openrouter",
764
- {
765
- "provider": {
766
- "only": ["moonshotai/fp8"]
767
- }
768
- }
769
- ]
770
- ]
771
- }
772
- }
773
- ```
774
- - `groq`: Adapts requests/responses for groq API.
775
- - `maxtoken`: Sets a specific `max_tokens` value.
776
- - `tooluse`: Optimizes tool usage for certain models via `tool_choice`.
777
- - `gemini-cli` (experimental): Unofficial support for Gemini via Gemini CLI [gemini-cli.js](https://gist.github.com/musistudio/1c13a65f35916a7ab690649d3df8d1cd).
778
- - `reasoning`: Used to process the `reasoning_content` field.
779
- - `sampling`: Used to process sampling information fields such as `temperature`, `top_p`, `top_k`, and `repetition_penalty`.
780
- - `enhancetool`: Adds a layer of error tolerance to the tool call parameters returned by the LLM (this will cause the tool call information to no longer be streamed).
781
- - `cleancache`: Clears the `cache_control` field from requests.
782
- - `vertex-gemini`: Handles the Gemini API using Vertex authentication.
783
- - `chutes-glm` Unofficial support for GLM 4.5 model via Chutes [chutes-glm-transformer.js](https://gist.github.com/vitobotta/2be3f33722e05e8d4f9d2b0138b8c863).
784
- - `qwen-cli` (experimental): Unofficial support for qwen3-coder-plus model via Qwen CLI [qwen-cli.js](https://gist.github.com/musistudio/f5a67841ced39912fd99e42200d5ca8b).
785
- - `rovo-cli` (experimental): Unofficial support for gpt-5 via Atlassian Rovo Dev CLI [rovo-cli.js](https://gist.github.com/SaseQ/c2a20a38b11276537ec5332d1f7a5e53).
786
- - `codex`: Adapts requests/responses for the Codex (ChatGPT) backend API. Supports OAuth via `ccr codex-auth` or PAT auth when `api_key` starts with `at-`.
787
- - `claude-auth`: Authenticates requests to Anthropic's API using your Claude Pro or Max subscription OAuth token. Converts Unified format to Anthropic format and handles SSE response conversion. Use it together with `Anthropic` in the provider chain, and authenticate via `ccr claude-auth`.
788
- - `antigravity-auth`: OAuth + envelope middleware for Google's Antigravity gateway (`cloudcode-pa`). Chain **after** `gemini`. For Antigravity you must set `cachedContent: false` on the Gemini stage (no `cachedContents` resource there); keep `thoughtSignatureFallback: "skip"` unless the endpoint rejects Google's thought-signature sentinel. Authenticate with `ccr antigravity-auth`.
789
- - `chrome-on-device`: Routes requests to Chrome's on-device Gemini Nano model via the Prompt API. Uses `responseConstraint` for structured JSON output. Requires a bridge process running on the host (`ccr chrome-bridge`).
790
-
791
- **Chrome On-Device Provider Configuration:**
792
-
793
- The `chrome-on-device` transformer routes requests to Chrome's built-in Gemini Nano model. This is a ~4GB on-device model that runs locally with no API costs. The model is accessed through Chrome's Prompt API (`window.LanguageModel`) via a bridge process.
794
-
795
- **Prerequisites:**
796
-
797
- 1. Google Chrome installed on your system (macOS, Windows, or Linux)
798
- 2. Enable Chrome flags (one-time):
799
- - `chrome://flags/#optimization-guide-on-device-model` → **Enabled**
800
- - `chrome://flags/#prompt-api-for-gemini-nano-multimodal-input` → **Enabled**
801
- 3. Restart Chrome after enabling flags and wait for the model to download (~4GB)
802
- 4. Start the bridge process on the host: `ccr chrome-bridge`
803
-
804
- **Provider Configuration:**
805
-
806
- ```json
807
- {
808
- "name": "chrome-nano",
809
- "api_base_url": "http://127.0.0.1:3457",
810
- "api_key": "placeholder",
811
- "models": ["gemini-nano"],
812
- "transformer": {
813
- "use": ["chrome-on-device", "tooluse"]
814
- }
815
- }
816
- ```
817
-
818
- > **Note**: The `tooluse` transformer is required alongside `chrome-on-device` to enable the a la carte tool-calling system (including the `ExitTool` for plain text responses) and to inject the necessary system reminders that help the model transition between thinking and acting.
819
-
820
- **Starting the Bridge:**
821
-
822
- The bridge is a standalone HTTP server that runs on the host and bridges HTTP requests to Chrome's Prompt API via CDP (Chrome DevTools Protocol):
823
-
824
- ```bash
825
- # Start the bridge (default: port 3457, CDP port 9222)
826
- ccr chrome-bridge
827
-
828
- # Custom ports
829
- ccr chrome-bridge --port 3457 --cdp 9222
830
- ```
831
-
832
- The bridge automatically launches Chrome with the required flags if it's not already running (`--remote-debugging-port=9222 --user-data-dir=<temp_dir>`).
833
-
834
- > **Note for Docker users**: The bridge must run on the Docker **host** (not inside the container), since it needs direct access to Chrome via CDP. When CCR runs in Docker, set the provider host to `http://host.docker.internal:3457`.
835
-
836
- **How It Works:**
837
-
838
- 1. The transformer replaces Claude Code's system prompt with a minimal tool-focused one listing 5 core tools (Bash, Read, Write, Edit, ExitTool)
839
- 2. The bridge maintains persistent `LanguageModel` sessions — one per client fingerprint (`User-Agent + IP` hash). Conversation history is carried forward naturally within each session, not rebuilt per turn. It calls `session.promptStreaming()` with a `responseConstraint` (JSON Schema) that forces structured output: `{"tool_calls": [{"name": "...", "arguments": {...}}]}`. Text responses are handled by the model calling the `ExitTool`.
840
- 3. The bridge transforms Claude Code's internal context blocks in user messages to conserve the limited context budget: `<system-reminder>` blocks containing tool calls or results are converted into structured `<tool_result>` tags, while other `<system-reminder>` blocks and `<command-*>` / `<local-command-*>` blocks for unsupported tools are stripped
841
- 4. The bridge parses the structured JSON response into OpenAI-format SSE chunks (`chat.completion.chunk`) or a single non-streaming response (`chat.completion`)
842
- 5. Tool calls are detected from the parsed JSON and converted to `tool_calls` in the response; `finish_reason` is set to `"tool_calls"` or `"stop"` accordingly
843
- 6. Multi-turn tool use is supported — consecutive requests are processed within the same persistent session
844
- 7. **Multi-session support**: Requests are fingerprinted by `User-Agent + IP` hash into separate sessions, allowing multiple concurrent Claude Code instances without context contamination. A built-in web dashboard (served on the bridge port) shows real-time stats for all sessions, including turn count, idle time, and context usage
845
- 8. **Idle session eviction**: Sessions idle for more than 5 minutes are automatically destroyed to free resources. The `cli` session (dashboard default) is never evicted. Sessions can also be manually evicted via the dashboard's Evict button
846
- 9. Auto-compaction triggers at 85% context usage, resetting the session while preserving the system prompt
847
-
848
- **Limitations:**
849
-
850
- - **Tool calling**: Uses `responseConstraint` (JSON Schema) for structured output rather than native function calling — this works reliably but depends on the model following the schema
851
- - **Multi-turn consistency**: The small on-device model may occasionally loop on the same tool call or respond with text instead of calling a needed tool. A retry mechanism with corrected prompts mitigates this
852
- - **No thinking/reasoning blocks**: The Prompt API doesn't separate thinking from visible output
853
- - **Context window**: Limited to 9216 tokens; auto-compaction engages at 85% usage. Old interactions are evicted on context overflow
854
- - **Output limit**: The model may stall on whitespace-heavy content (e.g., Python indentation). The bridge uses write-then-edit incremental file creation (3 lines per Write call) and whitespace stall detection with abort
855
- - **Cross-platform support**: Compatible with macOS, Windows, and Linux (requires Chrome installation and manual flag enablement)
856
-
857
- **Codex Provider Configuration:**
858
-
859
- The Codex transformer connects to the ChatGPT backend API, providing access to GPT-5.x models. It supports either OAuth authentication or a PAT in `api_key`.
860
-
861
- ```json
862
- {
863
- "name": "codex",
864
- "api_base_url": "https://chatgpt.com/backend-api/codex",
865
- "api_key": "oauth_dummy_key",
866
- "models": ["gpt-5.4"],
867
- "transformer": {
868
- "use": ["codex"]
869
- }
870
- }
871
- ```
872
-
873
- > **OAuth mode**: Keep `api_key` as a placeholder and run `ccr codex-auth`. OAuth tokens are stored in `~/.claude-code-router/codex_auth.json`.
874
-
875
- ```json
876
- {
877
- "name": "codex",
878
- "api_base_url": "https://chatgpt.com/backend-api/codex",
879
- "api_key": "at-your-personal-access-token",
880
- "models": ["gpt-5.4"],
881
- "transformer": {
882
- "use": ["codex"]
883
- }
884
- }
885
- ```
886
-
887
- > **PAT mode**: If `api_key` starts with `at-`, CCR uses it directly and skips `ccr codex-auth`.
888
-
889
- > **Note**: If `api_key` is not a PAT, CCR falls back to OAuth tokens from `~/.claude-code-router/codex_auth.json`.
890
-
891
- **Qwen Provider Configuration:**
399
+ - `Anthropic` passes through to an Anthropic endpoint unchanged. `OpenAI` registers the `/v1/chat/completions` route (the body is already in OpenAI shape).
400
+ - Provider adapters: `deepseek`, `groq`, `mistral`, `openrouter`, `gemini` / `vertex-gemini`, `codex`, `claude-auth`, `antigravity-auth`, `qwen-auth`, `cursor-sdk`, `chrome-on-device`.
401
+ - `maxtoken` sets a specific `max_tokens`. `tooluse` optimizes tool usage via `tool_choice`. `reasoning` replays provider `reasoning_content` across turns. `sampling` maps `temperature` / `top_p` / `top_k` / `repetition_penalty`. `enhancetool` adds error tolerance to tool-call parameters (disables streaming of tool calls). `cleancache` clears `cache_control`. `customparams` injects custom request parameters.
402
+ - Experimental gist/CLI integrations: `gemini-cli`, `chutes-glm`, `qwen-cli`, `rovo-cli`.
892
403
 
893
- The Qwen provider uses the `qwen-auth` transformer (for the `Authorization: Bearer <jwt>` header and trailing `<details>` strip) paired with the existing `OpenAI` transformer (which registers the `POST /v1/chat/completions` endpoint).
894
-
895
- ```json
896
- {
897
- "name": "qwen",
898
- "api_base_url": "https://qwen.aikit.club/v1/chat/completions",
899
- "api_key": "qwen-placeholder",
900
- "models": ["qwen3-max", "qwen3-coder-plus"],
901
- "transformer": {
902
- "use": ["qwen-auth", "reasoning", "OpenAI"]
903
- }
904
- }
905
- ```
906
-
907
- Three transformers are required in the chain:
908
-
909
- - `qwen-auth` — sets the `Authorization: Bearer <jwt>` header on every outbound request (loading/refreshing the JWT from `~/.claude-code-router/qwen_auth.json`) and strips the trailing `<details>...</details>` block Qwen injects into responses.
910
- - `reasoning` — maps Claude Code's unified `reasoning` field onto the request so the Qwen endpoint's `enable_thinking` and `thinking_budget` parameters are populated.
911
- - `OpenAI` — registers the `POST /v1/chat/completions` route. It is a thin endpoint stub with no body conversion, so it must remain last in the chain.
912
-
913
- > **Note**: The `api_key` field is a placeholder — actual authentication is handled via the JWT stored in `~/.claude-code-router/qwen_auth.json`. Run `ccr qwen-auth` to authenticate before using the Qwen provider.
914
-
915
- **Claude Subscription Provider Configuration:**
916
-
917
- The `claude-auth` transformer routes requests to Anthropic's API using your Claude Pro or Max subscription OAuth token instead of a static API key.
918
-
919
- ```json
920
- {
921
- "name": "claude-subscription",
922
- "api_base_url": "https://api.anthropic.com",
923
- "api_key": "no-key",
924
- "models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
925
- "transformer": {
926
- "use": ["claude-auth", "Anthropic"]
927
- }
928
- }
929
- ```
930
-
931
- Two transformers are required in the chain:
932
-
933
- - `claude-auth` — converts the request from Unified (OpenAI) format to Anthropic format, injects `Authorization: Bearer <token>` (loading/refreshing the token from `~/.claude-code-router/claude_auth.json`), and converts the Anthropic SSE response back to Unified format.
934
- - `Anthropic` — registers the `POST /v1/messages` route. It has no body conversion in the provider chain, so it acts as a no-op endpoint stub.
935
-
936
- > **Note**: The `api_key` field is a placeholder — actual authentication is handled via OAuth tokens stored in `~/.claude-code-router/claude_auth.json`. Run `ccr claude-auth` to authenticate before using this provider. The transformer automatically sends Anthropic's `oauth-2025-04-20` beta so subscription OAuth Bearer tokens are accepted.
937
-
938
- **DeepSeek via OpenCode (Mandatory Reasoning Replay):**
939
-
940
- DeepSeek models require previous assistant reasoning content to be replayed in subsequent requests. When using DeepSeek models through providers like OpenCode, apply the `reasoning` transformer at the model level to handle this automatically:
941
-
942
- ```json
943
- {
944
- "name": "opencode",
945
- "api_base_url": "https://opencode.ai/zen/go/v1/chat/completions",
946
- "api_key": "$OPENCODE_API_KEY",
947
- "models": ["deepseek-v4-pro", "deepseek-v4-flash"],
948
- "transformer": {
949
- "use": ["OpenAI"],
950
- "deepseek-v4-pro": {
951
- "use": ["reasoning"]
952
- },
953
- "deepseek-v4-flash": {
954
- "use": ["reasoning"]
955
- }
956
- }
957
- }
958
- ```
959
-
960
- > **Note**: The `reasoning` transformer must be applied specifically to DeepSeek models (not provider-wide). It replays the assistant's reasoning output from previous turns as required by the DeepSeek API.
404
+ > **See also**: The full transformer reference — including the `gemini` `cachedContent` / `thoughtSignatureFallback` options and the `openrouter` provider-routing parameter is in `docs/docs/server/config/transformers.md`.
961
405
 
962
406
  **Custom Transformers:**
963
407
 
964
- You can also create your own transformers and load them via the `transformers` field in `config.json`.
965
-
966
- ```json
967
- {
968
- "transformers": [
969
- {
970
- "path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js",
971
- "options": {
972
- "project": "xxx"
973
- }
974
- }
975
- ]
976
- }
977
- ```
408
+ Load your own transformers via the `transformers` field in `config.json`, e.g. `{ "transformers": [{ "path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js", "options": { "project": "xxx" } }] }`. See `docs/docs/server/config/transformers.md` for the full custom-transformer guide.
978
409
 
979
410
  #### Router
980
411
 
@@ -994,42 +425,7 @@ Example: `/model openrouter,anthropic/claude-3.5-sonnet`
994
425
 
995
426
  #### Custom Router
996
427
 
997
- For more advanced routing logic, you can specify a custom router script via the `CUSTOM_ROUTER_PATH` in your `config.json`. This allows you to implement complex routing rules beyond the default scenarios.
998
-
999
- In your `config.json`:
1000
-
1001
- ```json
1002
- {
1003
- "CUSTOM_ROUTER_PATH": "/User/xxx/.claude-code-router/custom-router.js"
1004
- }
1005
- ```
1006
-
1007
- The custom router file must be a JavaScript module that exports an `async` function. This function receives the request object and the config object as arguments and should return the provider and model name as a string (e.g., `"provider_name,model_name"`), or `null` to fall back to the default router.
1008
-
1009
- Here is an example of a `custom-router.js` based on `custom-router.example.js`:
1010
-
1011
- ```javascript
1012
- // /User/xxx/.claude-code-router/custom-router.js
1013
-
1014
- /**
1015
- * A custom router function to determine which model to use based on the request.
1016
- *
1017
- * @param {object} req - The request object from Claude Code, containing the request body.
1018
- * @param {object} config - The application's config object.
1019
- * @returns {Promise<string|null>} - A promise that resolves to the "provider,model_name" string, or null to use the default router.
1020
- */
1021
- module.exports = async function router(req, config) {
1022
- const userMessage = req.body.messages.find((m) => m.role === "user")?.content;
1023
-
1024
- if (userMessage && userMessage.includes("explain this code")) {
1025
- // Use a powerful model for code explanation
1026
- return "openrouter,anthropic/claude-3.5-sonnet";
1027
- }
1028
-
1029
- // Fallback to the default router configuration
1030
- return null;
1031
- };
1032
- ```
428
+ For advanced routing logic, set `CUSTOM_ROUTER_PATH` in `config.json` to a JS module exporting an `async function(req, config)` that returns a `"provider,model"` string, or `null` to fall back to the default router. See `custom-router.example.js` and `docs/docs/server/advanced/custom-router.md`.
1033
429
 
1034
430
  ##### Subagent Routing
1035
431
 
@@ -1068,10 +464,10 @@ See [the implementation plan and review](tasks/caching-plan.md) for the provider
1068
464
 
1069
465
  ## Status Line (Beta)
1070
466
  To better monitor the status of claude-code-router at runtime, version v1.0.40 includes a built-in statusline tool, which you can enable in the UI.
1071
- ![statusline-config.png](/blog/images/statusline-config.png)
467
+ ![statusline-config.png](blog/images/statusline-config.png)
1072
468
 
1073
469
  The effect is as follows:
1074
- ![statusline](/blog/images/statusline.png)
470
+ ![statusline](blog/images/statusline.png)
1075
471
 
1076
472
  ## 🤖 GitHub Actions
1077
473