下半年:铜还能杀回来吗
2026-07-08 3386693
2026-07-08 0
搭好一个 Agent 之后,第一件事往往是给它加工具:搜索、代码执行、数据库查询、API 调用……

用传统的 Function Calling,工具定义是这样的:
@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 架构 │
├──────────────────┬──────────────────────────────────────────────┤
│ 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 是独立进程:
独立进程意味着:工具可以用任何语言实现,可以被任意数量的 Agent 共享,更新工具不影响 Agent 代码。
@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)
实测三个问题:
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%.
功能完全正常。问题在架构层面:
工具定义在 THIS file: ['calculator', 'text_stats', 'weather_mock']如果 Agent B 也需要这些工具:copy-paste 或 re-import
如果修改 calculator 逻辑:需要找到所有用到它的 Agent 文件
如果工具是 TypeScript 实现的:这套方案根本走不通
用 FastMCP 实现同样的三个工具,作为独立 MCP Server:
# tools_server.py
from mcp.server.fastmcp import FastMCPmcp = 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 侧连接并发现工具:
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_clientserver_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() 返回:
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 代码里没有写 calculator、text_stats、weather_mock 这些名字。它只调用了 list_tools()——工具目录是从 Server 动态获取的。
直接调用工具(不经过 LLM):
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}
langchain-mcp-adapters 把 MCP 工具模式自动转换成 LangChain Tool 对象,Agent 代码和 Demo 1 几乎一模一样——区别在于工具来源:
from langchain_mcp_adapters.client import MultiServerMCPClientclient = 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()。
实测三问题回答:
问题:
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 协议调用了工具。
维度 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 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 工具
工具设计
description 写清楚工具能做什么、不能做什么(LLM 依赖这个决策是否调用)Server 实现
mcp.server.Server传输选择
客户端集成
langchain-mcp-adapters 用于 LangChain/LangGraph 集成await agent.ainvoke(),不用 agent.invoke()MultiServerMCPClient 统一管理连接五个核心结论:
list_tools() 让 Agent 不需要知道工具名就能发现和使用工具ainvoke()下一篇:A2A 协议与 Agent 网络 —— Agent 和 Agent 之间怎么协作?MCP 解决的是 Agent ↔ 工具,A2A 解决的是 Agent ↔ Agent。
更多实用知识和有趣产品,欢迎访问我的个人主页