MCP协议实战:从零搭建一个AI Agent工具服务器,让大模型真正"动手干活"

说白了,大模型再聪明,不能调API、不能读数据库、不能操作文件,那就是个"嘴强王者"。MCP就是解决这个问题的。

前言

最近跟几个做AI应用的朋友聊天,发现大家都有同一个痛点:大模型能力确实强,但一到"动手"就歇菜。你让GPT帮你查个数据库?它只能给你写SQL,执行还得你自己来。你让它帮你发个邮件?它只能给你草稿,发送还得你自己点。

说真的,这个问题困扰了我挺久。直到我开始用MCP(Model Context Protocol),才感觉事情开始变得不一样了。

今天这篇文章,我就从零开始,手把手带你搭一个MCP工具服务器,让大模型真正能"动手干活"。不是那种hello world级别的demo,是一个能实际用起来的工具。

MCP架构

什么是MCP?30秒搞懂

MCP,全称 Model Context Protocol,是Anthropic在2024年底开源的一个协议。你可以把它理解成大模型的USB接口

以前你想让大模型连数据库,得写一套代码;想让它操作文件,又得写一套;想让它调API,再写一套。每个大模型(GPT、Claude、Gemini)的接入方式还不一样。

MCP统一了这个事儿。你只需要按照MCP协议写一个"工具服务器",所有支持MCP的大模型都能直接用。

架构很简单:

```
用户 → 大模型(MCP客户端)→ MCP协议 → 工具服务器 → 实际操作
```

大模型决定"要不要用工具、用哪个工具",工具服务器负责"怎么执行"。各司其职,干净利落。

环境准备

我用Python来写,因为生态最成熟。需要的东西:

  • Python 3.10+
  • mcp SDK(官方Python包)
  • 一个支持MCP的客户端(我用Claude Desktop,你用Cursor也行)

先装依赖:

```bash
pip install mcp httpx
```

就这么简单,没有一堆乱七八糟的依赖。

MCP代码示例

实战:搭建一个"系统信息查询"工具

我做了一个实用的MCP工具服务器,功能包括:

  1. 查询系统信息 — CPU、内存、磁盘使用率
  2. 执行Shell命令 — 安全沙箱内的命令执行
  3. 读取文件内容 — 让大模型能直接读配置文件、日志

不是玩具,是真的能用的东西。

第一步:定义工具服务器骨架

```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 这个工具能力很强,但也很危险。所以我加了两层保护:

  1. 黑名单过滤 — 拦截 rm -rfmkfs 这类毁灭性命令
  2. 超时限制 — 最多跑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上是 dirgrep 在Windows上是 findstr

MCP进阶

进阶玩法

基础搞定了,你可以继续扩展:

  1. 接数据库 — 写一个 query_sql 工具,让大模型直接查数据库
  2. 接API — 封装你常用的API(天气、翻译、搜索)为MCP工具
  3. 接文件系统 — 让大模型能读写项目文件,变成一个真正的"AI助手"
  4. 多工具协作 — 一个服务器提供多个相关工具,让大模型自己编排工作流

我个人觉得最有价值的方向是把你的日常运维操作封装成MCP工具。比如查日志、重启服务、清理磁盘、检查告警。以后出了问题,你直接跟大模型说"帮我看看线上什么情况",它自己去查、自己分析、给你结论。

写在最后

MCP这个协议,说大不大,说小不小。它不是什么颠覆性技术,但它解决了一个实际问题:让大模型从"只会说"变成"能做事"

目前MCP生态还在早期,但增长很快。Claude、Cursor、Windsurf这些主流客户端都已经支持了。OpenAI据说也在跟进。

如果你现在就开始把你的工具封装成MCP服务器,等生态成熟的时候,你就已经领先一步了。


相关资源:


💬 你们在做大模型应用的时候,有没有遇到"大模型不能动手"的问题?欢迎评论区聊聊你的解决方案。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