从 stdio MCP 的 schema token 黑洞,到
npx三 agent 复用的工程实践
过去半年我把每个 agent 都在用的 stdio MCP 拆了:fetch 一个包,Bing CN 搜索一个包,所有 agent 共享。仓库 dej4vu/websearch,npm @dej4vu/websearch-cli。这件事的关键不在工具本身,而是它怎么重新分配"agent 用工具的成本"。
下面分两件事讲清楚:为什么 Skill 模式能赢过 stdio MCP,以及为什么 Bing CN 是中文场景下被低估的搜索源。
一、Skill vs stdio MCP:六维对比
我同时维护 Codex 桌面端、Claude Code CLI、Hermes Agent 三个 agent。以前每个 MCP 都是 stdio 子进程,跑在每个 agent 自己 host 上。下面六条是实际用下来最痛的点。
| 维度 | stdio MCP | Skill + npx CLI |
|---|---|---|
| schema token 占用 | 所有工具描述常驻 system prompt,开机即烧 token | SKILL.md 按 description 触发,schema 不常驻 |
| 多 agent 部署 | Codex / Claude / Hermes 各自装一份,升级改三处 | 一份 SKILL.md + 一个 npm 包,npx skills add 同时落到三个 agent |
| 沙箱代理兼容 | Node 子进程继承 shell 代理变量,沙箱一挡就崩 | CLI 走 undici Dispatcher,proxy 是显式 --proxy-url |
| 错误追踪 | JSON-RPC 包装,MCP 报错定位要追两层 | CLI 直接打 stderr / exit code |
| 能力扩展 | 增工具 = 改 schema + 重启 server + 三 agent 各自 reload | 增子命令 = 多一个 websearch xxx,SKILL.md 加一行 |
| 可观测性 | MCP 调用结果常被截断,agent 看不到元数据 | JSON 输出带 truncated / nextStartIndex / dateMissing / sort / freshness / resultCount |
最关键的一条:schema token。
开 5 个 MCP,system prompt 凭空多 4-8K token,每个对话都付这笔钱。而 agent 在 80% 的对话里其实只用了其中 1-2 个工具------剩下 3-4 个 MCP 就是常量沉没成本。Skill 模式把工具描述做成"按需加载":agent 先看 description 字段判断要不要用,需要才把完整 SKILL.md 拉进上下文。一个 100 行的 SKILL.md 在 agent 没用到的时候基本是 0 token。
我把它从 stdio MCP 拆出来以后,三个 agent 的 system prompt 总长度都下降了 3-5K,下游回答明显更聚焦。
第二关键:一份安装三个 agent。
npm 上的 skills(vercel-labs 出品)在 1.5.23 已经原生支持 Codex / Claude Code / Hermes Agent:
sh
npx -y skills@latest add dej4vu/websearch \
--skill websearch --agent codex --agent claude-code --agent hermes-agent \
--global --yes
装完以后:
- Codex 桌面端走 ~/.agents/skills/websearch
- Claude Code CLI 走 ~/.claude/skills/websearch
- Hermes Agent 走 ~/.hermes/skills/websearch(需 HERMES_HOME 或 mkdir -p .hermes)
升级 websearch CLI 时,三 agent 各自 reload 一次 skill 就同步。不像 MCP 那种"三个 host 改三遍"。
第三关键:沙箱代理不再突然崩。
stdio MCP 启动失败时常见这种错:
vbscript
MCP server fetch failed to start: Error: spawn node EPERM
at ChildProcess._handle.onexit
错误定位到 MCP 框架本身,根本看不出是 HTTP_PROXY=127.0.0.1:7890 被沙箱拒了。换成 npx CLI 后,加 --proxy-url 或在 shell 里 env -u 就行。README 里这段我直接给读者粘走的命令,避免每次都解释。
剩下三点(错误追踪 / 能力扩展 / 可观测性)的具体例子,在第三 / 四节展开。
二、Bing CN:被低估的中文搜索源
国内开发者一提到搜索就条件反射 Google / Brave / Tavily,但 cn.bing.com 在中文技术查询上有几个真优势。
2.1 中文长尾词命中率显著高于国际引擎
实测三个时效查询:
sh
# 同一时刻三 agent 跑:
npx -y @dej4vu/websearch-cli@latest search "智谱 GLM 最新模型" --count 10 --json
npx -y @dej4vu/websearch-cli@latest search "千问 最新模型" --count 10 --json
npx -y @dej4vu/websearch-cli@latest search "deepseek 最新模型" --count 10 --json
auto 模式默认启用 month freshness + 日期排序。结果是:官方 docs.bigmodel.cn / qwen.ai / api-docs.deepseek.com 都稳定在前 5 条,知乎/CSDN 排后面。国际搜索引擎切到中文 region 后仍有不少英文结果混排,特别是模型名 / 论文 ID 这种术语。
2.2 时间戳能力是 cn.bing.com 的原生优势
Bing 搜索结果页面会带:
- .news_dt 元素(如 2026年8月14日)
- snippet 里的相对日期(如 4 天之前、5 days ago)
- URL 路径里的日期(如 /2026/08/14/...)
- ISO 时间戳
我把这四个来源全接进一个解析器:
js
extractPublishedAt(result) => {
// 优先 .news_dt,其次 snippet 中"X 天/X 天前/YYYY 年 M 月 D 日",最后 URL。
// 任一命中就返回 { publishedAt, publishedAtText, publishedAtSource }。
// 全部不命中 → dateMissing: true,绝不伪造。
}
agent 拿到 JSON 里:
json
{
"publishedAt": "2026-08-14T00:00:00.000Z",
"publishedAtText": "2026年8月14日",
"publishedAtSource": "url",
"dateMissing": false
}
auto 模式遇到"最新 / 发布 / 上线 / release / news / new / latest"等时效意图词时,会:
- 把 freshness 提到 month(或更窄)
- 把排序切到 date 倒序
- 官方域名加权,避免严格时间倒序把权威源压到低质量新闻后
普通技术查询(如 "docker network 原理"、"jsdom 内存泄漏")auto 不会动 sort / freshness,结果保持相关性。
2.3 多页聚合去重,Bing 不会偷偷吃掉结果数
Bing 单页 10 条,HTML 响应里还有 first token 可以跳页。--count 30 时我会自动分 3 页、跟 token、合并去重。JSON 顶层会告诉你:
json
{
"sort": "auto",
"freshness": "month",
"requestedCount": 30,
"resultCount": 28,
"pagesFetched": 3,
"pagesRequested": 3,
"duplicatesRemoved": 4,
"totalResults": "约 1,230,000 条结果",
"hasMore": false
}
requestedCount 是你请求的,resultCount 是真实拿到的,duplicatesRemoved 是去掉 Bing 跨页重复的数量。不会让你以为拿到 30 条实际只 10 条。
同时:
- ?utm_source= / ?ref= 这类跟踪参数在比较前去重
- /ck/a?...query=...&u=... 这类 Bing 跳转还原回真实 URL
- 黑名单域名(zhihu / 小红书 / 微博 / 抖音 / B 站 / CSDN)在 search 里只标记 fetchBlocked: true 不删除,方便用户选
三、fetch:与官方 fetch MCP 同款表格保留
MCP 时代的 mcp-server-fetch 我一直很喜欢------HTML 表格保留为 Markdown 表格。我的第一版 CLI 没挂 turndown-plugin-gfm,表格被压成扁平段落,被某个 agent 实测吐槽"对总结文档任务,mcp fetch 直接可用"。
v0.3.2 修了:加上和官方同款的 turndown-plugin-gfm,表格恢复成 | ... | 形式。例如 docs.bigmodel.cn/cn/coding-plan/latest-model 里的"思考强度对照表"现在长这样:
markdown
| 工具传入值 | 实际档位 | 处理 |
| --- | --- | --- |
| thinking.type 未传、true、enabled、adaptive | max | 使用默认档 |
| reasoning\_effort 为 minimal、light、low | low | 自动转换 |
| reasoning\_effort 为 medium、high | high | 自动转换 |
fetch 还顺带做了:
-
Readability + 文档站 fallback:React/Next.js 文档站常见 main / .mdx-content,Readability 经常低估文本量,自动 fallback 到 .mdx-content 容器。
-
pre > span 高亮代码块:很多文档站代码高亮不是
css,保留成 fenced code 而不是被切成零碎段落。
- 分页元数据:JSON 里 truncated / nextStartIndex,续抓逻辑 agent 自己写就行:
bash
NEXT=$(... | jq -r ".nextStartIndex")
[ "$NEXT" != "null" ] && websearch fetch URL --start-index "$NEXT" --json
- robots / proxy / 超时默认遵守,--ignore-robots-txt / --proxy-url / --timeout-ms 都是显式开关。
一个最小调用:
sh
npx -y @dej4vu/websearch-cli@latest \
fetch "https://docs.bigmodel.cn/cn/coding-plan/latest-model" --json
四、SKILL.md:一份 skill 喂三个 agent
skills@1.5.23 一次安装到三 agent:
sh
npx -y skills@latest add dej4vu/websearch \
--skill websearch --agent codex --agent claude-code --agent hermes-agent \
--global --yes
落点:
| agent | 全局路径 |
|---|---|
| codex | ~/.agents/skills/websearch |
| claude-code | ~/.claude/skills/websearch |
| hermes-agent | ~/.hermes/skills/websearch(需 HERMES_HOME 或 mkdir -p .hermes) |
装完以后 agent 看到的 SKILL.md 头部:
markdown
---
name: websearch
description: Search Bing CN and fetch live web pages through the npx-runnable websearch CLI, with readable markdown extraction, robots.txt handling, chunked pagination, raw mode, proxy support, and JSON output for Codex, Claude Code, and other CLI-capable agents.
---
agent 接下来只要识别到任务需要"现在的网页 / 开放搜索 / 某个 URL 预览",就会自动调 websearch search 或 websearch fetch。
为什么 description 这么长?因为 description 是 agent 决定"要不要加载 SKILL.md"的唯一依据。写得越精确,agent 误触发越少,token 也越省。
升级时一行业务 CI 即可:
sh
npx -y skills@1.5.23 add "dej4vu/websearch#v0.3.2@websearch" \
--skill websearch --agent codex --agent claude-code --agent hermes-agent \
--global --yes
五、踩过的坑(避免你再踩)
1. npx @dej4vu/websearch-cli@latest 报 404 不是版本没发,是沙箱代理 / registry.npmmirror.com 镜像在拦截。用:
sh
env -u HTTP_PROXY -u HTTPS_PROXY -u ALL_PROXY \
-u http_proxy -u https_proxy -u all_proxy \
npx -y --registry=https://registry.npmjs.org/ \
@dej4vu/websearch-cli@0.3.2 search "GLM 最新模型" --json
这条已经写进 README。
2. fetch 把表格压成扁平段落 Turndown 默认不转
。官方 mcp-server-fetch 用 turndown-plugin-gfm,我跟了一样的做法。v0.3.2 之后表格回来了。
3. mcp fetch 报 timeout / MCP server 启动失败 多半是沙箱代理变量没清 + MCP stdio 进程崩在 child_process 层。换成 npx CLI + --proxy-url 后可控得多。
4. freshness=month 不是严格时间倒序 Bing 内部仍按相关性排,month 只是窗口过滤。要严格时间倒序要 --sort date + 解析出的 publishedAt,这个 JSON 里已经给你了。
5. stdio MCP 的 schema token 三个 agent 同时启用 5+ 个 MCP 时,system prompt 会无端多 8K token。把不常用 / 通用能力拆成 SKILL.md 之后立刻见效。
六、上手 60 秒
sh
# 1. 跑一次搜索(auto 模式)
npx -y @dej4vu/websearch-cli@latest search "智谱 GLM 最新模型" \
--count 10 --json | jq ".results[] | {rank, title, publishedAtText}"
# 2. 抓其中一篇
npx -y @dej4vu/websearch-cli@latest fetch \
"https://docs.bigmodel.cn/cn/coding-plan/latest-model" --json
# 3. 给 Codex / Claude Code / Hermes 装 skill
npx -y skills@latest add dej4vu/websearch \
--skill websearch --agent codex --agent claude-code --agent hermes-agent \
--global --yes
帮助:开 issue 或直接看 README.zh-CN.md。
七、Roadmap
- web search:通用网页搜索(MCP 同名),聚合多引擎;
- 元数据抽取:标题、作者、发布时间、关键词、摘要;
- sitemap 模式:从站点地图批量抓;
- 缓存层:相同 URL 短期复用,避免被反复限流;
- 插件化黑名单:用户可声明自己的拦截集合;
- 更多 agent 适配:Aider、Cline、Continue 等。
MIT,欢迎 PR / Issue / Star。
如果你也遇到 MCP 部署 / 沙箱代理 / 多 agent skill 复用的痛点,或者有 Bing CN 比其它引擎更适合的场景,欢迎评论区聊聊。
``