@caeliq/claude-code-router 2.0.3 → 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,247 +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
- - Discover models with `ccr model get cursor` (lists via `Cursor.models.list`, not REST `/models`)
511
- - Docker Compose passes `CURSOR_API_KEY` into the container when set; local Cursor sandboxing is forced off in Docker
512
- - 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.
513
- - 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.
514
- - Stopping a Cursor response cancels the owned SDK run with bounded cleanup and invalidates unsafe SDK sessions; active-run send failures use Cursor's native `local.force` retry before CCR falls back to a fresh full-transcript session.
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`.
515
306
 
516
- > **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`.
517
308
 
518
309
  #### Claude Subscription Authentication
519
310
 
520
- 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:
521
312
 
522
313
  ```shell
523
314
  ccr claude-auth
524
315
  ```
525
316
 
526
- This command:
527
- 1. Opens your browser to the Claude OAuth authorization page
528
- 2. After you sign in, the OAuth callback is handled by the running CCR server on port `1455`
529
- 3. Tokens are stored in `~/.claude-code-router/claude_auth.json`
530
- 4. The `claude-auth` transformer automatically refreshes tokens when they expire
531
-
532
- > **Note**: The server must be running for `ccr claude-auth` to work, as it hosts the OAuth callback endpoint on port 1455.
533
-
534
- **Running with Docker**:
535
-
536
- The OAuth callback uses port `1455`, which is mapped to the CCR server port in `docker-compose.yml` (`"1455:3456"`). When running in Docker:
537
-
538
- ```shell
539
- docker exec -it claude-code-router ccr claude-auth
540
- ```
541
-
542
- 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.
543
-
544
- A Claude subscription provider requires the `claude-auth` + `Anthropic` transformer chain:
545
-
546
- ```json
547
- {
548
- "name": "claude-subscription",
549
- "api_base_url": "https://api.anthropic.com",
550
- "api_key": "no-key",
551
- "models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
552
- "transformer": {
553
- "use": ["claude-auth", "Anthropic"]
554
- }
555
- }
556
- ```
557
-
558
- > **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.
559
318
 
560
- **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`.
561
320
 
562
321
  #### Qwen Provider Authentication
563
322
 
564
- 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:
565
324
 
566
325
  ```shell
567
326
  ccr qwen-auth
568
327
  ```
569
328
 
570
- This command:
571
- 1. Prints a URL (`http://127.0.0.1:<port>/qwen/auth`) for the in-browser auth page
572
- 2. The page offers two options:
573
- - **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.
574
- - **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.
575
- 3. The token is validated against `qwen.aikit.club/v1/validate` and saved to `~/.claude-code-router/qwen_auth.json` (mode 0600)
576
- 4. The `qwen-auth` transformer automatically refreshes the token when it nears expiry (within 6 hours)
577
-
578
- > **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.
579
-
580
- **Running with Docker**:
581
-
582
- The Qwen auth page is served on the regular CCR port (no separate callback port). When running in Docker:
583
-
584
- ```shell
585
- docker exec -it claude-code-router ccr qwen-auth
586
- ```
587
-
588
- 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.
589
330
 
590
- **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`.
591
332
 
592
333
  #### Chrome On-Device Bridge
593
334
 
594
- 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:
595
336
 
596
337
  ```bash
597
- # Start the bridge (default: port 3457, CDP port 9222)
598
- ccr chrome-bridge
599
-
600
- # Custom ports
338
+ ccr chrome-bridge # default: port 3457, CDP 9222
601
339
  ccr chrome-bridge --port 3457 --cdp 9222
602
340
  ```
603
341
 
604
- The bridge:
605
- 1. Checks if Chrome is running with remote debugging enabled (port 9222)
606
- 2. If not, launches Chrome with the required flags (`--remote-debugging-port=9222 --user-data-dir=/tmp/chrome-debug-profile`)
607
- 3. Connects to Chrome via Puppeteer/CDP with a 5-minute protocol timeout to handle slow model inference
608
- 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
609
- 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
610
- 6. Exposes an OpenAI-compatible HTTP API on `0.0.0.0:3457`:
611
- - `GET /v1/models` — returns available models with live context usage
612
- - `GET /v1/models/{model_name}` — returns individual model info (display_name, max_input_tokens, capabilities)
613
- - `POST /v1/chat/completions` — chat completions with streaming and non-streaming support
614
- - `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.
615
343
 
616
- **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.
617
345
 
618
- > **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`.
619
347
 
620
348
  ### 6. Presets Management
621
349
 
622
- 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:
623
351
 
624
352
  ```shell
625
- # Export current configuration as a preset
626
- ccr preset export my-preset
627
-
628
- # Export with metadata
353
+ ccr preset export my-preset # export current config as a preset
629
354
  ccr preset export my-preset --description "My OpenAI config" --author "Your Name" --tags "openai,production"
