进阶概览

完成 MCP 入门后,你已了解协议的基本概念与三层架构。本教程深入 MCP 的进阶特性——包括工具的错误处理与进度反馈、资源订阅、提示词模板参数化、Sampling 反向调用、传输层选择,以及在生产环境中的安全部署策略。

工具 · 高级模式
错误处理、进度通知、取消机制、注解(annotations)让 Tool 调用更健壮。
资源 · 订阅推送
静态资源之外,支持实时订阅:当资源内容变化时,Server 主动通知 Client。
提示词模板
Server 提供带参数的提示词模板,Client 填入参数后直接获得可用的 messages 数组。
Sampling 反向调用
Server 向 Client 发起文本生成请求,在 Server 端逻辑中嵌入 AI 推理能力。
传输层
本地 stdio vs 远程 HTTP+SSE——如何根据场景选择,以及如何正确处理连接生命周期。
生产安全
凭证隔离、输入校验、速率限制、最小权限、审计日志——五层防御体系。
ℹ️
本教程对应 Anthropic SkillJar Model Context Protocol — Advanced Topics 课程,建议先完成 MCP 入门 再阅读本文。

工具高级模式

入门阶段你已会定义 Tool 并暴露给 AI 调用。进阶模式在此基础上增加四个关键能力:错误处理、进度通知、取消支持,以及工具注解(Annotations)。

🔧
工具返回结构:Tool 的返回值是 CallToolResult,包含 content 数组(文本/图片/嵌入资源)和 isError 标志位。正常返回时 isError 为 false;业务层失败时将 isError 设为 true 并在 content 中说明原因——这与抛出异常不同,AI 可以读取错误内容并决定下一步。

2.1 错误处理:isError vs 异常

# 正确做法:业务错误用 isError,不要抛异常
from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent

mcp = FastMCP("my-server")

@mcp.tool()
async def read_file(path: str) -> str:
    try:
        with open(path) as f:
            return f.read()
    except FileNotFoundError:
        # 返回带 isError 的结果,AI 能读懂并处理
        return [TextContent(
            type="text",
            text=f"Error: file not found at {path}"
        )], True  # isError=True
// isError 让 AI 读取错误内容并决策,而非让整个调用崩溃
server.setRequestHandler(CallToolRequestSchema, async (req) => {
  if (req.params.name === "read_file") {
    try {
      const content = await fs.readFile(req.params.arguments.path, "utf-8");
      return { content: [{ type: "text", text: content }] };
    } catch (e) {
      return {
        content: [{ type: "text", text: `Error: ${e.message}` }],
        isError: true
      };
    }
  }
});

2.2 进度通知

长时间运行的 Tool(如大文件处理、批量 API 调用)可通过 progressToken 向 Client 推送进度,让用户看到实时反馈。

from mcp.server.fastmcp import Context

