最小可行性 MCP Server (Code Walkthrough)
最小可行性 MCP Server (Code Walkthrough)
這段程式碼展示了如何使用 TypeScript/JavaScript 建立一個 最小可行性的 Model Context Protocol (MCP) Server。
它的核心功能是:建立一個伺服器,透過標準輸入/輸出 (Stdio) 與 MCP Client(例如 Claude Desktop, Cursor, 或其他 AI 代理)溝通,並提供一個名為 add 的工具讓 AI 調用。
以下是詳細的逐段解釋:
1. 引入必要的模組
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as z from "zod";
-
McpServer: 這是 MCP SDK 的高階類別,用來簡化伺服器的建立。它幫你處理了底層的協議握手 (Handshake)、能力協商 (Capabilities) 等細節。 -
StdioServerTransport: 這是「傳輸層」。MCP 協議可以透過 HTTP (SSE) 跑,也可以透過 Stdio 跑。- 重要概念:在 Stdio 模式下,Server 與 Client 透過
stdin(接收指令) 和stdout(傳送回應) 溝通。
- 重要概念:在 Stdio 模式下,Server 與 Client 透過
-
z(Zod): 用於定義資料結構驗證 Schema。這在 MCP 中非常重要,因為 LLM (AI) 需要精確知道工具需要什麼格式的參數 (JSON Schema),Zod 是定義這些規則的標準方式。
2. 初始化伺服器實例
const server = new McpServer({ name: "minimal-mcp", version: "1.0.0" });
- 建立一個伺服器實例,並給予它一個識別名稱 (
name) 和版本號 (version)。這些資訊會在連線初期傳送給 Client,讓 AI 知道它正在跟誰溝通。
3. 註冊工具 (Register Tool)
這是整段程式碼的靈魂,定義了 AI 可以使用的「技能」。
server.registerTool(
"add", // 工具名稱 (Name)
{
title: "Add", // 人類可讀的標題
description: "Add two numbers", // 給 AI 看的描述 (Prompt),讓 AI 知道何時該用這個工具
inputSchema: { a: z.number(), b: z.number() }, // 輸入參數定義
outputSchema: { result: z.number() } // 輸出格式定義 (選填,但在高階應用中有助於型別安全)
},
// 工具的執行邏輯 (Handler)
async ({ a, b }) => {
const output = { result: a + b };
return {
content: [{ type: "text", text: JSON.stringify(output) }],
structuredContent: output // 回傳結構化資料
};
}
);
inputSchema: 這裡定義了 AI 必須 傳入兩個數字a和b。如果 AI 傳入字串,Zod 會擋下來並報錯。- Handler 函數:
- 它接收解構後的參數
{ a, b }。 - 執行邏輯:
a + b。 - Return (回傳值): MCP 協議規定回傳必須包含
content陣列。type: "text": 這是最通用的回傳,AI 會閱讀這段 JSON 字串來得知結果。structuredContent: 這通常用於讓 Client 端程式能直接解析結果,而不需再次 parse JSON 字串。
- 它接收解構後的參數
4. 連接與啟動
const transport = new StdioServerTransport();
await server.connect(transport);
- 實例化
StdioServerTransport。 server.connect(transport):將邏輯層 (McpServer) 與傳輸層綁定。這一步之後,Server 就開始監聽stdin的訊息了。
5. 記錄日誌 (Logging) 的關鍵細節
console.error("minimal-mcp running (stdio)");
- 為什麼用
console.error而不是console.log?- 這是開發 MCP Stdio Server 最重要 的一點。
- 因為
stdout(標準輸出) 被用於傳輸 JSON-RPC 協議的通訊數據。如果你用console.log印出除錯訊息(例如 "Server started"),這些文字會混入 JSON 數據流中,導致 Client 解析失敗 (JSON Parse Error) 而斷線。 stderr(標準錯誤輸出) 是安全的,Client 通常會將其顯示為 Logs 或除錯訊息,而不會干擾協議通訊。
總結
這段程式碼是一個標準的 Local MCP Server 樣板。當你將它配置在 Claude Desktop 或 Cursor 中時,流程如下:
- Client 啟動這個 script (透過
node script.js或bun run script.ts)。 - Client 透過
stdin發送 "ListTools" 請求。 - Server 回傳 JSON,告訴 Client:「我有個
add工具,需要a和b」。 - 使用者問 AI:「100 加 200 是多少?」
- AI 決定呼叫
add工具,Client 發送 JSON-RPC Call。 - Server 計算
300並回傳。 - AI 收到結果,回答使用者:「答案是 300」。
- ← Previous
JSON-RPC:程式界的掛號信 (The Core Protocol) - Next →
Node.js vs Bun:MCP 專案比較