用 Python 開發 MCP 服務很簡單,完整案例拿走不謝!

模型上下文協議 MCP 是一種開放協議,旨在標準化 AI 應用程序與外部數據源和工具的連接方式。其核心目標在於簡化大型語言模型 LLM 與各種上下文和工具的集成,從而解決將多種 LLM 與多種工具相集成的複雜性問題。

但你有沒有想過,自己動手打造一個 MCP 服務呢?想象一下,親手設計一個智能工具,無論是處理數據、調用外部 API,還是生成自定義報告。這種創造力和掌控力的體驗,會不會更香呢?

MCP 本身是一個通用的協議,設計上是爲了跨語言和跨平臺的,因此可以使用各種語言來編寫服務,比如常用的 Node.js。但如果不熟悉 Web 開發,也想自己寫一寫呢?Python 自然是不二之選。只是 MCP 具體的協議挺複雜的,很難快速上手。

不過,現在好了,因爲有了 FastMCP,它是一個 Python 軟件開發工具包 (SDK),專門設計用於簡化構建 MCP 服務的過程。它提供了一個高級且符合 Python 語言習慣的接口,用於定義工具、資源和提示。FastMCP 的核心優勢在於其能夠幫助開發者更輕鬆地創建符合 MCP 規範的服務,而無需深入瞭解底層的協議細節。

但值得注意的是,你看網上代碼會涉及兩個 FastMCP 包,其中一個是,

https://github.com/jlowin/fastmcp

另一個則是官方的,

https://github.com/modelcontextprotocol/python-sdk

這兩個什麼關係呢?其實是官方收編了上面第一個包,但官方集成的是 fastmcp 的 v1.0 版本。然而,jlowin 繼續開發 fastmcp,還發布了 v2.0 版本,其中包含代理和客戶端採樣等新功能。

使用的話這兩個都可以,先看第一個的安裝和導入,

pip install fastmcp
from fastmcp import FastMCP

官方的安裝和導入如下所示,

pip install mcp
from mcp.server.fastmcp import FastMCP

本文使用官方 Python SDK 裏的 FastMCP 來構建自己的 MCP 服務。

我們先來看一個最簡單的例子。

./demo/server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("Demo 🚀")

@mcp.tool()
def add_2_numbers(a: int, b: int) -> int:
    """兩個數字相加"""
    return a + b

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

這個例子展示瞭如何使用 FastMCP 庫來創建一個簡單的 MCP 服務器,它提供一個基本的工具(工具名:add_2_numbers),用於兩個數字相加。這是一個非常簡單的入門示例,適合理解 MCP 服務器的工作原理和 FastMCP 的基本用法。

運行效果:

運行 python ./demo/server.py,服務器會啓動並監聽 stdio。任何支持 MCP 協議的客戶端(例如 MCP Inspector 或 Claude Desktop)都可以連接到它,調用 add_2_numbers 工具。

比如,可以通過 MCP Inspector 測試一下這個工具。

% mcp dev ./demo/server.py
Need to install the following packages:
@modelcontextprotocol/inspector@0.8.2
Ok to proceed? (y) y

瀏覽器中打開 http://127.0.0.1:6274,可以測試一下上面這個 mcp 服務裏提供的 tool。

這個 mcp 過於簡單,咱們就不安裝來用了。下面我們手擼一個稍微複雜一點的 mcp 服務,並安裝上真正使用起來。

記賬 mcp 服務

我們基於 MCP 開發了一個輕量級(玩具級)記賬服務 Money Track MCP,旨在幫助用戶管理和跟蹤財務數據。該服務利用 Python 和 FastMCP 框架,提供了三種核心功能類型(工具、資源和提示),爲用戶提供一個靈活、高效的解決方案來記錄收入和支出、查詢賬戶狀態以及生成格式化的財務報告。

項目雖小,但涉及環境變量的設置和讀取,用戶數據的存儲等多個方面,對於開發更強大服務來說是一個不錯的基礎。完整代碼的地址見文末。

核心功能

  1. 工具 (@tool):
    服務提供了一個名爲 record_transaction 的工具,允許用戶動態輸入當天的收入和支出,如「今天賺了 500 元,花了 250 元」。工具會自動更新累積的總收入、總支出,並計算當前餘額。這些數據持久化存儲在用戶指定的文件中,確保數據不會丟失。

  2. 資源 (@resource):
    通過 get_account_status 資源,用戶可以安全地檢索當前賬戶的最新狀態,包括總收入、總支出和餘額。資源數據存儲在 JSON 文件中,文件路徑可以由用戶通過環境變量或配置文件自定義,默認位於 ./accounting_data/accounting_data.json

  3. 提示 (@prompt):
    format_account_report 提示負責將賬戶狀態格式化爲易讀的報告。

技術架構

使用場景

MoneyTrack MCP 適用於多種場景,包括但不限於:

具體代碼如下,

# ./src/server.py
from mcp.server.fastmcp import FastMCP
import os
import json
from typing import Dict, Optional
import logging

