实测 Claude Code 在空目录下首次请求消耗 23207 tokens;--strict-mcp-config --tools "" 可将请求压缩至 2664 tokens,skill 列表占 18962 tokens。
当我们在首次请求中传入 --disallowedTools "Skill" 时,Claude Code 的首次请求还剩 18,962 个 skill 列表字符,而请求仅缩减了 913 个 token,因为 Workflow 工具的描述内联了它通常从 skill 加载的引用,从 3,480 字符膨胀到 20,256 字符。在 2.1.285 版本的空目录下,首次执行 claude -p "Reply with just OK." 默认测量为 23,207 个输入 token,加上 --strict-mcp-config --tools "" 后仅为 2,664 个,只剩下系统提示和几条简短的备注。
在早些时候的实验中,六个 CLAUDE.md 文件、六个代码词、两次相同配置运行分别得到 23,624 和 24,410 个输入 token。当我们对比两份记录时,唯一的差异是延迟工具列表:一次运行包含了我们 claude.ai 账户中一个远程连接器的工具,另一次则没有。我们添加了 --strict-mcp-config,两组数据开始匹配,然后继续测量 CLAUDE.md 文件。
这留下了一个悬而未决的问题。如果一个连接器的工具名称就值 786 个 token,那么在涉及任何项目文件之前,首次请求中还包含什么?哪个命令行标志能移除哪部分?本文在 Claude Code 2.1.285(claude --version)上重新测量了这个基准——在空目录中——并用标志逐个拆解。
实验环境是 /tmp 下的一空目录。它没有 CLAUDE.md、没有 CLAUDE.local.md、没有 .mcp.json、没有 .claude/ 文件夹,也不是 git 仓库。Claude Code 在启动时从父目录加载 CLAUDE.md 文件,所以我们也向上遍历了直到 / 的每个父目录,均未发现这些文件。
但周围的机器并不空,这对以下所有测量都至关重要。用户设置启用了一个插件,这是一个带来 15 个工具和 21 个 skill 的 MCP 服务器。登录背后的 claude.ai 账户有四个连接器:两个已连接,共暴露 38 个工具;两个等待登录。另有 9 个 skill 从该账户同步而来。用户设置还将 auto 设为默认权限模式,并且开启了自动记忆。这些都不在实验目录里,但都出现在了首次请求中。
每次运行都使用相同的提示词和相同的基础标志,然后加上待测试的标志:
claude -p "Reply with just OK." --output-format stream-json --verbose \
--max-turns 1 --model opus --settings '{"disableAllHooks": true}'
--settings 覆盖在运行期间关闭了钩子,所以用户设置中的通知钩子保持静默。--model opus 固定了模型,实际解析为 Opus 5.5,因为相同的文本在不同分词器下计数不同。我们从另一个 Claude Code 会话内部启动运行,所以一个小包装器在调用 claude 之前移除了父会话导出的环境变量(如 CLAUDECODE)。没有这一步,子进程会知道它正运行在另一个会话之下。
我们在 2026-09-30 运行了十种配置,每种两次,共 20 次运行。每对中第二次运行的总数与第一次精确匹配,每次回复都是 OK。
下面每个表格中的数字都是首次请求的输入总量:从 usage 块中的 input_tokens 加上 cache_read_input_tokens 再加上 cache_creation_input_tokens。由于 --max-turns 1 和只返回一个单词的答案,每次运行恰好发出一条请求,所以流的 result 事件和 ~/.claude/projects/ 下记录中第一条助手消息携带了相同的三个数字。我们两者都读取了。总和如何在缓存读取和缓存写入之间分配,取决于之前的运行已经缓存了什么。但总和本身是不变的。
在 2.1.285 版中,记录单记录的内容远多于 usage。每段 Claude Code 围绕第一条用户消息添加的上下文都保存为附件记录,其 rendered 字段持有模型收到的文本,用 <system-reminder> 标签包裹。附件类型有可读的名称:environment、skill_listing、deferred_tools_delta、agent_listing_delta、mcp_instructions_delta 等等。还有一条 prompt_snapshot 记录,持有系统提示作为各部分的列表,加上随其 schema 一起发出的每个工具定义。CLI 参考文档解释了为什么会有这样的东西:"默认情况下,Claude Code 在会话的首次请求时构建一次系统提示,使用任何系统提示标志应用的文本,并将其记录在会话中。"我们读取的是那条记录,而非文档化的输出,它可能会发生变化。
所以请求的每个部分都可以按名称查看并以字符计量。记录单没有说明每个部分消耗了多少 token。为此,我们用标志移除各个部分,然后做减法。
流的 init 事件列出了默认会话已知的内容:78 个工具、5 个 MCP 服务器、48 个 skill 和 5 种 agent 类型。但并非所有内容都以完整形式发送。
11 个内置工具以完整定义发出:Agent、Bash、Edit、ListAgents、Read、ReportFindings、ScheduleWakeup、Skill、ToolSearch、Workflow 和 Write。仅这些工具的描述加起来就有 14,590 个字符,加上其输入 schema 序列化后达到 27,291。描述最长的两个是 Workflow(3,480 字符)和 ScheduleWakeup(3,148 字符)。另外 64 个工具——14 个内置和 50 个来自 MCP 服务器——只以名称形式发送,并附有一行提示,告诉 Claude 在调用前通过 ToolSearch 加载 schema。最后三个是同一提示公告为"ready to use"的连接器工具。这就涵盖了全部 78 个。
请求中的其他一切都是文本。以下是第一条用户消息携带的附件,按渲染文本的字符数计量:
系统提示本身有 6,659 个字符,分为 11 部分。快照还记录了此类运行的一行前缀:"You are a Claude agent, built on Anthropic's Claude Agent SDK." 最大的一部分是 2,200 字符的自动记忆部分,它告诉 Claude 此项目记忆目录的位置以及如何写入。
以字符计量,系统提示只是小部分。skill 列表本身就是它的近三倍,而十一个工具定义则更大。
我们按照在脚本中会采用的顺序移除各部分:首先移除账户的连接器,然后每个 MCP 服务器,然后两个最大列表背后的工具,然后除了文件和 shell 工具之外的一切,最后那些也移除。每行列出了在基础命令上添加的标志。

