你给智能体的每个工具都会在每次请求时把它的定义发送给模型,而你要为此支付输入 token 的费用。一个Model Context Protocol(MCP)服务器可能会给上下文窗口增加数万个 token。通常的替代方案是命令行工具(CLI)或技能。
我们在一个 GitHub 任务上进行了测量,使用了GitHub MCP server、gh CLI以及一个 gh 技能,在Claude Haiku 5.5、Claude Sonnet 5.5、GPT-6 Luna和Gemini 3.8 Flash上。在 Sonnet 5.5 上,三者都正确作答,但它们使用的输入 token 差异很大。CLI 使用了 1,421 个,技能使用了 3,315 个,而 MCP 服务器在加载其默认工具的情况下使用了 59,417 个。下面说明这些 token 都去了哪里,以及如何用我们的Tool Search服务器工具来削减它们。
TL;DR
-
GitHub MCP server 的 46 个默认工具在 Claude Sonnet 5.5 上为每次请求增加 19,436 个 token。
-
在 Sonnet 5.5 上,一个工具结果——GitHub 的 release 对象——花费了约 20,400 个 token,比全部 46 个定义还多。
-
Tool Search 将 46 个工具的前期成本削减到 600 个 token 以下。在整个任务中,它在 Sonnet 5.5 上将成本减半,而在 Claude Haiku 5.5 上则提高了成本。
MCP、CLI 和技能之间有什么区别?
三者都为模型提供一种能力。它们的区别在于模型在使用它之前需要读取什么。
| 模型在前期看到的内容 | 模型在行动时看到的内容 | |
|---|---|---|
| MCP 服务器 | 每个工具的名称、描述和 JSON schema,在每次请求时 | 工具的结果,以服务器返回的任何形式 |
| CLI | 一个 shell 工具和一行说明该 CLI 存在的文字 | 命令的输出,你可以用诸如 —json 的标志来塑造它 |
| 技能 | 每个技能的一行描述 | 该技能的 SKILL.md 文件,仅一次,然后是命令输出 |
CLI 依赖于模型已经知道该工具。技能教会模型如何使用一个工具,而模型只在任务需要时才加载完整指令。
一个 MCP 服务器为每次请求增加多少 token?
可靠的计数方法是询问模型。每个提供商在计数 token 之前会以自己的格式将工具定义写入提示中,本地分词器会遗漏这一点。发送带工具和不带工具的相同请求,usage.prompt_tokens 的差值就是这些工具在每一轮的成本。
将其保存为 mcp_cost.py。它使用2025-06-18 版本的 MCP,GitHub 服务器接受该版本。
"""一个 MCP 服务器会给每个请求增加多少 prompt token?问问模型。"""
importjson
importos
importsubprocess
importsys
importrequests
API="https://openrouter.ai/api/v1/chat/completions"
HEADERS={"Authorization":f"Bearer{os.environ['OPENROUTER_API_KEY']}"}
deflist_mcp_tools(command):
"""启动一个 stdio MCP 服务器,执行握手,并返回其 tools/list 结果。"""
proc=subprocess.Popen(command,stdin=subprocess.PIPE,stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,text=True)
defrpc(method, params, id=None):
msg={"jsonrpc":"2.0","method": method,"params": params}
ifidisnotNone:
msg["id"]=id
proc.stdin.write(json.dumps(msg)+"\n")
proc.stdin.flush()
whileidisnotNone:
reply=json.loads(proc.stdout.readline())
ifreply.get("id")==id:
returnreply["result"]
rpc("initialize", {"protocolVersion":"2025-06-18","capabilities": {},
"clientInfo": {"name":"mcp-cost","version":"1"}},id=1)
rpc("notifications/initialized", {})
tools, cursor,id=[],None,2
whileTrue:# 跟随 nextCursor,以便对 tools/list 分页的服务器也能被完整统计
page=rpc("tools/list", {"cursor": cursor}ifcursorelse{},id=id)
tools+=page["tools"]
cursor,id=page.get("nextCursor"),id+1
ifnotcursor:
break
proc.kill()
returntools
defprompt_tokens(model, tools=None):
body={"model": model,"max_tokens":16,"messages": [{"role":"user","content":"Say ok."}]}
iftools:
body["tools"]=tools
ifmodel.startswith("google/"):
# Google Vertex 和 Google AI Studio 对相同工具的计数方式不同,因此固定使用其中一个。
body["provider"]={"only": ["google-vertex"]}
r=requests.post(API,headers=HEADERS,json=body,timeout=120)
r.raise_for_status()
returnr.json()["usage"]["prompt_tokens"]
if__name__=="__main__":
mcp_tools=list_mcp_tools(sys.argv[1:])
# MCP 客户端发送给模型的内容:每个工具的名称、描述和输入 schema。
tools=[{"type":"function","function": {"name": t["name"],"description": t.get("description",""),
"parameters": t["inputSchema"]}}fortinmcp_tools]
print(f"{len(tools)}tools")
formodelin["anthropic/claude-haiku-5.5","openai/gpt-6-luna","google/gemini-3.8-flash"]:
added=prompt_tokens(model, tools)-prompt_tokens(model)
print(f"{model:30}+{added,}prompt tokens on every request")
将服务器的启动命令作为参数传入,如下所示,针对 GitHub MCP server v2.0.2。
$pythonmcp_cost.pygithub-mcp-serverstdio--toolsets=default
46个工具
anthropic/claude-haiku-5.5+19,436提示token在每次请求中
openai/gpt-6-luna+8,903提示token在每次请求中
google/gemini-3.8-flash+8,525提示token在每次请求中
每增加一个工具,成本就会增长。在 Haiku 5.5 上,issues 工具集有 10 个工具,会增加 5,801 个 token。users 工具集只有一个工具,会增加 619 个。
你使用的模型也会改变这个计数。同样的 46 个工具在 Haiku 5.5 上花费 19,436 个 token,但在 GPT-6 Luna 上只有 8,903 个,所以要在你计划运行的模型上进行测量。
甚至提供模型服务的供应商也可能改变它。在 Gemini 3.8 Flash 上,这 46 个工具通过 Google Vertex 花费 8,525 个 token,而通过 Google AI Studio 则花费 13,010 个。这就是脚本总是把 Gemini 请求发送到 Google Vertex 的原因。
同样的任务用 CLI、skill 和 MCP 分别要花多少成本?
我们向每个模型提问:“GitHub 仓库 cli/cli 的最新发布版本是什么,以及该仓库中最近打开的、带有 bug 标签的三个 issue 是什么?”每种方式都在我们的Responses API上作为工具调用循环运行,并且我们累加了各次请求的 usage.input_tokens。
-
CLI。一个 run_shell 工具,加上一行“GitHub CLI gh 已安装并已认证。”
-
Skill。同样的工具,再加上一行索引,指向一份 1,492 个字符的 gh 用法 SKILL.md。
-
MCP。服务器的工具,范围限定为任务所需的 2 个、默认的 46 个,或全部 91 个。
我们在 2026 年 10 月 20 日对每种组合各运行了三次,并展示中位数。Haiku 5.5 对提示的计数与 Sonnet 5.5 完全相同,但在某些运行中它多花了额外轮次,因此我们展示 Sonnet 更稳定的 token 数和计费成本。
| 方式 | Sonnet 5.5 token 数 | Sonnet 5.5 每任务成本 | GPT-6 Luna token 数 | Gemini 3.8 Flash token 数 |
|---|---|---|---|---|
| CLI | 1,421 | $0.0075 | 627 | 5,292 |
| Skill | 3,315 | $0.0118 | 1,542 | 6,107 |
| MCP,2 个工具 | 23,228 | $0.0552 | 14,479 | 17,267 |
| MCP,46 个默认工具 | 59,417 | $0.1274 | 116,282 | 35,409 |
| MCP,全部 91 个工具 | 101,351 | $0.2113 | 135,238 | 53,289 |
| MCP,2 个工具,结果上限为 4,000 个字符 | 5,357 | $0.0191 | 2,726 | 2,943 |
这张表中的每次运行都回答正确。失败出现在 Tool Search 上,详见下文。在 Sonnet 5.5 上,默认 MCP 服务器消耗的输入 token 是 CLI 的 42 倍,成本是后者的 17 倍,每 1,000 个任务为 127 美元对 7.50 美元。在 Sonnet 5.5 上,输出 token 的成本是输入的 5 倍,而 CLI 运行的 463 个输出 token 在其 0.0075 美元中占 0.0046 美元。