# 配置日誌
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 從環境變量或默認值獲取工作目錄
DEFAULT_WORKING_DIR = "~/accounting_data"
WORKING_DIR = os.getenv("ACCOUNTING_WORKING_DIR", DEFAULT_WORKING_DIR)

# 確保目錄存在
os.makedirs(WORKING_DIR, exist_ok=True)

# 數據文件路徑
DATA_FILE = os.path.join(WORKING_DIR, "accounting_data.json")

# 初始數據(如果文件不存在)
INITIAL_DATA = {
    "total_income": 0,
    "total_expense": 0,
    "balance"0
}

def load_data() -> Dict:
    """從文件中加載數據,如果文件不存在則創建默認數據"""
    try:
        if os.path.exists(DATA_FILE):
            with open(DATA_FILE, "r", encoding="utf-8") as f:
                return json.load(f)
        else:
            with open(DATA_FILE, "w", encoding="utf-8") as f:
                json.dump(INITIAL_DATA, f, indent=4)
            return INITIAL_DATA
    except Exception as e:
        logger.error(f"Failed to load data: {str(e)}")
        return INITIAL_DATA

def save_data(data: Dict) -> None:
    """將數據保存到文件"""
    try:
        with open(DATA_FILE, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=4)
    except Exception as e:
        logger.error(f"Failed to save data: {str(e)}")

# 創建 MCP 服務器
mcp = FastMCP("money-track-mcp", log_level="ERROR")

# 工具:記錄收入和支出
@mcp.tool()
def record_transaction(income: Optional[int] = 0, expense: Optional[int] = 0) -> Dict:
    """記錄今天的收入和支出,更新賬戶餘額"""
    ifnot isinstance(income, (int, float)) ornot isinstance(expense, (int, float)):
        return {"error""Income and expense must be numbers"}

    data = load_data()
    data["total_income"] += income
    data["total_expense"] += expense
    data["balance"] = data["total_income"] - data["total_expense"]
    save_data(data)

    return {
        "message""Transaction recorded successfully",
        "total_income": data["total_income"],
        "total_expense": data["total_expense"],
        "balance": data["balance"]
    }

# 資源:獲取當前賬戶狀態
@mcp.resource("accounting://status")
def get_account_status() -> Dict:
    """獲取當前賬戶的收入、支出和餘額"""
    data = load_data()
    return {
        "total_income": data["total_income"],
        "total_expense": data["total_expense"],
        "balance": data["balance"]
    }

# 提示:格式化賬戶報告
@mcp.prompt()
def format_account_report(status: Dict) -> str:
    """格式化賬戶狀態爲易讀的報告"""
    returnf"""
    === 賬戶報告 ===
    總收入: ${status["total_income"]:.2f}
    總支出: ${status["total_expense"]:.2f}
    當前餘額: ${status["balance"]:.2f}
    ================
    """

def run_server():
    """運行 MCP 服務器"""
    print("=== Money Track MCP 服務啓動 ===")
    logging.info("Money Track MCP 服務啓動")
    print(f"當前工作目錄: {os.getcwd()}")

    mcp.run(transport='stdio')

安裝和運行

git clone https://github.com/mathinml/mcp_money_track.git
cd mcp_money_track
pip install mcp
pip install .

打開你喜歡的 mcp 客戶端,比如 ClineRoo CodeCursor 等等。mcp 配置文件中請參考如下設置,

"money-track-mcp": {
      "command""/opt/anaconda3/bin/python",
      "args": [
        "-m",
        "mcp_money_track"
      ],
      "env": {
        "ACCOUNTING_WORKING_DIR""/Users/xyz/account"
      }
    },

環境變量 "ACCOUNTING_WORKING_DIR" 是用於設置賬本的保存目錄。

1、Cline

點擊 Approve 以後,

再次輸入:我今天收入才 500 元,花掉了 1500 元。

沒多說啥,它自動領會要記賬啦,請看結果。

打開保存目錄裏的賬本看一看,

2、Roo Code

然後在另一個客戶端裏試試,VS Code + 插件 Roo Code。

換套說辭,咱不提記賬不記賬,只跟它說:我想買個電腦,1 萬元左右,看看我的餘額還夠嗎?

點擊 Approve,如果覺得每次要點太煩人,也可以在 Auto-approve 裏設定總是允許。

餘額不夠啦,誰打賞一點讓我湊個整唄。

3、Cherry Studio

在 Cherry Studio 裏設置也是類似的,如圖。

然後,在聊天界面一定要在 MCP 服務器裏選中這個服務。

大模型這裏我們選用 OpenRoute 提供的免費 Gemini 2.5 Pro,

也還行,速度上感覺比前面兩個慢一丟丟。

好了,通過這個例子,應該已經明白如何使用 Python 來寫一個簡單但完整的 mcp 服務。

代碼:https://github.com/mathinml/mcp_money_track

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/g_A43tlarARXcWrcf8Hx-g