每个步骤都移除了可以命名的内容。关闭 claude.ai 连接器移除了它们 35 个延迟名称、三个完整加载的工具、两个未连接工具的登录提示,以及 2,053 字符的指令块:2,500 个 token。--strict-mcp-config 然后移除了插件的服务器,那是 15 个延迟名称,没有附加任何其他内容:391 个 token。禁用 Skill 和 Workflow 移除了两个定义、48 个 skill 的列表,以及系统提示中告诉 Claude 通过 Skill 调用 /<skill-name> 的那一行:9,155 个 token。将工具范围缩小到 Bash、Read、Edit 和 Write 移除了 Agent、ListAgents、ReportFindings、ScheduleWakeup 和 ToolSearch 的定义、14 个延迟内置名称,以及 agent 列表:5,490 个 token。--tools "" 移除了最后四个定义,以及 auto 模式说明和提交归属说明:3,007 个 token。
各步骤加起来:2,500 + 391 + 9,155 + 5,490 + 3,007 + 2,664 = 23,207。请将其读作穿过请求的一条路径,而非独立的价格。以另一顺序移除相同部分会得到不同的数字,下面的 Skill 部分展示了一种将 8,000 多个 token 从一个步骤移到另一个步骤的顺序。
--strict-mcp-config 这次移除了什么这正是代码词实验出现问题的地方,所以值得仔细看一下。CLI 参考文档用一句话描述了这个标志:"只使用来自 --mcp-config 的 MCP 服务器,忽略所有其他 MCP 配置。"我们根本没有传入 --mcp-config,所以会话只剩下没有 MCP 服务器的状态。四个连接器和插件的服务器都消失了,init 事件中什么都没有列出。
ENABLE_CLAUDEAI_MCP_SERVERS=false 的范围更窄。环境变量页面说明:"设为 false 以阻止 Claude Code 获取 claude.ai MCP 服务器。已登录用户默认启用。" 启用该选项后,四个连接器消失了,而插件的服务器保持连接,这就是我们能分开计费的原因。对于需要本地服务器但不应看到账户连接器的任务,这是那个开关。--strict-mcp-config 配合显式 --mcp-config 是另一条路径。
插件的 15 个名称花费了 391 个 token,平均每个名称约 26 个 token。它们向延迟列表增加了 735 个字符,所以这些名称的密度约为每 token 1.9 个字符。作为对比,本文后面用 --append-system-prompt 追加的纯英文达到了每 token 3.3 个字符。按每个名称 26 个 token 计算,密文运行中 786 token 的差距大约对应 30 个名称的成本,而这里两个已连接的连接器中恰好有一个暴露了 30 个工具。数字对得上,但这是算术推算,不是对旧运行的实测。
竞态回来了吗?在这些运行中没有。两次默认运行都携带了全部四个连接器,相同的两个已连接、相同的两个等待登录。在环境变量页面中 MCP_CONNECTION_NONBLOCKING 的条目下,说明写道:"在非交互模式(-p)且没有 --input-format stream-json 的情况下,Claude Code 也会等待仍处于 pending 状态的服务器,无论此变量如何设置。" 两次匹配的运行不能证明竞态已经消失。如果以后完全相同的运行再次出现几百 token 的差异,先比较两份转录的 deferred_tools_delta 附件。
禁用 Skill 节省了 913 个 token
技能列表是最大的单个附件,所以对于从不使用技能的脚本,显然可以把 Skill 工具去掉。CLI 参考文档对 --disallowedTools 的描述是:"拒绝规则。裸工具名称从 Claude 的上下文中移除匹配的工具:'Edit' 移除 Edit,'' 移除所有工具,'mcp__' 移除所有 MCP 工具。"
我们运行了 --strict-mcp-config --disallowedTools "Skill"。Skill 定义消失了,同样消失的还有 18,962 个字符的技能列表。第一次请求从 20,316 降至 19,403 个 token,共节省 913 个 token——这来自移除一个工具及其超过 20,000 字符的列表。
转录文件显示了剩余 token 的去向。当 Skill 可用时,Workflow 工具的描述包含这一行:

