本期导读
本文将详细讲解这两项关键技术,它们让大模型从"只会说"变成"能做事"。我们会通过实际案例,帮助你理解这两种技术的原理、差异和应用场景。
本文内容包括:
- 通过实际问题理解为什么需要这些技术
- Function Calling的工作原理和应用
- MCP协议的设计理念和三种传输方式对比
- 两种技术的详细对比分析(重点说明应用场景差异)
- Python完整代码实现(LangChain 0.3.79)
- MCP Inspector调试工具使用指南
- MCP Docker toolkit实战
- MCP Server资源推荐
- 生产环境的实战案例
一、从一个实际需求说起
1.1 只会"说"的AI有什么局限?
假设你正在开发一个智能助手,用户可以问它各种问题。目前的AI系统已经很强大了,但看看这个场景:
用户:"帮我查一下明天北京的天气"
传统大模型的回答:
"我无法直接查询实时天气信息。我的知识截止到2023年10月,
无法访问外部API。建议你访问天气网站或使用天气APP查询。"
问题:
- AI知道用户想做什么,但做不了
- 只能给出"建议",而不是"帮用户完成"
- 用户体验很差,AI显得很"笨"
这就是大模型的一个根本性限制:它只能生成文本,不能执行操作。
1.2 我们希望AI能做什么?
理想情况下,当用户问"明天北京的天气",我们希望AI能够:
- 理解用户的意图(查询天气)
- 知道需要调用天气API
- 构造正确的API调用参数(城市:北京,日期:明天)
- 执行API调用
- 将结果用自然语言呈现给用户
用户:"帮我查一下明天北京的天气"
理想的AI助手:
1. [内部处理] 识别需要调用天气API
2. [执行操作] 调用 get_weather(city="北京", date="明天")
3. [获取结果] {"temperature": "15-25°C", "weather": "晴", "wind": "微风"}
4. [生成回答] "明天北京天气晴朗,气温15-25°C,有微风,适合外出活动。"
这才是真正有用的AI助手!
1.3 更多实际场景
场景1:企业办公助手
用户:"帮我给张三发个邮件,提醒他下午3点开会"
需要的能力:
- 调用邮件系统API
- 查询通讯录获取张三的邮箱
- 发送邮件
场景2:数据分析助手
用户:"统计一下上个月销售额排名前10的产品"
需要的能力:
- 连接数据库
- 执行SQL查询
- 处理和展示数据
场景3:智能客服
用户:"我要退订这个月的订阅"
需要的能力:
- 查询用户订阅信息
- 调用订阅管理系统
- 执行退订操作
- 发送确认消息
这些场景的共同点是:AI需要能够调用外部工具和系统。
1.4 两种解决方案的诞生
为了解决这个问题,业界出现了两种主要方案:
方案1:Function Calling(函数调用)
- 由OpenAI在GPT-3.5/4中引入
- 让大模型能够调用预定义的函数
- 现在已经成为主流大模型的标准功能
方案2:MCP(Model Context Protocol)
- 由Anthropic提出的标准化协议
- 提供统一的工具调用接口规范
- 目标是建立一个开放的工具生态
两种方案都是为了让AI"能做事",但实现方式和设计理念有所不同。接下来我们详细了解它们。
二、Function Calling是什么
2.1 基本概念
Function Calling(函数调用)是大语言模型的一项功能,允许模型在对话过程中主动调用开发者预先定义的函数。
简单来说:
- 你告诉AI:"我有这些工具可以用"(函数定义)
- 用户提出需求
- AI判断需要用哪个工具,并提取参数
- 系统执行函数调用
- AI根据结果生成回答
2.2 工作流程
第1步:开发者定义函数
--------------------------------
def get_weather(city: str, date: str) -> dict:
"""获取天气信息"""
# 调用天气API
return {"temperature": "25°C", "weather": "晴"}
第2步:告诉AI可用的函数
--------------------------------
functions = [{
"name": "get_weather",
"description": "获取指定城市的天气信息",
"parameters": {
"city": {"type": "string", "description": "城市名称"},
"date": {"type": "string", "description": "日期"}
}
}]
第3步:用户提问
--------------------------------
用户:"北京明天天气怎么样?"
第4步:AI决定调用函数
--------------------------------
AI分析:需要查询天气 → 选择get_weather函数
AI提取参数:city="北京", date="明天"
AI返回:{
"function": "get_weather",
"arguments": {"city": "北京", "date": "明天"}
}
第5步:系统执行函数
--------------------------------
result = get_weather(city="北京", date="明天")
# 返回:{"temperature": "20-28°C", "weather": "晴"}
第6步:AI生成最终回答
--------------------------------
AI基于结果回答:"明天北京天气晴朗,气温20-28°C。"
2.3 Function Calling的特点
优点:
- 简单直接:只需要定义函数和参数,模型自动理解何时调用
- 原生支持:主流大模型(OpenAI、Claude、通义千问等)都内置支持
- 灵活性高:可以定义任意函数,满足各种业务需求
- 参数提取准确:大模型能准确从用户输入中提取函数参数
局限性:
- 缺乏标准化:不同厂商的实现方式略有差异
- 工具定义冗长:每个工具都需要详细的JSON schema定义
- 上下文占用:函数定义会占用模型的上下文窗口
- 无状态管理:需要开发者自己管理工具的状态和生命周期
三、MCP是什么
3.1 基本概念
MCP(Model Context Protocol)是Anthropic提出的一个开放协议,旨在标准化大模型与外部工具之间的交互方式。
可以把MCP理解为:
- USB接口的AI版本:就像USB统一了电脑外设的接口,MCP统一了AI工具的接口
- 工具的"应用商店" :有了标准协议,可以构建一个工具生态系统
- 即插即用:遵循MCP协议的工具可以直接被任何支持MCP的AI系统使用
3.2 MCP的设计理念
传统方式的问题:
每个AI系统都自己定义工具接口
→ 工具开发者需要适配多个平台
→ 重复劳动,生态碎片化
MCP的解决方案:
统一的协议标准
→ 工具开发一次,处处可用
→ AI系统接入MCP,自动支持所有MCP工具
→ 生态繁荣,互利共赢
3.3 MCP的架构
MCP采用客户端-服务器架构:
┌─────────────┐ MCP协议 ┌─────────────┐
│ │ ←──────────────────→ │ │
│ AI应用 │ │ MCP服务器 │
│ (MCP客户端) │ 1. 发现可用工具 │ (工具提供者)│
│ │ 2. 调用工具 │ │
│ │ 3. 获取结果 │ │
└─────────────┘ └─────────────┘
MCP服务器可以提供:
- Resources(资源):文件、数据、配置等
- Tools(工具):可执行的功能
- Prompts(提示词模板):预定义的提示词
3.4 MCP的核心组件
1. Resources(资源)
提供静态或动态的数据资源
例如:文件内容、数据库记录、API响应等
2. Tools(工具)
可被AI调用的功能
例如:发送邮件、执行搜索、数据分析等
3. Prompts(提示词)
预定义的提示词模板
帮助用户快速开始对话
4. Sampling(采样)
允许MCP服务器请求AI进行推理
实现更复杂的交互模式
3.5 MCP的三种传输方式
MCP支持三种不同的传输协议,每种都有自己的特点和适用场景:
3.5.1 STDIO(标准输入输出)
工作原理:
AI应用启动MCP服务器作为子进程
通过stdin/stdout进行通信
特点:
- 最简单的方式,适合本地开发
- 进程间通信,不需要网络
- 服务器随客户端启动和关闭
适用场景:
- 本地工具集成
- 桌面应用(如Claude Desktop)
- 开发和测试环境
配置示例:
{
"mcpServers": {
"my-server": {
"type": "stdio",
"command": "python",
"args": ["mcp_server.py"]
}
}
}
3.5.2 SSE(Server-Sent Events)
工作原理:
客户端通过HTTP连接到MCP服务器
服务器可以主动推送消息给客户端
基于HTTP长连接
特点:
- 单向通信(服务器 → 客户端)
- 适合需要实时推送的场景
- 客户端请求通过HTTP POST发送
适用场景:
- 远程MCP服务
- 需要服务器主动推送消息的场景
- Web应用集成
配置示例:
{
"mcpServers": {
"my-server": {
"type": "sse",
"url": "http://localhost:3000/sse"
}
}
}
3.5.3 Streamable HTTP
工作原理:
完全基于HTTP的双向通信
每次请求-响应都是独立的
支持流式响应
特点:
- 标准的HTTP协议
- 支持负载均衡和CDN
- 易于部署和扩展
- 无需维护长连接
适用场景:
- 云端MCP服务
- 需要横向扩展的场景
- 通过API网关访问
- 生产环境部署
配置示例:
{
"mcpServers": {
"my-server": {
"type": "streamable-http",
"url": "http://localhost:3000/mcp"
}
}
}
3.6 三种传输方式对比
| 维度 | STDIO | SSE | Streamable HTTP |
|---|---|---|---|
| 复杂度 | 最简单 | 中等 | 中等 |
| 网络要求 | 不需要 | HTTP长连接 | HTTP短连接 |
| 部署方式 | 本地进程 | 远程服务 | 远程服务 |
| 扩展性 | 差 | 中等 | 好 |
| 实时性 | 高 | 高 | 中等 |
| 推荐场景 | 本地开发 | 需要推送的远程服务 | 云端生产环境 |
3.7 SSE与Streamable HTTP详细对比
MCP ClientMCP ServerSSE (Server-Sent Events)HTTP GET /sse (建立长连接)连接保持打开状态event: messageevent: notificationevent: updateHTTP POST /request (独立请求)Response连接始终保持Streamable HTTPHTTP POST /mcp (请求1)Response (可能是流式)连接关闭HTTP POST /mcp (请求2)Response (可能是流式)每次请求都是新连接MCP ClientMCP Server
关键区别:
-
连接模式
- SSE: 保持一个长连接用于服务器推送
- HTTP: 每次请求都是独立的连接
-
消息流向
- SSE: 服务器可以主动推送(单向流)
- HTTP: 严格的请求-响应模式(双向但分离)
-
适用场景
- SSE: 需要实时通知、进度更新的场景
- HTTP: 标准的API调用、云服务部署
3.8 如何选择传输方式?
决策树:
开始
↓
本地开发/测试?
├─ 是 → 选择 STDIO
└─ 否 ↓
需要服务器主动推送消息?
├─ 是 → 选择 SSE
└─ 否 ↓
需要云端部署/负载均衡?
├─ 是 → 选择 Streamable HTTP
└─ 否 → 选择 SSE
推荐:
- 开发阶段: STDIO (最简单)
- 有推送需求: SSE (实时性好)
- 生产环境: Streamable HTTP (扩展性好)
四、Function Calling vs MCP - 小白必看的应用场景区别
很多初学者会混淆这两种技术,认为它们是"二选一"的关系。实际上,它们解决的问题层次不同。
4.1 核心差异(小白版)
| 对比维度 | Function Calling | MCP |
|---|---|---|
| 本质 | 一个功能特性 | 一个通信协议 |
| 类比 | 你直接喊你的助手做事 | 你的助手通过标准流程调用其他部门 |
| 工具定义在哪 | 在你的代码里 | 在独立的MCP服务器里 |
| 可复用性 | 只能在你的应用用 | 所有支持MCP的应用都能用 |
| 学习成本 | 低(几行代码就能用) | 中(需要理解协议和服务器) |
4.2 什么时候用Function Calling?
场景1: 快速给AI增加几个简单功能
# 你只想让AI能查天气和发邮件
# 10分钟就能搞定
@tool
def get_weather(city: str):
return "晴天"
@tool
def send_email(to: str, content: str):
return "已发送"
# 绑定到AI,完成!
这种情况选Function Calling:
- 功能很简单(1-5个工具)
- 只在当前应用使用
- 不需要其他人复用
- 快速开发原型
场景2: 调用公司内部API
# 公司有个用户信息API,你想让AI能查询
@tool
def get_user_info(user_id: str):
# 直接调用内部API
return requests.get(f"http://internal-api/users/{user_id}")
# 简单直接,Function Calling就够了
4.3 什么时候用MCP?
场景1: 构建可复用的工具平台
你开发了一个"数据分析工具包"
包含10个数据处理工具
希望:
- Claude Desktop能用
- 你自己的AI应用能用
- 同事的项目也能用
- 将来出现新的AI应用也能用
→ 这时候应该用MCP
写一个MCP服务器,所有人都能接入
场景2: 集成第三方MCP服务
你发现有人已经做了一个"天气查询MCP服务"
你想在自己的应用里用
如果是Function Calling:
→ 你需要自己写天气查询代码
如果是MCP:
→ 直接在配置文件添加这个MCP服务器地址
→ 立即就能用,不需要写代码
场景3: 企业级应用,需要统一管理工具
场景:
- 公司有50个不同的AI应用
- 每个都需要查询数据库、发送邮件等通用能力
- 需要统一的权限控制和审计
如果每个应用都用Function Calling:
- 50个应用都要写一遍数据库查询代码
- 权限控制要写50遍
- 代码改动要改50个地方
如果用MCP:
- 写1个MCP服务器,提供统一的数据库查询工具
- 所有应用都连接这个MCP服务器
- 权限和审计都在MCP服务器统一处理
- 改代码只需要改1个地方
4.4 实际应用举例
个人项目: 用Function Calling
# 场景:你在做个人的AI助手,需要3个功能
# 功能1:查天气
@tool
def weather(city: str):
return f"{city}天气晴朗"
# 功能2:设置提醒
@tool
def reminder(time: str, msg: str):
return f"已设置{time}的提醒"
# 功能3:记笔记
@tool
def note(content: str):
return "已保存笔记"
# 总共20行代码,完成!
企业项目: 用MCP
场景:公司的AI客服系统,需要:
- 查询订单(连接订单系统)
- 查询物流(连接物流系统)
- 查询用户信息(连接CRM系统)
- 创建工单(连接工单系统)
架构设计:
┌─────────┐ ┌────────────────┐
│ AI客服 │ │ 订单MCP服务 │
│ 应用 │─────▶│ (订单团队维护)│
│ │ └────────────────┘
│ │ ┌────────────────┐
│ │─────▶│ 物流MCP服务 │
│ │ │ (物流团队维护)│
│ │ └────────────────┘
│ │ ┌────────────────┐
│ │─────▶│ CRM MCP服务 │
│ │ │ (CRM团队维护) │
└─────────┘ └────────────────┘
好处:
- 每个团队维护自己的MCP服务
- AI应用不需要了解底层实现
- 新增AI应用时,直接复用现有MCP服务
4.5 能不能一起用?
可以!而且很常见:
# 简单的本地功能 → Function Calling
@tool
def calculate(expression: str):
"""简单计算器"""
return eval(expression)
# 复杂的企业服务 → MCP
mcp_client.connect("order-system-mcp") # 连接订单系统MCP服务
mcp_client.connect("crm-mcp") # 连接CRM MCP服务
# AI可以同时使用两种工具:
# - 用Function Calling做简单计算
# - 用MCP查询订单和客户信息
4.6 决策指南
如果你是新手,这样选:
-
先学Function Calling
- 更简单,10分钟就能上手
- 满足80%的个人项目需求
-
这些情况考虑MCP:
- 你的工具想给别人复用
- 你想用别人写好的MCP服务
- 公司要求统一的工具管理
- 需要跨多个AI应用使用
-
实际项目中:
- 从Function Calling开始
- 发现需要复用时,再把工具改成MCP
- 大多数情况下,两种技术会混合使用
五、Function Calling详解与实战
5.1 工作原理深入解析
Function Calling的完整流程可以分为三个阶段:
阶段1:函数注册
开发者定义函数 → 描述函数功能 → 定义参数Schema → 注册到系统
阶段2:意图识别
用户输入 → LLM分析意图 → 判断是否需要调用函数 → 选择合适的函数 → 提取参数
阶段3:执行和回复
系统执行函数 → 获取结果 → LLM生成自然语言回复 → 返回用户
5.2 函数定义最佳实践
一个好的函数定义应该包含:
{
"name": "search_products", # 函数名:清晰、动词开头
"description": "在产品数据库中搜索商品。支持按名称、类别、价格范围搜索。", # 描述:详细、包含使用场景
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,例如'iPhone'、'笔记本电脑'"
},
"category": {
"type": "string",
"enum": ["electronics", "clothing", "books", "food"],
"description": "产品类别,可选值:electronics(电子产品)、clothing(服装)、books(图书)、food(食品)"
},
"min_price": {
"type": "number",
"description": "最低价格(元),例如100"
},
"max_price": {
"type": "number",
"description": "最高价格(元),例如5000"
},
"limit": {
"type": "integer",
"description": "返回结果数量限制,默认10,最大100",
"default": 10
}
},
"required": ["query"] # 必填参数
}
}
关键点:
- description要详细:LLM主要根据description判断何时调用
- 参数要有示例:帮助LLM理解参数格式
- 枚举值要说明:如果是enum,给出每个值的含义
- 设置默认值:减少必填参数,提高调用成功率
5.3 Python实战(LangChain 0.3.79)
5.3.1 环境准备
# 安装依赖
pip install langchain==0.3.79
pip install langchain-openai
pip install langchain-core
pip install openai
# 设置API Key
export OPENAI_API_KEY="你的API密钥"
5.3.2 基础示例:天气查询
"""
Function Calling基础示例 - LangChain 0.3.79
功能:实现一个能查询天气的AI助手
"""
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from typing import Annotated
# 步骤1:定义工具函数
@tool
def get_weather(
city: Annotated[str, "城市名称,例如:北京、上海"],
date: Annotated[str, "日期,例如:今天、明天、2024-01-01"] = "今天"
) -> str:
"""
获取指定城市的天气信息
Args:
city: 城市名称
date: 查询日期
Returns:
天气信息的JSON字符串
"""
# 这里模拟调用天气API
# 实际应用中,这里应该调用真实的天气API
weather_data = {
"北京": {"今天": "晴,15-25°C", "明天": "多云,18-28°C"},
"上海": {"今天": "小雨,20-26°C", "明天": "阴,19-25°C"},
"深圳": {"今天": "晴,25-32°C", "明天": "晴,26-33°C"}
}
result = weather_data.get(city, {}).get(date, "暂无数据")
return f"{city}{date}的天气:{result}"
# 步骤2:初始化模型并绑定工具
llm = ChatOpenAI(
model="gpt-4o-mini",
temperature=0
)
# 绑定工具到模型
llm_with_tools = llm.bind_tools([get_weather])
# 步骤3:创建处理循环
def run_agent(user_input: str):
"""运行AI代理,处理用户输入"""
messages = [HumanMessage(content=user_input)]
print(f"用户: {user_input}\n")
# 调用模型
response = llm_with_tools.invoke(messages)
messages.append(response)
# 检查是否需要调用工具
if response.tool_calls:
print("AI决定调用工具:")
for tool_call in response.tool_calls:
print(f" 函数: {tool_call['name']}")
print(f" 参数: {tool_call['args']}")
# 执行工具调用
if tool_call['name'] == 'get_weather':
result = get_weather.invoke(tool_call['args'])
print(f" 结果: {result}\n")
# 将工具结果添加到消息历史
messages.append(ToolMessage(
content=result,
tool_call_id=tool_call['id']
))
# 基于工具结果生成最终回答
final_response = llm_with_tools.invoke(messages)
print(f"AI: {final_response.content}")
else:
print(f"AI: {response.content}")
# 使用示例
if __name__ == "__main__":
# 测试1:查询天气
run_agent("北京明天天气怎么样?")
print("\n" + "="*60 + "\n")
# 测试2:不需要调用工具的对话
run_agent("你好,你是谁?")
print("\n" + "="*60 + "\n")
# 测试3:查询今天的天气
run_agent("上海今天的天气如何?")
输出示例:
用户: 北京明天天气怎么样?
AI决定调用工具:
函数: get_weather
参数: {'city': '北京', 'date': '明天'}
结果: 北京明天的天气:多云,18-28°C
AI: 北京明天的天气是多云,温度在18-28°C之间。建议带件薄外套。
============================================================
用户: 你好,你是谁?
AI: 你好!我是一个AI助手,可以帮你查询天气信息和回答问题。有什么我可以帮你的吗?
5.3.3 进阶示例:多工具协同
"""
Function Calling进阶示例 - 多工具协同
功能:实现一个办公助手,支持查日历、发邮件、搜索文档
"""
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from datetime import datetime, timedelta
# 定义多个工具
@tool
def get_calendar_events(date: str) -> str:
"""
获取指定日期的日历事件
Args:
date: 日期,格式:YYYY-MM-DD 或 "今天"、"明天"
"""
# 模拟日历数据
events = {
"2024-01-15": [
{"time": "10:00", "title": "团队周会", "location": "会议室A"},
{"time": "14:00", "title": "项目评审", "location": "线上会议"}
],
"2024-01-16": [
{"time": "09:30", "title": "客户拜访", "location": "客户公司"}
]
}
# 处理"今天"、"明天"
if date == "今天":
date = datetime.now().strftime("%Y-%m-%d")
elif date == "明天":
date = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
events_list = events.get(date, [])
if not events_list:
return f"{date}没有安排日程"
result = f"{date}的日程安排:\n"
for event in events_list:
result += f"- {event['time']} {event['title']} ({event['location']})\n"
return result
@tool
def send_email(
to: str,
subject: str,
body: str
) -> str:
"""
发送邮件
Args:
to: 收件人邮箱或姓名
subject: 邮件主题
body: 邮件正文
"""
# 模拟发送邮件
print(f"\n发送邮件:")
print(f"收件人: {to}")
print(f"主题: {subject}")
print(f"内容: {body}\n")
return f"邮件已成功发送给{to}"
@tool
def search_documents(
query: str,
doc_type: str = "all"
) -> str:
"""
搜索文档
Args:
query: 搜索关键词
doc_type: 文档类型,可选值:all(全部)、report(报告)、contract(合同)、memo(备忘录)
"""
# 模拟文档搜索
documents = {
"销售": [
{"title": "2023Q4销售报告", "type": "report", "date": "2024-01-05"},
{"title": "华东区销售合同模板", "type": "contract", "date": "2023-12-20"}
],
"技术": [
{"title": "系统架构设计文档", "type": "memo", "date": "2024-01-10"},
{"title": "API接口文档", "type": "report", "date": "2023-12-15"}
]
}
# 简单的搜索逻辑
results = []
for key, docs in documents.items():
if query in key or any(query in doc['title'] for doc in docs):
for doc in docs:
if doc_type == "all" or doc['type'] == doc_type:
results.append(doc)
if not results:
return f"没有找到与'{query}'相关的文档"
result = f"找到{len(results)}个相关文档:\n"
for doc in results:
result += f"- {doc['title']} ({doc['type']}, {doc['date']})\n"
return result
# 创建Agent
# 1. 定义工具列表
tools = [get_calendar_events, send_email, search_documents]
# 2. 创建Prompt模板
prompt = ChatPromptTemplate.from_messages([
("system", """你是一个智能办公助手,可以帮助用户:
1. 查询日历事件
2. 发送邮件
3. 搜索文档
请根据用户需求,选择合适的工具来完成任务。
如果需要多个步骤,请逐步执行。
"""),
("human", "{input}"),
("placeholder", "{agent_scratchpad}")
])
# 3. 初始化LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 4. 创建Agent
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 显示中间步骤
max_iterations=5
)
# 使用示例
if __name__ == "__main__":
# 场景1:查询日历
print("="*60)
print("场景1:查询日历")
print("="*60)
result = agent_executor.invoke({
"input": "明天我有什么安排?"
})
print(f"\n最终回答: {result['output']}\n\n")
# 场景2:组合操作 - 先查日历,再发邮件
print("="*60)
print("场景2:查日历并发邮件")
print("="*60)
result = agent_executor.invoke({
"input": "查一下明天的日程,然后给张三发邮件提醒他明天的客户拜访"
})
print(f"\n最终回答: {result['output']}\n\n")
# 场景3:搜索文档
print("="*60)
print("场景3:搜索文档")
print("="*60)
result = agent_executor.invoke({
"input": "帮我找一下销售相关的报告文档"
})
print(f"\n最终回答: {result['output']}\n\n")
5.4 Function Calling最佳实践
1. 函数设计原则
- 单一职责:每个函数只做一件事
- 幂等性:多次调用相同参数应返回相同结果
- 错误处理:返回明确的错误信息
- 性能考虑:避免长时间运行的操作
2. 参数设计技巧
- 使用清晰的参数名
- 提供详细的描述和示例
- 合理设置默认值
- 使用枚举类型限制输入
3. 调试技巧
import logging
# 添加日志记录
logger = logging.getLogger(__name__)
@tool
def my_function(param: str) -> str:
"""函数描述"""
logger.info(f"函数被调用,参数:{param}")
try:
result = do_something(param)
logger.info(f"函数执行成功,结果:{result}")
return result
except Exception as e:
logger.error(f"函数执行失败:{str(e)}")
return f"错误:{str(e)}"
4. 安全考虑
- 验证输入参数
- 限制操作权限
- 记录审计日志
- 敏感操作需要确认
六、MCP详解与实战
6.1 MCP协议架构
MCP(Model Context Protocol)是一个客户端-服务器协议,定义了AI应用如何与工具提供者进行通信。
核心架构:
┌──────────────────┐
│ AI Application │
│ (MCP Client) │
└────────┬─────────┘
│
│ MCP Protocol
│ (JSON-RPC 2.0)
│
┌────────┴─────────┐
│ MCP Server │
│ (Tool Provider) │
└──────────────────┘
通信流程:
- 连接建立
Client: 发送 initialize 请求
Server: 返回服务器能力信息
Client: 发送 initialized 通知
- 工具发现
Client: 发送 tools/list 请求
Server: 返回可用工具列表及其Schema
- 工具调用
Client: 发送 tools/call 请求(工具名 + 参数)
Server: 执行工具
Server: 返回执行结果
- 资源访问
Client: 发送 resources/list 请求
Server: 返回可用资源列表
Client: 发送 resources/read 请求
Server: 返回资源内容
6.2 Python实现(LangChain 0.3.79集成MCP)
6.2.1 安装依赖
pip install mcp
pip install langchain==0.3.79
pip install langchain-openai
1. STDIO(本地 CLI 进程)
适用场景:客户端通过子进程方式调用工具,适合本地集成。
# server_stdio.py
from fastmcp import FastMCP
mcp = FastMCP("StdioDemo")
@mcp.tool()
def add(a: int, b: int) -> int:
return a + b
if __name__ == "__main__":
mcp.run(transport="stdio")
客户端示例:
# client_stdio.py
import asyncio
from fastmcp import Client
from fastmcp.client.transports import PythonStdioTransport
async def main():
transport = PythonStdioTransport("python", ["server_stdio.py"])
client = Client(transport)
async with client:
res = await client.call_tool("add", {"a": 2, "b": 3})
print("Result:", res.result) # → 5
asyncio.run(main())
2. SSE(HTTP with Server‑Sent Events,Legacy)
适用场景:远程 HTTP,服务器通过 SSE 向客户端推送事件。现已 Deprecated,仅用于兼容旧系统。
# server_sse.py
from fastmcp import FastMCP
mcp = FastMCP("SseDemo")
@mcp.tool()
def greet(name: str) -> str:
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="sse", host="127.0.0.1", port=8001)
客户端示例:
# client_sse.py
import asyncio
from fastmcp import Client
async def main():
client = Client("http://127.0.0.1:8001/sse")
async with client:
res = await client.call_tool("greet", {"name": "Bob"})
print(res.result) # → Hello, Bob!
asyncio.run(main())
3. Streamable HTTP(推荐,现代模式)
适用场景:单一 HTTP 端点,支持普通响应及 SSE 流,热更新、断点重连、会话 ID。
# server_http.py
from fastmcp import FastMCP
mcp = FastMCP("HttpDemo")
@mcp.tool()
async def count_up(to: int, ctx):
for i in range(1, to + 1):
await ctx.info(f"count:{i}")
return {"done": True}
if __name__ == "__main__":
mcp.run(transport="http", host="127.0.0.1", port=8002, path="/mcp")
客户端示例:
# client_http.py
import asyncio
from fastmcp import Client
from fastmcp.client.transports import StreamableHttpTransport
async def main():
transport = StreamableHttpTransport(url="http://127.0.0.1:8002/mcp")
client = Client(transport)
async with client:
res = await client.call_tool("count_up", {"to": 3})
print("Final result:", res.result)
asyncio.run(main())
七、MCP Inspector调试工具使用指南
MCP Inspector是MCP官方提供的可视化测试和调试工具,帮助开发者快速测试MCP服务器。
7.1 什么是MCP Inspector?
MCP Inspector是一个Web界面工具,提供:
- 可视化的MCP服务器连接
- 交互式工具测试
- 实时请求/响应查看
- 资源浏览
- Prompt测试
7.2 安装和启动
方法1:使用npx(推荐,无需安装)
# 直接运行,会自动下载并启动
npx @modelcontextprotocol/inspector
方法2:全局安装
# 安装
npm install -g @modelcontextprotocol/inspector
# 运行
mcp-inspector
启动后,浏览器会自动打开 http://localhost:6274
7.3 连接MCP服务器
7.3.1 连接STDIO服务器
# 启动Inspector并连接你的MCP服务器
npx @modelcontextprotocol/inspector python mcp_server.py
或者在UI界面中:
- 选择Transport: STDIO
- Command:
python - Arguments:
mcp_server.py - 点击"Connect"
7.3.2 连接SSE服务器
# 命令行方式
npx @modelcontextprotocol/inspector http://localhost:3000/sse
或者在UI界面中:
- 选择Transport: SSE
- Server URL:
http://localhost:3000/sse - 点击"Connect"
7.3.3 连接Streamable HTTP服务器
# 命令行方式
npx @modelcontextprotocol/inspector http://localhost:3000/mcp --transport http
或者在UI界面中:
- 选择Transport: Streamable HTTP
- Server URL:
http://localhost:3000/mcp - 点击"Connect"
7.4 使用配置文件
创建配置文件 mcp-config.json:
{
"mcpServers": {
"my-weather-server": {
"type": "stdio",
"command": "python",
"args": ["mcp_server.py"],
"env": {
"API_KEY": "your-api-key"
}
},
"remote-server": {
"type": "sse",
"url": "http://localhost:3000/sse"
},
"http-server": {
"type": "streamable-http",
"url": "http://api.example.com/mcp"
}
}
}
使用配置文件启动:
# 连接配置文件中的指定服务器
npx @modelcontextprotocol/inspector --config mcp-config.json --server my-weather-server
# 如果配置文件中只有一个服务器,可以省略--server参数
npx @modelcontextprotocol/inspector --config mcp-config.json
7.5 Inspector界面功能
1. Tools标签页
- 查看所有可用工具
- 测试工具调用
- 查看工具Schema
- 查看调用历史
2. Resources标签页
- 浏览可用资源
- 读取资源内容
- 查看资源元数据
3. Prompts标签页
- 测试预定义Prompt
- 查看Prompt模板
- 测试不同参数组合
4. 配置标签页
- 调整连接参数
- 查看服务器信息
- 导出配置
7.6 CLI模式(命令行模式)
Inspector也支持纯命令行模式,适合脚本和自动化:
# 列出所有工具
npx @modelcontextprotocol/inspector --cli python mcp_server.py --method tools/list
# 调用特定工具
npx @modelcontextprotocol/inspector --cli python mcp_server.py \
--method tools/call \
--tool-name get_weather \
--tool-arg city=北京 \
--tool-arg date=今天
# 使用JSON格式的参数
npx @modelcontextprotocol/inspector --cli python mcp_server.py \
--method tools/call \
--tool-name get_weather \
--tool-arg 'city=北京'
# 连接远程服务器并调用工具
npx @modelcontextprotocol/inspector --cli http://localhost:3000/mcp \
--transport http \
--method tools/call \
--tool-name get_weather \
--tool-arg city=上海
7.7 调试技巧
1. 查看详细日志
# 启动时开启详细日志
DEBUG=mcp:* npx @modelcontextprotocol/inspector python mcp_server.py
2. 使用请求历史
Inspector会记录所有请求和响应,可以:
- 重放之前的请求
- 导出为curl命令
- 复制为代码
3. 测试错误处理
在Inspector中故意传入错误参数,测试:
- 参数验证是否正确
- 错误消息是否清晰
- 是否正确返回错误状态
八、MCP Docker Toolkit使用
对于生产环境或复杂的部署场景,可以使用Docker来管理MCP服务器。
8.1 创建MCP Server Docker镜像
Dockerfile:
# Dockerfile for MCP Server
FROM python:3.11-slim
WORKDIR /app
# 安装依赖
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 复制MCP服务器代码
COPY mcp_server.py .
# 暴露端口(如果使用SSE或HTTP传输)
EXPOSE 3000
# 启动命令
CMD ["python", "mcp_server.py"]
requirements.txt:
mcp>=1.0.0
pydantic
uvicorn # 如果使用SSE或HTTP
starlette # 如果使用SSE或HTTP
构建镜像:
docker build -t my-mcp-server:latest .
8.2 运行MCP Server容器
方式1:STDIO(不推荐用Docker)
STDIO传输方式不适合Docker,因为Docker容器的stdin/stdout难以直接交互。
方式2:SSE传输
# 运行MCP服务器
docker run -d \
--name mcp-server \
-p 3000:3000 \
my-mcp-server:latest
# 测试连接
npx @modelcontextprotocol/inspector http://localhost:3000/sse
方式3:Streamable HTTP传输
# 运行MCP服务器
docker run -d \
--name mcp-server \
-p 3000:3000 \
-e API_KEY=your-secret-key \
my-mcp-server:latest
# 测试连接
npx @modelcontextprotocol/inspector \
http://localhost:3000/mcp \
--transport http \
--header "Authorization: Bearer your-secret-key"
8.3 使用Docker Compose管理多个MCP服务
docker-compose.yml:
version: '3.8'
services:
# 天气MCP服务
weather-mcp:
build:
context: ./weather-mcp
dockerfile: Dockerfile
ports:
- "3001:3000"
environment:
- WEATHER_API_KEY=${WEATHER_API_KEY}
restart: unless-stopped
# 数据库MCP服务
database-mcp:
build:
context: ./database-mcp
dockerfile: Dockerfile
ports:
- "3002:3000"
environment:
- DB_HOST=postgres
- DB_PORT=5432
- DB_NAME=myapp
depends_on:
- postgres
restart: unless-stopped
# CRM MCP服务
crm-mcp:
build:
context: ./crm-mcp
dockerfile: Dockerfile
ports:
- "3003:3000"
environment:
- CRM_API_URL=${CRM_API_URL}
restart: unless-stopped
# PostgreSQL数据库(如果database-mcp需要)
postgres:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
启动所有服务:
# 启动
docker-compose up -d
# 查看状态
docker-compose ps
# 查看日志
docker-compose logs -f weather-mcp
# 停止
docker-compose down
8.4 MCP服务的健康检查
在Dockerfile中添加健康检查:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY mcp_server.py .
COPY healthcheck.py .
EXPOSE 3000
# 添加健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD python healthcheck.py || exit 1
CMD ["python", "mcp_server.py"]
healthcheck.py:
import sys
import httpx
try:
response = httpx.get("http://localhost:3000/health", timeout=2.0)
if response.status_code == 200:
sys.exit(0)
else:
sys.exit(1)
except Exception:
sys.exit(1)
九、MCP Server资源推荐
9.1 官方资源
官方MCP Servers库
- GitHub: github.com/modelcontex…
- 包含官方维护的高质量MCP服务器
- 涵盖常见功能:文件系统、数据库、Git等
推荐服务器:
@modelcontextprotocol/server-filesystem: 文件系统操作@modelcontextprotocol/server-postgres: PostgreSQL数据库@modelcontextprotocol/server-github: GitHub集成@modelcontextprotocol/server-brave-search: Brave搜索@modelcontextprotocol/server-google-maps: Google Maps集成
9.2 社区资源平台
1. MCP.so
- 网站: mcp.so/
- 社区驱动的MCP服务器目录
- 按类别浏览和搜索MCP服务器
- 包含使用说明和示例
2. Smithery.ai
- 网站: smithery.ai/
- MCP服务器市场和管理平台
- 一键安装和配置MCP服务器
- 提供服务器评分和评论
3. MCP Higress
- 网站: mcp.higress.ai/
- 阿里云Higress团队维护的MCP资源
- 专注于云原生和微服务场景
- 提供中文文档和支持
4. Awesome MCP Servers
- GitHub: github.com/punkpeye/aw…
- 精选的MCP服务器列表
- 按功能分类整理
- 包含社区贡献的优质服务器
9.3 热门第三方MCP服务器
开发工具类:
mcp-server-docker: Docker容器管理mcp-server-kubernetes: K8s集群操作mcp-server-aws: AWS云服务集成mcp-server-vercel: Vercel部署管理
数据库类:
mcp-server-mysql: MySQL数据库mcp-server-mongodb: MongoDB集成mcp-server-redis: Redis缓存操作mcp-server-elasticsearch: ES搜索集成
通信协作类:
mcp-server-slack: Slack消息发送mcp-server-email: 邮件发送mcp-server-calendar: 日历管理mcp-server-notion: Notion集成
数据处理类:
mcp-server-pandas: 数据分析mcp-server-scraper: 网页爬取mcp-server-pdf: PDF处理mcp-server-image: 图像处理
十、常见问题
Q1: Function Calling会增加API调用成本吗?
回答:会有一定增加,因为:
- 函数定义会占用输入tokens
- 可能需要多轮调用(识别意图 → 执行函数 → 生成回答)
优化建议:
- 精简函数描述
- 使用更便宜的模型(如gpt-4o-mini)
- 缓存常见查询结果
Q2: Function Calling的准确率如何?
回答:主流大模型(GPT-4、Claude)的Function Calling准确率很高(>95%),但需要注意:
- 函数描述要清晰
- 参数类型要明确
- 避免歧义
Q3: MCP现在成熟吗?
回答:MCP是比较新的标准(2024年推出),目前:
- 协议规范已经稳定
- 工具生态正在快速增长
- 主要由Anthropic和社区推动
建议:
- 学习阶段可以了解MCP
- 小项目优先使用Function Calling
- 企业项目考虑MCP的长期价值
Q4: 三种MCP传输方式该如何选择?
回答:
- 开发测试: STDIO(最简单)
- 需要推送: SSE(实时性好)
- 生产环境: Streamable HTTP(扩展性好)
Q5: Function Calling和Agent有什么关系?
回答:
- Function Calling是能力
- Agent是应用模式
Agent通常使用Function Calling来:
- 调用工具
- 获取信息
- 执行操作
可以说:Function Calling是构建Agent的核心技术之一。
Q6: 如何确保MCP服务器的安全性?
最佳实践:
- 认证授权: 使用API Key或OAuth
- 参数验证: 严格验证输入参数
- 审计日志: 记录所有工具调用
- 速率限制: 防止滥用
- 网络隔离: 使用VPC或防火墙
十一、总结
核心要点
- Function Calling让AI能够调用函数,执行实际操作
- MCP提供了标准化的工具调用协议,促进生态发展
- MCP支持三种传输方式:STDIO(本地)、SSE(推送)、HTTP(云端)
- Function Calling适合快速开发和简单场景
- MCP适合工具复用和企业级应用
- 两者可以结合使用,发挥各自优势