精華筆記

· @aihub.tw

MCP 完全指南

自己做一個簡單 Server:不用是工程師

自己做一個簡單 Server:不用是工程師

前四課你連的都是別人寫好的 server:filesystem 讓 AI 讀你的硬碟、GitHub server 讓它看你的 PR、Notion server 讓它翻你的筆記。這些工具都有人幫你做好了。但遲早你會碰到一堵牆——你最需要的資料根本沒有現成 server 可用。

公司的客戶資料在自家 Google Sheets、產品庫存表在某張 Excel、工作流記錄在公司內部系統——這些工具太私人或太小眾,世界上不會有陌生人主動幫你做 server。你的需求就你一個人需要,唯一的出路就是自己做。

聽到「自己寫 server」先別關頁面。這堂課要做的東西就幾十行 Python,而且大部分程式碼讓 Claude Code 幫你寫——你的工作是出需求、看懂輸出、測試結果。我們會完整走過「用 AI 幫 AI 做工具」這個循環,最後你手上有一個真實可用的 server:問 Claude「我的試算表裡有沒有叫王小明的客戶?」,它真的去查你的 Google Sheets 再回答你。

這堂課適合誰 適合:想讓 AI 連上自家私有資料(Google Sheets、內部 API 等),找不到現成 server 的人(本課程屬進階應用專區)。需要基礎:會用終端機跑指令、聽說過 Python 但不用會寫。前置課:第 2 課(裝過第一個 server)、第 4 課(懂基本安全概念)。

這堂學什麼

  • MCP server 裡面有什麼:三個核心組成(工具定義、處理函式、server 啟動器)
  • FastMCP 是什麼,為什麼它是 2026 年最快的 server 起點
  • 用 Claude Code 幫你寫 MCP server 的完整 prompt 策略
  • 實戰:做一個 Google Sheets 查詢 server,包含 API 憑證設定全流程
  • 把自製 server 掛進 Claude Desktop 並真正用上它

觀念一:MCP Server 裡面到底有什麼

在動手之前,先搞清楚你要做的東西長什麼樣。一個 MCP server 本質上就三個部分:

MCP server 三層解剖圖:工具定義、處理函式、Server 啟動器

工具定義:告訴 AI 這個工具叫什麼、接受哪些參數、會做什麼事。AI 靠這段描述決定要不要呼叫這個工具——說明文字寫得愈清楚,AI 愈知道什麼時候該用它。這是你能掌控 AI 行為最重要的一個地方。

處理函式:工具被呼叫時實際執行的 Python 程式碼。查試算表、呼叫 API、讀檔案——業務邏輯都在這裡。Claude Code 最擅長的部分也是這裡,你只需要說清楚需求。

Server 啟動器:把所有工具包成符合 MCP 協定的 server 格式對外提供。過去你要自己實作 JSON-RPC 通訊協定,現在 FastMCP 一行搞定。

三個部分在程式碼裡的整體結構:

from fastmcp import FastMCP

mcp = FastMCP("我的工具名稱")     # 建立 server 實例

@mcp.tool()                        # 裝飾器:把這個函式變成 MCP 工具
def 工具名稱(參數: 型別) -> str:
    """工具說明文字:AI 靠這段決定什麼時候呼叫你"""
    # 實際邏輯在這裡
    return 結果字串

if __name__ == "__main__":
    mcp.run()                      # 啟動 server

就這樣。@mcp.tool() 這個裝飾器是整個框架最核心的一行:只要加上它,FastMCP 就自動把這個函式包成符合 MCP 協定的工具,不用管底層的序列化、通訊、錯誤處理。

觀念二:FastMCP 是現在最快的起點

你有兩種方式寫 MCP server:用 Anthropic 官方的原始 SDK,或用 FastMCP。

FastMCP 與原始 MCP SDK 程式碼行數對比:原始 SDK 約 80 行大多是樣板,FastMCP 5 行就有可用工具

原始 SDK 要你手動處理 JSON-RPC 通訊、定義 Tool schema、處理各種事件——沒有大量樣板程式碼根本跑不起一個空 server。FastMCP 把這些全包了,你只需要寫實際業務邏輯。

數字佐證:截至 2026 年,FastMCP 在所有語言的 MCP server 中佔約 70% 的使用率,Anthropic 官方 Python SDK 也已直接採納 FastMCP 作為推薦的高層框架。你在 registry 裡看到的 Python server,十個裡有七個背後用的是 FastMCP 或它的衍生版本。

