SSD Nodes Learn 🎉 VPS $4.99/月起
指南 Matt Connor作者: Matt Connor

Agent Skills、MCP 服务器与规则文件怎么选?

比较 Agent Skill、MCP 服务器和规则文件的加载时机、Token 成本与维护代价,解释 Claude Code 工具搜索为何改变 MCP 的开销,并给出选择规则。

Agent skills、MCP 服务器与规则文件:简要结论

Agent skills、MCP 服务器和规则文件都可以向编码代理提供知识。应根据知识的作用来选择。MCP(模型上下文协议)适用于下次查看时可能发生变化的数据。Skill 适用于今天可以写下,并且六周后仍然正确的操作流程。规则文件适用于每个会话都必须遵守的少量事实。

这种选择有代价,代价就是上下文。代理不需要的指令每占用一个 token,留给它读取代码的 token 就少一个。每一轮请求都会再次支付这些 token,因为每次请求都会重新发送整个上下文窗口。因此,真正有用的问题不是哪种机制能够完成任务。大多数时候,三种机制都可以。问题是,哪种机制在闲置时占用的成本最低。

使用前先了解每项功能的成本

三者在不同时间加载,而加载时机决定了全部差异。

规则文件会在启动时完整加载,并在每次会话中加载,无论当前是否相关。Claude Code 会在每次对话开始时读取 CLAUDE.md,并完整加载其中内容,不受文件长度影响。文档建议每个文件不超过 200 行,因为更长的文件会占用更多上下文,并且更难被可靠遵循。这两种影响方向一致,因此 900 行的规则文件不但没有帮助,反而更糟。

技能分两个阶段加载。启动时,每个 SKILL.md frontmatter 中只有 description 行会进入上下文,因此模型知道该技能存在,并大致了解其适用时机。调用技能后才会加载正文。因此,400 行的参考文档在真正需要之前几乎不会产生开销。

MCP 服务器过去是开销最大的一项,这也是现在许多比较文章已经过时的原因。当前 Claude Code 默认启用工具搜索。会话开始时只加载工具名称和服务器的 instructions 字段,完整的 JSON(JavaScript 对象表示法)架构会延迟到 Claude 搜索这些工具时才加载。添加服务器不再需要预先占用数千个 token。但它仍然会产生开销;在关闭工具搜索的配置中,它仍会在启动时产生全部开销。

ChartStartup and post-use context cost, estimated tokens
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 的 instructions 块。Claude Code 会将每个工具描述和每个服务器 instructions 字段截断为 2 KB,因此这部分有固定上限。下一节将说明如何读取您自己的实际数据。

请结合查看前两行。在没有人需要该规则文件的会话中,它会占用 2,500 个 token。技能在同一会话中占用 40 个 token;在十个会话中有一个调用该技能的情况下,则占用 3,000 个 token。最后两行是同一个服务器的两种情况,分别是启用和关闭工具搜索:500 个 token,对比 4,500 个 token。差值正是旧有 MCP 上下文膨胀建议仍在流传的原因。

工具搜索需要支持 tool_reference 块的模型。截至 August 2026,这意味着 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

决定性问题是:数据在不同调用之间是否会变化?

先问这个问题,因为它可以直接排除一种选项。如果代理需要读取或写入下次查看时可能已经不同的内容,就需要使用服务器。例如,问题跟踪系统、数据库、监控面板或您自己的内部 API(应用程序编程接口)。把内容记录下来没有帮助,因为其他人编辑记录后,您写下的内容就会立即过时。

如果六周后没有人维护,答案仍然正确,那么您需要的是技能。发布检查清单、迁移流程、错误响应的格式、此仓库要求如何编写测试。技能是 git 中的文件。它不占用端口,不运行进程,除了内容错误外没有其他故障模式;代码审查可以发现这类错误。

如果这是一个必须适用于您尚未考虑的工作的事实,就应将其写入规则文件。 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,确认 Memory files 下出现 CLAUDE.md。如果这里没有列出该文件,代理就从未读取过它,重新措辞也不会有帮助。如果还需要添加 Claude 专用内容,请改用导入形式,并将这些内容放在导入语句下方。

@AGENTS.md

## Claude Code

Use plan mode for changes under `src/billing/`.