@mcp.tool()
async def process_large_dataset(path: str, ctx: Context) -> str:
    rows = load_csv(path)
    total = len(rows)
    for i, row in enumerate(rows):
        await process_row(row)
        # 每处理 10% 推送一次进度
        if i % (total // 10) == 0:
            await ctx.report_progress(
                progress=i,
                total=total,
                message=f"处理中 {i}/{total} 行"
            )
    return f"完成,共处理 {total} 行"

2.3 工具注解(Annotations)

注解(Annotations)是附加在工具定义上的元数据,用于向 Host 声明工具的行为特征,帮助 Host 决定是否需要用户二次确认。

注解字段类型含义默认值
readOnlyHintboolean工具不修改任何状态,只读取数据false
destructiveHintboolean工具可能执行破坏性操作(如删除文件)true
idempotentHintboolean多次调用结果相同,安全重试false
openWorldHintboolean工具会与外部系统交互(如调用第三方 API)true
⚠️
注解是提示性声明,不是安全边界。Host 可能会依据注解决定是否展示确认对话框,但 Server 实现本身必须保证安全——不能依赖注解来阻止误操作。

资源系统

资源(Resources)是 MCP Server 向 AI 暴露的可读数据。入门阶段你了解了静态资源;进阶阶段重点是资源模板(动态 URI)和资源订阅(实时推送变化通知)。

3.1 资源模板:动态 URI

资源 URI 支持 RFC 6570 URI 模板语法,用 {variable} 表示动态参数,让一个注册规则覆盖大量同类资源。

# 注册动态资源模板:database:///{table}/{id}
@mcp.resource("database:///{table}/{id}")
async def get_record(table: str, id: str) -> str:
    # table 和 id 自动从 URI 解析注入
    record = await db.query(
        f"SELECT * FROM {table} WHERE id = ?", [id]
    )
    return json.dumps(record)

# 列出所有可用资源(支持模板展开)
@mcp.list_resources()
async def list_resources() -> list:
    tables = await db.get_tables()
    return [
        {"uri": f"database:///{t}/schema", "name": f"{t} 表结构"}
        for t in tables
    ]

3.2 资源订阅:实时推送

当资源内容发生变化时,Server 可以主动通知已订阅的 Client,Client 重新读取资源后更新上下文。适用于配置文件监听、数据库变更通知等场景。

# Server 端:当文件变化时通知所有订阅者
import asyncio
from watchfiles import awatch

async def watch_config(server):
    async for changes in awatch("/etc/myapp/config.json"):
        # 通知所有已订阅该资源的 Client
        await server.send_resource_updated(
            uri="file:///etc/myapp/config.json"
        )
💡
Client 侧(如 Claude Desktop)收到 notifications/resources/updated 消息后,会自动重新调用 resources/read 刷新内容,无需手动重启对话。

提示词模板

Prompts 是 MCP Server 提供的可复用提示词模板,与 Tools 和 Resources 并列为三大原语之一。Server 注册模板,Client 按需获取并填入参数,最终得到一个可直接发送给 AI 的 messages 数组。

ℹ️
Prompts vs System Prompt:System Prompt 由 Host 应用固定注入;MCP Prompts 是动态的、参数化的、由 Server 提供的——更接近"可复用的对话模板库"。用户可以在 UI 中选择触发哪个模板(例如 Claude Desktop 的 / 命令)。

4.1 定义带参数的模板

@mcp.prompt()
def code_review(
    language: str,
    code: str,
    focus: str = "correctness"
) -> list:
    """代码审查模板:返回可直接发送给 AI 的 messages 数组"""
    return [
        {
            "role": "user",
            "content": {
                "type": "text",
                "text": (
                    f"请对以下 {language} 代码进行审查,"
                    f"重点关注 {focus}:

```{language}
{code}
```"
                )
            }
        }
    ]

# 带多轮对话的模板(返回 user + assistant 交替)
@mcp.prompt()
def debug_session(error_msg: str, stack_trace: str) -> list:
    return [
        {"role": "user", "content": {"type": "text", "text": f"报错信息:{error_msg}"}},
        {"role": "assistant", "content": {"type": "text", "text": "我来分析这个错误。先看堆栈:"}},
        {"role": "user", "content": {"type": "text", "text": f"堆栈:{stack_trace}"}},
    ]

4.2 模板中嵌入资源

模板内容不限于纯文本——可以将 Resource 内容直接嵌入 messages,让 AI 在接收提示词的同时也获得相关数据上下文。

@mcp.prompt()
async def analyze_file(file_path: str) -> list:
    # 将文件内容作为 resource 嵌入模板
    return [
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "请分析以下文件的内容和潜在问题:"},
                {
                    "type": "resource",
                    "resource": {
                        "uri": f"file://{file_path}",
                        "mimeType": "text/plain"
                    }
                }
            ]
        }
    ]

Sampling 反向调用

Sampling 是 MCP 协议中最独特的能力——方向与普通 Tool 调用相反:不是 AI 调用 Server,而是 Server 向 Client 请求 AI 进行推理。这让 Server 端逻辑可以在自己的业务流程中按需嵌入 AI 能力。

普通 Tool 调用
AI (Client)
调用 Server Tool
返回结果给 AI
Sampling 反向调用
Server 业务逻辑
请求 AI 推理
Server 接收结果
⚠️
Human-in-the-loop 要求:MCP 规范要求 Client 在执行 Sampling 请求之前必须展示给用户确认,用户可以修改或拒绝请求内容。这是安全核心原则——Server 不能在用户不知情的情况下触发无限制的 AI 调用。

