iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
Claude AI

跟著 Claude Academy,重新認識 Claude系列 第 25 篇

Building with the Claude API(6/7):Tools, Resources & Prompts

  • 分享至 

  • xImage
  •  

MCP 怎麼把 Claude 接到外部工具和資料?它用 tools、resources、prompts
三種原語,搭配 MCP client 與 server 把整合責任拆開;本篇涵蓋 Model Context
Protocol 的第 47–56 堂與 Quiz 6,實作以 Python SDK 和本機 stdio
為主,資源注入的錯誤處理仍有明確邊界。

前幾篇把 Claude API 的工具使用、RAG 和提示快取一路接起來。到了 MCP,問題換成了另一個角度:如果每一個外部服務都要自己寫 schema、函式和維護流程,應用程式很快就會長成一團整合程式碼,這些工具能不能被整理成可重複使用的介面?

這次打開 Claude Academy 的 Building with the Claude API,也回到 Claude Platform 對照 API 的位置。本篇涵蓋官方 Model Context Protocol 的第 47–56 堂,共 10 堂課與 Quiz 6;課程用同一個 CLI 聊天機器人專案,同時建立 MCP server 和 client,走過 tools、resources、prompts 三條路徑。

章節課程地圖

Model Context Protocol 的第 47–56 堂,沿著同一個 CLI 聊天機器人專案,同時修改 mcp_server.py、mcp_client.py 和 main.py,把三種 MCP primitives 逐步接起來。

課程 加進 mcp_server.py 加進 mcp_client.py/main.py
47–49 MCP 架構、server/client 分工與專案骨架 CLI 聊天機器人與連線準備
50 docs 字典、read_doc_contents、edit_document 兩個工具 —
51 不改程式碼,使用 mcp dev 測試工具 —
52 — MCPClient、list_tools()、call_tool(),接上 tool-use 迴圈
53 list_docs、fetch_doc 兩個 resources —
54 — read_resource(),加入 @文件名 自動帶入文件內容
55 format prompt:預寫的 Markdown 格式化指令 —
56 — list_prompts()、get_prompt() 與斜線指令選單

Model Context Protocol

47. Introducing MCP(介紹 MCP)

MCP(Model Context Protocol)是一層通訊協定,讓 Claude 取得外部工具與上下文。它處理的重點,是把工具的定義與執行責任交給專門的 MCP server,應用程式可以透過 client 使用這些能力。

官方用 GitHub 做了一個很容易理解的例子:使用者想問「我所有 repository 裡有哪些開放中的 pull request?」。如果每一項 GitHub 功能都直接塞進聊天應用程式,儲存庫、PR、issue、project 都要各自寫 schema、函式、測試和維護邏輯。MCP 把這些能力放在專用 server 裡,應用程式透過標準介面使用它們。

課程把三個常見問題整理成這張表:

問題 課程中的回答
誰寫 MCP server? 任何人都可以,通常由服務提供商製作官方實作,例如 AWS 為自己的服務發布 MCP server
跟直接呼叫 API 有什麼差別? MCP server 已經定義好 schema 與函式;直接呼叫 API 時,這些定義由你的應用程式負責
MCP 和 tool use 是同一件事嗎? 兩者互補;MCP 關注的是工具由誰建立、維護與提供

我會把它記成「工具的供應鏈重組」:Claude 仍然可以提出工具請求,但工具怎麼被定義、放在哪裡執行,以及由誰長期維護,被拆到另一個可以獨立演進的 server。

48. MCP clients(MCP 客戶端)

MCP client 是你的應用程式和 MCP server 之間的橋。它負責處理訊息傳遞與協定細節,讓主程式不必自己拼裝每個外部服務的通訊格式。

MCP 的傳輸層與應用程式邏輯分開。client 和 server 可以透過不同方式溝通;最常見的設定是兩者在同一台機器上,透過標準輸入/輸出(stdio)通訊,也可以使用 HTTP、WebSockets 或其他網路協定。

最基本的兩組訊息是:

訊息 作用
ListToolsRequest / ListToolsResult client 詢問 server 提供哪些工具,取得工具清單
CallToolRequest / CallToolResult client 要求 server 用指定參數執行某個工具,取得執行結果

