AI TOOLS

EP01. "MCP 进阶:Sampling、Notifications 与 Roots 三个核心能力"

首页 › AI 工具 › Claude · Developer and Platform · MCP Advanced · EP01
约 18 分钟· #EP01#Claude#MCP Advanced
🔒 登录后可标记已读
  • 这门课接着 Introduction to Model Context Protocol,给已经在动手做正式(production)MCP Server 的工程师讲三个进阶能力:怎么让 Server 借用 Client 已经接好的 Claude(Sampling)、耗时的工具怎么让用户看到进度(Notifications)、怎么在安全边界内让 Claude 找到用户本机的文件(Roots)
  • 这篇笔记覆盖课程「Core MCP features」这个大分类,剩下的传输层(STDIO / StreamableHTTP)另开一篇
  • 前置知识:学过 Introduction to Model Context Protocol,会用 Python SDK 写基本的 Tools/Resources/Prompts

重点内容


Sampling:让 Server 借用 Client 已经接好的 Claude

Server 想要生成文本(比如帮用户总结一段内容),有两条路:

做法代价
方案一:Server 自己直接整合 Claude要自己的 Claude API key、自己管认证、自己承担费用、自己写整合代码
方案二:Sampling把生成文本这件事交给 Client——Client 本来就已经接好一条 Claude 连接了

流程:Server 做完自己该做的事 → 组好一个要生成文字用的 prompt → 把 sampling 请求发给 Client → Client 拿这个 prompt 去调用 Claude → Client 把生成结果传回 Server → Server 把结果整合进自己的响应里。

好处:降低 Server 自身的整合复杂度;token 费用转嫁给 Client 承担,不是 Server;Server 不需要自己的 Claude 凭证;对公开给陌生用户用的 Server 来说,能避免被无限调用、自己一直出钱。

Server 端:

@mcp.tool()
async def summarize(text_to_summarize: str, ctx: Context):
    prompt = f"""
    Please summarize the following text:
    {text_to_summarize}
    """

    result = await ctx.session.create_message(
        messages=[
            SamplingMessage(
                role="user",
                content=TextContent(
                    type="text",
                    text=prompt
                )
            )
        ],
        max_tokens=4000,
        system_prompt="You are a helpful research assistant",
    )

    if result.content.type == "text":
        return result.content.text
    else:
        raise ValueError("Sampling failed")

Client 端:要提供一个 sampling callback,负责实际去调 Claude:

async def sampling_callback(
    context: RequestContext, params: CreateMessageRequestParams
):
    # Call Claude using the Anthropic SDK
    text = await chat(params.messages)

    return CreateMessageResult(
        role="assistant",
        model=model,
        content=TextContent(type="text", text=text),
    )

建立 session 时要把这个 callback 传进去:

async with ClientSession(
    read,
    write,
    sampling_callback=sampling_callback
) as session:
    await session.initialize()

📌 Sampling 最适合公开访问、成本控制很重要的场景——把整合复杂度和账单一起转给 Client 承担,Client 反正本来就有现成的 Claude 连接和凭证。


Notifications:耗时的工具怎么让用户看到进度

Claude 调一个要跑一阵子的工具(比如做研究、处理一批数据)时,用户在结果出来之前通常什么都看不到——不知道是在正常跑还是卡死了,体验很差。

解法:Python SDK 会自动给每个 tool 函数传一个 Context 参数,里面提供两个方法:

  • context.info()——发日志消息给 Client
  • context.report_progress()——更新当前进度 / 总量
@mcp.tool(
    name="research",
    description="Research a given topic"
)
async def research(
    topic: str = Field(description="Topic to research"),
    *,
    context: Context
):
    await context.info("About to do research...")
    await context.report_progress(20, 100)
    sources = await do_research(topic)

    await context.info("Writing report...")
    await context.report_progress(70, 100)
    results = await generate_report(sources)

    return results

Client 端要设两种 callback:

async def logging_callback(params: LoggingMessageNotificationParams):
    print(params.data)

async def print_progress_callback(
    progress: float, total: float | None, message: str | None
):
    if total is not None:
        percentage = (progress / total) * 100
        print(f"Progress: {progress}/{total} ({percentage:.1f}%)")
    else:
        print(f"Progress: {progress}")

async def run():
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(
            read,
            write,
            logging_callback=logging_callback
        ) as session:
            await session.initialize()

            await session.call_tool(
                name="add",
                arguments={"a": 1, "b": 3},
                progress_callback=print_progress_callback,
            )

