MCP 协议为什么是 2026 年 Agent 开发的必学技术?与传统 Function Calling 对比实战
翻了翻 2026 年的招聘信息,AI Agent 相关岗位里 LangChain、LangGraph 基本成了标配,MCP 出现的频率也越来越高。它和传统的 Function Calling 到底差在哪?我直接用三段能跑的 Python 代码,把两种做法从头到尾对比一遍。
太长不看版(TL;DR)
- 传统 Function Calling:工具列表写在应用代码里,LLM 只是帮你"点菜",工具本体的定义、分发、生命周期全部耦合在应用内部
- MCP(Model Context Protocol):Anthropic 于 2024 年 11 月开源的开放协议,2025 年先后移交 Linux 基金会并成立独立治理项目;工具通常以独立进程(MCP Server)运行,任何支持 MCP 的客户端(Claude、Cursor、你自己的 Agent)即插即用
- 核心区别:Function Calling 绑定的是"函数",MCP 标准化的是"协议"
- 2026 年为什么必学:主流 Agent 客户端和框架都原生支持 MCP,官方 + 社区维护的 Server 也越来越多,数据库、浏览器、GitHub、Slack 等场景直接拿来用
- 实战要点:10 行代码写一个 MCP Server,20 行代码在 Client 里动态发现并调用它的工具——全程不需要写死工具 Schema
一、引言:为什么 2026 年突然都提 MCP?
都说 2026 年是 Agent 落地的爆发年,但有个点容易被忽略:Agent 的差距往往不在模型,而在工具链。只会聊天的 Agent 就是个陪聊机器人,能查库、能发邮件、能操作浏览器的 Agent 才是真正的生产力。
我自己折腾 Agent 的过程中发现,每个团队都在做同一件重复劳动:
- 用 JSON Schema 定义工具参数
- 把工具清单塞进 LLM 请求
- 解析返回的
tool_calls - 自己实现一个"分发器"(dispatch)去调用真实函数
- 换一个客户端、换一种语言,全部重来
传统 Function Calling 的痛点也在这:工具被锁死在某个应用、某种语言里。2026 年的诉求是"一套工具,到处都能用",MCP 就是冲着这个来的。2025 年内 OpenAI、Google、微软、AWS 先后宣布支持,它从 Anthropic 的私有规范变成了行业公共协议(依据见文末参考资料)。
打个比方:Function Calling 像打印机自带的驱动,MCP 像 USB 接口。打印机插上 USB,什么电脑都能用,现在行业正在把打印机统一换成 USB 口。
这篇文章适合三类人:刚开始学 Agent 开发的(MCP 是简历加分项)、用 LangChain/LangGraph 做工程的、以及想搞清楚工具调用接下来往哪走的人。
二、先看老办法:传统 Function Calling 是怎么工作的
2.1 原理
传统 Function Calling 是 LLM API(OpenAI、DeepSeek、通义等)内置的能力,流程是一个闭环:
用户问题 → LLM 判断"需要调工具" → 返回结构化参数(tool_calls)
→ 应用代码执行真实函数 → 把结果回填给 LLM → LLM 生成最终回答
关键在最后一步:工具的定义(Schema)、执行(函数本体)、分发(dispatch)全都写在应用代码里。换一个应用,这些就得复制粘贴一遍。
2.2 完整代码示例(OpenAI API 风格)
import json
from openai import OpenAI
client = OpenAI(api_key="sk-xxx") # 换成你的 key
# 1. 在代码里手写工具 Schema
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "查询指定城市的实时天气",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称,如北京"},
},
"required": ["city"],
},
},
},
{
"type": "function",
"function": {
"name": "get_news",
"description": "获取指定领域的新闻头条",
"parameters": {
"type": "object",
"properties": {
"topic": {"type": "string", "description": "新闻领域,如科技"},
},
"required": ["topic"],
},
},
},
]
# 2. 工具本体:真实业务逻辑
def get_weather(city: str) -> str:
# 实际项目中这里调用天气 API / 数据库
return f"{city} 今天晴,气温 26℃,微风"
def get_news(topic: str) -> str:
return f"今日{topic}头条:MCP 生态持续扩张,更多企业接入"
# 3. 分发器:根据工具名手动路由
def dispatch(name: str, arguments: dict) -> str:
if name == "get_weather":
return get_weather(arguments["city"])
if name == "get_news":
return get_news(arguments["topic"])
raise ValueError(f"未知工具: {name}")
# 4. 完整 Agent 循环
def agent_loop(user_input: str, max_rounds: int = 5) -> str:
messages = [{"role": "user", "content": user_input}]
for _ in range(max_rounds):
resp = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools, # 每轮都要把工具清单塞进去
)
msg = resp.choices[0].message
messages.append(msg)
if msg.tool_calls: # LLM 要求调用工具
for tc in msg.tool_calls:
result = dispatch(tc.function.name, json.loads(tc.function.arguments))
messages.append({
"role": "tool",
"tool_call_id": tc.id, # 必须回填这个 ID 才能对应上
"content": result,
})
else:
return msg.content # LLM 直接回答了,结束
raise RuntimeError("超出最大轮数")
if __name__ == "__main__":
print(agent_loop("北京今天天气怎么样?顺便看看科技新闻"))
这段代码有三个绕不开的问题:
- Schema 写死在应用里:工具多一个、少一个,都要改客户端代码并重新发版
- 分发逻辑要自己维护:
dispatch里的 if-else 会随工具数量爆炸 - 不可复用:这套
get_weather换个项目、换种语言(Java/TS)就得重写,且无法被 Cursor、Claude 等现成工具直接使用
三、MCP 是什么:协议的核心概念
MCP(Model Context Protocol,模型上下文协议)是一套开放标准,规定了"AI 应用(Host)"和"工具提供方(Server)"之间怎么通信。
┌─────────────────────────────┐
│ Host(AI 应用 / Agent) │ ← Claude Desktop、Cursor、VS Code、
│ ┌──────────┐ │ 你自己的 Python/JS 应用
│ │ MCP Client │ │
│ └──────────┘ │
└──────────┬──────────────────┘
│ MCP 协议(stdio / HTTP)
┌─────┴─────┬─────────┐
┌────▼───┐ ┌─────▼───┐ ┌───▼────┐
│Server A│ │Server B │ │Server C│ ← 独立的工具服务进程
│数据库 │ │浏览器 │ │GitHub |
└────────┘ └─────────┘ └────────┘
3.1 三个核心角色
| 角色 | 作用 | 类比 |
|---|---|---|
| Host | 运行 Agent 的应用(Claude Desktop、Cursor、你的程序) | 电脑主机 |
| Client | Host 内部的连接器,负责与 Server 通信 | USB 控制器 |
| Server | 独立部署的工具服务(通常为独立进程),暴露工具/资源,只认 MCP 协议,不关心谁来调用 | 打印机 |
3.2 三个核心原语(Primitives)
| 原语 | 是什么 | 对应 Function Calling 的什么 |
|---|---|---|
| Tools(工具) | 可被 LLM 调用的函数,参数由 JSON Schema 描述 | 就是 Function Calling 里的 function |
| Resources(资源) | 可被读取的数据(文档、配置、数据库查询结果),以 URI 标识 | Function Calling 没有对应物 |
| Prompts(提示词) | 可复用的提示词模板 | 类似 system prompt 模板 |
一个关键差异:传统 Function Calling 只有"函数"这一个维度,MCP 多了 Resources 这一层——Agent 可以先读资源,再决定要不要调工具。RAG、配置注入这些场景就多了条标准通道。
3.3 通信方式
- stdio:Client 以子进程方式拉起 Server,通过标准输入输出通信(本地开发最常用)
- HTTP(Streamable HTTP):跨机器、跨语言部署,支持 OAuth 认证,生产环境首选
四、MCP vs 传统 Function Calling:一张表看懂
| 对比维度 | 传统 Function Calling | MCP |
|---|---|---|
| 本质 | LLM API 内置的参数约定 | 独立于模型的开放协议(Linux 基金会治理) |
| 工具定义位置 | 写死在应用代码里 | 由独立 MCP Server 提供,动态发现 |
| Schema 维护 | 每加一个工具就改一次客户端代码 | Server 自描述,Client 调用 list_tools() 自动获取 |
| 跨语言复用 | 每个项目、每种语言各写一份 | 一套 Server,Python/TS/Java 客户端通用 |
| 可被谁使用 | 只有你自己的应用 | Claude、Cursor、Zed、VS Code、你的 Agent 全都能接 |
| 能力维度 | 只有函数调用 | Tools + Resources + Prompts |
| 认证与安全 | 自己设计,各自为政 | 标准化的 OAuth 2.1、授权范围(scopes) |
| 生态 | 无 | 官方 + 社区维护的上千个 Server(Postgres、GitHub、Slack、浏览器…) |
| 学习成本 | 低(会调 API 就行) | 中(理解协议模型,但代码量更少) |
简单说:Function Calling 解决"让 LLM 调我的函数",MCP 解决"让所有 Agent 调所有工具"——一个是应用内的事,一个是行业级的事。
五、实战:跑通你的第一个 MCP
5.1 环境准备
# Python 3.10+,安装官方 SDK(FastMCP 是官方 python-sdk 提供的高阶封装)与 OpenAI SDK
pip install "mcp[cli]" openai
5.2 写一个 MCP Server(10 行代码)
新建 weather_server.py:
from mcp.server.fastmcp import FastMCP
# 创建一个 MCP Server
mcp = FastMCP("weather-server")
# 工具(Tool):可被 LLM 调用的函数
@mcp.tool()
def get_weather(city: str) -> str:
"""查询指定城市的实时天气"""
# 实际项目中这里应调用真实天气 API / 数据库,此处用静态数据演示
weather_map = {"北京": "晴,气温 26℃", "上海": "多云,气温 28℃", "深圳": "阵雨,气温 30℃"}
desc = weather_map.get(city, "晴,气温 25℃")
return f"{city} 今天{desc},微风"
@mcp.tool()
def get_news(topic: str) -> str:
"""获取指定领域的新闻头条"""
return f"今日{topic}头条:MCP 已进入 Linux 基金会治理"
# 资源(Resource):可被读取的数据,以 URI 标识
# 注意:URI 需符合 RFC 3986,推荐使用纯 ASCII(如 city://beijing),避免中文导致的兼容性问题
@mcp.resource("city://beijing")
def beijing_weather() -> str:
"""北京天气的静态资源(可被 Agent 读取后再决定是否调工具)"""
return "北京 今天晴,气温 26℃,微风"
if __name__ == "__main__":
# 以 stdio 方式启动:作为子进程被 Client 拉起
mcp.run(transport="stdio")
注意到没有:没有 JSON Schema,没有 dispatch,也没有 if-else。函数签名(city: str)就是 Schema,FastMCP 自动生成并暴露出去。这就是 MCP 的自描述能力。
5.3 写一个 MCP Client(20 行代码)
新建 weather_client.py:
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
# 1. 配置:以子进程方式启动 Server
server_params = StdioServerParameters(
command="python",
args=["weather_server.py"],
)
# 2. 建立连接 + 握手
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize() # MCP 协议握手
# 3. 动态发现 Server 暴露的所有工具(无需手写 Schema!)
tools = await session.list_tools()
print("=== Server 暴露的工具 ===")
for t in tools.tools:
print(f" - {t.name}: {t.description}")
print(f" schema: {t.inputSchema}")
# 4. 直接调用工具
result = await session.call_tool("get_weather", {"city": "北京"})
print("\n=== 调用 get_weather 结果 ===")
print(result.content[0].text)
# 5. 读取资源
resource = await session.read_resource("city://beijing")
print("\n=== 读取资源 city://beijing ===")
print(resource.contents[0].text)
if __name__ == "__main__":
asyncio.run(main())
运行:
python weather_client.py
预期输出(schema 内容为示意,实际由 FastMCP 根据函数签名自动生成,可能包含 additionalProperties、required 等字段):
=== Server 暴露的工具 ===
- get_weather: 查询指定城市的实时天气
schema: {'type': 'object', 'properties': {'city': {'type': 'string'}}}
- get_news: 获取指定领域的新闻头条
schema: {'type': 'object', 'properties': {'topic': {'type': 'string'}}}
=== 调用 get_weather 结果 ===
北京 今天晴,气温 26℃,微风
=== 读取资源 city://beijing ===
北京 今天晴,气温 26℃,微风
六、实战进阶:让 LLM 动态发现并调用 MCP 工具
这一节讲 MCP 最实用的一点:把 Server 的工具清单自动转成 LLM 认识的 function schema。Agent 每轮拿到的工具列表都是最新的,加工具只改 Server 那一行代码,客户端不用动。
import asyncio
import json
from openai import OpenAI
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
llm = OpenAI(api_key="sk-xxx") # 换成你的 key
def build_tools_schema(mcp_tools) -> list:
"""把 MCP 动态发现的工具,自动转成 LLM 认识的 function schema"""
schemas = []
for t in mcp_tools:
schemas.append({
"type": "function",
"function": {
"name": t.name,
"description": t.description,
"parameters": t.inputSchema, # 直接复用 MCP 的 schema
},
})
return schemas
async def run_agent(prompt: str):
server_params = StdioServerParameters(command="python", args=["weather_server.py"])
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# 1. 动态发现工具 → 自动生成 schema(传统写法里这段要手写)
tools_result = await session.list_tools()
tools_schema = build_tools_schema(tools_result.tools)
# 2. 标准 Agent 循环(和第二节几乎一样,但 schema 是"发现"来的)
messages = [{"role": "user", "content": prompt}]
for _ in range(5):
resp = llm.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools_schema,
)
msg = resp.choices[0].message
messages.append(msg)
if not msg.tool_calls:
print("最终回答:", msg.content)
return
# 3. 工具执行交给 MCP Server,而不是本地 dispatch
for tc in msg.tool_calls:
args = json.loads(tc.function.arguments)
result = await session.call_tool(tc.function.name, args)
messages.append({
"role": "tool",
"tool_call_id": tc.id,
"content": result.content[0].text,
})
if __name__ == "__main__":
asyncio.run(run_agent("北京和上海今天天气怎么样?"))
运行效果:
最终回答: 北京今天晴,气温 26℃,微风;上海今天多云,气温 28℃。
注:此输出与 5.2 节
get_weather函数的静态数据一致;实际运行时,最终回答由模型根据工具返回内容生成,措辞可能略有差异。
跟第二节的代码一比,差别很明显:
| 传统 Function Calling | MCP 版 | |
|---|---|---|
| 工具 Schema | 手写 JSON,写死在代码里 | list_tools() 自动获取 |
| 工具执行 | 本地 dispatch() if-else |
session.call_tool() 一步到位 |
| 新增工具 | 改客户端代码 + 发版 | 只改 Server,客户端零改动 |
| 换客户端 | 全部重写 | 同一个 Server,Cursor/Claude 直接接 |
七、顺手的生态红利:现成 MCP Server 直接用
自己写 Server 是第一步,更省事的是直接用生态里现成的,比如:
@modelcontextprotocol/server-postgres:让 Agent 直接查数据库@modelcontextprotocol/server-filesystem:读写本地文件@modelcontextprotocol/server-github:操作 GitHub 仓库playwright-mcp:让 Agent 控制浏览器- 各类官方 Server:Slack、Notion、Google Drive、Figma…
官方 CLI 一条命令就能注册到 Claude Desktop:
# 全局安装 MCP CLI
pip install "mcp[cli]"
# 用 Inspector 图形化调试你的 Server
mcp dev weather_server.py
# 安装到 Claude Desktop
mcp install weather_server.py
LangChain / LangGraph 也内置了 MCP 适配器,能把 MCP Server 包装成普通 Tool:
# 需要先安装:pip install langchain-mcp-adapters
from langchain_mcp_adapters.tools import load_mcp_tools
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await load_mcp_tools(session) # MCP Server → LangChain Tool
# 然后就可以直接塞进 AgentExecutor / LangGraph 节点
八、什么场景该用哪个?选型建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 快速 Demo、单应用、工具就 2~3 个 | 传统 Function Calling | 零额外架构,代码最少 |
| 工具会持续增加、团队多项目共享 | MCP | 一次开发,多处复用 |
| 工具要接入 Cursor / Claude / VS Code | MCP | 客户端原生支持,别无选择 |
| 企业跨部门、跨语言工具平台 | MCP | 标准协议 + OAuth,治理成本最低 |
| Agent 需要读数据再决定行动(RAG 等) | MCP | Resources 原语天然支持 |
两者不是二选一,而是层级关系:一个是实现细节,一个是行业标准。现在的现实是,就算先用 Function Calling,后面接生态时多半也得包一层 MCP,不如一开始就按 MCP 设计。
九、总结
- 传统 Function Calling 是"应用内"技术:Schema 写死、分发靠自研、换项目就重来。但它简单直接,想搞懂工具调用还是得先学它
- MCP 是"行业级"协议:工具封装成独立 Server,靠
Tools / Resources / Prompts三个原语和stdio / HTTP两种传输,实现"一套工具,所有 Agent 通用" - 生态在涨、主流客户端和框架都原生支持、招聘市场认可度高,2026 年学 MCP 基本是稳的
- 上手路径:10 行代码写 Server → 20 行代码写 Client → 接入 LLM 动态调用 → 复用生态 Server → 用 LangGraph 编排成生产级 Agent
参考资料
更多推荐

所有评论(0)