2026年3月20日5 分鐘閱讀ai-cli-tools

20 分鐘用 Claude Code 建一個 MCP Server

手把手教學:用 TypeScript 建一個能用的 MCP server,接上 Claude Code,實際呼叫 tool 測試。

DH
Danny Huang

你會做出什麼

這篇教學結束後,你會有一個能運作的 MCP server,可以即時查詢 GitHub 使用者資料。Claude Code 會直接從終端呼叫它。你輸入「查一下 torvalds 的 GitHub 資料」,Claude Code 就會調用你的 server、打 GitHub API、回傳結構化的結果。

MCP (Model Context Protocol) 是一個開放標準,讓 AI agent 透過統一介面存取外部工具和資料。你不需要把 API 呼叫寫死在 prompt 裡,而是建一個 server 來公開 tool。任何支援 MCP 的 client(Claude Code、Claude Desktop、Cursor 等)都能自動發現並呼叫這些 tool。

這篇教學做的是真的東西。不是 hello-world echo server,是一個會打真實 API 回傳有用資料的 tool。

你需要:

  • Node.js 18+ 和 npm
  • 已安裝並驗證的 Claude Code
  • 一個終端
  • 20 分鐘

為什麼要自己建 MCP Server?

你可能已經在用 MCP server 了,只是沒意識到。Claude Code 內建的檔案讀取、網頁搜尋、bash 執行,底層都是 MCP tool。

自己建 server 代表你可以讓 Claude Code 存取任何東西:公司內部 API、資料庫、監控儀表板、部署管線。Protocol 處理發現、schema 驗證和傳輸。你只寫邏輯。

三個理由讓你選擇自己建:

  1. 自訂資料來源。 把 Claude Code 接上還沒有現成 MCP server 的 API。
  2. 控制存取。 你的 server 決定公開哪些資料、怎麼格式化。邊界你來定。
  3. 重複使用。 一個 server 在所有 MCP client 上都能用。建一次,到處用。

想了解 MCP 在整個 AI CLI 工具生態裡的位置,那篇指南有完整的全景。

第一步:建立專案

建一個新目錄並初始化:

mkdir github-mcp-server
cd github-mcp-server
npm init -y

安裝 MCP SDK 和 Zod(用來做 schema 驗證):

npm install @modelcontextprotocol/sdk zod@3
npm install -D @types/node typescript

更新 package.json,啟用 ES modules 並加入 build script:

{
  "type": "module",
  "bin": {
    "github-mcp": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && chmod 755 build/index.js"
  },
  "files": ["build"]
}

建立 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

建立原始碼目錄:

mkdir src

專案結構:

github-mcp-server/
├── package.json
├── tsconfig.json
└── src/
    └── index.ts       # 接下來要寫的檔案

第二步:撰寫 Server

建立 src/index.ts。這是整個 server。每一行都有行內說明。

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// 建立 MCP server 實例。
// name 和 version 會在 client 探索 server 時顯示。
const server = new McpServer({
  name: "github-lookup",
  version: "1.0.0",
});

// 定義 GitHub API 回應的 TypeScript 介面。
// 只取我們需要的欄位。
interface GitHubUser {
  login: string;
  name: string | null;
  bio: string | null;
  public_repos: number;
  followers: number;
  following: number;
  html_url: string;
  created_at: string;
}

// 註冊一個叫 "get_github_user" 的 tool。
// 第一個參數是 tool 名稱。
// 第二個參數是 metadata:description + input schema。
// 第三個參數是 handler function。
server.registerTool(
  "get_github_user",
  {
    description: "Look up a GitHub user profile by username",
    inputSchema: {
      username: z
        .string()
        .min(1)
        .describe("GitHub username (e.g. torvalds, sindresorhus)"),
    },
  },
  async ({ username }) => {
    const url = `https://api.github.com/users/${encodeURIComponent(username)}`;

    try {
      const response = await fetch(url, {
        headers: {
          "User-Agent": "github-mcp-server/1.0",
          Accept: "application/vnd.github.v3+json",
        },
      });

      if (!response.ok) {
        return {
          content: [
            {
              type: "text" as const,
              text: `GitHub API returned ${response.status}: user "${username}" not found or rate-limited.`,
            },
          ],
        };
      }

      const user = (await response.json()) as GitHubUser;

      const summary = [
        `**${user.name || user.login}** (@${user.login})`,
        user.bio ? `Bio: ${user.bio}` : null,
        `Public repos: ${user.public_repos}`,
        `Followers: ${user.followers} | Following: ${user.following}`,
        `Profile: ${user.html_url}`,
        `Account created: ${user.created_at.split("T")[0]}`,
      ]
        .filter(Boolean)
        .join("\n");

      return {
        content: [{ type: "text" as const, text: summary }],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text" as const,
            text: `Failed to reach GitHub API: ${error instanceof Error ? error.message : "unknown error"}`,
          },
        ],
      };
    }
  },
);

