跳到正文
原文
OpenRouter:Blog(Announcements)·· 2 小时前精选AI 评分60

OpenRouter 实测对比 MCP、CLI 与 Skills 的上下文 token 成本

MCP vs CLI vs Skills: What Each Costs in Context

文章要点
  • 1OpenRouter 实测同一 GitHub 任务,Sonnet 5.5 上 CLI 用 1,421 输入 token,MCP 默认 46 工具用 59,417
  • 2GitHub MCP 服务器 46 个默认工具在 Claude 上每请求增加 19,436 token,单个 release 结果约 20,400 token
  • 3同一组工具的开销随模型和服务商变化,GPT-6 Luna 为 8,903,Gemini 3.8 Flash 走 Vertex 8,525、走 AI Studio 13,010
  • 4Tool Search 把 46 个工具前置成本降到 600 以下,Sonnet 5.5 全任务减半,Haiku 5.5 反而上升
决策要点

Agent 接工具的真正成本在上下文而非调用本身,MCP 默认全量加载可能把单任务开销放大数十倍,接入方式值得先量化再选。

  • 影响:跑 agent 的团队 API 成本会因工具接入方式相差数倍到数十倍;收窄工具范围、裁剪返回字段是不改架构的省钱手段;token 计数绑定具体模型与服务商,换模型需重新测算。
  • 参考:可考虑用文中 mcp_cost.py 在自家主力模型上实测工具开销;优先选模型已熟悉的 CLI,无 CLI 时限定 MCP 工具数并让客户端裁剪结果;定义超过约 10,000 token 时值得评估延迟加载。
  • 依据:数据为 OpenRouter 2026 年 10 月 20 日在其平台对单一 GitHub 任务各跑三次取中位数,属厂商自测口径,文中自述仅测了一个服务器一个任务。
正文 · AI 翻译

你给智能体提供的每个工具都会在每次请求时把它的定义发送给模型,而你要为它支付输入 token 的费用。一个 Model Context Protocol(MCP)服务器可能会给上下文窗口增加数万 token。通常的替代方案是命令行工具(CLI)或技能(skill)。

我们在一个 GitHub 任务上对此进行了测量,分别使用 GitHub MCP 服务器、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

  • 在 Claude Sonnet 5.5 上,GitHub MCP 服务器的 46 个默认工具会给每次请求增加 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。它使用 MCP 的 2025-06-18 版本,GitHub 服务器支持该版本。

"""How many prompt tokens does an MCP server add to every request? Ask the model."""
import json
import os
import subprocess
import sys

import requests

API = "https://openrouter.ai/api/v1/chat/completions"
HEADERS = {"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"}

def list_mcp_tools(command):
    """Start a stdio MCP server, run the handshake, and return its tools/list result."""
    proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                            stderr=subprocess.DEVNULL, text=True)

    def rpc(method, params, id=None):
        msg = {"jsonrpc": "2.0", "method": method, "params": params}
        if id is not None:
            msg["id"] = id
        proc.stdin.write(json.dumps(msg) + "\n")
        proc.stdin.flush()
        while id is not None:
            reply = json.loads(proc.stdout.readline())
            if reply.get("id") == id:
                return reply["result"]

    rpc("initialize", {"protocolVersion": "2025-06-18", "capabilities": {},
                       "clientInfo": {"name": "mcp-cost", "version": "1"}}, id=1)
    rpc("notifications/initialized", {})
    tools, cursor, id = [], None, 2
    while True:  # follow nextCursor so servers that paginate tools/list are fully counted
        page = rpc("tools/list", {"cursor": cursor} if cursor else {}, id=id)
        tools += page["tools"]
        cursor, id = page.get("nextCursor"), id + 1
        if not cursor:
            break
    proc.kill()
    return tools

def prompt_tokens(model, tools=None):
    body = {"model": model, "max_tokens": 16, "messages": [{"role": "user", "content": "Say ok."}]}
    if tools:
        body["tools"] = tools
    if model.startswith("google/"):
        # Google Vertex and Google AI Studio count the same tools differently, so pin one.
        body["provider"] = {"only": ["google-vertex"]}
    r = requests.post(API, headers=HEADERS, json=body, timeout=120)
    r.raise_for_status()
    return r.json()["usage"]["prompt_tokens"]