uv:FastMCP 最推薦的執行方式 這堂課使用 `uv` 作為套件管理工具。`uv` 可以讓 Claude Desktop 在啟動 server 時自動下載並管理所需套件,不需要你事先手動 pip install——設定檔裡寫好依賴,第一次啟動時 uv 自己搞定。macOS 安裝:`brew install uv`。

觀念三:讓 AI 幫 AI 寫工具

「用 Claude Code 幫你寫 MCP server」乍聽有點 meta——你用 AI 工具做出一個讓 AI 更強的工具。但這正是這堂課最值錢的心得:

你不需要會寫 Python,你只需要會描述需求。

Claude Code 理解 FastMCP 的框架規範。你只要告訴它:這個 server 要做什麼事、工具要接受哪些輸入、工具要回傳什麼結果——它就能寫出一份功能完整的 server。你的角色從「工程師」變成「產品經理」:描述需求、驗收結果、提出修改。手把手實戰就走這個流程,而不是教你一行行打 Python。

手把手實戰

這堂課的目標:做一個能查你自己 Google Sheets 的 MCP server。它會有兩個工具:read_sheet(讀整張工作表)和 search_sheet(搜尋關鍵字)。

安裝 uv,建立專案資料夾

先確認 uv 已安裝:

uv --version

沒有的話用 Homebrew 安裝(macOS):

brew install uv

Windows 用 winget:

winget install --id=astral-sh.uv -e

建立專案資料夾:

mkdir ~/my-sheets-server
cd ~/my-sheets-server

這個資料夾就是你 server 的家。server.py 放在這裡,之後設定 Claude Desktop 也要用這個路徑。

用 Claude Code 寫 server 程式碼

~/my-sheets-server 資料夾裡打開 Claude Code,直接說:

幫我用 FastMCP 寫一個 MCP server,功能是查詢 Google Sheets。

需求:
1. 使用 gspread 和 google-auth 套件存取 Google Sheets
2. 從環境變數讀取設定:
   - GOOGLE_CREDENTIALS_PATH:服務帳戶 JSON 憑證的完整路徑
   - DEFAULT_SPREADSHEET_ID:預設試算表 ID(可在呼叫工具時覆寫)
3. 提供兩個工具:
   - read_sheet(sheet_name, spreadsheet_id):讀取整張工作表,回傳格式化的純文字表格
   - search_sheet(keyword, sheet_name, spreadsheet_id):搜尋包含關鍵字的列,不分大小寫
4. 兩個工具的 docstring 都要清楚說明:用途、適合的使用情境、參數說明、回傳格式
5. spreadsheet_id 省略時用 DEFAULT_SPREADSHEET_ID 環境變數;sheet_name 預設值「工作表1」
6. 錯誤處理:試算表找不到、無權限、網路錯誤都要回傳繁體中文錯誤訊息
7. 程式碼寫在 server.py

Claude Code 跑完後,你會拿到一份 server.py。以下是它應該長得像的樣子,方便你對照驗收:

import os
from fastmcp import FastMCP
import gspread
from google.oauth2.service_account import Credentials

mcp = FastMCP("Google Sheets 查詢助理")

SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"]


def _get_client():
    """建立 gspread 客戶端,從環境變數取得憑證路徑"""
    credentials_path = os.environ.get("GOOGLE_CREDENTIALS_PATH")
    if not credentials_path:
        raise ValueError("請設定環境變數 GOOGLE_CREDENTIALS_PATH")
    creds = Credentials.from_service_account_file(credentials_path, scopes=SCOPES)
    return gspread.authorize(creds)


def _get_records(spreadsheet_id: str, sheet_name: str) -> list[dict]:
    """取得指定工作表的所有資料列"""
    gc = _get_client()
    sh = gc.open_by_key(spreadsheet_id)
    worksheet = sh.worksheet(sheet_name)
    return worksheet.get_all_records()


def _format_table(records: list[dict]) -> str:
    """把 records 格式化成易讀的純文字表格"""
    if not records:
        return "(工作表是空的)"
    headers = list(records[0].keys())
    separator = "-" * (sum(len(h) for h in headers) + len(headers) * 3)
    rows = [" | ".join(headers), separator]
    for record in records:
        rows.append(" | ".join(str(record.get(h, "")) for h in headers))
    return "\n".join(rows)


