5分钟让你的AI Agent接入MCP工具:Python实现

---

title: "5分钟让你的AI Agent接入MCP工具:Python实现"

date: 2026-05-26

description: "从零到一,用Python让你的AI Agent快速接入MCP协议,调用任意工具"

tags: [Python, AI Agent, MCP, 工具调用, LLM]

---


> 本文同步发布在 Agent评测站


---


为什么要关注MCP?


MCP(Model Context Protocol)是由 Anthropic 提出的开放协议,它规范了 LLM 应用与外部工具之间的通信方式。简单说:你写一个 MCP Server 暴露工具,任何兼容 MCP 的 Agent 都能直接调用。不再需要为每个 Agent 单独写工具适配层。


今天的实战目标:用 Python 实现一个 MCP Client,让它调用一个“天气查询”MCP Server,整个过程控制在 5 分钟内可复现。


准备工作


pip install mcp httpx

确保 Python >= 3.10。


第一步:写一个 MCP Server


我们先定义一个简单的天气查询工具。MCP Server 负责注册工具并处理调用请求。


创建 weather_server.py


# weather_server.py
import json
import random
from mcp.server import Server, stdio_server

app = Server("weather-agent")

@app.tool()
async def get_weather(city: str) -> str:
    """查询指定城市的天气情况"""
    # 模拟天气数据,实际项目可对接真实API
    weathers = ["晴天", "多云", "小雨", "阴天", "大风"]
    temps = random.randint(5, 38)
    weather = random.choice(weathers)
    return json.dumps({
        "city": city,
        "weather": weather,
        "temperature": f"{temps}°C",
        "humidity": f"{random.randint(30, 90)}%",
        "advice": "出门记得看天气"
    }, ensure_ascii=False)

@app.tool()
async def get_air_quality(city: str) -> str:
    """查询空气质量指数"""
    aqi = random.randint(20, 200)
    level = "优" if aqi < 50 else "良" if aqi < 100 else "轻度污染" if aqi < 150 else "中度污染"
    return json.dumps({
        "city": city,
        "aqi": aqi,
        "level": level
    }, ensure_ascii=False)

if __name__ == "__main__":
    stdio_server.run(app)

关键点:

  • @app.tool() 装饰器将函数暴露为 MCP 工具
  • 函数签名中的类型注解会被 MCP 自动解析为工具参数 schema
  • 返回 JSON 字符串即可,Client 端会解析

第二步:写 MCP Client 连接 Server


MCP 支持 stdio 模式和 SSE 模式。本地开发最方便的是 stdio——你的 Agent 启动 Server 子进程,通过 stdin/stdout 通信。


创建 mcp_client.py


# mcp_client.py
import asyncio
import json
import subprocess
from mcp.client import stdio_client

async def main():
    # 启动 MCP Server 子进程
    server_process = subprocess.Popen(
        ["python", "weather_server.py"],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )

    # 通过 stdio 建立 MCP 连接
    async with stdio_client(server_process) as session:
        # 获取 Server 提供的工具列表
        tools = await session.list_tools()
        print("=== 可用工具 ===")
        for tool in tools:
            print(f"  - {tool.name}: {tool.description}")
            print(f"    参数: {tool.input_schema}")

        print("\n=== 调用天气查询 ===")
        result = await session.call_tool(
            "get_weather",
            arguments={"city": "北京"}
        )
        print(json.loads(result.content[0].text))

        print("\n=== 调用空气质量 ===")
        result = await session.call_tool(
            "get_air_quality",
            arguments={"city": "上海"}
        )
        print(json.loads(result.content[0].text))

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

运行:


python mcp_client.py

输出示例:


=== 可用工具 ===
  - get_weather: 查询指定城市的天气情况
    参数: {'type': 'object', 'properties': {'city': {'type': 'string'}}}
  - get_air_quality: 查询空气质量指数
    参数: {'type': 'object', 'properties': {'city': {'type': 'string'}}}

