MCP 实战系列(Day 2)- 动手搓个文件系统 MCP 服务器

发布于:2025-04-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

上期回顾MCP 实战系列(Day 1)- 什么是 MCP?

在上期文章中,我们详细介绍了 Model Context Protocol(MCP)的基本概念和应用场景。本节将带领大家开发一个简易的 Filesystem MCP Server,通过代码示例逐步讲解实现细节。

本期目标:掌握 MCP Server 开发的基础框架,实现一个支持基本文件系统操作的服务器。

开源地址:https://github.com/jinzcdev/mcp-demos

请添加图片描述

开发环境准备

官方提供了 Python、Node、Java、Kotlin、C# 五种语言的 MCP 服务器 SDK。本文以 Python 为例进行开发,其他语言的实现方式类似。读者可根据实际项目需求选择熟悉的开发语言。

MacOS/Linux 系统

  1. 安装 uv 工具
curl -LsSf https://astral.sh/uv/install.sh | sh
  1. 创建项目目录并安装依赖
# 创建项目目录并进入
uv init simple-filesystem-server-python
cd simple-filesystem-server-python

# 创建虚拟环境并激活
uv venv
source .venv/bin/activate

# 安装 MCP 相关依赖
uv add "mcp[cli]"

# 创建 MCP Server 文件
touch simple_filesystem.py

说明:uv 是一个高效的 Python 包管理工具,功能类似 pip。如果更熟悉 pip,也可以使用 pip install "mcp[cli]" 安装依赖或创建虚拟环境。mcp[cli] 中的 cli 表示额外安装 typer 和 python-dotenv 两个库,前者用于创建命令行界面,后者用于加载环境变量(可通过查看 mcp 依赖库源代码中的 project.optional-dependencies 配置项找到这两个额外依赖)。最后创建的 simple_filesystem.py 文件是定义 MCP 服务的核心源文件,我们将在其中创建相关工具。

编写 MCP Server 代码

我们将实现一个简易的 Filesystem MCP Server,支持在指定目录中进行文件查找、读写等操作。

值得一提的是,Claude 官方已经提供了一个功能完备的 Node.js 版本 Filesystem MCP Server,包含了丰富的文件系统操作工具。本文选择使用 Python 重新实现,目的是帮助读者更清晰地理解 MCP Server 的 开发流程工具设计思路,而非重复造轮子。

接下来,让我们逐步实现这个服务器。

1. 导入必要模块并初始化服务器

完成前面的准备步骤后,我们首先在 simple_filesystem.py 中导入所需模块,并通过命令行参数指定允许访问的目录:

from mcp.server.fastmcp import FastMCP
import os
import os.path as osp
import argparse

parser = argparse.ArgumentParser(description="Simple Filesystem MCP Server")
parser.add_argument("allowed_dirs", nargs="+", help="List of allowed directories for file operations")
args = parser.parse_args()

allowed_directories = [osp.abspath(dir_path) for dir_path in args.allowed_dirs]

mcp = FastMCP("SimpleFileSystemMCPServer", log_level="ERROR")

说明:使用 argparse 解析命令行参数,在 MCP 服务启动时指定可访问的目录(防止 LLM 访问系统中的任意目录)。这里 FastMCP 初始化了一个名为 Simple Filesystem MCP Server 的服务器实例,日志级别设为 ERROR(测试时发现 Cline 客户端因为服务运行时输出的 INFO 日志导致无法列举服务工具,但不影响工具调用)。

2. 实现工具逻辑

使用 @mcp.tool 装饰器定义以下功能工具,这里的 @mcp.tool 可以理解为在 MCP Server 中注册一个工具,不定义 name 的情况下,工具名默认为函数名称:

(1) list_directory - 查找文件和目录
@mcp.tool(
    description="获取指定路径下的所有文件和目录的详细列表。返回结果会通过 [FILE] 和 [DIR] 前缀明确区分文件类型。该工具对于了解目录结构及查找特定文件非常实用。仅限在允许访问的目录中操作。"
)
async def list_directory(dir_path) -> str:
    if not osp.exists(dir_path) or not osp.isdir(dir_path):
        raise ValueError(f"{dir_path} is not a valid directory")

    entries = os.listdir(dir_path)
    formatted = []
    for entry in entries:
        entry_path = osp.join(dir_path, entry)
        if osp.isdir(entry_path):
            formatted.append(f"[DIR] {entry}")
        else:
            formatted.append(f"[FILE] {entry}")

    return "\n".join(formatted)

该工具返回指定目录下的文件和子目录列表,使用 [DIR] 和 [FILE] 前缀区分。当目录不存在时抛出异常,帮助模型理解当前文件系统状态。

最初定义工具时,我采用了 os.walk 递归调用的方式来实现目录遍历,但目录中可能包含大量依赖文件(如 node_modules),浪费 Token 数,所以我们只需要返回第一层的文件和目录列表即可,必要时让模型继续向下搜索即可。

(2) read_file - 读取文件内容
@mcp.tool(
    description="读取文件系统中的完整文件内容。支持处理多种文本编码格式,若读取失败将返回详细错误信息。适用于需要检查单个文件内容的场景。仅可在允许访问的目录中操作。"
)
async def read_file(file_path) -> str:
    if not osp.exists(file_path) or not osp.isfile(file_path):
        raise ValueError(f"{file_path} is not a valid file")

    with open(file_path, "r") as file:
        content = file.read()
    return content

该工具读取单个文件内容,当文件不存在或无效时抛出异常。

