A lightweight translation server that makes Ollama compatible with Claude Code by translating between the Anthropic Messages API and Ollama's chat API format.
Claude Code is designed to communicate with the Anthropic API. This shim allows Claude Code to use Ollama (running locally with models like MiniMax M2) by:
- Accepting Anthropic-style API requests from Claude Code
- Translating them to Ollama's chat API format
- Forwarding requests to your local Ollama instance
- Translating Ollama's responses back to Anthropic format
- Returning them to Claude Code
Flow: Claude Code → Shim (localhost:4001) → Ollama (localhost:11434) → Shim → Claude Code
- ✅ Full Anthropic Messages API v1 compatibility
- ✅ Streaming support with Server-Sent Events (SSE)
- ✅ Tool calling (function calling) support with automatic format conversion
- ✅ Automatic request/response translation
- ✅ Handles text content, system messages, and multi-turn conversations
- ✅ Configurable via environment variables
- ✅ Docker-based deployment
- ✅ Health check endpoint
- ✅ Comprehensive logging with request/response truncation
- ✅ MiniMax M2 reasoning model support (thinking field handling)
While this shim works perfectly for simple requests, Claude Code sends very large requests (18KB+ system prompts, 20+ tool definitions, multi-turn conversation history) that frequently exceed Ollama Cloud's free tier limits, resulting in 500 Internal Server Error responses.
Recommendations:
- ✅ Use local Ollama - Install Ollama locally for unlimited usage with models like llama3.1, qwen2.5, or mistral
- ⏳ Wait for paid tier - Ollama Cloud paid plans (after free trial) may have higher limits
⚠️ Free tier works for: Simple API calls, testing, small conversations- ❌ Free tier struggles with: Claude Code's full feature set (agents, tools, long contexts)
Testing: The shim itself is production-ready - the limitation is upstream in Ollama Cloud's free tier capacity.
Before you begin, ensure you have:
-
Docker and Docker Compose installed
-
Ollama installed and running on macOS
-
MiniMax M2 model pulled in Ollama:
ollama pull minimax-m2:cloud
# macOS
brew install ollama
# Or download from https://ollama.ai/downloadollama serveOllama will start on http://localhost:11434 by default.
ollama pull minimax-m2:cloud# List available models
curl http://localhost:11434/api/tags
# Test chat endpoint
curl -X POST http://localhost:11434/api/chat \
-H "Content-Type: application/json" \
-d '{
"model": "minimax-m2:cloud",
"messages": [{"role": "user", "content": "Say hello"}],
"stream": false
}'You should see a response with the model's output.
-
Create a
.envfile (required for Ollama Cloud models):cp .env.example .env
Edit
.envand add your Ollama API key:OLLAMA_API_KEY=your-api-key-here
Get your API key from https://ollama.com/settings/keys
-
Start the shim:
./up.sh
-
View logs:
docker compose logs -f shim
-
Stop the shim:
./down.sh
-
Rebuild from scratch:
./rebuild.sh
You can customize the shim's behavior using environment variables:
| Variable | Default | Description |
|---|---|---|
OLLAMA_BASE_URL |
http://host.docker.internal:11434 |
Ollama API endpoint (from inside Docker) |
OLLAMA_MODEL |
minimax-m2:cloud |
Model to use (ignores model in requests) |
OLLAMA_API_KEY |
(none) | Required for cloud models - Get from ollama.com/settings/keys |
SHIM_PORT |
4001 |
Port for the shim to listen on |
LOG_LEVEL |
info |
Logging level (debug, info, warning, error) |
Example: Run on a different port with debug logging:
export SHIM_PORT=5000
export LOG_LEVEL=debug
./up.shTo make Claude Code use this shim instead of the Anthropic API:
Open or create ~/.claude/settings.json:
{
"env": {
"ANTHROPIC_BASE_URL": "http://localhost:4001",
"ANTHROPIC_AUTH_TOKEN": "not-used",
"ANTHROPIC_MODEL": "minimax-m2:cloud",
"ANTHROPIC_SMALL_FAST_MODEL": "minimax-m2:cloud",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "minimax-m2:cloud",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "minimax-m2:cloud",
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "minimax-m2:cloud"
}
}Note: The ANTHROPIC_AUTH_TOKEN value doesn't matter - the shim doesn't validate it. You can use any string.
After updating the settings file, restart Claude Code for the changes to take effect.
Start a conversation in Claude Code. You should see:
- Responses from the MiniMax M2 model
- Request logs in the shim container:
docker compose logs -f shim
curl http://localhost:4001/healthExpected response:
{"ok": true}curl -X POST http://localhost:4001/v1/messages \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"messages": [
{"role": "user", "content": "Write a short Python function that returns the nth Fibonacci number."}
],
"max_tokens": 256,
"temperature": 0.2,
"top_p": 0.95,
"stream": false
}'Expected response (Anthropic format):
{
"id": "msg_abc123...",
"type": "message",
"model": "minimax-m2:cloud",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Here's a Python function...[code]..."
}
],
"stop_reason": "end_turn",
"stop_sequence": null,
"usage": {
"input_tokens": 0,
"output_tokens": 0
}
}Note: Token counts are placeholders (Ollama doesn't provide them).
curl -X POST http://localhost:4001/v1/messages \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"messages": [{"role": "user", "content": "Count from 1 to 5"}],
"max_tokens": 100,
"stream": true
}'Expected response (Server-Sent Events stream):
event: message_start
data: {"type":"message_start","message":{"id":"msg_...","type":"message","role":"assistant","content":[],"model":"minimax-m2:cloud","stop_reason":null,"usage":{"input_tokens":0,"output_tokens":0}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"1"}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" 2 3 4 5"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":0}}
event: message_stop
data: {"type":"message_stop"}
curl -X POST http://localhost:4001/v1/messages \
-H "Content-Type: application/json" \
-d '{
"model": "claude-3-5-sonnet",
"messages": [
{"role": "user", "content": "What is the weather in San Francisco?"}
],
"max_tokens": 256,
"stream": false,
"tools": [
{
"name": "get_weather",
"description": "Get the current weather in a location",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The city and state"}
},
"required": ["location"]
}
}
]
}'Expected response includes tool_use blocks when the model decides to use tools:
{
"id": "msg_...",
"type": "message",
"role": "assistant",
"content": [
{"type": "text", "text": "I'll check the weather for you."},
{
"type": "tool_use",
"id": "toolu_...",
"name": "get_weather",
"input": {"location": "San Francisco, CA"}
}
],
"stop_reason": "tool_use",
"model": "minimax-m2:cloud",
"usage": {"input_tokens": 0, "output_tokens": 0}
}- Ensure the shim is running:
./up.sh - Configure
~/.claude/settings.json(see above) - Restart Claude Code
- Send a message: "Write a hello world function in Python"
- Check shim logs:
docker compose logs -f shim
You should see request/response logs and get a response from MiniMax M2.
┌─────────────┐ POST /v1/messages ┌──────────┐
│ Claude Code │ ──────────────────────────> │ Shim │
│ (Client) │ (Anthropic format) │ :4001 │
└─────────────┘ └──────────┘
│
│ POST /api/chat
│ (Ollama format)
↓
┌──────────┐
│ Ollama │
│ :11434 │
└──────────┘
| Endpoint | Method | Description |
|---|---|---|
/health |
GET | Health check - returns {"ok": true} |
/v1/messages |
POST | Anthropic Messages API endpoint |
Request transformation:
model→ Ignored, always usesOLLAMA_MODELmessages→ Extracted text from content blocks, tool_result → tool messagesmax_tokens→options.num_predict(defaults to 4096 for reasoning models if not specified)temperature→options.temperaturetop_p→options.top_ptools→ Ollama tools format (input_schema → parameters)
Response transformation:
- Ollama
message.content→ Anthropiccontent[0].text - Ollama
message.thinking→ Anthropic content text (fallback for reasoning models like MiniMax M2) - Ollama
tool_calls→ Anthropictool_useblocks - Generates fake UUID for message
idand tool use IDs - Adds required Anthropic fields:
type,stop_reason,usage - Sets
stop_reason: "tool_use"when tools are called, otherwise"end_turn"
Problem: Logs show connection errors to Ollama
Solutions:
- Verify Ollama is running:
curl http://localhost:11434/api/tags - On macOS, Docker uses
host.docker.internalto reach the host - Try alternative: Set
OLLAMA_BASE_URL=http://docker.for.mac.localhost:11434
Problem: Port 4001 is already in use
Solution: Change the port:
export SHIM_PORT=5000
./rebuild.shDon't forget to update ~/.claude/settings.json with the new port.
Problem: "model 'minimax-m2:cloud' not found"
Solution:
# List available models
ollama list
# Pull the model
ollama pull minimax-m2:cloud# Follow logs in real-time
docker compose logs -f shim
# View last 100 lines
docker compose logs --tail=100 shim# List running containers
docker compose ps
# Restart container
docker compose restart shimProblem: Claude Code still uses Anthropic API
Solutions:
- Verify
~/.claude/settings.jsonhas correctANTHROPIC_BASE_URL - Restart Claude Code completely
- Check shim is running:
curl http://localhost:4001/health - Check shim logs for incoming requests
- Token counts are placeholders: Always returns 0 (Ollama doesn't provide them)
- Model name mapping: All model names map to
OLLAMA_MODELenv var - macOS/Docker only: Uses
host.docker.internalfor Mac Docker Desktop - Ollama Cloud request size limits: Very large requests (18KB+ system prompts, 20+ tools) may cause 500 errors from Ollama Cloud
- Add actual token counting (estimate from text length)
- Support multiple model mappings (map claude-opus → llama3, etc.)
- Add request caching
- Add metrics and monitoring endpoints
- Add rate limiting
- Improve error handling for Ollama Cloud request size limits
.
├── src/
│ └── server.py # Main FastAPI server
├── requirements.txt # Python dependencies
├── Dockerfile # Container definition
├── docker-compose.yml # Docker Compose config
├── .dockerignore # Docker build exclusions
├── .env.example # Environment variables template
├── .gitignore # Git exclusions
├── up.sh # Start the shim
├── down.sh # Stop the shim
├── rebuild.sh # Rebuild from scratch
└── README.md # This file
This is a production-ready implementation with full streaming and tool calling support. Contributions welcome for:
- Additional model mappings
- Better error handling for Ollama Cloud limits
- Token count estimation
- Request caching
- Tests
- Rate limiting
MIT License - feel free to modify and distribute.
For issues or questions:
- Check the troubleshooting section above
- Review logs:
docker compose logs -f shim - Verify Ollama is working independently
- Check Claude Code settings configuration
Happy coding with local models! 🚀