首页 › tutorials › 补全MCP三原语:资源与提示词挂载实战

补全MCP三原语:Resources 与 Prompts 挂载实战

绝大多数「第一个 MCP 服务器」教程止步于 Tools:写几个 @mcp.tool() 函数,跑通 Inspector,挂到 Claude Code,收工。但 MCP 协议一共定义了三种原语——Tools、Resources、Prompts,它们各自回答不同的问题,在 Claude Code 里的暴露方式也完全不同。只造工具,等于只用了协议的三分之一。

本篇聚焦那两块最常被忽略的拼图:用 Resources 把数据像文件一样暴露出去(可被 @ 提及),用 Prompts 把可复用工作流变成 / 斜杠命令。我们用一个可运行的「个人知识库 MCP」把三原语一次性串起来。

一、概述与适用人群

学习目标:读完本篇,你将能(1)在 30 秒内判断某个能力该做成 Tool、Resource 还是 Prompt;(2)用官方 Python SDK 的 FastMCP 在一个文件里同时实现三原语;(3)用 MCP Inspector 调试,并挂载到 Claude Code,分别用「工具调用 / @ 提及 / 斜杠命令」三种方式触发。

适用人群:已经写过一个 Hello-World 级别的 MCP 工具、想理解协议全貌的开发者;给团队设计内部 MCP 服务、需要把文档和标准操作流程(SOP)一并交付的架构师。

二、前置条件

python -m venv .venv && source .venv/bin/activate
pip install "mcp[cli]"
mcp --version   # 确认 MCP CLI 可用

三、核心决策:为什么工具不是全部

这是本篇最重要的一节。三个原语的根本区别在于「谁来决定使用它」(控制方向)和「它有没有副作用」:

落到 Claude Code 里,三种原语长得完全不一样:

能力类型 该做成 在 Claude Code 中的形态 典型触发者
只读数据 / 文档 / 配置 Resource @server:scheme://path 的 @ 提及,自动注入为附件 宿主/你
执行动作、有副作用 Tool 模型自主调用,命中权限规则 模型
可复用的提示词工作流 Prompt /mcp__server__prompt 斜杠命令 你

一句话判断法:「提供上下文」用 Resource,「改变世界」用 Tool,「复用打法」用 Prompt。 把一条 SOP 文档做成 Tool 是错配——它是数据不是动作;把「代码评审流程」做成 Resource 也是错配——它是流程不是数据。

flowchart LR
    Q["新能力需求"] --> R1{"只读数据/文档?"}
    R1 -->|是| RES["Resource\n@server:..."]
    R1 -->|否| R2{"有副作用/执行动作?"}
    R2 -->|是| TOOL["Tool\n模型自主调用"]
    R2 -->|否| PROMPT["Prompt\n/mcp__server__cmd"]
    RES --> CC1["自动注入上下文"]
    TOOL --> CC2["需权限确认"]
    PROMPT --> CC3["用户显式触发"]
    style RES fill:#e8f5e9,stroke:#2e7d32
    style TOOL fill:#fff3e0,stroke:#ef6c00
    style PROMPT fill:#e3f2fd,stroke:#1565c0

四、实战项目:个人知识库 MCP(kb-mcp)

我们要造一个 markdown 笔记知识库服务器 kb-mcp,把三原语用足:

Step 1:数据模型与存储

笔记存成一个本地 JSON 索引文件(index.json),路径由 KB_DIR 环境变量控制,便于隔离测试。新建 kb_mcp.py:

import json, os, re
from pathlib import Path
from mcp.server.fastmcp import FastMCP

KB_DIR = Path(os.environ.get("KB_DIR", Path.home() / ".kb-mcp"))
KB_DIR.mkdir(parents=True, exist_ok=True)
INDEX_FILE = KB_DIR / "index.json"

mcp = FastMCP("kb-mcp")

def slugify(title: str) -> str:
    s = re.sub(r"[^a-z0-9]+", "-", title.lower()).strip("-")[:60]
    return s or "note"

def _load() -> dict:
    return json.loads(INDEX_FILE.read_text("utf-8")) if INDEX_FILE.exists() else {}

def _save(idx: dict) -> None:
    INDEX_FILE.write_text(json.dumps(idx, ensure_ascii=False, indent=2), "utf-8")

预期结果:KB_DIR 不存在会自动创建;index.json 首次写入即生成。

Step 2:实现 Tools(动作)

