一个零依赖的 Node.js MCP Server 从开发到上线的完整复盘:JSON-RPC 协议细节、魔搭托管部署检测规则,以及一个让我排查了整整半天的
id=0玄学 Bug。

背景:为什么要把新媒体数据做成 MCP
做新媒体相关工具的人应该都有同感:抖音热点、小红书爆款、公众号 10w+、微博热搜、B 站榜单……数据分散在七八个平台,每个平台一套 API,对接成本极高。
更麻烦的是,这些数据大多是实时变化的——大模型训练数据里根本没有。想给 AI 应用接上"实时数据能力",就得走 MCP(Model Context Protocol):让 AI 客户端通过标准协议调用外部数据服务。
我们的目标很直接:把红狐数据(redfox.hk)覆盖全平台的新媒体数据能力,打包成一个 MCP Server,让任何支持 MCP 的客户端(Qoder / Claude / Cursor 等)开箱即用。
最终交付:91 个工具,9 大平台,包体 38KB,零依赖。
一、技术选型:为什么用纯 Node.js
第一版方案考虑过 Python(FastMCP),最终选了纯 Node.js,理由很实际:
- 零依赖:Node 18+ 内置
fetch、AbortController,不需要任何第三方库,npm install都省了; - 包体小:38KB 的包,
npx秒下,魔搭这类托管平台的部署检测不容易超时; - 路径友好:
__dirname相对路径引用资源,打包后行为一致。
架构很简单,三层:
MCP 客户端 ──JSON-RPC/stdio──> server.js ──> build-tools.js 工具注册表
│
▼
redfox-client.js(X-API-KEY 双发)
│
▼
https://redfox.hk/story/api/...目录结构:
redfox-mcp/
├── server.js # MCP stdio 服务器入口(JSON-RPC 2.0)
├── tools-manifest.json # 工具清单(API 端点/参数自动提取)
├── src/
│ ├── redfox-client.js # API 统一客户端(双请求头、错误处理、任务轮询)
│ ├── build-tools.js # 工具构建器(合并 manifest + overrides)
│ └── overrides.js # 人工校正层(精确参数映射、异步任务定义)几个实现细节值得说:
① 双请求头兼容:仓库里不同脚本用的请求头不一样(X-API-KEY / REDFOX_API_KEY),统一双发,少踩很多坑:
const headers = {
'Content-Type': 'application/json',
'X-API-KEY': apiKey,
'REDFOX_API_KEY': apiKey,
};② 异步任务自动轮询:视频生成、评论拉取这类耗时任务,提交后拿 taskId,自动轮询直到结果就绪,对客户端完全透明:
const taskId = dig(submitResp, taskIdField) || dig(submitResp, 'data.taskId');
for (let i = 0; i < maxAttempts; i++) {
await sleep(intervalMs);
const r = await request({ url: resultUrl.replace('{taskId}', encodeURIComponent(taskId)), ... });
if (status === 'success' || status === 'done') return { taskId, result: r };
}③ 日志走 stderr:MCP stdio 协议下 stdout 只能输出 JSON-RPC 消息,任何 console.log 都会污染协议通道。所有启动日志一律 console.error。
二、发布 npm:被 2FA 卡住的发布流程
包打包没问题(files 白名单 + npm pack --dry-run 验证),真正卡住的是发布本身。
现在的 npm 强制策略:浏览器登录的 session 不能用于 publish,必须 2FA 或带 Bypass 2FA 的 Granular Token。要么:
- 开启两步验证,
npm publish时输入 OTP; - 或者生成 Granular Token(勾选 Read and write + All packages + Bypass two-factor authentication)。
小提示:publish 前一定要先
npm pack --dry-run检查 files 白名单,确认src/、tools-manifest.json等运行时文件都进去了。我们发布前用临时目录npm install后 spawn 实测过tools/list,91 个工具全部返回。
三、托管魔搭:部署检测的三条硬规则
魔搭 MCP 广场支持托管部署,检测流程是:下载包 → 启动进程 → 调 tools/list,成功即通过。三条硬规则踩过才知道:
规则 1:command 只认 npx / uvx
写 sh 或其他命令直接拒。正确姿势:
{
"mcpServers": {
"redfox-mcp-server": {
"command": "npx",
"args": ["-y", "redfox-mcp-server@latest"],
"env": { "REDFOX_API_KEY": "your-api-key-here" }
}
}
}规则 2:JSON 不允许注释和尾逗号,env 值不能为空字符串
环境变量留空也会被拒,先填占位值,用户连接时再填自己的 Key。
规则 3:tools/list 不能依赖外部凭证
部署检测阶段只调 tools/list,不填 API Key。所以工具列表必须静态注册,不能在检测时去请求外部服务。
四、踩坑合集:那个让我排查半天的 Bug
坑 1:JSON-RPC id=0 被当成"通知"丢弃(最致命)
这是本文含金量最高的一个坑。
MCP 客户端的 initialize 请求 id 通常是 0。而服务器代码判断"是否为通知(无需响应)"时写的是:
if (!id) {
return null; // 忽略
}!0 === true,于是 id=0 的 initialize 请求被当成通知静默丢弃了。 客户端永远等不到握手响应 → 超时 → 部署检测失败。而本地测试"通过"是因为测试脚本只校验了 id=1 的 tools/list 响应,根本没检查 id=0 的 initialize 有没有回包——这个 Bug 被完美掩盖了。
修复:
// 通知类(无响应):仅当 id 为 undefined/null 时才是通知
if (id === undefined || id === null) {
return null;
}教训:JSON-RPC 的 id 可以是 0,判断通知必须用严格相等,不能用 falsy 判断。
坑 2:initialize 协议版本要回显
一开始写死 protocolVersion: '2024-11-05',但新版 MCP SDK 可能请求 2025-06-18 / 2025-03-26。稳妥做法是回显客户端请求的版本(在自己支持列表内):
const SUPPORTED_PROTOCOLS = ['2025-06-18', '2025-03-26', '2024-11-05', '2024-10-07'];
const negotiated = SUPPORTED_PROTOCOLS.includes(requested) ? requested : PROTOCOL_VERSION;
return respond(id, { protocolVersion: negotiated, ... });坑 3:本地测试通过 ≠ 平台检测通过
本地冒烟测试脚本一定要同时断言 initialize 和 tools/list 两条响应,只测其中一个很容易被"响应正常"的假象骗过去——坑 1 就是这么溜掉的。
五、上线效果
服务托管魔搭后,用户只需在服务页填自己的 API Key,拿到专属 Remote URL,填入任意 MCP 客户端即可使用全部 91 个工具:
https://mcp-<uuid>.api-inference.modelscope.cn/sse能力覆盖(节选):
| 能力域 | 工具 |
|---|---|
| 抖音(12) | douyin-search, douyin-hot-trend, douyin-daily-hot, douyin-top-account, douyin-account-diagnosis |
| 小红书(16) | xiaohongshu-search, xiaohongshu-title-score, xiaohongshu-account-analyzer, xiaohongshu-dailytop |
| 公众号(14) | wechat-10w-hot, wechat-original-hot, wechat-search, wechat-title |
| B站/快手/微博 | bilibili-keywords-search, kuaishou-search, weibo-hot-search, 评论/下载类工具 |
| 多平台 | cn-last30days 近30天跨平台爆款聚合, trending-hub, stock-feed |
| AI 搜索 | deepseek-websearch, doubao-websearch, kimi-websearch |
总结
- 纯 Node.js 零依赖实现 MCP Server,38KB 包体,91 个工具覆盖 9 大平台;
- 发布 npm 需 2FA 或带 Bypass 的 Granular Token;
- 魔搭托管检测:command 只认 npx/uvx、JSON 无注释、tools/list 静态注册;
- 最大坑:JSON-RPC id=0 的 falsy 判断陷阱,判断通知必须用
id === undefined || id === null。
资源:
- MCP 服务页:https://modelscope.cn/mcp/servers/xabaaba/redfox-mcp-server
- API Key:https://redfox.hk/settings/api-keys?source-juejin
- npm:
npx -y redfox-mcp-server@latest
如果这篇文章对你有帮助,欢迎点赞收藏;有 MCP 部署问题可以在评论区交流,一起避坑。