MCP協議實戰:用Python給AI Agent裝上萬能工具調用接口

給你的Agent裝個“外掛”?別再被私有協議坑了
最近在折騰AI Agent的工具調用,被各種私有協議搞得頭大。OpenAI的Function Calling一套規則,Claude又是另一套,等你對接完自家內部的API,發現格式又不一樣了。每次換模型或者加新工具,都得重寫一遍適配層,這誰頂得住?
直到我發現了MCP(Model Context Protocol)協議——這玩意兒簡直就是Agent工具調用的“萬能插座”。今天我就用踩坑記錄的方式,帶你用Python給Agent集成MCP協議,徹底告別重復造輪子。
MCP協議:Agent工具的“USB標準”
先說清楚MCP是什么。簡單理解,它是一套讓AI模型和外部工具對話的標準化協議。就像USB接口一樣,不管你是插U盤、鍵盤還是手機,接口都一樣。MCP讓不同模型(Claude、GPT、龍蝦模型)都能用同一套方式調用工具。
核心三件套:
- MCP Server:工具提供方,比如你的天氣查詢API、數據庫接口
- MCP Client:Agent側,負責和模型溝通,轉發工具調用請求
- 協議格式:JSON-RPC 2.0,定義了請求/響應的標準結構
對比Function Calling,MCP最大優勢是解耦。Function Calling是模型廠商定義的,模型換了你就得改。MCP是中間層,模型和工具都通過這個標準接口對話,換模型不用動工具代碼。
踩坑實錄:從環境配置到協議調試
第一步:環境配置的坑
# 我以為直接pip install mcp就行,結果...
pip install mcp
# 報錯:缺少依賴
# 實際需要:
pip install "mcp[cli]" httpx sse-starlette坑點1:官方文檔沒明確說需要sse-starlette,但不用的話Server啟動會報錯。這是MCP支持流式傳輸的依賴。
第二步:工具函數封裝
假設我們要封裝一個天氣查詢工具:
# weather_tool.py
from mcp.server import Server
from mcp.types import Tool, TextContent
import httpx
app = Server("weather-server")
@app.list_tools()
async def list_tools():
return [
Tool(
name="get_weather",
description="獲取指定城市的天氣",
inputSchema={
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名稱"}
},
"required": ["city"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "get_weather":
city = arguments["city"]
# 調用實際的天氣API
async with httpx.AsyncClient() as client:
resp = await client.get(
f"https://api.weather.com/v1/{city}",
headers={"Authorization": "Bearer your_key"}
)
data = resp.json()
return [TextContent(type="text", text=f"{city}今天{data['temp']}℃,{data['condition']}")]坑點2:inputSchema必須嚴格符合JSON Schema規范。我第一次少寫了"type": "object",Client死活調不通,報錯信息還不明確。
第三步:協議調試技巧
調試MCP最實用的幾招:

# 調試腳本:直接測試Server
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def test_server():
server_params = StdioServerParameters(
command="python",
args=["weather_tool.py"]
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# 1. 先列工具,看Schema對不對
tools = await session.list_tools()
print("可用工具:", [t.name for t in tools.tools])
# 2. 測試調用
result = await session.call_tool("get_weather", {"city": "北京"})
print("調用結果:", result.content[0].text)
asyncio.run(test_server())坑點3:MCP默認用stdio通信,但生產環境需要用SSE(Server-Sent Events)。切換時要注意:
# 生產環境用SSE
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
sse = SseServerTransport("/messages")
async def handle_sse(request):
async with sse.connect_sse(request.scope, request.receive, request._send) as streams:
await app.run(streams[0], streams[1], app.create_initialization_options())
app = Starlette(
routes=[
Route("/sse", endpoint=handle_sse, methods=["GET"]),
Route("/messages/", endpoint=sse.handle_post_message, methods=["POST"]),
]
)實戰對比:MCP vs Function Calling
我做了個簡單測試,用同一個天氣工具:
| 維度 | Function Calling | MCP |
|---|---|---|
| 代碼量 | 每個模型寫一套適配 | 一套工具,多模型通用 |
| 調試難度 | 得看各廠商文檔 | 統一協議,調試工具一致 |
| 流式支持 | 部分模型支持 | 原生支持SSE |
| 工具發現 | 靜態定義 | 動態發現,可運行時增刪 |
實際場景:我們團隊有個內部知識庫查詢工具,之前用Function Calling給GPT用,后來想接Claude,重寫了兩天。換成MCP后,工具代碼一行沒改,只在Agent側換了Client實現,半小時搞定。
商業價值:這不只是技術優化
- 降低多模型切換成本:今天用GPT,明天換龍蝦模型,工具層完全不用動
- 工具市場標準化:可以像npm/pip一樣發布MCP工具包,別人直接用
- 私有協議兼容:把內部API包裝成MCP Server,對外就是標準接口
我們有個客戶做電商客服Agent,需要對接十幾個內部系統(訂單、庫存、CRM)。之前每個系統一套接口,Agent代碼臃腫不堪。用MCP重構后,每個系統變成一個MCP Server,Agent只管調用,維護成本降了70%。
下一步行動
- 立即試水:把上面的天氣工具代碼跑通,10分鐘就能看到效果
- 改造現有工具:挑一個你正在用的API,包裝成MCP Server
- 加入生態:去www.nhjb.com.cn(www.nhjb.com.cn)的MCP工具市場看看,已經有現成的數據庫、搜索引擎工具包
關鍵提醒:MCP還在快速迭代,協議版本要鎖定。我用的是2024-11-05版本,生產環境建議固定版本號,別用latest。
想深入交流MCP踩坑經驗,或者需要具體場景的代碼示例,直接去www.nhjb.com.cn的開發者社區發帖,我都在。