从零搭建 MCP Server:用 Python 把任意数据源接入 Claude

为什么需要 MCP?

大型语言模型(LLM)的能力再强,也受限于训练数据的时效性和封闭性------它不知道你的数据库里有什么,也没法直接调用外部 API。MCP(Model Context Protocol)就是为了解决这个问题而诞生的开放协议。

简单说,MCP 定义了一套标准接口:LLM 通过 MCP Client 发现工具 → Client 调用 Server 提供的工具 → Server 执行实际逻辑 → 结果返回 LLM。整个过程就像给 AI 装上了"万能插头"。

对比维度 MCP 传统 Function Calling
协议标准 开放统一 各厂商自定
动态发现 ✅ Server 自动声明工具 ❌ 需预定义 Schema
资源模型 Tools + Resources + Prompts 仅函数调用
多语言支持 任意语言实现 Server SDK 绑定
远程调用 内置 Transport 层 需自己封装

环境准备

MCP 官方提供了 Python SDK,安装非常简单:

bash 复制代码
pip install mcp

要求 Python 3.10+,建议在虚拟环境中操作。

实战:构建一个天气查询 Server

我们要实现一个 MCP Server,它提供两个工具:get_weather(查询指定城市的实时天气)和 get_forecast(查询未来 3 天天气预报)。

python 复制代码
# weather_server.py
from mcp.server import Server, NotificationOptions
from mcp.server.models import InitializationOptions
import mcp.server.stdio
import httpx
import json

server = Server("weather-server")

@server.list_tools()
async def handle_list_tools():
    return [
        {
            "name": "get_weather",
            "description": "查询指定城市的实时天气",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名,如 北京、上海"
                    }
                },
                "required": ["city"]
            }
        },
        {
            "name": "get_forecast",
            "description": "查询未来 3 天的天气预报",
            "inputSchema": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "城市名"}
                },
                "required": ["city"]
            }
        }
    ]

@server.call_tool()
async def handle_call_tool(name: str, arguments: dict):
    city = arguments.get("city", "北京")
    async with httpx.AsyncClient() as client:
        if name == "get_weather":
            resp = await client.get(
                "https://api.openweathermap.org/data/2.5/weather",
                params={"q": city, "appid": "YOUR_KEY", "units": "metric", "lang": "zh_cn"}
            )
            data = resp.json()
            return {
                "content": [{
                    "type": "text",
                    "text": json.dumps({
                        "城市": city,
                        "温度": f"{data['main']['temp']}°C",
                        "天气": data['weather'][0]['description'],
                        "湿度": f"{data['main']['humidity']}%",
                        "风速": f"{data['wind']['speed']}m/s"
                    }, ensure_ascii=False)
                }]
            }
        elif name == "get_forecast":
            resp = await client.get(
                "https://api.openweathermap.org/data/2.5/forecast",
                params={"q": city, "appid": "YOUR_KEY", "units": "metric", "lang": "zh_cn", "cnt": 3}
            )
            data = resp.json()
            days = []
            for item in data["list"]:
                days.append({
                    "时间": item["dt_txt"],
                    "温度": f"{item['main']['temp']}°C",
                    "天气": item["weather"][0]["description"]
                })
            return {
                "content": [{
                    "type": "text",
                    "text": json.dumps(days, ensure_ascii=False, indent=2)
                }]
            }

async def main():
    async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
        await server.run(
            read_stream, write_stream,
            InitializationOptions(server_name="weather-server")
        )

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

核心逻辑就三步:声明工具列表(list_tools)→ 实现工具逻辑(call_tool)→ 启动 Server。

在 Claude Code 中配置

要让 Claude Code 使用这个 MCP Server,在项目根目录创建 .claude/settings.json

json 复制代码
{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["weather_server.py"]
    }
  }
}

重启 Claude Code 后输入"北京的天气怎么样",Claude 就会自动调用 get_weather 工具返回实时数据。

MCP 配置参数详解

参数 类型 说明 示例
command string 启动命令 pythonnodeuvx
args string\[\] 命令参数 ["server.py"]
env object 环境变量(可选) {"API_KEY": "xxx"}
transport string 传输方式 stdio(默认)
disabled boolean 是否禁用 false

进阶:把任意 REST API 封装成工具

上面的模式可以泛化:任何 HTTP API 都可以用同样三步封装成 MCP 工具:

  1. 阅读 API 文档,提取输入参数和返回结构
  2. 定义 inputSchema,把 API 参数映射为 JSON Schema
  3. 用 httpx 发起请求,处理好错误和超时
  4. 返回格式化结果,让 LLM 能直接理解

这样做的好处是:团队的数据服务、内部 API、第三方平台统统可以通过 MCP 暴露给 AI,不需要为每个场景重复写集成代码。

踩坑记录

实战中几个容易翻车的地方:

  • Tool name 不能重复:同一个 Server 里工具名必须唯一,否则启动报错
  • 阻塞操作要加 timeouthttpx 默认不超时,网络慢会导致 LLM 等待太久
  • 返回内容别太长 :单次 content 建议控制在 5000 token 以内,太长影响 LLM 理解
  • 环境变量别硬编码 :API Key 通过 env 字段传入,不写死在代码里
  • 路径兼容性 :Windows 用户注意 command 要用完整路径或确保 python 在 PATH 中

下篇预告

本文实现了基础的 stdio 传输 MCP Server。下篇会介绍 SSE(Server-Sent Events)传输模式,让 MCP Server 可以远程部署、多个 Client 共享------真正把工具变成微服务。欢迎在评论区留下你想深入的方向,我会在后续文章中优先安排。