@mcp.tool()
def read_sheet(
    sheet_name: str = "工作表1",
    spreadsheet_id: str = "",
) -> str:
    """
    讀取 Google Sheets 工作表的全部資料,回傳格式化的表格文字。
    適合使用情境:「列出所有客戶」、「顯示完整庫存表」、「我的試算表裡有什麼資料」。
    如果使用者問試算表的整體內容,優先使用這個工具。

    Args:
        sheet_name: 工作表分頁的名稱,預設「工作表1」
        spreadsheet_id: 試算表 ID(網址 /d/ 後面那串),省略時使用環境變數預設值
    Returns:
        包含欄位標題與所有資料列的純文字表格,第一行是「共 N 筆資料」
    """
    sid = spreadsheet_id or os.environ.get("DEFAULT_SPREADSHEET_ID", "")
    if not sid:
        return "錯誤:請提供試算表 ID 或設定 DEFAULT_SPREADSHEET_ID 環境變數"
    try:
        records = _get_records(sid, sheet_name)
        return f"共 {len(records)} 筆資料:\n{_format_table(records)}"
    except gspread.exceptions.SpreadsheetNotFound:
        return f"錯誤:找不到試算表 {sid},請確認 ID 是否正確且服務帳戶 email 已加入試算表共用名單"
    except gspread.exceptions.WorksheetNotFound:
        return f"錯誤:找不到工作表「{sheet_name}」,請確認分頁名稱是否正確"
    except Exception as e:
        return f"讀取失敗:{str(e)}"


@mcp.tool()
def search_sheet(
    keyword: str,
    sheet_name: str = "工作表1",
    spreadsheet_id: str = "",
) -> str:
    """
    在 Google Sheets 工作表中搜尋包含關鍵字的資料列,不分大小寫。
    適合使用情境:「找王小明的訂單」、「有沒有蘋果口味的商品」、「搜尋包含 XXX 的紀錄」。
    當使用者要查找特定資料時使用,不需要看全表的情況比 read_sheet 更精準。

    Args:
        keyword: 要搜尋的關鍵字,會比對所有欄位的值
        sheet_name: 工作表分頁的名稱,預設「工作表1」
        spreadsheet_id: 試算表 ID,省略時使用環境變數預設值
    Returns:
        符合條件的資料列表格,或「找不到」的提示訊息
    """
    sid = spreadsheet_id or os.environ.get("DEFAULT_SPREADSHEET_ID", "")
    if not sid:
        return "錯誤:請提供試算表 ID 或設定 DEFAULT_SPREADSHEET_ID 環境變數"
    try:
        records = _get_records(sid, sheet_name)
        matches = [
            r for r in records
            if any(keyword.lower() in str(v).lower() for v in r.values())
        ]
        if not matches:
            return f"在「{sheet_name}」中找不到包含「{keyword}」的資料"
        return f"找到 {len(matches)} 筆相符資料:\n{_format_table(matches)}"
    except Exception as e:
        return f"搜尋失敗:{str(e)}"


if __name__ == "__main__":
    mcp.run()

把 Claude Code 產出的版本和這份對照,確認兩個工具都在、錯誤處理有覆蓋、docstring 有說明使用情境。如果有缺,直接告訴 Claude Code 補上。

取得 Google Sheets API 憑證

這是整個流程裡設定步驟最多的一步,但只需要做一次,以後所有試算表都能用。整體分三個階段,先看全貌再動手:

Google Sheets 憑證設定三階段:建立服務帳戶、授權試算表、取得試算表 ID

在 Google Cloud Console 建立服務帳戶

  1. 前往 console.cloud.google.com,登入你的 Google 帳號
  2. 點左上角「選取專案」→「新增專案」,名字隨意(例如「MCP Tools」)
  3. 進入新專案後,左側選單:「API 和服務」→「程式庫」,搜尋 Google Sheets API,點進去按「啟用」
  4. 左側選單:「API 和服務」→「憑證」→「建立憑證」→「服務帳戶」
  5. 填服務帳戶名稱(例如 mcp-reader),按「建立並繼續」→「繼續」→「完成」
  6. 回到憑證頁,點剛建立的服務帳戶 email,進入「金鑰」分頁
  7. 「新增金鑰」→「建立新金鑰」→選「JSON」→「建立」,瀏覽器自動下載一個 JSON 憑證檔

把下載的 JSON 移到安全位置,例如:

mkdir -p ~/.secrets
mv ~/Downloads/xxx-xxxx.json ~/.secrets/google-service-account.json

把服務帳戶加進試算表的共用名單

打開下載的 JSON,找到 client_email 欄位,值長得像:

mcp-reader@my-project-123456.iam.gserviceaccount.com

複製這個 email。打開你的 Google Sheets 試算表,點右上角「共用」按鈕,把這個 email 貼進去,權限選「檢視者」即可。按「傳送」。

這一步不做,server 連過去會看到「找不到試算表」的錯誤——即使試算表 ID 完全正確也一樣,因為服務帳戶沒有被授權。

取得試算表 ID

打開你的試算表,看網址列:

https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms/edit

/d//edit 之間那一長串就是 spreadsheet ID:

1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms

把這個值記好,等一下設定 Claude Desktop 時要用。

本地測試:用 MCP Inspector 先確認工具能跑

在終端機切到 ~/my-sheets-server,設好環境變數後啟動 dev 模式:

cd ~/my-sheets-server

export GOOGLE_CREDENTIALS_PATH=~/.secrets/google-service-account.json
export DEFAULT_SPREADSHEET_ID=你的試算表ID

uv run --with fastmcp --with gspread --with "google-auth" fastmcp dev server.py

第一次跑這行,uv 會下載所需套件(可能要等三十秒以上,網速慢的話更久——正常現象,不是當掉了)。下載完成後,命令列會顯示:

MCP Inspector is up and running at http://localhost:6274

瀏覽器打開 http://localhost:6274(第一次會自動帶上一組認證 token,直接用終端機印出的完整網址開啟最保險),你會看到 FastMCP 內建的 MCP Inspector 介面:左側列出你的工具名稱,點進去可以填參數、按「Run Tool」執行,右側顯示回傳結果。

MCP Inspector 介面示意:左側列出 read_sheet 與 search_sheet 工具,右側填參數按 Run Tool,下方顯示回傳的表格

在 Inspector 裡測試兩件事:

  1. 呼叫 read_sheet(不填 spreadsheet_id,用環境變數預設值)——看能不能拿到試算表資料
  2. 呼叫 search_sheet,keyword 填你知道存在的一個關鍵字——確認搜尋有正確回傳

兩個工具都測過、結果正確,再進下一步。在 Inspector 裡排錯比在 Claude Desktop 裡直覺多了,出了問題也更容易看到錯誤訊息。測試完按 Ctrl+C 關掉。

掛進 Claude Desktop,正式上線

打開 Claude Desktop 設定檔(Settings → Developer → Edit Config),在 mcpServers 裡新增你的 server:

{
  "mcpServers": {
    "my-sheets": {
      "command": "uv",
      "args": [
        "run",
        "--with", "fastmcp",
        "--with", "gspread",
        "--with", "google-auth",
        "fastmcp",
        "run",
        "/Users/你的使用者名稱/my-sheets-server/server.py"
      ],
      "env": {
        "GOOGLE_CREDENTIALS_PATH": "/Users/你的使用者名稱/.secrets/google-service-account.json",
        "DEFAULT_SPREADSHEET_ID": "你的試算表ID"
      }
    }
  }
}

三個地方要換成你自己的值:

  • server.py 的完整絕對路徑(不能用 ~,要展開成 /Users/yourname/...)
  • 憑證 JSON 的完整絕對路徑
  • 你的試算表 ID
絕對路徑,不是 ~ 縮寫 JSON 設定裡不認識 `~` 這個 shell 縮寫,一定要寫完整路徑。macOS 的完整路徑形式是 `/Users/你的帳號名稱/...`。不確定的話,在終端機輸入 `echo $HOME` 就能看到你的 home 目錄完整路徑。

存檔後完全關掉 Claude Desktop(macOS 按 Cmd+Q),重新打開。成功的話,對話框左下角的錘子 icon 展開,應該能看到 read_sheetsearch_sheet 兩個工具出現在清單裡。

真實查詢:看著 AI 用你做的工具

開一個新對話,先試一般查詢:

我的試算表裡有什麼資料?幫我列出來。

Claude 應該呼叫 read_sheet 工具,把你的試算表資料整理後回答你。接著試搜尋:

試算表裡有沒有叫王小明的資料?

你會看到 Claude 的回應流程:判斷這是關鍵字查詢 → 呼叫 search_sheet("王小明") → 取得結果 → 用自然語言告訴你找到幾筆、內容是什麼。

自製 server 的完整查詢流程:用戶輸入到 Claude 判斷、呼叫 my-sheets、Google Sheets API、格式化回傳、Claude 回答

這就是「讓 AI 幫 AI 做工具」的完整閉環:Claude Code 幫你寫工具 → 你把工具裝進 Claude Desktop → Claude 靠這個工具幫你查自己的資料。整個過程你沒有打過一行 Python。

常見坑

坑 1:uv 找不到,錘子 icon 不出現

症狀:設定 JSON 看起來完全沒問題,重啟 Claude Desktop 後錘子 icon 就是不出來。在終端機跑 uv --version 正常,Claude Desktop 就是找不到 uv

原因:Claude Desktop 用自己的環境啟動 server,這個環境的 PATH 不等於你終端機的 PATH。用 Homebrew 安裝的 uv 通常在 /opt/homebrew/bin/uv

解法:在終端機查 uv 的完整路徑:

which uv
# 輸出範例:/opt/homebrew/bin/uv

把查到的完整路徑填進設定的 command:

{
  "mcpServers": {
    "my-sheets": {
      "command": "/opt/homebrew/bin/uv",
      "args": ["run", "--with", "fastmcp", "--with", "gspread", "--with", "google-auth", "fastmcp", "run", "/Users/yourname/my-sheets-server/server.py"]
    }
  }
}

坑 2:Google Sheets 回傳「找不到試算表」——即使 ID 是對的

幾乎都是服務帳戶沒有被加入試算表共用名單。gspread 的行為是:沒有授權的試算表和不存在的試算表回傳一樣的錯誤 SpreadsheetNotFound,所以光看錯誤訊息看不出是哪個問題。

確認方法:打開試算表,「共用」按鈕→查看共用名單——服務帳戶的 email(憑證 JSON 裡的 client_email 欄位)應該在列表裡。沒有的話把它加進去,給「檢視者」權限。

另外確認「Google Sheets API」有在 Google Cloud Console 啟用:左側選單「API 和服務」→「已啟用的 API 和服務」,看 Google Sheets API 有沒有在清單裡。

坑 3:AI 不知道要用你的工具,或總是選錯工具

你問「幫我查試算表」,但 Claude 回答說「我不確定你是問哪份資料」而不去呼叫 read_sheet。或者你有多個工具,Claude 總是選到不對的那個。

原因幾乎都是 docstring 寫得不夠具體。AI 靠 docstring 決定什麼情況要呼叫哪個工具——「讀取工作表」這種說法太模糊,Claude 無法明確對應到你的問法。

解法:在 server.py 的 docstring 加入具體的使用情境範例(就是這堂課 Claude Code 版本裡的「適合使用情境:...」那幾句)。修好之後重啟 Claude Desktop 即可,不需要改任何設定。

@mcp.tool()
def read_sheet(...) -> str:
    """
    讀取 Google Sheets 工作表的全部資料。
    適合使用情境:「列出所有客戶」、「顯示完整庫存表」、「我的試算表裡有什麼資料」。
    如果使用者問試算表的整體內容或要看全部資料,使用這個工具。
    ...
    """

坑 4:第一次啟動很慢,以為 server 壞了

第一次在 Claude Desktop 啟動時,uv 要從網路下載 fastmcpgspreadgoogle-auth 等套件——網速慢的話可能要三十秒以上。Claude Desktop 在背景處理,你看不到任何進度,只覺得錘子 icon 一直沒出現。

解法:在終端機手動跑一次相同的 uv run 指令,讓套件先下載好、被快取起來:

uv run --with fastmcp --with gspread --with google-auth fastmcp run ~/my-sheets-server/server.py

跑起來後按 Ctrl+C 停掉。之後 Claude Desktop 重啟,套件已在快取,錘子 icon 幾秒內就出現。

作業

  1. 主線任務:照這堂課的步驟,用 Claude Code 寫出你自己的 Google Sheets server 並掛進 Claude Desktop。成功標準:開新對話,問「幫我列出試算表的所有資料」,Claude 真的呼叫工具、回傳真實資料。

  2. 擴充工具:完成主線任務後,回頭找 Claude Code,說:「請再加一個工具 get_sheet_info,讀取試算表後回傳:有哪幾個分頁、每個分頁有幾欄幾列、欄位名稱是什麼」。練習出需求的精準度——這是你製作自己 server 時最核心的能力。

  3. 換資料來源(進階選做):告訴 Claude Code:「把資料來源換成本機 CSV 檔,不再需要 Google API 憑證,直接讀 ~/data/records.csv」。讓它改寫 server。完成後你就知道:這個架構換資料來源多快——工具的名稱和 docstring 不用動,只有處理函式裡的幾行邏輯換掉。

下一課預告

你現在手上有了自己做的 server,加上前四課陸續裝好的工具組——filesystem 讀本機檔案、GitHub server 看程式碼、Notion server 查筆記、再加上今天的 Google Sheets server。工具不少了,但它們還是各自為政,沒有一個完整的工作流把它們串起來。

第 6 課《綜合實戰:個人 MCP 工具箱》就是整個課程的收尾:帶你設計一套從「早上打開電腦第一件事」到「下班前 review 今天進度」的完整日常工作流——每個環節用哪個工具、怎麼提問才能讓多個 server 協作、以及整個工具箱怎麼維護和升級。前五課的每一個設定,在第 6 課都會用上。你的個人 MCP 工具箱,下一課就把它組裝起來。

#MCP#FastMCP#Python#自建 server#Google Sheets

← 回所有文章