=== 调用天气查询 ===
{'city': '北京', 'weather': '多云', 'temperature': '24°C', 'humidity': '65%', 'advice': '出门记得看天气'}

=== 调用空气质量 ===
{'city': '上海', 'aqi': 85, 'level': '良'}

第三步:接入 LLM Agent,让模型自己决定调用哪个工具


手动调工具有什么意思?真正的 Agent 是让 LLM 自主决策调用哪个工具。


我们需要一个函数调用(Function Calling) 环节:把 MCP Server 暴露的工具列表转换成 LLM 可理解的 tools 格式,模型返回的 tool_call 再转成 MCP 调用。


创建 llm_agent.py


# llm_agent.py
import asyncio
import json
import subprocess
from openai import AsyncOpenAI
from mcp.client import stdio_client

client = AsyncOpenAI(
    api_key="your-api-key",
    base_url="https://api.openai.com/v1"
)

async def main():
    server_process = subprocess.Popen(
        ["python", "weather_server.py"],
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True
    )

    async with stdio_client(server_process) as session:
        # 1. 获取 MCP tools 并转换成 OpenAI function calling 格式
        mcp_tools = await session.list_tools()
        openai_tools = []
        for tool in mcp_tools:
            openai_tools.append({
                "type": "function",
                "function": {
                    "name": tool.name,
                    "description": tool.description,
                    "parameters": tool.input_schema
                }
            })

        # 2. 用户提问
        messages = [
            {"role": "user", "content": "北京今天天气怎么样?空气质量如何?"}
        ]

        # 3. 调用 LLM
        response = await client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=openai_tools,
            tool_choice="auto"
        )

        msg = response.choices[0].message
        messages.append(msg)

        # 4. 处理 tool_calls
        if msg.tool_calls:
            for tc in msg.tool_calls:
                func_name = tc.function.name
                args = json.loads(tc.function.arguments)
                print(f"[Agent] 调用工具: {func_name}({args})")

                result = await session.call_tool(func_name, arguments=args)
                tool_result = result.content[0].text

                messages.append({
                    "role": "tool",
                    "tool_call_id": tc.id,
                    "content": tool_result
                })

            # 5. 将工具结果传给 LLM 生成最终回答
            final_response = await client.chat.completions.create(
                model="gpt-4o",
                messages=messages
            )
            print(final_response.choices[0].message.content)
        else:
            print(msg.content)

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

输出:


[Agent] 调用工具: get_weather({'city': '北京'})
[Agent] 调用工具: get_air_quality({'city': '北京'})

北京今天天气多云,气温约24°C,湿度65%。
空气质量指数为85,属于"良"级别,适合户外活动。
出门记得看天气哦!

流程拆解:


用户提问
  → LLM 返回 tool_calls(自主决策调用哪些工具)
  → Agent 转发给 MCP Server 执行
  → 结果返回给 LLM 生成自然语言回复
  → 最终回答呈现给用户

第四步:用 SSE 模式连接远程 MCP Server


如果 MCP Server 部署在远程服务器上,需要用 SSE(Server-Sent Events)模式通信。


Server 端(用 FastAPI 或直接 mcp 包提供的 SSE 支持):


# weather_sse_server.py
from mcp.server import Server, sse_server

app = Server("weather-agent")

@app.tool()
async def get_weather(city: str) -> str:
    # ... 同上
    pass

if __name__ == "__main__":
    sse_server.run(app, host="0.0.0.0", port=8000)

Client 端通过 HTTP 连接:


# sse_client.py
from mcp.client import sse_client
import asyncio

async def main():
    async with sse_client("http://your-server:8000/mcp") as session:
        tools = await session.list_tools()
        print(tools)
        result = await session.call_tool("get_weather", arguments={"city": "深圳"})
        print(result.content[0].text)

asyncio.run(main())

进阶技巧


1. 工具调用超时控制


