MCP(Model Context Protocol)框架新课程教案

课程名称:MCP 框架精讲与实践
适用对象:AI 应用开发者、LLM 集成工程师
目标:全面掌握 MCP 协议架构、核心概念、开发方式与使用规范,能够构建标准 MCP 服务器与客户端。
格式:可直接导出 PDF 的 Markdown 教案


目录

  1. MCP 概述与架构

  2. 传输机制与生命周期

  3. 核心概念:Resources

  4. 核心概念:Prompts

  5. 核心概念:Tools

  6. 高级特性:Sampling、Roots、Logging

  7. 使用规范与功能表达

  8. 开发实战:构建天气 MCP Server

  9. 配置与部署示例

  10. 附录:知识点速查表


1. MCP 概述与架构

1.1 什么是 MCP?

  • 定义:模型上下文协议(Model Context Protocol),由 Anthropic 提出并开源的开放协议,用于标准化应用向大语言模型(LLM)提供上下文的方式。

  • 类比:就像 USB-C 统一了设备连接,MCP 统一了 AI 模型与外部数据源、工具的连接方式。

  • 目标

    • 消除各 LLM 客户端/插件生态的碎片化。

    • 让数据、工具、提示模板可一次构建,多处复用。

    • 实现安全、双向的上下文交换。

1.2 核心架构

架构采用 客户端-服务器 模型,包含三个关键角色:

角色 描述 示例
MCP Host 运行 LLM 的应用,负责连接并管理多个 MCP Client Claude Desktop、VSCode 插件、自研 APP
MCP Client 与 MCP Server 维持 1:1 连接的组件,由 Host 管理 Host 内建的协议客户端
MCP Server 提供上下文能力的轻量服务,实现 Resources/Prompts/Tools 文件系统服务器、数据库服务器、天气API服务器

通信拓扑
Host (内含多个 Client) ↔ Client ↔ Server
一个 Host 可连接多个 Server,每个 Server 对应一个 Client。

1.3 能力协商

初始化时,客户端与服务器交换各自的能力(Capabilities),如:

  • resources: 是否支持资源与订阅

  • prompts: 是否支持提示模板

  • tools: 是否支持工具调用

  • sampling: 服务器是否可反向请求 LLM 生成(仅客户端声明)

  • roots: 是否支持文件根路径列表(客户端声明)

  • logging: 是否支持日志


2. 传输机制与生命周期

2.1 传输方式

MCP 使用 JSON-RPC 2.0 作为消息格式,目前定义两种底层传输:

传输 适用场景 启动方式 连接地址格式
stdio 本地子进程通信,无需网络 Client 直接 spawn Server 进程 命令(如 python server.py
Streamable HTTP 远程服务器,支持流式响应 Client 通过 HTTP 连接到 Server http://host:port/mcp
  • stdio:通过标准输入/输出传递 JSON-RPC,无加密,仅本机。

  • Streamable HTTP(前身为 SSE):Client 发送 HTTP POST 请求,Server 通过分块传输响应流。需处理会话 ID 和重连。

2.2 协议生命周期

  1. 连接建立:启动传输通道。

  2. 初始化 (Initialize)

    • Client 发 initialize 请求,携带自身能力与客户端信息。

    • Server 返回其能力与服务器信息。

    • Client 发送 initialized 通知,表示准备就绪。

  3. 能力交换与协商:基于双方声明的能力确定可用功能。

  4. 正常运行:交换资源读取、工具执行、提示获取等请求/通知。

  5. 断开:进程终止或 HTTP 会话超时。

2.3 心跳与健康检查

  • 可通过 ping 请求/回复检测连接活性。


3. 核心概念:Resources

3.1 定义

资源(Resources)是将数据或文件内容以可寻址的 URI 暴露给 LLM 的接口。模型可像读取文件一样读取资源,无需提前复制。

3.2 资源类型

  • 文本资源:返回文本内容,带 MIME 类型 text/*

  • 二进制资源:返回 base64 编码的 blob,MIME 为 image/png 等。

  • 资源模板:含参数的 URI 模板(如 weather://{city}/current),支持动态匹配。

3.3 资源交互

方法 方向 描述
resources/list Client→Server 列出所有可用资源及模板
resources/read Client→Server 读取指定 URI 的内容
resources/subscribe Client→Server 订阅某资源的变更通知
notifications/resources/updated Server→Client 资源更新时推送通知
notifications/resources/list_changed Server→Client 资源列表发生变化时通知

3.4 使用规范

  • 资源 URI 应具有唯一性、描述性,如 file:///path/to/doc.mddb://users/table

  • 优先使用资源模板处理动态参数,避免无限列表。

  • 大文件建议分页或使用资源内容内嵌(Embedded Resource)。


4. 核心概念:Prompts

4.1 定义

提示(Prompts)是参数化的提示模板,由 Server 预定义,支持多轮消息(System/User/Assistant),并可包含资源引用。

4.2 功能表达

  • 模板可接受参数(可选必填),动态生成完整提示。

  • 消息可引用资源:在 content 中嵌入 resource 类型,指向某个资源 URI 或内容块。

4.3 交互方法

方法 方向 描述
prompts/list Client→Server 获取所有提示模板的清单
prompts/get Client→Server 根据名称和参数获取具体提示消息

4.4 示例结构

json

{
  "name": "summarize_report",
  "description": "总结季度报告",
  "arguments": [
    { "name": "quarter", "required": true }
  ]
}
// 调用 prompts/get 返回 messages:
{
  "messages": [
    { "role": "user", "content": { "type": "text", "text": "请总结以下报告..." } },
    { "role": "user", "content": { "type": "resource", "resource": { "uri": "report://2024/Q1" } } }
  ]
}

4.5 使用规范

  • 提示名称简洁、语义化。

  • 描述应清晰说明用途,便于 Host 自动发现。

  • 参数说明需完整,最好给出示例值。


5. 核心概念:Tools

5.1 定义

工具(Tools)是可由模型调用的函数,用于执行计算、查询 API、修改数据等操作。MCP 将工具发现与执行标准化。

5.2 工具 Schema

每个工具包含:

  • name: 唯一名称(如 get_weather

  • description: 详细功能说明(直接影响模型调用准确度)

  • inputSchema: JSON Schema 定义输入参数

5.3 交互方法

方法 方向 描述
tools/list Client→Server 列出可用工具及其 Schema
tools/call Client→Server 调用指定工具并传参,返回结果
notifications/tools/list_changed Server→Client 工具列表变更时通知

5.4 调用结果

返回 content 数组,可以是文本、图像、嵌入式资源等。错误使用 isError: true 标记。

5.5 使用规范

  • 描述应极尽详细,说明何时使用、参数含义、返回格式,因为 LLM 依赖描述进行函数选择。

  • 工具应是幂等的(如果可能),避免副作用歧义。

  • 复杂结果可使用结构化文本(Markdown)或资源引用。


6. 高级特性:Sampling、Roots、Logging

6.1 Sampling(服务器采样)

  • 允许 Server 向 Client 发起 LLM 生成请求,实现“服务器主动要求模型思考”。

  • 典型场景:工具内部需要智能总结、翻译或决策。

  • 方法:sampling/createMessage,由 Server 发送至 Client。

  • 安全性:Client 必须明确声明支持 sampling,且可限制使用。

6.2 Roots(根目录)

  • Client 可向 Server 声明一组文件系统“根”路径(如 /home/user/project)。

  • Server 使用 roots/list 请求获取根列表(Client→Server 方向有通知 notifications/roots/list_changed)。

  • 用于文件型 Server 确定可操作范围,增强安全性。

6.3 Logging(日志)

  • Server 可通过 notifications/logging/message 向 Client 发送结构化日志。

  • 等级:debuginfowarningerror

  • 须在初始化时声明 logging 能力。

6.4 错误处理

  • 基于 JSON-RPC 错误码,预定义标准错误类型:

    • -32700 解析错误,-32600 无效请求,-32601 方法未找到,-32602 无效参数,-32603 内部错误。

  • 业务逻辑错误在 result 中通过 isError: true 返回。


7. 使用规范与功能表达

7.1 设计原则

  1. 单一职责:一个 Server 聚焦一类能力(如只有文件操作,或只有数据库查询)。

  2. 描述即接口:Tools/Prompts 的描述质量直接影响 LLM 调用的正确率。

  3. 渐进式暴露:通过资源模板、参数化提示避免一次性暴露海量数据。

  4. 安全第一:明确读写权限,工具应校验输入,遵循最小权限原则。

7.2 功能表达检查清单

  • Resources 是否覆盖静态数据和动态数据(模板)?

  • Prompts 是否封装了常用任务流?是否包含参数说明?

  • Tools 的 inputSchema 是否精确且约束合理?

  • 所有文本均用 Markdown 优化可读性?

  • 是否注册了列表变更通知以支持动态更新?

  • 传输选择:本地用 stdio,远程用 Streamable HTTP。

7.3 开发方式

推荐使用官方 SDK:

  • Pythonmcp[cli] (pip install mcp)

  • TypeScript@modelcontextprotocol/sdk

  • Go/Java 社区 SDK

构建 Server 的核心步骤:

  1. 创建服务器实例并声明能力。

  2. 注册 resources/listresources/read 等处理器。

  3. 使用 @server.list_prompts() @server.call_tool() 装饰器定义功能。

  4. 选择传输并启动 server.run()

调试工具:MCP Inspector (npx @modelcontextprotocol/inspector) 可可视化测试 Server。


8. 开发实战:构建天气 MCP Server

示例使用 Python SDK,展示 Resources、Prompts、Tools 的综合运用。

python

import json
from mcp.server import Server, NotificationOptions
from mcp.server.stdio import stdio_server
import mcp.types as types

# 创建服务器实例
server = Server("weather-server")

# --- Resources ---
@server.list_resources()
async def list_resources() -> list[types.Resource]:
    return [
        types.Resource(
            uri="weather://current/Beijing",
            name="北京当前天气",
            mimeType="text/plain",
            description="实时天气数据"
        )
    ]

@server.read_resource()
async def read_resource(uri: str) -> str:
    if uri == "weather://current/Beijing":
        # 模拟获取数据
        data = {"city": "北京", "temp": 28, "condition": "晴"}
        return json.dumps(data, ensure_ascii=False)
    raise ValueError("未知资源")

# --- Prompts ---
@server.list_prompts()
async def list_prompts() -> list[types.Prompt]:
    return [
        types.Prompt(
            name="weather_advice",
            description="根据天气给出穿衣建议",
            arguments=[
                types.PromptArgument(name="city", required=True)
            ]
        )
    ]

@server.get_prompt()
async def get_prompt(name: str, arguments: dict) -> types.GetPromptResult:
    if name == "weather_advice":
        city = arguments.get("city", "北京")
        return types.GetPromptResult(
            messages=[
                types.PromptMessage(
                    role="user",
                    content=types.TextContent(
                        type="text",
                        text=f"请根据 {city} 的当前天气(从资源读取),给出具体的穿衣建议。"
                    )
                )
            ]
        )
    raise ValueError("未知提示")

# --- Tools ---
@server.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="get_forecast",
            description="获取指定城市未来3天天气预报,输入城市中文名,返回摘要",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名,如 上海"}
                },
                "required": ["city"]
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    if name == "get_forecast":
        city = arguments["city"]
        # 模拟预报
        forecast = f"{city}未来三天:25-30°C,多云转晴"
        return [types.TextContent(type="text", text=forecast)]
    raise ValueError("未知工具")

# 启动
async def main():
    async with stdio_server() as (read, write):
        await server.run(read, write, server.create_initialization_options())

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

9. 配置与部署示例

9.1 在 Claude Desktop 中配置 stdio Server

编辑 claude_desktop_config.json

json

{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["path/to/weather_server.py"]
    }
  }
}

重启 Claude Desktop,即可通过对话调用 weather 工具和资源。

9.2 Streamable HTTP 部署

使用 mcp 包提供的 ASGI/WSGI 集成,或直接使用 FastAPI 包装:

python

from mcp.server.streamable_http import StreamableHTTPServerTransport
# ... 定义 server 后
transport = StreamableHTTPServerTransport("/mcp")
app = transport.get_asgi_app()

Client 端配置连接 URL。


10. 附录:知识点速查表

分类 知识点 关键方法/事项
协议基础 JSON-RPC 2.0、请求/响应/通知 initializeping, 错误码
传输 stdio 子进程标准输入输出,无网络
传输 Streamable HTTP HTTP POST + 流式响应,会话管理
能力 resources, prompts, tools, sampling, roots, logging 初始化协商
资源 静态资源、资源模板、内容类型 resources/listresources/read, 订阅更新
提示 参数化模板、多轮消息、嵌入资源 prompts/listprompts/get
工具 JSON Schema 输入、结构化输出 tools/listtools/call
反向 Server→Client 生成请求 sampling/createMessage
文件系统作用域 roots/list,安全限制
日志 Server→Client 日志 logging/message,级别控制
开发 Python/TS SDK,装饰器注册 @server.call_tool 等
调试 MCP Inspector 可视化测试工具
部署 Claude Desktop 配置、HTTP 部署 配置 mcpServers 字段
设计规范 描述即接口、单一职责、安全校验 Tools 描述详细化,资源 URI 唯一
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