用 Python 开发 MCP 服务很简单,完整案例拿走不谢!

发布于 2025-4-28 00:16
浏览
0收藏

模型上下文协议 MCP 是一种开放协议,旨在标准化 AI 应用程序与外部数据源和工具的连接方式。其核心目标在于简化大型语言模型 LLM 与各种上下文和工具的集成,从而解决将多种 LLM 与多种工具相集成的复杂性问题。

之前的文章介绍了一个挺不错的 MCP 应用案例,

​用 MCP 让大模型自动批量下载文献,太香了!​

但你有没有想过,自己动手打造一个 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 的基本用法。

  • 首先,从​​mcp.server.fastmcp​​ 模块中导入了 FastMCP 类。接着,创建了一个 FastMCP 类的实例,命名为 mcp。FastMCP 是服务器的核心类,它负责管理工具、资源和通信。参数「Demo 🚀」是服务器的名称,用于标识这个 MCP 服务器。
  • 然后,​​@mcp.tool()​​​ 是一个装饰器(​​decorator​​​),它告诉 FastMCP 将​​add_2_numbers​​​ 函数注册为一个可供客户端调用的工具。文档字符串("""两个数字相加"""):这是函数的描述,客户端(如​​MCP Inspector​​)会显示这个描述,帮助用户理解工具的功能。
  • 这个工具可以被外部客户端调用,例如通过 MCP 协议发送请求,传入两个整数 a 和 b,服务器会返回它们的和。例如,如果客户端调用​​add_2_numbers(3, 5)​​,服务器会返回 8。
  • ​mcp.run(transport='stdio')​​​ 启动 MCP 服务器,进入监听状态,等待客户端的连接和请求。​​transport='stdio'​​​ 指定了通信方式为标准输入输出(​​Standard I/O​​​)。这意味着服务器通过命令行的标准输入(stdin)和标准输出(stdout)与客户端通信,适合本地开发和测试。如果需要远程通信,可以更改为其他传输方式(如​​'sse'​​​,​​Server-Sent Events​​)。
运行效果:

运行 ​​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

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

浏览器中打开 ​​http://127.0.0.1:6274​​,可以测试一下上面这个 mcp 服务里提供的 tool。

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

这个 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​​ 提示负责将账户状态格式化为易读的报告。

技术架构

  • 框架:基于​​FastMCP​​,一个高效的 Python 库,简化了 MCP 服务器的开发。
  • 存储:数据保存在 JSON 文件中,支持用户自定义存储路径(如通过环境变量​​ACCOUNTING_WORKING_DIR​​ 或配置文件)。
  • 通信:默认使用标准输入输出 (​​stdio​​) 传输协议,方便本地开发和测试。
  • 日志:支持​​INFO​​​ 和​​DEBUG​​ 级别的日志记录,帮助开发者监控服务运行状态和调试问题。

使用场景

​MoneyTrack MCP​​ 适用于多种场景,包括但不限于:

  • 个人财务管理:个人用户可以通过客户端记录日常收支,并随时查看余额。
  • 企业/团队记账:小型企业或团队可以集成此服务到更大的财务系统中,快速统计现金流。
  • 教育与开发示例:开发者可以作为 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 客户端,比如 ​​Cline​​​,​​Roo Code​​​, ​​Cursor​​ 等等。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

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

点击 Approve 以后,

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

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

没多说啥,它自动领会要记账啦,请看结果。

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

打开保存目录里的账本看一看,

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

2、Roo Code

然后在另一个客户端里试试,VS Code + 插件 Roo Code。

换套说辞,咱不提记账不记账,只跟它说:我想买个电脑,1 万元左右,看看我的余额还够吗?

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

点击 Approve,如果觉得每次要点太烦人,也可以在 Auto-approve 里设定总是允许。

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

余额不够啦,谁打赏一点让我凑个整呗。

3、Cherry Studio

在 Cherry Studio 里设置也是类似的,如图。

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

然后,在聊天界面一定要在 MCP 服务器里选中这个服务。

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

大模型这里我们选用 OpenRoute 提供的免费 Gemini 2.5 Pro,

用 Python 开发 MCP 服务很简单,完整案例拿走不谢!-AI.x社区

也还行,速度上感觉比前面两个慢一丢丢。

好了,通过这个例子,应该已经明白如何使用 Python 来写一个简单但完整的 mcp 服务。

代码:​​https://github.com/mathinml/mcp_money_track​

本文转载自​机器学习与数学​,作者:大师兄

已于2025-4-28 09:39:48修改
收藏
回复
举报
回复
相关推荐