在前一篇文章中,我們學會了如何使用現有的 MCP Server 來擴展 Claude 的能力。但如果現有的 Server 無法滿足你的需求,或者你想為團隊打造專屬的工具整合,那就需要自己動手開發 MCP Server 了。本篇將帶你使用 Python 搭配官方的 MCP SDK 與 FastMCP 框架,從零開始建構一個功能完整的 MCP Server。
為什麼選擇 Python 開發 MCP Server
MCP Server 可以使用多種語言開發,官方提供了 TypeScript 和 Python 兩套 SDK。選擇 Python 有幾個明確的優勢:
- 語法簡潔直覺:Python 的 decorator 語法讓定義 Tool 和 Resource 變得非常優雅,幾行程式碼就能完成一個工具
- 豐富的生態系:Python 擁有大量的資料處理、機器學習、網路爬蟲等套件,開發 MCP Server 時可以直接整合這些能力
- FastMCP 框架:官方 SDK 內建的 FastMCP 框架大幅簡化了開發流程,用類似 Flask 的寫法就能建立 Server
- 廣泛的使用者基礎:Python 是全球最受歡迎的程式語言之一,團隊成員更容易上手維護
- 快速原型開發:Python 的動態特性讓你能快速驗證想法,適合敏捷開發流程
環境準備與安裝
在開始之前,請確認你的開發環境已準備就緒。MCP Python SDK 需要 Python 3.10 以上的版本,建議使用 uv 作為套件管理工具,因為它速度更快且是 MCP 官方推薦的方案。
安裝 uv 套件管理工具
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
# 驗證安裝
uv --version
建立專案與安裝 SDK
# 建立專案目錄
mkdir my-mcp-server
cd my-mcp-server
# 初始化 Python 專案
uv init
# 安裝 MCP Python SDK(內含 FastMCP)
uv add "mcp[cli]"
# 如需額外的 HTTP 功能
uv add httpx
安裝完成後,你的 pyproject.toml 會自動加入 mcp 作為依賴項。MCP Python SDK 的核心模組就是 FastMCP,它是整個開發體驗的基礎。
認識 FastMCP 框架
FastMCP 是 MCP Python SDK 的核心框架,它的設計理念類似 Python 的 Web 框架(如 Flask、FastAPI),透過 decorator 來註冊各種功能。FastMCP 最初由社群開發,後來被納入官方 SDK,現已成為 Python 開發 MCP Server 的標準方式。
一個最基本的 MCP Server 只需要幾行程式碼:
from mcp.server.fastmcp import FastMCP
# 建立 MCP Server 實例
mcp = FastMCP("my-server")
# 定義一個簡單的 Tool
@mcp.tool()
def hello(name: str) -> str:
"""向指定的人打招呼"""
return f"你好,{name}!歡迎使用 MCP Server。"
# 啟動 Server
if __name__ == "__main__":
mcp.run()
這段程式碼展示了 FastMCP 的核心概念:建立實例、用 decorator 註冊功能、啟動伺服器。FastMCP 會自動根據函式的型別標註(type hints)和 docstring 產生 MCP 協定所需的 schema,你不需要手動撰寫 JSON Schema。
MCP Server 的三大核心功能
MCP 協定定義了三種主要的功能類型,每種都有不同的用途和使用場景:
| 功能類型 | 說明 | 類比 | 使用場景 |
|---|---|---|---|
| Tools | 可被 AI 呼叫的函式,會產生副作用或執行運算 | 類似 POST API | 執行查詢、發送通知、操作資料庫 |
| Resources | 提供資料給 AI 讀取,類似檔案或 API 端點 | 類似 GET API | 讀取設定檔、載入資料集、取得狀態 |
| Prompts | 預定義的提示詞範本,引導 AI 執行特定任務 | 類似訊息範本 | 程式碼審查、報告產生、文件摘要 |
定義 Tools(工具)
Tools 是 MCP Server 最常用的功能,它讓 AI 能夠呼叫你定義的函式來執行各種操作。定義 Tool 的關鍵在於提供清楚的型別標註和 docstring,因為 FastMCP 會將這些資訊轉換為 AI 可以理解的描述。
from mcp.server.fastmcp import FastMCP
import httpx
import json
mcp = FastMCP("weather-server")
@mcp.tool()
async def get_weather(city: str, unit: str = "celsius") -> str:
"""查詢指定城市的天氣資訊
Args:
city: 城市名稱,例如 "Taipei" 或 "Tokyo"
unit: 溫度單位,可選 "celsius" 或 "fahrenheit",預設 celsius
"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://api.weatherapi.com/v1/current.json",
params={"q": city, "key": "YOUR_API_KEY"}
)
data = response.json()
temp = data["current"]["temp_c"] if unit == "celsius" else data["current"]["temp_f"]
condition = data["current"]["condition"]["text"]
return f"{city} 目前天氣:{condition},溫度 {temp}°{'C' if unit == 'celsius' else 'F'}"
@mcp.tool()
def calculate_bmi(weight_kg: float, height_cm: float) -> str:
"""計算身體質量指數(BMI)
Args:
weight_kg: 體重(公斤)
height_cm: 身高(公分)
"""
height_m = height_cm / 100
bmi = weight_kg / (height_m ** 2)
if bmi < 18.5:
category = "體重過輕"
elif bmi < 24:
category = "正常範圍"
elif bmi < 27:
category = "過重"
else:
category = "肥胖"
return f"BMI: {bmi:.1f}({category})"
上面的範例展示了幾個重要的 Tool 開發技巧:
- 型別標註是必要的:FastMCP 透過型別標註來產生 JSON Schema,告訴 AI 每個參數的型別
- docstring 就是描述:函式的 docstring 會成為 Tool 的說明文字,AI 會依此判斷何時該使用這個工具
- 支援 async:對於需要網路請求的 Tool,建議使用 async 函式來提升效能
- 預設值會變成可選參數:有預設值的參數在 Schema 中會被標記為 optional
定義 Resources(資源)
Resources 用來向 AI 提供可讀取的資料,它的角色類似 REST API 的 GET 端點。每個 Resource 都有一個唯一的 URI,AI 可以透過這個 URI 來存取資料。
import json
from pathlib import Path
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("data-server")
# 靜態資源:固定 URI
@mcp.resource("config://app-settings")
def get_app_settings() -> str:
"""取得應用程式的設定資訊"""
settings = {
"app_name": "MyApp",
"version": "2.1.0",
"environment": "production",
"features": ["auth", "logging", "cache"]
}
return json.dumps(settings, indent=2, ensure_ascii=False)
# 動態資源:URI 中包含參數
@mcp.resource("file://logs/{date}")
def get_log_by_date(date: str) -> str:
"""讀取指定日期的 log 檔案
Args:
date: 日期格式為 YYYY-MM-DD
"""
log_path = Path(f"/var/log/app/{date}.log")
if log_path.exists():
return log_path.read_text()
return f"找不到 {date} 的 log 檔案"
# Resource Template:動態產生資源清單
@mcp.resource("db://users/{user_id}")
def get_user(user_id: int) -> str:
"""根據 ID 查詢使用者資訊"""
# 模擬資料庫查詢
users = {
1: {"name": "Alice", "role": "admin"},
2: {"name": "Bob", "role": "user"}
}
user = users.get(user_id)
if user:
return json.dumps(user, ensure_ascii=False)
return f"找不到 ID 為 {user_id} 的使用者"
定義 Prompts(提示範本)
Prompts 是預先定義好的提示詞範本,讓使用者可以快速啟動特定的 AI 工作流程。它不像 Tool 是由 AI 自動決定呼叫,而是由使用者主動選擇觸發。
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("code-review-server")
@mcp.prompt()
def code_review(code: str, language: str = "python") -> str:
"""產生程式碼審查的提示詞
Args:
code: 需要審查的程式碼
language: 程式語言,預設 python
"""
return f"""請審查以下 {language} 程式碼,並提供改善建議:
```{language}
{code}
```
請從以下面向進行審查:
1. 程式碼品質與可讀性
2. 潛在的 Bug 或安全性問題
3. 效能最佳化建議
4. 是否符合 {language} 的最佳實踐"""
@mcp.prompt()
def summarize_doc(content: str, max_words: int = 200) -> str:
"""產生文件摘要的提示詞"""
return f"請將以下內容摘要為不超過 {max_words} 字的重點整理:\n\n{content}"
完整範例:專案管理 MCP Server
接下來,我們用一個實際的專案管理工具來展示如何結合 Tools、Resources 和 Prompts 開發一個功能完整的 MCP Server。這個範例模擬了一個簡單的 TODO 管理系統。
from mcp.server.fastmcp import FastMCP
from datetime import datetime
import json
# 建立 Server 並設定相依套件
mcp = FastMCP(
"todo-manager",
dependencies=["httpx"]
)
# 模擬的任務資料庫
tasks: dict[int, dict] = {}
next_id = 1
# ===== Tools =====
@mcp.tool()
def add_task(title: str, priority: str = "medium") -> str:
"""新增一個待辦任務
Args:
title: 任務標題
priority: 優先級,可選 high、medium、low
"""
global next_id
task = {
"id": next_id,
"title": title,
"priority": priority,
"status": "pending",
"created_at": datetime.now().isoformat()
}
tasks[next_id] = task
next_id += 1
return f"已新增任務:{title}(ID: {task['id']})"
@mcp.tool()
def complete_task(task_id: int) -> str:
"""將指定任務標記為完成
Args:
task_id: 任務 ID
"""
if task_id not in tasks:
return f"找不到 ID 為 {task_id} 的任務"
tasks[task_id]["status"] = "completed"
return f"任務 {tasks[task_id]['title']} 已標記為完成"
@mcp.tool()
def delete_task(task_id: int) -> str:
"""刪除指定的任務
Args:
task_id: 任務 ID
"""
if task_id not in tasks:
return f"找不到 ID 為 {task_id} 的任務"
removed = tasks.pop(task_id)
return f"已刪除任務:{removed['title']}"
# ===== Resources =====
@mcp.resource("todo://tasks")
def list_all_tasks() -> str:
"""取得所有任務的清單"""
return json.dumps(list(tasks.values()), indent=2, ensure_ascii=False)
@mcp.resource("todo://tasks/{task_id}")
def get_task_detail(task_id: int) -> str:
"""取得單一任務的詳細資訊"""
task = tasks.get(task_id)
if task:
return json.dumps(task, indent=2, ensure_ascii=False)
return f"找不到 ID 為 {task_id} 的任務"
@mcp.resource("todo://stats")
def get_task_stats() -> str:
"""取得任務統計資訊"""
total = len(tasks)
completed = sum(1 for t in tasks.values() if t["status"] == "completed")
pending = total - completed
return json.dumps({
"total": total,
"completed": completed,
"pending": pending
})
# ===== Prompts =====
@mcp.prompt()
def daily_summary() -> str:
"""產生每日任務摘要的提示詞"""
return "請根據目前的任務清單,產生今日的工作摘要報告,包含已完成和待處理的任務。"
if __name__ == "__main__":
mcp.run()
在 Claude Code 中測試你的 Server
開發完成後,你需要在 Claude Code 中實際測試 Server 的功能。MCP SDK 提供了多種測試方式。
使用 MCP Inspector 測試
MCP Inspector 是官方提供的視覺化測試工具,它提供了一個 Web 介面讓你可以互動式地測試每個 Tool、Resource 和 Prompt。
# 啟動 MCP Inspector
mcp dev server.py
# Inspector 預設會在 http://localhost:5173 開啟
# 你可以在介面中:
# - 查看所有已註冊的 Tools、Resources、Prompts
# - 手動輸入參數測試每個功能
# - 檢視回傳結果和錯誤訊息
直接加入 Claude Code
確認功能正常後,就可以將 Server 加入 Claude Code 使用:
# 將 Server 加入 Claude Code(本地 stdio 模式)
claude mcp add todo-manager -- uv run server.py
# 如果使用 HTTP 傳輸模式
# 先啟動 Server
uv run server.py --transport http --port 8000
# 再加入 Claude Code
claude mcp add --transport http todo-manager http://localhost:8000/mcp
# 驗證 Server 已正確載入
claude mcp list
加入成功後,你可以直接在 Claude Code 中使用自然語言與 Server 互動。例如,輸入「幫我新增一個高優先級的任務:完成 API 文件撰寫」,Claude 就會自動呼叫 add_task 工具來執行。
Python vs TypeScript:該選哪個?
MCP 官方同時提供了 Python 和 TypeScript 兩套 SDK,兩者都能開發功能完整的 MCP Server,但在開發體驗和適用場景上有一些差異:
| 比較項目 | Python SDK | TypeScript SDK |
|---|---|---|
| 框架 | FastMCP(decorator 風格) | McpServer(method chain 風格) |
| 型別系統 | 動態型別 + type hints | 靜態型別(Zod schema) |
| Schema 產生 | 自動從 type hints 和 docstring 產生 | 需手動定義 Zod schema |
| 套件管理 | uv / pip | npm / yarn |
| 非同步支援 | asyncio(原生支援) | Promise / async-await |
| 適合場景 | 資料處理、ML 整合、快速原型 | 前端整合、Node.js 生態系 |
| 學習曲線 | 較低(Python 開發者友善) | 中等(需熟悉 Zod) |
| 程式碼量 | 較少(decorator 自動化程度高) | 較多(需手動定義 schema) |
簡單來說,如果你的團隊主要使用 Python,或者你需要整合資料處理和機器學習的功能,Python SDK 是更好的選擇。如果你的團隊以前端或 Node.js 為主,TypeScript SDK 會更自然。
進階技巧:Context 與 Lifespan
FastMCP 提供了一些進階功能,讓你能建構更複雜的 Server。
使用 Context 存取 MCP 功能
在 Tool 函式中,你可以透過 Context 物件來存取 MCP 的進階功能,例如回報進度、記錄日誌,或讀取其他 Resource。
from mcp.server.fastmcp import FastMCP, Context
mcp = FastMCP("advanced-server")
@mcp.tool()
async def process_data(file_path: str, ctx: Context) -> str:
"""處理大量資料並回報進度
Args:
file_path: 資料檔案路徑
"""
# 回報進度給 Client
await ctx.report_progress(0, 100)
# 記錄日誌
await ctx.info(f"開始處理檔案:{file_path}")
# 模擬處理過程
for i in range(1, 101):
# 處理資料...
if i % 10 == 0:
await ctx.report_progress(i, 100)
await ctx.info("處理完成")
return "資料處理完成,共處理 100 筆記錄"
使用 Lifespan 管理生命週期
當你的 Server 需要管理外部連線(如資料庫連接池)時,可以使用 lifespan 來處理啟動和關閉時的初始化邏輯。
from contextlib import asynccontextmanager
from mcp.server.fastmcp import FastMCP
@asynccontextmanager
async def app_lifespan(server: FastMCP):
"""管理 Server 的生命週期"""
# 啟動時執行:建立資料庫連線
db = await create_db_connection()
try:
yield {"db": db} # 將資源傳遞給 Tools
finally:
# 關閉時執行:釋放資源
await db.close()
mcp = FastMCP("db-server", lifespan=app_lifespan)
@mcp.tool()
async def query_users(ctx: Context) -> str:
"""查詢所有使用者"""
db = ctx.request_context.lifespan_context["db"]
users = await db.fetch_all("SELECT * FROM users")
return json.dumps(users, ensure_ascii=False)
部署你的 MCP Server
MCP Server 支援兩種主要的傳輸模式,各有不同的部署方式。
stdio 模式(本地部署)
stdio 是最簡單的部署方式,Server 作為本地行程執行,透過標準輸入/輸出與 Claude Code 溝通。適合個人使用或開發階段。
# server.py 的啟動方式
if __name__ == "__main__":
mcp.run() # 預設使用 stdio
# 加入 Claude Code
claude mcp add my-server -- uv run server.py
HTTP 模式(遠端部署)
HTTP 模式(Streamable HTTP)適合部署到雲端服務器,讓多人可以共享同一個 MCP Server。這是目前官方推薦的遠端傳輸方式。
# server.py 使用 HTTP 模式啟動
if __name__ == "__main__":
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)
# 加入 Claude Code(連線到遠端 Server)
claude mcp add --transport http my-remote-server https://your-server.com/mcp
專案結構建議
當你的 MCP Server 功能越來越多時,良好的專案結構能讓程式碼更容易維護。以下是建議的目錄配置:
my-mcp-server/
├── pyproject.toml # 專案設定與依賴管理
├── README.md # 專案說明文件
├── src/
│ └── my_mcp_server/
│ ├── __init__.py
│ ├── server.py # Server 主程式與 FastMCP 實例
│ ├── tools/ # Tools 定義
│ │ ├── __init__.py
│ │ ├── task_tools.py
│ │ └── data_tools.py
│ ├── resources/ # Resources 定義
│ │ ├── __init__.py
│ │ └── data_resources.py
│ └── prompts/ # Prompts 定義
│ ├── __init__.py
│ └── review_prompts.py
└── tests/ # 測試檔案
├── test_tools.py
└── test_resources.py
常見問題與除錯技巧
在開發 MCP Server 的過程中,你可能會遇到一些常見的問題。以下整理了幾個最常見的狀況和解決方法:
- Tool 沒有出現在 Claude 中:確認 docstring 是否完整,FastMCP 需要 docstring 才能產生描述。同時檢查
claude mcp list確認 Server 狀態為 running - 參數型別錯誤:確保所有參數都有型別標註,FastMCP 無法處理沒有標註的參數。使用
int、str、float、bool等基本型別 - async 函式沒有正確執行:如果你的 Tool 使用了 async,確認所有呼叫鏈都是 async 的。混合使用同步和非同步可能導致執行卡住
- Server 啟動後立即退出:檢查是否有語法錯誤或 import 失敗。使用
mcp dev server.py來查看詳細的錯誤訊息 - Resource URI 無法存取:確認 URI 格式正確,動態參數需要與函式參數名稱一致。例如
file://logs/{date}對應函式參數date
總結
使用 Python 開發 MCP Server 是擴展 Claude Code 能力最靈活的方式。透過 FastMCP 框架,你可以用簡潔的 decorator 語法快速定義 Tools、Resources 和 Prompts,不需要手動處理複雜的 MCP 協定細節。
本篇涵蓋了從環境建置、核心功能開發到測試部署的完整流程。你已經學會了如何建立 FastMCP 實例、定義三種核心功能類型、使用 Context 和 Lifespan 處理進階場景,以及如何在 Claude Code 中測試和部署你的 Server。
下一篇我們將進一步探討如何用 TypeScript 開發 MCP Server,並深入比較兩種語言在 MCP 開發上的實際體驗差異。如果你的需求偏向資料處理或 Python 生態系整合,現在就可以開始動手打造你的第一個 Python MCP Server 了。