MCP 協議入門:讓 AI 連接外部世界

介紹 Anthropic 的 Model Context Protocol,教你開發 MCP Server 讓 Claude 能存取資料庫、API 等外部資源。

AI Academy 編輯部2025年11月27日

MCP 協議入門:讓 AI 連接外部世界

什麼是 MCP?

MCP(Model Context Protocol)是 Anthropic 推出的開放協議,讓 AI 模型能安全地與外部系統互動。透過 MCP,Claude 可以存取資料庫、呼叫 API、讀取檔案等,大幅擴展其能力。

MCP 架構

┌─────────────┐     MCP 協議     ┌─────────────────┐
│   Claude    │ ←─────────────→ │   MCP Server    │
│  (Client)   │                 │ (你開發的服務)  │
└─────────────┘                 └────────┬────────┘
                                         │
                                         ▼
                                ┌─────────────────┐
                                │   外部資源      │
                                │ (DB、API、檔案) │
                                └─────────────────┘

MCP 的優勢

特點說明
標準化統一的協議,一次開發多處使用
安全明確的權限控制
模組化每個 Server 專注一個功能
開放開源協議,社群共建

核心概念

MCP Server 可以提供三種類型的功能:

1. Tools(工具)

讓 AI 執行操作:

- 查詢資料庫
- 發送郵件
- 呼叫外部 API
- 執行程式碼

2. Resources(資源)

提供資料給 AI 讀取:

- 檔案內容
- 資料庫記錄
- API 回應
- 系統狀態

3. Prompts(提示模板)

預設的提示範本:

- 程式碼審查模板
- 文件撰寫模板
- 分析報告模板

快速開始:建立第一個 MCP Server

1. 環境準備

# 安裝 MCP SDK
pip install mcp

2. 建立簡單的天氣查詢 Server

# weather_server.py
from mcp.server import Server
from mcp.types import Tool, TextContent
import httpx

# 建立 Server 實例
server = Server("weather-server")

@server.list_tools()
async def list_tools():
    """列出可用的工具"""
    return [
        Tool(
            name="get_weather",
            description="查詢指定城市的天氣",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名稱,例如:Taipei"
                    }
                },
                "required": ["city"]
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    """執行工具"""
    if name == "get_weather":
        city = arguments["city"]

        # 呼叫天氣 API(這裡用模擬資料)
        weather_data = {
            "Taipei": {"temp": 28, "condition": "晴天"},
            "Tokyo": {"temp": 22, "condition": "多雲"},
            "New York": {"temp": 15, "condition": "下雨"},
        }

        if city in weather_data:
            data = weather_data[city]
            return [TextContent(
                type="text",
                text=f"{city} 目前天氣:{data['condition']},氣溫 {data['temp']}°C"
            )]
        else:
            return [TextContent(
                type="text",
                text=f"找不到 {city} 的天氣資料"
            )]

    raise ValueError(f"Unknown tool: {name}")

# 啟動 Server
if __name__ == "__main__":
    import asyncio
    from mcp.server.stdio import stdio_server

    asyncio.run(stdio_server(server))

3. 設定 Claude Desktop

在 Claude Desktop 設定檔加入 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"]
    }
  }
}

4. 使用

重啟 Claude Desktop 後,你可以直接問:

「台北現在天氣如何?」

Claude 會自動呼叫你的 MCP Server 來取得天氣資訊。

實用範例:資料庫查詢 Server

# db_server.py
from mcp.server import Server
from mcp.types import Tool, TextContent, Resource
import sqlite3

server = Server("database-server")

# 資料庫連線
def get_db():
    return sqlite3.connect("app.db")

