claude-code-router

One local control plane for every AI agent: route across models, fuse new capabilities, orchestrate tools, and stay fully in control.

36,611 stars TypeScript Markdown Skills API Spec
AI Prompts & Specs

Repository: musistudio/claude-code-router


Stars: 32440

CLAUDE.md

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Claude Code Router is a tool that routes Claude Code requests to different LLM providers. It uses a Monorepo architecture with four main packages:

- cli (@musistudio/claude-code-router): Command-line tool providing the ccr command
- server (@CCR/server): Core server handling API routing and transformations
- shared (@CCR/shared): Shared constants, utilities, and preset management
- ui (@CCR/ui): Web management interface (React + Vite)

Build Commands

Build all packages


bash
pnpm build

Build individual packages


bash
pnpm build:cli      # Build CLI
pnpm build:server # Build Server
pnpm build:ui # Build UI

Development mode


bash
pnpm dev:cli        # Develop CLI (ts-node)
pnpm dev:server # Develop Server (ts-node)
pnpm dev:ui # Develop UI (Vite)

Publish


bash
pnpm release        # Build and publish all packages

Core Architecture

1. Routing System (packages/server/src/utils/router.ts)

The routing logic determines which model a request should be sent to:

- Default routing: Uses Router.default configuration
- Project-level routing: Checks ~/.claude/projects/<project-id>/claude-code-router.json
- Custom routing: Loads custom JavaScript router function via CUSTOM_ROUTER_PATH
- Built-in scenario routing:
- background: Background tasks (typically lightweight models)
- think: Thinking-intensive tasks (Plan Mode)
- longContext: Long context (exceeds longContextThreshold tokens)
- webSearch: Web search tasks
- image: Image-related tasks

Token calculation uses tiktoken (cl100k_base) to estimate request size.

2. Transformer System

The project uses the @musistudio/llms package (external dependency) to handle request/response transformations. Transformers adapt to different provider API differences:

- Built-in transformers: anthropic, deepseek, gemini, openrouter, groq, maxtoken, tooluse, reasoning, enhancetool, etc.
- Custom transformers: Load external plugins via transformers array in config.json

Transformer configuration supports:
- Global application (provider level)
- Model-specific application
- Option passing (e.g., max_tokens parameter for maxtoken)

3. Agent System (packages/server/src/agents/)

Agents are pluggable feature modules that can:
- Detect whether to handle a request (shouldHandle)
- Modify requests (reqHandler)
- Provide custom tools (tools)

Built-in agents:
- imageAgent: Handles image-related tasks

Agent tool call flow:
1. Detect and mark agents in preHandler hook
2. Add agent tools to the request
3. Intercept tool call events in onSend hook
4. Execute agent tool and initiate new LLM request
5. Stream results back

4. SSE Stream Processing

The server uses custom Transform streams to handle Server-Sent Events:
- SSEParserTransform: Parses SSE text stream into event objects
- SSESerializerTransform: Serializes event objects into SSE text stream
- rewriteStream: Intercepts and modifies stream data (for agent tool calls)

5. Configuration Management

Configuration file location: ~/.claude-code-router/config.json

Key features:
- Supports environment variable interpolation ($VAR_NAME or ${VAR_NAME})
- JSON5 format (supports comments)
- Automatic backups (keeps last 3 backups)
- Hot reload requires service restart (ccr restart)

Configuration validation:
- If Providers are configured, both HOST and APIKEY must be set
- Otherwise listens on 0.0.0.0 without authentication

6. Logging System

Two separate logging systems:

Server-level logs (pino):
- Location: ~/.claude-code-router/logs/ccr-*.log
- Content: HTTP requests, API calls, server events
- Configuration: LOG_LEVEL (fatal/error/warn/info/debug/trace)

Application-level logs:
- Location: ~/.claude-code-router/claude-code-router.log
- Content: Routing decisions, business logic events

CLI Commands

bash
ccr start      # Start server
ccr stop # Stop server
ccr restart # Restart server
ccr status # Show status
ccr code # Execute claude command
ccr model # Interactive model selection and configuration
ccr preset # Manage presets (export, install, list, info, delete)
ccr activate # Output shell environment variables (for integration)
ccr ui # Open Web UI
ccr statusline # Integrated statusline (reads JSON from stdin)

Preset Commands

bash
ccr preset export <name>      # Export current configuration as a preset
ccr preset install <source> # Install a preset from file, URL, or name
ccr preset list # List all installed presets
ccr preset info <name> # Show preset information
ccr preset delete <name> # Delete a preset