注意:如果文件内容过大也可能导致 Token 数溢出,实际开发中可设置字符数限制,超过则截断或做其他处理。

(3) write_file - 写入文件
@mcp.tool(
    description="创建新文件或完全覆盖现有文件内容。使用时需谨慎,此操作将直接覆盖目标文件且无警告提示。支持正确处理文本编码,仅限在允许的目录内执行。"
)
async def write_file(file_path, content) -> str:
    if not osp.exists(osp.dirname(file_path)):
        raise ValueError(f"Directory for {file_path} does not exist")

    with open(file_path, "w") as file:
        file.write(content)
    return f"Successfully wrote to {file_path}"

该工具实现文件写入功能,在目标目录不存在时抛出异常,成功则返回确认信息。

注意:写文件是危险操作,建议在 MCP 工具调用时确认是否执行该操作,避免误操作导致数据丢失。

(4) read_multiple_files - 批量读取文件
@mcp.tool(
    description="同时读取多个文件的内容。当需要分析或比较多个文件时,这种方式比逐个读取更高效。每个文件的内容将与其路径一并返回,便于追溯。即使个别文件读取失败,也不会中断整体操作。仅限在允许的目录内执行。"
)
async def read_multiple_files(file_paths) -> str:
    results = []
    for file_path in file_paths:
        try:
            if not osp.exists(file_path) or not osp.isfile(file_path):
                results.append(f"{file_path}: Error - Not a valid file")
                continue
            with open(file_path, "r") as file:
                content = file.read()
            results.append(f"{file_path}:\n{content}")
        except Exception as e:
            results.append(f"{file_path}: Error - {str(e)}")

    return "\n---\n".join(results)

该工具支持批量文件读取操作,使用分隔符区分不同文件内容(保持数据结构化便于模型理解)。为保证健壮性,单文件读取失败不会中断整个操作,并将错误信息返回给模型处理。

说明:为了便于读者理解,我在上述 4 个工具的 description 部分使用了中文描述,在实际测试中,发现中英文描述均不影响模型理解工具含义。

3. 启动服务器

在文件末尾添加启动代码:

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

最后,在项目根目录下,运行命令启动 MCP 服务器:

uv run simple_filesystem.py /path/to/dir1 /path/to/dir2

服务启动后不会有输出,这属于正常现象。下面我们将使用 MCP 客户端测试服务器。

测试 Filesystem MCP Server

常用的 MCP 客户端包括 Claude for Desktop、Cursor、Cline、GitHub Copilot(现已支持 MCP)、Cheery Studio 等。由于 Claude 客户端可能存在国内网络访问问题,而 Cline 与 GitHub Copilot 都内置了文件读取工具,不便测试,因此我们选择 Cherry Studio 进行测试。

安装并配置客户端

从官网 https://cherry-ai.com/ 下载最新版 Cherry Studio。

配置服务器

1、打开 Cherry Studio 的 设置,点击 MCP 服务器
2、手动 添加服务器 或点击 编辑 MCP 配置,将以下配置添加到配置文件中:

请添加图片描述
请添加图片描述

{
    "mcpServers": {
        "simplefilesystem": {
            "isActive": true,
            "name": "Filesystem MCP Server",
            "type": "stdio",
            "description": "用于获取本地目录列表、读写本地文件的 MCP Server",
            "registryUrl": "https://pypi.tuna.tsinghua.edu.cn/simple",
            "command": "uv",
            "args": [
                "--directory",
                "/ABSOLUTE/PATH/TO/simple-filesystem-server-python",
                "run",
                "simple_filesystem.py",
                "/path/to/dir1",
                "/path/to/dir2"
            ]
        }
    }
}

注意:上述配置中的命令行参数 args,需修改为真实的项目地址与允许服务访问的目录。

3、在 设置-模型服务 中选择模型并输入 API Key(需开启函数调用功能),这里选择了 DeepSeek V3:

在这里插入图片描述

4、返回聊天页面,在输入框下启用 MCP 服务

功能验证

请添加图片描述

通过多次工具调用,模型成功读取了本地文件并执行了内容总结。


完整代码已经上传到我的 GitHub 仓库,并封装了更多文件操作的工具,可通过以下命令获取源代码:

git clone https://github.com/jinzcdev/mcp-demos.git

总结

本文详细介绍了如何使用 Python 开发一个简易的 Filesystem MCP Server,实现了四个核心功能工具:

  1. list_directory - 目录内容列表
  2. read_file - 单文件读取
  3. write_file - 文件写入
  4. read_multiple_files - 批量文件读取

通过 Cherry Studio 客户端的测试验证了 Filesystem MCP Server 的功能。开发过程中我总结了以下创建 MCP Server Tools 的核心经验:

  1. 详尽的工具描述:清晰说明工具功能和使用场景,提升模型调用准确率
  2. 明确的错误反馈:工具调用失败时返回具体错误信息,辅助模型调整策略
  3. 保证工具健壮性:局部错误不要中断整体操作,返回部分成功结果

本文是 《从原理到实战:掌握 MCP》 系列的第二篇文章,间隔时间较长了。如果文章对你有帮助欢迎收藏、转发,您的关注是我更新的最大动力。如有疑问欢迎在评论区留言。

往期推荐:

  1. MCP 实战系列(Day 1):什么是 MCP?
  2. VS Code 预览版 Copilot 终于支持 MCP 了:试试使用 MCP 快速查询 GitHub Issues 吧

网站公告

今日签到

点亮在社区的每一天
去签到