技能比裸 CLI 成本更高,因为文件一旦被模型读取就会一直留在对话中。对于模型熟悉的 CLI 工具,技能是可选的。对于你自己的 CLI,技能是模型学习命令的方式。
为什么 MCP 工具结果的成本和定义一样高?
get_latest_release 返回了 GitHub 的完整 release 对象,42,182 个字符,其中 87% 描述的是 22 个下载文件。模型需要的是 tag 和日期,而这正是 gh release view 返回的全部内容。
$ghreleaseview-Rcli/cli--jsontagName,publishedAt
{"publishedAt":"2026-09-30T02:40:02Z","tagName":"v2.102.0"}
该对象会留在上下文窗口中,并在之后的每次请求中重新发送,在 Sonnet 5.5 上它增加了约 20,400 个 token。将服务器限定为两个工具后,首次请求从 19,487 个 token 降至 1,406 个。整个任务仍然花费了 23,228 个 token。
表中展示了两种修复方式。第一,只暴露智能体需要的工具,使用 —tools=get_latest_release,list_issues 或 —toolsets=issues,repos。第二,在模型看到结果之前,在客户端中缩小结果。4,000 字符的上限使限定范围后的路线降至 5,357 个 token,且每次运行都正确,因为 GitHub 把 tag 和日期放在靠近顶部的位置。
上限之所以有效,只是因为字段恰好位于那个位置。与 Tool Search 结合后,该上限使 Gemini 3.8 Flash 和 Haiku 5.5 在各自三次运行中只有一次正确答案,而 Gemini 在重试时曾一次触及我们 12 次请求的上限。
改为保留完整字段,就像 CLI 的 —json 那样。GitHub 服务器的 list_issues 接受 fields 参数,只请求 number、title 和 creation date 后,其结果从 8,336 个字符降至 714 个。没有它,大小由模型决定。在加载 46 或 91 个工具的六次运行中,有四次 GPT-6 Luna 请求了 100 个 issue 及其全文,返回了 281,963 个字符。
Tool Search 能解决 MCP 的上下文问题吗?
Tool Search removes most of the cost of tool definitions. You mark tools defer_loading: true and add {“type”: “openrouter:tool_search”}. We withhold the deferred definitions and send the search tool, plus any tools you left loaded. The model searches tool names and descriptions with a regular expression and calls a match on its next turn. It’s in beta and works withany model and provideron the Responses and Messages APIs.
With all 46 GitHub tools deferred, the cost before any search was 578 tokens on the Claude models, 144 on GPT-6 Luna, and 130 on Gemini 3.8 Flash.
This script sends one question with all 46 tools loaded, then deferred. Save it next to mcp_cost.py, since it reuses list_mcp_tools.
"""Send the same MCP tools with defer_loading and openrouter:tool_search, and compare prompt tokens."""
importos
importsys
importrequests
frommcp_costimportlist_mcp_tools
API="https://openrouter.ai/api/v1/responses"
HEADERS={"Authorization":f"Bearer{os.environ['OPENROUTER_API_KEY']}"}
QUESTION="What is the latest release of the GitHub repository cli/cli?"
defask(model, tools):
r=requests.post(API,headers=HEADERS,timeout=300,
json={"model": model,"input":QUESTION,"tools": tools})
r.raise_for_status()
returnr.json()
mcp_tools=list_mcp_tools(sys.argv[1:])
# The Responses API takes function tools flat: type, name, description, parameters.
tools=[{"type":"function","name": t["name"],"description": t.get("description",""),
"parameters": t["inputSchema"]}fortinmcp_tools]
deferred=[{"type":"openrouter:tool_search"}]+[{**t,"defer_loading":True}fortintools]
formodelin["anthropic/claude-haiku-5.5","openai/gpt-6-luna"]:
forlabel, tool_listin[("all loaded", tools), ("tool search", deferred)]:
res=ask(model, tool_list)
steps=[f"search{o['query']!r}"ifo["type"]=="openrouter:tool_search"elsef"call{o['name']}"
foroinres["output"]ifo["type"]in("openrouter:tool_search","function_call")]
print(f"{model:28}{label:12}{res['usage']['input_tokens']:>6,}input tokens{' -> '.join(steps)}")
$pythontool_search.pygithub-mcp-serverstdio--toolsets=default
anthropic/claude-haiku-5.5allloaded19,464inputtokenscallget_latest_release
anthropic/claude-haiku-5.5toolsearch7,069inputtokenssearch'github|release'->search'latest release|list_releases|get_latest_release'->callget_latest_release
openai/gpt-6-lunaallloaded8,923inputtokenscallget_latest_release
openai/gpt-6-lunatoolsearch4,453inputtokenssearch'github repository latest release cli cli'->search'github'->search'search web internet browser'->search'release'->callget_latest_release
要在你的智能体上试用,请将你的 MCP 工具标记为 defer_loading: true,添加 {“type”: “openrouter:tool_search”},并比较前后的 usage.input_tokens。
Tool Search 各行会随模型的搜索方式而变化。在该脚本的五次独立运行中,Haiku 5.5 使用了 2,134 到 13,225 个 token,且每次搜索都会增加。GPT-6 Luna 写的是短语而非模式,进行了三到六次搜索,并有一次放弃、未调用该工具。我们的文档建议保留你最常用的三到五个工具处于加载状态。
在整个任务中,结果取决于模型。
| 模型 | MCP,加载 46 个工具 | Tool Search,延迟 46 个 | Tool Search,延迟 91 个 |
|---|---|---|---|
| Claude Sonnet 5.5 | 59,417 | 30,334 | 31,615 |
| GPT-6 Luna | 116,282 | 25,544 | 57,975 |
| Gemini 3.8 Flash | 35,409 | 37,568 | 38,445 |
| Claude Haiku 5.5 | 99,319 | 154,623 | 31,781 |
GPT-6 Luna 的加载数值包含上文所述的 100 个 issue 请求。
在 Sonnet 5.5 上,91 个延迟工具比 46 个多花费 1,281 个 token,而全部加载 91 个则多花费约 42,000 个。Tool Search 不会缩小工具结果,release 对象仍占据了 Sonnet 第二次请求的大部分。
Haiku 5.5 在延迟 46 个工具时比全部加载时花费更高。在全部三次运行中,它都向 list_issues 请求了该工具不提供的字段,然后用错误类型的参数重试了三到四次,例如将 labels 写成字符串而非数组。在全部工具加载的情况下,Haiku 在三次运行中的两次犯了同样的错误,并在下一次调用时修正了它。为你的循环中的请求设置上限,并在某次运行成本激增时检查工具错误。
Tool Search 通过放宽 tool_choice 来揭示工具,因此同时强制进行工具调用的请求会失败。
400 `tool_choice` 与 `openrouter:tool_search` 冲突。延迟工具是通过 `tool_choice` 揭示的,因此必须省略它或将其设置为 `{“type”: “allowed_tools”, …}`。移除 `tool_choice`,或从你的工具中去掉 `defer_loading` 以按原样使用。
Chat Completions 不接受 openrouter:tool_search 并返回 400,所以请先把该智能体迁移到 Responses 或 Messages API。
MCP vs CLI vs skills:你应该用哪个?
当存在 CLI 且你的智能体可以运行命令时,就用 CLI。当它不能时,就用 MCP,限定范围并保持结果精简。
| 你的情况 | 使用 | 基于我们在 Sonnet 5.5 上的运行结果 |
|---|---|---|
| 存在一个知名的 CLI,且智能体可以运行命令 | CLI | 每个任务 1,421 tokens |
| 你自己的 CLI,或模型使用不稳定的 CLI | CLI 加一个 skill | 3,315 tokens,每次运行命令相同 |
| 没有 CLI、没有 shell,或者你需要服务器的认证和权限 | MCP,限定为你需要的工具 | 2 个工具时 23,228 tokens,而 46 个工具时为 59,417 |
| 跨多个服务器的大型工具库 | 带 Tool Search 的 MCP | 在 Sonnet 5.5 上,91 个延迟加载的工具成本大约相当于之前的 46 个 |
| 任何 MCP 路线 | 将工具结果裁剪为你需要的字段 | 从 23,228 降至 5,357 tokens |
如果你的智能体没有 shell,我们的Shell 服务器工具可以为任何模型提供一个沙箱化的 shell。在我们的 Agent SDK 中,MCP Tools可连接远程 MCP 服务器,而skills loader 示例仅在模型请求后才添加 skill 的指令。
我们只在一个任务上测试了一个服务器,所以在做决定前请在你的环境上运行 mcp_cost.py。
常见问题
在 AI 智能体语境中,CLI 是什么?
CLI(命令行界面)是你用输入命令运行的程序。智能体获得一个 shell 工具并自行编写命令。在我们的测试中,gh CLI 在一个 GitHub 任务上使用了 1,421 个输入 tokens,而 GitHub MCP 服务器为 59,417 个。
MCP 服务器和连接器有什么区别?
MCP 服务器通过 Model Context Protocol 暴露工具。连接器是 Claude 这类应用对其连接到远程 MCP 服务器的链路的称呼。我们的 Agent SDK 可以通过 MCP Tools 连接远程 MCP 服务器。
skills 会取代 MCP 吗?
不会。skill 是指令,通常针对 CLI,仅在任务需要时加载。MCP 提供带自身认证的类型化工具,且无需 shell 即可工作。在 OpenRouter 上,Tool Search 让你可以保留 MCP 并仅在需要时加载每个工具。
提示缓存能解决 MCP 的 token 成本吗?
它降低了这些 tokens 的价格,但并没有消除它们。截至 2026 年 10 月,在 OpenRouter 上,缓存输入在 Claude Haiku 5.5 和 GPT-6 Luna 上的费用是正常费率的十分之一,根据我们的实时模型价格,并且仍然能填满上下文窗口。
多少个 MCP 工具算太多?
我们的 Tool Search 文档建议,当工具定义超过约 10,000 个 token 或大约 10 个工具时,就应推迟加载工具。GitHub MCP 服务器的 46 个默认工具在 Claude 上达到 19,436 个 token。
后续步骤
-
Tool Search涵盖了所有选项,包括 max_results。
-
构建可靠的工具调用智能体循环展示了我们运行中所用的那种循环,并带有迭代上限。
-
OpenRouter 提示缓存解释了缓存 token 的费用。