@server.list_tools()
async def list_tools():
    return [
        Tool(
            name="query_users",
            description="查詢用戶資料",
            inputSchema={
                "type": "object",
                "properties": {
                    "email": {
                        "type": "string",
                        "description": "用戶 email(可選)"
                    },
                    "limit": {
                        "type": "integer",
                        "description": "回傳筆數限制",
                        "default": 10
                    }
                }
            }
        ),
        Tool(
            name="count_users",
            description="統計用戶數量",
            inputSchema={
                "type": "object",
                "properties": {}
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    db = get_db()
    cursor = db.cursor()

    try:
        if name == "query_users":
            email = arguments.get("email")
            limit = arguments.get("limit", 10)

            if email:
                cursor.execute(
                    "SELECT id, name, email FROM users WHERE email = ? LIMIT ?",
                    (email, limit)
                )
            else:
                cursor.execute(
                    "SELECT id, name, email FROM users LIMIT ?",
                    (limit,)
                )

            rows = cursor.fetchall()
            result = "\n".join([f"ID: {r[0]}, Name: {r[1]}, Email: {r[2]}" for r in rows])
            return [TextContent(type="text", text=result or "沒有找到用戶")]

        elif name == "count_users":
            cursor.execute("SELECT COUNT(*) FROM users")
            count = cursor.fetchone()[0]
            return [TextContent(type="text", text=f"目前共有 {count} 位用戶")]

    finally:
        db.close()

    raise ValueError(f"Unknown tool: {name}")

提供 Resources

讓 Claude 能讀取檔案或資料:

@server.list_resources()
async def list_resources():
    """列出可用資源"""
    return [
        Resource(
            uri="config://app",
            name="應用程式設定",
            description="應用程式的設定檔內容",
            mimeType="application/json"
        )
    ]

@server.read_resource()
async def read_resource(uri: str):
    """讀取資源內容"""
    if uri == "config://app":
        with open("config.json", "r") as f:
            return f.read()

    raise ValueError(f"Unknown resource: {uri}")

常用 MCP Servers

社群已經開發了許多實用的 MCP Server:

Server功能
mcp-server-sqliteSQLite 資料庫操作
mcp-server-filesystem檔案系統存取
mcp-server-githubGitHub API 整合
mcp-server-slackSlack 訊息發送
mcp-server-puppeteer網頁自動化

安裝社群 Server

# 使用 npx 安裝
npx @anthropic/mcp-server-sqlite --db-path ./my-database.db

設定多個 Server

{
  "mcpServers": {
    "sqlite": {
      "command": "npx",
      "args": ["@anthropic/mcp-server-sqlite", "--db-path", "./app.db"]
    },
    "filesystem": {
      "command": "npx",
      "args": ["@anthropic/mcp-server-filesystem", "--root", "./documents"]
    },
    "weather": {
      "command": "python",
      "args": ["./my-servers/weather_server.py"]
    }
  }
}

安全考量

1. 權限控制

# 限制可存取的目錄
ALLOWED_PATHS = ["/data", "/uploads"]

def is_path_allowed(path: str) -> bool:
    return any(path.startswith(p) for p in ALLOWED_PATHS)

2. 輸入驗證

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    # 驗證輸入
    if name == "query_db":
        query = arguments.get("query", "")

        # 防止 SQL Injection
        if any(keyword in query.upper() for keyword in ["DROP", "DELETE", "UPDATE"]):
            return [TextContent(type="text", text="不允許的操作")]

3. 資源限制

import asyncio

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    # 設定超時
    try:
        result = await asyncio.wait_for(
            do_operation(arguments),
            timeout=30.0
        )
        return result
    except asyncio.TimeoutError:
        return [TextContent(type="text", text="操作超時")]

Debug 技巧

查看 Server 日誌

import logging

logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    logger.debug(f"Tool called: {name} with {arguments}")
    # ...

測試 Server

# test_server.py
import asyncio
from weather_server import server

async def test():
    # 測試列出工具
    tools = await server.list_tools()
    print("Available tools:", tools)

    # 測試呼叫工具
    result = await server.call_tool("get_weather", {"city": "Taipei"})
    print("Result:", result)

asyncio.run(test())

總結

MCP 讓 Claude 能安全地與外部系統互動:

  1. Tools:執行操作(查詢、發送、計算)
  2. Resources:提供資料(檔案、設定)
  3. Prompts:預設模板

下一步行動

  1. 安裝 Claude Desktop 並設定 MCP
  2. 嘗試使用社群的 MCP Server
  3. 開發自己的第一個 MCP Server
  4. 整合到你的工作流程中

相關文章

參考資源