// 把 server 接上 stdio transport。
// Claude Code 透過 stdin/stdout 跟 MCP server 溝通。
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("GitHub MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

重點說明:

  • server.registerTool 接收名稱、設定物件(含 descriptioninputSchema)、以及一個 async handler。inputSchema 直接使用 Zod 物件。SDK 會在 handler 執行前自動驗證輸入。
  • 回傳格式一律是 { content: [{ type: "text", text: "..." }] }。這是 MCP 標準的回應結構。
  • console.error 而非 console.log。基於 stdio 的 MCP server 用 stdout 傳送 JSON-RPC 訊息。任何 console.log 都會破壞 protocol。除錯訊息一律用 console.error

第三步:Build 並在本機測試

編譯 TypeScript:

npm run build

你應該會看到 build/index.js 檔案。測試它能不能正常啟動:

node build/index.js

Process 會停住(等待 stdin 輸入)。這是正確的。按 Ctrl+C 停止。

在接上 Claude Code 之前,用 MCP Inspector 做互動式測試:

npx @modelcontextprotocol/inspector node build/index.js

這會在 http://localhost:6274 開一個 web UI。你可以選 get_github_user tool、輸入使用者名稱、看原始回應。Inspector 是在整合到任何 client 之前驗證 tool 運作最快的方法。

第四步:接上 Claude Code

Claude Code 從專案根目錄的 .mcp.json 讀取 MCP server 設定。在你的工作專案(不是 MCP server 目錄裡,而是你用 Claude Code 的那個專案)建立這個檔案:

{
  "mcpServers": {
    "github-lookup": {
      "command": "node",
      "args": ["/absolute/path/to/github-mcp-server/build/index.js"]
    }
  }
}

/absolute/path/to/ 換成實際路徑。在 github-mcp-server 目錄裡執行 pwd 就能拿到。

或者用 Claude Code CLI 加入,讓它在所有專案都可用:

claude mcp add github-lookup --scope user -- node /absolute/path/to/github-mcp-server/build/index.js

加完設定後重啟 Claude Code。在 Claude Code 裡執行 /mcp 確認 server 已連線。你應該會看到 github-lookup 顯示綠色狀態。

第五步:實際使用

在設定好 server 的任何專案裡打開 Claude Code,輸入:

查一下 torvalds 的 GitHub 資料

Claude Code 會:

  1. 辨識 get_github_user 是適合這個請求的 tool。
  2. 詢問你是否同意呼叫(只有第一次,除非你啟用了自動核准)。
  3. {"username": "torvalds"} 傳給你的 server。
  4. 顯示格式化後的結果。

再試幾個:

sindresorhus 的 GitHub 帳號有什麼專案?
比較 gaearon 和 dan-abramov 的 GitHub 資料

Claude Code 讀取 tool 的 description 和 input schema 來決定何時、如何呼叫。你不需要寫任何 prompt engineering。MCP protocol 處理了所有接線。

疑難排解

/mcp 輸出裡看不到 server。 .mcp.json 裡的路徑不對。用絕對路徑。手動執行 node /your/path/build/index.js 確認它能啟動。

Server 啟動了但 tool 沒出現。 重新執行 npm run build。Claude Code 跑的是編譯後的 JavaScript,不是 TypeScript 原始碼。改了 src/index.ts 就必須重新 build。

GitHub 回傳 "Rate limit exceeded"。 GitHub API 未驗證的請求每小時限 60 次。要更高額度,加一個 GITHUB_TOKEN 環境變數,在 fetch headers 裡用 Bearer token。你可以在 .mcp.json 裡傳環境變數給 MCP server:

{
  "mcpServers": {
    "github-lookup": {
      "command": "node",
      "args": ["/absolute/path/to/github-mcp-server/build/index.js"],
      "env": {
        "GITHUB_TOKEN": "ghp_your_token_here"
      }
    }
  }
}

Server 無聲地 crash。 檢查 stderr 輸出。手動跑 server:node build/index.js 2>&1,看有沒有錯誤訊息。常見原因:缺少 node_modules(跑 npm install)、Node.js 版本太舊(需要 18+)、build 輸出有語法錯誤。

Claude Code 出現 JSON parse 錯誤。 你的 server 程式碼裡有 console.log。把每個 console.log 都換成 console.error。Stdout 是 MCP protocol 訊息專用的。

開發 MCP Server 的多面板工作流

在開發 MCP server 的時候,你同時要處理三件事:編輯 src/index.ts、執行 npm run build 並重啟 server、用 Claude Code 或 MCP Inspector 測試。用 Ctrl+TabAlt+Tab 在這些之間切換很慢,而且會打斷專注。

有效率的做法是三個終端面板同時可見。左邊放編輯器,右上放 build 和 server log,右下放 Claude Code 或 Inspector。存檔、看 build 輸出、測試 tool。不切視窗。不丟失脈絡。

Try Termdock Multi Terminal Layout works out of the box. Free download →

下一步

你已經有一個能運作的 MCP server。接下來可以往這些方向走。

加更多 tool。 再呼叫一次 server.registerTool,給不同的名稱和 schema。一個 server 可以公開數十個 tool。考慮加入 get_github_repo(儲存庫詳情)、list_github_repos(列出使用者的儲存庫)、或 search_github_code(跨儲存庫搜尋程式碼)。

加 resource。 MCP server 還能公開 resource(唯讀資料,像檔案或 API 回應)和 prompt(預先寫好的範本)。SDK 三種都支援。詳情請參考 MCP 規格文件

發布分享。 如果你的 server 對別人有用,發到 npm 上。寫清楚 README,附上 .mcp.json 設定片段。MCP 生態正在快速成長,最好的貢獻方式就是為還沒有 server 的 API 建一個。

探索生態系。MCP servers 儲存庫瀏覽現有的 server 找靈感。如果你正在建構自己的 AI CLI 工作流,MCP server 就是讓 agent 真正好用的可組合元件。

更深入的 Claude Code 設定模式,包括 CLAUDE.md 常見錯誤進階 agent skill,那些指南從這篇教學結束的地方接續。

DH
Free Download

Ready to streamline your terminal workflow?

Multi-terminal drag-and-drop layout, workspace Git sync, built-in AI integration, AST code analysis — all in one app.

Download Termdock →
#mcp#model-context-protocol#claude-code#tutorial#ai-cli#server

相關文章