Agent技能、MCP服务器与规则文件:如何选择?
编码Agent上下文配置指南。对比规则文件、技能与MCP服务器的加载机制与Token成本。了解为何规则文件应限制在200行以内,以及如何通过延迟加载优化Claude Code的上下文窗口占用,避免不必要的性能损耗。
Agent 技能、MCP 服务器与规则文件的简要对比
Agent 技能、MCP 服务器和规则文件均用于向编码 Agent 提供知识。请根据知识的用途进行选择。MCP (Model Context Protocol) 适用于每次查看时可能发生变化的数据。技能适用于即便在 6 周后依然有效的既定流程。规则文件则适用于在每次会话中必须遵循的少量事实。
选择这些机制需要付出代价,即上下文窗口的消耗。Agent 不需要但被强制读取的指令每消耗一个 token,用于读取代码的 token 就会减少。由于每次请求都会重新发送整个上下文窗口,这些 token 在每一轮对话中都会被重复计算。因此,真正的问题不在于哪种机制“能够”完成任务(通常三者都能),而在于哪种机制在闲置时占用的成本最低。
使用前的各项成本
这三者的加载时机各不相同,而这种时机差异正是成本区别的根本所在。
规则文件在每次会话启动时会完整加载,无论其内容是否相关。Claude Code 会在每次对话开始时读取 CLAUDE.md,并忽略长度限制将其全部加载。建议的目标是每个文件不超过 200 行,因为文件过长不仅会消耗更多上下文,且模型对其遵循的可靠性也会下降。这两个因素的影响方向一致,这就是为什么 900 行的规则文件不仅无用,反而有害。
技能分两个阶段加载。启动时,仅每个 SKILL.md 前置元数据中的 description 行进入上下文,以便模型了解该技能的存在及其大致适用场景。技能主体仅在调用时加载。因此,一个 400 行的参考文档在被调用前几乎不产生任何成本。
MCP server 曾经是成本最高的选项,这也是目前大多数相关文章观点过时的原因。在当前的 Claude Code 中,工具搜索功能默认开启。会话开始时仅加载工具名称和服务器的指令字段,完整的 JSON (JavaScript object notation) 模式则推迟到 Claude 搜索它们时才加载。添加服务器不再需要预先消耗数千个 token。它仍会产生一定成本,且在关闭工具搜索的配置中,它仍会预先加载所有内容。
The data behind this chart
[
{
"label": "Rules file, 200 lines",
"at_startup": "2,500",
"after_use": "2,500"
},
{
"label": "Skill, 12 KB body",
"at_startup": 40,
"after_use": "3,000"
},
{
"label": "MCP server, tool search on",
"at_startup": 500,
"after_use": "3,200"
},
{
"label": "MCP server, tool search off",
"at_startup": "4,500",
"after_use": "4,500"
}
]以上均为估算值,而非您机器上的实际测量值。这些估算基于各机制加载的文本大小,按每个 token 约 4 个字符计算:一个 200 行的规则文件约为 10 KB 的 markdown,一个技能描述约为 160 个字符,而一个提供 12 个工具的服务器包含约 18 KB 的模式和 2 KB 的指令块。Claude Code 会将每个工具描述和服务器指令字段截断在 2 KB,因此该部分有上限。下一节将介绍如何读取您自己的实际数据。
请结合前两行阅读。在无人需要的情况下,规则文件在会话中消耗 2,500 个 token。同一会话中,技能消耗 40 个 token;而在十分之一的触发会话中,其消耗为 3,000 个 token。最后两行是同一个服务器在开启和关闭工具搜索时的对比:500 个 token 对比 4,500 个 token。这种差距正是关于 MCP 上下文膨胀的旧建议至今仍在流传的原因。
工具搜索需要支持 tool_reference 块的模型,截至 2026 年 8 月,这意味着 Claude Sonnet 4.5、Haiku 4.5、Opus 4.5 及更高版本。当 ANTHROPIC_BASE_URL 指向非第一方主机时,Claude Code 会关闭该功能,因为大多数代理不会转发这些块。通过设置 ENABLE_TOOL_SEARCH 进行控制:false 会预先加载所有模式,true 会推迟所有模式的加载,而 auto 仅在模式大小不超过上下文窗口 10% 时才预先加载。
# Load schemas up front only if they fit in 5% of the window
ENABLE_TOOL_SEARCH=auto:5 claude决定性问题:数据在多次调用之间会发生变化吗?
首先询问这个问题,因为它能直接排除一种选项。如果 Agent 需要读取或写入的内容在下次查看时可能会有所不同,那么你需要一个服务器。例如问题跟踪器、数据库、监控仪表板或你自己的内部 API(应用程序编程接口)。将数据写下来没有帮助,因为一旦有人编辑了记录,你写下的内容就过时了。你自己的代码库也属于此类,因为它的结构会随着每次提交而变动,这就是为什么建议 通过 MCP 向 Agent 提供解析后的代码库映射,而不是用一个会过时的文件来描述布局。
如果该内容在六周后无人维护时依然正确,那么你需要的是一个技能(skill)。例如发布检查清单、迁移流程、错误响应的格式,或者该代码库要求的测试编写方式。技能就是一个 git 中的文件。它没有端口,没有进程,除了内容错误(可以通过代码审查发现)之外,没有其他故障模式。
如果某个事实必须适用于你尚未考虑到的工作,请将其放入规则文件(rules file)中。Run make lint before committing. Never push to main. Handlers live in src/api/handlers/. 每行一条。一旦条目扩展为多个步骤,它就不再是事实,而变成了流程,此时应将其移至技能中。
何时使用规则文件即可
规则文件会从多个位置加载,优先级从最宽泛到最具体依次为:托管策略文件、您的个人 ~/.claude/CLAUDE.md、项目的 ./CLAUDE.md 或 ./.claude/CLAUDE.md,以及被 git 忽略的 ./CLAUDE.local.md。所有发现的文件会进行拼接而非相互覆盖,且越靠近您当前工作目录的文件读取优先级越高。
Claude Code 读取 CLAUDE.md,而非 AGENTS.md。如果您的仓库中已经为其他工具维护了 AGENTS.md,请勿维护两份会导致配置偏离的副本。
ln -s AGENTS.md CLAUDE.md符号链接在成功时不会输出任何内容。启动会话,运行 /context,并确认 CLAUDE.md 出现在 Memory files 下。如果此处未列出,说明代理从未识别到该文件,任何措辞调整都无效。如果您还需要 Claude 特有的规则行,请使用导入形式并将它们放在导入语句下方。
@AGENTS.md
## Claude Code
Use plan mode for changes under `src/billing/`.这里有一个陷阱。@path 导入不会保存上下文。导入的文件会在启动时与引用它的文件一起展开并加载,最多支持四层嵌套。将一个 600 行的规则文件拆分为六个导入文件可以方便人类阅读,但对 token 成本没有任何影响。在确定布局之前,值得阅读 AGENTS.md 及其面向人类的对应文件的约定。
真正能降低成本的是带有 paths 字段的 .claude/rules/。带有 paths 元数据的规则文件仅在代理触及匹配其中模式的文件时才会加载。
---
paths:
- "src/api/**/*.ts"
---
# API rules
- Every endpoint validates its input.
- Use the standard error response shape.没有 paths 字段的规则会在启动时加载,其优先级与 .claude/CLAUDE.md 相同。因此,推荐的工作模式是:简短的无条件规则,加上针对仅在特定目录内生效内容的 paths 列表。
当您需要一项技能时
技能是一个包含 SKILL.md 的目录。个人技能位于 ~/.claude/skills/<name>/SKILL.md,适用于您机器上的所有项目。项目技能位于 .claude/skills/<name>/SKILL.md,随代码仓库一同分发,并可像其他文件一样在拉取请求(pull request)中进行审查。
mkdir -p ~/.claude/skills/summarize-changes---
name: summarize-changes
description: Summarizes uncommitted changes and flags anything risky. Use when the user asks what changed, wants a commit message, or asks to review their diff.
---
Run `git status` and `git diff` against the merge base.
Group the changes by intent, not by file.
Call out anything touching auth, migrations or deletions.description 是该文件中唯一在技能运行前处于上下文的部分,因此它承担两项任务。它既说明了技能的功能,也定义了何时调用该技能。如果描述写为“有助于部署”,模型将无法将其与请求进行匹配,导致技能悄无声息地从未触发,您会因此认为技能无法工作。
目录名称即为命令,因此上述示例为您提供了 /summarize-changes。在个人或项目技能中,Frontmatter 中的 name 仅用于设置列表中的显示标签。
技能一旦被调用,其渲染后的内容就会作为一条消息进入对话,并在整个会话期间保留。Claude Code 不会在后续轮次重新读取该文件。应编写长期生效的指令,而不是只执行一次的步骤,并尽量精简正文,因为从此以后,每一行内容都会在每次请求中重复占用上下文。自动压缩后,Claude Code 会重新附加每个技能最近一次调用的内容,并将每个技能的前 5,000 个 token 纳入 25,000 token 的总预算。如果在一个会话中调用多个大型技能,最早调用的技能会被完全移除。因此,长时间对话后,某个技能可能看起来不再生效。再次调用该技能即可恢复。
流程较多的技能能具体体现这种权衡:unlazy 技能及其 Depth Tree 方法会使用实际的上下文空间来执行检查门槛并维护计划文件,从而让代理不再过早宣布工作已完成。当同一流程适用于多个代码库时,应在多个代码库之间共享同一个技能,而不是复制文件。
何时需要 MCP 服务器
添加服务器只需一条命令,其运行形式由传输方式决定。
# Remote HTTP server
claude mcp add --transport http notion https://mcp.notion.com/mcp
# Remote HTTP server behind a bearer token
claude mcp add --transport http secure-api https://api.example.com/mcp \
--header "Authorization: Bearer your-token"
# Local stdio server: everything after -- is passed through untouched
claude mcp add --env AIRTABLE_API_KEY=YOUR_KEY --transport stdio airtable \
-- npx -y airtable-mcp-server-- 非常重要。对于 stdio 服务器,它用于区分 Claude Code 自身的选项与启动服务器的命令行参数。如果省略该分隔符,原本发给服务器的 --port 8080 会被解析为 claude mcp add 的选项,导致解析失败。
claude mcp list
claude mcp get notionclaude mcp add 会通过 Added ... 行进行确认,这仅表示配置已写入磁盘。claude mcp list 命令才是判断真实状态的依据,它会在每个服务器旁显示健康状态:✔ Connected、! Needs authentication 或 ✘ Failed to connect。状态显示为失败意味着 Claude Code 无法连接到该服务器,而非 list 命令本身出错。在会话内部,/mcp 可查看每个服务器的相同状态及工具数量。
每次对 MCP 服务器的调用都是独立的,并携带所需的所有信息,这正是 MCP 服务器不记录上一次请求的原因。这是一种设计选择,但也带来了一个必须承担的后果:任何需要保留的状态都必须存储在服务器后端,即数据库或文件中,而这正是你现在需要维护的内容。
MCP 服务器是一个必须运行的进程
这是供应商对比中忽略的成本。Skill 是一个文件。MCP 服务器是运行在某处的软件,当这个“某处”是你的 VPS(虚拟专用服务器)时,你需要负责它的正常运行时间。
stdio 服务器属于低成本情况。Claude Code 在会话开始时将其作为子进程启动,并在会话结束时将其终止。无需监控,也无需按独立计划进行补丁更新。远程 HTTP 服务器则是一个长驻服务,它需要任何长驻服务所必需的维护。
[Unit]
Description=Notes MCP server
After=network-online.target
Wants=network-online.target
[Service]
User=mcp
WorkingDirectory=/srv/notes-mcp
ExecStart=/usr/bin/node /srv/notes-mcp/dist/server.js
Environment=PORT=8931
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now notes-mcp
systemctl is-active notes-mcp
journalctl -u notes-mcp -n 50 --no-pagersystemctl is-active 应该输出 active。如果它输出 failed,日志中会记录原因;在首次运行时,这通常是因为缺少环境变量或端口已被其他进程占用。Restart=on-failure 在此并非可选项,因为崩溃的 MCP 服务器不会主动通知。你只有在 Agent 告诉你无法读取问题追踪器时才会发现。
将进程绑定到 127.0.0.1,并在其前端部署带有 TLS(传输层安全协议)的反向代理。如果一个能访问你数据库的 MCP 服务器在公网端口上响应且没有身份验证,就等于你公开了数据库。在 VPS 上运行 MCP 服务器 详细介绍了代理、证书和防火墙的正确配置。
然后,请客观评估后续的维护工作。该服务有其独立的安全性更新计划,与调用它的 Agent 无关。当 OAuth 令牌过期时,claude mcp list 会在不合时宜的时刻开始输出 ! Needs authentication。其凭据存储在配置文件或 Authorization 头信息中,因此需要像对待其他密钥一样妥善保管,这是一个独立的话题:防止 AI Agent 获取密钥。对于 Skill 而言,这些维护工作都不存在。
在构建之前,请权衡替代方案。如果服务器背后的数据每季度才变动一次,那么编写一个告诉 Agent 去哪里查找数据及其字段含义的 Skill,要比维护一个必须保持在线的服务成本更低。
如何衡量您的上下文成本
停止估算,直接在会话中运行 /context。它会打印启动明细:系统提示词、内存文件、工具以及 MCP 服务器,并显示每个部分的 Token 权重。
请检查两点。在 Memory files 下,确认您预期的每个规则文件都已列出。如果缺少文件,Agent 将无法感知,因此当指令被忽略时,首先要排除的就是这种情况。如果文件已列出但规则仍被跳过,则原因完全在别处,在重写该行之前,值得先排查 Agent 忽略可见指令的原因。接着查看服务器的成本。如果您每月仅使用两次的服务器在列表中占据了最大的份额,请在 /mcp 中将其关闭,仅在需要时开启。无论哪种方式,配置都会被保留。
远程服务器也可能报告 cached 2h ago · connects on first use · 5 tools 状态。这意味着 Claude Code 读取的是之前会话的工具列表,而非在启动时进行连接;它会在首次调用工具时进行连接。由于工具从您的第一条消息起就可用,因此无需修复。如果您希望每个服务器都在启动时连接,请设置 MCP_DISCOVERY_CACHE=0。若要了解更全面的信息,管理 Claude Code 上下文窗口 涵盖了压缩后保留的内容,而 这些 Token 的实际成本 则会将数值转化为具体的费用。
为什么我的技能从未触发?
常见原因是 description。这是技能运行前上下文中唯一的文本,如果它没有描述当前情况,则无法匹配。请将触发条件写入句子中,例如:“当用户询问发生了什么变化、需要提交信息或要求审查差异时使用。” 模糊的描述会静默失败,导致问题难以察觉。
第二个原因是 frontmatter 拼写错误,这种错误通常会报错。未知的键会被直接拒绝:
Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, name第三个原因是位置问题。项目技能会从工作目录及其直至仓库根目录的每一级父目录中的 .claude/skills/ 加载。启动时,位于当前工作目录“下方”嵌套目录中的技能不会被加载。只有当代理首次读取或编辑该子目录中的文件时,这些技能才会出现;在此之前,它们不会自动补全,也无法通过名称调用。
MCP 中导致此类静默失败的原因是 .mcp.json 条目中包含 url 但缺少 type。Claude Code 会将任何没有 type 的条目视为 stdio 服务器,因此它会跳过该条目并报告:
MCP server "notes" has a "url" but no "type"; add "type": "http" (or "sse" / "ws") to this entry三者协同使用
这些机制并非互斥。高效的配置方案会在各自最适用的场景下使用它们。规则文件(rules file)仅包含少量适用于全局的通用准则。技能(skills)包含具体的操作流程,且仅在适用时加载。MCP 服务器(通常配置一个,偶尔两个)用于连接那些内容不可预知的系统。如果您仍在构建关于前者的认知模型,代理技能的本质 一文详细介绍了其格式。
通过一项测试即可解决关于功能归属的大多数争议:删除该功能,开启一个新会话,并向代理下达任务。如果代理只是执行变慢,说明它属于技能;如果代理表现得自信但结果错误,说明它属于规则文件;如果代理完全无法获取信息,说明您需要一个服务器,且此时还需制定维护该服务器运行的方案。
FAQ
我应该编写技能(skill)还是搭建 MCP 服务器?
取决于信息在两次调用之间是否会发生变化。如果代理必须读取他人可编辑的实时状态(如问题追踪器、数据库或仪表板),则需要 MCP 服务器,因为一旦记录发生变更,你写下的任何内容都会立即失效。如果一个答案写下后在六周内依然准确,请编写技能。技能只是 git 仓库中的一个文件,无需运行进程、无需暴露端口、无需维护补丁,因此在任何可行的情况下,它都是成本更低的方案。
MCP 服务器还会占满我的上下文窗口吗?
比以前少得多。当前的 Claude Code 默认启用工具搜索,因此会话开始时仅加载工具名称和服务器的指令字段,完整的 schema 仅在 Claude 搜索时才会获取。在关闭工具搜索时,仍会发生预加载:使用 ENABLE_TOOL_SEARCH=false 时、将 ANTHROPIC_BASE_URL 指向非官方代理时,或使用早于 Claude 4.5 代的模型时。运行 /context 查看你当前所处的情况,因为旧的对比文章中的数据假设的是预加载模式。
Claude Code 会读取 AGENTS.md 吗?
不会。Claude Code 读取 CLAUDE.md。如果你的仓库已经为其他代理准备了 AGENTS.md,请将其中一个指向另一个,而不是维护两份副本。运行 ln -s AGENTS.md CLAUDE.md 创建一个简单的符号链接,或者在 CLAUDE.md 的第一行写入 @AGENTS.md,并在下方添加 Claude 专属指令。随后启动会话并运行 /context,确认 CLAUDE.md 出现在 Memory files 下方。
为什么我的技能在会话进行到一半时失效了?
通常是因为自动压缩(auto-compaction)。当对话被总结时,Claude Code 会重新附加每个技能的最近一次调用,保留每个技能的前 5000 个 token,所有技能的总配额为 25000 个 token。该配额从最近调用的技能开始填充,因此如果你调用了多个大型技能,较旧的技能会被完全丢弃。再次调用该技能即可恢复其全部内容。
如何阻止长规则文件在每个会话中加载?
将仅在特定情况下生效的部分移至 .claude/rules/ 文件中,并在其元数据(frontmatter)中设置 paths 字段,这样只有当代理触及匹配文件时,该部分才会加载。将文件拆分为 @path 导入并无帮助,因为导入的文件会在启动时与引用它的文件一起展开并加载。任何多步骤流程而非固定事实的内容,都应转换为技能,因为技能主体在被调用前不会占用任何资源。