完整流程可以先畫成這樣:

使用者 → 你的伺服器 → MCP client → MCP server(真的打 GitHub)→ 原路送回
            ↑
        Claude 只跟你的伺服器講話

假設使用者問「我有哪些 repository?」:你的伺服器先從 MCP client 取得工具清單,再把問題和工具送給 Claude。Claude 判斷要用哪個工具後,你的伺服器把請求交給 client,client 再交給 MCP server。GitHub 的結果沿著原路回來,最後由你的伺服器把結果交給 Claude 整理。

這張圖的重點是責任邊界:Claude 不直接碰 GitHub;它只和你的伺服器溝通,伺服器再透過 MCP client 使用外部能力。

49. Project setup(專案設定)

課程接著建立一個 CLI 聊天機器人,讓使用者透過命令列和一組文件互動。專案裡有兩個元件:處理使用者互動的 MCP client,以及管理文件操作的自訂 MCP server。文件先放在記憶體裡,不使用資料庫。

這個範例同時實作 client 和 server,主要是為了把完整流程走過一次。實際專案通常會依角色選擇其中一邊:做 server,是把自己的服務公開給其他開發者;做 client,則是連到已經存在的 MCP server。

設定完成後,課程提供兩種啟動方式:

uv run main.py      # 建議
python main.py      # 標準 Python

這堂最值得留下來的習慣,出現在真正開始加工具之前。先問一個你能立刻驗證的問題,例如「1+1 等於多少?」確認回覆確實是 2,再問「文件裡寫了什麼?」。第二個問題在文件工具還沒建立以前,不可能真的讀到文件;模型回什麼,都是之後加入工具前可以對照的基準。

這個步驟很小,卻把驗證成本和出錯代價接在一起:如果金鑰、相依套件或聊天迴圈一開始就有問題,後面每一堂都會被錯誤拖著走。先用已知答案建立基準,後續每加一層就有東西可以比較。

50. Defining tools with MCP(使用 MCP 定義工具)

Python SDK 把定義工具的工作縮短到 decorator、type hints 和 Field。你不必手寫完整的 JSON schema,SDK 會從函式簽名和參數描述產生 Claude 需要的結構。

初始化 MCP server:

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("DocumentMCP", log_level="ERROR")

文件先用一個 dictionary 放在記憶體裡,key 是文件 ID,value 是文件內容。

讀取工具的定義如下:

@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]

另一個 edit_document 工具則要求 doc_id、要尋找的文字,以及要替換成的新文字;完整實作可以直接看 第 50 堂的 commit。

這段設計有三個實用細節:函式名稱和 description 讓 Claude 知道工具用途,Field 描述參數的意義,ValueError 則把「找不到文件」這件事變成模型可以理解的錯誤訊息。相較於只回傳一個模糊的 server error,具體錯誤讓 Claude 有機會修正下一次呼叫的參數。

51. The server inspector(伺服器檢查工具)

MCP server 還沒接進完整應用程式前,可以先用 Python MCP SDK 內建的瀏覽器版 inspector 單獨測試:

mcp dev mcp_server.py

它會啟動一個開發伺服器,預設使用 port 6277,並提供本機 URL 開啟 MCP Inspector。操作流程是先按 Connect 啟動 server,再到 Tools → List Tools,選取工具、填入參數,最後按 Run Tool 看回傳結果。

Inspector 也能把操作串起來驗證:先用 edit_document 修改文件,再用 read_doc_contents 讀回來,確認兩個工具之間真的共享同一份資料。

官方提醒 Inspector 仍在積極開發,畫面可能和課程截圖不同。這個提醒很重要:可依賴的是它提供獨立測試 tools、resources、prompts 的能力,畫面配置和細節則要以目前版本為準。

52. Implementing a client(實作客戶端)

MCP client 可以拆成兩層:

元件 來源
MCP Client 自己建立的類別,讓 session 的使用方式更容易管理
Client Session MCP Python SDK 提供的實際連線

多包一層自訂類別的理由,是把 session 的資源清理集中管理。CLI 對 MCP server 的核心工作只有兩件事:取得工具清單,以及在 Claude 要求時呼叫工具。

