Agent 系列(10):MCP 协议——工具生态的标准化接入

工具越来越多,管理越来越乱

搭好一个 Agent 之后,第一件事往往是给它加工具:搜索、代码执行、数据库查询、API 调用......

用传统的 Function Calling,工具定义是这样的:

python 复制代码
@tool
def search_jira(query: str) -> str:
    """搜索 Jira 工单"""
    ...

@tool
def query_database(sql: str) -> str:
    """执行 SQL 查询"""
    ...

agent = create_react_agent(model=llm, tools=[search_jira, query_database, ...])

这套方案很好用------直到你开始构建第二个 Agent。

问题出现了 :Agent B 也需要 search_jira。你把函数 import 过去,或者复制一份。然后是 Agent C、Agent D。工具定义开始在代码库里四处漂移。某天你修改了 search_jira 的逻辑,你需要找到所有引用它的地方------它们分散在四个不同的文件里。

这就是 MCP(Model Context Protocol) 要解决的问题:把工具从"每个 Agent 自己定义"变成"统一的服务,所有 Agent 按协议连接"


MCP 的三层架构

MCP 把工具调用拆成三个角色:

arduino 复制代码
┌────────────────────────────────────────────────────────────────┐
│                        MCP 架构                                 │
├──────────────────┬──────────────────────────────────────────────┤
│ Host             │ 承载 Agent 的环境:Claude Desktop、          │
│                  │ Claude Code、自定义 LangChain App            │
│                  │ 包含一个或多个 MCP Client                    │
├──────────────────┼──────────────────────────────────────────────┤
│ Client           │ 内嵌在 Host 里的协议客户端                   │
│                  │ 负责与 MCP Server 建立连接和通信             │
├──────────────────┼──────────────────────────────────────────────┤
│ Server           │ 独立进程,暴露工具(Tools)                  │
│                  │ 也可以暴露资源(Resources)和提示(Prompts) │
└──────────────────┴──────────────────────────────────────────────┘

通信方式:
  本地:stdio(标准输入输出,子进程)
  远程:HTTP + SSE(Server-Sent Events)

与 Function Calling 的核心区别在于 Server 是独立进程

  • Function Calling:工具是 Python 函数,写在 Agent 代码里,同进程调用
  • MCP:工具是独立服务,Agent(Client)通过 JSON-RPC 协议跨进程调用

独立进程意味着:工具可以用任何语言实现,可以被任意数量的 Agent 共享,更新工具不影响 Agent 代码。


Demo 1:传统 Function Calling 的问题

python 复制代码
@lc_tool
def calculator(expression: str) -> str:
    """Evaluate a simple arithmetic expression."""
    ...

@lc_tool
def text_stats(text: str) -> str:
    """Return word count, sentence count, and character count."""
    ...

@lc_tool
def weather_mock(city: str) -> str:
    """Return mock weather data for a city."""
    ...

traditional_tools = [calculator, text_stats, weather_mock]
agent = create_react_agent(model=llm, tools=traditional_tools)

实测三个问题:

vbnet 复制代码
Q: What is 2 ** 10 + 100 / 4?
A: The result of 2 ** 10 + 100 / 4 is 1049.0.

Q: Analyze this text: 'Python is elegant. It is readable. Everyone loves it!'
A: 9 words, 3 sentences, 53 characters.

Q: What's the weather in Beijing?
A: sunny, 25°C, humidity 40%.

功能完全正常。问题在架构层面:

go 复制代码
工具定义在 THIS file: ['calculator', 'text_stats', 'weather_mock']

如果 Agent B 也需要这些工具:copy-paste 或 re-import
如果修改 calculator 逻辑:需要找到所有用到它的 Agent 文件
如果工具是 TypeScript 实现的:这套方案根本走不通

Demo 2:MCP Server------动态工具发现

用 FastMCP 实现同样的三个工具,作为独立 MCP Server:

python 复制代码
# tools_server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("demo-tools")