# 为每个工具调用设置超时
result = await asyncio.wait_for(
    session.call_tool("get_weather", arguments={"city": "广州"}),
    timeout=10.0  # 10秒超时
)

2. 批量工具注册


如果你有大量工具,可以用目录扫描自动注册:


import os
import importlib

@app.startup()
async def register_tools():
    tools_dir = "tools"
    for f in os.listdir(tools_dir):
        if f.endswith(".py") and not f.startswith("_"):
            mod = importlib.import_module(f"tools.{f[:-3]}")
            for attr in dir(mod):
                obj = getattr(mod, attr)
                if callable(obj) and hasattr(obj, "_mcp_tool"):
                    app.add_tool(obj)

3. 带上下文的工具(依赖注入)


MCP 支持在工具函数中注入上下文,比如获取当前对话历史:


@app.tool()
async def query_database(sql: str, ctx: dict) -> str:
    # ctx 包含 session_id, user_id 等信息
    user_id = ctx.get("user_id")
    # 按用户权限执行查询
    ...

4. 配合 LangChain / CrewAI / AutoGen 使用


MCP 社区已经有很多集成。例如 LangChain 的 MCPAdapter


from langchain_mcp import MCPAdapter
from langchain.agents import create_openai_functions_agent

# 将 MCP tools 包装成 LangChain tools
adapter = MCPAdapter(session)
langchain_tools = await adapter.get_tools()

agent = create_openai_functions_agent(
    llm=llm,
    tools=langchain_tools,
    prompt=prompt
)

项目结构与生产建议


my-mcp-agent/
├── servers/           # MCP Server 实现
│   ├── weather.py
│   ├── database.py
│   └── file_search.py
├── agent/             # Agent 逻辑
│   ├── mcp_client.py
│   └── llm_agent.py
├── config.yaml        # Server 连接配置
└── main.py

生产环境建议:


  • 使用进程池管理多个 MCP Server:每个 Server 一个子进程,通过 stdio 通信,出错后自动重启
  • 工具注册中心:启动时扫描所有 Server,构建统一工具列表
  • 安全沙箱:MCP Server 应该运行在受限环境中,避免任意命令执行
  • 日志与监控:记录每个工具调用的入参、出参、耗时,方便调试和审计

踩坑记录


  1. subprocess 挂死:如果你用 subprocess.Popen 但没正确处理 stdout/stderr 的缓冲区,进程可能会阻塞。解决方案是用 asyncio.create_subprocess_exec 代替,或设置 bufsize=1(行缓冲)。

  1. Tool Call 格式不匹配:不同 LLM 厂商的 tool_call 格式有微妙差异。OpenAI 返回 function.arguments 是字符串,而 Gemini 可能直接返回 dict。做一层适配器很必要。

  1. 并发调用冲突:如果多个 Agent 共享一个 MCP Server 进程,注意加锁或使用独立进程。

  1. Server 崩溃:MCP stdio 模式下 Server 进程挂了 Client 并不知道。建议加心跳检测,5 秒无响应则重启。

生态工具推荐


| 工具 | 说明 |

|------|------|

| mcp-python-sdk | Anthropic 官方 Python SDK |

| mcp-cli | 命令行测试工具,快速验证 Server 是否正常 |

| fastmcp | 社区轻量框架,简化的装饰器 API |

| mcp-proxy | 将 stdio Server 暴露为 SSE 服务 |

| langchain-mcp | LangChain 的 MCP 集成包 |


小结


MCP 的价值在于标准化:Server 只写一次,任何 MCP 兼容的 Client、框架、IDE 都能用。Python 生态中实现起来非常直接——装饰器定义工具,stdio/sse 通信,几行代码就能让你的 Agent 拥有调用外部工具的能力。


如果还手动为每个工具写 if-else 分支,不妨试试 MCP,写完一次你就回不去了。


你遇到过什么坑?评论区聊聊。


---


本文同步发布在 Agent评测站