这里有一个容易踩的坑。@path 导入不会节省上下文。导入的文件会在启动时展开,并与引用它的文件一起加载,最多深入四层。将一个 600 行的规则文件拆成六个导入文件,只会方便人工阅读,token 成本完全不会改变。确定布局前,建议阅读 AGENTS.md 及其面向人类的对应文件所遵循的约定

真正能降低成本的是带有 paths 字段的 .claude/rules/。包含 paths frontmatter 的规则文件,只有在代理处理与其中某个模式匹配的文件时才会加载。

---
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,会随代码仓库一起维护,也可以像其他文件一样在拉取请求中审核。

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 个令牌纳入总计 25,000 个令牌的预算。若在一次会话中调用多个大型技能,最早调用的技能会被完全丢弃。因此,长时间对话后,某个技能可能看起来不再生效。再次调用该技能即可恢复。若同一过程适用于多个代码库,请在多个代码仓库之间共享一个技能,不要复制文件。

需要 MCP 服务器时

添加 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 notion

claude mcp add 会显示一行 Added ...,但这只能说明配置已写入磁盘。claude mcp list 才能反映实际状态,因为它会在每个服务器旁显示健康状态:✔ Connected! Needs authentication✘ Failed to connect。状态为失败表示 Claude Code 无法连接到该服务器,而不是列表命令执行失败。在会话中,/mcp 会按服务器提供相同视图,并额外显示工具数量。

每次调用 MCP 服务器都是独立的,并携带所需的全部内容,这就是 MCP 服务器不会记住您上一次请求的原因。这是一个设计选择,但由此产生的后果需要您承担:任何需要保留的状态都必须存放在服务器后端的数据库或文件中,而这也成为您需要运维的一部分。

MCP 服务器是需要运行的进程

供应商对比中遗漏了这项成本。技能是一个文件。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.target
sudo systemctl daemon-reload
sudo systemctl enable --now notes-mcp
systemctl is-active notes-mcp
journalctl -u notes-mcp -n 50 --no-pager

systemctl is-active 应输出 active。如果输出 failed,原因会记录在 journal 中;首次运行时,几乎总是因为缺少环境变量,或端口已被其他程序占用。这里 Restart=on-failure 不是可选项,因为崩溃的 MCP 服务器不会主动报告故障。通常要等代理告诉您它无法读取 issue tracker,您才会发现问题。

将进程绑定到 127.0.0.1,并在其前面配置带 TLS(传输层安全)的反向代理。能够访问数据库、却在没有身份验证的情况下通过公网端口响应请求的 MCP 服务器,相当于将数据库公开到互联网。在 VPS 上运行 MCP 服务器详细介绍了代理、证书和防火墙的正确配置方式。

然后如实计算持续维护工作。该服务会按照自己的计划获取安全更新,与连接它的代理无关。它的 OAuth 令牌会过期,而 claude mcp list 可能在不合适的时间开始输出 ! Needs authentication。其凭据存放在配置文件或 Authorization 标头中,因此需要像其他机密信息一样妥善保护。这本身就是一个完整主题:防止 AI 代理接触机密信息。技能不需要这些工作。

在构建之前,先将它与替代方案进行比较。如果拟议服务器背后的数据大约每季度才变化一次,那么使用技能告诉代理应查询的位置以及字段含义,成本会低于维护一个必须持续运行的服务。

如何衡量您自己的上下文成本

不要再估算,直接在会话中运行 /context。它会输出启动开销明细:系统提示、内存文件、工具和 MCP 服务器,以及每项占用的 token 数。

检查两点。在 Memory files 下,确认列出了您预期的每个规则文件。代理无法看到缺失的文件,因此当指令被忽略时,首先应排除这一原因。然后查看各服务器的开销。如果您每月只使用两次的服务器在列表中占用的 token 数却位居前列,请在 /mcp 中将其关闭,并在需要它的会话中重新启用。无论是否启用,配置都会保留。

远程服务器还可能报告 cached 2h ago · connects on first use · 5 tools 之类的状态。这表示 Claude Code 使用了上一个会话中的工具列表,而不是在启动时连接服务器;首次调用工具时,它才会建立连接。工具从您的第一条消息开始就可用,因此无需修复任何问题。如果您希望每个服务器都在启动时连接,请设置 MCP_DISCOVERY_CACHE=0。如需了解整体情况,请参阅管理 Claude Code 上下文窗口,其中介绍压缩后哪些内容仍会保留;这些 token 实际会产生多少成本则会将这些数字换算成金额。