if __name__ == "__main__":
    mcp_tools = list_mcp_tools(sys.argv[1:])
    # What an MCP client sends the model: each tool's name, description and input schema.
    tools = [{"type": "function", "function": {"name": t["name"], "description": t.get("description", ""),
                                               "parameters": t["inputSchema"]}} for t in mcp_tools]
    print(f"{len(tools)} tools")
    for model in ["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 服务器 v2.0.2。

$ python mcp_cost.py github-mcp-server stdio --toolsets=default
46 tools
anthropic/claude-haiku-5.5     +19,436 prompt tokens on every request
openai/gpt-6-luna              +8,903 prompt tokens on every request
google/gemini-3.8-flash        +8,525 prompt tokens on every request

成本会随着你添加的每个工具而增长。在 Haiku 5.5 上,issues 工具集有 10 个工具,增加 5,801 个 token;users 工具集有 1 个工具,增加 619 个 token。

你使用的模型也会改变这个数字。同样 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、技能和 MCP 各需多少成本?

我们向每个模型提问:“GitHub 仓库 cli/cli 的最新 release 是什么?该仓库中带有 bug 标签的最近打开的三个 open 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 数
CLI1,421$0.00756275,292
Skill3,315$0.01181,5426,107
MCP,2 个工具23,228$0.055214,47917,267
MCP,46 个默认工具59,417$0.1274116,28235,409
MCP,全部 91 个工具101,351$0.2113135,23853,289
MCP,2 个工具,结果上限 4,000 字符5,357$0.01912,7262,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 美元。

Bar chart of input tokens for the whole task, split into tool definitions and instructions versus task, tool results and replies: CLI 1,421, Skill 3,315, MCP with 2 tools capped 5,357, MCP with 2 tools 23,228, MCP with 46 tools 59,417, MCP with 91 tools 101,351.

Skill 的成本高于纯 CLI,因为模型一旦读取该文件,它就会留在对话中。对于模型已经熟悉的 CLI 工具,skill 可有可无。而对于你自己的 CLI,skill 是模型学习命令的方式。

为什么 MCP 工具结果的成本和工具定义一样高?

get_latest_release 返回了 GitHub 完整的 release 对象,共 42,182 字符,其中 87% 是对 22 个下载文件的描述。模型只需要标签和日期,而这正是 gh release view 返回的全部内容。

$ gh release view -R cli/cli --json tagName,publishedAt
{"publishedAt":"2026-09-30T02:40:02Z","tagName":"v2.102.0"}

该对象会留在上下文窗口中,并随之后的每次请求重新发送,在 Sonnet 5.5 上它增加了约 20,400 个 token。把服务器限定为两个工具后,首次请求从 19,487 个 token 降到 1,406。但整个任务仍耗费 23,228。

表中给出了两个解决办法。第一,只暴露代理所需的工具,使用 —tools=get_latest_release,list_issues 或 —toolsets=issues,repos。第二,在模型看到结果之前,先在客户端中缩减结果。4,000 字符的上限把限定工具的方案降到 5,357 个 token,且每次运行都正确,因为 GitHub 把标签和日期放在靠前的位置。

这个上限之所以有效,只是因为这些字段恰好位于靠前的位置。与 Tool Search 结合使用时,该上限让 Gemini 3.8 Flash 和 Haiku 5.5 在各自的三次运行中只有一次回答正确,而且 Gemini 有一次在重试时触发了我们的 12 次请求上限。

更好的做法是保留完整字段,就像 CLI 的 —json 那样。GitHub 服务器的 list_issues 接受一个 fields 参数,只请求 number、title 和创建日期,就把结果从 8,336 字符减少到 714。没有这个参数,结果大小就由模型决定。在加载 46 或 91 个工具的六次运行中,有四次 GPT-6 Luna 请求了 100 个 issue 及其全文,结果拿回了 281,963 字符。

Tool Search 能解决 MCP 的上下文问题吗?

Tool Search 移除了工具定义的大部分成本。你把工具标记为 defer_loading: true,并添加 {“type”: “openrouter:tool_search”}。我们会扣留这些延后加载的定义,只发送搜索工具,以及你保留加载的任何工具。模型用正则表达式搜索工具名称和描述,并在下一轮调用匹配结果。该功能目前处于测试阶段,在 Responses 和 Messages API 上适用于 任何模型和提供商。

在全部 46 个 GitHub 工具都延后加载的情况下,任何搜索之前的成本在 Claude 模型上为 578 个 token,GPT-6 Luna 为 144,Gemini 3.8 Flash 为 130。

这个脚本会分别在加载全部 46 个工具和延迟加载的情况下发送同一个问题。把它保存在 mcp_cost.py 旁边,因为它复用了 list_mcp_tools。

"""Send the same MCP tools with defer_loading and openrouter:tool_search, and compare prompt tokens."""
import os
import sys

import requests

from mcp_cost import list_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?"

def ask(model, tools):
    r = requests.post(API, headers=HEADERS, timeout=300,
                      json={"model": model, "input": QUESTION, "tools": tools})
    r.raise_for_status()
    return r.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"]} for t in mcp_tools]
deferred = [{"type": "openrouter:tool_search"}] + [{**t, "defer_loading": True} for t in tools]

for model in ["anthropic/claude-haiku-5.5", "openai/gpt-6-luna"]:
    for label, tool_list in [("all loaded", tools), ("tool search", deferred)]:
        res = ask(model, tool_list)
        steps = [f"search {o['query']!r}" if o["type"] == "openrouter:tool_search" else f"call {o['name']}"
                 for o in res["output"] if o["type"] in ("openrouter:tool_search", "function_call")]
        print(f"{model:28} {label:12} {res['usage']['input_tokens']:>6,} input tokens  {' -> '.join(steps)}")
$ python tool_search.py github-mcp-server stdio --toolsets=default
anthropic/claude-haiku-5.5   all loaded   19,464 input tokens  call get_latest_release
anthropic/claude-haiku-5.5   tool search   7,069 input tokens  search 'github|release' -> search 'latest release|list_releases|get_latest_release' -> call get_latest_release
openai/gpt-6-luna            all loaded    8,923 input tokens  call get_latest_release
openai/gpt-6-luna            tool search   4,453 input tokens  search 'github repository latest release cli cli' -> search 'github' -> search 'search web internet browser' -> search 'release' -> call get_latest_release

要在你的 agent 上试试,把你的 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.559,41730,33431,615
GPT-6 Luna116,28225,54457,975
Gemini 3.8 Flash35,40937,56838,445
Claude Haiku 5.599,319154,62331,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,因此请先把那个 agent 迁移到 Responses 或 Messages API。

MCP vs CLI vs skills:你应该使用哪一个?

当存在 CLI 且你的 agent 能运行命令时,就使用 CLI。如果不能,就使用 MCP,并限定作用范围、保持结果精简。

你的情况使用基于我们在 Sonnet 5.5 上的运行
存在知名的 CLI 且 agent 能运行命令该 CLI每个任务 1,421 个 token
你自己的 CLI,或模型使用得不稳定的 CLICLI 加上一个 skill3,315 个 token,每次运行命令相同
没有 CLI、没有 shell,或者你需要服务器的认证和权限MCP,限定在你需要的工具范围内2 个工具时为 23,228 个 token,而 46 个时为 59,417 个
跨多个服务器的大型工具库搭配 Tool Search 的 MCP在 Sonnet 5.5 上,延迟 91 个工具的成本与 46 个大致相当
任何 MCP 方式把工具结果精简到你所需要的字段从 23,228 个降到 5,357 个 token

如果你的 agent 没有 shell,我们的 Shell server tool 可以为任何模型提供一个沙箱化的 shell。在我们的 Agent SDK 中,MCP Tools 可连接远程 MCP 服务器,而 skills loader 示例 只在模型请求之后才添加 skill 的指令。

我们只在一个任务上测试了一个服务器,所以在你做决定之前,请先在你的任务上运行 mcp_cost.py。

常见问题

在 AI agent 语境中,CLI 是什么?

CLI(命令行界面)是一种通过键入命令来运行的程序。Agent 则会拿到一个 shell 工具并自行编写命令。在我们的测试中,gh CLI 在一个 GitHub 任务上使用了 1,421 个输入 token,而 GitHub MCP server 使用了 59,417 个。

MCP server 和 connector 有什么区别?

MCP server 通过 Model Context Protocol 提供工具。Connector 是像 Claude 这样的应用对其与远程 MCP server 的连接的称呼。我们的 Agent SDK 可以通过 MCP Tools 连接远程 MCP server。

Skill 正在取代 MCP 吗?

不是。Skill 是一组指令,通常面向 CLI,只在任务需要时才加载。MCP 提供带有各自身份验证的类型化工具,并且无需 shell 即可工作。在 OpenRouter 上,Tool Search 让你既能保留 MCP,又只在需要时加载各个工具。

Prompt caching 能解决 MCP 的 token 成本问题吗?

它能降低这些 token 的价格,但并不能消除它们。根据我们的实时模型价格,截至 2026 年 10 月,在 OpenRouter 上,Claude Haiku 5.5 和 GPT-6 Luna 的缓存输入价格为正常费率的十分之一,但仍会占用上下文窗口。

多少个 MCP 工具才算太多?

我们的 Tool Search 文档建议,一旦工具定义超过大约 10,000 个 token 或约 10 个工具,就采用延迟加载。GitHub MCP server 的 46 个默认工具在 Claude 上总计达到 19,436 个 token。

后续步骤

来源:OpenRouter:Blog(Announcements) · openrouter.ai