AI Agent 实战(二):MCP 协议深度解析——让 AI 拥有“万能工具箱”
AI Agent 实战(二):MCP 协议深度解析——让 AI 拥有“万能工具箱”

图 2-1:Model Context Protocol (MCP) 标准交互流程示意图
第二章:MCP 协议深度解析——让 AI 拥有“万能工具箱”
2.1 MCP 是什么?
MCP(Model Context Protocol) 是由 Anthropic 在 2024 年底推出的开源协议,旨在解决 AI 应用中的一个核心痛点:如何让 LLM 灵活、安全地调用外部工具?
在 MCP 出现之前,AI 调用工具的方式五花八门:
- 有的通过 HTTP API。
- 有的通过命令行参数。
- 有的通过数据库查询。
- 有的甚至直接操作文件系统。
这种碎片化导致:
- 开发成本高:每新增一个工具,都要为 LLM 编写专门的调用逻辑。
- 维护困难:工具接口变更时,LLM 的调用逻辑也要同步更新。
- 安全性差:缺乏统一的权限控制和错误处理机制。
MCP 的核心思想是“标准化”:
- 工具定义:所有工具遵循统一的描述格式(JSON Schema)。
- 工具调用:LLM 通过标准协议发起调用,无需关心底层实现。
- 错误处理:统一的错误格式,便于 LLM 理解和恢复。
类比:
- 没有 MCP:就像你每次买新电器,都要重新学习如何使用插头和开关。
- 有 MCP:就像所有电器都使用标准插座,你只需学会一次,就能插拔任何电器。
2.2 MCP 的核心组件
MCP 协议由三个核心组件构成:
2.2.1 MCP Server(工具提供者)
MCP Server 是工具的“宿主”,负责:
- 暴露工具:将本地功能(如文件操作、数据库查询、API 调用)封装为 MCP 工具。
- 处理请求:接收 LLM 的调用请求,执行工具逻辑,返回结果。
- 错误反馈:如果工具执行失败,返回结构化的错误信息。
示例:halo-mcp-server 是一个 MCP Server,它暴露了以下工具:
list_my_posts:列出博客文章。create_post:创建新文章。publish_post:发布文章。delete_post:删除文章。
2.2.2 MCP Client(工具调用者)
MCP Client 是 LLM 的“代理”,负责:
- 发现工具:向 MCP Server 查询可用工具列表。
- 发起调用:将 LLM 的意图转换为 MCP 工具调用请求。
- 处理结果:将 MCP Server 的返回结果传递给 LLM。
在 OpenClaw 中,mcporter 就是 MCP Client 的实现。它负责:
- 读取
~/.mcporter/mcporter.json配置文件,找到所有已注册的 MCP Server。 - 通过
mcporter call <server> <tool> <params>命令调用工具。
2.2.3 工具描述(Tool Schema)
每个 MCP 工具都有一个JSON Schema描述,包括:
- 名称:工具的唯一标识(如
create_post)。 - 描述:工具的用途(LLM 据此判断何时调用)。
- 参数:工具的输入参数及其类型、必填项。
- 返回格式:工具的输出格式。
示例:
{
"name": "create_post",
"description": "创建一篇新的博客文章",
"parameters": {
"type": "object",
"properties": {
"title": {"type": "string", "description": "文章标题"},
"content": {"type": "string", "description": "文章内容(Markdown 格式)"},
"category": {"type": "string", "description": "文章分类"}
},
"required": ["title", "content"]
}
}
2.3 实战:编写一个自定义 MCP Server
现在,让我们动手编写一个自定义 MCP Server,演示如何暴露一个简单功能给 LLM。
2.3.1 场景设定
假设我们想做一个**“天气查询工具”**,让 LLM 能够查询任意城市的天气。
2.3.2 实现步骤
步骤 1:创建项目结构
mkdir -p weather-mcp-server
cd weather-mcp-server
touch main.py requirements.json
步骤 2:编写 MCP Server 代码(Python + FastAPI)
# main.py
from fastapi import FastAPI
from pydantic import BaseModel
import requests
from typing import Optional
app = FastAPI(title="Weather MCP Server", version="1.0.0")
# 工具参数定义
class WeatherQuery(BaseModel):
city: str
unit: Optional[str] = "celsius" # celsius 或 fahrenheit
# 工具实现
@app.post("/tools/weather_query")
async def weather_query(query: WeatherQuery):
"""查询指定城市的天气"""
try:
# 调用免费天气 API(示例使用 OpenWeatherMap)
api_key = "YOUR_API_KEY" # 实际使用时请替换为真实 Key
url = f"http://api.openweathermap.org/data/2.5/weather"
params = {
"q": query.city,
"appid": api_key,
"units": "metric" if query.unit == "celsius" else "imperial"
}
response = requests.get(url, params=params)
response.raise_for_status()
data = response.json()
# 构造返回结果
result = {
"city": data["name"],
"temperature": data["main"]["temp"],
"description": data["weather"][0]["description"],
"humidity": data["main"]["humidity"],
"wind_speed": data["wind"]["speed"]
}
return {"success": True, "data": result}
except Exception as e:
return {"success": False, "error": str(e)}
# MCP 协议要求:暴露工具列表
@app.get("/tools")
async def list_tools():
return [
{
"name": "weather_query",
"description": "查询指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius"}
},
"required": ["city"]
}
}
]
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
步骤 3:安装依赖
pip install fastapi uvicorn requests pydantic
步骤 4:启动 MCP Server
python main.py
# Server 运行在 http://localhost:8000
2.3.3 验证工具
测试工具列表:
curl http://localhost:8000/tools
返回:
[
{
"name": "weather_query",
"description": "查询指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius"}
},
"required": ["city"]
}
}
]
测试工具调用:
curl -X POST http://localhost:8000/tools/weather_query \
-H "Content-Type: application/json" \
-d '{"city": "Beijing", "unit": "celsius"}'
返回:
{
"success": true,
"data": {
"city": "Beijing",
"temperature": 15.2,
"description": "clear sky",
"humidity": 45,
"wind_speed": 3.2
}
}
2.4 集成 OpenClaw:注册并调用自定义工具
现在,我们将这个天气 MCP Server 集成到 OpenClaw 中。
2.4.1 配置 mcporter
编辑 ~/.mcporter/mcporter.json,添加我们的 Server:
{
"mcpServers": {
"weather-mcp-server": {
"command": "python3",
"args": ["/path/to/weather-mcp-server/main.py"],
"transport": "http",
"url": "http://localhost:8000"
}
}
}
2.4.2 验证连接
mcporter list
输出应包含 weather-mcp-server。
2.4.3 调用工具
mcporter call weather-mcp-server weather_query city="Shanghai" unit="celsius"
返回:
{
"success": true,
"data": {
"city": "Shanghai",
"temperature": 18.5,
"description": "partly cloudy",
"humidity": 62,
"wind_speed": 2.8
}
}
2.4.4 LLM 调用
现在,当你对 OpenClaw 说:
“帮我查一下上海的天气。”
OpenClaw 会:
- 解析意图,识别出需要调用
weather_query工具。 - 通过
mcporter调用weather-mcp-server。 - 将返回结果整理成自然语言回复: “上海当前天气:18.5°C,多云,湿度 62%,风速 2.8m/s。”
2.5 深度思考:MCP 如何改变 AI 工具生态?
2.5.1 从“硬编码”到“动态发现”
传统方式下,每新增一个工具,都要修改 LLM 的调用逻辑。而 MCP 让工具动态注册,LLM 只需知道“有哪些工具可用”,无需关心“工具如何实现”。
类比:
- 传统:就像你每次去新餐厅,都要重新学习菜单和点餐流程。
- MCP:就像所有餐厅都用同一套点餐系统,你只需学会一次,就能在任何餐厅点餐。
2.5.2 从“孤岛”到“生态”
MCP 的标准化,让不同开发者编写的工具可以互操作。例如:
- 一个开发者写了一个“博客发布工具”。
- 另一个开发者写了一个“图片生成工具”。
- LLM 可以同时调用这两个工具,完成“生成配图并发布博客”的复杂任务。
这将催生一个开放的 AI 工具生态,类似 npm、PyPI 这样的包管理器,但针对的是AI 工具。
2.5.3 安全性与权限控制
MCP 协议本身不直接处理权限,但可以通过Server 端实现:
- 认证:API Key、OAuth2。
- 授权:基于角色的访问控制(RBAC)。
- 审计:记录所有工具调用日志。
未来展望: 随着 MCP 的普及,我们可能会看到:
- MCP 应用商店:开发者发布工具,用户一键安装。
- MCP 沙箱:工具在隔离环境中运行,防止恶意操作。
- MCP 标准组织:制定更详细的协议规范,推动互操作性。
(第二章完)
下一章预告:第三章将深入解析 Agent Reach,教你如何配置和使用它来访问 Twitter、Reddit、YouTube、Bilibili 等平台,实现“互联网之眼”。