Featured image of post 實戰 MCP 伺服端:STDIO 模式

實戰 MCP 伺服端:STDIO 模式

本文將實現一個簡易的 MCP 伺服器,讓 AI 代理能夠查詢即時的加密貨幣價格。通過這個實現,你將學習到如何建立一個 MCP 伺服器,以及了解 MCP 協定的具體運作機制,包括 MCP 客戶端如何與 MCP 伺服器協作以及資料傳遞的格式。

客戶端與伺服端之生命週期

MCP 協定定義了 MCP 客戶端與伺服端之間的三個不同的階段,確保它們之間的協作是穩定和安全的。這三個階段包括:

  • 初始化階段:MCP 客戶端向 MCP 伺服端發送初始化請求,通知其完成初始化並準備處理客戶端的請求。
  • 操作階段:MCP 客戶端向 MCP 伺服端發送請求,要求其執行各種任務,例如列出所有工具或使用特定工具。
  • 關閉階段:MCP 客戶端向 MCP 伺服端發送通知,表示其將斷開連線。

通訊資料格式:JSON-RPC

MCP 協定採用 JSON-RPC 2.0 作為 MCP 客戶端和 MCP 伺服端之間的資料交換和協作格式。

MCP Client 發出的資料格式如下:

{
  jsonrpc: "2.0",
  id: number | string,
  method: string,
  params?: object
}

其中:

  • jsonrpc:JSON-RPC 版本號,固定為 “2.0”。
  • id:請求的唯一識別號,可以是數字或字符串。
  • method:請求的方法名稱。
  • params:請求的參數,為可選項。

MCP Server 回覆的資料格式如下:

{
  jsonrpc: "2.0",
  id: number | string,
  result?: object,
  error?: {
    code: number,
    message: string,
    data?: unknown
  }
}

其中:

  • jsonrpc:JSON-RPC 版本號,固定為 “2.0”。
  • id:請求的唯一識別號,可以是數字或字符串。
  • result:回覆的結果,為可選項。
  • error:錯誤信息,為可選項。

初始化範例

如果您要通知 MCP Server 進行初始化,可以傳遞以下資料給它:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": {
        "listChanged": true
      },
      "sampling": {}
    },
    "clientInfo": {
      "name": "ExampleClient",
      "version": "1.0.0"
    }
  }
}

MCP Server 會回傳以下資料給您,讓你知道它完成了初始化:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "logging": {},
      "prompts": {
        "listChanged": true
      },
      "resources": {
        "subscribe": true,
        "listChanged": true
      },
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "ExampleServer",
      "version": "1.0.0"
    },
    "instructions": "Optional instructions for the client"
  }
}

實作簡易 MCP 伺服端

STDIO 與 SSE 模式

在實作 MCP 伺服端之前,開發者必須先決定它將會在使用者的本地端或是雲端運行。MCP 協議提供兩種模式來滿足不同的情況:

  • STDIO 模式:MCP 伺服端運行在使用者本地端,MCP 客戶端透過終端機的標準輸入/輸出與 MCP 伺服端相互溝通。
  • SSE 模式:MCP 伺服端運行在雲端,MCP 客戶端透過 Server-Sent Events (SSE) 伺服器推送技術來獲得即時的 MCP 伺服端回應。

由於大部分的 MCP 伺服端都是 STDIO 模式的,因此本文將採用 STDIO 模式來實作一個 MCP 伺服端。

加密貨幣價格查詢工具

AI 模型本身無法得知現實世界的資料或直接對某些事情進行操作。為了讓 AI 模型獲得即時的加密貨幣價格,我們需要幫它加載一個 MCP 伺服端。這裡將使用 Python 的 FastMCP 函式庫實作,讓我們可以快速簡單地完成這個 MCP 伺服端的開發。

首先,讓我們建構一個名為 crypto 的 FastMCP 物件:

import os
import requests
from fastmcp import FastMCP

mcp = FastMCP("crypto")

接下來,只要實作一個函式就可以完成這個 MCP 伺服端的開發了。實作函式的重點是加入註解說明這個函式的用途,以及它的相關參數。這樣 AI 模型就可以知道該如何使用這個函式(工具):