为什么我的技能从未触发?

通常原因是 description。技能运行前,上下文中只有这段文本,因此如果它没有说明具体场景,就不会匹配。请将触发条件直接写入句子中:“当用户询问发生了什么变化、需要提交消息,或要求审查其 diff 时使用。”含糊的描述会静默失败,因此很难发现问题。

第二个原因是 frontmatter 拼写错误,这种错误会直接报错。未知键会被立即拒绝:

Unexpected key(s) in SKILL.md frontmatter: argument-hint. Allowed properties are: allowed-tools, compatibility, description, license, metadata, name

第三个原因是位置。项目技能会从工作目录中的 .claude/skills/ 以及工作目录到仓库根目录之间的所有父目录加载。启动时不会加载起始目录下方嵌套目录中的技能。只有当代理首次读取或编辑该子目录中的文件时,这些技能才会出现。因此在此之前,它们不会出现在自动补全中,也无法按名称调用。

MCP 中与此类似的静默失败,是存在一个带有 url 但没有 type.mcp.json 条目。Claude Code 会将任何不含 type 的条目当作 stdio 服务器,因此会跳过该条目并报告:

MCP server "notes" has a "url" but no "type"; add "type": "http" (or "sse" / "ws") to this entry

三者结合使用

这三种机制并不争用同一个位置。有效的配置会在成本最低的地方使用每种机制。规则文件只保留少量在所有场景下都成立的内容。技能保存操作流程,仅在适用时加载。一个 MCP 服务器,偶尔两个,连接那些内容无法预先确定的系统。如果您还在建立对第一个机制的理解,代理技能究竟是什么会详细介绍其格式。

有一个测试可以解决大多数归属问题。删除相关内容,启动一个全新的会话,然后将任务交给代理。如果代理只是变慢了,相关内容就应该放在技能中。如果代理自信地给出了错误结果,相关内容就应该放在规则文件中。如果代理完全无法获取这些信息,您就需要服务器,同时还需要制定保持该服务器运行的方案。

FAQ

我应该编写 skill,还是部署 MCP 服务器?

根据信息是否会在两次调用之间发生变化来决定。如果代理必须读取其他人可以修改的实时状态,例如 issue 跟踪器、数据库或仪表板,就需要 MCP 服务器,因为记录一旦发生变化,预先写下的任何内容都会过时。如果答案写下来后,六周内仍然正确,就编写 skill。skill 是 git 中的一个文件,不需要运行进程、暴露端口或安排补丁,因此只要可行,它就是成本更低的选项。

MCP 服务器还会填满我的上下文窗口吗?

现在少得多。当前版本的 Claude Code 默认启用工具搜索,因此会话开始时只加载工具名称和服务器的 instructions 字段;Claude 搜索工具时才会获取完整 schema。在以下情况下,仍会预先加载:禁用工具搜索时;使用 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 创建普通符号链接,或者将 @AGENTS.md 放在 CLAUDE.md 的第一行,并在其下方添加 Claude 专用说明。然后启动会话并运行 /context,确认 CLAUDE.md 是否显示在 Memory files 下。

为什么我的 skill 在会话进行到一半时不再生效?

通常是自动压缩导致的。对话经过摘要后,Claude Code 会重新附加每个 skill 最近一次调用的内容,并保留每个 skill 的前 5,000 个 token;所有 skill 合计预算为 25,000 个 token。它会从最近调用的 skill 开始填充预算,因此如果调用过多个较大的 skill,较早的 skill 会被完全丢弃。再次调用该 skill,即可恢复其完整内容。

如何阻止较长的规则文件在每个会话中加载?

将只在特定情况下需要的部分移入 .claude/rules/ 文件,并在其 frontmatter 中添加 paths 字段。这样,只有代理访问匹配的文件时,相关内容才会加载。将文件拆分为 @path 导入并无帮助,因为导入文件会在启动时展开并加载,同时加载引用它们的文件。凡是多步骤流程,而不是持续适用的事实,都应改为 skill,因为 skill 正文只有在调用时才会产生开销。