從 0 開發一個 MCP Server
手把手教你構建 AI 工具調用能力

若你的 Claude / GPT / Cursor 只能聊天、無法查資料庫、讀檔案或調 API——你缺的不是更長的 Prompt,而是可複用的工具層。本文面向後端與全棧開發者,從 Hello World 到 ChromaDB 知識庫 MCP Server,涵蓋 Tools / Resources / Prompts 三大能力、stdio 與 HTTP+SSE 雙傳輸、除錯測試與 Docker 生產部署;讀完後你將擁有一套可在 Cursor 裡直接調用的自訂工具,並理解如何把 Server 放到遠端 Mac 7×24 常駐(協定背景可先讀 MCP 協定解讀篇)。

01

為什麼 AI 需要 MCP Server:沒有工具的 LLM 只是「會說話的大腦」

大模型訓練資料有截止日期,無法存取你的 CRM、Git 倉庫或內網 API。2024 年前,給 Claude 寫一套 Function Calling、給 GPT 寫 Plugins、給 Cursor 寫另一套格式——換模型 = 推倒重來。MCP Server 讓你把「工具能力」封裝成獨立程序,寫一次、Claude Desktop / Cursor / Gemini 都能用

典型場景:Claude Desktop 裡讓 AI 查 Postgres 銷售資料;Cursor 裡讓 Agent 讀專案文件並改程式碼;GPT 透過 HTTP MCP 調用你公司的工單系統——底層都是同一個 Server。

本文價值:不是講概念,而是帶你從 say_hello 走到帶向量檢索的生產級知識庫 Server。目標讀者:有 Python 或 TypeScript 基礎、希望在 IDE 或 Desktop 裡擴展 AI 能力的開發者。

六大痛點:為什麼「直接寫 API 包裝」不夠

  1. 01

    模型供應商鎖定:OpenAI Function Calling 與 Claude Tool Use 格式不同,每換一家就要重寫適配層。

  2. 02

    工具無法被發現:REST API 靠靜態文件,AI 無法在執行時期調用 tools/list 自主發現能力清單。

  3. 03

    IDE 各自為政:Cursor、VS Code 擴充功能、JetBrains 外掛的工具定義無法複用,維護 N×M 套整合。

  4. 04

    上下文與資料割裂:LLM 無法穩定讀取設定檔、使用者偏好或即時日誌——需要標準化的 Resources 唯讀通道。

  5. 05

    Prompt 模板散落:Code Review、Incident 報告等複用模板沒有統一註冊機制,團隊各自複製貼上。

  6. 06

    本機 vs 遠端部署混亂:stdio 子程序適合開發,生產 HTTP 閘道、鑑權與監控缺少統一範式(參見 stdio 子程序治理篇)。

「給 AI 接上 MCP Server,就像給程式設計師裝上 IDE 外掛——能力邊界瞬間從『聊天』擴展到『操作真實世界』。」

02

MCP 是什麼:從 Function Calling 到開放協定

演進路徑:Function Calling(2023)ChatGPT PluginsMCP(2024.11,Anthropic 開源)。Anthropic 設計 MCP 為 AI 與外部世界的「USB-C」:Host(Cursor/Claude Desktop)內嵌 MCP Client,與你的 MCP Server 建立 1:1 會話。

架構:Client / Server 與三大能力

  • Tools(工具):AI 可調用、有副作用的操作——查庫、寫檔案、發 HTTP 請求。
  • Resources(資源):唯讀上下文——設定、文件片段、使用者 profile,透過 URI 尋址。
  • Prompts(提示模板):預置多輪對話骨架,如 code review、incident postmortem。

底層通訊基於 JSON-RPC 2.0initializetools/list / tools/callresources/read。傳輸層兩種生命週期:

  • stdio:Host 以子程序啟動 Server,stdin/stdout 交換 JSON-RPC,程序退出即會話結束。
  • HTTP + SSE:Client 連遠端 URL,Server 推送事件流,適合團隊共享與水平擴展。

完整規範見官方網站 modelcontextprotocol.io

MCP vs OpenAI FC vs LangChain 工具

維度MCPOpenAI Function CallingLangChain Tools
開放性跨廠商開放協定,AAIF 治理綁定 OpenAI API框架內抽象,非傳輸標準
發現機制執行時期 tools/list請求內嵌 functions 陣列程式碼註冊,無標準發現
唯讀資料Resources + URI scheme無一等公民Retriever 概念,非協定級
Prompt 模板Prompts 標準介面PromptTemplate 類別
傳輸stdio / HTTP+SSE / Streamable HTTPHTTPS API 一體取決於 Agent 執行環境
複用性同一 Server 服務 Cursor + Claude + Gemini僅 OpenAI 生態跨框架需重寫綁定
03

