Agent 與 MCP
動手寫第一個 MCP server:用 Python SDK 十幾行搞定
動手寫第一個 MCP server:用 Python SDK 十幾行搞定
看懂概念之後,最快的學法就是自己寫一個。這篇我們用官方 Python SDK 蓋一個文件管理的 MCP server:文件放在記憶體裡的 dict,提供讀取與編輯兩個工具,再加上 resources 與 prompts,最後用內建的 Inspector 測試。你會發現整件事精簡得出乎意料。
先說一個實務提醒:真實專案裡,你通常只會實作 client 或 server 其中一邊。要把自家服務開放給其他開發者,寫 server;要接上現成的服務,寫 client。教學專案兩邊都寫,純粹是為了看懂它們怎麼配合。
你將學到什麼
一行初始化
FastMCP 一行就能建出完整的 MCP server 骨架。
decorator 定義工具
型別提示自動變 JSON schema,不用手寫規格。
resources 與 prompts
同一套寫法,把資料通道與指令範本一起補上。
Inspector 即時測試
瀏覽器裡列出、執行、驗證工具,不必接完整應用。
動手前:把專案跑起來
起手式是一個 CLI 專案包,照 README 三步就能啟動:把 Anthropic API key 填進 .env、用 UV(推薦)或 pip 安裝依賴、跑一次主程式確認一切正常。啟動後會看到聊天提示,先問個「1 加 1 等於多少」確認 Claude 有回應,再開始動工。
# 用 UV(推薦)
uv run main.py
# 或用標準 Python
python main.py
專案主要就三個檔案:main.py、mcp_client.py、mcp_server.py。這篇我們只動 mcp_server.py,client 那一側留給下一篇。
初始化:一行就有一個 server
官方 Python SDK 提供 FastMCP,初始化一行搞定。文件用最簡單的 dict 存放,key 是文件 id,value 是內容,先不碰資料庫:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("DocumentMCP", log_level="ERROR")
docs = {
"deposition.md": "This deposition covers the testimony of Angela Smith, P.E.",
"report.pdf": "The report details the state of a 20m condenser tower.",
"plan.md": "The plan outlines the steps for the project's implementation.",
}
FastMCP 的第一個引數是 server 名稱,log_level 控制日誌量。這個 server 接下來會提供兩個工具:一個讀文件內容,一個做找字取代的編輯,正好對應「讀」與「寫」兩種最基本的文件操作。
用 decorator 定義工具,不手寫 schema
SDK 最大的好處:不用手寫 JSON schema。你用 @mcp.tool decorator 加上型別提示,SDK 自動生成 Claude 需要的 schema;Pydantic 的 Field 則提供參數說明,幫 Claude 理解每個引數的用途。先看讀取工具:
@mcp.tool(
name="read_doc_contents",
description="Read the contents of a document and return it as a string."
)
def read_document(
doc_id: str = Field(description="Id of the document to read")
):
if doc_id not in docs:
raise ValueError(f"Doc with id {doc_id} not found")
return docs[doc_id]
拆開來看:decorator 的 name 與 description 告訴 Claude 這個工具是誰、能做什麼;參數上的型別提示與 Field 描述,則變成 schema 裡的欄位說明。Claude 挑工具、填引數時讀的就是這些文字,所以描述寫得越清楚,Claude 用得越準。
編輯工具做簡單的找字取代,三個參數:文件 id、要找的字串、要換上的字串。實作直接用 Python 內建的字串 replace:
@mcp.tool(
name="edit_document",
description="Edit a document by replacing a string in the documents content with a new string."
)
def edit_document(
doc_id: str = Field(description="Id of the document that will be edited"),
old_str: str = Field(description="The text to replace. Must match exactly, including whitespace."),
new_str: str = Field(description="The new text to insert in place of the old text.")
):
if doc_id not in docs:
raise ValueError(f"Doc with id {doc_id} not found")
docs[doc_id] = docs[doc_id].replace(old_str, new_str)
兩個工具都有基本的錯誤處理:文件不存在就丟出 ValueError,訊息寫清楚,Claude 收到後能理解發生什麼事,甚至據此調整下一步。整理一下 SDK 寫法的好處:
- 型別提示自動變成 JSON schema,不必手寫也不會寫歪。
- Pydantic 內建參數驗證,錯的輸入進不來。
- 錯誤處理就用 Python 例外,寫起來很自然。
- 樣板程式碼大幅減少,程式乾淨好維護。
加上 resources 與 prompts
同一套 decorator 模式延伸到另外兩個原語。resources 用 @mcp.resource 定義:URI 固定的是 direct resource,URI 帶參數的是 templated resource,SDK 會自動解析參數傳給函式;mime_type 給 client 一個解析提示,回傳值由 SDK 自動序列化,不必自己轉 JSON 字串:
@mcp.resource(
"docs://documents",
mime_type="application/json"
)
def list_docs() -> list[str]:
return list(docs.keys())
@mcp.resource(
"docs://documents/{doc_id}",
mime_type="text/plain"
)
def fetch_doc(doc_id: str) -> str:
if doc_id not in docs:
raise ValueError(f"Doc with id {doc_id} not found")
return docs[doc_id]
mime_type 常用的值:application/json 表示結構化資料、text/plain 表示純文字,其他合法的 MIME type 也都可以,甚至二進位資料。
prompts 用 @mcp.prompt 定義,回傳一組可以直接送給 Claude 的訊息。這裡做一個 format 指令,把指定文件重寫成 markdown 格式:
@mcp.prompt(
name="format",
description="Rewrites the contents of the document in Markdown format."
)
def format_document(
doc_id: str = Field(description="Id of the document to format")
) -> list[base.Message]:
prompt = f"""
Your goal is to reformat a document to be written with markdown syntax.
The id of the document you need to reformat is:
<document_id>
{doc_id}
</document_id>
Add in headers, bullet points, tables, etc as necessary.
Use the 'edit_document' tool to edit the document.
"""
return [base.UserMessage(prompt)]
留意兩件事:一是 prompt 用 XML 標籤把 doc_id 包起來,界線清楚;二是範本裡明確指示 Claude 用 edit_document 工具改文件,prompt 與工具本來就是設計來搭配使用的。在 Inspector 的 Prompts 分頁可以直接測:填一個 doc_id,看變數安插後產生的訊息長什麼樣,確認沒問題再交給使用者。
用 Inspector 邊寫邊測
寫好的 server 不必接上完整應用才能測。SDK 內建瀏覽器版的 MCP Inspector,一行啟動:
mcp dev mcp_server.py
啟動後打開它給的本機網址,按 Connect 連上 server,連線狀態會從 Disconnected 變成 Connected。接著就能在 Tools、Resources、Prompts 幾個分頁裡逐一測試。以讀取工具為例:進 Tools 分頁按 List Tools 列出全部工具,選 read_doc_contents,在右側欄位填入 deposition.md 這類文件 id,按 Run Tool,下方就會顯示執行狀態與回傳內容。
你也可以串著測,先用 edit_document 改一段文字,馬上再用 read_doc_contents 讀回來,確認修改真的生效;Inspector 會在工具呼叫之間保留 server 的狀態,這種「改完馬上讀回來」的串測完全可行。Inspector 還在快速開發中,介面版本更新很快,但核心功能一致。
除了正常路徑,也順手測錯誤情境:填一個不存在的文件 id,確認回來的是那句寫清楚的錯誤訊息,而不是整個 server 掛掉。邊角情況在這裡先驗過,之後 Claude 真的踩到時,你才知道它會看到什麼。
之後的開發節奏就是固定四拍:改 server 程式、在 Inspector 裡測個別工具、驗證結果、隔離除錯。等這裡全綠,再把 server 接進 client 與 Claude,整合階段就只剩接線,不會邊接邊抓蟲。
延伸學習
高效思維與腦內模擬:30 日練習與行動系統
每天一件事,三十天把「想到就做」換成「先在腦中跑一遍再動手」。六週依序練目標設定、腦內模擬、結果檢查、反饋與應變、知識萃取、總結與新目標,每天一課圖文,附可以直接填的練習表。週一給概念、週二實作、週三看案例、週四反思、週五收束,跟著節奏走就好,不必自己安排。
NT$ 1,599
HE201|Harness Engineering System Design(6 小時)
六小時的實作課:從 Blueprint 走到可以跑的規格,再用 No-code、n8n 低程式碼與程式碼三條路各做一次同一個 harness,最後處理可靠度——重試、錯誤處理、人工覆核。7 章 54 課,含常見坑與排錯、Capstone 實作,附學員講義 PDF。
NT$ 5,999

