ptc-fs-mcp
ptc-fs-mcp
一个轻量级文件系统 MCP 服务器:通过 stdio 在一个受约束的根目录下读写文件。
演示软件。 它的存在是为了让智能体(agentic)运行时在教程、示例和集成测试中能有一个真实、确定性的外部工具可指向。它有意识地做到足够小,小到可以一次读完,并复制进你自己的项目。请不要把它部署为生产环境的文件服务。
它是为 PtcRunner 智能体框架构建的;在该框架中,文件系统能力完全通过宿主机配置获得,而不是写在运行时代码里。服务器中没有任何部分与 PtcRunner 绑定——它通过 stdio 讲标准 MCP 协议,所以任何 MCP 客户端都可以安装它。
npx -y ptc-fs-mcp --root ./workspace --include '**'工具
工具 | 作用 | 返回 |
| 读取 | 相对前缀之下的排序、分页目录项 |
| 读取 | 包含字面子串的排序、分页路径 |
| 读取 | 分页的字面匹配结果,带路径与行证据 |
| 读取 | 分页的精确 UTF-8 字节块 |
| 写入 | 替换一个普通文件,报告路径与字节数 |
四个读取工具接受可选的 cursor 与 limit,并恰好返回 items、next_cursor 与 content_hash。从无游标开始,沿 next_cursor 一直走到它为 null。对 read_text_file 来说,把各条目的 text 拼接即可精确还原该文件。
Related MCP server: Files MCP Server
实时字节
读取反映的是调用那一刻的文件系统状态,所以一次写入对紧随其后的读取立即可见。这正是此服务器的价值所在,并由此带来两个值得直接说明、而不是等用户踩坑才发现的情况。
游标宁可失败,也不撕裂。 游标携带其遍历所依赖状态的摘要。如果该状态发生变动,下一页会被拒绝,并给出错误讯 the filesystem changed since this cursor was issued; start the traversal again。一个撕裂的页面——一半来自变化之前,一半来自变化之后——才是唯一值得抛出一个错误来应对的结果。
结果实际依赖的状态才会被绑定,因此不相关的变动不会使游标失效:
工具 | 会使其失效的变动 | 可以存活的变动 |
| 所列条目发生变化 | 某个被列出的子目录的更深处出现新文件 |
| 匹配路径的集合发生变化 | 匹配文件的内容被编辑 |
| 任一范围内文件的内容或其身份发生变化 | 搜索前缀之外的变动 |
| 该文件自身发生变化 | 任何其他文件发生变化 |
游标使用每进程的密钥签名,绑定到工具及参数上,并且必须原样呈现。来自另一段遍历、另一个进程,或经改动的游标字符串都会被拒绝。
每个结果都携带 content_hash——该调用返回字节的 SHA-256 摘要。因此引用所指向的是实际读取的那一字节序列,而不是在某个其他时刻碰巧存在的目录树。write_text_file 同样报告它对所写字节计算的摘要,于是写入与其后的读取可以互相对照。
这里不提供整棵树的哈希,也没有可安装的 snapshot_identity。摘要只能覆盖有界的扫描范围,而此服务器并不做整树扫描。
运行
ptc-fs-mcp --root ./workspace --include 'lib/**' --include 'docs/**' --exclude '**/secrets/**'选项 | 含义 |
| 约束到的根目录。必填。 |
| 服务匹配的路径。必填,可重复。 |
| 绝不服务匹配的路径。可重复;只能进一步收窄。 |
| 大于此字节数的文件不服务。 |
| 单次 |
--include 必传,默认是“什么文件都不服务”,所以不带它启动的服务器不会暴露任何东西。被排除的路径会在任何 stat 或 open 之前被跳过,因此永远不会被放入清单。glob 中 * 匹配单个路径段,** 跨段;lib/** 既选中 lib/a.ts,也选中 lib/deep/a.ts。
写入落在根目录里,所以 include 规则也必须达到根目录。 write_text_file 只接收一个基本文件名(basename),绝不接收目录,因此每次写入都直接落在根目录内。如果一个 include 集合只切入子目录——比如 --include 'lib/**'——那么这些文件可以提供服务以供读取,但一个次写入也接不了,并且每次尝试都会以 no --include pattern of this root matches a file in the root 被拒绝。对于只读安装来说,这是一个完全合法的配置,因此服务器照常启动,并在 stderr 上说明这一点:
ptc-fs-mcp: no --include pattern matches a file in the root itself, so
write_text_file will refuse every call.凡是映射了写入工具的位置,请使用 --include '**',或者在目录级模式之外再加一条根级模式,比如 --include '*.md'。
从宿主文档安装时,请用固定版本的方式:
"transport": {
"type": "stdio",
"command": "npx",
"args": ["-y", "ptc-fs-mcp@0.1.0", "--root", "workspace", "--include", "**"],
"inherit_environment": true
}在没有继承环境的情况下启动进程
那种写法需要在 PATH 里依赖两次:npx 要从 PATH 里找到;而安装后的二进制文件以 #!/usr/bin/env node 开头,同样从 PATH 里解析出运行解释器。一个以净化(穷化)环境来启动的宿主机——即 PtcRunner 的 inherit_environment: false,它自己的端到端测试就是这么用——将无法启动这个服务器,而且失败会表现为 acquisition 错误,例如 provider_unavailable,而不会提及 PATH。版本管理器让这种情况更尖锐而非更缓和:nvm 解释器位于类似 ~/.nvm/versions/node/v20.19.0/bin/node 的路径,别处都不存在。
两种配置互斥。要想在封闭环境(hermetically)启动,请预先安装该包,并把解释器和启动脚本都用绝对路径写全,从而规避掉 npx 和 shebang 两处:
npm install ptc-fs-mcp@0.1.0
node -p process.execPath
node -p "require.resolve('ptc-fs-mcp/package.json').replace(/package\.json$/, 'dist/cli.js')""transport": {
"type": "stdio",
"command": "/absolute/path/to/bin/node",
"args": [
"/absolute/path/to/node_modules/ptc-fs-mcp/dist/cli.js",
"--root",
"/absolute/path/to/workspace",
"--include",
"**"
],
"inherit_environment": false,
"env": {}
}服务器本身对环境没有依赖:它不启动任何子进程,不打开网络连接,也不读取自己的环境变量。--root 是相对工作目录解析的,所以除非宿主机设置并由你控制 cwd,请把它也写成绝对路径。examples/ptc-host.json 里的 hermetic_workspace 就是这种形态。
拆分不同权限,而不拆分服务器
MCP 宿主决定哪些上游工具变成能力,因此这一个包的某次安装可以只映射 read_text_file,而第二次安装——指向另一个根目录——只映射 write_text_file。这样生成的读取程序就完全解析不到写入工具。参见 examples/ptc-host.json。
在 Node 中使用 Node
该包本身也是一个库。openRoot 校验配置并固定根目录;createServer 构建与二进制所服务同一个 McpServer,你可以为它挂接任意传输层。
import { createServer, openRoot } from 'ptc-fs-mcp'
const root = openRoot({ root: './workspace', include: ['**'], exclude: ['*.secret'] })
const server = createServer(root)
await server.connect(myTransport)examples/embed.mjs 是一个可运行示例,会在 SDK 的内存传输上、于同一进程内写入一个文件、读回并搜索它:
npm run build && node examples/embed.mjsopenRoot 在配置不可用时抛 ConfigError,各个工具抛 ToolError;两者都会导出,并同时导出 normalizeRelative、compileGlob、createSelector 与 DEFAULT_LIMITS,这样宿主无需重做即可复用同样的路径约定。TypeScript 声明随包附带。
协议
仅支持 2026-07-28。没有 initialize 回退,没有降级协商,也没有兼容性分支:2025 时代的握手会被拒绝,拒绝时为 unsupported-protocol-version 错误,并指明本服务器实现的协议档位。只发布 tools 这一个能力——没有 Roots、没有 Trim、没有 Logging、没有 Tasks。
约束
只接受相对路径。绝对路径、
.与..段、NUL 字节以及 Windows 分隔符会被直接拒绝,而非被解析。符号链接会被跳过,绝不被跟随,因此根目录内的一条链接无法触及目录外字节。最终
open使用O_NOFOLLOW,因此在检查之后被换入的链接仍会失败。某个目录之所以出现在清单中,只因为它的内部有被服务的项;未被服务的目录名不会泄露。
write_text_file只接受一个全部小写基本名——不接受目录,不做目录遍历——并对载荷设置上限,且通过它将要写入的同一描述符确认目标是普通文件,而不是通过某个独立的stat让符号链接抢跑。目标位于--include之外会被拒绝,因为一个写读不回来的写入不是功能,而是陷阱。由于写入直接落到根目录,只伸进子目录的 include 规则会拒绝所有写入;参见运行。路径清单对内容不敏感;内容工具拒绝它们无法解码的内容。
read_text_file遇到非有效 UTF-8 文件的失败;search_text会跳过该行,该行字节若不解码,就会整行跳过,因此一行要么全部报告,要么完全不报告。结果被劣化,负担整个已解码 MCP 结果,文本搜索另有扫描字节预算。因此,当稀疏文件需要更多扫描时,空搜索页可以携带进度游标。
错误信息是简短的可操作文本——不加堆栈,不返回宿主路径。
不 spawn 任何进程,不使用任何网络,stdout 只承载协议消息;诊断输出发到 stderr。
它不防什么
根目录必须是可信静的,且没有足够权限的调用端:可移植的 Node 路径 API 无法对上游每个目录做描述符级隔离,因此一个能在调用中调换父目录的角色不在防御范围之内。服务器拒绝可观测的符号链接,并在最终 open 的关闭了 follow 位;它并不声称在被攻击乡等变化中的恶意源根上进行防御。
游标过期检测依据 size、mtime、ctime 与 inode 编号。在时间戳化粒度的文件系统上,同 mbit 相同长度的复写如果发生在同一个时间刻度内,是无法探测的。本程序所支持的主流文件系统常记录纳秒时间,并且 ctime 不能由用户空间设置。
开发
npm install
npm run build # tsc to dist/, with declarations and source maps
npm test # builds, then runs the suite against the built binary
npm run verify # format check, typecheck, and tests测试套件把构建出的 dist/cli.js 作为真实子进程,经由真实的 stdio 驱动,因此发布的版本经过实测。测试无关的根目录是被逐测试生成的,而不是随仓库提交,因为这个服务器既读也写。
何为 License
MIT。参见 LICENSE。
This server cannot be deployed
Maintenance
Related MCP Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
- flockfsOAuthcom.flockfs
A real-time, multiplayer filesystem for agents and humans: shared files, live edits, history.
MCP server for Superserve sandboxes: create, exec, and manage Firecracker microVMs
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA lightweight, stdio-based MCP server enabling AI assistants to perform local file system operations like reading, writing, searching, and executing commands.3,012 npmMIT
- AlicenseNot gradedqualityDmaintenanceStdio MCP server for sandboxed file access — read files, search content, safely edit with checksums, and manage file structure.13 npmISC
- AlicenseNot gradedqualityCmaintenanceAn MCP server that gives AI agents file system access: read, write, search, hash, directory trees — 12 tools, zero dependencies, pure Python stdlib.MIT
- AlicenseNot gradedqualityBmaintenanceA sandboxed file-access MCP server for Claude Desktop, enabling safe read/write access to selected directories with atomic verified writes, undo, and read-only git operations.1MIT