5.1 Server 发起 Sampling 请求

@mcp.tool()
async def smart_summarize(url: str, ctx: Context) -> str:
    # 1. Server 先抓取页面内容
    raw_content = await fetch_page(url)

    # 2. Server 请求 AI 进行摘要(Sampling 反向调用)
    result = await ctx.sample(
        messages=[{
            "role": "user",
            "content": {
                "type": "text",
                "text": f"请用三句话概括以下内容:

{raw_content}"
            }
        }],
        max_tokens=200,
        system_prompt="你是一个简洁的摘要助手,只输出摘要,不添加其他内容。"
    )
    # 3. Server 拿到 AI 的回答,继续处理
    return result.content.text

5.2 适用场景

场景描述优势
智能体工作流Server 编排多步骤任务,在每个步骤按需调用 AI把编排逻辑放在 Server 而非提示词中,更清晰
批量内容处理Server 遍历数据集,对每条记录请求 AI 分析速率控制、并发管理在 Server 侧统一处理
动态决策树Server 根据 AI 对上一步的判断决定下一步操作复杂分支逻辑不依赖 AI 上下文保持
RAG 增强Server 检索文档后请求 AI 基于检索结果回答Retrieval 与 Generation 解耦,各自优化

传输层详解

MCP 支持两种传输方式:本地进程间通信(stdio)和远程 HTTP+SSE。选择合适的传输层对安全性、延迟和部署复杂度影响显著。

特性stdio(本地)HTTP + SSE(远程)
适用场景桌面应用、CLI 工具、开发环境云服务、多用户平台、API 集成
数据安全数据不离开本机,最安全需 TLS 加密 + 认证机制
延迟极低(进程间通信)取决于网络,需考虑重连
部署难度简单(与 Host 同机运行)需独立服务、域名、TLS 证书
并发支持单连接(1 个 Client)多连接,支持水平扩展
连接持久性随 Host 进程生命周期需实现心跳与重连逻辑

6.1 stdio 模式配置(Claude Desktop)

{
  "mcpServers": {
    "my-server": {
      "command": "python",
      "args": ["-m", "my_mcp_server"],
      "env": {
        "DATABASE_URL": "postgresql://localhost/mydb",
        "API_KEY": "your-api-key-here"
      }
    }
  }
}

6.2 HTTP+SSE 模式:连接生命周期

1
客户端连接 SSE 端点 — 建立持久的 Server-Sent Events 连接,Server 通过此通道推送消息
2
能力协商 — Client 发送 initialize 请求,双方协商支持的 MCP 版本和功能集
3
正常通信 — Client 通过 HTTP POST 发送请求,Server 通过 SSE 推送响应和通知
4
连接断开处理 — Client 应实现指数退避重连;Server 应在断开后清理会话状态
5
优雅关闭 — 发送 shutdown 请求,等待 Server 确认后再关闭连接
💡
Streamable HTTP(新传输协议):MCP 规范 2025-03-26 版引入了 Streamable HTTP 作为 HTTP+SSE 的升级替代,支持双向流、更好的负载均衡和 CDN 兼容性。如果你在构建新的远程 Server,建议直接使用 Streamable HTTP。

安全最佳实践

MCP Server 有能力访问文件、数据库、API——这意味着安全设计必须从第一行代码就开始考虑,而不是上线前补丁。以下是生产级 MCP Server 的五层防御体系。

1. 输入校验
所有 Tool 参数在执行前必须严格校验:类型、范围、路径遍历(../)、SQL 注入模式。使用 Pydantic 模型声明参数类型,利用 FastMCP 的自动校验。
2. 最小权限
Roots 机制声明 Server 只能访问的路径范围。文件 Server 只允许访问用户指定目录;数据库 Server 只授予 SELECT 权限(只读场景)。
3. 速率限制
对每个 Tool 调用实施速率限制,防止 AI 进入工具调用循环消耗资源。建议使用令牌桶算法,超限返回明确的错误信息而非静默失败。
4. 凭证隔离
API Key、数据库密码通过环境变量注入,绝不硬编码。生产环境使用 Vault 或云服务的 Secret Manager。敏感凭证不出现在日志中。
5. 审计日志
记录所有 Tool 调用的请求参数和响应摘要(注意脱敏敏感字段)。日志必须包含:时间戳、调用工具名、输入参数摘要、执行时长、是否 isError。
6. 沙箱执行
需要执行代码或 shell 命令的 Tool,必须在容器或 VM 中运行,限制网络出口和文件系统访问。切勿在 Server 主进程中 exec() 用户输入的内容。

