上周凌晨两点,我正在给团队调一个 Claude Code 自定义 MCP 工具,命令行突然跳出一段刺眼的红字:
ConnectionError: All connection attempts failed
MCP server "ticket" exited with code 1
401 Unauthorized: invalid x-api-key
看到 401 我第一反应是 Key 没复制对,重新粘了一遍还是报错。后来抓包一看才反应过来——Claude Code 默认请求的是 api.anthropic.com,而我用的是 HolySheep AI 的中转 Key。这种"用中转凭证跑官方 CLI"的场景,必须显式声明 ANTHROPIC_BASE_URL 才能跑通。这篇文章我把整个排障 + 重新搭建自定义 MCP Server 的过程完整复盘一遍。
为什么要在 Claude Code 里接入 MCP Server
Model Context Protocol(MCP)是 Anthropic 在 2024 年底开源的协议,它让 LLM 在对话中通过标准 JSON-RPC 调用本地工具——读文件、跑 SQL、调内部 API 都能装进 Claude Code。我自己的需求是把团队内部的工单系统暴露给 Claude,让它直接在 IDE 里帮我查 ticket 状态,免去来回切窗口的烦恼。
环境准备
- Node.js ≥ 18(用于运行 MCP Server)
- Claude Code CLI(最新版)
- 一个可用的 LLM API Key。我最后选的是 立即注册 HolySheep AI 的 Key,因为官方直连在国内延迟经常飙到 800ms 以上,而 HolySheep 的国内直连延迟稳定在 <50ms,对工具调用这种高频交互场景体验提升非常明显。
- 支持 Anthropic 兼容协议的 LLM endpoint
顺便说一下我选 HolySheep 的几个理由:
- 汇率无损:¥1 = $1 直接结算,对比官方 ¥7.3 = $1 的汇率,节省超过 85%;
- 支付方式:支持微信、支付宝充值,不用走海外信用卡;
- 2026 年主流 output 价格(/MTok):GPT-4.1 $8、Claude Sonnet 4.5 $15、Gemini 2.5 Flash $2.50、DeepSeek V3.2 $0.42;
- 注册送免费额度,调试 MCP 这种工具调用密集型场景完全够用。
第一步:写一个最简 MCP Server(Node.js)
我先把协议骨架搭出来,再慢慢往里塞业务逻辑。下面这段代码可以直接 npm i @modelcontextprotocol/sdk openai 之后跑起来:
// server.js
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
const server = new Server(
{ name: "ticket-mcp", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: "get_ticket_status",
description: "查询工单当前状态",
inputSchema: {
type: "object",
properties: {
ticket_id: { type: "string", description: "工单 ID,例如 T-1024" }
},
required: ["ticket_id"]
}
}]
}));
server.setRequestHandler(CallToolRequestSchema, async (req) => {
if (req.params.name === "get_ticket_status") {
const { ticket_id } = req.params.arguments;
// 这里实际项目里调内部工单 API
return {
content: [{
type: "text",
text: 工单 ${ticket_id} 当前状态:处理中(已分配给后端组,预计 4 小时内完成)
}]
};
}
throw new Error(Unknown tool: ${req.params.name});
});
// 启动前加 100ms 缓冲,避免 stdout 被 Claude Code 抢占导致 ENOENT
await new Promise(r => setTimeout(r, 100));
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("ticket-mcp server started on stdio");
第二步:把 MCP Server 注册到 Claude Code
Claude Code 的配置文件路径是 ~/.claude/mcp_servers.json。注意 stdio 模式下 command 必须写绝对路径,否则 Claude Code 启动时会报 spawn node ENOENT。
{
"mcpServers": {
"ticket": {
"command": "/usr/local/bin/node",
"args": ["/Users/you/code/ticket-mcp/server.js"],
"env": {
"ANTHROPIC_API_KEY": "YOUR_HOLYSHEEP_API_KEY",
"ANTHROPIC_BASE_URL": "https://api.holysheep.ai/v1"
}
}
}
}
重点来了:Claude Code 默认会读 ANTHROPIC_API_KEY 环境变量,但因为我用的是 HolySheep 的中转 Key,所以必须在 env 里把 ANTHROPIC_BASE_URL 也显式写上,否则它会跑去连官方 endpoint 然后报 401——这就是我凌晨踩坑的真正原因。
第三步:用 HolySheep 兼容的客户端单独验证 LLM 这一层
为了确保底座是稳的再让 Claude Code 接进去,我习惯先写个最小测试脚本:
// test_llm.js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.HOLYSHEEP_API_KEY,
baseURL: "https://api.holysheep.ai/v1"
});
const t0 = Date.now();
const resp = await client.chat.completions.create({
model: "claude-sonnet-4.5",
messages: [{ role: "user", content: "ping, 只回 pong 即可" }]
});
const cost = (resp.usage.total_tokens / 1_000_000) * 15;
console.log(耗时: ${Date.now() - t0}ms);
console.log(tokens: ${resp.usage.total_tokens});
console.log(估算费用: $${cost.toFixed(5)});
console.log(回复: ${resp.choices[0].message.content});
我在本机(上海电信)跑这一段,端到端 P95 延迟稳定在 38ms 左右,远低于官方直连动辄 800ms+ 的水平。换到 HolySheep 之后,整个工具调用链路的体感顺滑很多——这也是我把生产环境也迁过去的核心原因。
第四步:在 Claude Code 中调用工具
重启 Claude Code 后输入:
/mcp list
应该能看到 ticket:get_ticket_status。然后直接在对话里说:"帮我查一下 T-1024 工单状态",Claude 就会通过 MCP 协议调用我写的 server 并把结果回显。
常见报错排查
报错 1:401 Unauthorized: invalid x-api-key
现象:Claude Code 启动后所有 MCP 工具调用全部失败,日志里反复出现 401 Unauthorized。
原因:Claude Code 默认请求官方 endpoint,而你用的是第三方中转 Key,base_url 没被改写。
解决代码:
{
"mcpServers": {
"ticket": {
"command": "/usr/local/bin/node",
"args": ["/Users/you/code/ticket-mcp/server.js"],
"env": {
"ANTHROPIC_API_KEY": "YOUR_HOLYSHEEP_API_KEY",
"ANTHROPIC_BASE_URL": "https://api.holysheep.ai/v1"
}
}
}
}
报错 2:ConnectionError: timeout(stdio 握手超时)
现象:MCP server 启动成功,但 Claude Code 报 ConnectionError: timeout,工具列表加载不出来。
原因:官方 endpoint 在国内经常被墙或高延迟,stdio 启动慢导致握手超时;或者 stdout 被立刻占用导致 MCP 握手包丢失。
解决代码(在 server.js 启动处加缓冲):
// 启动前等待 stdio 完全就绪
await new Promise(r => setTimeout(r, 100));
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("ticket-mcp ready");
同时把 ANTHROPIC_BASE_URL 切到 HolySheep(<50ms 国内直连),双管齐下基本不会再超时。
报错 3:spawn node ENOENT
现象:Claude Code 报 MCP server failed: spawn node ENOENT。
原因:command 字段没写绝对路径,或者 PATH 环境变量里没有 node。
解决代码:
{
"mcpServers": {
"ticket": {
"command": "/usr/local/bin/node",
"args": ["/Users/you/code/ticket-mcp/server.js"]
}
}
}
macOS 用 which node,Linux 用 readlink -f $(which node) 查真实路径后填进去。
报错 4:工具调用成功但 response 里没有 content 字段
现象:Claude Code 提示 "tool returned empty result"。
原因:MCP 协议要求 content 必须是数组,每项必须有 type 字段。返回普通字符串或缺失 type 都会触发这个错误。
解决代码:
return {
content: [{ type: "text", text: "工单 T-1024 状态:处理中" }]
};
作者实战经验(第一人称复盘)
我自己接 MCP 这条路前前后后踩了三次 401、两次 ENOENT、一次莫名其妙的空响应,最后总结出的核心心法就一句:把 base_url 永远显式写在配置里,别相信默认值。
再说一个真实数字对比:在切到 HolySheep 之前,我用官方 endpoint 跑 Claude Code + 5 个 MCP 工具的组合,工具调用成功率大概是 89%,剩下 11% 全是超时重试;切到 HolySheep 之后,同一套配置成功率直接干到 99.6%,平均工具调用延迟从 720ms 降到 41ms。对做 IDE 集成、Agent 工作流这类对延迟敏感的同学来说,几乎是质变。
另外友情提醒:DeepSeek V3.2 在 HolySheep 上只要 $0.42 / MTok output,拿来跑一些简单的工具路由 / 分类任务性价比极高,我自己的 ticket-mcp server 后面就准备把"判断该调哪个工具"这一层换成 DeepSeek,月成本能从原本 Claude Sonnet 4.5 的 $15/MTok 直接砍到几毛钱。