@caeliq/claude-code-router 2.1.0 → 2.1.2
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/LICENSE +1 -1
- package/README.md +87 -685
- package/dist/cli.js +1256 -1143
- package/dist/index.html +45 -53
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-

|
|
1
|
+

|
|
2
2
|
|
|
3
3
|
[](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
|
|
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.
|
|
@@ -21,8 +25,10 @@ This fork is based on [claude-code-router](https://github.com/musistudio/claude-
|
|
|
21
25
|
- **Mistral Integration**: Added specific handling for Mistral's reasoning parameters and decoupled transformation logic.
|
|
22
26
|
- **Build & Deployment**: Integrated the UI package into the Docker build process and added a Docker Compose configuration.
|
|
23
27
|
- **Code Quality**: Localized codebase (English comments), improved error handling, and addressed Copilot review feedback.
|
|
28
|
+
- **Dependency Security**: Keeps the workspace audit-clean with scoped security floors. Docusaurus's archived `image-size` dependency is replaced only on the MDX loader edge by a private `image-size/fromFile` compatibility adapter backed by maintained `probe-image-size`; it is build tooling and does not enter CCR runtime packages.
|
|
24
29
|
- **Gemini Stability & Tool Use Fixes**: Corrected `thoughtSignature` placement in Gemini request bodies (Gemini 3 expects it as a sibling field on the `functionCall` part itself, and validates only the first such part per step); filtered synthetic `ccr_` placeholder signatures from outgoing Gemini requests to prevent Gemini 500 errors; fixed `tool_result` content-array serialization in the Anthropic transformer so models receive plain text instead of JSON-wrapped arrays (resolves "Error editing file" in Claude Code); fixed Fastify `onSend` hook to prevent `invalid type 'object'` unhandled rejections on error responses.
|
|
25
30
|
- **Codex (ChatGPT) Integration**: Added Codex transformer for the ChatGPT backend API (Responses API), supporting both OAuth-based authentication (`ccr codex-auth`) and PAT auth via `api_key: "at-..."`, plus SSE streaming, reasoning/thinking content, tool calls with web search, and image handling.
|
|
31
|
+
- **Gateway and Codex model discovery**: The server's OpenAI-compatible `GET /v1/models` enriches configured models with native-provider metadata from models.dev (friendly names, descriptions, context/output limits, reasoning effort levels, and Claude Desktop family tiers). `MODEL_ID_OUTPUT` controls whether discovery emits literal canonical IDs (the default) or masks otherwise-filtered IDs as reversible `claude-<lowercase UTF-8 hex>` aliases. Inbound always accepts both forms. Separately, the CLI's `ccr codex-config` command uses the same exact-model/native-provider matching rule for Codex's local picker catalog: a unique model-id row is authoritative, duplicate ids are resolved to the native provider, and a true miss falls back to a 200K context window with `low`/`medium`/`high` effort. It also wires Codex's `config.toml` to route over the Responses API.
|
|
26
32
|
- **Cursor SDK Integration**: Added `cursor-sdk` transformer that runs Cursor models in-process via `@cursor/sdk`. Default **bridge** mode keeps Claude Code as the tool host (Cursor built-ins denied); supports `plan` / `agent` modes, `crsr_` / `CURSOR_API_KEY` auth, `ccr model get cursor` model discovery, and Docker runtime install of the SDK native packages.
|
|
27
33
|
- **Claude Subscription Integration**: Added `claude-auth` support for routing through a Claude Pro or Max subscription via OAuth (`ccr claude-auth`), using the `claude-auth` + `Anthropic` transformer chain.
|
|
28
34
|
- **Antigravity Integration**: Added Google Antigravity OAuth via `ccr antigravity-auth`, with the `antigravity-auth` + `gemini` transformer chain targeting the Antigravity / `cloudcode-pa` API. Supports Gemini and Claude models under that quota, thought-signature round-tripping / fallback, and Claude tool-schema sanitization for Gemini-backed Claude models. Requires `gemini` options `{"cachedContent": false}` because Antigravity has no Google `cachedContents` resource (leaving the default `true` causes 404s).
|
|
@@ -41,20 +47,23 @@ This fork is based on [claude-code-router](https://github.com/musistudio/claude-
|
|
|
41
47
|
#### Prerequisites
|
|
42
48
|
|
|
43
49
|
Before you begin, ensure you have the following installed on your system:
|
|
44
|
-
- **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.
|
|
50
|
+
- **Docker** (Recommended): The primary way to run the router via the published image. See [Docker Install Guide](https://docs.docker.com/get-docker/).
|
|
51
|
+
- **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
52
|
- **Claude Code**: See the [official quickstart guide](https://code.claude.com/docs/en/quickstart) for installation instructions.
|
|
47
53
|
|
|
48
54
|
#### Quick Start with Docker
|
|
49
55
|
|
|
50
|
-
The fastest way to launch Claude Code Router is
|
|
56
|
+
The fastest way to launch Claude Code Router is the published Docker image:
|
|
51
57
|
|
|
52
58
|
```shell
|
|
53
|
-
|
|
54
|
-
docker
|
|
59
|
+
mkdir -p ~/.claude-code-router
|
|
60
|
+
docker run -d --name ccr \
|
|
61
|
+
-p 3456:3456 \
|
|
62
|
+
-v ~/.claude-code-router:/root/.claude-code-router \
|
|
63
|
+
ghcr.io/oakimov/claude-code-router:latest
|
|
55
64
|
```
|
|
56
65
|
|
|
57
|
-
The
|
|
66
|
+
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
67
|
|
|
59
68
|
### 2. Configuration
|
|
60
69
|
|
|
@@ -68,8 +77,9 @@ The `config.json` file has several key sections:
|
|
|
68
77
|
- **Logging Systems**: The Claude Code Router uses two separate logging systems:
|
|
69
78
|
- **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
79
|
- **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.
|
|
80
|
+
- **`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
81
|
- **`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"`.
|
|
82
|
+
- **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
83
|
- **`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
84
|
|
|
75
85
|
- **`Providers`**: Used to configure different model providers.
|
|
@@ -97,125 +107,34 @@ Claude Code Router supports environment variable interpolation for secure API ke
|
|
|
97
107
|
|
|
98
108
|
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
109
|
|
|
100
|
-
Here is a
|
|
110
|
+
Here is a minimal configuration:
|
|
101
111
|
|
|
102
112
|
```json
|
|
103
113
|
{
|
|
104
114
|
"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
115
|
"Providers": [
|
|
110
116
|
{
|
|
111
117
|
"name": "openrouter",
|
|
112
118
|
"api_base_url": "https://openrouter.ai/api/v1/chat/completions",
|
|
113
|
-
"api_key": "
|
|
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
|
-
],
|
|
119
|
+
"api_key": "$OPENROUTER_API_KEY",
|
|
120
|
+
"models": ["anthropic/claude-sonnet-4"],
|
|
120
121
|
"transformer": {
|
|
121
122
|
"use": ["openrouter"]
|
|
122
123
|
}
|
|
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
124
|
}
|
|
207
125
|
],
|
|
208
126
|
"Router": {
|
|
209
|
-
"default": "
|
|
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"
|
|
127
|
+
"default": "openrouter,anthropic/claude-sonnet-4"
|
|
215
128
|
}
|
|
216
129
|
}
|
|
217
130
|
```
|
|
218
131
|
|
|
132
|
+
> **See also**: The complete config reference, per-provider examples, and routing
|
|
133
|
+
> options are in `docs/docs/server/config/basic.md`,
|
|
134
|
+
> `docs/docs/server/config/providers.md`,
|
|
135
|
+
> `docs/docs/server/config/transformers.md`, and
|
|
136
|
+
> `docs/docs/server/config/routing.md`.
|
|
137
|
+
|
|
219
138
|
#### Adding a New Provider
|
|
220
139
|
|
|
221
140
|
If you want to add a new provider and automatically discover its models, follow these steps:
|
|
@@ -278,6 +197,21 @@ You can also configure Claude Code to always use the router by editing its `sett
|
|
|
278
197
|
|
|
279
198
|
This approach lets you run `claude` directly without needing `ccr code`.
|
|
280
199
|
|
|
200
|
+
#### Via OpenAI-compatible clients
|
|
201
|
+
|
|
202
|
+
OpenAI SDK clients can use CCR without an Anthropic compatibility layer:
|
|
203
|
+
|
|
204
|
+
```shell
|
|
205
|
+
export OPENAI_BASE_URL=http://127.0.0.1:3456/v1
|
|
206
|
+
export OPENAI_API_KEY=your-router-api-key
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
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.
|
|
210
|
+
|
|
211
|
+
Discover what the router can reach with `GET /v1/models` (alias `/models`). By default it emits literal `provider,model` IDs. Set `"MODEL_ID_OUTPUT": "masked"` to expose otherwise-filtered IDs as `claude-<hex>` while leaving IDs beginning with `claude` or `anthropic` unchanged. Chat routes accept both representations regardless of this output setting. To surface CCR models in Codex's native picker, use `ccr codex-config`, which writes a Codex model catalog and the managed `config.toml` block that points Codex at the router.
|
|
212
|
+
|
|
213
|
+
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, and provider file IDs return an explicit 400 error instead of being silently discarded. Client-hosted `custom` tools (the shape Codex uses for MCP / plugin tools) are projected onto function tools rather than rejected, so Codex can call them through the router.
|
|
214
|
+
|
|
281
215
|
> **Note**: After modifying the configuration file, you need to restart the service for the changes to take effect:
|
|
282
216
|
>
|
|
283
217
|
> ```shell
|
|
@@ -294,7 +228,7 @@ ccr ui
|
|
|
294
228
|
|
|
295
229
|
This will open a web-based interface where you can easily view and edit your `config.json` file.
|
|
296
230
|
|
|
297
|
-

|
|
298
232
|
|
|
299
233
|
### 5. CLI Model Management
|
|
300
234
|
|
|
@@ -305,24 +239,9 @@ ccr model
|
|
|
305
239
|
```
|
|
306
240
|

|
|
307
241
|
|
|
308
|
-
|
|
309
|
-
|
|
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.
|
|
242
|
+
`ccr model` lets you view configured models, switch models per scenario, add models, and create providers with transformer configuration — all with validation and prompts.
|
|
324
243
|
|
|
325
|
-
For non-interactive model discovery
|
|
244
|
+
For non-interactive model discovery from any provider with a model-list endpoint:
|
|
326
245
|
|
|
327
246
|
```shell
|
|
328
247
|
ccr model get claude
|
|
@@ -330,44 +249,13 @@ ccr model get gemini
|
|
|
330
249
|
ccr model get openai
|
|
331
250
|
```
|
|
332
251
|
|
|
333
|
-
|
|
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
|
|
337
|
-
|
|
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
|
-
```
|
|
252
|
+
`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.
|
|
367
253
|
|
|
368
|
-
|
|
254
|
+
> **See also**: `docs/docs/server/guides/model-discovery.md` and `docs/docs/cli/commands/model-get.md`.
|
|
255
|
+
>
|
|
256
|
+
> **Note**: After syncing models into `config.json`, restart the service with `ccr restart`.
|
|
369
257
|
|
|
370
|
-
> **Note**:
|
|
258
|
+
> **Note — account OAuth providers**: The provider auth flows below (Antigravity, Codex, Claude subscription, Qwen) authenticate through your account-level OAuth session rather than a dedicated API key. See [DISCLAIMER.md](DISCLAIMER.md) for the interoperability and compliance notes that apply to those providers.
|
|
371
259
|
|
|
372
260
|
#### Antigravity Authentication
|
|
373
261
|
|
|
@@ -403,9 +291,7 @@ Example provider:
|
|
|
403
291
|
}
|
|
404
292
|
```
|
|
405
293
|
|
|
406
|
-
|
|
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.
|
|
294
|
+
`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
295
|
|
|
410
296
|
> **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
297
|
|
|
@@ -413,248 +299,74 @@ Why those Gemini options:
|
|
|
413
299
|
|
|
414
300
|
The Codex provider supports two authentication modes:
|
|
415
301
|
|
|
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**:
|
|
302
|
+
- **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.
|
|
303
|
+
- **PAT** via a literal `api_key: "at-..."` (or an env var containing an `at-` token) — skips `ccr codex-auth`.
|
|
443
304
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
```shell
|
|
447
|
-
docker exec -it claude-code-router ccr codex-auth
|
|
448
|
-
```
|
|
305
|
+
An `at-` value is always treated as a PAT and never silently falls back to OAuth; any other placeholder selects OAuth tokens.
|
|
449
306
|
|
|
450
|
-
|
|
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`.
|
|
307
|
+
> **See also**: Full Codex setup, both auth modes, provider config, and troubleshooting are in `docs/docs/server/guides/codex.md`.
|
|
480
308
|
|
|
481
309
|
#### Cursor Provider Authentication
|
|
482
310
|
|
|
483
|
-
The Cursor provider
|
|
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.
|
|
311
|
+
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
312
|
|
|
517
|
-
> **See also**: Full Cursor setup
|
|
313
|
+
> **See also**: Full Cursor setup, bridge/plan/agent modes, and configuration are in `docs/docs/server/guides/cursor.md`.
|
|
518
314
|
|
|
519
315
|
#### Claude Subscription Authentication
|
|
520
316
|
|
|
521
|
-
|
|
317
|
+
Route Claude Code through your Claude Pro or Max subscription via OAuth:
|
|
522
318
|
|
|
523
319
|
```shell
|
|
524
320
|
ccr claude-auth
|
|
525
321
|
```
|
|
526
322
|
|
|
527
|
-
|
|
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`.
|
|
323
|
+
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
324
|
|
|
561
|
-
**
|
|
325
|
+
> **See also**: Full setup, the transformer chain, client classification, and billing/identity details are in `docs/docs/server/guides/claude-auth.md`.
|
|
562
326
|
|
|
563
327
|
#### Qwen Provider Authentication
|
|
564
328
|
|
|
565
|
-
|
|
329
|
+
Authenticate with Qwen Chat by saving a JWT from `chat.qwen.ai` localStorage:
|
|
566
330
|
|
|
567
331
|
```shell
|
|
568
332
|
ccr qwen-auth
|
|
569
333
|
```
|
|
570
334
|
|
|
571
|
-
|
|
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.
|
|
335
|
+
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
336
|
|
|
591
|
-
**
|
|
337
|
+
> **See also**: Full Qwen setup and provider config are in `docs/docs/server/guides/qwen.md`.
|
|
592
338
|
|
|
593
339
|
#### Chrome On-Device Bridge
|
|
594
340
|
|
|
595
|
-
|
|
341
|
+
Use Chrome's built-in Gemini Nano (~4GB local model) with zero API cost via a host-side bridge:
|
|
596
342
|
|
|
597
343
|
```bash
|
|
598
|
-
|
|
599
|
-
ccr chrome-bridge
|
|
600
|
-
|
|
601
|
-
# Custom ports
|
|
344
|
+
ccr chrome-bridge # default: port 3457, CDP 9222
|
|
602
345
|
ccr chrome-bridge --port 3457 --cdp 9222
|
|
603
346
|
```
|
|
604
347
|
|
|
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
|
|
348
|
+
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
349
|
|
|
617
|
-
**Prerequisites**:
|
|
350
|
+
**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
351
|
|
|
619
|
-
> **Note for Docker**: The bridge runs on the Docker **host**, not inside the container
|
|
352
|
+
> **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
353
|
|
|
621
354
|
### 6. Presets Management
|
|
622
355
|
|
|
623
|
-
|
|
356
|
+
Save, share, and reuse configurations:
|
|
624
357
|
|
|
625
358
|
```shell
|
|
626
|
-
#
|
|
627
|
-
ccr preset export my-preset
|
|
628
|
-
|
|
629
|
-
# Export with metadata
|
|
359
|
+
ccr preset export my-preset # export current config as a preset
|
|
630
360
|
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
|
|
361
|
+
ccr preset install /path/to/preset # install from a directory
|
|
636
362
|
ccr preset list
|
|
637
|
-
|
|
638
|
-
# Show preset information
|
|
639
363
|
ccr preset info my-preset
|
|
640
|
-
|
|
641
|
-
# Delete a preset
|
|
642
364
|
ccr preset delete my-preset
|
|
643
365
|
```
|
|
644
366
|
|
|
645
|
-
|
|
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
|
|
367
|
+
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
368
|
|
|
652
|
-
**
|
|
653
|
-
```
|
|
654
|
-
~/.claude-code-router/presets/
|
|
655
|
-
├── my-preset/
|
|
656
|
-
│ └── manifest.json # Contains configuration and metadata
|
|
657
|
-
```
|
|
369
|
+
> **See also**: `docs/docs/cli/commands/preset.md`.
|
|
658
370
|
|
|
659
371
|
### 7. Activate Command (Environment Variables Setup)
|
|
660
372
|
|
|
@@ -682,299 +394,24 @@ The `activate` command sets the following environment variables:
|
|
|
682
394
|
|
|
683
395
|
> **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
396
|
|
|
685
|
-
#### Providers
|
|
686
|
-
|
|
687
|
-
The `Providers` array is where you define the different model providers you want to use. Each provider object requires:
|
|
397
|
+
#### Providers and Transformers
|
|
688
398
|
|
|
689
|
-
|
|
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.
|
|
399
|
+
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
400
|
|
|
695
|
-
|
|
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
|
-
```
|
|
401
|
+
> **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
402
|
|
|
748
403
|
**Available Built-in Transformers:**
|
|
749
404
|
|
|
750
|
-
- `Anthropic
|
|
751
|
-
- `deepseek
|
|
752
|
-
- `
|
|
753
|
-
|
|
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:**
|
|
405
|
+
- `Anthropic` — passes through to an Anthropic endpoint unchanged. `OpenAI` — registers the `/v1/chat/completions` route (the body is already in OpenAI shape).
|
|
406
|
+
- Provider adapters: `deepseek`, `groq`, `mistral`, `openrouter`, `gemini` / `vertex-gemini`, `codex`, `claude-auth`, `antigravity-auth`, `qwen-auth`, `cursor-sdk`, `chrome-on-device`.
|
|
407
|
+
- `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.
|
|
408
|
+
- Experimental gist/CLI integrations: `gemini-cli`, `chutes-glm`, `qwen-cli`, `rovo-cli`.
|
|
892
409
|
|
|
893
|
-
The
|
|
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.
|
|
410
|
+
> **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
411
|
|
|
962
412
|
**Custom Transformers:**
|
|
963
413
|
|
|
964
|
-
|
|
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
|
-
```
|
|
414
|
+
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
415
|
|
|
979
416
|
#### Router
|
|
980
417
|
|
|
@@ -994,42 +431,7 @@ Example: `/model openrouter,anthropic/claude-3.5-sonnet`
|
|
|
994
431
|
|
|
995
432
|
#### Custom Router
|
|
996
433
|
|
|
997
|
-
For
|
|
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
|
-
```
|
|
434
|
+
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
435
|
|
|
1034
436
|
##### Subagent Routing
|
|
1035
437
|
|
|
@@ -1068,10 +470,10 @@ See [the implementation plan and review](tasks/caching-plan.md) for the provider
|
|
|
1068
470
|
|
|
1069
471
|
## Status Line (Beta)
|
|
1070
472
|
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
|
-

|
|
1072
474
|
|
|
1073
475
|
The effect is as follows:
|
|
1074
|
-

|
|
1075
477
|
|
|
1076
478
|
## 🤖 GitHub Actions
|
|
1077
479
|
|