 MCP Server 完全實(shí)戰(zhàn)指南:用裝飾器與 config.toml 接入 TaoToken)
1. 為什么 FastMCP 值得你花一個(gè)下午跑通如果你寫過原生 MCP Python SDK 的 Server大概記得那種感覺注冊(cè)一個(gè)工具要手寫 JSON Schema、手動(dòng)分發(fā)tools/list和tools/call、參數(shù)校驗(yàn)和錯(cuò)誤處理全靠自己兜。一個(gè)能用的 Server 動(dòng)輒三四十行樣板代碼改一個(gè)參數(shù)類型要同步改三處。FastMCP 把這件事壓成了裝飾器函數(shù)簽名即 Schemadocstring 即工具描述mcp.tool一貼就注冊(cè)完成。它現(xiàn)在是 Python 圈構(gòu)建 MCP Server 最主流的框架絕大多數(shù)公開的 MCP Server 都用它寫。這篇面向已經(jīng)會(huì)寫 Python、想快速把本地能力暴露給 AI 客戶端的開發(fā)者。我會(huì)帶你從零搭一個(gè)可運(yùn)行的 Server重點(diǎn)講清楚三件事裝飾器怎么注冊(cè)工具與資源、參數(shù)校驗(yàn)怎么靠類型注解自動(dòng)完成、以及怎么用一份config.toml把模型調(diào)用統(tǒng)一走 TaoToken 的 Key 和 API 通道。全程可復(fù)制跑完你能得到一個(gè)本地能調(diào)、客戶端能連、模型能用的完整鏈路。適合誰想給自己項(xiàng)目加 MCP 能力的后端、想把內(nèi)部工具接進(jìn) Cursor/Claude 的工程師、以及第一次接觸 MCP 協(xié)議想找條最短路徑的人。2. 前置準(zhǔn)備環(huán)境、依賴與 TaoToken 通道2.1 Python 環(huán)境與 FastMCP 安裝要求 Python 3.10 以上推薦 3.12。包管理我建議用 uvFastMCP 的命令行工具依賴它速度也比 pip 快很多。# 安裝 uvmacOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh # Windows PowerShell powershell -c irm https://astral.sh/up/install.ps1 | iex # 驗(yàn)證 uv --version建項(xiàng)目并裝依賴mkdir fastmcp-demo cd fastmcp-demo uv init uv add fastmcp httpx如果你習(xí)慣 pippip install fastmcp httpx同樣可以只是后面fastmcp dev這類命令需要 uv 在 PATH 里。2.2 為什么要在 MCP Server 里接 TaoTokenMCP Server 本身只負(fù)責(zé)暴露工具但很多工具內(nèi)部要調(diào)模型——比如一個(gè)「代碼審查」工具、一個(gè)「文本摘要」工具。如果每個(gè)工具各自去配 Key、各自處理不同廠商的 base_url配置會(huì)散得到處都是。TaoToken 提供統(tǒng)一的 Key 和 API 通道你只需要在config.toml里寫一份所有工具共享。這樣換模型、換通道只改一個(gè)文件不用動(dòng)業(yè)務(wù)代碼。先去控制臺(tái)拿 Key訪問 https://taotoken.net/console 在 API Keys 頁面創(chuàng)建一個(gè)。接入文檔在 https://taotoken.net/doc 里面有各語言的調(diào)用示例。API 基址是https://taotoken.net/api注意這個(gè)地址不帶任何查詢參數(shù)。注意Key 只顯示一次創(chuàng)建后立刻復(fù)制保存。不要把它硬編碼進(jìn)提交到 Git 的代碼里用環(huán)境變量或本地配置文件。3. 可復(fù)制配置config.toml 骨架與 Server 代碼3.1 config.toml 完整骨架在項(xiàng)目根目錄建config.toml# config.toml —— MCP Server 統(tǒng)一配置 [server] name FastMCP Demo transport stdio # 本地開發(fā)用 stdio遠(yuǎn)程部署改 streamable-http host 0.0.0.0 port 8000 [taotoken] # 統(tǒng)一模型通道所有工具共享這一份配置 base_url https://taotoken.net/api api_key sk-你的Key # 生產(chǎn)環(huán)境請(qǐng)改用環(huán)境變量注入 default_model claude-sonnet-4-20250514 timeout 60 [tools] # 工具級(jí)開關(guān)方便按環(huán)境裁剪 enable_summarize true enable_code_review true max_input_chars 8000讀取配置用一個(gè)輕量函數(shù)避免引入額外依賴# config_loader.py import tomllib from pathlib import Path def load_config(path: str config.toml) - dict: with open(Path(path), rb) as f: return tomllib.load(f) CONFIG load_config()tomllib是 Python 3.11 起的內(nèi)置庫3.10 用戶裝tomli并把 import 換成import tomli as tomllib即可。3.2 用裝飾器注冊(cè)工具與資源核心文件server.py。這里演示三種注冊(cè)方式普通工具、帶校驗(yàn)的工具、以及只讀資源。# server.py import httpx from typing import Literal, Optional from fastmcp import FastMCP from fastmcp.exceptions import ToolError from config_loader import CONFIG mcp FastMCP( nameCONFIG[server][name], instructions演示用 MCP Server提供文本摘要與代碼審查工具。, ) TAO CONFIG[taotoken] async def call_model(prompt: str, model: Optional[str] None) - str: 統(tǒng)一走 TaoToken 通道調(diào)用模型 payload { model: model or TAO[default_model], messages: [{role: user, content: prompt}], } headers { Authorization: fBearer {TAO[api_key]}, Content-Type: application/json, } async with httpx.AsyncClient(timeoutTAO[timeout]) as client: resp await client.post( f{TAO[base_url]}/v1/chat/completions, jsonpayload, headersheaders, ) resp.raise_for_status() data resp.json() return data[choices][0][message][content] mcp.tool async def summarize(text: str, style: Literal[brief, detailed] brief) - str: 對(duì)輸入文本做摘要。 Args: text: 待摘要的原文長度不超過配置上限 style: 摘要風(fēng)格brief 為要點(diǎn)式detailed 為段落式 Returns: 摘要結(jié)果字符串 limit CONFIG[tools][max_input_chars] if len(text) limit: raise ToolError(f輸入長度 {len(text)} 超過上限 {limit}請(qǐng)先截?cái)? if not text.strip(): raise ToolError(text 不能為空) instruction 用三到五條要點(diǎn)總結(jié) if style brief else 用一段話詳細(xì)總結(jié) return await call_model(f{instruction}以下內(nèi)容\n\n{text}) mcp.tool async def code_review(code: str, language: str python) - str: 對(duì)代碼片段做審查返回問題清單與改進(jìn)建議。 if not CONFIG[tools][enable_code_review]: raise ToolError(code_review 工具在當(dāng)前環(huán)境已禁用) prompt ( f你是資深 {language} 工程師請(qǐng)審查以下代碼 f指出潛在 bug、性能問題與可讀性問題\n\n{language}\n{code}\n ) return await call_model(prompt) mcp.resource(config://runtime) def runtime_config() - dict: 暴露當(dāng)前運(yùn)行配置脫敏供客戶端查看。 return { server_name: CONFIG[server][name], default_model: TAO[default_model], max_input_chars: CONFIG[tools][max_input_chars], } if __name__ __main__: transport CONFIG[server][transport] if transport streamable-http: mcp.run( transportstreamable-http, hostCONFIG[server][host], portCONFIG[server][port], ) else: mcp.run()幾個(gè)關(guān)鍵點(diǎn)值得展開。mcp.tool會(huì)自動(dòng)把函數(shù)名當(dāng)工具名、docstring 當(dāng)描述、類型注解轉(zhuǎn)成 JSON Schema所以style: Literal[brief, detailed]會(huì)被約束成枚舉客戶端傳別的值直接報(bào)錯(cuò)不用你寫校驗(yàn)。ToolError拋出的消息會(huì)原樣返回給 AI寫清楚「哪里錯(cuò)了、該怎么改」比拋通用異常有用得多。資源用mcp.resource(scheme://path)注冊(cè)只讀、無副作用適合放配置、文檔、統(tǒng)計(jì)這類上下文。3.3 參數(shù)校驗(yàn)的邊界FastMCP 基于 Pydantic 做校驗(yàn)常見類型都支持int/float/str/bool、List[T]、Dict[K,V]、Optional[T]、Literal[...]、Union、以及 Pydantic 的BaseModel。不支持的包括裸Tuple和自定義非 Pydantic 類。如果你要傳復(fù)雜結(jié)構(gòu)定義一個(gè)BaseModel子類當(dāng)參數(shù)類型最省事from pydantic import BaseModel class ReviewRequest(BaseModel): code: str language: str python max_issues: int 10 mcp.tool async def review_structured(req: ReviewRequest) - str: 結(jié)構(gòu)化入?yún)⒌拇a審查。 return await call_model(f審查 {req.language} 代碼最多列 {req.max_issues} 個(gè)問題\n{req.code})4. 啟動(dòng)驗(yàn)證與調(diào)用測試4.1 用 Inspector 做可視化調(diào)試最快的方式是fastmcp dev它會(huì)啟動(dòng)你的 Server 并自動(dòng)打開 MCP Inspectoruv run fastmcp dev server.py瀏覽器打開http://127.0.0.1:6274在 Tools 標(biāo)簽頁能看到summarize和code_review填參數(shù)點(diǎn) Run Tool 就能看到返回。Resources 標(biāo)簽頁里config://runtime會(huì)顯示脫敏后的運(yùn)行配置。這一步能確認(rèn)工具注冊(cè)成功、Schema 生成正確。4.2 用內(nèi)置 Client 寫程序化測試Inspector 適合手動(dòng)點(diǎn)回歸測試用 Client 更靠譜# test_server.py import asyncio from fastmcp import Client async def main(): async with Client(server.py) as client: await client.ping() print(Server 在線) tools await client.list_tools() print(工具列表:, [t.name for t in tools]) result await client.call_tool( summarize, {text: FastMCP 用裝飾器簡化了 MCP Server 開發(fā)。, style: brief}, ) print(摘要結(jié)果:, result.content[0].text) cfg await client.read_resource(config://runtime) print(運(yùn)行配置:, cfg) asyncio.run(main())運(yùn)行uv run python test_server.py。如果模型通道配置正確你會(huì)看到摘要文本返回如果 Key 或 base_url 有問題這里會(huì)直接拋 HTTP 錯(cuò)誤方便定位。4.3 接入客戶端以 Cursor 為例在.cursor/mcp.json里加{ mcpServers: { fastmcp-demo: { command: uv, args: [--directory, /絕對(duì)路徑/fastmcp-demo, run, python, server.py] } } }路徑必須用絕對(duì)路徑這是最常見的連不上的原因。Claude Desktop 的配置在~/Library/Application Support/Claude/claude_desktop_config.jsonmacOS結(jié)構(gòu)相同。改完重啟客戶端在工具列表里就能看到你的 Server。5. 本篇常見錯(cuò)排查工具不出現(xiàn)九成是忘了貼mcp.tool或者函數(shù)定義在if __name__塊里沒被執(zhí)行到。檢查裝飾器是否緊貼函數(shù)??蛻舳诉B不上先看路徑是不是絕對(duì)路徑再看 JSON 有沒有多余逗號(hào)。用uv run python server.py手動(dòng)跑一遍能正常等待輸入說明 Server 本身沒問題。參數(shù)校驗(yàn)失敗報(bào)錯(cuò)信息里會(huì)寫清楚哪個(gè)字段、期望什么類型。常見是客戶端傳了字符串10而注解是int或者Literal傳了不在枚舉里的值。模型調(diào)用 401Key 錯(cuò)了或沒帶Bearer前綴。檢查config.toml里的api_key和base_urlbase_url 結(jié)尾不要多加斜杠。模型調(diào)用超時(shí)把timeout調(diào)大或者檢查網(wǎng)絡(luò)。長文本摘要建議在工具里先截?cái)鄤e把幾萬字直接塞進(jìn)去。改了代碼不生效fastmcp dev有熱重載但客戶端連接的是獨(dú)立進(jìn)程改完要重啟客戶端。6. 把鏈路固定下來跑通之后建議把 Key 從config.toml挪到環(huán)境變量用os.environ[TAOTOKEN_API_KEY]讀取配置文件里只留占位。這樣提交代碼不會(huì)泄露憑證。工具粒度上一個(gè) Server 專注一類能力別把摘要、審查、數(shù)據(jù)庫查詢?nèi)黄鹂蛻舳诉x擇工具時(shí)會(huì)更準(zhǔn)。docstring 寫詳細(xì)點(diǎn)AI 靠它判斷什么時(shí)候調(diào)用你的工具寫得含糊它就不敢用。需要長期跑編碼類 Agent、把 MCP 工具接進(jìn)日常開發(fā)流的可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。想先在網(wǎng)頁里驗(yàn)證模型通道是否通用模型對(duì)話https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 。Key 管理和接入細(xì)節(jié)分別在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 和 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。