Monitor and intercept Claude Code API requests with multi-provider LLM support.
A hackable Claude API proxy for monitoring AI agent requests and connecting multiple LLM providers. No complex AI frameworks - just FastAPI, requests, and clean code you can easily modify.
pip install .With Gemini (default):
export OPENAICOMPATIBLE_API_KEY=your-gemini-key
claudecodexWith AWS Bedrock:
export LLM_PROVIDER=bedrock
claudecodexexport ANTHROPIC_BASE_URL=http://localhost:8082That's it! Claude Code will now send requests through your proxy.
- Monitor requests - See all Claude Code API calls in real-time with color-coded logs
- Switch providers - Run the same workflow on Claude, GPT-4, Gemini, or local models
- Debug workflows - Watch which tools get called and when
- Extend capabilities - Simple translation layer makes adding new providers straightforward
┌─────────────┐ ANTHROPIC_BASE_URL=localhost:8082 ┌─────────────────┐
│ Claude Code │ ──────────────────────────────────────► │ Claude Codex │
└─────────────┘ POST /v1/messages │ (server.py) │
└─────────────────┘
│
get_provider() │
(Protocol-based) │
│
┌──────────────────────────────────────┼──────────────────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│BedrockProvider │ │OpenAICompatible │ │ Your Custom │
│ (bedrock.py) │ │ Provider │ │ Provider │
│ │ │(openai_compat.py│ │ (LLMProvider) │
│Implements: │ │ │ │ │
│ LLMProvider │ │Implements: │ │Implements: │
│ Protocol │ │ LLMProvider │ │ LLMProvider │
│ │ │ Protocol │ │ Protocol │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ AWS Bedrock │ │ OpenAI API │ │ Any LLM │
│ Converse API │ │ Chat Completions│ │ Provider │
│ │ │ │ │ │
│ • Claude Sonnet │ │ • GPT-4/3.5 │ │ • Local Models │
│ • Claude Haiku │ │ • Gemini 2.0 │ │ • Custom APIs │
│ • Claude Opus │ │ • Ollama/LM Std │ │ • Fine-tuned │
└─────────────────┘ └─────────────────┘ └─────────────────┘
How it works:
- Claude Code sends requests to Claude Codex (localhost:8082)
server.pyroutes to provider instance viaget_provider()- Provider implements
LLMProviderprotocol withcompletion()andcount_tokens()methods - Each provider translates Claude API ↔ Provider API formats
- Response flows back to Claude Code
Provider Support:
- AWS Bedrock: Claude Sonnet/Haiku/Opus (native format)
- OpenAI: GPT-4, GPT-3.5 (via OpenAI API)
- Google Gemini: 2.0-flash, 1.5-pro (via OpenAI-compatible API)
- Local: Ollama, LM Studio (via OpenAI-compatible API)
- See what Claude Code is actually doing - intercept and log all requests/responses
- Monitor tool calling patterns - watch which tools get called and when
- Debug agentic workflows - understand multi-step task execution in real-time
- No AI frameworks required - pure FastAPI + requests, easy to modify and extend
- Connect any LLM - swap between Claude, GPT-4, Gemini, local models instantly
- Extend time limits - run longer workflows by routing through your own infrastructure
- Add new providers - protocol-based design makes adding new LLMs straightforward
- Custom model access - use fine-tuned models or experimental endpoints
The protocol-based architecture makes it easy to add new LLM providers:
-
Create a new provider file (e.g.,
src/claudecodex/custom_provider.py) -
Implement the
LLMProviderprotocol:from claudecodex.provider import LLMProvider from claudecodex.models import MessagesRequest, MessagesResponse, TokenCountRequest, TokenCountResponse class CustomProvider: """Your custom LLM provider.""" def completion(self, request: MessagesRequest) -> MessagesResponse: # 1. Convert Claude format to your provider's format # 2. Call your provider's API # 3. Convert response back to Claude format pass def count_tokens(self, request: TokenCountRequest) -> TokenCountResponse: # Count tokens for your provider pass
-
Register in
server.py:from claudecodex.custom_provider import CustomProvider def get_provider() -> LLMProvider: provider_type = get_provider_type() if provider_type == "bedrock": return BedrockProvider() elif provider_type == "openai_compatible": return OpenAICompatibleProvider() elif provider_type == "custom": return CustomProvider() # ...
Reference the existing BedrockProvider and OpenAICompatibleProvider implementations for translation patterns.
As developers working with AI agents, we often wonder:
- What requests is Claude Code actually making?
- How does it decide which tools to call?
- Can I run the same workflow on a different/cheaper model?
- How can I extend session time limits?
This proxy gives you full visibility and control without complex AI frameworks.
- AWS Bedrock: Claude Sonnet/Haiku/Opus (
LLM_PROVIDER=bedrock) - Google Gemini: gemini-2.0-flash (
LLM_PROVIDER=openai_compatible+ Gemini config) - OpenAI: GPT-4/3.5 (
LLM_PROVIDER=openai_compatible+ OpenAI config) - Local: Ollama, LM Studio (
LLM_PROVIDER=openai_compatible+ local config)
This project uses uv for package management and ruff for linting and formatting.
-
Install
uv:curl -LsSf https://astral.sh/uv/install.sh | sh -
Create a virtual environment and install dependencies:
uv venv uv pip sync requirements.txt
-
Activate the virtual environment:
source .venv/bin/activate # macOS/Linux .venv\Scripts\activate # Windows
-
Lint and format:
ruff check src ruff format src
pytest tests/ -v # All tests
pytest tests/unit/ -v # Unit tests only (fast)
pytest tests/integration/ -v # Integration tests (requires API keys)pip install -e . # Install in editable modeImportant: Do not commit the .env file to version control. It should be added to your .gitignore file.
# Provider selection
LLM_PROVIDER=bedrock|openai_compatible # default: openai_compatible
SERVER_PORT=8082 # default: 8082
# Bedrock provider
AWS_PROFILE=your-profile # default: saml
AWS_DEFAULT_REGION=us-east-1 # default: us-east-1
BEDROCK_MODEL_ID=model-id # default: us.anthropic.claude-sonnet-4-*
# OpenAI-compatible provider
OPENAICOMPATIBLE_API_KEY=your-key # required for openai_compatible
OPENAICOMPATIBLE_BASE_URL=endpoint-url # provider-specific
OPENAI_MODEL=model-name # default: gemini-2.0-flash
# Claude Code integration
ANTHROPIC_BASE_URL=http://localhost:8082├── pyproject.toml # Modern packaging configuration
├── src/claudecodex/ # Core package
│ ├── __init__.py # Package info
│ ├── main.py # Entry point
│ ├── server.py # FastAPI server & routing
│ ├── provider.py # LLMProvider protocol definition
│ ├── bedrock.py # AWS Bedrock provider
│ ├── openai_compatible.py # OpenAI-compatible provider
│ ├── models.py # Pydantic models
│ └── logging_config.py # Monitoring & logging
├── tests/ # Test package
│ ├── unit/ # Fast tests with mocks
│ │ ├── test_api_endpoints.py # API endpoint tests
│ │ └── test_openai_compatible.py # OpenAI service tests
│ └── integration/ # Full system tests
│ ├── test_bedrock_integration.py # Bedrock end-to-end tests
│ └── test_gemini_integration.py # Gemini end-to-end tests
├── logs/ # Generated log files
├── requirements.txt # Dependencies
└── README.md # This file
Protocol-Based Architecture: Extensible design makes adding new providers easy
provider.py-LLMProviderprotocol interfaceserver.py- FastAPI server with provider routing and API endpointsbedrock.py- AWS Bedrock provider implementingLLMProvideropenai_compatible.py- OpenAI-compatible provider implementingLLMProvider
LLMProvider- Protocol defining provider interface:completion(request)- Get completion from LLMcount_tokens(request)- Count tokens for a request
get_provider_type()- Determine provider from environmentget_provider()- Get provider instance (returnsLLMProvider)call_llm_service(request)- Route requests to providerscount_llm_tokens(request)- Token countingget_provider_info()- Runtime configuration infocreate_message(request)-/v1/messagesendpointcount_tokens(request)-/v1/messages/count_tokensendpointhealth()-/healthendpoint
BedrockProvider- Provider class implementingLLMProvidercompletion(request)- Get completion from AWS Bedrockcount_tokens(request)- Count tokens for Bedrock
call_bedrock_converse(request)- AWS Bedrock API callscount_request_tokens(request)- Bedrock token countingget_bedrock_client()- AWS client setupconvert_to_bedrock_messages()- Claude → Bedrock formatcreate_claude_response()- Bedrock → Claude format
OpenAICompatibleProvider- Provider class implementingLLMProvidercompletion(request)- Get completion from OpenAI-compatible providercount_tokens(request)- Count tokens for OpenAI
call_openai_compatible_chat(request)- OpenAI API callscount_openai_tokens(request)- Token estimationget_openai_compatible_client()- HTTP session setupconvert_to_openai_messages()- Claude → OpenAI formatcreate_claude_response_from_openai()- OpenAI → Claude format