A thin proxy wrapper for Claude Code that automatically routes API calls to the right model depending on what Claude is doing.
Claude Code has a built-in opusplan model alias that is supposed to switch from Opus (for planning) to Sonnet (for execution), but it has known reliability issues. ccm solves this properly by running a local HTTP proxy that:
- Uses your plan model (default:
claude-opus-4-6) while Claude is in Plan mode - where it reasons about what to do and structures its approach - Automatically switches to your execution model (default:
claude-sonnet-4-6) the moment Claude exits Plan mode to start doing the work - Falls back through a configurable chain of models when rate-limited (HTTP 429), so your session never gets stuck
- Shows which model is active in the terminal at all times
Claude Code → ccm proxy (localhost:7777) → Anthropic API
The proxy intercepts every /v1/messages request and rewrites the model field based on current session state. It detects the plan→execution transition by watching the streaming API response for the exit_plan_mode internal tool call that Claude Code emits when leaving Plan mode.
Per-model API keys are supported, so you can use a high-tier key for plan requests and a standard key for execution, keeping billing separate.
npm install -g claude-model-routerThis is the recommended installation method for download tracking and automatic updates.
git clone https://github.com/Always-Friday/ccm
cd ccm
npm link # or: ln -s "$(pwd)/bin/ccm.js" /usr/local/bin/ccmRequires Node.js 22+ (already installed if you have Claude Code).
ccm setupThis creates ~/.ccm/config.json. You will be prompted for:
| Setting | Default | Description |
|---|---|---|
| Plan mode API base URL | https://api.anthropic.com |
Anthropic API (or Azure/Bedrock endpoint) for plan requests |
| Plan model | claude-opus-4-6 |
Used while in Plan mode |
| Plan API key | env ANTHROPIC_API_KEY |
Optional separate key for plan requests |
| Execution mode API base URL | (same as plan) | Optional separate URL for execution requests |
| Execution model | claude-sonnet-4-6 |
Used after exiting Plan mode |
| Execution API key | env ANTHROPIC_API_KEY |
Optional separate key for execution requests |
| Proxy port | 7777 |
Local port for the proxy daemon |
ccm installThis appends a claude() wrapper function to your shell RC file (~/.zshrc, ~/.bashrc, or ~/.config/fish/config.fish). After that, typing claude in any terminal automatically:
- Checks whether the ccm proxy daemon is already running
- Starts it in the background if it isn't
- Launches Claude Code with
ANTHROPIC_BASE_URLpointing at the proxy
Restart your terminal (or source ~/.zshrc) once after running ccm install. Running ccm install again is safe — it detects the existing hook and does nothing.
If you prefer not to use the hook, just replace claude with ccm in your workflow — they are equivalent.
# One-time install so `claude` routes through ccm automatically
ccm install
# Then use `claude` as normal — ccm handles the rest
claude
claude --continue
claude "fix the failing test in auth.spec.ts"Or, without the shell hook, replace claude with ccm:
# Start a new session
ccm
# Pass any claude arguments as normal
ccm --continue
ccm "fix the failing test in auth.spec.ts"
ccm --model claude-opus-4-6 # override per-session if needed
# Check which model is currently active
ccm status
# Stop the background proxy daemon
ccm stopOn startup:
[ccm] plan: claude-opus-4-6 → exec: claude-sonnet-4-6 | port 7777
When Claude exits Plan mode:
[ccm] plan → execution | now using: claude-sonnet-4-6
On a 429 rate limit:
[ccm] rate limited | claude-opus-4-6 → claude-sonnet-4-6
~/.ccm/config.json (created by ccm setup):
{
"planModel": "claude-opus-4-6",
"planApiKey": null,
"planBaseUrl": "https://api.anthropic.com",
"executionModel": "claude-sonnet-4-6",
"executionApiKey": null,
"executionBaseUrl": null,
"fallbackChain": [
{ "model": "claude-sonnet-4-6", "apiKey": null },
{ "model": "claude-haiku-4-5-20251001", "apiKey": null }
],
"proxyPort": 7777
}Setting an apiKey to null falls back to the ANTHROPIC_API_KEY environment variable.
If you have multiple Anthropic accounts (e.g. a high-tier account for planning and a standard account for execution), configure keys explicitly:
{
"planModel": "claude-opus-4-6",
"planApiKey": "sk-ant-opus-key...",
"planBaseUrl": "https://api.anthropic.com",
"executionModel": "claude-sonnet-4-6",
"executionApiKey": "sk-ant-sonnet-key...",
"executionBaseUrl": "https://api.anthropic.com",
"fallbackChain": [
{ "model": "claude-haiku-4-5-20251001", "apiKey": "sk-ant-fallback-key..." }
]
}Set planBaseUrl and/or executionBaseUrl to your provider's endpoint. The proxy forwards all headers including anthropic-version and x-api-key as-is (replacing x-api-key with the configured key for the active model).
ccm only works with endpoints that speak the Anthropic Messages API format. It will not work with OpenAI-compatible APIs (e.g. Ollama, local models, or any provider using Authorization: Bearer + OpenAI request/response shapes).
| Provider | Compatible | Notes |
|---|---|---|
| Anthropic API | Yes | Default |
| AWS Bedrock (Claude models) | Yes | Set planBaseUrl/executionBaseUrl to your Bedrock endpoint |
| Azure AI (Claude models) | Yes | Set planBaseUrl/executionBaseUrl to your Azure endpoint |
| LiteLLM | Yes | Only when configured in Anthropic-format passthrough mode |
| OpenAI API | No | Different auth header and request/response format |
| Ollama / local models | No | OpenAI-compatible format only |
The proxy relies on three Anthropic-specific details that have no OpenAI equivalent:
- Auth via
x-api-keyheader (notAuthorization: Bearer) - Request body with a
modelfield insidePOST /v1/messages - SSE events shaped as
content_block_startwithtool_usetype, used to detect theexit_plan_modetransition
The proxy runs as a background daemon process. It starts automatically when you run ccm and persists between sessions on the same machine (so subsequent ccm invocations start instantly).
The daemon's PID is stored in ~/.ccm/state.json. If the process dies unexpectedly, ccm restarts it automatically on the next launch.
To stop it manually:
ccm stopTo completely remove ccm from your system:
# Stop the proxy daemon
ccm stop
# Uninstall the npm package
npm uninstall -g claude-model-router
# Remove configuration and state files
rm -rf ~/.ccm
# Remove the shell hook from your shell RC file
# For zsh:
sed -i '' '/# ccm shell hook/,+1d' ~/.zshrc
# For bash:
sed -i '/# ccm shell hook/,+1d' ~/.bashrc
# For fish:
# Edit ~/.config/fish/config.fish and remove the ccm sectionAfter removing the shell hook, restart your terminal or run source ~/.zshrc (or source ~/.bashrc).
MIT