@mcp.tool()
def calculator(expression: str) -> str:
    """Evaluate a simple arithmetic expression (e.g. '2 ** 10', '100 / 7')."""
    ...

@mcp.tool()
def text_stats(text: str) -> str:
    """Return word count, sentence count, and character count for the given text."""
    ...

@mcp.tool()
def weather_mock(city: str) -> str:
    """Return mock weather data for a city (demo only --- not real data)."""
    ...

if __name__ == "__main__":
    mcp.run(transport="stdio")

Client 侧连接并发现工具:

python 复制代码
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

server_params = StdioServerParameters(command="python", args=["tools_server.py"])

async with stdio_client(server_params) as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()

        # 动态发现工具------client 代码里没有任何工具名硬编码
        tools_result = await session.list_tools()

实测 list_tools() 返回:

sql 复制代码
Server: demo-tools
Discovered 3 tools:
  ● calculator       --- Evaluate a simple arithmetic expression (e.g. '2 ** 10', '100 / 7').
  ● text_stats       --- Return word count, sentence count, and character count for the given text.
  ● weather_mock     --- Return mock weather data for a city (demo only --- not real data).

关键点:client 代码里没有写 calculatortext_statsweather_mock 这些名字 。它只调用了 list_tools()------工具目录是从 Server 动态获取的。

直接调用工具(不经过 LLM):

scss 复制代码
calculator('2 ** 10 + 100 / 4')  → 2 ** 10 + 100 / 4 = 1049.0
weather_mock('Shanghai')          → {"city": "Shanghai", "temp": 22, "condition": "cloudy", "humidity": 75}
text_stats(...)                   → {"words": 9, "sentences": 3, "chars": 53}

Demo 3:LLM Agent 通过 MCP 调用工具

langchain-mcp-adapters 把 MCP 工具模式自动转换成 LangChain Tool 对象,Agent 代码和 Demo 1 几乎一模一样------区别在于工具来源:

python 复制代码
from langchain_mcp_adapters.client import MultiServerMCPClient

client = MultiServerMCPClient(
    {
        "demo-tools": {
            "command": "python",
            "args": ["tools_server.py"],
            "transport": "stdio",
        }
    }
)
mcp_tools = await client.get_tools()
# → ['calculator', 'text_stats', 'weather_mock']
# 工具定义在 Server 里,不在这个文件里

agent = create_react_agent(model=llm, tools=mcp_tools)
result = await agent.ainvoke({"messages": [HumanMessage(multi_q)]})

注意:MCP 工具是异步的,必须用 await agent.ainvoke(),不能用同步的 agent.invoke()

实测三问题回答:

vbnet 复制代码
问题:
  1. What is sqrt(144) + 2 ** 8?
  2. What's the weather in Shenzhen?
  3. Count the stats for: 'MCP is a protocol. It standardizes tools. Agents love it!'

回答:
  1. The result of sqrt(144) + 2 ** 8 is 260.
  2. (LLM 调用了 weather_mock 工具,但因为工具标注了 "demo only",
       LLM 在回答里加了 "not real-time data" 的声明)
  3. word count: 15, sentence count: 1, character count: 52

三个工具的 CallToolRequest 都出现在 Server 日志里,说明 Agent 确实通过 MCP 协议调用了工具。


MCP vs Function Calling 对比矩阵

arduino 复制代码
维度              Function Calling           MCP
──────────────────────────────────────────────────────────────────────
工具定义位置       在 Agent 代码里            在独立 MCP Server 里
工具发现方式       import / 硬编码             list_tools()(动态)
多 Agent 复用      copy-paste 或 re-import    所有 Agent 连接同一 Server
更新工具逻辑       修改每个 Agent 文件          只修改 Server
跨语言支持         同语言(Python to Python)  任意语言(JSON-RPC 协议)
调用方式           同进程函数调用              跨进程 JSON-RPC
启动开销           无(函数调用)              有(子进程启动)
适用规模           单 Agent / 小型项目         多 Agent / 团队协作