async def list_tools(self) -> list[types.Tool]:
    result = await self.session().list_tools()
    return result.tools

async def call_tool(self, tool_name: str, tool_input: dict) -> types.CallToolResult | None:
    return await self.session().call_tool(tool_name, tool_input)

把完整流程接起來後,資料會依序經過:取工具清單、把使用者問題送給 Claude、Claude 決定呼叫 read_doc_contents、client 執行工具、結果回到 Claude,最後才產生回覆。這個 client 的程式碼比我原本預期的小:兩個 async 方法,加上一個負責清理的 context manager,連線與協定的複雜度大多由 SDK 處理。

53. Defining resources(定義資源)

工具適合執行動作;資源則適合向 client 公開資料。課程用 HTTP 的 GET handler 來類比 resources:你想取得資訊時讀取資源,需要改變狀態時才呼叫工具。

文件提及功能就是一個好例子。使用者輸入 @ 時,需要先拿到所有可用文件供自動完成;選定文件後,再依 URI 取得特定文件內容。資源的 URI 就像資料的位址。

MCP 有兩種資源:

類型 特徵 例子
直接資源 固定、不帶參數的 URI docs://documents
範本化資源 URI 裡帶參數 docs://documents/{doc_id}

Python SDK 會從範本化 URI 解析參數,再以關鍵字引數傳給函式:

@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 是給 client 的提示,告訴它回傳內容應該如何理解。SDK 會依回傳型別自動序列化,程式碼不用自己把資料轉成 JSON 字串。

54. Accessing resources(存取資源)

client 這一側要加上 read_resource:

import json
from pydantic import AnyUrl

async def read_resource(self, uri: str) -> Any:
    result = await self.session().read_resource(AnyUrl(uri))
    resource = result.contents[0]

回應裡有一個 contents 清單,通常先取第一個元素,再依 MIME type 決定解析方式:

if isinstance(resource, types.TextResourceContents):
    if resource.mimeType == "application/json":
        return json.loads(resource.text)
    return resource.text

使用者輸入 @report.pdf 這份文件在講什麼? 時,client 會先完成文件選擇,再把資源內容放進送給 Claude 的提示。這和讓 Claude 另外呼叫一個讀檔工具的流程不同:文件已經在提示裡,模型收到上下文後就能直接回答。

一次提及多份文件時,也能把比較資料一併注入:

You: @report.pdf 這份文件在講什麼?
     @financials.docx 跟 @outlook.pdf 有什麼差異?

多份文件注入後,文件內容會直接進入提示,Claude 不需要再透過另一輪工具呼叫取回它們。

55. Defining prompts(定義提示)

MCP 的 prompts 是 server 作者預先寫好、測試過的高品質指令。使用者仍然可以自己輸入「把 report.pdf 轉成 Markdown」,但如果 server 已經把格式、結構和輸出要求整理成一份可重複呼叫的 prompt,結果比較容易維持一致。

這個 prompt 會回傳一組 user/assistant 訊息,client 取得後可以直接送給 Claude。完整的 format prompt 與 edit_document 呼叫方式放在 第 55 堂的 commit,文章保留它的設計重點就好:資源提供內容,prompt 提供經過測試的任務指令,工具負責真的修改文件。

這段的關鍵在於 prompt 會明確告訴 Claude 下一步可以使用 edit_document。提示、工具和資源因此形成一組設計。

Inspector 的 Prompts 區段可以選擇 prompt、填入參數,直接查看展開後要送給 Claude 的訊息。測試不同輸入時,這個畫面能確認變數真的插入指令,也能確認 prompt 和 server 的工具是否配得起來。

56. Prompts in the client(客戶端中的提示)

client 需要實作兩個方法,分別列出可用的 prompts,以及依名稱和參數取得某一個 prompt:

async def list_prompts(self) -> list[types.Prompt]:
    result = await self.session().list_prompts()
    return result.prompts

async def get_prompt(self, prompt_name, args: dict[str, str]):
    result = await self.session().get_prompt(prompt_name, args)
    return result.messages

伺服器端 prompt 函式的參數,會對應到 client 呼叫 get_prompt 時傳入的 dictionary key。CLI 裡的使用者體驗則像 slash command:輸入斜線,看到可用指令,選取 prompt,再填入文件 ID,完整 prompt 就會送給 Claude。

