MCP(Model Context Protocol)作为AI时代的新兴协议标准,正在重塑AI助手与外部工具的交互方式。本文将以OpenClaw(腾讯AI助手)为平台,手把手教你开发自定义MCP工具,实现AI助手的能力扩展。
一、MCP协议核心概念
MCP是一种标准化的工具集成协议,允许AI助手通过统一的接口调用外部工具和服务。其核心架构包含三个关键组件:
【配图位置】
- MCP Server:工具提供方,负责实现具体的工具逻辑
- MCP Client:工具调用方,通常是AI助手平台
- Transport Layer:通信传输层,支持stdio、HTTP、SSE等多种方式
MCP通信模型
# MCP消息结构示例
# 所有消息遵循JSON-RPC 2.0规范
# 客户端发送请求
request = {
"jsonrpc": "2.0",
"id": 1, # 请求ID,用于匹配响应
"method": "tools/call", # 调用方法
"params": {
"name": "weather_query", # 工具名称
"arguments": {
"city": "上海",
"date": "2024-05-14"
}
}
}
# 服务端返回响应
response = {
"jsonrpc": "2.0",
"id": 1, # 与请求ID匹配
"result": {
"content": [
{
"type": "text",
"text": "上海今日天气:晴,温度22-28°C"
}
]
}
}
二、开发环境搭建
依赖安装
# 使用Python 3.10+版本
# pip install mcp # MCP官方Python SDK
# 也可以使用FastMCP简化开发
# pip install fastmcp
# 验证安装
import mcp
print(f"MCP SDK版本: {mcp.__version__}")
项目结构规划
# 推荐的项目目录结构
my_mcp_server/
├── server.py # MCP服务器主入口
├── tools/
│ ├── __init__.py
│ ├── weather.py # 天气查询工具
│ ├── calculator.py # 计算器工具
│ └── file_ops.py # 文件操作工具
├── config.py # 配置管理
└── requirements.txt # 依赖清单
三、实现第一个MCP工具
下面我们开发一个实用的"系统状态监控"工具,可实时获取服务器运行状态。
工具定义与注册
# server.py - MCP服务器核心代码
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import psutil # 系统监控库
import asyncio
# 创建MCP服务器实例
app = Server("system-monitor-server")
# 定义工具元数据(供AI助手识别和调用)
@app.list_tools()
async def list_tools() -> list[Tool]:
"""
向AI助手声明可用工具列表
每个工具需包含名称、描述和参数schema
"""
return [
Tool(
name="get_system_status",
description="获取系统运行状态,包括CPU、内存、磁盘使用率",
inputSchema={
"type": "object",
"properties": {
"detail": {
"type": "boolean",
"description": "是否返回详细信息"
}
},
"required": [] # 无必填参数
}
),
Tool(
name="get_process_info",
description="获取指定进程的运行信息",
inputSchema={
"type": "object",
"properties": {
"process_name": {
"type": "string",
"description": "进程名称(如python、nginx)"
}
},
"required": ["process_name"] # process_name为必填
}
)
]
【配图位置】
工具执行逻辑实现
# server.py - 工具执行处理器
@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
"""
工具调用入口,根据工具名称分发到对应处理函数
name: 工具名称(与list_tools中定义一致)
arguments: 用户传入的参数字典
"""
if name == "get_system_status":
return await handle_system_status(arguments)
elif name == "get_process_info":
return await handle_process_info(arguments)
else:
raise ValueError(f"未知工具: {name}")
async def handle_system_status(args: dict) -> list[TextContent]:
"""
系统状态监控实现
使用psutil库获取硬件资源使用情况
"""
detail = args.get("detail", False)
# 获取CPU使用率
cpu_percent = psutil.cpu_percent(interval=1)
cpu_count = psutil.cpu_count()
# 获取内存信息
memory = psutil.virtual_memory()
mem_total = memory.total / (1024**3) # 转换为GB
mem_used = memory.used / (1024**3)
mem_percent = memory.percent
# 获取磁盘信息
disk = psutil.disk_usage('/')
disk_total = disk.total / (1024**3)
disk_used = disk.used / (1024**3)
disk_percent = disk.percent
# 构造返回结果
if detail:
# 详细模式:包含更多指标
result = f"""系统状态详细报告:
CPU: {cpu_percent}% 使用率 (共{cpu_count}核)
内存: {mem_percent}% 使用率 ({mem_used:.2f}GB/{mem_total:.2f}GB)
磁盘: {disk_percent}% 使用率 ({disk_used:.2f}GB/{disk_total:.2f}GB)
网络连接数: {len(psutil.net_connections())}
运行进程数: {len(psutil.pids())}"""
else:
# 简洁模式:核心指标摘要
result = f"CPU:{cpu_percent}% | 内存:{mem_percent}% | 磁盘:{disk_percent}%"
return [TextContent(type="text", text=result)]
async def handle_process_info(args: dict) -> list[TextContent]:
"""
进程信息查询实现
根据进程名称查找并返回运行状态
"""
process_name = args["process_name"]
found_processes = []
# 遍历所有进程,匹配目标名称
for proc in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_info']):
try:
if proc.info['name'].lower().contains(process_name.lower()):
mem_mb = proc.info['memory_info'].rss / (1024**2)
found_processes.append({
'pid': proc.info['pid'],
'name': proc.info['name'],
'cpu': proc.info['cpu_percent'],
'memory': f"{mem_mb:.1f}MB"
})
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue # 忽略已终止或无权限的进程
if not found_processes:
return [TextContent(type="text", text=f"未找到进程: {process_name}")]
# 格式化输出
output = f"找到{len(found_processes)}个'{process_name}'进程:\n"
for p in found_processes:
output += f" PID:{p['pid']} | CPU:{p['cpu']}% | 内存:{p['memory']}\n"
return [TextContent(type="text", text=output)]
四、启动MCP服务器
# server.py - 服务器启动入口
async def main():
"""
启动MCP服务器,使用stdio传输模式
stdio模式适合本地集成,AI助手通过管道通信
"""
# 初始化服务器
async with stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options()
)
# 标准入口点
if __name__ == "__main__":
asyncio.run(main())
【配图位置】
五、配置OpenClaw接入
开发完成后,需要在OpenClaw中配置MCP服务器接入:
配置文件示例
// OpenClaw MCP配置文件(通常为mcp_servers.json)
{
"mcpServers": {
"system-monitor": {
"command": "python",
"args": ["server.py"],
"cwd": "/path/to/my_mcp_server",
"env": {
"PYTHONUNBUFFERED": "1" // 禁用Python输出缓冲
}
}
}
}
配置完成后,重启OpenClaw即可在对话中调用自定义工具。
六、进阶:HTTP传输模式
对于需要远程访问的场景,可以使用HTTP传输模式:
# http_server.py - HTTP模式的MCP服务器
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
# 创建SSE传输层(Server-Sent Events)
sse = SseServerTransport("/messages")
async def handle_sse(request):
"""处理SSE连接建立"""
async with sse.connect_sse(
request.scope,
request.receive,
request.send,
) as streams:
await app.run(
streams[0],
streams[1],
app.create_initialization_options(),
)
# 构建Starlette应用
app_http = Starlette(
routes=[
Route("/sse", endpoint=handle_sse),
Route("/messages", endpoint=sse.handle_post_message),
]
)
# 使用uvicorn运行
# uvicorn http_server:app_http --host 0.0.0.0 --port 8080
七、最佳实践总结
开发MCP工具时,建议遵循以下原则:
# 最佳实践要点清单(伪代码示意)
"""
1. 工具描述要清晰准确
- description应详细说明工具功能和适用场景
- 避免模糊描述导致AI误判调用时机
2. 参数设计要合理
- 合理设置required字段,减少AI猜测
- 为参数提供default值,降低调用门槛
3. 异步处理提升性能
- 使用async/await处理耗时操作
- 避免阻塞主线程影响响应速度
4. 错误处理要完善
- 捕获可能的异常,返回友好的错误信息
- 错误信息应指导用户正确使用
5. 安全性考虑
- 对敏感操作添加权限校验
- 验证用户输入,防止注入攻击
"""
八、官方资源
- MCP协议规范:https://modelcontextprotocol.io/
- MCP Python SDK:https://github.com/modelcontextprotocol/python-sdk
- OpenClaw官方文档:https://openclaw.tencent.com/docs
上海华万通信科技有限公司,腾讯系企业软件生态服务商,为企业提供腾讯会议、企业微信、腾讯电子签、DocuSign国际电子签名、Microsoft Teams、Zoom视频会议、AI产品(硅基能量、腾讯云ClawPro/ADP)等一站式解决方案。