開發環境搭建:Python FastMCP vs TypeScript SDK

兩條主流路線:Python mcp + FastMCP(資料/腳本友好)與 TypeScript @modelcontextprotocol/sdk(Web/API 整合、型別安全)。SDK 原始碼:python-sdktypescript-sdk

bash
# Python 路線
python -m venv .venv && source .venv/bin/activate
pip install "mcp[cli]" httpx pydantic

# TypeScript 路線
npm init -y && npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node

推薦專案結構

tree
my-mcp-server/
├── pyproject.toml          # 或 package.json
├── src/
│   ├── server.py           # FastMCP 入口
│   ├── tools/              # 各工具模組
│   ├── resources/          # Resource 提供者
│   └── prompts/            # Prompt 模板
├── tests/
│   └── test_tools.py       # pytest + ClientSession
├── Dockerfile
└── README.md

六步環境就緒清單

  1. 01

    選語言棧:資料/ML 團隊優先 Python;Node 全棧選 TypeScript。

  2. 02

    建立虛擬環境並鎖定依賴:pip freezepackage-lock.json,避免 schema 漂移。

  3. 03

    安裝 MCP Inspector:npx @modelcontextprotocol/inspector,視覺化除錯 JSON-RPC。

  4. 04

    設定 Claude Desktop:編輯 ~/Library/Application Support/Claude/claude_desktop_config.json,添加 Server command/args。

  5. 05

    設定 Cursor:Settings → MCP → Add Server,stdio 填 python -m src.server 或絕對路徑。

  6. 06

    驗證 Inspector 連通:啟動 Server → Inspector 連線 → 確認 tools/list 回傳非空。

json
// Cursor / Claude Desktop MCP 設定範例
{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["-m", "src.server"],
      "env": { "API_KEY": "your-key" }
    }
  }
}
04

Hello World:30 秒跑通第一個 MCP Server

用 FastMCP 寫一個 say_hello 工具,驗證整條鏈路:程式碼 → Inspector → Cursor。

python
# src/server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("hello-server")

@mcp.tool()
def say_hello(name: str = "World") -> str:
    """向指定對象打招呼"""
    return f"Hello, {name}! MCP is working."

if __name__ == "__main__":
    mcp.run()  # 預設 stdio 傳輸
bash
# 用 Inspector 除錯
npx @modelcontextprotocol/inspector python -m src.server

# 或直接 stdio 啟動
python -m src.server

在 Cursor 裡添加同一 command 後,對 Agent 說「用 say_hello 跟 NodeMini 打招呼」——若回傳 JSON 結果,說明 Client ↔ Server 握手成功。

info

提示:FastMCP 從函式 docstring 與型別註解自動產生 JSON Schema,無需手寫參數描述。

05

Tools 深度實戰:從 Schema 到五個生產級工具

Tool 是 MCP 最核心的能力:AI 透過 tools/call 執行帶副作用的操作。每個 Tool 需暴露名稱、描述、inputSchema;FastMCP 用 Pydantic 模型增強校驗。

python
from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    query: str = Field(..., description="搜尋關鍵字")
    limit: int = Field(10, ge=1, le=100, description="回傳條數")

@mcp.tool()
async def search_docs(params: SearchInput) -> str:
    """在文件庫中搜尋"""
    results = await index.search(params.query, params.limit)
    return json.dumps(results, ensure_ascii=False)

五個常用 Tool 模式

  • calculator:安全 eval 或 ast.literal_eval,禁止 exec
  • file_read / file_write:限制根目錄白名單,防止路徑遍歷。
  • fetch_url(async):用 httpx 非同步請求,設 timeout 與網域白名單。
  • db_query:唯讀 SQL + 參數化查詢,禁止 DDL。
  • get_current_time:回傳 ISO8601 時區時間,解決 LLM 時間幻覺。
python
import httpx
from datetime import datetime, timezone

@mcp.tool()
async def fetch_url(url: str) -> str:
    """HTTP GET 取得 URL 內容(網域白名單內)"""
    allowed = ("api.github.com", "nodemini.com")
    if not any(url.startswith(f"https://{d}") for d in allowed):
        raise ValueError(f"Domain not allowed: {url}")
    async with httpx.AsyncClient(timeout=10.0) as client:
        resp = await client.get(url)
        resp.raise_for_status()
        return resp.text[:8000]

@mcp.tool()
def get_current_time() -> str:
    """回傳目前 UTC 時間"""
    return datetime.now(timezone.utc).isoformat()