logging_callback 在建立 session 时传一次就好;progress_callback 则是每次 call_tool 各自传,比较灵活。

呈现方式因应用类型而不同:CLI 应用直接把消息 print 到终端;Web 应用用 WebSocket、Server-Sent Events 或轮询把更新推给浏览器;桌面应用更新进度条/状态栏。

📌 实作这些通知完全是可选的——可以整个不理会、只挑某几类呈现、或按自己应用的需要自订呈现方式,它们纯粹是体验层的优化,不影响功能本身。


Roots:怎么在安全边界内让 Claude 找到用户的文件

没有 Roots 时,Claude 没办法搜索整个文件系统去定位一个文件。比如用户说「把 biking.mp4 转成 mov 格式」,Claude 根本不知道这个文件在 Movies/Sports 这种深层目录结构里的哪个位置。最直接但体验很差的解法是要求用户自己贴完整路径。

有 Roots 之后的流程:

  1. 用户提出文件操作请求
  2. Claude 调用 list_roots,拿到有哪些目录被授权访问
  3. Claude 在被授权的目录里调用 read_dir,找到目标文件
  4. Claude 用找到的完整路径去执行工具

用户从此只要讲文件名,不用自己记/贴完整路径。

安全边界:Roots 会限制 Server 只能碰被指定的目录——如果只授权了 Desktop,Server 就碰不到 Documents 或 Downloads。Claude 想访问授权范围外的路径时会收到错误,可以据此告知用户。

📌 SDK 不会自动帮你挡:这是个实作细节容易被忽略的坑——MCP SDK 本身不会自动强制执行这个访问限制,开发者要自己写一个校验函数(常见做法是 is_path_allowed()),在真正执行文件操作之前先检查请求的路径是否落在被授权的 Root 范围内。

好处:用户不用打完整路径;检索范围收窄在授权目录内,找文件更快;避免 Claude 误触到不该碰的敏感文件;Root 本身还能透过 tool 或 prompt 动态配置,不是写死的。


学完你会

  • ✅ 分得清 Sampling 跟「Server 自己接 Claude」的差别,知道什么场景该用 Sampling 省掉凭证和费用
  • ✅ 会用 context.info() / context.report_progress() 给耗时工具加上进度提示,Client 端也接好对应的 callback
  • ✅ 理解 Roots 怎么让 Claude 在不要求用户贴完整路径的前提下,安全地在授权目录内找到文件
  • ✅ 知道 SDK 不会自动帮你做 Roots 的权限校验,这一步要自己写

常见错误

  • ❌ Server 想生成文字,第一反应就是自己申请一把 Claude API key——先想想能不能用 Sampling 把这件事丢给 Client,省掉一整套凭证管理 / 计费 / 整合代码
  • ❌ 耗时的工具完全不做任何进度回报——用户会以为程序卡死了;哪怕只是简单加两行 context.info() / report_progress(),体验也会好很多
  • ❌ 以为 Notifications 是协议要求必须实现的功能——它纯粹是体验层的可选优化,可以完全不理、也可以只挑部分类型呈现
  • ❌ 用了 Roots 就以为 SDK 会自动帮你挡住越界的文件路径——SDK 不会自动强制执行这个限制,校验逻辑(比如 is_path_allowed())要自己写,不写等于没有真正的安全边界
  • 💡 公开给很多陌生用户使用的 Server,优先考虑用 Sampling,避免每个用户的调用都变成自己出 token 钱

Sources

官方文档:

  1. Model Context Protocol: Advanced Topics(课程总览)— https://academy.claude.com/courses/model-context-protocol-advanced-topics
  2. Sampling — https://academy.claude.com/courses/model-context-protocol-advanced-topics/sampling
  3. Sampling walkthrough — https://academy.claude.com/courses/model-context-protocol-advanced-topics/sampling-walkthrough
  4. Log and progress notifications — https://academy.claude.com/courses/model-context-protocol-advanced-topics/log-and-progress-notifications
  5. Notifications walkthrough — https://academy.claude.com/courses/model-context-protocol-advanced-topics/notifications-walkthrough
  6. Roots — https://academy.claude.com/courses/model-context-protocol-advanced-topics/roots
  7. Roots walkthrough — https://academy.claude.com/courses/model-context-protocol-advanced-topics/roots-walkthrough