以 /format financials.docx 為例,使用者只選一個指令和文件,剩下的工具順序由 Claude 依 prompt 內容完成。

走到這裡,我會把 /指令 記成 get_prompt:server 把一段測過的對話開場白交給 client,Claude 再從這個起點繼續使用工具。

Course Quiz 6(課程測驗)

有 6 題。官方頁面本身是繁中,以下保留實際題目與正確答案。

1. 您想為您的 MCP 伺服器建立一個讀取文件內容的工具。使用 Python SDK,定義此工具最簡單的方法是什麼?

答案:在函式上使用 @mcp.tool 裝飾器

2. 您正在建立一個文件系統,使用者可以輸入 @document_name 來引用檔案。哪個 MCP 功能最適合用於公開文件內容?

答案:Resources

3. 您想為使用者提供一個高品質、經過預先測試的文件格式化指令。您應該使用哪個 MCP 功能?

答案:Prompts

4. 您正在建立一個需要存取 GitHub 資料的聊天機器人。使用 MCP 而不是自行編寫 GitHub 整合的主要好處是什麼?

答案:MCP 會為您處理工具定義和執行

5. 您已經建立了一個 MCP 伺服器,並想在將工具連接到 Claude 之前測試您的工具。最好的測試方式是什麼?

答案:在瀏覽器中使用 MCP Inspector

6. 您的 MCP 伺服器和客戶端需要進行通訊。在開發過程中,它們最常見的連接方式是什麼?

答案:透過同一台機器上的標準輸入/輸出

實作地圖:每堂課一個 commit

完整程式碼放在 claude-academy-api-app。

課程 主題 實作內容 Commit
47 Introducing MCP MCP 架構與工具供應鏈概念 —
48 MCP clients client、server 與訊息流程 —
49 Project setup 建立 CLI 聊天機器人骨架(REPL 迴圈) df2b5c7
50 Defining tools with MCP docs dictionary,以及 read_doc_contents、edit_document 兩個工具 b1b4d4a
51 The server inspector 以 mcp dev 測試第 50 堂加入的工具 —
52 Implementing a client MCPClient 類別、list_tools()、call_tool(),main.py 接上 tool-use 迴圈 2e8e584
53 Defining resources list_docs、fetch_doc,以及兩種 docs:// URI f94219c
54 Accessing resources read_resource() 與 @文件名 自動注入 c895bc0
55 Defining prompts format prompt:預寫的 Markdown 格式化指令 6fa7e9f
56 Prompts in the client list_prompts()、get_prompt() 與斜線指令選單 90a9f65

小結

這堂課最像在看一個 MCP 專案慢慢長出來:先把 client 和 server 的骨架接起來,再一個功能一個功能往上疊,從 tools 到 resources、prompts,讓我看懂 MCP 接進 CLI 之後,兩邊的訊息怎麼流動。

我覺得更值得留下來的是課程的開發順序。它採用交叉開發:先在 server 加上一個能力,再用 MCP Inspector 單獨測試,確認可以運作後,回到 client 把它串起來,接著才進下一個功能。一次只走完一條完整路徑,出了問題比較容易知道是哪一段造成的,也能理解為什麼這一堂先改 server,下一堂再回 client。

上到前面幾堂時,我一度覺得這些內容好像已經學過了。回頭和第八天的〈Model Context Protocol 簡介:從零打造 MCP 客戶端與伺服器〉比對,我粗略估計重疊程度大概有 90%。但跟著課程章節一路開發,把整個流程實際跑通,直到 client 和 server 真的串通,理解會深很多,也更有感。

所以我會建議至少跟著這門 MCP 課程完整走過一次。它適合邊看邊寫;把每一段實際接起來之後,才會比較清楚每個元件各自負責什麼。

Building with the Claude API|GitHub Source Code


我是 Jasper,從事軟體開發,目前專注打造 AI 工作流程。
官方圖解與完整表格在 Blog 版,和我一起探討更多 AI 議題 🚀


上一篇
Building with the Claude API(5/7):Claude 的功能與提示快取
系列文
跟著 Claude Academy,重新認識 Claude 共 25 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言