錯誤處理最佳實踐

  • 回傳結構化錯誤:raise ValueError("human-readable msg"),Client 會把 message 回傳給 LLM。
  • 區分可重試 vs 致命:網路逾時標註 retryable;權限拒絕直接 fail。
  • 日誌與脫敏:記錄 tool 名與耗時,不 log 完整 API Key 或 PII。
  • 冪等設計:寫操作帶 idempotency_key,避免 Agent 重複調用造成髒資料。
06

Resources:唯讀上下文與 URI 方案

Tool vs Resource:Tool 有副作用、由 AI 主動調用;Resource 是唯讀上下文,Host 可在對話前注入或 AI 透過 resources/read 拉取。URI scheme 自訂,如 config://user://file://

python
@mcp.resource("config://app/settings")
def app_settings() -> str:
    """靜態應用設定(text/plain)"""
    return open("config/settings.json").read()

@mcp.resource("user://{user_id}/profile")
def user_profile(user_id: str) -> str:
    """動態使用者 profile(application/json)"""
    return json.dumps(get_user(user_id))

Resource 內容類型

MIME 類型用途範例
text/plain日誌、READMEfile://logs/app.log
application/json設定、API 回應config://env
application/octet-stream二進位(base64)PDF 摘要
text/event-stream即時訂閱日誌 tail、metrics 流

檔案系統 Resource Server 模式:實作 resources/list 掃描目錄、resources/read 按 URI 讀取、resources/subscribe 監聽 watchfiles 變更並推送更新——適合把程式碼庫文件暴露給 Cursor Agent。

07

Prompts:可複用的多輪對話模板

MCP Prompt 是註冊在 Server 上的對話骨架,Client 透過 prompts/get 拉取訊息列表,可含 user / assistant 角色與參數占位符——團隊共享 Code Review、Incident 流程而不必每人維護 Prompt 檔案。

python
from mcp.types import PromptMessage, TextContent

@mcp.prompt()
def code_review_prompt(language: str = "python") -> list[PromptMessage]:
    """標準化 Code Review 多輪模板"""
    return [
        PromptMessage(role="user", content=TextContent(
            type="text",
            text=f"你是資深 {language} 工程師。請按安全、效能、可讀性三維審查以下 diff。"
        )),
        PromptMessage(role="assistant", content=TextContent(
            type="text",
            text="請貼上 diff 或指定 PR 編號,我將按 CHECKLIST 輸出結構化 review。"
        )),
    ]

多輪模板可嵌套變數({ticket_id}{severity}),Server 端統一版本管理,Client 升級 Server 即全員同步最新 review 標準。

08

HTTP 傳輸:從 stdio 到 Streamable HTTP 生產部署

維度stdioHTTP + SSE / Streamable HTTP
部署本機子程序,Host 啟動獨立服務,URL 連線
擴展單機,難水平擴展負載平衡、多副本
鑑權依賴 Host 環境變數Bearer Token / API Key / mTLS
除錯Inspector 直連curl + SSE 用戶端
適用個人開發、Cursor 本機團隊共享、SaaS 整合
python
# Streamable HTTP 模式(FastMCP 2026)
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("prod-server", host="0.0.0.0", port=8080)

# ... 註冊 tools ...

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

生產必做:Bearer Token 校驗中介軟體、CORS 白名單(僅允許自家 Host 網域)、rate limit(如 100 req/min/IP)、HTTPS 終止於反向代理。遠端閘道維運細節見 HTTP 閘道治理篇

warning

注意:HTTP MCP 切勿裸奔公網——2026 年仍有大量未授權暴露的 Server,務必加鑑權與 IP 限制。

09

除錯與測試:Inspector + pytest 單元測試

MCP Inspector 是官方視覺化除錯器:連線 stdio 或 URL 後,可手動發 tools/listtools/call,查看 JSON-RPC 往返,比盲猜 Cursor 日誌高效十倍。

python
# tests/test_tools.py
import pytest
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