Subagent Routing

Use special tags in subagent prompts to specify models:

text
<CCR-SUBAGENT-MODEL>provider,model</CCR-SUBAGENT-MODEL>
Please help me analyze this code...

Preset System

The preset system allows users to save, share, and reuse configurations easily.

Preset Structure

Presets are stored in ~/.claude-code-router/presets/<preset-name>/manifest.json

Each preset contains:
- Metadata: name, version, description, author, keywords, etc.
- Configuration: Providers, Router, transformers, and other settings
- Dynamic Schema (optional): Input fields for collecting required information during installation
- Required Inputs (optional): Fields that need to be filled during installation (e.g., API keys)

Core Functions

Located in packages/shared/src/preset/:

- export.ts: Export current configuration as a preset directory
- exportPreset(presetName, config, options): Creates preset directory with manifest.json
- Automatically sanitizes sensitive data (api_key fields become {{field}} placeholders)

- install.ts: Install and manage presets
- installPreset(preset, config, options): Install preset to config
- loadPreset(source): Load preset from directory
- listPresets(): List all installed presets
- isPresetInstalled(presetName): Check if preset is installed
- validatePreset(preset): Validate preset structure

- merge.ts: Merge preset configuration with existing config
- Handles conflicts using different strategies (ask, overwrite, merge, skip)

- sensitiveFields.ts: Identify and sanitize sensitive fields
- Detects api_key, password, secret fields automatically
- Replaces sensitive values with environment variable placeholders

Preset File Format

manifest.json (in preset directory):

json
{
"name": "my-preset",
"version": "1.0.0",
"description": "My configuration",
"author": "Author Name",
"keywords": ["openai", "production"],
"Providers": [...],
"Router": {...},
"schema": [
{
"id": "apiKey",
"type": "password",
"label": "OpenAI API Key",
"prompt": "Enter your OpenAI API key"
}
]
}

CLI Integration

The CLI layer (packages/cli/src/utils/preset/) handles:
- User interaction and prompts
- File operations
- Display formatting

Key files:
- commands.ts: Command handlers for ccr preset subcommands
- export.ts: CLI wrapper for export functionality
- install.ts: CLI wrapper for install functionality

Dependencies

text
cli β†’ server β†’ shared
server β†’ @musistudio/llms (core routing and transformation logic)
ui (standalone frontend application)

Development Notes

1. Node.js version: Requires >= 18.0.0
2. Package manager: Uses pnpm (monorepo depends on workspace protocol)
3. TypeScript: All packages use TypeScript, but UI package is ESM module
4. Build tools:
- cli/server/shared: esbuild
- ui: Vite + TypeScript
5. @musistudio/llms: This is an external dependency package providing the core server framework and transformer functionality, type definitions in packages/server/src/types.d.ts
6. Code comments: All comments in code MUST be written in English
7. Documentation: When implementing new features, add documentation to the docs project instead of creating standalone md files

Configuration Example Locations

- Main configuration example: Complete example in README.md
- Custom router example: custom-router.example.js


README.md

![](blog/images/claude-code-router-img.png)