禁用 Skill 后,这一行被一个标题 # Workflow authoring reference 取代,后面跟着 133 行:参考文档本身被写入了工具描述中。描述从 3,480 增长到 20,256 个字符。随着技能列表离开的大部分内容,又以另一种形式回到了 Workflow 内部。只有同时禁用两个工具后,请求才降至 11,161 个 token,比 strict 基线低 9,155。
文档对指定工具的描述是准确的:Skill 确实离开了 Claude 的上下文。但它没有说明——我们也不会猜到——另一个工具的定义会围绕保留下来的工具被重建。我们从中得出的规则很简单:改变工具集后,重新测量,因为你没有动过的定义也可能发生变化。
--tools 把列表一并带走
CLI 参考文档对这一标志的描述是:"限制 Claude 可以使用的内置工具。使用 '' 禁用所有,使用 'default' 使用默认集合,或使用工具名称如 'Bash,Edit,Read'。" 它补充道:"该标志不影响 MCP 工具;要同时拒绝这些,请使用 --disallowedTools "mcp__*"。" 这就是为什么上面的每个 --tools 运行都同时携带了 --strict-mcp-config。
使用 --tools "Bash,Read,Edit,Write" 时,请求为 5,671 个 token:四个定义,没有列表。init 事件仍报告了 48 个技能和 5 个代理类型,但由于没有 Skill 工具也没有 Agent 工具,两个列表都没有发送。14 个延迟的内置名称也消失了,ToolSearch 也没有发送。
--tools "" 把请求降至 2,664 个 token,最后四个工具消失时,另外两个附件也跟着消失了:auto 模式说明和 commit 归属说明。在只允许 Bash、Read、Edit 和 Write 的运行中,它们仍然存在,所以每个都依赖这四个工具中的一个或多个。我们没有测试具体是哪一个。
2,664 这个数字代表没有任何工具标志能触及的部分:6,523 个字符的系统提示、环境块、模型行、日期、token 预算行、账户说明,以及 prompt 本身。这个底线有一个细节令我们意外。2,200 个字符的自动记忆部分仍然在系统提示中,尽管它告诉 Claude 使用 Write 工具保存记忆,而本会话根本没有 Write 工具。
在接受脚本的默认工具集之前,值得看一下两个最大的内置定义。在转录中,ScheduleWakeup 的描述开头是"调度何时恢复 /loop 动态模式下的工作",Workflow 则告诉 Claude"仅当用户明确选择多智能体编排时才能调用此工具"。一次性执行的 claude -p 调用(总结一个文件或修复一个 lint 错误)两者都不需要。除非工具列表另有说明,否则两者都会在会话的每个请求中完整发送。
哪些是你的,哪些是 Claude Code 的
两个标志删除的是自定义内容而不是工具,它们共同区分了这台机器添加的部分和 Claude Code 自身带来的部分。
CLI 参考文档将 --setting-sources 描述为"要加载的设置源逗号分隔列表(user, project, local)。" 本 lab 没有项目或本地设置,所以 --setting-sources project,local 只留下命令行中的 --settings 覆盖。配合 --strict-mcp-config 使用,请求从 20,316 降至 15,767 个 token,减少了 4,549 个。技能数量从 48 降至 18。插件的 21 个技能随插件一起消失,同步自 claude.ai 账户的 9 个技能也消失了,所以在这台机器上它们也是通过 user 源进入的。默认权限模式从 auto 回退到 default,auto 模式说明随之消失,技能列表从 18,962 缩减到 6,332 个字符。
--safe-mode 以不同方向切入。CLI 参考文档说它"在所有自定义功能禁用的情况下启动,用于排查损坏的配置:CLAUDE.md、skills、插件、hooks、MCP 服务器、自定义命令和代理、输出样式、workflows、自定义主题、自定义键绑定、状态栏和文件建议命令、LSP 服务器以及自动记忆都不加载。认证、模型选择、内置工具和权限正常工作,这与 --bare 不同。" 我们在不带 --strict-mcp-config 的情况下运行它,它仍然丢弃了全部五个 MCP 服务器,包括 claude.ai 连接器。它保留了与 --setting-sources project,local 相同的 18 个技能,并移除了自动记忆部分,所以系统提示从 6,659 降至 4,459 个字符。权限"正常工作",权限模式保持 auto。请求为 15,239 个 token,这是我们测量到的所有内置工具仍存在时的最低值。
所以在这台机器上,23,207 个 token 中大约 15,200 个来自 Claude Code 自身,约 8,000 个来自 --safe-mode 关闭的部分:在这里是连接器、一个插件、同步的技能和自动记忆。另一台机器会有不同的划分,两次 --safe-mode 运行是一种低成本的发现方式。
--bare 应该是自然的下一行,但我们无法运行它。无头页面的示例说"运行前设置 ANTHROPIC_API_KEY,因为 bare 模式不使用订阅登录",而这台机器只有订阅登录。
--append-system-prompt:953 个字符,285 个 token
最后一个标志是添加而不是移除。CLI 参考文档将其描述为"将自定义文本追加到默认系统提示末尾"。我们追加了一段 953 个字符、170 个词的 CI 规则:不提问、不推送、只编辑 src/ 和 test/、按名称报告失败的测试、回复以 PASS 或 FAIL 开头。在 --strict-mcp-config 之上,请求从 20,316 增至 20,601 个 token,增加了 285 个。
在快照中,这段文本成为系统提示的第十二个也是最后一个部分。已记录的默认提示的第二部分是行 __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__。CLI 参考文档描述了这个自定义提示的标记:Claude Code 在该行处拆分提示并移除它,"以便其上的部分保持缓存,而其下的部分会变化。" 追加的文本远在它下方。第二次运行读取了全部 20,599 个缓存 token,所以固定的追加文本像前缀的其余部分一样被缓存了。我们没有测试每次调用都会变化的文本。
我们会传递给脚本化运行的内容
以下是对于已知任务的 claude -p 调用,我们会根据以上数字做出的选择:
如果任务只需要文件和 shell,不需要其他,那么带上显式列表的 --tools 是最大幅度的单次削减,从 20,316 降至 5,671 tokens,因为技能(Skill)和智能体(Agent)列表会随它们所属的工具一起离开。
如果任务需要技能(Skill),就保留它,并预期列表会随着用户拥有的每个技能和插件而增长。--setting-sources project,local 可以将用户的插件和同步的技能排除在以此用户登录运行的作业之外。
不要仅仅为了节省 tokens 就禁用 Skill。在 2.1.285 版本上,它节省了 913 个 tokens。可以禁用 Skill 和 Workflow 的组合,或使用 --tools。
添加 --strict-mcp-config,如果任务需要服务器则配合显式的 --mcp-config 使用,这样账户的连接器就不会跟随作业进入 CI。ENABLE_CLAUDEAI_MCP_SERVERS=false 仅移除连接器。
前缀在每个请求上都要支付,而非每个会话只支付一次。在 total_cost_usd 中,一个从缓存读取整个前缀的重复调用的费用为:默认 23,207 tokens 时 $0.0047,2,664 tokens 底线时 $0.0006。第一次默认运行写入了 12,755 tokens 到缓存,费用为 $0.104。
以上所有数据均来自一台机器、一个账户、一个模型(Opus 5.5)和一个版本(2.1.285)。不同的模型对相同文本的计数不同,不同的账户会带来不同的连接器和同步技能。
实验目录不是 git 仓库,因此不涉及 git status。codewords 实验室是一个仓库,其不包含 CLAUDE.md 的严格基线在 2.1.273 上为 18,442 tokens,而这里是 20,316。两个实验室的差异不止版本,因此我们不能将 1,874 tokens 的差异归因于版本升级。
我们没有运行 --bare(需要 API key)。也没有运行 --system-prompt、--system-prompt-file、--exclude-dynamic-system-prompt-sections、--disallowedTools "mcp__*" 或 disableClaudeAiConnectors 设置,因为我们使用了环境变量代替。我们仅在 --strict-mcp-config 配合下运行了 --setting-sources,因此无法断言省略用户源是否也会将连接器拒之门外。
每种配置各运行了两次。codewords 实验室中出现的连接器竞态在两次默认运行中没有出现,但这不能证明它不可能发生。
每步的 token 计数来自一种移除路径。技能(Skill)结果显示各部分并非独立的,因此不同的顺序会将同样的 23,207 tokens 以不同方式拆分。
附件(attachment)和 prompt_snapshot 记录未在文档中记录。它们的名称和内容可能在任何版本中发生变化,上面每个字符计数都依赖于它们。
全部 20 次回复均为 OK。我们没有测试移除某个工具是否改变了 Claude 的能力范围,只是让它回答一个词。20 次运行合计在 total_cost_usd 中报告为 $0.60。
LAB=$(mktemp -d) && cd "$LAB"
probe() { # probe <name> [flags under test...]
local name=$1; shift
claude -p "Reply with just OK." --output-format stream-json --verbose \
--max-turns 1 --model opus --settings '{"disableAllHooks": true}' "$@" \
> "$name.jsonl" < /dev/null
}
first_in() {
for r in "$@"; do
printf '%-8s %6s\n' "$r" "$(jq -r 'select(.type=="result") | .usage
| .input_tokens + .cache_read_input_tokens + .cache_creation_input_tokens' "$r.jsonl")"
done
}
probe default
probe strict --strict-mcp-config
probe tools4 --strict-mcp-config --tools "Bash,Read,Edit,Write"
probe tools0 --strict-mcp-config --tools ""
probe noskill --strict-mcp-config --disallowedTools "Skill"
probe noskillwf --strict-mcp-config --disallowedTools "Skill" "Workflow"
first_in default strict tools4 tools0 noskill noskillwf
要查看各部分内容,打开一次运行的转录(transcript),列出其附件和记录的工具描述:
sid=$(jq -r 'select(.type=="result") | .session_id' default.jsonl)
t=$(ls ~/.claude/projects/*/"$sid".jsonl)
jq -r 'select(.type=="attachment" and .rendered != null)
| [.attachment.type, (.rendered | map(.content // "") | join("") | length)] | @tsv' "$t"
jq -r 'select(.type=="attachment" and .attachment.type=="prompt_snapshot" and .attachment.tools != null)
| .attachment | "system prompt: \(.systemPrompt | map(length) | add)",
(.tools[] | "\(.name)\t\(.schema.description | length)")' "$t"
如果你从另一个 Claude Code 会话内部启动这些运行,先取消设置它导出的会话变量,如上所述。你的总数不会与我们的匹配,因为它们取决于你的插件、连接器和技能。但整体形状应该看起来很熟悉:工具及它们携带的列表占据了第一次请求的大部分,而 --tools 是移动它们的标志。
Rulestack 为 Claude Code 生产规则文件、技能和钩子(hooks),销售地址为 rulestack.gumroad.com。本文中的测量仅需要 claude、jq 和一个空目录,因此每次 Claude Code 升级后都很容易重复。
如果你的空会话以不同方式拆分,请在下方评论中发布你的总数,并关注 @ai-shop.bsky.social 获取更多类似的第一次请求测量数据。