| name | claude-code-litellm-maas |
| description | Configure Claude Code with LiteLLM proxy for Huawei Cloud MaaS models. Covers Node.js/Claude Code installation, venv setup, config generation with Claude model-name aliases, environment variable configuration, startup script, and model switching. |
| license | MIT |
| compatibility | claude-code |
| metadata | {"audience":"infrastructure-engineers","workflow":"claude-code-litellm-maas"} |
Claude Code + LiteLLM Proxy for Huawei Cloud MaaS
Set up Claude Code to use LiteLLM proxy as a gateway to Huawei Cloud ModelArts MaaS models. Claude Code sends requests to LiteLLM (localhost:4000), which forwards them to MaaS using the custom_openai provider.
Key technique: Claude Code validates model names at startup. We register Claude-recognized model names (e.g. claude-3-5-haiku-coding) as aliases in LiteLLM's config.yaml, mapping them to actual MaaS models (e.g. deepseek-v4-flash). Claude Code thinks it's using Claude models; LiteLLM transparently routes to MaaS.
Claude Code → LiteLLM (localhost:4000) → Huawei Cloud MaaS → DeepSeek/GLM models
Rules
- ALWAYS use a Python 3.12 venv — Python 3.14+ breaks
orjson which litellm[proxy] depends on. Use uv to manage the venv if the system Python is too new.
- NEVER hardcode
MAAS_API_KEY in config.yaml — use os.environ/MAAS_API_KEY so the key stays in env vars only.
- NEVER expose
MAAS_API_KEY in logs — LiteLLM logs may contain request details. Keep the key out of shell history by sourcing env scripts instead of passing it inline.
- ALWAYS
unset ANTHROPIC_API_KEY — if this env var is set, Claude Code attempts official Anthropic authentication and ignores ANTHROPIC_AUTH_TOKEN. This is the #1 cause of "Claude Code still tries to login to Anthropic".
- ALWAYS kill existing LiteLLM on port 4000 before starting — stale processes cause "address already in use" errors.
- Use
setsid + disown to daemonize LiteLLM — nohup alone may die when the parent shell exits. setsid creates a new session group so the proxy survives.
- ALWAYS register Claude model-name aliases in config.yaml — Claude Code expects specific model name patterns (
claude-3-5-haiku-*, claude-3-5-sonnet-*, claude-3-opus-*). Without these aliases mapped in LiteLLM, Claude Code will fail at startup or when switching model tiers.
ANTHROPIC_MODEL must match a model_name in config.yaml — this is the model Claude Code uses for main responses. If it doesn't match a LiteLLM entry, requests will fail.
- Health check via
/v1/models with auth header — the /health endpoint returns 500 on some LiteLLM versions even when the proxy is fully functional. /v1/models with Authorization: Bearer <master_key> is the reliable check.
Prerequisites
| Requirement | Details |
|---|
| Node.js 20+ | Required by Claude Code. Install via nodesource setup script if missing. |
| npm | Comes with Node.js. Used to install @anthropic-ai/claude-code. |
| Python 3.12+ | 3.14+ is NOT supported (orjson build fails). Install via uv if needed. |
uv (recommended) or pip | For venv + package management. uv handles Python version downloads automatically. |
MAAS_API_KEY | Huawei Cloud ModelArts API key (create in MaaS console → API Key Management). |
lsof | For port checking in the startup script. Usually pre-installed on Linux. |
curl | For health checks and API testing. Pre-installed on most systems. |
Pre-condition: An ECS instance on Huawei Cloud with EIP (public IP) for accessing MaaS endpoints. Ubuntu 24.04 used as reference OS.
Minimum resources: 512 MB RAM, 200 MB disk (for venv + litellm packages).
Available MaaS Models (July 2026)
Note: DeepSeek-V3.1-Terminus was fully retired on 2026-07-24. DeepSeek-V4-Flash replaces it as the recommended cost-effective model. Source: DeepSeek API changelog.
| Model | MaaS model ID | Context | Max Output | Deep Thinking | Cost Tier |
|---|
| DeepSeek-V4-Flash | deepseek-v4-flash | 1M | 128K | Configurable | $$ |
| DeepSeek-V4-Pro | deepseek-v4-pro | 1M | 128K | Configurable | $$$$ |
| DeepSeek-V3.2 | deepseek-v3.2 | 160K | 32K | Configurable | $ |
| DeepSeek-V3 | DeepSeek-V3 | 128K | 64K | No | $ |
| DeepSeek-R1 | deepseek-r1-250528 | 128K | 32K | Always active | $$$$ |
| GLM-5.2 | glm-5.2 | 198K | 128K | Configurable | $$$ |
| GLM-5.1 | glm-5.1 | 198K | 128K | Configurable | $$$$ |
| GLM-5 | glm-5 | 198K | 64K | Configurable | $$$$ |
Model Name Aliases
Claude Code sends model names it recognizes. We map these to MaaS models in LiteLLM:
Claude Code sees (model_name) | LiteLLM routes to (litellm_params.model) | Env var | Use case |
|---|
claude-3-5-haiku-coding | custom_openai/deepseek-v4-flash | ANTHROPIC_MODEL | Default main model |
claude-3-5-haiku-20241022 | custom_openai/deepseek-v3.2 | ANTHROPIC_SMALL_FAST_MODEL, ANTHROPIC_DEFAULT_HAIKU_MODEL, CLAUDE_CODE_SUBAGENT_MODEL | Background/fast tasks |
claude-3-5-sonnet-20241022 | custom_openai/deepseek-v4-pro | ANTHROPIC_DEFAULT_SONNET_MODEL | Heavy coding |
claude-3-opus-20240229 | custom_openai/deepseek-v4-pro | ANTHROPIC_DEFAULT_OPUS_MODEL | Opus tier |
Additionally, all 8 MaaS models are registered with their real names for direct access via /model switching.
Workflow
Step 1: CHECK PREREQUISITES
Verify Node.js, Python, and check if Claude Code is already installed:
node --version
python3 --version
which claude 2>/dev/null
which uv || which pip3
If system Python is 3.14+, install uv to manage a 3.12 venv:
curl -LsSf https://astral.sh/uv/install.sh | sh
Step 2: INSTALL CLAUDE CODE
Skip this step if claude --version already returns a version number.
2a. Install Node.js 20 (if missing or too old)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
node --version
2b. Install Claude Code
npm install -g @anthropic-ai/claude-code
claude --version
Step 3: CREATE VENV & INSTALL LITELLM
~/.local/bin/uv venv ~/litellm-env --python 3.12
~/.local/bin/uv pip install 'litellm[proxy]' --python ~/litellm-env/bin/python
python3.12 -m venv ~/litellm-env
source ~/litellm-env/bin/activate
pip install 'litellm[proxy]'
Verify:
source ~/litellm-env/bin/activate
python -c "import litellm; print('litellm OK')"
which litellm
Step 4: CREATE CONFIG DIRECTORY & FILES
mkdir -p ~/litellm
4a. ~/litellm/config.yaml
Create the LiteLLM proxy config with Claude model-name aliases + all MaaS models:
model_list:
- model_name: claude-3-5-haiku-coding
litellm_params:
model: custom_openai/deepseek-v4-flash
api_base: https://api-ap-southeast-1.modelarts-maas.com/openai/v1
api_key: os.environ/MAAS_API_KEY
- model_name: claude-3-5-haiku-20241022
litellm_params:
4b. ~/litellm/init_litellm_env.sh
Exports the MaaS API key. Replace the placeholder with your actual key.
#!/bin/bash
export MAAS_API_KEY="<YOUR_MAAS_API_KEY>"
chmod +x ~/litellm/init_litellm_env.sh
4c. ~/litellm/init_claude_env.sh
Sets environment variables so Claude Code talks to the local LiteLLM proxy instead of Anthropic's servers:
#!/bin/bash
export LITELLM_MASTER_KEY="sk-123456"
source ~/litellm/init_litellm_env.sh
export ANTHROPIC_BASE_URL="http://127.0.0.1:4000"
export ANTHROPIC_AUTH_TOKEN="$LITELLM_MASTER_KEY"
export ANTHROPIC_MODEL="claude-3-5-haiku-coding"
export ANTHROPIC_SMALL_FAST_MODEL="claude-3-5-haiku-20241022"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="claude-3-5-haiku-20241022"
export ANTHROPIC_DEFAULT_SONNET_MODEL="claude-3-5-sonnet-20241022"
export ANTHROPIC_DEFAULT_OPUS_MODEL="claude-3-opus-20240229"
export CLAUDE_CODE_SUBAGENT_MODEL="claude-3-5-haiku-20241022"
unset ANTHROPIC_API_KEY
export API_TIMEOUT_MS=600000
export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
chmod +x ~/litellm/init_claude_env.sh
Step 5: CREATE STARTUP SCRIPT
Create ~/start_claude.sh — kills any stale proxy, starts LiteLLM, waits for it to be ready, then launches Claude Code:
#!/bin/bash
set -euo pipefail
LITELLM_DIR="$HOME/litellm"
LITELLM_ENV="$HOME/litellm-env"
LITELLM_PORT=4000
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
info() { echo -e "${GREEN}[INFO]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
PID=$(lsof -ti :$LITELLM_PORT 2>/dev/null || true)
if [[ -n "$PID" ]]; then
warn "Killing LiteLLM on port $LITELLM_PORT (PID: $PID)..."
kill -9 $PID 2>/dev/null || true
sleep 1
PID2=$(lsof -ti :$LITELLM_PORT 2>/dev/null || true)
[[ -n "$PID2" ]] && error "Cannot free port $LITELLM_PORT"
info "Port freed"
else
info "Port free"
info
setsid litellm --config --port \
> 2>&1 &
info
info
i $( 1 30);
curl -sf -H \
>/dev/null 2>&1;
info
1
! curl -sf -H \
>/dev/null 2>&1;
error
info
info
info
info
info
info
info
info
info
info
info
claude
chmod +x ~/start_claude.sh
Step 6: VERIFY
Start the proxy manually and confirm it works:
source ~/litellm-env/bin/activate
source ~/litellm/init_litellm_env.sh
setsid litellm --config ~/litellm/config.yaml --port 4000 > ~/litellm/proxy.log 2>&1 &
disown
curl -s -H "Authorization: Bearer sk-123456" http://127.0.0.1:4000/v1/models | python3 -m json.tool
Expected output: a JSON list with 12 model IDs including claude-3-5-haiku-coding, claude-3-5-haiku-20241022, claude-3-5-sonnet-20241022, claude-3-opus-20240229, and all 8 MaaS model names.
Test an API call:
curl http://localhost:4000/v1/chat/completions \
-H "Authorization: Bearer sk-123456" \
-H "Content-Type: application/json" \
-d '{"model": "claude-3-5-haiku-coding", "messages": [{"role": "user", "content": "Hello"}]}'
Verify Claude Code:
claude --version
Generated Files
~/litellm/
config.yaml # LiteLLM proxy config (4 Claude aliases + 8 MaaS models)
init_litellm_env.sh # Exports MAAS_API_KEY
init_claude_env.sh # Sets ANTHROPIC_* env vars for Claude Code
proxy.log # LiteLLM proxy log (created at runtime)
~/litellm-env/ # Python 3.12 venv with litellm[proxy]
~/start_claude.sh # One-command startup: kill stale -> start proxy -> launch claude
Daily Usage
~/start_claude.sh
source ~/litellm-env/bin/activate
source ~/litellm/init_litellm_env.sh
litellm --config ~/litellm/config.yaml --port 4000
cd ~/yourproject
source ~/litellm/init_claude_env.sh
claude
Switching models in Claude Code
Inside a Claude Code conversation, use the /model command:
/model deepseek-v4-pro
/model deepseek-v4-flash
/model deepseek-v3.2
/model glm-5.2
/model deepseek-r1
/model claude-3-5-sonnet-20241022
After switching, subsequent conversation will use the new model.
Troubleshooting
| Problem | Solution |
|---|
orjson build fails | System Python is 3.14+. Use uv venv --python 3.12 to get a compatible venv. |
| Claude Code still tries to login to Anthropic | ANTHROPIC_API_KEY is set. Run unset ANTHROPIC_API_KEY and ensure ANTHROPIC_AUTH_TOKEN is set correctly. |
| API call timeout | Network connectivity issue. Increase API_TIMEOUT_MS (e.g. export API_TIMEOUT_MS=600000). |
| Authenticate fail | master_key in config.yaml doesn't match ANTHROPIC_AUTH_TOKEN in init_claude_env.sh. Make them consistent. |
| Proxy dies when shell closes | Use setsid + disown instead of nohup alone. |
/health returns 500 | Known LiteLLM quirk. Use /v1/models with auth header for health checks. |
| "address already in use" | Kill stale process: kill $(lsof -ti :4000) |
MAAS_API_KEY not set | Source init_litellm_env.sh before starting the proxy. |
| Model not found | The model_name in the error must exist in config.yaml. Check spelling and that LiteLLM is running with the correct config file. |
pip install failure | Python env conflict. Use venv: python3.12 -m venv ~/litellm-env && source ~/litellm-env/bin/activate |
tail -f ~/litellm/proxy.log
curl -s -H "Authorization: Bearer sk-123456" http://127.0.0.1:4000/v1/models
curl -s -H "Authorization: Bearer sk-123456" http://127.0.0.1:4000/v1/models | python3 -m json.tool
kill $(lsof -ti :4000)
~/start_claude.sh
FAQ
Q: Why do we need Claude model-name aliases?
A: Claude Code validates model names at startup and when switching model tiers (haiku/sonnet/opus). By registering aliases like claude-3-5-haiku-coding in LiteLLM's config.yaml, Claude Code accepts the model name while LiteLLM transparently routes the request to the actual MaaS model.
Q: Can I use MaaS model names directly without aliases?
A: Yes, for the /model switch command (e.g. /model deepseek-v4-pro). However, the default model in ANTHROPIC_MODEL should use a Claude-recognized name to avoid startup issues. The env vars ANTHROPIC_DEFAULT_HAIKU_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, and ANTHROPIC_DEFAULT_OPUS_MODEL must also point to names that exist in config.yaml.
Q: What happened to DeepSeek-V3.1-Terminus?
A: It was fully retired on 2026-07-24. DeepSeek-V4-Flash replaces it as the recommended cost-effective model. The DeepSeek API changelog states: "deepseek-chat & deepseek-reasoner will be fully retired and inaccessible after Jul 24th, 2026."
Q: Why is unset ANTHROPIC_API_KEY critical?
A: If ANTHROPIC_API_KEY is set in the environment (e.g. from a previous Anthropic setup), Claude Code will attempt to authenticate with Anthropic's servers and ignore ANTHROPIC_AUTH_TOKEN. Unsetting it forces Claude Code to use the auth token against the proxy.
Q: What is drop_params: true?
A: MaaS doesn't support all OpenAI/Anthropic parameters. drop_params: true tells LiteLLM to automatically drop unsupported parameters instead of returning an error.