@mcp.tool()
def create_note(title: str, body: str, tags: str = "") -> str:
    """创建一条笔记。title 标题;body 正文(Markdown);tags 逗号分隔标签。返回新笔记 URI。"""
    idx = _load()
    slug = slugify(title)
    idx[slug] = {"title": title, "body": body,
                 "tags": [t.strip() for t in tags.split(",") if t.strip()]}
    _save(idx)
    return f"created notes://{slug}"

@mcp.tool()
def search_notes(query: str) -> str:
    """按关键词检索标题、正文、标签,返回匹配的 URI 列表。"""
    idx, q = _load(), query.lower()
    hits = [s for s, n in idx.items()
            if q in s or q in n["title"].lower() or q in n["body"].lower()
            or any(q in t.lower() for t in n["tags"])]
    return "\n".join(f"- notes://{s} — {idx[s]['title']}" for s in hits) or "无匹配笔记。"

@mcp.tool()
def delete_note(slug: str) -> str:
    """按 slug 删除笔记。"""
    idx = _load()
    if slug not in idx:
        return f"未找到 notes://{slug}"
    del idx[slug]; _save(idx)
    return f"deleted notes://{slug}"

每个工具的文档字符串会变成给模型的工具说明——这是触发准确性的关键,务必写清「做什么」和「参数含义」。

Step 3:实现 Resources(数据暴露)

这是与 Tools 教程的分水岭。Resources 用 @mcp.resource("URI"),URI 是数据标识而非函数名。静态资源用一个常量 URI;资源模板用 RFC 6570 风格的 {param} 占位,函数参数名与之对应:

@mcp.resource("notes://index")
def note_index() -> str:
    """知识库目录清单(所有笔记的 URI 与标题)。"""
    idx = _load()
    if not idx:
        return "(知识库为空)"
    return "\n".join(f"- notes://{s} — {n['title']} [{', '.join(n['tags'])}]"
                     for s, n in idx.items())

@mcp.resource("notes://{slug}")
def read_note(slug: str) -> str:
    """读取单条笔记全文。"""
    idx = _load()
    if slug not in idx:
        raise ValueError(f"未知笔记: {slug}")
    n = idx[slug]
    return f"# {n['title']}\n\n{n['body']}\n\n_标签: {', '.join(n['tags'])}_"

if __name__ == "__main__":
    mcp.run()

两个关键点:

  1. notes://{slug} 是资源模板。Claude Code 在 @ 自动补全里会模糊匹配 slug,命中后自动 resources/read 拉取全文注入上下文。
  2. 资源函数每次被读取时实时求值——索引变了,下一次 resources/read notes://index 立刻反映最新状态,天然「动态」。

关于 list_changed:当你的资源列表是静态注册(启动时固定)时,若运行期增删,应发送 notifications/resources/list_changed 通知让宿主刷新列表。FastMCP 这种「函数即资源」写法每次读取都重新求值,已隐含动态性;若改用预注册列表,记得补这条通知。

Step 4:实现 Prompts(可复用工作流)

Prompts 是「带参数的预制提示词」,在 Claude Code 里变成 /mcp__server__name 斜杠命令:

@mcp.prompt()
def summarize_note(slug: str) -> str:
    """对指定笔记生成结构化摘要:三句话概述 + 关键点 + 一条建议。"""
    return (f"请引用 @kb-mcp:notes://{slug},"
            "输出:1) 三句话概述;2) 3-5 个关键点;3) 一条可执行建议。")

@mcp.prompt()
def weekly_digest() -> str:
    """生成知识库简报:读取目录并把笔记按标签归类。"""
    return "请读取 @kb-mcp:notes://index,把所有笔记按标签分组,挑出 3 条最重要的。"

