上周发了篇手写 MCP Server 连数据库的文章,有个读者留言说:“能不能也写篇怎么让 AI 读写文件的?”我当时愣了一下——好像确实,网上聊 MCP 的大多都在讲数据库、API、浏览器自动化,很少有人提最基础的“读写文件”这件事。

但我真去试了之后发现,文件系统控制反而是 MCP 最有实用价值的场景之一。不是因为它多复杂——代码量可能比数据库那个还少——而是因为你每天都在跟文件打交道:找一份好久没打开的笔记、批量重命名一堆下载文件、把散落在各个文件夹的素材汇总到一起。这些事情 AI 本来做不了,因为它看不见你的硬盘。但现在能了。

MCP 协议本质上就是个“桥”——它让 AI 通过一套标准接口调用你本地的工具。文件系统操作是这套接口里最直观的一种:读文件、写文件、搜文件,跟你在终端里做的事情一模一样。

代码大概长这样。

server.py — 不到 50 行的 MCP 文件服务器

import os
from pathlib import Path
from mcp.server import Server, stdio

server = Server("filesystem-server")

@server.tool()
async def read_file(path: str) -> str:
    path = Path(path).resolve()
    if not path.exists():
        return f"文件不存在: {path}"
    return path.read_text(encoding="utf-8")

@server.tool()
async def write_file(path: str, content: str):
    path = Path(path).resolve()
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(content, encoding="utf-8")
    return f"已写入 {len(content)} 字符到 {path}"

@server.tool()
async def search_files(pattern: str, root: str = "."):
    root = Path(root).resolve()
    results = list(root.rglob(pattern))
    if not results:
        return "没找到匹配的文件"
    return "\n".join(str(f) for f in results[:50])

@server.tool()
async def get_file_info(path: str) -> str:
    path = Path(path).resolve()
    if not path.exists():
        return "文件不存在"
    stat = path.stat()
    return f"大小: {stat.st_size} bytes\n修改时间: {stat.st_mtime}"

if __name__ == "__main__":
    server.run(stdio())

看到没,四个工具函数,加起来不到 50 行。你甚至可以去掉 get_file_info,核心就三个。

配置 Claude Desktop 连接

写好 server.py 之后,在 Claude Desktop 的配置文件里加上一行:

{
  "mcpServers": {
    "filesystem": {
      "command": "python",
      "args": ["/path/to/server.py"]
    }
  }
}

重启 Claude Desktop,你就能在工具列表里看到这四个函数了。

这里踩了个坑——路径安全

一开始我没加 Path(path).resolve(),结果 Claude 问我能不能读 /etc/passwd。我当时汗都下来了——不是因为我写了个有漏洞的代码,而是因为 Claude 真的会试探边界。

resolve() 的作用是把相对路径转成绝对路径,但真正重要的是后面还要加一层校验。我后面加了个白名单目录,只允许操作某个目录下的文件:

ALLOWED_DIR = Path("/home/user/allowed").resolve()

def safe_path(path: str) -> Path:
    p = Path(path).resolve()
    if not str(p).startswith(str(ALLOWED_DIR)):
        raise ValueError(f"不允许访问: {path}")
    return p

这个问题其实挺微妙的。MCP 本身不做权限控制,它只是一个通道。你让 AI 能调用什么工具、能访问什么范围,完全取决于你写的 server。开太多权限等于把家门钥匙交给了陌生人。我的建议是:让 MCP Server 的访问范围越小越好,比如只开放一个 ~/Documents/projects/ 目录,不要给整盘权限。

实际用起来的体验

配好之后我试了几个场景:

让 Claude “帮我找到上周写的那个关于 MCP 的笔记”——它会先 search_files("*MCP*"),找到几个文件,然后 read_file 一个个看内容,最后告诉我哪个是。

让它“把下载文件夹里的截图按日期重命名”——它会 search_files("*.png") 拿到列表,然后写一段 Python 脚本(用 write_file 写到临时文件),然后让我确认后再跑。这个组合拳特别好用:AI 做策略和代码,用户做最终确认。

让我意外的是写文件的场景居然最常用。有时候我想写一段多步骤的操作指南、或者整理笔记,直接在对话里跟 Claude 说了,它给我写进文件,我打开确认就行。比起在编辑器里一个字一个字敲,效率高很多。

值得注意的几点

MCP 的 stdio 模式是走标准输入输出通信的,所以写文件这种操作不会有明显的延迟感。但由于 Claude Desktop 目前只支持单线程串行调用,如果你同时调用了 search_filesread_file,它们是排队执行的。大目录搜索时(比如 **/*.log),Claude 会等搜索完再读,体验上会有停顿。

另外就是文件编码问题。write_text 默认用的是系统编码。我在 Windows 上写过中文内容的文件,读回来乱码了一回——后来在参数里加了 encoding="utf-8" 就好了。上面的代码已经修了这个问题,但如果你用的是 Python 3.6 以下版本,记得确认编码参数被正确传递。

其实这东西真正有意思的地方不是技术本身,而是它改变了你跟文件打交道的方式。以前要找文件,你得自己打开终端用 find、用 grep、翻 Finder/资源管理器。现在你跟 Claude 说一句自然语言,它在后台帮你把这些苦力活干了。

这50行代码,我觉得最值钱的是那个 search_files 函数。它让 AI 第一次能看见你的“个人数据宇宙”。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