Claude MCP Server 自建教學:用 Python FastMCP 打造專屬 AI 工具外掛
什麼是 MCP?為什麼你需要自建 Server
說真的,當我第一次聽到 MCP(Model Context Protocol)的時候,我的反應是「又一個協定?」但實際玩了一陣子之後,我得說這東西真的改變了我跟 AI 互動的方式。簡單來說,MCP 就是讓 Claude 這類大型語言模型能夠「伸出手來」操作外部工具的標準協定。
想像一下,你跟 Claude 聊天的時候,它不只能回答問題,還能直接幫你查資料庫、呼叫公司內部 API、甚至操作你的開發環境。這就是 MCP 帶來的可能性。如果你之前看過我們的MCP 協定實戰教學,你應該對基本概念有一定了解了。今天我們要更進一步,自己動手打造一個 MCP Server。
MCP 架構快速解析
MCP 的架構其實很直覺。它分成三個角色:Host(像是 Claude Desktop)、Client(由 Host 內建管理)、以及 Server(就是我們要自建的部分)。Server 負責暴露「工具」(Tools)給 AI 使用,Host 透過 Client 跟 Server 溝通。整個流程走的是 JSON-RPC 2.0,所以不管你用什麼語言實作都可以。
不過老實說,用 Python 的 FastMCP 框架是目前最省事的做法。它把底層的協定細節全包起來了,你只需要專注在寫工具邏輯就好。
為什麼要自建而不用現成的?
市面上已經有不少現成的 MCP Server,像是檔案系統存取、GitHub 整合、資料庫查詢等等。但問題是,每個團隊的需求都不一樣。你可能需要連接公司內部的 ERP 系統,或者查詢自建的知識庫,這些東西不可能有現成方案。自建 Server 的好處就是完全客製化,你要什麼功能就做什麼功能,安全性也由你自己掌控。
環境準備與 FastMCP 安裝
Python 環境設定
開始之前,確保你的環境符合以下需求。我個人建議用 Python 3.11 以上的版本,搭配虛擬環境來隔離套件依賴。
# 建立專案資料夾
mkdir my-mcp-server
cd my-mcp-server
# 建立虛擬環境
python -m venv .venv
# 啟用虛擬環境(macOS/Linux)
source .venv/bin/activate
# 啟用虛擬環境(Windows)
.venv\Scripts\activate
虛擬環境這步千萬別省略。我之前就是因為偷懶直接裝在全域環境,結果套件版本衝突搞了我一整個下午。真的是花了很多冤枉時間才學到這個教訓。
安裝 FastMCP 套件
pip install fastmcp
# 如果需要資料庫功能
pip install asyncpg aiohttp
FastMCP 是 Anthropic 官方推薦的 Python MCP 框架,API 設計得非常 Pythonic,用裝飾器就能把一般函式變成 MCP 工具,學習曲線幾乎是零。我自己從零到跑起第一個 Server,大概只花了不到二十分鐘。
第一個 MCP Server:從 Hello World 開始
建立基本工具函式
先來寫個最簡單的範例,讓你感受一下 FastMCP 有多方便。建立一個 server.py 檔案:
from fastmcp import FastMCP
# 初始化 Server
mcp = FastMCP("我的工具箱")
@mcp.tool()
def greet(name: str) -> str:
"""跟使用者打招呼的工具"""
return f"哈囉 {name}!我是你的 MCP 工具,有什麼可以幫忙的嗎?"
@mcp.tool()
def calculate_bmi(weight_kg: float, height_cm: float) -> str:
"""計算 BMI 值"""
height_m = height_cm / 100
bmi = weight_kg / (height_m ** 2)
return f"BMI = {bmi:.1f}"
注意看,每個工具就是一個普通的 Python 函式,加上 @mcp.tool() 裝飾器就搞定了。函式的 docstring 會自動變成工具的描述,讓 Claude 知道什麼時候該用這個工具。型別提示(type hints)也很重要,FastMCP 會自動根據它們產生參數的 JSON Schema。
註冊工具到 Server
在檔案最後面加上啟動指令:
if __name__ == "__main__":
mcp.run(transport="stdio")
這邊用的是 stdio 傳輸方式,也就是透過標準輸入輸出來跟 Host 通訊。這是目前最常見的本地 MCP Server 通訊方式。如果你要部署成遠端服務,可以改用 SSE(Server-Sent Events)傳輸。
實戰:打造資料庫查詢工具
好,玩具等級的東西寫完了,來點真正有用的。假設你團隊用 PostgreSQL 存放產品資料,你想讓 Claude 直接幫你查詢。這在做AI Agent 記憶系統設計的時候特別有用,因為 Agent 經常需要從外部資料來源取得上下文。
資料庫連線設定
import asyncpg
import os
from contextlib import asynccontextmanager
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:pass@localhost:5432/mydb")
@asynccontextmanager
async def get_db_connection():
conn = await asyncpg.connect(DATABASE_URL)
try:
yield conn
finally:
await conn.close()
這邊用 asyncpg 而不是 psycopg2,因為 FastMCP 本身是基於 asyncio 的。另外,資料庫連線字串一定要用環境變數,千萬不要寫死在程式碼裡面,這是最基本的安全觀念。
查詢工具實作
@mcp.tool()
async def query_products(
keyword: str,
limit: int = 10
) -> str:
"""根據關鍵字搜尋產品資料庫,回傳符合的產品清單"""
async with get_db_connection() as conn:
rows = await conn.fetch(
"SELECT id, name, price, stock FROM products "
"WHERE name ILIKE $1 LIMIT $2",
f"%{keyword}%", limit
)
if not rows:
return "沒有找到符合的產品"
results = []
for row in rows:
results.append(
f"- {row['name']}(NT${row['price']:,},庫存 {row['stock']})"
)
return "\n".join(results)
幾個重點提醒。第一,一定要用參數化查詢($1, $2),絕對不要用 f-string 拼接 SQL,SQL injection 不是開玩笑的。第二,設定 limit 上限,避免一次撈出幾萬筆資料把 context window 塞爆。第三,回傳格式盡量簡潔,Claude 不需要看到原始的 JSON 物件,格式化過的文字更好處理。
實戰:打造 API 呼叫工具
除了資料庫,串接外部 API 也是常見需求。比如說你想讓 Claude 查詢即時匯率:
import aiohttp
@mcp.tool()
async def get_exchange_rate(
from_currency: str,
to_currency: str
) -> str:
"""查詢即時匯率,例如 USD 對 TWD"""
url = f"https://api.exchangerate-api.com/v4/latest/{from_currency}"
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if resp.status != 200:
return f"查詢失敗:HTTP {resp.status}"
data = await resp.json()
rate = data["rates"].get(to_currency)
if rate is None:
return f"找不到 {to_currency} 的匯率"
return f"1 {from_currency} = {rate} {to_currency}"
這種工具在實際工作中超級好用。想像你在跟 Claude 討論國際定價策略,它可以即時幫你換算匯率,不用再自己切換瀏覽器分頁查。把 AI 助手跟真實世界的資料串起來,這才是 MCP 真正的威力。
連接 Claude Desktop 設定
設定 claude_desktop_config.json
Server 寫好之後,要讓 Claude Desktop 知道去哪裡找到它。打開 Claude Desktop 的設定檔:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"my-toolbox": {
"command": "/path/to/.venv/bin/python",
"args": ["/path/to/server.py"],
"env": {
"DATABASE_URL": "postgresql://user:pass@localhost:5432/mydb"
}
}
}
}
這邊要注意 command 要指向虛擬環境裡的 Python 路徑,不是系統的 Python。環境變數可以直接在這裡設定,不用另外搞 .env 檔案。設定完存檔後,重啟 Claude Desktop 就能看到你的工具了。
測試與除錯技巧
開發 MCP Server 最讓人抓狂的地方就是除錯。因為 Server 是被 Claude Desktop 啟動的子程序,你看不到它的 stdout。以下幾個技巧是我血淚換來的經驗:
- 用 logging 模組輸出到檔案:把 log 寫到固定路徑,方便追蹤每次工具呼叫的輸入輸出。
- 用 MCP Inspector 工具:Anthropic 有提供一個互動式的除錯工具,可以直接跟你的 Server 對話測試。
- 先單獨測試工具函式:在寫 MCP 包裝之前,先用 pytest 確保底層邏輯是對的。
# 用 MCP Inspector 除錯
npx @anthropic/mcp-inspector python server.py
Inspector 會開一個瀏覽器介面,讓你手動呼叫每個工具並看到回傳結果,非常方便。我現在開發新工具的時候,第一步一定是先用 Inspector 測過一輪。
MCP Server 開發最佳實踐
經過這幾個月的實際開發經驗,我整理了幾條最佳實踐:
- 工具粒度要適中:一個工具做一件事就好。不要寫一個萬能工具接受十幾個參數,Claude 會搞不清楚什麼時候該用它。
- Docstring 寫清楚:這是 Claude 判斷要不要使用工具的關鍵依據。描述要具體,包含使用場景和參數說明。
- 做好錯誤處理:工具出錯不應該讓整個 Server 掛掉。用 try-except 包起來,回傳有意義的錯誤訊息。
- 限制回傳資料量:Claude 的 context window 有限,不要一次灌幾千行資料進去。必要時做摘要或分頁。
- 安全性優先:只讀操作盡量跟寫入操作分開。寫入工具加上確認機制,避免 AI 誤操作。
這些原則跟設計好的RAG 評估指標有點類似,核心都是確保 AI 取得的資訊品質夠高、夠精準。
常見問題排解
以下是我自己和同事踩過的坑,希望能幫你少走彎路:
- Server 啟動但工具沒出現:檢查 Python 路徑是否正確,以及虛擬環境裡有沒有裝 fastmcp。這個問題佔了我除錯時間的一半以上。
- 工具執行超時:FastMCP 預設有超時限制。如果你的查詢比較慢,可以在
@mcp.tool()裡設定 timeout 參數。 - 中文亂碼:確保你的 Python 檔案是 UTF-8 編碼,然後在回傳字串時不要手動做 encode/decode。
- 資料庫連線池問題:如果用 asyncpg 的 pool,記得在 Server 啟動時初始化、關閉時釋放。用 FastMCP 的 lifespan 功能來管理生命週期。
- Windows 路徑問題:設定檔裡的路徑用正斜線或雙反斜線,避免轉義問題。
總結與下一步
回顧一下,今天我們從零開始,用 Python FastMCP 框架建立了一個功能完整的 MCP Server,包含資料庫查詢和 API 呼叫兩種工具。整個過程其實不難,FastMCP 把大部分複雜的協定處理都封裝好了,你只需要專注在業務邏輯上。
接下來你可以嘗試的方向包括:加入更多工具(檔案操作、圖表生成、郵件發送)、用 SSE 傳輸方式部署成遠端服務、或者把多個 Server 組合起來建立更複雜的 AI 工作流程。MCP 的生態系正在快速成長,現在投入學習絕對是好時機。
如果你對 AI Agent 的整體架構有興趣,推薦去看看我們的其他相關文章,裡面有更完整的系統設計思路。有任何問題歡迎在下面留言,我們一起討論!
繼續閱讀
AI Agent 記憶系統設計:從短期到長期記憶的完整實作指南
想讓你的 AI Agent 記住之前的對話、學會累積經驗?這篇從零開始教你設計完整的記憶系統。
相關文章
你可能也喜歡
探索其他領域的精選好文