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 拼接即可精确还原该文件。
实时字节
读取反映的是调用那一刻的文件系统状态,所以一次写入对紧随其后的读取立即可见。这正是此服务器的价值所在,并由此带来两个值得直接说明、而不是等用户踩坑才发现的情况。
游标宁可失败,也不撕裂。 游标携带其遍历所依赖状态的摘要。如果该状态发生变动,下一页会被拒绝,并给出错误讯 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 installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Artifact store for AI agents. Hosted OAuth at mcp.artifacta.io/mcp; local stdio via npm/PyPI.
Project management MCP for AI agents with safe task reads and writes.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/andreasronge/ptc-fs-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server