MCP协议实战:从零搭建一个AI Agent工具服务器,让大模型真正“动手干活“
最近跟几个做AI应用的朋友聊天,发现大家都有同一个痛点:大模型能力确实强,但一到"动手"就歇菜。你让GPT帮你查个数据库?它只能给你写SQL,执行还得你自己来。你让它帮你发个邮件?它只能给你草稿,发送还得你自己点。说真的,这个问题困扰了我挺久。直到我开始用MCP(Model Context Protocol),才感觉事情开始变得不一样了。今天这篇文章,我就从零开始,手把手带你搭一个MCP工具服务器
MCP协议实战:从零搭建一个AI Agent工具服务器,让大模型真正"动手干活"
说白了,大模型再聪明,不能调API、不能读数据库、不能操作文件,那就是个"嘴强王者"。MCP就是解决这个问题的。
前言
最近跟几个做AI应用的朋友聊天,发现大家都有同一个痛点:大模型能力确实强,但一到"动手"就歇菜。你让GPT帮你查个数据库?它只能给你写SQL,执行还得你自己来。你让它帮你发个邮件?它只能给你草稿,发送还得你自己点。
说真的,这个问题困扰了我挺久。直到我开始用MCP(Model Context Protocol),才感觉事情开始变得不一样了。
今天这篇文章,我就从零开始,手把手带你搭一个MCP工具服务器,让大模型真正能"动手干活"。不是那种hello world级别的demo,是一个能实际用起来的工具。

什么是MCP?30秒搞懂
MCP,全称 Model Context Protocol,是Anthropic在2024年底开源的一个协议。你可以把它理解成大模型的USB接口。
以前你想让大模型连数据库,得写一套代码;想让它操作文件,又得写一套;想让它调API,再写一套。每个大模型(GPT、Claude、Gemini)的接入方式还不一样。
MCP统一了这个事儿。你只需要按照MCP协议写一个"工具服务器",所有支持MCP的大模型都能直接用。
架构很简单:
```
用户 → 大模型(MCP客户端)→ MCP协议 → 工具服务器 → 实际操作
```
大模型决定"要不要用工具、用哪个工具",工具服务器负责"怎么执行"。各司其职,干净利落。
环境准备
我用Python来写,因为生态最成熟。需要的东西:
- Python 3.10+
mcpSDK(官方Python包)- 一个支持MCP的客户端(我用Claude Desktop,你用Cursor也行)
先装依赖:
```bash
pip install mcp httpx
```
就这么简单,没有一堆乱七八糟的依赖。