知名 MCP Server 生态

MCP 的价值很大程度上来自已有的 Server 生态:

vbscript 复制代码
官方 MCP Servers(@modelcontextprotocol/):
  server-filesystem   --- 本地文件系统读写
  server-github       --- GitHub Repo / Issue / PR 操作
  server-postgres     --- PostgreSQL 查询
  server-brave-search --- 网络搜索(需要 Brave API Key)

社区维护:
  搜索 awesome-mcp-servers 获取完整列表
  包括:Jira、Slack、Notion、Linear、Figma 等

Claude Code 的工具就是通过 MCP 接入的:
  → 文件读写、终端命令、代码搜索......都是 MCP Server 工具

MCP Server 开发 Checklist

工具设计

  • 每个工具做一件事,输入/输出有明确 Schema
  • description 写清楚工具能做什么、不能做什么(LLM 依赖这个决策是否调用)
  • 敏感操作(写文件、执行命令)加确认步骤或白名单校验

Server 实现

  • FastMCP 适合快速开发;需要更细粒度控制时用底层 mcp.server.Server
  • 工具函数返回字符串(MCP 协议的 TextContent 类型)
  • 错误处理:返回描述性错误字符串,不要让异常传播到协议层

传输选择

  • 本地工具 → stdio(子进程启动,零配置)
  • 远程/共享工具 → HTTP + SSE(需要认证和网络配置)
  • 生产环境 → 考虑连接池和超时设置

客户端集成

  • langchain-mcp-adapters 用于 LangChain/LangGraph 集成
  • MCP 工具是异步的:用 await agent.ainvoke(),不用 agent.invoke()
  • 多个 Server 时,MultiServerMCPClient 统一管理连接

总结

五个核心结论:

  1. MCP 解决的不是"怎么调工具",而是"工具怎么管理和共享":Function Calling 是单 Agent 内的工具绑定,MCP 是跨 Agent 的工具服务化
  2. 动态发现是关键能力list_tools() 让 Agent 不需要知道工具名就能发现和使用工具
  3. 独立进程带来跨语言能力:JavaScript 实现的 MCP Server 可以被 Python Agent 调用,反之亦然
  4. Claude Code 的工具就是 MCP:你用 Claude Code 读文件、执行命令,背后走的就是这套协议
  5. 异步调用是 MCP 工具的约定 :MCP 工具通过异步调用执行,LangChain 集成时必须用 ainvoke()

下一篇:A2A 协议与 Agent 网络 ------ Agent 和 Agent 之间怎么协作?MCP 解决的是 Agent ↔ 工具,A2A 解决的是 Agent ↔ Agent。


参考资料


更多实用知识和有趣产品,欢迎访问我的个人主页

相关推荐
极客老王说Agent1 小时前
屏幕理解能力是下一代自动化的关键吗?2026年自动化范式演进深度解析
运维·人工智能·ai·chatgpt·自动化
YueJoy.AI2 小时前
低算力场景下中小企业接入大模型的商业化路径
人工智能·ai·语言模型
smart19982 小时前
U.2 NVMe全闪磁盘阵列让AI, ML, HPC业务运行稳性高效
人工智能·科技·存储
懷淰メ2 小时前
【AI加持】基于PyQt+YOLO+DeepSeek的疟原虫检测系统(详细介绍)
人工智能·yolo·计算机视觉·pyqt·医疗·ai分析·疟原虫
Black蜡笔小新2 小时前
自动化AI算法训练服务器DLTM训推一体化平台助力农业生产管理实现安全智能化
人工智能·算法·自动化
米小虾2 小时前
Claude Code、Codex、Cursor三分天下:2026年AI编程Agent生态全景剖析
人工智能·agent
ZHW_AI课题组2 小时前
腾讯云调用IP定位
人工智能·python·机器学习
Wch1G0z8A3 小时前
Google 开源了啥,让 AI Agent 碰数据库不再是定时炸弹
数据库·人工智能·开源