动机

前文曾提到可以使用MCPM管理各种现成的MCP服务以使得大语言模型接入许多现成工具,那么自然会想要自己写一个适合自身需求的工具来让大模型调用,本文就来简单记录一下这个过程。

关于MCP服务的编写,GitHub上已经有了现成的仓库了:

作为一名练习时长两个两年半的Python练习生,我选择了其中的Python SDK

配置环境

这个Python SDK支持的Python版本为:>=3.10,并且最好使用uv管理环境。

因此先安装uv

curl -LsSf https://astral.sh/uv/install.sh | sh

uv安装完成后,初始化项目目录:

uv init mcp_server

该操作会生成一些配置文件,以及一个虚拟环境,检查.python-version以及pyproject.toml里面Python的版本,需要>=3.10,如不满足则手动修改,然后运行uv venv,即可重置环境。

接下来安装这个SDK:

uv add "mcp[cli]"

编写代码

我希望让LLM为我处理一个PDF格式的账单文件,将它其中的数据条目进行细致的分类,再生成csv,因此我额外安装了两个库:

uv add pdfplumber pandas

编写cash_classifier.py

from mcp.server.fastmcp import FastMCP
import os
import json

import pdfplumber
import logging
logging.getLogger("pdfminer").setLevel(logging.ERROR)

mcp = FastMCP('cash-classifier')


@mcp.tool()
async def parse_cashbook(filapath):
    """解析pdf账单,将表格提取出来
    Args:
        filepath: 字符串,账单文件绝对路径
    """
    with pdfplumber.open(filapath) as pdf:
        data = []
        headers = ['交易时间', '收/支', '交易分类', '收/付款方式', '金额', '流水归属', '交易对方', '备注']
        for page in pdf.pages:
            table = page.extract_tables()[0]
            for row in table[1:]:
                number = float(row[3].replace(',', ''))
                data.append([
                    row[0],
                    '支出' if number < 0 else '收入',
                    row[5],
                    row[6],
                    abs(number),
                    '',
                    row[9],
                    row[8]
                ])
            data.append(table[1:])
    return json.dumps({
        'headers': headers,
        'data': data
    }, ensure_ascii=False, indent=None, separators=(',', ':'))

这里暂时只实现了解析PDF文件并转为JSON字符串的逻辑。

配置Claude Desktop

在Claude Desktop的配置文件(claude_desktop_config.json)中添加一项:

{
  "mcpServers": {
    ...
    "cash-classifier": {
      "command": "/absolute/path/to/uv",
      "args": [
        "run",
        "--project",
        "/absolute/path/to/project/dir/mcp_server",
        "mcp",
        "run",
        "/absolute/path/to/project/dir/mcp_server/cash_classifier.py"
      ]
    }
  }
}

这里有三个绝对路径需要替换,分别是uv的绝对路径、通过uv初始化生成的项目目录mcp_server的绝对路径,以及前面创建的cash_classifier.py文件的绝对路径。如使用相对路径则会失败(

接下来打开Claude Desktop,并让它解析账单内容,Claude会作出回应调用我们刚刚写的函数:

image-20250505234328124

可见Claude已经成功调用我们写的函数,解析得到账单的信息了,然而:

image-20250505234414773

草。因此后续的处理流程还没有写。


然而并不是很想给Claude打钱,求求ChatGPT Desktop赶紧更新MCP支持🙏🙏🙏。

image-20250505234832457