伪装成 Claude Code CLI 客户端,从只对 Agent 开放的 Coding Plan API 骗取服务。
核心问题:部分 Coding Plan API 只对 Claude Code 等 Coding Agent 开放,普通 API 调用直接拒绝服务。本代理将请求头伪装为 Claude Code CLI,骗过上游的身份检查,让任何 OpenAI 格式客户端都能接入。
- Claude Code 伪装 — 请求头 1:1 模拟 Claude Code CLI(User-Agent、anthropic-version、anthropic-beta),骗过上游的 Agent 身份校验,拿到服务资格
- 自动模型发现 — 启动时从上游拉取可用模型列表,
/v1/models自动同步,无需手动配置 - 零过滤透传 — 请求中的
model字段不做任何校验,爱传啥传啥,上游自行裁决 - OpenAI 格式兼容 — 实现
/v1/chat/completions和/v1/models端点,所有 OpenAI SDK 客户端开箱即用 - 双向格式转换 — 请求/响应自动转换,覆盖 tool calls、tool results、图片输入等复杂场景
- 流式输出 — SSE 流式响应,逐 token 实时返回
- 可选鉴权 — 支持为代理设置 API Key,防止蹭网
- System Prompt 注入 — 可选在无 system 消息时自动注入默认 system prompt
- 手上有 OpenAI 兼容客户端,但上游 Coding Plan API 只认 Claude Code / Agent 请求,普通调用直接拒
- 需要 prompt caching、computer-use 等 Claude Code 独占特性,但不想用 Claude Code CLI 的开发者
- Python 3.10+
- Anthropic API Key(获取地址)
- 克隆仓库:
git clone https://github.com/lvysssss/codingplanproxy.git
cd codingplanproxy- 安装依赖:
pip install -r requirements.txt依赖列表:
| 包 | 最低版本 | 用途 |
|---|---|---|
| fastapi | 0.110.0 | Web 框架 |
| uvicorn | 0.29.0 | ASGI 服务器 |
| httpx | 0.27.0 | 异步 HTTP 客户端 |
| python-dotenv | 1.0.0 | .env 文件加载 |
- 配置环境变量:
cp .env.example .env编辑 .env 文件,填入你的 Anthropic API Key(必填),其余按需配置。
- 启动服务:
python main.pyWindows 用户可双击 startcodingplanproxy.bat 一键启动。
启动成功后会看到:
代理启动 → 上游: https://api.anthropic.com, 端口: 8000
所有配置通过项目根目录的 .env 文件管理,参考 .env.example:
| 变量 | 必填 | 默认值 | 说明 |
|---|---|---|---|
BASE_URL |
否 | https://api.anthropic.com |
Anthropic API 上游地址,可改为中转地址 |
API_KEY |
是 | — | 上游 API Key |
AVAILABLE_MODELS |
否 | (空) | 本地补充模型列表(逗号分隔),留空则仅使用远端自动获取的模型;上游不展示但实际可用的模型可手动在此添加 |
DEFAULT_MAX_TOKENS |
否 | 16384 |
默认最大输出 token 数 |
SYSTEM_PROMPT |
否 | (空) | 注入的默认 system prompt,仅在请求无 system 消息时生效 |
PORT |
否 | 8000 |
代理服务监听端口 |
PROXY_API_KEY |
否 | (空) | 客户端调用代理时需提供的 API Key,留空则不校验 |
最简配置(只需 API Key):
API_KEY=sk-ant-api03-your-key-here完整配置:
BASE_URL=https://api.anthropic.com
API_KEY=sk-ant-api03-your-key-here
AVAILABLE_MODELS=
DEFAULT_MAX_TOKENS=16384
SYSTEM_PROMPT=You are a helpful assistant.
PORT=8000
PROXY_API_KEY=my-proxy-secret代理启动后,所有端点兼容 OpenAI API 格式。将你的 OpenAI 客户端 base_url 指向代理地址即可:
http://localhost:8000/v1
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /v1/chat/completions |
核心端点,聊天补全 |
| GET | /v1/models |
返回可用模型列表 |
| GET | /health |
健康检查,含模型来源统计 |
服务启动时自动从上游 GET /v1/models(Anthropic 格式)获取可用模型列表,与本地 AVAILABLE_MODELS 合并后通过 /v1/models(OpenAI 格式)暴露。上游模型列表缓存 30 分钟,过期自动刷新。
# 查看当前可用模型(本地 + 远端合并)
curl http://localhost:8000/v1/models
# 查看模型来源统计
curl http://localhost:8000/health
# → {"local_models": 0, "remote_models": 120, ...}如果上游不支持 Models API,remote_models 为 0,仅使用本地 AVAILABLE_MODELS 配置。
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": "Hello, who are you?"}
]
}'添加 "stream": true 即可启用 SSE 流式响应:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": "Write a poem about coding"}
],
"stream": true
}'curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "system", "content": "You are a Python expert."},
{"role": "user", "content": "How to read a file in Python?"}
]
}'curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": "What is the weather in Beijing?"}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"}
},
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}'返回 tool_calls 后,提交 tool 结果继续对话:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": "What is the weather in Beijing?"},
{"role": "assistant", "content": null, "tool_calls": [
{"id": "call_abc123", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\": \"Beijing\"}"}}
]},
{"role": "tool", "tool_call_id": "call_abc123", "content": "25°C, sunny"}
]
}'curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": [
{"type": "text", "text": "What is in this image?"},
{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQ..."}}
]}
]
}'配置 PROXY_API_KEY 后,请求需携带 Authorization 头:
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer my-proxy-secret" \
-d '{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": "Hello"}
]
}'from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="any-string" # 未配置 PROXY_API_KEY 时可填任意值
)
response = client.chat.completions.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": "Hello!"}],
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")import OpenAI from "openai";
const client = new OpenAI({
baseURL: "http://localhost:8000/v1",
apiKey: "any-string",
});
const stream = await client.chat.completions.create({
model: "claude-sonnet-4-20250514",
messages: [{ role: "user", content: "Hello!" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || "");
}请求中的 model 字段不作任何校验,任意值均直接透传上游。使用 /v1/models 端点可查看当前可用模型列表(本地配置 + 远端自动获取)。
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "any-model-you-want",
"messages": [
{"role": "user", "content": "Hello"}
]
}'| OpenAI 参数 | Anthropic 映射 | 说明 |
|---|---|---|
model |
model |
模型名称,支持动态选择 |
messages |
messages + system |
system 消息提取为顶层字段 |
max_tokens / max_completion_tokens |
max_tokens |
最大输出 token |
temperature |
temperature |
采样温度 |
top_p |
top_p |
核采样 |
stream |
stream |
是否流式输出 |
stop |
stop_sequences |
停止序列,字符串或数组 |
tools |
tools |
工具定义,function → input_schema |
tool_choice |
tool_choice |
auto/required/none/function |
确认 .env 文件存在于项目根目录,且 API_KEY 已填入有效值:
- 如果配置了
PROXY_API_KEY,请求需携带Authorization: Bearer <your-proxy-key>头 - 如果未配置
PROXY_API_KEY仍返回 401,确认请求头格式正确
- 检查
BASE_URL是否正确,网络是否能访问上游 API - 如果在国内,可能需要设置 HTTP 代理或使用中转地址:
BASE_URL=https://your-proxy-domain.com可能原因及解决:
- 上游不支持 Models API(
/health中remote_models: 0)→ 手动在AVAILABLE_MODELS中添加模型 - 上游鉴权失败 → 检查
API_KEY是否正确 - 网络不通 → 检查
BASE_URL和网络
上游连接断开时,流式响应可能缺少 [DONE] 标记。检查:
- 网络稳定性
- 上游 API 是否超时(代理默认 300 秒超时)
- 是否触发了 Anthropic API 的速率限制
- 确认
tools参数格式符合 OpenAI 规范(type: "function"+function对象) tool_choice值none在 Anthropic 中无直接对应,代理映射为auto- 返回的 tool result 消息需使用
role: "tool"并携带tool_call_id
claudecode_headers.py 中硬编码了 Claude Code 的请求头版本。如果 Anthropic 更新了 API,可能需要手动更新以下常量:
CLAUDE_CODE_USER_AGENT = "claude-code/x.x.xxx (node/xx.x.x; darwin; amd64)"
ANTHROPIC_VERSION = "2026-04-14"
ANTHROPIC_BETA = "prompt-caching-2026-04-14,computer-use-2026-04-14"欢迎贡献!请遵循以下流程:
- Fork 本仓库
- 创建功能分支:
git checkout -b feature/your-feature - 提交改动:
git commit -m "feat: description of your change" - 推送分支:
git push origin feature/your-feature - 创建 Pull Request
提交信息请使用 Conventional Commits 格式:
feat:新功能fix:修复refactor:重构docs:文档chore:杂项
MIT