输入校验示例

from pydantic import BaseModel, field_validator
import os

class FileReadArgs(BaseModel):
    path: str
    allowed_base: str = "/home/user/workspace"

    @field_validator("path")
    def no_traversal(cls, v, values):
        base = values.data.get("allowed_base", "")
        real = os.path.realpath(v)
        # 阻止路径遍历攻击
        if not real.startswith(os.path.realpath(base)):
            raise ValueError("路径越界:不允许访问 workspace 以外的目录")
        return real

@mcp.tool()
async def read_file(path: str) -> str:
    args = FileReadArgs(path=path)  # 自动校验并抛出错误
    with open(args.path) as f:
        return f.read()
⚠️
Prompt Injection 防御:MCP Server 读取的外部内容(网页、文件、数据库记录)可能包含恶意提示词,试图操控 AI 执行非预期操作。对于高风险场景,Server 在将外部内容返回给 AI 前应先做内容过滤或封装隔离(如用 XML 标签包裹,明确标注这是"外部不可信内容")。

生产部署

从本地开发到生产部署,MCP Server 需要考虑容器化、配置管理、健康检查和监控。以下是标准的 Docker 部署模式。

8.1 Dockerfile(Python FastMCP)

# 使用官方 Python slim 镜像
FROM python:3.12-slim

WORKDIR /app

# 先复制依赖文件,利用 Docker 层缓存
COPY pyproject.toml .
RUN pip install --no-cache-dir fastmcp pydantic

COPY src/ ./src/

# 非 root 用户运行(最小权限原则)
RUN useradd -r -s /bin/false mcpuser
USER mcpuser

# 健康检查
HEALTHCHECK --interval=30s --timeout=10s   CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

EXPOSE 8000
CMD ["python", "-m", "src.server"]

8.2 环境变量配置模式

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    # 所有配置从环境变量读取,带类型校验和默认值
    database_url: str
    api_key: str
    max_requests_per_minute: int = 60
    allowed_base_path: str = "/data/workspace"
    log_level: str = "INFO"

    class Config:
        env_file = ".env"  # 本地开发用 .env,生产用 Secret Manager

settings = Settings()  # 缺少必填字段时启动即失败,快速发现配置错误

8.3 关键指标监控

指标含义告警阈值(参考)
mcp_tool_duration_msTool 调用耗时p99 > 5000ms
mcp_tool_error_rateisError 返回比率> 5%
mcp_active_connections当前活跃 SSE 连接数> 预期上限 80%
mcp_sampling_requestsSampling 请求次数(防循环)单会话 > 50 次/分钟
mcp_auth_failures认证失败次数> 10 次/分钟
💡
结构化日志:生产环境的 Server 应输出 JSON 格式日志,包含 trace_id(与 Claude 会话关联)、tool_nameduration_msis_error 字段。这让你能快速从监控平台追踪单个会话的完整工具调用链。

进阶学习

掌握 MCP 进阶特性后,以下资源可帮助你在实际项目中落地。

方向内容资源
官方 MCP 进阶课程Anthropic SkillJar 系统讲解进阶特性前往 SkillJar ↗
FastMCP 文档Python SDK 完整 API 参考,含所有装饰器和类型GitHub ↗
MCP 协议规范完整消息格式、错误码、传输层规范modelcontextprotocol.io ↗
官方 Server 示例文件系统、数据库、GitHub、Slack 等参考实现GitHub ↗
MCP 入门回顾三层架构、核心概念、快速上手MCP 入门 →
Tool Use 教程深入理解 Claude 的工具调用机制与 JSON Schema工具调用教程 →