若你想讓 Claude、Cursor 或 Gemini 直接查公司資料庫、讀內部文件、執行自訂 API,卻卡在「Function Calling 寫了三次、換模型又要重寫」——本文是 2026 年面向後端與 AI 工程師的 MCP Server 動手教學。你會得到:協定原理速覽、Python FastMCP 環境、Hello World 到 Tools/Resources/Prompts 完整範例、HTTP+SSE 遠端模式、MCP Inspector 除錯,以及 ChromaDB 知識庫實戰。結構:六道痛點 → 三大能力對照表 → 八步落地 → 三條硬數據 → 生態展望 → FAQ。協定層背景請先讀MCP 為何是 AI 時代 HTTP;與Agent Skill 指南互補——Skill 教 Agent「怎麼做」,MCP 提供「能做什麼工具」。
MCP(Model Context Protocol) 用開放標準把「AI 如何發現、選擇並呼叫工具」統一起來。本篇不重複協定辯論,直接帶你從空目錄寫出第一個可連 Cursor 的 Server。
initialize、tools/list、tools/call 等 JSON-RPC 請求。本地開發首選 STDIO(標準輸入/輸出):Host 以子行程啟動 Server,零連接埠衝突。跨網路共享用 HTTP + SSE:Client 透過 SSE 接收 Server 推送,適合遠端伺服器部署。
| 能力 | Tools | Resources | Prompts |
|---|---|---|---|
| 本質 | 可執行函式,有副作用 | 唯讀資料 URI | 預設提示範本 |
| 典型用途 | 查 DB、發 Slack、跑腳本 | 讀設定檔、知識庫片段 | 程式碼審查、PR 摘要範本 |
| RPC 方法 | tools/list、tools/call |
resources/list、resources/read |
prompts/list、prompts/get |
| Agent 使用頻率 | 最高(核心「手腳」) | 中(補充上下文) | 低(標準化重複任務) |
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "query_users",
"arguments": { "limit": 10 }
},
"id": 2
}
與 REST 的差別:REST 解決「能不能呼叫 endpoint」;MCP 讓 AI 在執行時期透過 tools/list 動態發現工具,且每個 Tool 自帶 JSON Schema 描述參數。詳見協定解讀文。
本篇以 Python + 官方 Python SDK 的 FastMCP 為主;TypeScript 團隊可對照 @modelcontextprotocol/sdk,API 概念一致。
mkdir my-mcp-server && cd my-mcp-server python3 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install "mcp[cli]" chromadb # FastMCP + 後續知識庫實戰 # 驗證 CLI mcp --help
目錄建議:
my-mcp-server/ ├── server.py # MCP Server 主程式 ├── requirements.txt ├── .env # API Key、DB 連線(勿提交 Git) └── data/ # 本地知識庫向量索引
以下 Server 暴露一個 ping 工具,確認 JSON-RPC 握手成功:
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-mcp")
@mcp.tool()
def ping(message: str = "hello") -> str:
"""回傳確認訊息,用於連線測試。"""
return f"pong: {message}"
if __name__ == "__main__":
mcp.run() # 預設 STDIO 傳輸
終端機測試(不透過 Host):
# 安裝 MCP Inspector(Node 18+) npx @modelcontextprotocol/inspector python server.py
Inspector 開啟瀏覽器 UI,可手動發 tools/list 與 tools/call,確認 ping 出現在清單且能回傳 pong。
在 Cursor Settings → MCP → Add Server,或編輯 ~/.cursor/mcp.json:
{
"mcpServers": {
"hello-mcp": {
"command": "/path/to/my-mcp-server/.venv/bin/python",
"args": ["/path/to/my-mcp-server/server.py"]
}
}
}
Tool 是 MCP 的核心。FastMCP 用型別註解自動生成 JSON Schema;docstring 會成為 AI 理解用途的說明。
import os
import httpx
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("biz-tools")
@mcp.tool()
async def get_weather(city: str) -> str:
"""查詢指定城市的即時天氣(示範外部 API 整合)。"""
api_key = os.environ.get("WEATHER_API_KEY", "")
async with httpx.AsyncClient() as client:
r = await client.get(
f"https://api.example.com/weather?q={city}&key={api_key}"
)
return r.text
@mcp.tool()
def query_users(limit: int = 10) -> list[dict]:
"""從 PostgreSQL 查詢使用者列表。limit 上限 100。"""
limit = min(max(limit, 1), 100)
# 實務中替換為 psycopg / SQLAlchemy 查詢
return [{"id": 1, "name": "demo", "limit_used": limit}]
安全紅線:Tool 內一律做參數校驗與權限檢查;勿把 raw SQL 或 shell 指令直接暴露給 AI。生產環境在 Server 層集中管理 API Key,而非寫進 Prompt。
Resources 以 URI 識別,適合讓 AI 讀取不需「執行」的靜態內容——設定檔、Markdown 知識片段、OpenAPI spec。
from pathlib import Path
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("docs-server")
DOCS = Path(__file__).parent / "docs"
@mcp.resource("docs://readme")
def readme() -> str:
"""專案 README 全文。"""
return (DOCS / "README.md").read_text(encoding="utf-8")
@mcp.resource("docs://api-spec")
def api_spec() -> str:
"""內部 REST API OpenAPI 規格。"""
return (DOCS / "openapi.yaml").read_text(encoding="utf-8")
Client 透過 resources/list 發現 URI,再以 resources/read 拉取內容——與 Tools 的「執行」語意明確分離。
Prompts 提供帶參數的提示範本,Host 可一鍵注入對話上下文,減少使用者反覆貼同一段指令。
from mcp.server.fastmcp import FastMCP
from mcp.types import PromptMessage, TextContent
mcp = FastMCP("review-prompts")
@mcp.prompt()
def code_review(language: str = "python", focus: str = "security") -> list[PromptMessage]:
"""產生程式碼審查提示,聚焦指定語言與審查面向。"""
text = f"""請審查以下 {language} 程式碼,重點檢查:{focus}。
列出:1) 高風險問題 2) 建議修復 3) 可選重構。使用繁體中文回覆。"""
return [PromptMessage(role="user", content=TextContent(type="text", text=text))]
當 STDIO 子行程無法滿足「多 Client 共用同一 Server」時,改用 HTTP+SSE。FastMCP 支援以 Starlette / uvicorn 啟動:
# server_http.py — 在原有 FastMCP 實例上
if __name__ == "__main__":
mcp.run(transport="sse", host="127.0.0.1", port=8765)
{
"mcpServers": {
"remote-biz-tools": {
"url": "https://mcp.example.com/sse",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}
注意事項:
推薦除錯流程:
npx @modelcontextprotocol/inspector python server.py,排除 Host 設定問題。tools/call 會回傳 -32602 Invalid params。mcp 關鍵字。| 症狀 | 可能原因 | 處置 |
|---|---|---|
| Server 無法啟動 | Python 路徑錯誤、venv 未啟用 | 在 mcp.json 寫絕對路徑;Inspector 先跑通 |
| tools/list 為空 | 裝飾器未生效、import 失敗 | 確認 @mcp.tool() 在 run() 前註冊 |
| HTTP SSE 頻斷 | 反向代理逾時、筆電睡眠 | 調 Nginx proxy_read_timeout;遷至 7×24 節點 |
| Authorization 失敗 | Token 過期、Header 名稱不符 | 對照 Server middleware 與 Client headers |
將 Server 容器化,便於在雲端伺服器以固定設定重啟:
FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8765 CMD ["python", "server_http.py"]
# docker-compose.yml
services:
mcp-server:
build: .
ports: ["8765:8765"]
env_file: .env
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://127.0.0.1:8765/health"]
interval: 30s
timeout: 5s
retries: 3
部署檢查清單:Secrets 走環境變數或 Vault;限制 Tool 的網路 egress;對外只暴露 SSE 端點而非整台主機 SSH。
整合向量資料庫,讓 Cursor 能語意搜尋你的筆記與文件:
import chromadb
from chromadb.utils import embedding_functions
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("kb-search")
client = chromadb.PersistentClient(path="./data/chroma")
ef = embedding_functions.DefaultEmbeddingFunction()
collection = client.get_or_create_collection("notes", embedding_function=ef)
@mcp.tool()
def search_notes(query: str, n_results: int = 5) -> list[dict]:
"""語意搜尋個人知識庫,回傳最相關的筆記片段。"""
n_results = min(max(n_results, 1), 20)
res = collection.query(query_texts=[query], n_results=n_results)
out = []
for doc, meta, dist in zip(res["documents"][0], res["metadatas"][0], res["distances"][0]):
out.append({"text": doc, "source": meta.get("source", ""), "distance": dist})
return out
@mcp.tool()
def ingest_note(text: str, source: str = "manual") -> str:
"""寫入一則筆記到向量索引。"""
import uuid
collection.add(documents=[text], metadatas=[{"source": source}], ids=[str(uuid.uuid4())])
return f"ingested: {len(text)} chars from {source}"
首次 ingest 後,在 Cursor 中問「搜尋知識庫:MCP 部署注意事項」,Agent 應呼叫 search_notes 並引用回傳片段。向量索引建議放在持久化卷,避免容器重建後資料遺失。
mcp --help。ping 工具跑通 STDIO。從 Hello World 到 ChromaDB 知識庫,你已掌握 MCP 三大能力與兩種傳輸模式。下一步可以是:把公司內網 API 封裝成 Tools、用 Resources 暴露合規文件、用 Prompts 標準化 code review 流程。
但若把知識庫向量索引與 HTTP+SSE 長連線跑在會睡眠的筆電、或與同事共用、記憶體與 CPU 被搶佔的開發機上,你會遇到三項隱性成本:SSE 工作階段因合蓋中斷、索引載入失敗導致 Tool 呼叫重試、以及無法維持 7×24 Agent 編排。對需要穩定 MCP 工作階段的生產環境,把 Server 落在 MACCOME Mac mini(M4 / M4 Pro)獨占節點,通常比在本地與睡眠策略搏鬥更省總成本;公開檔位見Mac Mini 雲端租用價格,接入問題可參考雲端 Mac 協助中心。
常見問題
MCP Server 應該用 Python 還是 TypeScript 開發?
兩者皆有官方 SDK。Python 的 FastMCP 上手最快,適合後端與資料庫整合;TypeScript 適合 Node.js 生態。建議先用 Python 完成 STDIO 驗證,再評估 HTTP 部署。
Tools、Resources、Prompts 三者有什麼差別?
Tools 是可執行函式;Resources 是唯讀 URI 資料;Prompts 是提示範本。Agent 工作流以 Tools 為主。協定層對照見MCP 協定解讀。
MCP 和 Agent Skill(SKILL.md)怎麼配合?
Skill 教 Agent「何時、按何步驟」完成任務;MCP 提供可呼叫的工具。詳見Agent Skill 指南。
生產環境如何常駐 MCP Server?
本地開發用 STDIO;跨團隊用 HTTP+SSE + Docker。避免筆電合蓋中斷長連線。MACCOME 提供 M4/M4 Pro 雲 Mac 獨占節點,適合 7×24 MCP 常駐。報價見租用價格頁。