注意 Prompt 的返回值本身就是给模型的指令——它可以把 Resource(@kb-mcp:notes://...)、Tool 调用编排进一条工作流里,这正是 Prompt 作为「打法封装」的价值。

五、用 MCP Inspector 调试(先别急着挂 Claude Code)

Inspector 是官方 GUI 调试器,能分别对三种原语做单点测试,强烈建议先在这里验证:

mcp dev kb_mcp.py

浏览器打开 Inspector 后:

常见问题:Resources 面板空白 → 多半是没 import 或函数没加 @mcp.resource;Prompts 参数不生效 → 检查函数形参名与 Inspector 字段是否一致。

六、挂载到 Claude Code 并验证三种触发方式

挂载(stdio 传输)

注意 stdio 用法中 -- 把 Claude 自己的选项和启动命令隔开:

# 用绝对路径,避免依赖当前工作目录
KB_DIR="$HOME/.kb-mcp" \
  claude mcp add --transport stdio --env KB_DIR="$HOME/.kb-mcp" \
  kb-mcp -- python "$PWD/kb_mcp.py"

claude mcp list          # 应看到 kb-mcp

进入会话后用 /mcp 确认连接,面板会显示该服务器暴露的工具数量。Claude Code 支持 list_changed 通知,资源/提示词变动会自动刷新,无需重连。

方式 A:触发 Tool(模型自主调用)

帮我用 create_note 建一条笔记:标题 "MCP 三原语",正文讲 Tools/Resources/Prompts 区别,标签 mcp,arch

模型自主决定调用 create_note,命中权限规则后执行;返回 created notes://mcp-san-yuan-yu。

方式 B:@ 提及 Resource(宿主注入)

在输入框敲 @,自动补全里会列出 @kb-mcp:notes://index 等:

把 @kb-mcp:notes://index 读出来,告诉我知识库里都有什么

引用的资源会被自动 resources/read 并作为附件注入。这是 Resource 区别于 Tool 的核心体验——你主动选定上下文,而不是让模型去猜。

方式 C:执行 Prompt 斜杠命令(用户显式触发)

敲 / 能看到 /mcp__kb-mcp__summarize_note 与 /mcp__kb-mcp__weekly_digest。带参数的 prompt 用空格传参:

/mcp__kb-mcp__summarize_note mcp-san-yuan-yu

渲染后的提示词直接进入对话,模型据此执行摘要工作流。

sequenceDiagram
    participant U as 你
    participant CC as Claude Code
    participant S as kb-mcp 服务器
    Note over U,S: Tool 路径 —— 模型自主调用
    U->>CC: "建一条关于 MCP 的笔记"
    CC->>S: tools/call create_note(...)
    S-->>CC: created notes://mcp-san-yuan-yu
    Note over U,S: Resource 路径 —— @ 提及注入
    U->>CC: "分析 @kb-mcp:notes://index"
    CC->>S: resources/read notes://index
    S-->>CC: 目录全文(作为附件)
    Note over U,S: Prompt 路径 —— 斜杠命令
    U->>CC: /mcp__kb-mcp__summarize_note mcp-san-yuan-yu
    CC->>S: prompts/get summarize_note{slug}
    S-->>CC: 渲染后的提示词
    CC->>CC: 据此执行摘要工作流

七、完整可运行示例

把上面四段代码拼进同一个 kb_mcp.py(Step 1 的导入/存储 + Step 2~4 的三原语),python kb_mcp.py 即为标准 stdio 服务器。配套验证脚本:

# 1. 清空环境变量起步
export KB_DIR="$(mktemp -d)"
# 2. Inspector 手测
mcp dev kb_mcp.py
# 3. 挂载(另开终端)
claude mcp add --transport stdio --env KB_DIR="$KB_DIR" kb-mcp -- python "$PWD/kb_mcp.py"
# 4. 进会话验证
claude
#   /mcp                              → 确认 kb-mcp 已连接
#   "用 create_note 建一条笔记..."    → 验证 Tool
#   "@kb-mcp:notes://index 读出来"    → 验证 Resource
#   /mcp__kb-mcp__weekly_digest       → 验证 Prompt

八、常见问题与陷阱

九、延伸阅读

十、训练营适用性评估

参考来源

  1. Resources — Model Context Protocol — 资源原语:application-driven 模型、capabilities、resources/list 与 resources/read、资源模板、annotations。
  2. Prompts — Model Context Protocol — 提示词原语:user-controlled 模型、prompts/list 与 prompts/get、参数化与多模态内容。
  3. Connect Claude Code to tools via MCP — Claude Code 侧:claude mcp add/scope/.mcp.json、@ 引用资源、/mcp__server__prompt 斜杠命令、Tool Search、maxResultSizeChars 注解。
  4. Build an MCP server — Model Context Protocol — 三原语(Tools/Resources/Prompts)的服务端构建总览与能力声明。
  5. How to Build Your First MCP Server for Claude (2026) — 2026 年度 MCP 服务器构建实战教程,含 Tools/Resources 综合示例。

由「51学通信」原创出品 · 站长爱卫生(微信:gprshome201101)

[!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 等场景全覆盖。

51学通信知识星球海报-扫码加入