{"id":50619366,"url":"https://github.com/starbaser/ccproxy","last_synced_at":"2026-06-09T21:00:45.600Z","repository":{"id":307920506,"uuid":"1028650374","full_name":"starbaser/ccproxy","owner":"starbaser","description":"Build mods for Claude Code: Hook any request, modify any response, /model \"with-your-custom-model\", intelligent model routing using your logic or ours","archived":false,"fork":false,"pushed_at":"2026-05-24T00:35:42.000Z","size":10405,"stargazers_count":400,"open_issues_count":9,"forks_count":27,"subscribers_count":5,"default_branch":"main","last_synced_at":"2026-05-24T02:24:55.917Z","etag":null,"topics":["ai","ai-gateway","ai-proxy","ai-tools","anthropic","claude","claude-ai","claude-api","claude-code","claude-max","claudecode","gemini","gemini-cli","litellm","llm","llm-gateway","llm-proxy","llmops","openai","openrouter"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/starbaser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-29T21:13:45.000Z","updated_at":"2026-05-23T04:37:58.000Z","dependencies_parsed_at":"2025-08-04T21:48:16.492Z","dependency_job_id":null,"html_url":"https://github.com/starbaser/ccproxy","commit_stats":null,"previous_names":["starbased-co/ccproxy","starbased-co/claude-code-proxy","starbaser/ccproxy"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/starbaser/ccproxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbaser%2Fccproxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbaser%2Fccproxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbaser%2Fccproxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbaser%2Fccproxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/starbaser","download_url":"https://codeload.github.com/starbaser/ccproxy/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/starbaser%2Fccproxy/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34125332,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-gateway","ai-proxy","ai-tools","anthropic","claude","claude-ai","claude-api","claude-code","claude-max","claudecode","gemini","gemini-cli","litellm","llm","llm-gateway","llm-proxy","llmops","openai","openrouter"],"created_at":"2026-06-06T10:00:20.063Z","updated_at":"2026-06-09T21:00:45.594Z","avatar_url":"https://github.com/starbaser.png","language":"Python","funding_links":[],"categories":["LLMOps","Monitoring and Observability","Monitoring and Dashboards","*Ops for AI"],"sub_categories":["LLM Gateways \u0026 Proxies","LLMOps"],"readme":"# `ccproxy` - Claude Code Proxy [![Version](https://img.shields.io/badge/version-1.2.0-blue.svg)](https://github.com/starbased-co/ccproxy)\n\n\u003e [Discord](https://starbased.net/discord)\n\n`ccproxy` unlocks the full potential of your Claude Code by enabling Claude use alongside other LLM providers like OpenAI, Gemini, and Perplexity\n\nIt works by intercepting Claude Code's requests through a [LiteLLM Proxy Server](https://docs.litellm.ai/docs/simple_proxy), allowing you to route different types of requests to the most suitable model - keep your unlimited Claude for standard coding, send large contexts to Gemini's 2M token window, route web searches to Perplexity, all while Claude Code thinks it's talking to the standard API.\n\n\u003e 🚀 **v2.0 prerelease is now available** — a ground-up rewrite that drops the LiteLLM proxy server entirely. v2.0 jails your process in a rootless WireGuard namespace, intercepts at the network layer with full TLS inspection, and routes traffic through a DAG-driven hook pipeline. Any LLM client works — not just Claude Code. **[Check it out →](https://github.com/starbased-co/ccproxy/tree/dev)**\n\n## Installation\n\n**Important:** ccproxy must be installed with LiteLLM in the same environment so that LiteLLM can import the ccproxy handler.\n\n### Recommended: Install as uv tool\n\n```bash\n# Install from PyPI\nuv tool install claude-ccproxy --with 'litellm[proxy]'\n\n# Or install from GitHub (latest)\nuv tool install git+https://github.com/starbased-co/ccproxy.git --with 'litellm[proxy]'\n```\n\nThis installs:\n\n- `ccproxy` command (for managing the proxy)\n- `litellm` bundled in the same environment (so it can import ccproxy's handler)\n\n### Alternative: Install with pip\n\n```bash\n# Install both packages in the same virtual environment\npip install git+https://github.com/starbased-co/ccproxy.git\npip install 'litellm[proxy]'\n```\n\n**Note:** With pip, both packages must be in the same virtual environment.\n\n### Verify Installation\n\n```bash\nccproxy --help\n# Should show ccproxy commands\n\nwhich litellm\n# Should point to litellm in ccproxy's environment\n```\n\n## Usage\n\nRun the automated setup:\n\n```bash\n# This will create all necessary configuration files in ~/.ccproxy\nccproxy install\n\ntree ~/.ccproxy\n# ~/.ccproxy\n# ├── ccproxy.yaml\n# └── config.yaml\n\n# ccproxy.py is auto-generated when you start the proxy\n\n# Start the proxy server\nccproxy start --detach\n\n# Start Claude Code\nccproxy run claude\n# Or add to your .zshrc/.bashrc\nexport ANTHROPIC_BASE_URL=\"http://localhost:4000\"\n# Or use an alias\nalias claude-proxy='ANTHROPIC_BASE_URL=\"http://localhost:4000\" claude'\n```\n\nCongrats, you have installed `ccproxy`! The installed configuration files are intended to be a simple demonstration, thus continuing on to the next section to configure `ccproxy` is **recommended**.\n\n### Configuration\n\n#### `ccproxy.yaml`\n\nThis file controls how `ccproxy` hooks into your Claude Code requests and how to route them to different LLM models based on rules. Here you specify rules, their evaluation order, and criteria like token count, model type, or tool usage.\n\n```yaml\nccproxy:\n  debug: true\n\n  # OAuth token sources - map provider names to shell commands\n  # Tokens are loaded at startup for SDK/API access outside Claude Code\n  oat_sources:\n    anthropic: \"jq -r '.claudeAiOauth.accessToken' ~/.claude/.credentials.json\"\n    # Extended format with custom User-Agent:\n    # gemini:\n    #   command: \"jq -r '.token' ~/.gemini/creds.json\"\n    #   user_agent: \"MyApp/1.0\"\n\n  hooks:\n    - ccproxy.hooks.rule_evaluator # evaluates rules against request (needed for routing)\n    - ccproxy.hooks.model_router # routes to appropriate model\n    - ccproxy.hooks.forward_oauth # forwards OAuth token to provider\n    - ccproxy.hooks.extract_session_id # extracts session ID for LangFuse tracking\n    # - ccproxy.hooks.capture_headers  # logs HTTP headers (with redaction)\n    # - ccproxy.hooks.forward_apikey   # forwards x-api-key header\n  rules:\n    # example rules\n    - name: token_count\n      rule: ccproxy.rules.TokenCountRule\n      params:\n        - threshold: 60000\n    - name: web_search\n      rule: ccproxy.rules.MatchToolRule\n      params:\n        - tool_name: WebSearch\n    # basic rules\n    - name: background\n      rule: ccproxy.rules.MatchModelRule\n      params:\n        - model_name: claude-3-5-haiku-20241022\n    - name: think\n      rule: ccproxy.rules.ThinkingRule\n\nlitellm:\n  host: 127.0.0.1\n  port: 4000\n  num_workers: 4\n  debug: true\n  detailed_debug: true\n```\n\nWhen `ccproxy` receives a request from Claude Code, the `rule_evaluator` hook labels the request with the first matching rule:\n\n1. `MatchModelRule`: A request with `model: claude-3-5-haiku-20241022` is labeled: `background`\n2. `ThinkingRule`: A request with `thinking: {enabled: true}` is labeled: `think`\n\nIf a request doesn't match any rule, it receives the `default` label.\n\n#### `config.yaml`\n\n[LiteLLM's proxy configuration file](https://docs.litellm.ai/docs/proxy/config_settings) is where your model deployments are defined. The `model_router` hook takes advantage of [LiteLLM's model alias feature](https://docs.litellm.ai/docs/completion/model_alias) to dynamically rewrite the model field in requests based on rule criteria before LiteLLM selects a deployment. When a request is labeled (e.g., think), the hook changes the model from whatever Claude Code requested to the corresponding alias, allowing seamless redirection to different models.\n\nThe diagram shows how routing labels (⚡ default, 🧠 think, 🍃 background) map to their corresponding model deployments:\n\n```mermaid\ngraph LR\n    subgraph ccproxy_yaml[\"\u003ccode\u003eccproxy.yaml\u003c/code\u003e\"]\n        R1[\"\u003cdiv style='text-align:left'\u003e\u003ccode\u003erules:\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e- name: default\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e- name: think\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e- name: background\u003c/code\u003e\u003c/div\u003e\"]\n    end\n\n    subgraph config_yaml[\"\u003ccode\u003econfig.yaml\u003c/code\u003e\"]\n        subgraph aliases[\" \"]\n            A1[\"\u003cdiv style='text-align:left'\u003e\u003ccode\u003emodel_name: default\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003elitellm_params:\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e\u0026nbsp;\u0026nbsp;model: claude-sonnet-4-5-20250929\u003c/code\u003e\u003c/div\u003e\"]\n            A2[\"\u003cdiv style='text-align:left'\u003e\u003ccode\u003emodel_name: think\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003elitellm_params:\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e\u0026nbsp;\u0026nbsp;model: claude-opus-4-5-20251101\u003c/code\u003e\u003c/div\u003e\"]\n            A3[\"\u003cdiv style='text-align:left'\u003e\u003ccode\u003emodel_name: background\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003elitellm_params:\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e\u0026nbsp;\u0026nbsp;model: claude-3-5-haiku-20241022\u003c/code\u003e\u003c/div\u003e\"]\n        end\n\n        subgraph models[\" \"]\n            M1[\"\u003cdiv style='text-align:left'\u003e\u003ccode\u003emodel_name: claude-sonnet-4-5-20250929\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003elitellm_params:\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e\u0026nbsp;\u0026nbsp;model: anthropic/claude-sonnet-4-5-20250929\u003c/code\u003e\u003c/div\u003e\"]\n            M2[\"\u003cdiv style='text-align:left'\u003e\u003ccode\u003emodel_name: claude-opus-4-5-20251101\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003elitellm_params:\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e\u0026nbsp;\u0026nbsp;model: anthropic/claude-opus-4-5-20251101\u003c/code\u003e\u003c/div\u003e\"]\n            M3[\"\u003cdiv style='text-align:left'\u003e\u003ccode\u003emodel_name: claude-3-5-haiku-20241022\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003elitellm_params:\u003c/code\u003e\u003cbr/\u003e\u003ccode\u003e\u0026nbsp;\u0026nbsp;model: anthropic/claude-3-5-haiku-20241022\u003c/code\u003e\u003c/div\u003e\"]\n        end\n    end\n\n    R1 ==\u003e|\"⚡ \u003ccode\u003edefault\u003c/code\u003e\"| A1\n    R1 ==\u003e|\"🧠 \u003ccode\u003ethink\u003c/code\u003e\"| A2\n    R1 ==\u003e|\"🍃 \u003ccode\u003ebackground\u003c/code\u003e\"| A3\n\n    A1 --\u003e|\"\u003ccode\u003ealias\u003c/code\u003e\"| M1\n    A2 --\u003e|\"\u003ccode\u003ealias\u003c/code\u003e\"| M2\n    A3 --\u003e|\"\u003ccode\u003ealias\u003c/code\u003e\"| M3\n\n    style R1 fill:#e6f3ff,stroke:#4a90e2,stroke-width:2px,color:#000\n\n    style A1 fill:#fffbf0,stroke:#ffa500,stroke-width:2px,color:#000\n    style A2 fill:#fff0f5,stroke:#ff1493,stroke-width:2px,color:#000\n    style A3 fill:#f0fff0,stroke:#32cd32,stroke-width:2px,color:#000\n\n    style M1 fill:#f8f9fa,stroke:#6c757d,stroke-width:1px,color:#000\n    style M2 fill:#f8f9fa,stroke:#6c757d,stroke-width:1px,color:#000\n    style M3 fill:#f8f9fa,stroke:#6c757d,stroke-width:1px,color:#000\n\n    style aliases fill:#f0f8ff,stroke:#333,stroke-width:1px\n    style models fill:#f5f5f5,stroke:#333,stroke-width:1px\n    style ccproxy_yaml fill:#e8f4fd,stroke:#2196F3,stroke-width:2px\n    style config_yaml fill:#ffffff,stroke:#333,stroke-width:2px\n```\n\nAnd the corresponding `config.yaml`:\n\n```yaml\n# config.yaml\nmodel_list:\n  # aliases here are used to select a deployment below\n  - model_name: default\n    litellm_params:\n      model: claude-sonnet-4-5-20250929\n\n  - model_name: think\n    litellm_params:\n      model: claude-opus-4-5-20251101\n\n  - model_name: background\n    litellm_params:\n      model: claude-3-5-haiku-20241022\n\n  # deployments\n  - model_name: claude-sonnet-4-5-20250929\n    litellm_params:\n      model: anthropic/claude-sonnet-4-5-20250929\n      api_base: https://api.anthropic.com\n\n  - model_name: claude-opus-4-5-20251101\n    litellm_params:\n      model: anthropic/claude-opus-4-5-20251101\n      api_base: https://api.anthropic.com\n\n  - model_name: claude-3-5-haiku-20241022\n    litellm_params:\n      model: anthropic/claude-3-5-haiku-20241022\n      api_base: https://api.anthropic.com\n\nlitellm_settings:\n  callbacks:\n    - ccproxy.handler\ngeneral_settings:\n  forward_client_headers_to_llm_api: true\n```\n\nSee [docs/configuration.md](docs/configuration.md) for more information on how to customize your Claude Code experience using `ccproxy`.\n\n\u003c!-- ## Extended Thinking --\u003e\n\n\u003c!-- Normally, when you send a message, Claude Code does a simple keyword scan for words/phrases like \"think deeply\" to determine whether or not to enable thinking, as well the size of the thinking token budget. [Simply including the word \"ultrathink](https://claudelog.com/mechanics/ultrathink-plus-plus/) sets the thinking token budget to the maximum of `31999`. --\u003e\n\n## Routing Rules\n\n`ccproxy` provides several built-in rules as an homage to [claude-code-router](https://github.com/musistudio/claude-code-router):\n\n- **MatchModelRule**: Routes based on the requested model name\n- **ThinkingRule**: Routes requests containing a \"thinking\" field\n- **TokenCountRule**: Routes requests with large token counts to high-capacity models\n- **MatchToolRule**: Routes based on tool usage (e.g., WebSearch)\n\nSee [`rules.py`](src/ccproxy/rules.py) for implementing your own rules.\n\nCustom rules (and hooks) are loaded with the same mechanism that LiteLLM uses to import the custom callbacks, that is, they are imported as by the LiteLLM python process as named module from within it's virtual environment (e.g. `import custom_rule_file.custom_rule_function`), or as a python script adjacent to `config.yaml`.\n\n## Hooks\n\nHooks are functions that process requests at different stages. Configure them in `ccproxy.yaml`:\n\n| Hook                 | Description                                                                         |\n| -------------------- | ----------------------------------------------------------------------------------- |\n| `rule_evaluator`     | Evaluates rules and labels requests for routing                                     |\n| `model_router`       | Routes requests to appropriate model based on labels                                |\n| `forward_oauth`      | Forwards OAuth tokens to providers (supports multi-provider with custom User-Agent) |\n| `forward_apikey`     | Forwards `x-api-key` header to proxied requests                                     |\n| `extract_session_id` | Extracts session ID from Claude Code's `user_id` for LangFuse tracking              |\n| `capture_headers`    | Logs HTTP headers as LangFuse trace metadata (with sensitive value redaction)       |\n\nHooks can accept parameters via configuration:\n\n```yaml\nhooks:\n  - hook: ccproxy.hooks.capture_headers\n    params:\n      - headers: [\"user-agent\", \"x-request-id\"] # Optional: filter specific headers\n```\n\nSee [`hooks.py`](src/ccproxy/hooks.py) for implementing custom hooks.\n\n## CLI Commands\n\n`ccproxy` provides several commands for managing the proxy server:\n\n```bash\n# Install configuration files\nccproxy install [--force]\n\n# Start LiteLLM\nccproxy start [--detach]\n\n# Stop LiteLLM\nccproxy stop\n\n# Check proxy server status (includes url field for tool detection)\nccproxy status         # Human-readable output\nccproxy status --json  # JSON output with url field\n\n# View proxy server logs\nccproxy logs [-f] [-n LINES]\n\n# Run any command with proxy environment variables\nccproxy run \u003ccommand\u003e [args...]\n```\n\nAfter installation and setup, you can run any command through the `ccproxy`:\n\n```bash\n# Run Claude Code through the proxy\nccproxy run claude --version\nccproxy run claude -p \"Explain quantum computing\"\n\n# Run other tools through the proxy\nccproxy run curl http://localhost:4000/health\nccproxy run python my_script.py\n\n```\n\nThe `ccproxy run` command sets up the following environment variables:\n\n- `ANTHROPIC_BASE_URL` - For Anthropic SDK compatibility\n- `OPENAI_API_BASE` - For OpenAI SDK compatibility\n- `OPENAI_BASE_URL` - For OpenAI SDK compatibility\n\n## Development\n\n### Request Lifecycle\n\n```mermaid\nsequenceDiagram\n    participant CC as cli app\n    participant CP as litellm request → ccproxy\n    participant LP as ccproxy ← litellm response\n    participant API as api.anthropic.com\n\n    Note over CC,API: Request Flow\n    CC-\u003e\u003eCP: API Request\u003cbr/\u003e(messages, model, tools, etc.)\n    Note over CP,LP: \u003cAdd hooks in any working order here\u003e\n\n    Note right of CP: ccproxy.hooks.rule_evaluator\n    CP--\u003e\u003eCP: ↓\n    Note right of CP: ccproxy.hooks.model_router\n    CP--\u003e\u003eCP: ↓\n    Note right of CP: ccproxy.hooks.forward_oauth\n    CP--\u003e\u003eCP: ↓\n    Note right of CP: \u003cYour code here\u003e\n    CP-\u003e\u003eAPI: LiteLLM: Outbound Modified Provider-specific Request\n\n    Note over CC,API: Response Flow (Streaming)\n    API--\u003e\u003eLP: Streamed Response\n    Note right of CP: First to see response\u003cbr/\u003eCan modify/hook into stream\n    LP--\u003e\u003eCC: Streamed Response\u003cbr/\u003e(forwarded to cli app)\n```\n\n### Local Setup\n\nWhen developing ccproxy locally:\n\n```bash\ncd /path/to/ccproxy\n\n# Install in editable mode with litellm bundled\n# Changes to source code are reflected immediately without reinstalling\nuv tool install --editable . --with 'litellm[proxy]' --force\n\n# Restart the proxy to pick up code changes\nccproxy stop\nccproxy start --detach\n\n# Run tests\nuv run pytest\n\n# Linting \u0026 formatting\nuv run ruff format .\nuv run ruff check --fix .\n```\n\nThe `--editable` flag enables live code changes without reinstallation. The handler file (`~/.ccproxy/ccproxy.py`) is automatically regenerated on every `ccproxy start`.\n\n**Note:** Custom `ccproxy.py` files are preserved - auto-generation only overwrites files containing the `# AUTO-GENERATED` marker.\n\n## Troubleshooting\n\n### ImportError: Could not import handler from ccproxy\n\n**Symptom:** LiteLLM fails to start with import errors like:\n\n```\nImportError: Could not import handler from ccproxy\n```\n\n**Cause:** LiteLLM and ccproxy are in different isolated environments.\n\n**Solution:** Reinstall ccproxy with litellm bundled:\n\n```bash\n# Using uv tool (from PyPI)\nuv tool install claude-ccproxy --with 'litellm[proxy]' --force\n\n# Or from GitHub (latest)\nuv tool install git+https://github.com/starbased-co/ccproxy.git --with 'litellm[proxy]' --force\n\n# Or for local development (editable mode)\ncd /path/to/ccproxy\nuv tool install --editable . --with 'litellm[proxy]' --force\n```\n\n### Handler Configuration Not Updating\n\n**Symptom:** Changes to `handler` field in `ccproxy.yaml` don't take effect.\n\n**Cause:** Handler file is only regenerated on `ccproxy start`.\n\n**Solution:**\n\n```bash\nccproxy stop\nccproxy start --detach\n# This regenerates ~/.ccproxy/ccproxy.py\n```\n\n### Verifying Installation\n\nCheck that ccproxy is accessible to litellm:\n\n```bash\n# Find litellm's environment\nwhich litellm\n\n# Check if ccproxy is installed in the same environment\n$(dirname $(which litellm))/python -c \"import ccproxy; print(ccproxy.__file__)\"\n# Should print path without errors\n```\n\n## Contributing\n\nI welcome contributions! Please see the [Contributing Guide](CONTRIBUTING.md) for details on:\n\n- Reporting issues and asking questions\n- Setting up development environment\n- Code style and testing requirements\n- Submitting pull requests\n\nSince this is a new project, I especially appreciate:\n\n- Bug reports and feedback\n- Documentation improvements\n- Test coverage additions\n- Feature suggestions\n- Any of your implementations using `ccproxy`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstarbaser%2Fccproxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstarbaser%2Fccproxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstarbaser%2Fccproxy/lists"}