630
-
631
- # Install a preset from local directory
632
- ccr preset install /path/to/preset
633
-
634
- # List all installed presets
355
+ ccr preset install /path/to/preset # install from a directory
635
356
  ccr preset list
636
-
637
- # Show preset information
638
357
  ccr preset info my-preset
639
-
640
- # Delete a preset
641
358
  ccr preset delete my-preset
642
359
  ```
643
360
 
644
- **Preset Features:**
645
- - **Export**: Save your current configuration as a preset directory (with manifest.json)
646
- - **Install**: Install presets from local directories
647
- - **Sensitive Data Handling**: API keys and other sensitive data are automatically sanitized during export (marked as `{{field}}` placeholders)
648
- - **Dynamic Configuration**: Presets can include input schemas for collecting required information during installation
649
- - **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.
650
362
 
651
- **Preset File Structure:**
652
- ```
653
- ~/.claude-code-router/presets/
654
- ├── my-preset/
655
- │ └── manifest.json # Contains configuration and metadata
656
- ```
363
+ > **See also**: `docs/docs/cli/commands/preset.md`.
657
364
 
658
365
  ### 7. Activate Command (Environment Variables Setup)
659
366
 
@@ -681,299 +388,24 @@ The `activate` command sets the following environment variables:
681
388
 
682
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`).
683
390
 
684
- #### Providers
685
-
686
- The `Providers` array is where you define the different model providers you want to use. Each provider object requires:
391
+ #### Providers and Transformers
687
392
 
688
- - `name`: A unique name for the provider.
689
- - `api_base_url`: The full API endpoint for chat completions.
690
- - `api_key`: Your API key for the provider.
691
- - `models`: A list of model names available from this provider.
692
- - `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.
693
394
 
694
- #### Transformers
695
-
696
- Transformers allow you to modify the request and response payloads to ensure compatibility with different provider APIs.
697
-
698
- - **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.
699
- ```json
700
- {
701
- "name": "openrouter",
702
- "api_base_url": "https://openrouter.ai/api/v1/chat/completions",
703
- "api_key": "sk-xxx",
704
- "models": [
705
- "google/gemini-2.5-pro-preview",
706
- "anthropic/claude-sonnet-4",
707
- "anthropic/claude-3.5-sonnet"
708
- ],
709
- "transformer": { "use": ["openrouter"] }
710
- }
711
- ```
712
- - **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.
713
-
714
- ```json
715
- {
716
- "name": "deepseek",
717
- "api_base_url": "https://api.deepseek.com/chat/completions",
718
- "api_key": "sk-xxx",
719
- "models": ["deepseek-chat", "deepseek-reasoner"],
720
- "transformer": {
721
- "use": ["deepseek"],
722
- "deepseek-chat": { "use": ["tooluse"] }
723
- }
724
- }
725
- ```
726
-
727
- - **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.
728
- ```json
729
- {
730
- "name": "siliconflow",
731
- "api_base_url": "https://api.siliconflow.cn/v1/chat/completions",
732
- "api_key": "sk-xxx",
733
- "models": ["moonshotai/Kimi-K2-Instruct"],
734
- "transformer": {
735
- "use": [
736
- [
737
- "maxtoken",
738
- {
739
- "max_tokens": 16384
740
- }
741
- ]
742
- ]
743
- }
744
- }
745
- ```
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`.
746
396
 
747
397
  **Available Built-in Transformers:**
748
398
 
