:從 0 到 1 構建你的第一個 MCP Server,附完整代碼與 TaoToken 接入)
1. 為什么我要自己寫一個 MCP ServerMCPModel Context Protocol模型上下文協議是 Anthropic 開源的一套標準用來讓大模型以統(tǒng)一方式調用外部工具和數據源。它能做什么簡單說你寫一次 ServerClaude Desktop、Cursor、Continue 這些支持 MCP 的客戶端都能直接調用不用為每家模型單獨適配 Function Calling 格式。適合誰適合手里有內部 API、數據庫、腳本想讓 AI 直接調用的 Python 開發(fā)者以及想把 Claude Desktop 變成自己工具臺的用戶。我第一次動手寫 MCP Server 時踩了不少坑官方文檔偏協議規(guī)范缺少階梯式教程JSON-RPC 和 stdio 通信對沒接觸過的人一頭霧水市面上的現成 Server 又滿足不了業(yè)務定制。折騰了兩個下午才跑通第一個 Hello World。這篇文章就把這條路重新鋪一遍從項目結構、依賴清單、啟動命令到把 Claude Desktop 的 MCP 配置改到 TaoToken 統(tǒng)一 Key/API 通道最后用一次工具調用驗證連通性。MCP 的核心價值在于解耦。以前讓模型調工具要么用 OpenAI 的 Function Calling、Anthropic 的 Tool Use各寫一套要么被 LangChain 這類框架綁死。MCP 把「工具怎么被發(fā)現、怎么被描述、怎么被調用」抽成協議層Server 只寫一次任何支持 MCP 的 Client 都能用。它基于 JSON-RPC 2.0傳輸層支持 stdio本地子進程和 HTTPSSE遠程服務三大原語是 Tool、Resource、Prompt。本文聚焦最常用的 Tool帶你從零構建一個能查天氣、能做四則運算的 Server。2. TaoToken 前置準備統(tǒng)一 Key 與 API 通道在寫代碼之前先把模型調用通道理順。Claude Desktop 默認走 Anthropic 官方接口但如果你同時用多個模型、多個工具Key 管理會很亂。TaoToken 提供統(tǒng)一的 API 通道一個 Key 就能覆蓋多種模型調用MCP Server 里如果需要調用模型能力比如做二次推理、生成摘要也可以走這個通道。你需要先拿到兩樣東西Base URL 和 API Key。Base URL 是https://taotoken.net/apiAPI Key 在控制臺的 API Keys 頁面創(chuàng)建。創(chuàng)建時建議按用途命名比如mcp-demo方便后續(xù)排查。拿到 Key 后不要硬編碼進代碼用環(huán)境變量管理。# Linux / macOS export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api # Windows PowerShell $env:TAOTOKEN_API_KEYsk-你的key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用的是 Claude Code 或 Codex 這類編碼工具配置方式略有不同。Claude Code 的 settings 文件里需要寫全三件套Base URL、Key、Model ID。Codex 的auth.json也是類似結構。下面是一個 Claude Code 的 settings 片段示例路徑按你的實際安裝位置調整{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }注意 Model ID 要和你實際使用的模型對應不同客戶端對模型名的寫法可能不同以控制臺文檔為準。配置完成后可以用一個最簡單的 curl 驗證通道是否通curl -X POST https://taotoken.net/api/v1/messages \ -H Content-Type: application/json \ -H x-api-key: $TAOTOKEN_API_KEY \ -H anthropic-version: 2023-06-01 \ -d { model: claude-sonnet-4-20250514, max_tokens: 64, messages: [{role: user, content: ping}] }如果返回里有正常的content字段說明通道沒問題。這一步很關鍵因為后面 MCP Server 如果涉及模型調用走的就是這個通道。如果這里就報 401先檢查 Key 是否復制完整、有沒有多余空格。3. 可復制配置從零搭建 MCP Server 項目現在進入正題。我們構建一個包含兩個 Tool 的 Serverget_weather查天氣和calculator四則運算。項目結構如下mcp-demo-server/ ├── server.py # MCP Server 主程序 ├── tools/ │ ├── __init__.py │ ├── weather.py # 天氣查詢工具 │ └── calculator.py # 計算器工具 ├── requirements.txt └── claude_desktop_config.json # Claude Desktop 配置示例先寫依賴清單requirements.txtmcp1.0.0 httpx0.27.0安裝依賴pip install -r requirements.txt主程序server.py負責創(chuàng)建 Server 實例、注冊 Tool 列表、處理調用請求、啟動 stdio 傳輸 MCP Demo Server —— 天氣查詢 計算器 使用官方 MCP Python SDK 構建 import asyncio import json from mcp.server import Server, NotificationOptions from mcp.server.models import InitializationCapabilities from mcp.server.stdio import stdio_server from tools.weather import get_weather_data from tools.calculator import calculate # 1. 創(chuàng)建 MCP Server 實例 server Server(mcp-demo-server) # 2. 注冊 Tool 列表 server.list_tools() async def handle_list_tools() - list: 返回當前 Server 支持的所有 Tool 列表 return [ { name: get_weather, description: 獲取指定城市的當前天氣信息包括溫度、濕度、天氣狀況和風力, inputSchema: { type: object, properties: { city: { type: string, description: 城市名稱支持中文如北京或英文如Beijing } }, required: [city] } }, { name: calculator, description: 執(zhí)行基本的四則運算加減乘除支持整數和浮點數, inputSchema: { type: object, properties: { operation: { type: string, enum: [add, subtract, multiply, divide], description: 運算類型add-加法, subtract-減法, multiply-乘法, divide-除法 }, a: {type: number, description: 第一個操作數}, b: {type: number, description: 第二個操作數} }, required: [operation, a, b] } } ] # 3. 注冊 Tool 調用處理器 server.call_tool() async def handle_call_tool(name: str, arguments: dict) - list: 處理來自 Client 的 Tool 調用請求 if name get_weather: city arguments.get(city, 北京) weather_data await get_weather_data(city) return [{ type: text, text: json.dumps(weather_data, ensure_asciiFalse, indent2) }] elif name calculator: operation arguments[operation] a arguments[a] b arguments[b] result await calculate(operation, a, b) return [{ type: text, text: f計算結果{a} {operation} {result} }] else: raise ValueError(f未知的 Tool: {name}) # 4. 啟動 Server async def main(): 通過 stdio 傳輸啟動 MCP Server async with stdio_server() as (read_stream, write_stream): await server.run( read_stream, write_stream, InitializationCapabilities( sampling{}, experimental{}, ), notification_optionsNotificationOptions( tools_changedTrue ) ) if __name__ __main__: asyncio.run(main())天氣工具tools/weather.py調用免費天氣 API并做好異常降級 天氣查詢工具 —— 調用免費天氣 API 獲取實時天氣數據 import httpx WEATHER_API_URL https://wttr.in/{}?formatj1 async def get_weather_data(city: str Beijing) - dict: 獲取指定城市的天氣信息 try: async with httpx.AsyncClient(timeout10.0) as client: response await client.get(WEATHER_API_URL.format(city)) if response.status_code ! 200: return _get_mock_weather(city) data response.json() current data[current_condition][0] weather { city: city, temperature_c: int(current[temp_C]), humidity: int(current[humidity]), condition: current[lang_zh][0][value] if current.get(lang_zh) else current[weatherDesc][0][value], wind_speed_kmh: int(current[windspeedKmph]), feels_like_c: int(current[FeelsLikeC]), observation_time: current[observation_time] } return weather except Exception as e: return { **_get_mock_weather(city), note: f模擬數據API 請求失敗{str(e)} } def _get_mock_weather(city: str) - dict: 返回模擬天氣數據用于 API 不可用時的降級處理 return { city: city, temperature_c: 25, humidity: 60, condition: 晴, wind_speed_kmh: 15, feels_like_c: 26, observation_time: 12:00 PM }計算器工具tools/calculator.py 計算器工具 —— 提供安全的四則運算能力 async def calculate(operation: str, a: float, b: float) - float: 執(zhí)行基本四則運算 if operation not in (add, subtract, multiply, divide): raise ValueError(f不支持的運算類型{operation}) if operation add: return a b elif operation subtract: return a - b elif operation multiply: return a * b elif operation divide: if b 0: raise ValueError(除數不能為零請檢查第二個參數。) return a / btools/__init__.py留空即可。接下來配置 Claude Desktop。配置文件路徑Windows 在%APPDATA%\Claude\claude_desktop_config.jsonmacOS 在~/Library/Application Support/Claude/claude_desktop_config.json。內容如下{ mcpServers: { mcp-demo-server: { command: python, args: [C:\\path\\to\\mcp-demo-server\\server.py], description: 天氣查詢和計算器服務 } } }如果你希望 MCP Server 內部調用模型時走 TaoToken 通道可以在配置里加環(huán)境變量{ mcpServers: { mcp-demo-server: { command: python, args: [C:\\path\\to\\mcp-demo-server\\server.py], env: { TAOTOKEN_API_KEY: sk-你的key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }保存后完全退出 Claude Desktop 再重啟輸入框下方會出現工具圖標代表 MCP Tool 已就緒。4. 驗證請求與成功結果重啟 Claude Desktop 后做三組測試。第一組測天氣你北京今天天氣怎么樣 Claude自動調用 get_weather北京今天晴溫度 25°C濕度 60%風力 15km/h。第二組測計算器你幫我算一下 156.5 乘以 38.2 等于多少 Claude自動調用 calculator156.5 × 38.2 5978.3。第三組測組合調用你北京和上海哪個城市今天更熱 Claude分別調用 get_weather 兩次對比后回答北京 25°C上海 28°C上海今天更熱。如果工具圖標沒出現先用mcp dev命令單獨測試 Server它會給出比直接連 Claude Desktop 更詳細的錯誤信息pip install mcp mcp dev server.py這個命令會啟動一個開發(fā)模式你能看到 JSON-RPC 消息的收發(fā)過程。實測下來大部分問題在這一步就能定位。比如 Server 啟動后 Claude Desktop 連不上通常是 stdout 被print()污染了——stdio 傳輸下stdout 只能走協議消息日志必須走 stderr 或文件。把print(Server started)改成logging.info(Server started)就能解決。驗證成功后你可以嘗試修改 Tool 邏輯比如把天氣 API 換成自己的數據源或者加一個新 Tool 做數據庫查詢。MCP 的擴展性就在這里Server 端改一次所有支持 MCP 的 Client 都能用上新能力。5. 本篇常見錯誤排查開發(fā) MCP Server 時下面這些報錯我基本都遇到過對照排查能省不少時間。401 Unauthorized如果 MCP Server 內部調用模型走 TaoToken 通道時報 401先檢查TAOTOKEN_API_KEY環(huán)境變量是否傳入。Claude Desktop 的配置里env字段要寫全Key 不要有多余空格。用 curl 單獨測一次通道確認 Key 本身有效。local proxy failed / connection refused這類錯誤通常出現在 Server 啟動階段。檢查command和args路徑是否正確Windows 下路徑要用雙反斜杠或正斜杠。如果 Python 不在系統(tǒng) PATH 里command要寫 Python 的絕對路徑。reading choices 報錯如果 Server 返回的消息格式不符合 JSON-RPC 2.0 規(guī)范Client 解析時會報類似reading choices的錯誤。檢查handle_call_tool的返回值必須是[{type: text, text: ...}]這種結構不能直接返回裸字典。OAuth 相關報錯部分客戶端在連接遠程 MCP Server 時會走 OAuth 流程。如果你用的是 stdio 本地 Server一般不會遇到如果遇到檢查 Client 的認證配置確認沒有誤配遠程地址。Tool 不出現先確認 Claude Desktop 完全退出再重啟不是關窗口。然后檢查 JSON 配置文件格式多一個逗號都會導致解析失敗。用mcp dev server.py確認 Server 本身能正常列出 Tool。Tool 調用參數錯誤模型是根據inputSchema生成調用參數的。如果required數組漏了關鍵字段模型可能不傳參。每個 Tool 的必填參數都要寫進required。description也要寫清楚模型靠它理解 Tool 用途描述越明確調用越準。異步阻塞導致 Server 無響應在 async 上下文里調用同步阻塞函數比如requests.get會卡住整個 Server。用httpx.AsyncClient替代requests所有耗時操作都走異步。一個實用技巧Tool 的description字段極其重要。模型是根據描述來決定是否調用、怎么調用的。描述里寫清「做什么、什么場景用、參數含義」調用成功率能明顯提升。我試過把描述從「查天氣」改成「獲取指定城市的當前天氣信息包括溫度、濕度、天氣狀況和風力」模型調用準確率肉眼可見地變好。6. 繼續(xù)深入從 Demo 到生產可用跑通 Demo 只是起點。接下來你可以做幾件事把天氣 API 換成自己的業(yè)務數據源比如訂單查詢、用戶信息加一個 Resource 原語把數據庫表結構暴露給模型作為上下文或者用 HTTPSSE 傳輸把 Server 部署到遠程讓團隊共用。如果你在配置 Claude Code 或 Codex 時遇到認證問題記得三件套要寫全Base URL 用https://taotoken.net/apiKey 從控制臺 API Keys 頁面獲取Model ID 按實際使用的模型填寫。需要長期跑編碼任務或 Agent 場景可以了解 Coding Plan只是想驗證模型連通性用模型對話頁面發(fā)一條消息即可。接入文檔里有各客戶端的完整配置示例排障時對照檢查效率更高。MCP 正在成為 LLM 應用開發(fā)的基礎設施。現在動手寫第一個 Server比等到生態(tài)完全成熟再入場能更早理解協議層的設計取舍。遇到問題別急著換方案先用mcp dev把 JSON-RPC 消息打出來看大部分坑都在消息格式和傳輸層上。