- MCP(Model Context Protocol)是 AI 工具整合的「USB-C」——一個開放協議統一所有工具連接,將 N 個 AI 應用對接 M 個工具的 N×M 問題簡化為 N+M
- 三層架構設計:Host(AI 應用,如 Claude Desktop)→ Client(協議層,管理連線與安全)→ Server(工具與資料提供者),職責分離清晰
- 已被 Claude Desktop、Cursor、Windsurf、Zed、Sourcegraph Cody 等主流開發工具採用,社群已建構超過千個開源 MCP Server
- 本文附兩個 Google Colab 實作:從零建構天氣查詢 MCP Server、建構多工具 MCP Server 並以 Client SDK 測試完整呼叫流程
一、為何需要 MCP?AI 工具整合的碎片化困境
大型語言模型(LLM)的能力邊界,取決於它能存取多少外部工具與資料。無論是查詢資料庫、操作 API、讀取檔案系統,還是與第三方服務互動,LLM 都需要一個可靠的「橋樑」來連接外部世界。然而,這座橋樑至今仍未標準化[6]。
1.1 N×M 問題:每個整合都是一座孤島
假設你是一家企業的技術主管,團隊使用 3 個 AI 應用(Claude、ChatGPT、Gemini)和 5 個內部工具(CRM、ERP、知識庫、Slack、Jira)。在沒有統一協議的情況下,你需要建構 3 × 5 = 15 個獨立的整合。每當新增一個 AI 應用或一個工具,整合數量就呈乘法增長。這就是所謂的 N×M 問題。
Schick 等人在 Toolformer 研究中[3]證明了 LLM 可以自主學習使用工具,但工具的接入介面仍然是碎片化的。每個平台有自己的 API 格式、認證機制和錯誤處理邏輯,開發者必須為每個組合撰寫定制的膠水代碼(glue code)。
1.2 Function Calling 的局限
OpenAI 在 2023 年推出的 Function Calling[11] 是一次重要的嘗試。它允許開發者以 JSON Schema 定義函式,讓模型決定何時呼叫、傳入什麼參數。Google 的 Gemini API 也提供了類似機制[12]。
然而,Function Calling 存在三個結構性限制:
- 平台鎖定:OpenAI 的函式定義格式與 Anthropic、Google 的不相容。為 ChatGPT 寫的工具描述無法直接用於 Claude
- 無狀態性:Function Calling 是單次請求-回應模式,無法維持工具的連線狀態或管理長期會話
- 缺乏發現機制:AI 應用無法自動發現有哪些工具可用、每個工具的能力範圍是什麼。工具清單必須在每次請求時手動傳入
1.3 MCP 的解法:統一協議層
2024 年 11 月,Anthropic 開源發布了 Model Context Protocol(MCP)[2],目標是成為 AI 工具整合領域的「USB-C」——一個統一的、開放的、廠商中立的協議標準。
MCP 的核心洞見是:與其讓每個 AI 應用分別對接每個工具(N×M),不如建立一個中間協議層。AI 應用只需實作一次 MCP Client,就能連接所有 MCP Server;工具提供者只需實作一次 MCP Server,就能被所有支援 MCP 的 AI 應用存取。整合數量從 N×M 降為 N+M。
傳統模式(N×M 整合):
Claude ──┬── Slack 整合
├── GitHub 整合
└── PostgreSQL 整合
ChatGPT ─┬── Slack 整合(重寫)
├── GitHub 整合(重寫)
└── PostgreSQL 整合(重寫)
MCP 模式(N+M 整合):
Claude ──── MCP Client ──┐
ChatGPT ── MCP Client ──┤ MCP 協議
Cursor ─── MCP Client ──┤
├── Slack MCP Server
├── GitHub MCP Server
└── PostgreSQL MCP Server
二、MCP 協議架構全解析
MCP 的協議設計遵循 JSON-RPC 2.0 標準[1],定義了三個核心角色與三大能力原語(primitives)。理解這六個概念,就掌握了 MCP 的全貌。
2.1 三角色架構:Host / Client / Server
Host(宿主)是使用者直接互動的 AI 應用程式,例如 Claude Desktop、Cursor IDE 或自建的 chatbot。Host 負責管理使用者介面、處理對話流程,並決定何時需要呼叫外部工具。
Client(客戶端)是 Host 內部的協議層元件。每個 Client 與一個 Server 維持一對一的有狀態連線。Client 負責協議握手(initialization handshake)、能力協商(capability negotiation)、訊息路由,以及最重要的——安全把關。Host 內部可以同時運行多個 Client,分別連接不同的 Server。
Server(伺服器)是工具與資料的提供者。一個 MCP Server 可以暴露任意數量的工具(Tools)、資源(Resources)和提示模板(Prompts)。Server 是一個輕量級的進程,通常以 stdio 或 HTTP+SSE 方式與 Client 通訊。
MCP 架構圖:
┌─────────────────────────────────────────────┐
│ Host(如 Claude Desktop) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Client A│ │ Client B│ │ Client C│ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
└───────┼────────────┼────────────┼───────────┘
│ │ │
MCP Protocol MCP Protocol MCP Protocol
(JSON-RPC) (JSON-RPC) (JSON-RPC)
│ │ │
┌────┴────┐ ┌────┴────┐ ┌────┴────┐
│Server A │ │Server B │ │Server C │
│(GitHub) │ │(Slack) │ │(DB) │
└─────────┘ └─────────┘ └─────────┘
2.2 三大能力原語
MCP Server 透過三種原語向 Client 暴露能力:
Tools(工具)是模型可以呼叫的函式。每個 Tool 有名稱、描述和 JSON Schema 定義的輸入參數。Tool 呼叫由模型發起,但需經過 Client 的安全審核(人機迴圈,human-in-the-loop)。典型用途包括:執行資料庫查詢、呼叫外部 API、操作檔案系統。
Resources(資源)是模型可以讀取的結構化資料。每個 Resource 以 URI 標識(例如 file:///path/to/doc 或 db://table/row),並附帶 MIME 類型。Resources 由應用程式控制——使用者或 Host 決定何時將哪些資源注入模型的上下文視窗。
Prompts(提示模板)是預定義的提示指令模板,由 Server 提供、使用者選擇觸發。Prompts 可以包含參數化的佔位符,使用者填入後展開為完整的提示文本。典型用途:程式碼審查模板、資料分析報告模板。
| 能力原語 | 控制方 | 描述 | 類比 |
|---|---|---|---|
| Tools | 模型發起(Model-controlled) | 模型判斷何時呼叫、傳入什麼參數 | POST API endpoint |
| Resources | 應用控制(Application-controlled) | Host/使用者決定何時載入哪些資源 | GET API endpoint |
| Prompts | 使用者觸發(User-controlled) | 使用者選擇要使用哪個提示模板 | 預設的 slash command |
2.3 Transport 層:stdio vs SSE
MCP 規範定義了兩種傳輸機制:
stdio(標準輸入輸出)適用於本地端場景。Host 以子進程方式啟動 Server,透過 stdin/stdout 交換 JSON-RPC 訊息。優點是零網路配置、低延遲、安全性高(進程隔離)。Claude Desktop 和 Cursor 目前主要使用此模式。
HTTP + Server-Sent Events(SSE)適用於遠端場景。Client 以 HTTP POST 發送請求,Server 以 SSE 串流回傳結果。優點是可跨網路部署、支援多 Client 連線。適合企業級的共享 MCP Server 部署。
2.4 比較表:MCP vs Function Calling vs LangChain Tools
| 特性 | MCP | Function Calling(OpenAI) | LangChain Tools |
|---|---|---|---|
| 協議標準 | 開放規範(JSON-RPC 2.0) | OpenAI 私有 API | Python 框架 API |
| 連線狀態 | 有狀態(持久連線) | 無狀態(每次請求) | 視實作而定 |
| 工具發現 | 自動(tools/list) |
手動傳入工具清單 | 手動註冊 |
| 資源管理 | 原生支援(Resources) | 不支援 | 需另行實作 |
| 提示模板 | 原生支援(Prompts) | 不支援 | PromptTemplate |
| 多模型支援 | 廠商中立 | 僅 OpenAI | 多模型(透過框架抽象) |
| 傳輸方式 | stdio / HTTP+SSE | HTTPS API | 進程內呼叫 |
| 語言 SDK | TypeScript, Python(官方) | 多語言(OpenAI SDK) | Python 為主 |
| 安全模型 | Client 端 guard + 人機迴圈 | 應用層自行實作 | 應用層自行實作 |
三、MCP Server 的核心概念
理解 MCP Server 的設計模式,是掌握 MCP 實戰的關鍵。以下我們深入剖析 Server 的四個核心概念[1]。
3.1 Tool 定義:名稱、描述與 inputSchema
每個 Tool 由三個要素組成。名稱(name)是工具的唯一識別符,遵循 snake_case 慣例。描述(description)是自然語言文本,告訴模型這個工具的用途和適用場景——描述的品質直接影響模型是否能正確選擇工具。inputSchema 是 JSON Schema 格式的參數定義,規範了工具接受的輸入結構。
# Tool 定義範例(Python SDK)
@server.tool()
async def get_weather(city: str, units: str = "celsius") -> str:
"""查詢指定城市的即時天氣資訊。
Args:
city: 城市名稱(英文),例如 "Taipei" 或 "Tokyo"
units: 溫度單位,"celsius" 或 "fahrenheit",預設 celsius
Returns:
包含溫度、濕度、風速的天氣摘要文字
"""
# 實際實作...
Python MCP SDK 會自動從函式的型別標註和 docstring 生成對應的 JSON Schema,開發者不需要手動撰寫 schema 定義。
3.2 Resource 暴露:URI 模式與 MIME 類型
Resources 使用 URI 模式來標識資料來源。MCP 規範支援自定義 URI scheme,例如:
file:///home/user/documents/report.pdf— 本地檔案postgres://localhost/mydb/users— 資料庫表github://repo/owner/issues— GitHub issuesweather://current/taipei— 自定義資源
每個 Resource 附帶 MIME 類型(如 text/plain、application/json、image/png),讓 Client 知道如何處理回傳的內容。Resource 也支援模板化 URI(Resource Templates),例如 weather://current/{city},Client 可以動態填入參數。
3.3 Prompt 模板:可重用的提示指令
Prompts 讓 Server 定義可重用的提示模板。使用者在 Host 介面中選擇一個 Prompt,填入參數後,展開為包含 Resources 和指令的完整提示。這對於標準化工作流程特別有用:
# Prompt 定義範例
@server.prompt()
async def code_review(language: str, code: str) -> str:
"""程式碼審查模板:檢查指定語言程式碼的品質與安全性。"""
return f"""請對以下 {language} 程式碼進行全面審查,檢查:
1. 程式碼品質:命名慣例、可讀性、DRY 原則
2. 安全性:注入攻擊、敏感資料洩漏
3. 效能:時間/空間複雜度、潛在瓶頸
4. 最佳實踐:錯誤處理、日誌記錄
程式碼:
{code}"""
3.4 安全模型:Client 作為 Guard
MCP 的安全模型將 Client 定位為安全閘道。Server 暴露能力,但所有實際操作都必須經過 Client 的審核。這體現在幾個層面:
- 能力協商:連線建立時,Client 與 Server 交換支援的能力清單,Client 可以拒絕不信任的能力
- 人機迴圈:Tool 呼叫前,Client 可以向使用者顯示即將執行的操作,要求確認
- 資料過濾:Client 可以在 Server 回傳的資料送入模型之前進行過濾或遮蔽
- 進程隔離:stdio 模式下,每個 Server 運行在獨立的子進程中,無法存取其他 Server 的資料
這種設計確保了即使 MCP Server 來自不信任的第三方,Client 仍然可以控制風險邊界[8]。
四、Hands-on Lab 1:用 Python 建構你的第一個 MCP Server
以下實作從零建構一個天氣查詢 MCP Server,使用 Open-Meteo 免費 API(無需 API Key),定義 Tool 與 Resource 兩種能力,並以 stdio transport 運行。所有程式碼可直接在 Google Colab 中執行。
# ============================================================
# Lab 1: 建構天氣查詢 MCP Server
# 環境: Google Colab / Python 3.10+
# ============================================================
# --- 0. 安裝依賴 ---
!pip install -q mcp httpx pydantic
import asyncio
import json
import httpx
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import (
Tool,
TextContent,
Resource,
ResourceTemplate,
)
# --- 1. 建立 MCP Server 實例 ---
server = Server("weather-server")
# --- 2. Open-Meteo API 查詢函式 ---
GEOCODING_URL = "https://geocoding-api.open-meteo.com/v1/search"
WEATHER_URL = "https://api.open-meteo.com/v1/forecast"
async def fetch_coordinates(city: str) -> dict:
"""透過 Open-Meteo Geocoding API 取得城市經緯度。"""
async with httpx.AsyncClient() as client:
resp = await client.get(
GEOCODING_URL,
params={"name": city, "count": 1, "language": "en"}
)
resp.raise_for_status()
data = resp.json()
if not data.get("results"):
raise ValueError(f"找不到城市: {city}")
result = data["results"][0]
return {
"name": result["name"],
"latitude": result["latitude"],
"longitude": result["longitude"],
"country": result.get("country", ""),
}
async def fetch_weather(latitude: float, longitude: float) -> dict:
"""透過 Open-Meteo Forecast API 取得天氣資料。"""
async with httpx.AsyncClient() as client:
resp = await client.get(
WEATHER_URL,
params={
"latitude": latitude,
"longitude": longitude,
"current": "temperature_2m,relative_humidity_2m,"
"wind_speed_10m,weather_code",
"timezone": "auto",
}
)
resp.raise_for_status()
return resp.json()
# WMO 天氣代碼對應描述
WMO_CODES = {
0: "晴天", 1: "大致晴朗", 2: "局部多雲", 3: "多雲",
45: "霧", 48: "沉積霧", 51: "細雨", 53: "中雨",
55: "大雨", 61: "小雨", 63: "中雨", 65: "大雨",
71: "小雪", 73: "中雪", 75: "大雪", 80: "陣雨",
81: "中陣雨", 82: "強陣雨", 95: "雷暴",
}
# --- 3. 定義 Tool: get_weather ---
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="get_weather",
description=(
"查詢指定城市的即時天氣資訊,包含溫度、濕度、風速和天氣狀況。"
"輸入城市英文名稱即可。支援全球任何城市。"
),
inputSchema={
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名稱(英文),例如 Taipei, Tokyo, London"
}
},
"required": ["city"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "get_weather":
city = arguments["city"]
coords = await fetch_coordinates(city)
weather = await fetch_weather(coords["latitude"], coords["longitude"])
current = weather["current"]
code = current.get("weather_code", 0)
description = WMO_CODES.get(code, "未知")
report = (
f"📍 {coords['name']}, {coords['country']}\n"
f"🌡️ 溫度: {current['temperature_2m']}°C\n"
f"💧 相對濕度: {current['relative_humidity_2m']}%\n"
f"💨 風速: {current['wind_speed_10m']} km/h\n"
f"🌤️ 天氣狀況: {description}"
)
return [TextContent(type="text", text=report)]
raise ValueError(f"未知工具: {name}")
# --- 4. 定義 Resource: weather://current/{city} ---
@server.list_resource_templates()
async def list_resource_templates() -> list[ResourceTemplate]:
return [
ResourceTemplate(
uriTemplate="weather://current/{city}",
name="目前天氣",
description="取得指定城市的目前天氣資料(JSON 格式)",
mimeType="application/json",
)
]
@server.read_resource()
async def read_resource(uri: str) -> str:
if uri.startswith("weather://current/"):
city = uri.split("/")[-1]
coords = await fetch_coordinates(city)
weather = await fetch_weather(coords["latitude"], coords["longitude"])
result = {
"city": coords["name"],
"country": coords["country"],
"current": weather["current"],
}
return json.dumps(result, ensure_ascii=False, indent=2)
raise ValueError(f"未知資源 URI: {uri}")
# --- 5. 啟動 Server(stdio transport)---
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(
read_stream,
write_stream,
server.create_initialization_options()
)
# 在 Colab 中測試:直接呼叫工具函式驗證邏輯
async def test_locally():
"""本地測試:直接呼叫內部函式,不走 MCP 協議。"""
print("=== 測試 get_weather ===")
coords = await fetch_coordinates("Taipei")
print(f"座標: {coords}")
weather = await fetch_weather(coords["latitude"], coords["longitude"])
current = weather["current"]
code = current.get("weather_code", 0)
print(f"溫度: {current['temperature_2m']}°C")
print(f"濕度: {current['relative_humidity_2m']}%")
print(f"風速: {current['wind_speed_10m']} km/h")
print(f"天氣: {WMO_CODES.get(code, '未知')}")
print("\n=== 測試 Resource ===")
resource_data = await read_resource("weather://current/Tokyo")
print(resource_data)
# 在 Colab 中執行本地測試
await test_locally()
# 若要以 MCP Server 模式啟動(在終端機中執行):
# asyncio.run(main())
上述程式碼的設計要點:
- Tool vs Resource 的區分:
get_weatherTool 回傳人類可讀的文字摘要,適合模型直接嵌入回答;weather://current/{city}Resource 回傳結構化 JSON,適合應用程式做進一步處理 - WMO 天氣代碼映射:Open-Meteo API 回傳的
weather_code是 WMO 標準代碼,我們將其映射為中文描述 - 非同步設計:MCP SDK 全面使用
async/await,確保高並發場景下的效能 - 本地測試函式:
test_locally()直接呼叫內部邏輯,方便在 Colab 中驗證 API 呼叫是否正確,無需啟動完整的 MCP Server
五、Hands-on Lab 2:建構多工具 MCP Server 與 Client 測試
本實作建構一個包含三個工具(計算器、字數統計、文本翻譯模擬)的 MCP Server,並使用 MCP Python SDK 的 Client 端進行完整的連線測試,展示工具發現、呼叫與結果處理的完整流程。
# ============================================================
# Lab 2: 多工具 MCP Server + Client 完整測試
# 環境: Google Colab / Python 3.10+
# ============================================================
# --- 0. 安裝依賴 ---
!pip install -q mcp pydantic
import asyncio
import json
import math
import re
from io import StringIO
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import (
Tool,
TextContent,
Prompt,
PromptMessage,
PromptArgument,
)
# ==========================================
# Part A: 建構多工具 MCP Server
# ==========================================
server = Server("multi-tool-server")
# --- Tool 1: 科學計算器 ---
def safe_eval_math(expression: str) -> float:
"""安全地計算數學表達式(不使用 eval)。"""
allowed_names = {
"abs": abs, "round": round,
"sin": math.sin, "cos": math.cos, "tan": math.tan,
"sqrt": math.sqrt, "log": math.log, "log10": math.log10,
"pi": math.pi, "e": math.e,
"pow": pow, "ceil": math.ceil, "floor": math.floor,
}
sanitized = expression.replace("^", "**")
code = compile(sanitized, "", "eval")
for name in code.co_names:
if name not in allowed_names:
raise ValueError(f"不允許的函式或變數: {name}")
return eval(code, {"__builtins__": {}}, allowed_names)
# --- Tool 2: 文本分析 ---
def analyze_text(text: str) -> dict:
"""分析文本的字數、字元數、句數等統計資料。"""
chars = len(text)
chars_no_spaces = len(text.replace(" ", ""))
words = len(text.split())
sentences = len(re.split(r'[.!?。!?]', text))
sentences = max(1, sentences - 1)
paragraphs = len([p for p in text.split("\n") if p.strip()])
cjk_chars = len(re.findall(r'[\u4e00-\u9fff]', text))
return {
"characters": chars,
"characters_no_spaces": chars_no_spaces,
"words": words,
"sentences": sentences,
"paragraphs": paragraphs,
"cjk_characters": cjk_chars,
"avg_word_length": round(chars_no_spaces / max(1, words), 1),
}
# --- Tool 3: 翻譯模擬(示範用途)---
TRANSLATION_DICT = {
"hello": "你好", "world": "世界", "ai": "人工智慧",
"model": "模型", "protocol": "協議", "server": "伺服器",
"client": "客戶端", "tool": "工具", "data": "資料",
"context": "上下文", "language": "語言", "learning": "學習",
"machine": "機器", "network": "網路", "computer": "電腦",
}
def simple_translate(text: str, target_lang: str = "zh-TW") -> str:
"""簡易英翻中(示範用途,實際應串接翻譯 API)。"""
if target_lang != "zh-TW":
return f"[僅支援翻譯至 zh-TW,收到: {target_lang}]"
words = text.lower().split()
translated = []
for w in words:
clean = re.sub(r'[^\w]', '', w)
if clean in TRANSLATION_DICT:
translated.append(TRANSLATION_DICT[clean])
else:
translated.append(w)
return " ".join(translated)
# --- 註冊三個 Tools ---
@server.list_tools()
async def list_tools() -> list[Tool]:
return [
Tool(
name="calculator",
description=(
"安全的科學計算器。支援基本運算(+, -, *, /, **)和"
"數學函式(sin, cos, tan, sqrt, log, log10, abs, "
"round, ceil, floor)。常數:pi, e。"
),
inputSchema={
"type": "object",
"properties": {
"expression": {
"type": "string",
"description": "數學表達式,例如 'sqrt(144) + pi * 2'"
}
},
"required": ["expression"]
}
),
Tool(
name="text_analyzer",
description=(
"分析文本的統計資料:字元數、詞數、句數、段落數、"
"CJK 字元數、平均詞長。支援中文與英文混合文本。"
),
inputSchema={
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "要分析的文本內容"
}
},
"required": ["text"]
}
),
Tool(
name="translate",
description=(
"將英文文本翻譯為繁體中文。"
"注意:這是簡易示範版本,僅支援常見 AI 術語的翻譯。"
),
inputSchema={
"type": "object",
"properties": {
"text": {
"type": "string",
"description": "要翻譯的英文文本"
},
"target_language": {
"type": "string",
"description": "目標語言代碼,目前僅支援 zh-TW",
"default": "zh-TW"
}
},
"required": ["text"]
}
),
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
if name == "calculator":
expr = arguments["expression"]
try:
result = safe_eval_math(expr)
return [TextContent(
type="text",
text=f"計算結果: {expr} = {result}"
)]
except Exception as e:
return [TextContent(
type="text",
text=f"計算錯誤: {e}"
)]
elif name == "text_analyzer":
text = arguments["text"]
stats = analyze_text(text)
lines = [
"📊 文本分析結果:",
f" 字元數: {stats['characters']}",
f" 字元數(不含空格): {stats['characters_no_spaces']}",
f" 詞數: {stats['words']}",
f" 句數: {stats['sentences']}",
f" 段落數: {stats['paragraphs']}",
f" CJK 字元數: {stats['cjk_characters']}",
f" 平均詞長: {stats['avg_word_length']}",
]
return [TextContent(type="text", text="\n".join(lines))]
elif name == "translate":
text = arguments["text"]
target = arguments.get("target_language", "zh-TW")
result = simple_translate(text, target)
return [TextContent(
type="text",
text=f"翻譯結果 (en → {target}):\n原文: {text}\n譯文: {result}"
)]
raise ValueError(f"未知工具: {name}")
# --- 註冊 Prompt 模板 ---
@server.list_prompts()
async def list_prompts() -> list[Prompt]:
return [
Prompt(
name="summarize_stats",
description="將文本分析結果整理為簡潔摘要",
arguments=[
PromptArgument(
name="text",
description="要分析並摘要的文本",
required=True,
)
]
)
]
@server.get_prompt()
async def get_prompt(name: str, arguments: dict) -> list[PromptMessage]:
if name == "summarize_stats":
text = arguments["text"]
stats = analyze_text(text)
return [PromptMessage(
role="user",
content=TextContent(
type="text",
text=(
f"以下文本共 {stats['characters']} 字元、"
f"{stats['words']} 詞、{stats['sentences']} 句。"
f"其中 CJK 字元佔 {stats['cjk_characters']} 個。"
f"\n\n請根據以上統計資料,用一段話描述這段文本的特徵。"
f"\n\n原文:\n{text[:500]}"
)
)
)]
raise ValueError(f"未知 Prompt: {name}")
# ==========================================
# Part B: 本地測試(模擬 Client 行為)
# ==========================================
async def test_multi_tool_server():
"""在 Colab 中測試:直接呼叫 Server handler 函式。"""
print("=" * 60)
print("MCP Multi-Tool Server 本地測試")
print("=" * 60)
# 測試 1: 列出所有工具
print("\n--- 1. 列出所有可用工具 ---")
tools = await list_tools()
for t in tools:
print(f" 工具: {t.name}")
print(f" 描述: {t.description[:60]}...")
print()
# 測試 2: 計算器
print("--- 2. 測試 Calculator ---")
result = await call_tool("calculator", {"expression": "sqrt(144) + pi * 2"})
print(f" {result[0].text}")
result = await call_tool("calculator", {"expression": "log10(1000) + 2**10"})
print(f" {result[0].text}")
result = await call_tool("calculator", {"expression": "sin(pi/6)"})
print(f" {result[0].text}")
# 測試 3: 文本分析
print("\n--- 3. 測試 Text Analyzer ---")
sample_text = (
"Model Context Protocol 是 Anthropic 開源的協議標準。"
"它讓 AI 應用能夠透過統一介面連接外部工具與資料來源。"
"MCP 的設計靈感來自 USB-C,一個接口搞定所有連線。"
)
result = await call_tool("text_analyzer", {"text": sample_text})
print(f" {result[0].text}")
# 測試 4: 翻譯
print("\n--- 4. 測試 Translate ---")
result = await call_tool("translate", {
"text": "machine learning model context protocol",
"target_language": "zh-TW"
})
print(f" {result[0].text}")
# 測試 5: Prompt 模板
print("\n--- 5. 測試 Prompt 模板 ---")
prompts = await list_prompts()
print(f" 可用 Prompts: {[p.name for p in prompts]}")
prompt_result = await get_prompt("summarize_stats", {"text": sample_text})
print(f" 展開的 Prompt:\n {prompt_result[0].content.text[:200]}...")
print("\n" + "=" * 60)
print("所有測試通過!Server 邏輯驗證完成。")
print("=" * 60)
# 執行測試
await test_multi_tool_server()
這個 Lab 展示了幾個重要的設計模式:
- 安全計算器:使用
compile()+ 白名單方式取代危險的eval(),只允許預定義的數學函式和常數 - 多語言文本分析:使用正規表達式偵測 CJK(中日韓)字元,支援中英混合文本
- 三大原語齊備:同時實作了 Tools(三個工具)、Prompts(摘要模板),展示完整的 MCP Server 能力
- 測試策略:直接呼叫 handler 函式進行單元測試,無需啟動完整的 MCP 協議棧,適合在 Colab 環境中快速驗證
六、MCP 生態系統:從 Claude 到 Cursor 的整合實踐
MCP 自 2024 年底發布以來,已迅速被多個主流開發工具採用[2]。以下介紹幾個關鍵的整合場景。
6.1 Claude Desktop 配置
Claude Desktop 是第一個原生支援 MCP 的 AI 應用。使用者只需在配置檔中宣告 MCP Server,即可在對話中使用工具:
// macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
// Windows: %APPDATA%/Claude/claude_desktop_config.json
{
"mcpServers": {
"weather": {
"command": "python",
"args": ["/path/to/weather_server.py"],
"env": {}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxxxxxxxxxxx"
}
},
"postgres": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-postgres",
"postgresql://user:pass@localhost:5432/mydb"
]
}
}
}
配置檔以 JSON 格式宣告每個 Server 的啟動指令(command)、參數(args)和環境變數(env)。Claude Desktop 啟動時會自動以 stdio 方式啟動這些 Server 進程。
6.2 Cursor MCP 整合
Cursor IDE 從 0.45 版本開始支援 MCP[15],允許開發者在 AI 輔助編程的過程中調用外部工具。配置方式與 Claude Desktop 類似,放在 .cursor/mcp.json 中。Cursor 目前僅支援 MCP 的 Tools 能力,Resources 和 Prompts 尚未整合。
Windsurf、Zed 編輯器、Sourcegraph Cody 等工具也陸續宣布支援 MCP,顯示這個協議正在成為 AI 開發工具的事實標準。
6.3 社群 MCP Server 生態
MCP 的開放性催生了蓬勃的社群生態。以下是一些受歡迎的開源 MCP Server:
| MCP Server | 功能描述 | 維護者 |
|---|---|---|
| server-github | GitHub API 整合:issues、PR、repo 搜尋 | Anthropic(官方) |
| server-postgres | PostgreSQL 資料庫查詢(唯讀) | Anthropic(官方) |
| server-slack | Slack 頻道訊息讀取與發送 | Anthropic(官方) |
| server-puppeteer | 無頭瀏覽器自動化與網頁截圖 | Anthropic(官方) |
| server-filesystem | 本地檔案系統讀寫(含沙盒限制) | Anthropic(官方) |
| server-brave-search | Brave 搜尋引擎 API 整合 | Anthropic(官方) |
| server-notion | Notion 文件與資料庫操作 | 社群 |
| server-linear | Linear 專案管理工具整合 | 社群 |
6.4 企業自建 MCP Server 的架構考量
對於企業場景,自建 MCP Server 需要考慮以下架構面向:
- 認證與授權:將企業的 SSO/OAuth 整合到 MCP Server 的 transport 層,確保只有授權的 Client 能連線
- 審計日誌:記錄每一次 Tool 呼叫的參數與結果,供合規審計使用
- 速率限制:對高成本的工具(如資料庫查詢、外部 API 呼叫)設定速率限制,避免模型過度呼叫
- 多租戶隔離:在 SSE 模式下,確保不同使用者的資料完全隔離
- 高可用性:以容器化部署 MCP Server,搭配負載平衡器實現故障轉移
七、決策框架:企業何時該採用 MCP
MCP 並非萬能解方。企業在決定是否採用 MCP 時,應根據自身的技術架構和需求場景做出理性判斷。
7.1 適用場景
- 多 AI 應用共享工具:如果企業同時使用 Claude、GPT、本地模型等多個 AI 應用,且希望它們都能存取同一套內部工具,MCP 的統一協議層價值最大
- 快速迭代的工具生態:如果企業的內部工具頻繁增減或更新,MCP 的工具發現機制能減少維護負擔
- IDE 整合開發流程:如果開發團隊使用 Cursor、Windsurf 等支援 MCP 的 IDE,建構 MCP Server 可以直接提升開發效率
- 安全敏感場景:MCP 的 Client-as-guard 模型提供了比直接 API 呼叫更完善的安全邊界
7.2 不適用場景
- 單一 AI 應用 + 少量工具:如果只有一個 AI 應用對接 2-3 個工具,直接使用 Function Calling 更簡單
- 超低延遲需求:MCP 的 JSON-RPC 協議層會引入額外延遲(通常 5-20ms),對於需要毫秒級回應的即時系統可能不適合
- 純批次處理:如果 AI 工具呼叫是離線批次作業,不需要互動式的工具發現和有狀態連線
7.3 比較表:直接 API 整合 vs MCP vs LangChain
| 評估維度 | 直接 API 整合 | MCP | LangChain |
|---|---|---|---|
| 初始開發成本 | 低(最快上線) | 中(需學習協議) | 中(需學習框架) |
| 長期維護成本 | 高(N×M 增長) | 低(N+M 增長) | 中(框架升級風險) |
| 模型可攜性 | 無(綁定特定平台) | 高(廠商中立) | 中(框架抽象層) |
| 生態系統 | 自行建構 | 成長中(1000+ 社群 Server) | 成熟(大量整合) |
| 安全模型 | 自行實作 | 內建(Client guard) | 自行實作 |
| 適合規模 | 小型專案 | 中大型企業 | 中型專案 |
7.4 安全性考量
MCP 的安全模型雖然優於直接 API 整合,但企業仍需注意以下風險[8]:
- 提示注入攻擊(Prompt Injection):惡意的 Resource 內容可能包含注入指令,誘使模型執行非預期的 Tool 呼叫。Yao 等人在 ReAct 研究中[4]強調了 Agent 推理-行動迴圈中的安全邊界問題
- Tool 描述中毒(Tool Poisoning):不受信任的 MCP Server 可能在 Tool 描述中嵌入惡意指令,影響模型的工具選擇行為
- 資料外洩路徑:模型可能將一個 MCP Server 提供的敏感資料,作為參數傳給另一個 MCP Server,形成非預期的資料流動
建議的對策包括:僅使用受信任的 MCP Server 來源、啟用人機迴圈確認機制、限制 Server 的權限範圍、部署輸入輸出的內容過濾。
7.5 遷移路徑建議
對於已有 Function Calling 或 LangChain Tools 整合的企業,建議以下遷移路徑:
- 第一階段(2-4 週):選擇一個低風險的內部工具(如天氣查詢、文件搜尋),建構第一個 MCP Server,驗證團隊對協議的理解
- 第二階段(1-2 個月):將核心業務工具(CRM 查詢、知識庫搜尋)封裝為 MCP Server,在 Claude Desktop 或 Cursor 中測試
- 第三階段(2-3 個月):部署 SSE 模式的共享 MCP Server,整合認證、審計、速率限制等企業級功能
- 第四階段(持續):將現有的 Function Calling 定義逐步遷移為 MCP Tools,建構企業的 MCP Server 目錄
八、MCP 的未來展望與策略價值
8.1 Anthropic 的開放策略
MCP 是 Anthropic 在 AI 生態系統中的一步重要棋局。透過開源協議標準,Anthropic 走了一條與 OpenAI(封閉 Plugins 生態)不同的路線。這背後的策略邏輯是:誰定義了協議,誰就掌握了生態系統的架構權。
這類似於 Google 開源 Android 的策略——不控制每個應用,但透過定義平台規範來建立生態影響力。Anthropic 不需要擁有每個 MCP Server,只要 MCP 成為事實標準,Claude 就自然成為這個生態系統中的首選 AI 應用[2]。
8.2 與 OpenAI Plugins、Google Extensions 的競合
AI 工具整合領域目前存在三股力量:
- OpenAI:從 ChatGPT Plugins(已停止)轉向 GPTs + Actions,仍是封閉平台模式
- Google:Gemini Extensions 提供 Google 服務整合,但缺乏開放的第三方協議
- Anthropic:MCP 走開放協議路線,鼓勵社群和企業建構 Server 生態
Patil 等人的 Gorilla 研究[5]已經證明,當 LLM 能連接大量 API 時,工具使用能力會顯著提升。問題在於誰能建立最大、最開放的工具生態。MCP 的開放設計使其具有先發優勢。
8.3 對 AI Agent 生態的影響
MCP 對 AI Agent 生態有深遠影響。Wang 等人的 LLM Agent 調查研究[8]指出,工具使用能力是 Agent 從「對話系統」升級為「行動系統」的關鍵。MCP 透過標準化工具介面,降低了 Agent 對接外部世界的門檻。
隨著 AutoGPT[13]、AutoGen[14]等 Agent 框架的發展,MCP 可能成為 Agent 存取外部工具的預設協議。這意味著未來的 AI Agent 不再需要為每個工具撰寫定制的整合代碼,而是透過 MCP 動態發現和呼叫工具。
HuggingGPT[7]展示了 LLM 如何作為控制器調度多個 AI 模型完成複雜任務。MCP 可以進一步將這種模式延伸到任意外部工具,不僅限於 AI 模型。WebGPT[9]證明了 LLM 可以有效地使用瀏覽器作為工具;MCP 的 server-puppeteer 正是這種能力的標準化實現。
8.4 企業 CTO 的行動建議
基於以上分析,我們對企業技術決策者提出以下建議:
- 立即行動:在團隊中指定 1-2 位工程師學習 MCP 協議與 SDK,建構第一個內部 MCP Server 作為 POC
- 短期(3-6 個月):評估現有的 AI 工具整合架構,識別哪些整合可以統一為 MCP Server,制定遷移路線圖
- 中期(6-12 個月):建構企業的 MCP Server 目錄與治理框架,包含版本控制、安全審核、效能監控
- 長期觀察:追蹤 MCP 規範的演進(特別是認證標準化、多 Agent 協作協議),以及競爭者(OpenAI、Google)是否推出對應的開放標準
九、結語
MCP 的出現,標誌著 AI 工具整合從「手工藝時代」進入「工業標準化時代」。正如 USB-C 統一了硬體連接介面,MCP 正在統一 AI 與外部世界的軟體連接介面。
然而,協議只是基礎設施。真正的價值在於企業如何在這個基礎設施之上,建構與自身業務深度結合的工具生態。我們在 Tool Learning[6] 和 LangChain[10] 的實踐中已經看到,工具的品質——描述的精確度、錯誤處理的完善度、安全邊界的嚴謹度——直接決定了 AI 應用的可靠性。
MCP 不會取代 Function Calling 或 LangChain Tools,而是在更高的抽象層提供了一個統一的協議框架。對於需要連接多個 AI 應用與多個工具的企業,MCP 提供了一條降低整合複雜度、提升可維護性的清晰路徑。
超智諮詢的研究團隊持續追蹤 MCP 生態的最新發展,並協助企業客戶設計與實作符合其業務需求的 MCP Server 架構。從協議理解到實戰部署,從安全評估到效能最佳化,我們致力於將最前沿的 AI 工程實踐帶入企業場景。