749
- - `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).
750
- - `deepseek`: Adapts requests/responses for DeepSeek API.
751
- - `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", { ... }]`:
752
- - **`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.
753
- - **`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.
754
- - `mistral`: Adapts requests/responses for Mistral API.
755
- - `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:
756
- ```json
757
- "transformer": {
758
- "use": ["openrouter"],
759
- "moonshotai/kimi-k2": {
760
- "use": [
761
- [
762
- "openrouter",
763
- {
764
- "provider": {
765
- "only": ["moonshotai/fp8"]
766
- }
767
- }
768
- ]
769
- ]
770
- }
771
- }
772
- ```
773
- - `groq`: Adapts requests/responses for groq API.
774
- - `maxtoken`: Sets a specific `max_tokens` value.
775
- - `tooluse`: Optimizes tool usage for certain models via `tool_choice`.
776
- - `gemini-cli` (experimental): Unofficial support for Gemini via Gemini CLI [gemini-cli.js](https://gist.github.com/musistudio/1c13a65f35916a7ab690649d3df8d1cd).
777
- - `reasoning`: Used to process the `reasoning_content` field.
778
- - `sampling`: Used to process sampling information fields such as `temperature`, `top_p`, `top_k`, and `repetition_penalty`.
779
- - `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).
780
- - `cleancache`: Clears the `cache_control` field from requests.
781
- - `vertex-gemini`: Handles the Gemini API using Vertex authentication.
782
- - `chutes-glm` Unofficial support for GLM 4.5 model via Chutes [chutes-glm-transformer.js](https://gist.github.com/vitobotta/2be3f33722e05e8d4f9d2b0138b8c863).
783
- - `qwen-cli` (experimental): Unofficial support for qwen3-coder-plus model via Qwen CLI [qwen-cli.js](https://gist.github.com/musistudio/f5a67841ced39912fd99e42200d5ca8b).
784
- - `rovo-cli` (experimental): Unofficial support for gpt-5 via Atlassian Rovo Dev CLI [rovo-cli.js](https://gist.github.com/SaseQ/c2a20a38b11276537ec5332d1f7a5e53).
785
- - `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-`.
786
- - `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`.
787
- - `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`.
788
- - `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`).
789
-
790
- **Chrome On-Device Provider Configuration:**
791
-
792
- 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.
793
-
794
- **Prerequisites:**
795
-
796
- 1. Google Chrome installed on your system (macOS, Windows, or Linux)
797
- 2. Enable Chrome flags (one-time):
798
- - `chrome://flags/#optimization-guide-on-device-model` → **Enabled**
799
- - `chrome://flags/#prompt-api-for-gemini-nano-multimodal-input` → **Enabled**
800
- 3. Restart Chrome after enabling flags and wait for the model to download (~4GB)
801
- 4. Start the bridge process on the host: `ccr chrome-bridge`
802
-
803
- **Provider Configuration:**
804
-
805
- ```json
806
- {
807
- "name": "chrome-nano",
808
- "api_base_url": "http://127.0.0.1:3457",
809
- "api_key": "placeholder",
810
- "models": ["gemini-nano"],
811
- "transformer": {
812
- "use": ["chrome-on-device", "tooluse"]
813
- }
814
- }
815
- ```
816
-
817
- > **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.
818
-
819
- **Starting the Bridge:**
820
-
821
- 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):
822
-
823
- ```bash
824
- # Start the bridge (default: port 3457, CDP port 9222)
825
- ccr chrome-bridge
826
-
827
- # Custom ports
828
- ccr chrome-bridge --port 3457 --cdp 9222
829
- ```
830
-
831
- The bridge automatically launches Chrome with the required flags if it's not already running (`--remote-debugging-port=9222 --user-data-dir=<temp_dir>`).
832
-
833
- > **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`.
834
-
835
- **How It Works:**
836
-
837
- 1. The transformer replaces Claude Code's system prompt with a minimal tool-focused one listing 5 core tools (Bash, Read, Write, Edit, ExitTool)
838
- 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`.
839
- 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
840
- 4. The bridge parses the structured JSON response into OpenAI-format SSE chunks (`chat.completion.chunk`) or a single non-streaming response (`chat.completion`)
841
- 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
842
- 6. Multi-turn tool use is supported — consecutive requests are processed within the same persistent session
843
- 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
844
- 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
845
- 9. Auto-compaction triggers at 85% context usage, resetting the session while preserving the system prompt
846
-
847
- **Limitations:**
848
-
849
- - **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
850
- - **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
851
- - **No thinking/reasoning blocks**: The Prompt API doesn't separate thinking from visible output
852
- - **Context window**: Limited to 9216 tokens; auto-compaction engages at 85% usage. Old interactions are evicted on context overflow
853
- - **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
854
- - **Cross-platform support**: Compatible with macOS, Windows, and Linux (requires Chrome installation and manual flag enablement)
855
-
856
- **Codex Provider Configuration:**
857
-
858
- 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`.
859
-
860
- ```json
861
- {
862
- "name": "codex",
863
- "api_base_url": "https://chatgpt.com/backend-api/codex",
864
- "api_key": "oauth_dummy_key",
865
- "models": ["gpt-5.4"],
866
- "transformer": {
867
- "use": ["codex"]
868
- }
869
- }
870
- ```
871
-
872
- > **OAuth mode**: Keep `api_key` as a placeholder and run `ccr codex-auth`. OAuth tokens are stored in `~/.claude-code-router/codex_auth.json`.
873
-
874
- ```json
875
- {
876
- "name": "codex",
877
- "api_base_url": "https://chatgpt.com/backend-api/codex",
878
- "api_key": "at-your-personal-access-token",
879
- "models": ["gpt-5.4"],
880
- "transformer": {
881
- "use": ["codex"]
882
- }
883
- }
884
- ```
885
-
886
- > **PAT mode**: If `api_key` starts with `at-`, CCR uses it directly and skips `ccr codex-auth`.
887
-
888
- > **Note**: If `api_key` is not a PAT, CCR falls back to OAuth tokens from `~/.claude-code-router/codex_auth.json`.
889
-
890
- **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`.
891
403
 
892
- 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).
893
-
894
- ```json
895
- {
896
- "name": "qwen",
897
- "api_base_url": "https://qwen.aikit.club/v1/chat/completions",
898
- "api_key": "qwen-placeholder",
899
- "models": ["qwen3-max", "qwen3-coder-plus"],
900
- "transformer": {
901
- "use": ["qwen-auth", "reasoning", "OpenAI"]
902
- }
903
- }
904
- ```
905
-
906
- Three transformers are required in the chain:
907
-
908
- - `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.
909
- - `reasoning` — maps Claude Code's unified `reasoning` field onto the request so the Qwen endpoint's `enable_thinking` and `thinking_budget` parameters are populated.
910
- - `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.
911
-
912
- > **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.
913
-
914
- **Claude Subscription Provider Configuration:**
915
-
916
- 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.
917
-
918
- ```json
919
- {
920
- "name": "claude-subscription",
921
- "api_base_url": "https://api.anthropic.com",
922
- "api_key": "no-key",
923
- "models": ["claude-opus-4-8", "claude-sonnet-4-6", "claude-haiku-4-5"],
924
- "transformer": {
925
- "use": ["claude-auth", "Anthropic"]
926
- }
927
- }
928
- ```
929
-
930
- Two transformers are required in the chain:
931
-
932
- - `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.
933
- - `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.
934
-
935
- > **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.
936
-
937
- **DeepSeek via OpenCode (Mandatory Reasoning Replay):**
938
-
939
- 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:
940
-
941
- ```json
942
- {
943
- "name": "opencode",
944
- "api_base_url": "https://opencode.ai/zen/go/v1/chat/completions",
945
- "api_key": "$OPENCODE_API_KEY",
946
- "models": ["deepseek-v4-pro", "deepseek-v4-flash"],
947
- "transformer": {
948
- "use": ["OpenAI"],
949
- "deepseek-v4-pro": {
950
- "use": ["reasoning"]
951
- },
952
- "deepseek-v4-flash": {
953
- "use": ["reasoning"]
954
- }
955
- }
956
- }
957
- ```
958
-
959
- > **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`.
960
405
 
961
406
  **Custom Transformers:**
962
407
 
963
- You can also create your own transformers and load them via the `transformers` field in `config.json`.
964
-
965
- ```json
966
- {
967
- "transformers": [
968
- {
969
- "path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js",
970
- "options": {
971
- "project": "xxx"
972
- }
973
- }
974
- ]
975
- }
976
- ```
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.
977
409
 
978
410
  #### Router
979
411
 
@@ -993,42 +425,7 @@ Example: `/model openrouter,anthropic/claude-3.5-sonnet`
993
425
 
994
426
  #### Custom Router
995
427
 
996
- 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.
997
-
998
- In your `config.json`:
999
-
1000
- ```json
1001
- {
1002
- "CUSTOM_ROUTER_PATH": "/User/xxx/.claude-code-router/custom-router.js"
1003
- }
1004
- ```
1005
-
1006
- 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.
1007
-
1008
- Here is an example of a `custom-router.js` based on `custom-router.example.js`:
1009
-
1010
- ```javascript
1011
- // /User/xxx/.claude-code-router/custom-router.js
1012
-
1013
- /**
1014
- * A custom router function to determine which model to use based on the request.
1015
- *
1016
- * @param {object} req - The request object from Claude Code, containing the request body.
1017
- * @param {object} config - The application's config object.
1018
- * @returns {Promise<string|null>} - A promise that resolves to the "provider,model_name" string, or null to use the default router.
1019
- */
1020
- module.exports = async function router(req, config) {
1021
- const userMessage = req.body.messages.find((m) => m.role === "user")?.content;
1022
-
1023
- if (userMessage && userMessage.includes("explain this code")) {
1024
- // Use a powerful model for code explanation
1025
- return "openrouter,anthropic/claude-3.5-sonnet";
1026
- }
1027
-
1028
- // Fallback to the default router configuration
1029
- return null;
1030
- };
1031
- ```
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`.
1032
429
 
1033
430
  ##### Subagent Routing
1034
431
 
@@ -1067,10 +464,10 @@ See [the implementation plan and review](tasks/caching-plan.md) for the provider
1067
464
 
1068
465
  ## Status Line (Beta)
1069
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.
1070
- ![statusline-config.png](/blog/images/statusline-config.png)
467
+ ![statusline-config.png](blog/images/statusline-config.png)
1071
468
 
1072
469
  The effect is as follows:
1073
- ![statusline](/blog/images/statusline.png)
470
+ ![statusline](blog/images/statusline.png)
1074
471
 
1075
472
  ## 🤖 GitHub Actions
1076
473