universal-fs-mcp
# universal-fs-mcp
一个通用、宿主无关的 [MCP](https://modelcontextprotocol.io/) 文件系统服务器。
基于官方 TypeScript SDK(`@modelcontextprotocol/sdk`)+ `zod`,走 `stdio` 传输,不依赖任何特定沙盒或客户端,任何能跑 Node.js 18+ 的环境都能用。
## 一键安装(推荐)
下载 [`universal-fs-mcp.mcpb`](https://github.com/jingchenGOD/universal-fs-mcp/releases/download/v1/universal-fs-mcp.mcpb)(或到 [Releases](../../releases/latest) 页面),**双击安装**即可,无需 node 环境配置、无需 `npm install`。
安装过程中会让你选择一个**工作区目录**,之后所有文件操作都被限制在该目录内。
> `.mcpb` 是 MCP Bundle 格式(官方打包规范),需要宿主应用支持,例如 Claude for macOS / Windows。若你的客户端不支持 `.mcpb`,请走下面的「从源码运行」。
## 从源码运行
```bash
git clone https://github.com/jingchenGOD/universal-fs-mcp.git
cd universal-fs-mcp
npm install
npm run build
```
> **注意**:仓库里不含 `dist/`(已在 `.gitignore` 中忽略)。
> **从源码运行必须先跑 `npm install && npm run build`**,否则没有 `dist/index.js`,MCP 客户端无法启动服务器。
> 用上面的 `.mcpb` 一键安装的话,不需要做任何构建。
## 特点
- **沙箱化**:所有路径 `path.resolve` 后校验必须落在工作区根目录内,`../` 穿越会被直接拒绝
- **省 token**:`fs_grep` 只回文件名 + 匹配次数 + 行号,不吐整行内容;`fs_read` 支持按行切片
- **安全写入**:`fs_write` 默认不覆盖已有文件;`fs_edit` 在匹配到多处时拒绝执行,除非显式允许
- **零污染**:stdout 只走 MCP 协议,所有日志输出到 stderr
- **依赖极简**:仅 `@modelcontextprotocol/sdk`、`zod` 和 Node 内置模块
## 自行打包 .mcpb
仓库里的 `manifest.json` 是 MCP Bundle 的打包配置(规范版本 0.3):
| 字段 | 值 |
|---|---|
| `server.type` | `node` |
| `server.entry_point` | `dist/index.js` |
| `mcp_config.command` | `node` |
| `mcp_config.args` | `["${__dirname}/dist/index.js"]` |
| `user_config.workspace_root` | 类型 `directory`、必填,安装时让用户选工作区目录,值注入到 `MCP_WORKSPACE_ROOT` |
改完代码后重新打包:
```bash
npm run build
npm install -g @anthropic-ai/mcpb # 首次需要
mcpb validate manifest.json
mcpb pack . universal-fs-mcp.mcpb
```
> 打包工具在 npm 上的包名是 **`@anthropic-ai/mcpb`**(不是 `@modelcontextprotocol/mcpb`,后者不存在)。
> `.mcpbignore` 会把 `src`、`tsconfig.json` 和 `typescript` 等打包期依赖排除,包体约 2.5 MB。
## 配置
| 环境变量 | 默认值 | 说明 |
|---|---|---|
| `MCP_WORKSPACE_ROOT` | `process.cwd()` | 工作区根目录。所有路径参数都相对它解析,且不允许越出该目录。**强烈建议显式设置。** |
手动启动(供调试):
```bash
MCP_WORKSPACE_ROOT=/workspace node dist/index.js
```
## 工具列表(6 个)
| 工具 | 参数 | 作用 |
|---|---|---|
| `fs_list` | `path`(默认 `.`)、`show_hidden`(默认 `false`) | 列出目录内容;目录带 `/` 后缀,文件附带字节大小;`show_hidden` 控制是否显示点文件 |
| `fs_read` | `path`、`start_line`(默认 `1`)、`end_line`(可选) | 按行读取文本文件,返回指定行区间。第一条是 `# 相对路径 行 a-b/总行数` 头,适合大文件只取需要的片段,避免整包读取 |
| `fs_stat` | `path` | 查询路径是否存在,返回类型(file/directory)、大小、修改时间;不存在时返回明确提示而非报错 |
| `fs_write` | `path`、`content`、`overwrite`(默认 `false`) | 写入文件,父目录不存在会自动递归创建;文件已存在且 `overwrite=false` 时**不覆盖**,只返回提示,防止误删内容 |
| `fs_edit` | `path`、`old_text`、`new_text`、`replace_all`(默认 `false`) | 精确字符串替换。匹配为 0 处时报错;匹配到多处且未设 `replace_all=true` 时**拒绝执行**,避免误改;返回实际替换的处数 |
| `fs_grep` | `keyword`、`path`(默认 `.`)、`max_files`(默认 `20`,上限 `100`)、`ignore_case`(默认 `false`) | 正则检索文本文件。**只返回文件名、匹配次数和行号数组,绝不返回匹配行内容**;自动跳过 `.git`、`node_modules`、`dist`、二进制文件和超过 2MB 的文件;也可直接传单个文件路径 |
所有路径参数都支持相对路径(相对 `MCP_WORKSPACE_ROOT`)或该目录内的绝对路径。
## 在 MCP 客户端中注册
以 DeepSeek Harness 为例,在 profile 的 `cordis.patch.yml` 中追加:
```yaml
- insert:
- id: mcp-universal-fs
name: "@deepseek-ai/dsh-mcp-client"
config:
serverName: universal-fs
transport: stdio
command: node
args:
- /绝对路径/universal-fs-mcp/dist/index.js
cwd: /你的工作区
env:
MCP_WORKSPACE_ROOT: /你的工作区
toolCallTimeoutMs: 60000
failOnStartupError: false
```
注册后工具名形如 `mcp__universal-fs__fs_list`。
其他客户端(Claude Desktop、Cursor 等)通用写法:
```json
{
"mcpServers": {
"universal-fs": {
"command": "node",
"args": ["/绝对路径/universal-fs-mcp/dist/index.js"],
"env": { "MCP_WORKSPACE_ROOT": "/你的工作区" }
}
}
}
```
## 安全说明
- 所有路径在 `path.resolve` 之后用 `path.relative` 校验,越出根目录一律抛错
- 写操作是普通 `writeFileSync`,**没有回滚机制**,重要文件请先用 `fs_read` 读取备份
- 服务器以 stdin 上的 MCP 协议通信,不要用交互式终端直接运行后手敲命令
## License
[MIT](./LICENSE)
TDQS
Scored across 6 tools
The verbs (list, read, stat, write, edit, grep) map to mostly distinct filesystem operations, and the fs_ prefix makes the target domain obvious. The main risk is fs_write vs fs_edit (overwrite/create vs in-place modification) and fs_read vs fs_stat, ambiguities that no descriptions exist to resolve.
Every tool follows the exact same pattern: fs_ prefix plus a short lowercase verb, all snake_case. There are no deviations or mixed conventions.
Six tools is well-scoped for a filesystem server, covering the core read/inspect/search/write operations without padding. Each tool appears to earn its place.
The surface covers reading, listing, stat, writing, editing, and searching, but there is no delete/remove, move/rename, copy, or explicit mkdir operation. These are standard filesystem lifecycle operations whose absence will force agents to find workarounds or fail.