@mcp.tool()
def get_price(symbol: str) -> int:
    """Get the current price of the given cryptocurrency.
    Args:
        symbol: The symbol name of a certain cryptocurrency (e.g. BTC, ETH, ADA)
    """
    try:
        headers = {"X-CMC_PRO_API_KEY": os.environ["CMC_PRO_API_KEY"]}
        response = requests.get(f"https://pro-api.coinmarketcap.com/v2/cryptocurrency/quotes/latest?symbol={symbol}", headers=headers)
        data = response.json()
        price = data["data"][symbol][0]["quote"]["USD"]["price"]
        return price if price is not None else -1
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
        return -1

最後,指定這個 MCP 伺服端會以 STDIO 模式運行,就完成了這個 MCP 伺服端的開發:

if __name__ == "__main__":
    mcp.run(transport="stdio")

手動驅動 MCP 伺服端

為了更深入地了解整個 MCP 客戶端與伺服端的生命週期,我們將不使用 MCP Host 來引入 MCP 伺服端,而是直接在終端機中透過標準輸入/輸出機制來請求 MCP 伺服端工作並獲得回應。

假設 MCP 伺服端的程式碼存在 main.py 中,先切換到該檔案的存放資料夾,然後輸入 export CMC_PRO_API_KEY="your-api-key" && uv run main.py。您將會看到以下的訊息出現在終端機中:

[05/02/25 19:01:31] INFO     Starting server "crypto"...

接下來,使用帶有 "method": "initialize" 的 JSON 資料來初始化 crypto MCP 伺服端。複製以下字串,直接貼入終端機並輸入:

{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"MCP Client","version":"1.0.0"}}}

你將會在終端機中看到 MCP 伺服端有以下的回應,表示它已經完成了初始化:

{
    "jsonrpc": "2.0",
    "id": 1,
    "result": {
        "protocolVersion": "2024-11-05",
        "capabilities": {
            "experimental": {},
            "prompts": {
                "listChanged": false
            },
            "resources": {
                "subscribe": false,
                "listChanged": false
            },
            "tools": {
                "listChanged": false
            }
        },
        "serverInfo": {
            "name": "crypto",
            "version": "1.6.0"
        }
    }
}

然後,使用以下的訊息輸入至終端機,通知 MCP 伺服端你準備要開始進行任務的要求:

{"jsonrpc":"2.0","method":"notifications/initialized"}

最後,使用以下的訊息輸入至終端機,調用名為 get_price 的函式,去查詢即時的比特幣價格:

{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_price","arguments":{"symbol":"BTC"}}}

這個過程可以想像成當有一個使用者詢問 AI 模型最近的比特幣價格,AI 模型會自動生成 {"symbol": "BTC"} JSON 資料,並且告知 MCP 客戶端去呼叫 MCP 伺服端中的 get_price 工具,以獲得最新的比特幣價格。

在終端機中,您將會看到以下的訊息,其中 result 中的 97026.50588164855 就是當時的比特幣價格:

{
    "jsonrpc": "2.0",
    "id": 2,
    "result": {
        "content": [
            {
                "type": "text",
                "text": "97026.50588164855"
            }
        ],
        "isError": false
    }
}

總結

  • MCP 協定的生命週期:MCP 協議的三個生命週期,包括初始化、操作和關閉階段,為 MCP Client 和 Server 之間的溝通提供了明確的框架。
  • JSON-RPC 2.0 資料格式:JSON-RPC 2.0 定義了 MCP Client 和 Server 之間的資料格式,確保了兩者之間的溝通是標準化和可靠的。
  • MCP 伺服端的工具集:MCP 伺服端中的一個函式就是一個工具,因此一個 MCP 伺服端就是一組幫助 AI 模型完成特定任務的工具集。這種設計使得 MCP 伺服端可以提供多種功能和服務,以支持 AI 模型的運作。

參考資料

  1. Lifecycle - Model Context Protocol
  2. Transports - Model Context Protocol
  3. Women Working in the Kitchen of a Farmhouse near Olevano, Italy (1845 – 1848)
comments powered by Disqus
使用 Hugo 建立
主題 StackJimmy 設計