Key Findings
  • 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 存在三個結構性限制:

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/docdb://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,例如:

每個 Resource 附帶 MIME 類型(如 text/plainapplication/jsonimage/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 的審核。這體現在幾個層面:

這種設計確保了即使 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())

上述程式碼的設計要點:

五、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 展示了幾個重要的設計模式:

六、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 需要考慮以下架構面向:

七、決策框架:企業何時該採用 MCP

MCP 並非萬能解方。企業在決定是否採用 MCP 時,應根據自身的技術架構和需求場景做出理性判斷。

7.1 適用場景

7.2 不適用場景

7.3 比較表:直接 API 整合 vs MCP vs LangChain

評估維度 直接 API 整合 MCP LangChain
初始開發成本 低(最快上線) 中(需學習協議) 中(需學習框架)
長期維護成本 高(N×M 增長) 低(N+M 增長) 中(框架升級風險)
模型可攜性 無(綁定特定平台) 高(廠商中立) 中(框架抽象層)
生態系統 自行建構 成長中(1000+ 社群 Server) 成熟(大量整合)
安全模型 自行實作 內建(Client guard) 自行實作
適合規模 小型專案 中大型企業 中型專案

7.4 安全性考量

MCP 的安全模型雖然優於直接 API 整合,但企業仍需注意以下風險[8]

建議的對策包括:僅使用受信任的 MCP Server 來源、啟用人機迴圈確認機制、限制 Server 的權限範圍、部署輸入輸出的內容過濾。

7.5 遷移路徑建議

對於已有 Function Calling 或 LangChain Tools 整合的企業,建議以下遷移路徑:

  1. 第一階段(2-4 週):選擇一個低風險的內部工具(如天氣查詢、文件搜尋),建構第一個 MCP Server,驗證團隊對協議的理解
  2. 第二階段(1-2 個月):將核心業務工具(CRM 查詢、知識庫搜尋)封裝為 MCP Server,在 Claude Desktop 或 Cursor 中測試
  3. 第三階段(2-3 個月):部署 SSE 模式的共享 MCP Server,整合認證、審計、速率限制等企業級功能
  4. 第四階段(持續):將現有的 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 工具整合領域目前存在三股力量:

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. 立即行動:在團隊中指定 1-2 位工程師學習 MCP 協議與 SDK,建構第一個內部 MCP Server 作為 POC
  2. 短期(3-6 個月):評估現有的 AI 工具整合架構,識別哪些整合可以統一為 MCP Server,制定遷移路線圖
  3. 中期(6-12 個月):建構企業的 MCP Server 目錄與治理框架,包含版本控制、安全審核、效能監控
  4. 長期觀察:追蹤 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 工程實踐帶入企業場景。