实战:搭建一个"系统信息查询"工具
我做了一个实用的MCP工具服务器,功能包括:
- 查询系统信息 — CPU、内存、磁盘使用率
- 执行Shell命令 — 安全沙箱内的命令执行
- 读取文件内容 — 让大模型能直接读配置文件、日志
不是玩具,是真的能用的东西。
第一步:定义工具服务器骨架
```python
import asyncio
import json
import platform
import subprocess
import os
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
server = Server(“system-tools”)
@server.list_tools()
async def list_tools():
“”“告诉大模型:我有哪些工具可以用”“”
return [
Tool(
name=“get_system_info”,
description=“获取当前系统的CPU、内存、磁盘使用情况”,
inputSchema={“type”: “object”, “properties”: {}, “required”: []}
),
Tool(
name=“run_command”,
description=“在安全沙箱内执行Shell命令,返回输出结果”,
inputSchema={
“type”: “object”,
“properties”: {
“command”: {“type”: “string”, “description”: “要执行的Shell命令”}
},
“required”: [“command”]
}
),
Tool(
name=“read_file”,
description=“读取指定路径的文件内容”,
inputSchema={
“type”: “object”,
“properties”: {
“path”: {“type”: “string”, “description”: “文件的绝对路径”}
},
“required”: [“path”]
}
)
]
```
注意看 list_tools() 这个函数,它的作用就是告诉大模型"我有什么能力"。description 很重要,大模型靠这个来判断什么时候该调用你的工具。
第二步:实现工具逻辑
```python
@server.call_tool()
async def call_tool(name: str, arguments: dict):
if name == “get_system_info”:
import psutil
cpu_percent = psutil.cpu_percent(interval=1)
memory = psutil.virtual_memory()
disk = psutil.disk_usage(‘/’)
info = {
“系统”: platform.system(),
“CPU使用率”: f"{cpu_percent}%“,
“内存”: f”{memory.used / (10243):.1f}GB / {memory.total / (10243):.1f}GB ({memory.percent}%)“,
“磁盘”: f”{disk.used / (10243):.1f}GB / {disk.total / (10243):.1f}GB ({disk.percent}%)"
}
return [TextContent(type=“text”, text=json.dumps(info, ensure_ascii=False, indent=2))]
elif name == "run_command":
cmd = arguments.get("command", "")
dangerous = ["rm -rf", "mkfs", "dd if=", "> /dev/", "chmod 777"]
if any(d in cmd for d in dangerous):
return [TextContent(type="text", text="❌ 命令被安全策略拦截")]
try:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=10)
output = result.stdout if result.returncode == 0 else f"错误: {result.stderr}"
return [TextContent(type="text", text=output[:2000])]
except subprocess.TimeoutExpired:
return [TextContent(type="text", text="❌ 命令执行超时(10秒限制)")]
elif name == "read_file":
path = arguments.get("path", "")
if not os.path.exists(path):
return [TextContent(type="text", text=f"❌ 文件不存在: {path}")]
try:
with open(path, 'r', encoding='utf-8') as f:
content = f.read(50000)
return [TextContent(type="text", text=content)]
except Exception as e:
return [TextContent(type="text", text=f"❌ 读取失败: {str(e)}")]
```
重点说一下安全设计。run_command 这个工具能力很强,但也很危险。所以我加了两层保护:
- 黑名单过滤 — 拦截
rm -rf、mkfs这类毁灭性命令 - 超时限制 — 最多跑10秒,防止死循环
第三步:启动服务器
```python
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream, server.create_initialization_options())
if name == “main”:
asyncio.run(main())
```
整个服务器就这些代码,不到100行。
第四步:配置客户端
以Claude Desktop为例,编辑配置文件:
```json
{
“mcpServers”: {
“system-tools”: {
“command”: “python”,
“args”: [“/path/to/mcp_server.py”]
}
}
}
```
重启Claude Desktop,你就能在对话里直接让它查系统信息、执行命令、读文件了。
踩坑记录
搞MCP开发这段时间,踩过几个坑:
坑1:description写得不好,大模型不调用工具
description 是大模型决定"用不用这个工具"的唯一依据。写得太模糊,它就不用。写得太长,它也容易忽略。建议一句话说清楚"这个工具干什么"。
坑2:inputSchema校验太严格
大模型传参有时候会多传字段、少传字段、类型不对。你的Schema别定义得太死,required 只放真正必须的参数。
坑3:返回内容太长
大模型的上下文窗口有限。你的工具返回结果最好控制在合理长度内。我在 read_file 里限制了50KB,run_command 限制了2000字符。
坑4:Windows和Linux的命令差异
如果你的工具服务器要跨平台用,注意Shell命令的差异。比如 ls 在Windows上是 dir,grep 在Windows上是 findstr。

进阶玩法
基础搞定了,你可以继续扩展:
- 接数据库 — 写一个
query_sql工具,让大模型直接查数据库 - 接API — 封装你常用的API(天气、翻译、搜索)为MCP工具
- 接文件系统 — 让大模型能读写项目文件,变成一个真正的"AI助手"
- 多工具协作 — 一个服务器提供多个相关工具,让大模型自己编排工作流
我个人觉得最有价值的方向是把你的日常运维操作封装成MCP工具。比如查日志、重启服务、清理磁盘、检查告警。以后出了问题,你直接跟大模型说"帮我看看线上什么情况",它自己去查、自己分析、给你结论。
写在最后
MCP这个协议,说大不大,说小不小。它不是什么颠覆性技术,但它解决了一个实际问题:让大模型从"只会说"变成"能做事"。
目前MCP生态还在早期,但增长很快。Claude、Cursor、Windsurf这些主流客户端都已经支持了。OpenAI据说也在跟进。
如果你现在就开始把你的工具封装成MCP服务器,等生态成熟的时候,你就已经领先一步了。
相关资源:
💬 你们在做大模型应用的时候,有没有遇到"大模型不能动手"的问题?欢迎评论区聊聊你的解决方案。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)