![](README_zh.md)
![Discord](https://discord.gg/rdftVMaUcS)
![](https://github.com/musistudio/claude-code-router/blob/main/LICENSE)

<hr>

![](blog/images/sponsors/glm-en.jpg)

This project is sponsored by Z.ai, supporting us with their GLM CODING PLAN.

GLM CODING PLAN is a subscription service designed for AI coding, starting at just $10/month. It provides access to their flagship GLM-4.7 & (GLM-5 Only Available for Pro UsersοΌ‰model across 10+ popular AI coding tools (Claude Code, Cline, Roo Code, etc.), offering developers top-tier, fast, and stable coding experiences.

Get 10% OFF GLM CODING PLAN:https://z.ai/subscribe?ic=8JVLJQFSKB

Progressive Disclosure of Agent Tools from the Perspective of CLI Tool Style

A powerful tool to route Claude Code requests to different models and customize any request.

![](blog/images/claude-code.png)

✨ Features

- Model Routing: Route requests to different models based on your needs (e.g., background tasks, thinking, long context).
- Multi-Provider Support: Supports various model providers like OpenRouter, DeepSeek, Ollama, Gemini, Volcengine, and SiliconFlow.
- Request/Response Transformation: Customize requests and responses for different providers using transformers.
- Dynamic Model Switching: Switch models on-the-fly within Claude Code using the /model command.
- CLI Model Management: Manage models and providers directly from the terminal with ccr model.
- GitHub Actions Integration: Trigger Claude Code tasks in your GitHub workflows.
- Plugin System: Extend functionality with custom transformers.

πŸš€ Getting Started

1. Installation

First, ensure you have Claude Code installed:

shell
npm install -g @anthropic-ai/claude-code

Then, install Claude Code Router:

shell
npm install -g @musistudio/claude-code-router

2. Configuration

Create and configure your ~/.claude-code-router/config.json file. For more details, you can refer to config.example.json.

The config.json file has several key sections:

- PROXY_URL (optional): You can set a proxy for API requests, for example: "PROXY_URL": "http://127.0.0.1:7890".
- LOG (optional): You can enable logging by setting it to true. When set to false, no log files will be created. Default is true.
- LOG_LEVEL (optional): Set the logging level. Available options are: "fatal", "error", "warn", "info", "debug", "trace". Default is "debug".
- Logging Systems: The Claude Code Router uses two separate logging systems:
- 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
- Application-level logs: Routing decisions and business logic events are logged in ~/.claude-code-router/claude-code-router.log
- 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".
- 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".
- 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.

- Providers: Used to configure different model providers.
- Router: Used to set up routing rules. default specifies the default model, which will be used for all requests if no other route is configured.
- API_TIMEOUT_MS: Specifies the timeout for API calls in milliseconds.

#### Environment Variable Interpolation

Claude Code Router supports environment variable interpolation for secure API key management. You can reference environment variables in your config.json using either $VAR_NAME or ${VAR_NAME} syntax:

json
{
"OPENAI_API_KEY": "$OPENAI_API_KEY",
"GEMINI_API_KEY": "${GEMINI_API_KEY}",
"Providers": [
{
"name": "openai",
"api_base_url": "https://api.openai.com/v1/chat/completions",
"api_key": "$OPENAI_API_KEY",
"models": ["gpt-5", "gpt-5-mini"]
}
]
}

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.

Here is a comprehensive example:

json
{
"APIKEY": "your-secret-key",
"PROXY_URL": "http://127.0.0.1:7890",
"LOG": true,
"API_TIMEOUT_MS": 600000,
"NON_INTERACTIVE_MODE": false,
"Providers": [
{
"name": "openrouter",
"api_base_url": "https://openrouter.ai/api/v1/chat/completions",
"api_key": "sk-xxx",
"models": [
"google/gemini-2.5-pro-preview",
"anthropic/claude-sonnet-4",
"anthropic/claude-3.5-sonnet",
"anthropic/claude-3.7-sonnet:thinking"
],
"transformer": {
"use": ["openrouter"]
}
},
{
"name": "deepseek",
"api_base_url": "https://api.deepseek.com/chat/completions",
"api_key": "sk-xxx",
"models": ["deepseek-chat", "deepseek-reasoner"],
"transformer": {
"use": ["deepseek"],
"deepseek-chat": {
"use": ["tooluse"]
}
}
},
{
"name": "ollama",
"api_base_url": "http://localhost:11434/v1/chat/completions",
"api_key": "ollama",
"models": ["qwen2.5-coder:latest"]
},
{
"name": "gemini",
"api_base_url": "https://generativelanguage.googleapis.com/v1beta/models/",
"api_key": "sk-xxx",
"models": ["gemini-2.5-flash", "gemini-2.5-pro"],
"transformer": {
"use": ["gemini"]
}
},
{
"name": "volcengine",
"api_base_url": "https://ark.cn-beijing.volces.com/api/v3/chat/completions",
"api_key": "sk-xxx",
"models": ["deepseek-v3-250324", "deepseek-r1-250528"],
"transformer": {
"use": ["deepseek"]
}
},
{
"name": "modelscope",
"api_base_url": "https://api-inference.modelscope.cn/v1/chat/completions",
"api_key": "",
"models": ["Qwen/Qwen3-Coder-480B-A35B-Instruct", "Qwen/Qwen3-235B-A22B-Thinking-2507"],
"transformer": {
"use": [
[
"maxtoken",
{
"max_tokens": 65536
}
],
"enhancetool"
],
"Qwen/Qwen3-235B-A22B-Thinking-2507": {
"use": ["reasoning"]
}
}
},
{
"name": "dashscope",
"api_base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions",
"api_key": "",
"models": ["qwen3-coder-plus"],
"transformer": {
"use": [
[
"maxtoken",
{
"max_tokens": 65536
}
],
"enhancetool"
]
}
},
{
"name": "aihubmix",
"api_base_url": "https://aihubmix.com/v1/chat/completions",
"api_key": "sk-",
"models": [
"Z/glm-4.5",
"claude-opus-4-20250514",
"gemini-2.5-pro"
]
}
],
"Router": {
"default": "deepseek,deepseek-chat",
"background": "ollama,qwen2.5-coder:latest",
"think": "deepseek,deepseek-reasoner",
"longContext": "openrouter,google/gemini-2.5-pro-preview",
"longContextThreshold": 60000,
"webSearch": "gemini,gemini-2.5-flash"
}
}

3. Running Claude Code with the Router

Start Claude Code using the router:

shell
ccr code

Note: After modifying the configuration file, you need to restart the service for the changes to take effect:

> ``shell

ccr restart

`

4. UI Mode

For a more intuitive experience, you can use the UI mode to manage your configuration:

shell
ccr ui

This will open a web-based interface where you can easily view and edit your config.json file.

!UI

5. CLI Model Management

For users who prefer terminal-based workflows, you can use the interactive CLI model selector:

shell
ccr model

![](blog/images/models.gif)

This command provides an interactive interface to:

- View current configuration:
- See all configured models (default, background, think, longContext, webSearch, image)
- Switch models: Quickly change which model is used for each router type
- Add new models: Add models to existing providers
- Create new providers: Set up complete provider configurations including:
- Provider name and API endpoint
- API key
- Available models
- Transformer configuration with support for:
- Multiple transformers (openrouter, deepseek, gemini, etc.)
- Transformer options (e.g., maxtoken with custom limits)
- Provider-specific routing (e.g., OpenRouter provider preferences)

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.

6. Presets Management

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.

shell

Export current configuration as a preset


ccr preset export my-preset

Export with metadata


ccr preset export my-preset --description "My OpenAI config" --author "Your Name" --tags "openai,production"

Install a preset from local directory


ccr preset install /path/to/preset

List all installed presets


ccr preset list

Show preset information


ccr preset info my-preset

Delete a preset


ccr preset delete my-preset

Preset Features:
- Export: Save your current configuration as a preset directory (with manifest.json)
- Install: Install presets from local directories
- Sensitive Data Handling: API keys and other sensitive data are automatically sanitized during export (marked as
{{field}} placeholders)
- Dynamic Configuration: Presets can include input schemas for collecting required information during installation
- Version Control: Each preset includes version metadata for tracking updates

Preset File Structure:

text
~/.claude-code-router/presets/
β”œβ”€β”€ my-preset/
β”‚ └── manifest.json # Contains configuration and metadata

7. Activate Command (Environment Variables Setup)

The activate command allows you to set up environment variables globally in your shell, enabling you to use the claude command directly or integrate Claude Code Router with applications built using the Agent SDK.

To activate the environment variables, run:

shell
eval "$(ccr activate)"

This command outputs the necessary environment variables in shell-friendly format, which are then set in your current shell session. After activation, you can:

- Use claude command directly: Run claude commands without needing to use ccr code. The claude command will automatically route requests through Claude Code Router.
- Integrate with Agent SDK applications: Applications built with the Anthropic Agent SDK will automatically use the configured router and models.

The activate command sets the following environment variables:

- ANTHROPIC_AUTH_TOKEN: API key from your configuration
-
ANTHROPIC_BASE_URL: The local router endpoint (default: http://127.0.0.1:3456)
-
NO_PROXY: Set to 127.0.0.1 to prevent proxy interference
-
DISABLE_TELEMETRY: Disables telemetry
-
DISABLE_COST_WARNINGS: Disables cost warnings
-
API_TIMEOUT_MS: API timeout from your configuration

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).

#### Providers

The Providers array is where you define the different model providers you want to use. Each provider object requires:

- name: A unique name for the provider.
-
api_base_url: The full API endpoint for chat completions.
-
api_key: Your API key for the provider.
-
models: A list of model names available from this provider.
-
transformer (optional): Specifies transformers to process requests and responses.

#### Transformers

Transformers allow you to modify the request and response payloads to ensure compatibility with different provider APIs.

- 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.

json
{
"name": "openrouter",
"api_base_url": "https://openrouter.ai/api/v1/chat/completions",
"api_key": "sk-xxx",
"models": [
"google/gemini-2.5-pro-preview",
"anthropic/claude-sonnet-4",
"anthropic/claude-3.5-sonnet"
],
"transformer": { "use": ["openrouter"] }
}

- 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.

json
{
"name": "deepseek",
"api_base_url": "https://api.deepseek.com/chat/completions",
"api_key": "sk-xxx",
"models": ["deepseek-chat", "deepseek-reasoner"],
"transformer": {
"use": ["deepseek"],
"deepseek-chat": { "use": ["tooluse"] }
}
}

- 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.

json
{
"name": "siliconflow",
"api_base_url": "https://api.siliconflow.cn/v1/chat/completions",
"api_key": "sk-xxx",
"models": ["moonshotai/Kimi-K2-Instruct"],
"transformer": {
"use": [
[
"maxtoken",
{
"max_tokens": 16384
}
]
]
}
}

Available Built-in Transformers:

- 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).
-
deepseek: Adapts requests/responses for DeepSeek API.
-
gemini: Adapts requests/responses for Gemini API.
-
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. See an example below:

json
"transformer": {
"use": ["openrouter"],
"moonshotai/kimi-k2": {
"use": [
[
"openrouter",
{
"provider": {
"only": ["moonshotai/fp8"]
}
}
]
]
}
}

-
groq: Adapts requests/responses for groq API.
-
maxtoken: Sets a specific max_tokens value.
-
tooluse: Optimizes tool usage for certain models via tool_choice.
-
gemini-cli (experimental): Unofficial support for Gemini via Gemini CLI gemini-cli.js.
-
reasoning: Used to process the reasoning_content field.
-
sampling: Used to process sampling information fields such as temperature, top_p, top_k, and repetition_penalty.
-
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).
-
cleancache: Clears the cache_control field from requests.
-
vertex-gemini: Handles the Gemini API using Vertex authentication.
-
chutes-glm Unofficial support for GLM 4.5 model via Chutes chutes-glm-transformer.js.
-
qwen-cli (experimental): Unofficial support for qwen3-coder-plus model via Qwen CLI qwen-cli.js.
-
rovo-cli (experimental): Unofficial support for gpt-5 via Atlassian Rovo Dev CLI rovo-cli.js.

Custom Transformers:

You can also create your own transformers and load them via the transformers field in config.json.

json
{
"transformers": [
{
"path": "/User/xxx/.claude-code-router/plugins/gemini-cli.js",
"options": {
"project": "xxx"
}
}
]
}

#### Router

The Router object defines which model to use for different scenarios:

- default: The default model for general tasks.
-
background: A model for background tasks. This can be a smaller, local model to save costs.
-
think: A model for reasoning-heavy tasks, like Plan Mode.
-
longContext: A model for handling long contexts (e.g., > 60K tokens).
-
longContextThreshold (optional): The token count threshold for triggering the long context model. Defaults to 60000 if not specified.
-
webSearch: Used for handling web search tasks and this requires the model itself to support the feature. If you're using openrouter, you need to add the :online suffix after the model name.
-
image (beta): Used for handling image-related tasks (supported by CCR’s built-in agent). If the model does not support tool calling, you need to set the config.forceUseImageAgent property to true.

- You can also switch models dynamically in Claude Code with the /model command:
/model provider_name,model_name
Example:
/model openrouter,anthropic/claude-3.5-sonnet

#### Custom Router

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.

In your config.json:

json
{
"CUSTOM_ROUTER_PATH": "/User/xxx/.claude-code-router/custom-router.js"
}

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.

Here is an example of a custom-router.js based on custom-router.example.js:

javascript
// /User/xxx/.claude-code-router/custom-router.js

/
* A custom router function to determine which model to use based on the request.
*
* @param {object} req - The request object from Claude Code, containing the request body.
* @param {object} config - The application's config object.
* @returns {Promise<string|null>} - A promise that resolves to the "provider,model_name" string, or null to use the default router.
*/
module.exports = async function router(req, config) {
const userMessage = req.body.messages.find((m) => m.role === "user")?.content;

if (userMessage && userMessage.includes("explain this code")) {
// Use a powerful model for code explanation
return "openrouter,anthropic/claude-3.5-sonnet";
}

// Fallback to the default router configuration
return null;
};

##### Subagent Routing

For routing within subagents, you must specify a particular provider and model by including <CCR-SUBAGENT-MODEL>provider,model</CCR-SUBAGENT-MODEL> at the beginning of the subagent's prompt. This allows you to direct specific subagent tasks to designated models.

Example:

text
<CCR-SUBAGENT-MODEL>openrouter,anthropic/claude-3.5-sonnet</CCR-SUBAGENT-MODEL>
Please help me analyze this code snippet for potential optimizations...

Status Line (Beta)


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.
!statusline-config.png

The effect is as follows:
!statusline

πŸ€– GitHub Actions

Integrate Claude Code Router into your CI/CD pipeline. After setting up Claude Code Actions, modify your .github/workflows/claude.yaml to use the router:

yaml
name: Claude Code

on:
issue_comment:
types: [created]
# ... other triggers

jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
# ... other conditions
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Prepare Environment
run: |
curl -fsSL https://bun.sh/install | bash
mkdir -p $HOME/.claude-code-router
cat << 'EOF' > $HOME/.claude-code-router/config.json
{
"log": true,
"NON_INTERACTIVE_MODE": true,
"OPENAI_API_KEY": "${{ secrets.OPENAI_API_KEY }}",
"OPENAI_BASE_URL": "https://api.deepseek.com",
"OPENAI_MODEL": "deepseek-chat"
}
EOF
shell: bash

- name: Start Claude Code Router
run: |
nohup ~/.bun/bin/bunx @musistudio/[email protected] start &
shell: bash

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@beta
env:
ANTHROPIC_BASE_URL: http://localhost:3456
with:
anthropic_api_key: "any-string-is-ok"

Note: When running in GitHub Actions or other automation environments, make sure to set "NON_INTERACTIVE_MODE": true` in your configuration to prevent the process from hanging due to stdin handling issues.

This setup allows for interesting automations, like running tasks during off-peak hours to reduce API costs.

πŸ“ Further Reading

- Project Motivation and How It Works
- Maybe We Can Do More with the Router
- GLM-4.6 Supports Reasoning and Interleaved Thinking

❀️ Support & Sponsoring

If you find this project helpful, please consider sponsoring its development. Your support is greatly appreciated!

![ko-fi](https://ko-fi.com/F1F31GN2GM)

Paypal

<table>
<tr>
<td><img src="/blog/images/alipay.jpg" width="200" alt="Alipay" /></td>
<td><img src="/blog/images/wechat.jpg" width="200" alt="WeChat Pay" /></td>
</tr>
</table>

Our Sponsors

A huge thank you to all our sponsors for their generous support!


- AIHubmix
- BurnCloud
- 302.AI
- Zζ™Ίθ°±
- @Simon Leischnig
- @duanshuaimin
- @vrgitadmin
- @\*o
- @ceilwoo
- @\*θ―΄
- @\*ζ›΄
- @K\*g
- @R\*R
- @bobleer
- @\*θ‹—
- @\*εˆ’
- @Clarence-pan
- @carter003
- @S\*r
- @\*ζ™–
- @\*敏
- @Z\*z
- @\*η„Ά
- @cluic
- @\*θ‹—
- @PromptExpert
- @\*εΊ”
- @yusnake
- @\*飞
- @θ‘£\*
- @\*ζ±€
- @\*ζΆ―
- @\*:-οΌ‰
- @\\磊
- @\*琒
- @\*成
- @Z\*o
- @\*琨
- @congzhangzh
- @\*\_
- @Z\*m
- @*ι‘«
- @c\*y
- @\*ζ˜•
- @witsice
- @b\*g
- @\*δΊΏ
- @\*θΎ‰
- @JACK
- @\*ε…‰
- @W\*l
- @kesku
- @biguncle
- @δΊŒε‰ε‰
- @a\*g
- @\*ζž—
- @\*ε’Έ
- @\*明
- @S\*y
- @f\*o
- @\*ζ™Ί
- @F\*t
- @r\*c
- @qierkang
- @\*军
- @snrise-z
- @\*ηŽ‹
- @greatheart1000
- @\*ηŽ‹
- @zcutlip
- @Peng-YM
- @\*ζ›΄
- @\*.
- @F\*t
- @\*ζ”Ώ
- @\*ι“­
- @\*叢
- @δΈƒ\*o
- @\*青
- @\\晨
- @\*远
- @\*ιœ„
- @\\吉
- @\\飞
- @\\ι©°
- @x\*g
- @\\东
- @\*落
- @哆\*k
- @\*ζΆ›
- @θ‹—ε€§
- @\*ε‘’
- @\d*u
- @crizcraig
- s\*s
- \*火
- \*ε‹€
- \\ι”Ÿ
- \*ζΆ›
- \\明
- \*ηŸ₯
- \*θ―­
- \*η“œ


(If your name is masked, please contact me via my homepage email to update it with your GitHub username.)