本文將實現一個簡易的 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 模型的運作。