绝大多数 Claude Code 的 MCP 教程都只教一件事:用 @mcp.tool() 暴露一个函数给 AI 调用。但 MCP 协议其实定义了三大原语——Tools(工具)、Resources(资源)、Prompts(提示词)。本教程聚焦被严重低估的 Resources 与 Prompts,带你搞清楚三者本质区别、控制层级的差异,并通过一个可运行的"团队周报知识库 MCP"实战项目,掌握何时该用工具、何时该用资源、何时该用提示词。适合已经能写出基本 Tool 类 MCP、想进阶理解协议全貌的开发者。
claude mcp add 接入过至少一个 MCP 服务器async/await 基本语法打开任何一篇"如何写第一个 MCP 服务器"的教程,你大概率会看到这样的代码:
@mcp.tool()
def get_weather(city: str) -> str:
return f"{city} 今天晴"
这给你一个错觉:MCP = 给 AI 一堆可调用的函数。这只说对了三分之一。
MCP(Model Context Protocol,模型上下文协议)定义的服务器能力其实分三类,每类的控制方不同,这是整个协议最精妙的设计——也是新手最容易忽视的地方:
| 原语 | 英文 | 控制方 | 本质 | 典型用途 |
|---|---|---|---|---|
| 工具 | Tools | 模型(AI) | 可执行的动词,有副作用 | 查数据库、发请求、写文件 |
| 资源 | Resources | 应用 / 用户 | 只读的名词,提供上下文 | 暴露文件、配置、文档全文 |
| 提示词 | Prompts | 用户 | 结构化交互模板 | 代码评审、周报复盘等固定流程 |
关键洞察:"谁决定用它"决定了它应该设计成哪一类原语。
这个三方划分不是为了炫技,而是为了权限边界:你不希望 AI 自己决定把整个数据库 dump 进上下文(那应该用 Resource 由你控制),也不希望它自动执行写操作而不经你确认(Tool 的副作用需要审慎)。
flowchart LR
U([用户]) -->|"选用 Prompt"| P[Prompt 模板]
U -->|"挂载 Resource"| R[Resource 数据]
CC[Claude Code 宿主]
R --> CC
AI[AI 模型] -->|"自主调用"| T[Tool 工具]
T -->|执行/副作用| W[(外部系统)]
CC -->|上下文| AI
P -->|注入指令| CC
style T fill:#ffe4e1,stroke:#c44
style R fill:#e1f0ff,stroke:#248
style P fill:#e8ffe1,stroke:#282
写 MCP 服务器前,先问自己三个问题,决定用哪种原语:
flowchart TD
Start([要暴露一个能力]) --> Q1{AI 需要执行<br/>有副作用的动作吗?}
Q1 -->|是| Tool[✅ 用 Tool]
Q1 -->|否| Q2{它是一段只读数据,<br/>希望按需注入上下文?}
Q2 -->|是| Resource[✅ 用 Resource]
Q2 -->|否| Q3{它是一套固定的<br/>提问/工作流模板?}
Q3 -->|是| Prompt[✅ 用 Prompt]
Q3 -->|否| Rethink[重新审视需求]
style Tool fill:#ffe4e1,stroke:#c44
style Resource fill:#e1f0ff,stroke:#248
style Prompt fill:#e8ffe1,stroke:#282
一个反直觉的结论:不要把所有东西都做成 Tool。 很多人把"读取配置文件"做成 @mcp.tool(),这是典型的滥用——配置是只读数据,AI 每次"调用工具"读取都浪费一次往返,而且无法被宿主预加载或缓存。正确的做法是做成 Resource,让 Claude Code 在需要时一次性挂载。
我们用 FastMCP——这是 2026 年 Python 生态构建 MCP 服务器的事实标准,语法风格类似 FastAPI。它同时被官方 MCP Python SDK 作为高层封装集成。
创建项目目录并安装依赖:
mkdir weekly-kb-mcp && cd weekly-kb-mcp
python -m venv .venv && source .venv/bin/activate
pip install fastmcp
创建 server.py,先写最简骨架:
from fastmcp import FastMCP
# 服务器名称会显示在 Claude Code 的 /mcp 列表中
mcp = FastMCP("weekly-kb")
# 内嵌示例数据,让代码复制即可运行(真实场景改为读文件/数据库)
REPORTS = {
"week-28": {
"author": "张三",
"content": "# 第28周周报\n\n- 完成 MCP 资源模块开发\n- 修复 3 个 SQL 探针 bug\n- 重构日志诊断模块",
},
"week-29": {
"author": "张三",
"content": "# 第29周周报\n\n- 上线周报知识库 MCP\n- 编写三原语文档\n- Code Review 5 次",
},
}
if __name__ == "__main__":
# 默认以 stdio 传输运行,适合 Claude Code 本地接入
mcp.run()
预期结果:python server.py 启动后挂起等待,无报错。stdio 传输意味着 Claude Code 会把它作为子进程拉起,通过标准输入输出通信。
常见问题:
- ModuleNotFoundError: No module named 'fastmcp'——确认激活了虚拟环境(source .venv/bin/activate)。
- 端口占用——mcp.run() 默认 stdio 不占端口;若你误传了 transport="http" 才会监听端口。
工具是最熟悉的一类,这里只放对比用的实现——一个带副作用/计算的能力:
@mcp.tool()
def summarize_author(author: str) -> str:
"""统计某人在所有周报中的出现情况并返回汇总。
Args:
author: 作者姓名
"""
hits = [wk for wk, r in REPORTS.items() if r["author"] == author]
return f"{author} 共提交 {len(hits)} 篇周报:{', '.join(hits)}"
注意工具的标志:它有计算逻辑、有结构化返回,且 AI 会自主决定何时调用它(比如你问"张三这阵子都干了啥",AI 自己判断要调这个工具)。
资源是把只读数据通过 URI 暴露出去。这是本教程的核心增量。
@mcp.resource("kb://latest")
def latest_report() -> str:
"""最新一篇周报全文"""
last_week = list(REPORTS.keys())[-1]
return REPORTS[last_week]["content"]
这是资源最强大的特性——用 {占位符} 声明 URI 模板,FastMCP 会自动注册成可变资源:
@mcp.resource("report://2026/{week}")
def get_report(week: str) -> str:
"""获取指定周次的周报全文。
Args:
week: 周次标识,如 week-28
"""
if week not in REPORTS:
raise ValueError(f"未找到周报: {week}")
return REPORTS[week]["content"]
声明后,Claude Code 会识别出 report://2026/week-28、report://2026/week-29 等所有合法实例。FastMCP 会自动从函数签名和 docstring 推断参数类型,无需手写 schema。
这是 Resources 与 Tools 体验差异最大的地方:
@,会弹出资源选择器,列出 kb://latest、report://2026/week-28 等。选中后,其内容被注入上下文。常见问题:
- 资源不显示在 @ 列表——检查 URI scheme 是否合法(必须是 scheme://path 形式,如 report://、config://、file://)。
- 模板资源参数推断失败——给函数参数加上类型注解(week: str),并在 docstring 里写清楚含义。
- 想返回富文本——返回类型可声明为带 MIME 的字符串,FastMCP 支持指定 text/markdown 等。
提示词是预制的工作流模板,由用户主动选用。它把"怎么提问"标准化,避免每次手敲长指令。
@mcp.prompt()
def performance_review(author: str, week: str = "week-29") -> str:
"""生成绩效复盘:阅读指定周报,总结贡献并给出改进建议。
使用方式:在 Claude Code 斜杠命令中选用此 prompt,填入作者与周次。
"""
return f"""请扮演资深技术管理者,对以下信息做绩效复盘:
1. 先读取资源 report://2026/{week}
2. 重点评估 {author} 在该周的产出
3. 输出三段:本周亮点、风险事项、下周建议
要求语气客观,用 bullet 列点,避免空话套话。"""
注意这个 Prompt 内部引用了前面定义的 Resource(report://2026/{week})——这正是三原语协同的威力:Prompt 是工作流编排,它指挥 Claude 去读 Resource、必要时调 Tool。
MCP prompts 在 Claude Code 里会以斜杠命令形式出现,命名规则是 /mcp__<服务器名>__<prompt名>。所以上面的 prompt 会显示为:
/mcp__weekly-kb__performance_review
输入它会弹出参数表单(author、week),填好后整段模板被注入为新对话的起始指令。这就是"user-controlled"——只有你主动选用,它才会触发,AI 不会自作主张套用模板。
常见问题:
- 斜杠命令不出现——确认服务器已成功加载(/mcp 查看状态),且 prompt 函数无语法错误。
- 参数表单没字段——FastMCP 依赖类型注解和默认值推断;给参数加 : str 和默认值。
- 想返回多轮消息——@mcp.prompt() 也支持返回消息列表(list[dict]),可构造 system + user 多条消息。
在项目根目录创建 .mcp.json(团队共享)或用命令行添加(个人):
claude mcp add weekly-kb -- python /绝对路径/weekly-kb-mcp/server.py
或编辑 .mcp.json:
{
"mcpServers": {
"weekly-kb": {
"command": "python",
"args": ["/绝对路径/weekly-kb-mcp/server.py"]
}
}
}
注意:command 用绝对路径更稳妥;若用虚拟环境,写成 .venv/bin/python 可避免依赖缺失。
验证连接:在 Claude Code 中输入 /mcp,应看到 weekly-kb 状态为 connected,并展开显示其暴露的 tools、resources、prompts 三类能力。
三种典型用法对照:
| 你想做的事 | 在 Claude Code 里的操作 | 触发的原语 |
|---|---|---|
| 看最新周报内容 | 输入 @ 选 kb://latest |
Resource |
| 看 28 周详情 | 输入 @ 选 report://2026/week-28 |
Resource(模板) |
| 让 AI 统计张三产出 | 直接问"张三提交了几篇周报" | Tool(AI 自主调用) |
| 做一次绩效复盘 | 输入 /mcp__weekly-kb__performance_review |
Prompt |
调试时不要每次都开 Claude Code,用 Inspector 可视化测试三类原语最快:
# 方式一:FastMCP 内置(推荐)
fastmcp dev server.py
# 方式二:官方 Inspector
npx @modelcontextprotocol/inspector python server.py
Inspector 会打开一个网页,左侧列出该服务器的 Tools / Resources / Prompts / Templates 四个标签页:
这是排查"为什么我的资源/提示词没出现在 Claude Code 里"最快的工具——如果 Inspector 里能看到,说明服务器没问题,问题在配置或缓存(试试重启 Claude Code)。
下面是整合三种原语的完整 server.py,复制即可运行。它演示了一个真实知识库 MCP 该有的分层:数据层(Resources)、计算层(Tools)、工作流层(Prompts)。
from fastmcp import FastMCP
mcp = FastMCP("weekly-kb")
# ============ 数据源(真实场景替换为读文件 / 查 DB)============
REPORTS = {
"week-28": {"author": "张三", "tags": ["mcp", "bugfix"],
"content": "# 第28周周报\n- 完成 MCP 资源模块\n- 修复 SQL 探针 3 个 bug\n- 重构日志诊断模块"},
"week-29": {"author": "张三", "tags": ["mcp", "doc", "review"],
"content": "# 第29周周报\n- 上线周报知识库 MCP\n- 编写三原语文档\n- Code Review 5 次"},
"week-29-lisi": {"author": "李四", "tags": ["frontend", "perf"],
"content": "# 第29周周报\n- 优化首屏加载\n- 修复白屏问题\n- 引入懒加载"},
}
# ============ Resource:只读数据层(application/user-controlled)============
@mcp.resource("kb://latest")
def latest_report() -> str:
"""最新一篇周报全文"""
return REPORTS["week-29"]["content"]
@mcp.resource("report://2026/{week}")
def get_report(week: str) -> str:
"""获取指定周次的周报全文"""
if week not in REPORTS:
raise ValueError(f"未找到: {week}")
return REPORTS[week]["content"]
# ============ Tool:计算/聚合层(model-controlled,有副作用语义)============
@mcp.tool()
def search_by_tag(tag: str) -> str:
"""按标签搜索周报,返回匹配的周次列表。
Args:
tag: 标签名,如 mcp / frontend / bugfix
"""
hits = [wk for wk, r in REPORTS.items() if tag in r["tags"]]
if not hits:
return f"没有周报包含标签 {tag}"
return f"标签 [{tag}] 命中:{', '.join(hits)}"
@mcp.tool()
def count_by_author(author: str) -> str:
"""统计某人提交的周报数量"""
n = sum(1 for r in REPORTS.values() if r["author"] == author)
return f"{author} 共提交 {n} 篇周报"
# ============ Prompt:工作流层(user-controlled,斜杠命令)============
@mcp.prompt()
def performance_review(author: str, week: str = "week-29") -> str:
"""绩效复盘:读周报 → 评估贡献 → 给建议"""
return f"""请扮演技术管理者做绩效复盘:
1. 读取资源 report://2026/{week}
2. 评估 {author} 的产出
3. 输出:本周亮点 / 风险事项 / 下周建议
语气客观,bullet 列点。"""
@mcp.prompt()
def weekly_digest() -> str:
"""生成全员本周摘要:先按标签搜索,再汇总"""
return """生成本周全员工作摘要:
1. 用 search_by_tag 逐个查找 mcp / frontend / bugfix 标签
2. 汇总各人产出,按人分组
3. 用一段话总结本周团队重心"""
if __name__ == "__main__":
mcp.run()
这个设计的精髓在于职责清晰:
sequenceDiagram
participant U as 用户
participant CC as Claude Code
participant AI as AI 模型
participant S as weekly-kb MCP
U->>CC: /mcp__weekly-kb__performance_review(张三, week-29)
Note over CC: Prompt 被注入为指令<br/>(user-controlled)
CC->>AI: 携带指令开始对话
AI->>S: 读取 Resource report://2026/week-29
S-->>AI: 返回周报全文(application-controlled 数据)
AI->>S: 调用 Tool count_by_author(张三)
Note over AI: model-controlled,<br/>AI 自主决定是否调
S-->>AI: 返回 "张三共提交 2 篇"
AI-->>U: 输出绩效复盘(亮点/风险/建议)
Q:一个能力该做成 Tool 还是 Resource? A:有副作用、需要计算/聚合 → Tool;纯只读、希望按需注入 → Resource。记住:把"读文件"做成 Tool 是最常见的反模式。
Q:Prompt 和 CLAUDE.md 里写指令有什么区别? A:CLAUDE.md 是常驻的全局指令,永远在上下文里;Prompt 是按需触发的工作流模板,只在用户主动选用时注入。频繁流程用 Prompt,长期规则用 CLAUDE.md。
Q:模板资源 {week} 和 Tool 参数 week 有何不同?
A:模板资源是宿主预先枚举成多个独立 URI,适合有限、稳定的命名空间(周次、ID);Tool 参数是运行时动态传入,适合开放查询(搜索关键词)。前者可被 @ 选择器列出,后者不能。
Q:发布给别人用时,stdio 还是 HTTP 传输?
A:本地命令行用 stdio(mcp.run());要部署成云端服务供多人/多客户端连接,用 mcp.run(transport="http") 跑 Streamable HTTP,并配合鉴权。
modelcontextprotocol.iogofastmcp.comcode.claude.com/docs/en/mcpgithub.com/modelcontextprotocol.mcp.json 配置与 claude mcp add 命令说明。[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。
