MCP是AI工具调用的标准化协议,解决了Function Call碎片化问题。本节课带你理解MCP的设计思想、核心概念和实际应用。
MCP(Model Context Protocol)是 AI 工具调用的标准化协议,解决了 Function Call 的碎片化问题。
8.1 什么是 MCP?
MCP(Model Context Protocol,模型上下文协议) 是 Anthropic 于 2024 年底推出的开放标准协议,让 LLM 用统一接口连接外部工具和数据源。
类比 Web 开发:
1 2
| Function Call = 早期 Ajax(每个浏览器实现不同) MCP = REST API 标准(统一的接口规范)
|
就像 Language Server Protocol(LSP)统一了编程语言支持,MCP 统一了 AI 工具集成。
8.2 MCP 解决了什么问题?
| 问题 | 没有 MCP | 有了 MCP |
|---|
| 工具发现 | 手动注册,硬编码 | 客户端自动发现服务器的工具列表 |
| 接口格式 | 每个厂商不同 | 统一 JSON-RPC 2.0 协议 |
| 跨应用共享 | 不可能 | 一个 MCP Server 可被多个客户端使用 |
| 上下文传递 | 不支持 | 支持 Roots、Sampling 等上下文机制 |
| 安全控制 | 应用层自行处理 | 协议级用户授权 |
8.3 MCP 架构:Client-Server 模型
graph TD
subgraph Host["Host 宿主应用"]
C1["MCP Client (连接A)"]
C2["MCP Client (连接B)"]
C3["MCP Client (连接C)"]
end
C1 -->|JSON-RPC| S1["MCP Server (文件系统)"]
C2 -->|JSON-RPC| S2["MCP Server (数据库)"]
C3 -->|JSON-RPC| S3["MCP Server (GitHub)"]
style Host fill:#e3f2fd,stroke:#1565c0
style S1 fill:#fff9c4,stroke:#f9a825
style S2 fill:#fff9c4,stroke:#f9a825
style S3 fill:#fff9c4,stroke:#f9a825
MCP 通信序列:
sequenceDiagram
participant Host as Host 应用
participant Client as MCP Client
participant Server as MCP Server
Host->>Client: 初始化连接
Client->>Server: initialize 请求
Server-->>Client: 返回服务器能力
Client->>Server: list_tools
Server-->>Client: 返回工具列表
Client->>Server: list_resources
Server-->>Client: 返回资源列表
Client->>Server: list_prompts
Server-->>Client: 返回提示词模板
rect rgb(232, 245, 233)
Note over Host,Server: 用户触发工具调用
Host->>Client: 调用工具
Client->>Server: call_tool(name, arguments)
Server->>Server: 执行工具逻辑
Server-->>Client: 返回执行结果
Client-->>Host: 返回给 LLM
end
三个角色:
| 角色 | 说明 | 类比 |
|---|
| Host | LLM 应用,发起连接 | 浏览器 |
| Client | Host 内部的连接器 | 浏览器内的 Tab |
| Server | 提供工具和数据的服务 | Web API 服务器 |
8.4 MCP 三大原语(Primitives)
LLM 可以调用的函数 — 与 Function Call 直接对应。
1 2 3 4 5 6 7 8 9 10 11 12 13
| from mcp.server.fastmcp import FastMCP
mcp = FastMCP("SQL Assistant")
@mcp.tool() def execute_query(sql: str) -> str: """执行 SQL 查询并返回结果""" return db.execute(sql)
@mcp.tool() def list_tables() -> str: """列出数据库中所有表""" return str(db.get_table_names())
|
2. Resources(资源)
LLM 可以读取的数据 — 类似 REST API 的 GET。
1 2 3 4 5 6 7 8 9
| @mcp.resource("schema://{table_name}") def get_table_schema(table_name: str) -> str: """获取指定表的结构""" return db.get_schema(table_name)
@mcp.resource("config://database") def get_db_config() -> str: """获取数据库配置信息""" return f"Database: {db.name}, Dialect: {db.dialect}"
|
3. Prompts(提示词模板)
预定义的提示词模板 — 类似 REST API 的固定查询模式。
1 2 3 4 5 6 7 8 9 10 11 12
| @mcp.prompt() def sql_expert(question: str, dialect: str = "sqlite") -> str: """生成 SQL 专家提示词""" return f"""你是一个 {dialect} 数据库专家。 请根据以下问题编写 SQL 查询:
问题:{question}
要求: - 只使用 SELECT 语句 - 限制返回 10 条结果 - 使用正确的 {dialect} 语法"""
|
8.5 快速开始:创建你的第一个 MCP Server
安装
创建 Server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| from mcp.server.fastmcp import FastMCP
mcp = FastMCP("My First MCP Server")
@mcp.tool() def add(a: int, b: int) -> int: """Add two numbers""" return a + b
@mcp.tool() def search_city(name: str) -> str: """Search for a city by name""" cities = {"北京": "中国首都,人口2189万", "上海": "中国最大城市,人口2487万"} return cities.get(name, f"未找到城市: {name}")
@mcp.resource("greeting://{name}") def get_greeting(name: str) -> str: """Get a personalized greeting""" return f"你好, {name}!"
@mcp.prompt() def help_prompt(topic: str) -> str: """Generate a help prompt""" return f"请详细介绍 {topic} 的使用方法"
if __name__ == "__main__": mcp.run(transport="streamable-http")
|
运行 Server
1 2 3 4 5 6 7 8
| python server.py
uv run mcp dev server.py
uv run mcp install server.py
|
创建 Client 连接 Server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import asyncio from mcp import ClientSession from mcp.client.streamable_http import streamable_http_client
async def main(): async with streamable_http_client("http://localhost:8000/mcp") as (read, write, _): async with ClientSession(read, write) as session: await session.initialize()
tools = await session.list_tools() print(f"可用工具: {[t.name for t in tools.tools]}")
result = await session.call_tool("add", arguments={"a": 5, "b": 3}) print(f"5 + 3 = {result.content[0].text}")
resources = await session.list_resources() print(f"可用资源: {[r.uri for r in resources.resources]}")
from pydantic import AnyUrl greeting = await session.read_resource(AnyUrl("greeting://世界")) print(f"问候: {greeting}")
prompts = await session.list_prompts() print(f"可用提示词: {[p.name for p in prompts.prompts]}")
asyncio.run(main())
|
8.6 MCP 高级特性
结构化输出
1 2 3 4 5 6 7 8 9 10 11
| from pydantic import BaseModel, Field
class WeatherData(BaseModel): temperature: float = Field(description="温度(摄氏度)") humidity: float = Field(description="湿度百分比") condition: str = Field(description="天气状况")
@mcp.tool() def get_weather(city: str) -> WeatherData: """获取天气信息 — 返回结构化数据""" return WeatherData(temperature=22.5, humidity=45.0, condition="sunny")
|
进度报告
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| from mcp.server.fastmcp import Context, FastMCP
mcp = FastMCP("Progress Demo")
@mcp.tool() async def long_task(task_name: str, ctx: Context, steps: int = 5) -> str: """执行耗时任务,报告进度""" for i in range(steps): await ctx.report_progress( progress=(i + 1) / steps, total=1.0, message=f"步骤 {i + 1}/{steps}", ) return f"任务 '{task_name}' 完成"
|
用户交互(Elicitation)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| from pydantic import BaseModel, Field
class BookingConfirm(BaseModel): check_alternative: bool = Field(description="是否查看其他日期?") alternative_date: str = Field(default="2026-05-10", description="备选日期")
@mcp.tool() async def book_room(date: str, ctx: Context) -> str: """预订房间""" if date == "2026-05-07": result = await ctx.elicit( message="该日期已满,是否查看其他日期?", schema=BookingConfirm, ) if result.action == "accept" and result.data and result.data.check_alternative: return f"已预订 {result.data.alternative_date}" return f"已预订 {date}"
|
生命周期管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| from contextlib import asynccontextmanager from dataclasses import dataclass
@dataclass class AppContext: db_connection: Database
@asynccontextmanager async def app_lifespan(server: FastMCP): db = await Database.connect() try: yield AppContext(db_connection=db) finally: await db.disconnect()
mcp = FastMCP("App", lifespan=app_lifespan)
@mcp.tool() def query_data(sql: str, ctx: Context) -> str: """查询数据""" db = ctx.request_context.lifespan_context.db_connection return db.query(sql)
|
8.7 MCP vs Function Call 对比
| 维度 | Function Call | MCP |
|---|
| 定义 | 代码中的函数 | 独立运行的服务 |
| 协议 | 无(各厂商不同) | JSON-RPC 2.0(标准化) |
| 发现 | 手动注册 | 客户端自动 list_tools |
| 跨应用 | 不可能 | 一个 Server 服务多个 Client |
| 上下文 | 需要手动传递 | 内置 Roots、Sampling |
| 资源 | 不支持 | 支持 Resources(数据读取) |
| 提示词 | 不支持 | 支持 Prompts(模板) |
| 运行方式 | 进程内函数调用 | 独立进程 / 网络服务 |
| 部署 | 随应用一起 | 独立部署和扩展 |
| 适用规模 | 单应用 | 跨应用、跨团队 |
选型建议:
1 2 3 4 5 6 7 8 9 10 11 12
| Function Call 适合: - 快速原型 - 工具数量少(<10) - 不需要跨应用共享 - 简单的单 Agent 场景
MCP 适合: - 工具需要跨应用共享 - 多个 Agent/应用需要同一组工具 - 需要动态发现工具 - 企业级部署,工具独立维护 - 第三方工具集成
|
8.8 在 DeepAgents 中使用 MCP
DeepAgents 本身不直接内置 MCP 客户端,但你可以通过两种方式集成:
方式一:将 MCP 工具包装为 DeepAgents 工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import asyncio from mcp import ClientSession from mcp.client.streamable_http import streamable_http_client from deepagents import create_deep_agent
async def fetch_mcp_tools(): """从 MCP Server 获取工具列表,转换为 DeepAgents 工具""" async with streamable_http_client("http://localhost:8000/mcp") as (read, write, _): async with ClientSession(read, write) as session: await session.initialize() tools_response = await session.list_tools() return tools_response.tools, session
mcp_tools, session = asyncio.run(fetch_mcp_tools())
def make_tool_func(mcp_tool, mcp_session): async def tool_func(**kwargs): result = await mcp_session.call_tool(mcp_tool.name, arguments=kwargs) return result.content[0].text tool_func.__name__ = mcp_tool.name tool_func.__doc__ = mcp_tool.description return tool_func
tools = [make_tool_func(t, session) for t in mcp_tools] agent = create_deep_agent(model="openai:gpt-4o", tools=tools)
|
方式二:通过 LangChain MCP 适配器
1 2 3 4 5 6 7 8 9 10 11
| from langchain_mcp import MCPToolkit from deepagents import create_deep_agent
mcp_toolkit = MCPToolkit(url="http://localhost:8000/mcp") tools = mcp_toolkit.get_tools()
agent = create_deep_agent( model="openai:gpt-4o", tools=tools, )
|
8.9 传输方式
| 传输方式 | 适用场景 | 说明 |
|---|
| stdio | 本地开发 | 标准输入输出,最简单 |
| SSE | 旧版网络通信 | Server-Sent Events |
| Streamable HTTP | 生产推荐 | HTTP 双向通信,支持流式 |
1 2 3 4 5 6 7 8
| mcp.run()
mcp.run(transport="streamable-http")
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)
|
8.10 安全模型
MCP 的四个安全原则:
- 用户授权:所有数据访问和操作必须用户明确同意
- 数据隐私:Host 不得在未经同意的情况下传输用户数据
- 工具安全:工具 = 任意代码执行,必须用户审批
- 采样控制:用户必须批准 LLM 采样请求,控制服务器可见内容