@pytest.mark.asyncio
async def test_say_hello():
    params = StdioServerParameters(command="python", args=["-m", "src.server"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("say_hello", {"name": "MCP"})
            assert "MCP" in result.content[0].text

常見錯誤對照表

現象原因修復
Server 啟動後立即退出stdout 被 print 污染日誌寫 stderr;禁止 print 到 stdout
tools/list 為空裝飾器未註冊或 import 順序錯誤確保 @mcp.tool()run() 前執行
Cursor 顯示 disconnectedcommand 路徑錯誤或 venv 未啟用用絕對路徑;設定裡寫完整 python 路徑
JSON-RPC parse error非 JSON 輸出混入 stdio關閉 debug banner;函式庫日誌級別 WARNING+
10

生產部署:Docker、雲平台與可觀測性

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY src/ src/
EXPOSE 8080
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1
CMD ["python", "-m", "src.server", "--transport", "streamable-http"]

部署平台選型:Railway / Render 適合快速驗證;AWS ECS / GCP Cloud Run 適合企業合規;VPS + Docker Compose 成本最低但需自管修補程式。

可引用生產參數(EEAT)

  • 協定版本:MCP 2025-03-26 spec 起支援 Streamable HTTP;Client 握手時 initialize 協商 protocolVersion,Server 應宣告相容範圍。
  • 資源基線:輕量 Tool Server ~128MB RAM;帶 ChromaDB 向量索引建議 ≥2GB RAM、SSD 持久卷。
  • 可觀測性棧:結構化 JSON 日誌 → Loki/CloudWatch;Prometheus 暴露 /metrics(tool 調用 QPS、P99 延遲);Sentry 捕獲未處理例外;/health 供 K8s liveness。
11

實戰專案:ChromaDB 知識庫 MCP Server

把內部 Wiki / Markdown 文件向量化,暴露 index_documentsearch_knowledgewrite_note 三個 Tool,讓 Cursor Agent 能「查公司知識庫再寫程式碼」。

需求:支援增量索引(watchfiles 監聽 docs/)、語意搜尋 Top-K、可選寫入 scratchpad。

python
import chromadb
from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction

client = chromadb.PersistentClient(path="./data/chroma")
collection = client.get_or_create_collection(
    "wiki", embedding_function=SentenceTransformerEmbeddingFunction()
)

@mcp.tool()
def search_knowledge(query: str, top_k: int = 5) -> str:
    """語意搜尋內部知識庫"""
    hits = collection.query(query_texts=[query], n_results=top_k)
    return json.dumps(hits["documents"][0], ensure_ascii=False)

@mcp.tool()
def index_document(path: str) -> str:
    """索引單個 Markdown 檔案"""
    text = open(path).read()
    collection.upsert(ids=[path], documents=[text], metadatas=[{"path": path}])
    return f"Indexed: {path}"

Cursor 演示查詢:「搜尋知識庫裡關於 MCP HTTP 部署的文件,然後總結三步上線清單」——Agent 會先 search_knowledge,再基於檢索結果回答。向量庫也可換 Qdrant(遠端 gRPC)以支援更大規模。

12

生態、2026 趨勢與學習路徑

官方與社群已有大量開箱即用 Server,不必重複造輪子:

  • mcp-server-filesystem — 安全沙箱檔案讀寫
  • mcp-server-github — PR/Issue API
  • brave-search / postgres / slack — 搜尋、SQL、團隊協作

2026 趨勢:MCP Marketplace 湧現、OAuth 2.1 工具授權進入 spec 路線圖、Streamable HTTP 逐步取代純 SSE。學習路徑 checklist:① 讀 spec → ② Hello World → ③ 寫 3 個 Tool → ④ 加 Resource → ⑤ pytest → ⑥ Docker 上線 → ⑦ 接入 Cursor。

總結

say_hello 到 ChromaDB 知識庫,你已掌握 MCP Server 全棧能力:Tools 執行、Resources 上下文、Prompts 模板、雙傳輸、測試與生產維運。下一步可 fork 社群 Server 或把公司 API 封裝為團隊標準工具層。

純本機 stdio 適合個人實驗,但多 Server 並行、向量索引常駐、HTTP 長連線會讓 16GB 筆電頻繁 swap;廉價 Linux VPS 又難以跑 macOS 專屬工具鏈。自建 HTTP 閘道若缺少 session 親和與鑑權,還容易出現連線洩漏與未授權暴露——長期穩定性往往不如預期。

對需要把 MCP 作為生產基礎設施、同時跑 Cursor Agent 與 iOS/macOS CI 的團隊,把 MCP Server 放在可獨佔的雲端 Mac 上 7×24 執行,通常比押寶本機筆電或通用 VM 更可控。NodeMini Mac Mini 雲端租賃可作為 MCP + Agent 執行層:換底層 LLM 時 SSH 節點與 Server 設定保持不變。規格見 租賃價格說明,接入見 幫助中心

FAQ

常見問題

Python FastMCP 上手最快,適合資料/腳本類工具;TypeScript SDK 型別安全、與 Node 生態無縫。兩者協定完全相容。若需 7×24 跑多個 Server,可參考 租賃價格說明 選擇遠端 Mac 配置。

Function Calling 綁定 OpenAI;MCP 是跨 Claude/GPT/Gemini/Cursor 的開放協定,支援 Resources 與 Prompts。背景詳見 MCP 協定解讀篇

輕量 stdio 可在本機執行;多 Server + 向量庫 + HTTP 長連線 建議獨佔遠端 Mac。接入步驟見 幫助中心,維運參見 stdio 子程序治理篇