MCP 的工作原理

理解 MCP 的工作流程,对开发 Server 非常有帮助。一次典型的 MCP 调用分为三个阶段:

第一阶段:初始化(Initialization) Client 启动 Server 进程后,双方通过 JSON-RPC 交换能力信息。Server 声明自己支持哪些协议版本和功能特性,Client 告诉 Server 自己期望的配置。这一步是握手,确保双方在同一频道对话。

第二阶段:工具发现(List Tools) 初始化完成后,Client 调用 tools/list 请求,Server 返回所有可用工具的 JSON Schema 描述。这个描述包含了工具名称、说明和参数结构。Client 拿到这份"菜单"后,LLM 根据用户的问题自主判断该调用哪个工具。

第三阶段:工具调用(Call Tool) 当 LLM 决定使用某个工具时,Client 发起 tools/call 请求,传入工具名和参数。Server 执行实际逻辑后将结果返回,LLM 把结果整合进自己的回复中。

整个流程是被动驱动的------Server 只负责注册和实现工具,什么时候调用、传什么参数,完全由 LLM 根据用户输入动态决定。这种设计让 MCP Server 的开发者只需要关注业务逻辑本身,不需要操心 AI 调度。

协议层对比

选型时常见的几种 AI 工具集成方案:

方案 复杂度 灵活性 适用范围
MCP 跨平台、跨模型工具共享
Function Calling 单模型、固定工具集
LangChain Tool 多模型编排、复杂链路
自定义 HTTP API 简单查询场景

MCP 的最大优势在于去厂商绑定。写一次 Server,Claude、Cursor、VS Code 的 GitHub Copilot 等支持 MCP 的客户端都能直接用。

JSON-RPC 消息格式

MCP 底层用 JSON-RPC 2.0 通信。查看 Server 和 Client 的交互日志,有助于调试问题。一个典型的 tools/call 请求如下:

json 复制代码
// Client → Server
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "get_weather",
    "arguments": {
      "city": "北京"
    }
  }
}

// Server → Client
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{"城市": "北京", "温度": "22°C", "天气": "晴", "湿度": "45%", "风速": "3m/s"}"
      }
    ]
  }
}

理解这个格式后,就算不用 SDK,自己用 Socket 也能实现一个 MCP Server。协议本身并不复杂,SDK 只是帮你省掉了 JSON-RPC 的解析样板代码。

实战经验分享

我在多个项目中接入了 MCP,总结几条实用经验:

第一,工具粒度要适中。一个工具如果做的事情太多,参数列表会很长,LLM 容易传错参数;如果太细,调用次数就会过多。好的实践是一个工具对应一个"业务操作",参数控制在 3-5 个。

第二,错误信息要友好 。当 API 调用失败时,不要只返回错误码。返回人类可读的中文错误描述,LLM 才能理解并尝试修复。例如 "城市名不能为空,请输入有效城市名""error: code 400" 有用得多。

第三,充分利用描述字段description 不只是给人看的------LLM 会用它来理解工具用途。描述写得越清楚,LLM 选对工具的概率越高。建议包含使用场景和参数格式说明。

第四,善用 Resources 能力 。MCP 除了 Tools(函数调用)还支持 Resources(资源读取)和 Prompts(提示模板)。比如可以让 Server 提供 weather://current/北京 这样的资源 URI,Client 直接读取而非调用函数。这个能力在很多教程里被忽视,但实际非常实用。

总结

MCP 正在快速成为 AI 工具集成的事实标准。Claude Code、Cursor、VS Code、JetBrains 等主流开发工具都已支持,生态越来越丰富。用 Python 构建一个 MCP Server 并不复杂------常规的 HTTP API 封装,一两个小时就能跑通。

关键收获:

  • MCP 是开放协议,写一次 Server 多客户端通用
  • Python SDK 封装了 JSON-RPC 细节,开发者只需要关注业务逻辑
  • 工具粒度、错误信息、描述文档直接影响 LLM 调用成功率
  • 从简单的天气查询开始,逐步扩展到内部数据服务、数据库查询、文件操作等

如果还在纠结"AI 怎么接入我的数据",MCP 就是目前最直接的答案。

相关推荐
老程序猿1 小时前
AI 的「狼来了」,该不该信?一次真正感受到「恶意提示词」的误报
aigc·ai编程
AI袋鼠帝3 小时前
开源「仓颉.Skill」2.0,你现在可以蒸馏任何视频!
开源·aigc
AI袋鼠帝3 小时前
懂事的Agent已经自己看屏幕干活了!效率起飞~
aigc
threerocks14 小时前
一用一个不吱声的视频解析 Skill,你值得拥有
aigc·ai编程
吴佳浩16 小时前
AI 工程师知识地图:模型格式、框架、部署工具一次讲明白
人工智能·aigc·ai编程
后端小肥肠17 小时前
小红书虚拟商品怎么做?我先用 Skill 跑通了壁纸品类
人工智能·aigc·agent
feiyu_gao17 小时前
从零搭建个人 AI 工作台:一个管理者的 3 个月实验
人工智能·aigc·团队管理
JEECG官方17 小时前
Claude Code Loop 快速入门:从一行命令到自动迭代
aigc
AlbertZein19 小时前
别只盯着最强模型了,Agent 场景更该看这类 Flash 档模型
aigc·openai·ai编程