The Problem
Developers using Claude Code need a way to route requests to different LLM providers, apply request/response transformations, and manage models from the command line. Without a router they must edit Claude Code’s config manually or write ad‑hoc scripts, which is error‑prone and hard to version.
What This Does
claude-code-router sits between Claude Code and the underlying model APIs. It reads a JSON config (~/.claude-code-router/config.json) and, based on the request type, selects a provider (OpenRouter, Gemini, Ollama, etc.) and applies a transformer chain. The core logic lives in packages/core/src (transformers, plugin manager, server) while the CLI front‑end lives in packages/cli/src. The documentation site (docs/src/pages/index.tsx) and the example presets (examples/*.json) illustrate typical usage.
How It Is Wired
- Entry point – the CLI is launched via
packages/cli/src/cli.ts(mainfunction, line 99). mainparses arguments (via@inquirer/prompts) and callsruninpackages/cli/src/utils/index.ts(line 186).runcreates a readline interface, then invokesgetServerfrompackages/server/src/index.ts.getServerconstructs an Express app (packages/core/src/server.ts:createApp) and registers routes frompackages/core/src/api/routes.ts.- When a Claude Code request arrives, the route handler calls
registerNamespace→initialize→registerDefaultTransformersInternal(see the call chainstart → registerNamespace → initialize → registerDefaultTransformersInternal). This iteratesObject.values(Transformers)and triggers each transformer (e.g.,packages/core/src/transformer/anthropic.transformer.ts,openai.responses.transformer.ts). - The selected transformer calls the underlying model SDK (10 distinct functions across the repo, e.g.,
postinpackages/server/src/index.ts). These are the only outward‑facing calls: they perform HTTP requests to provider endpoints. - After the model response returns, the transformer chain may modify the payload before the server sends it back to Claude Code.
Blast‑radius hotspots – packages/core/src/types/llm.ts and packages/core/src/types/transformer.ts are imported by 17 other modules each, making them high‑impact “hub” modules. The import cycle among packages/core/src/types/llm.ts, packages/core/src/types/transformer.ts, and packages/cli/src/utils/index.ts adds instability (instability = 1). The most‑connected functions (cn, delete, post) are called from dozens of places, so changes there ripple widely.
How To Use It
# 1. Install Claude Code (global)
npm install -g @anthropic-ai/claude-code
# 2. Install the router (global)
npm install -g @musistudio/claude-code-router
# 3. Create a config (example path shown in README)
cat > ~/.claude-code-router/config.json <<'EOF'
{
"PROXY_URL": "http://127.0.0.1:7890",
"LOG": true,
"LOG_LEVEL": "debug",
"APIKEY": "your-secret-key",
"HOST": "127.0.0.1"
}
EOF
# 4. Start the router server (CLI command)
ccr start # implemented in packages/cli/src/cli.ts
# 5. Switch models from Claude Code
/model gpt-4 # routed through the `/model` command handled by the transformer chain
All commands are documented under docs/docs/cli/commands/ and the example presets (examples/simple-preset-example.json) show how to declare provider‑specific settings.
Real‑World Use
A CI pipeline can run unit tests, then invoke ccr start in a Docker container (see packages/server/Dockerfile). A GitHub Action can call ccr model list to verify that the correct provider is selected before deploying code generation steps. Because the router logs both server‑level (pino files) and application‑level events, operators can audit which model answered a given request.
Code Health & Issues
- High – Pin GitHub Actions –
.github/workflows/*uses version tags (@v4,@v3). Replace with 40‑char commit SHAs to avoid supply‑chain attacks. - High – No test suite – 157 source files, 0 test files. Add at least one test per public entry point and run them in CI.
- Medium – Enable Dependabot – No
dependabot.yml; add to keep dependencies patched. - Medium – Use lockfile in CI – Workflows run
npm install; switch tonpm ci(orpnpm install --frozen-lockfile). - Medium – Pin Docker base image –
node:20-alpineshould be referenced by digest. - Medium – Add vulnerability scan – No dependency‑review step; integrate
osv-scannerordependency-review-action. - Medium – Checkout without persisting credentials – Add
persist-credentials: falseto the checkout step. - Medium – Run as non‑root – Dockerfile lacks a non‑root
USER. - Low – Job timeouts –
.github/workflows/docker-publish.ymlomitstimeout-minutes. - Low – Repository conventions – Missing
.editorconfig,.gitattributes, and formatter config.
Additional observations: the repo includes a full Docusaurus site, Docker support, and CI, but lacks automated tests and some hardening best practices identified above.
The Bottom Line
claude-code-router provides a concrete, extensible way to route Claude Code requests across multiple LLM providers and apply custom transformations, with a clear CLI and server architecture. However, the lack of tests, the presence of import cycles, and several security‑hardening gaps mean the codebase needs prudent review before production deployment. It is suitable for teams comfortable tweaking TypeScript monorepos and willing to add the missing test and hardening steps.