2.1 filesystem:带访问控制的文件读写
它是什么
官方描述:sandboxed file read/write with path allowlists——带路径白名单的沙箱化文件读写。npm 包 @modelcontextprotocol/server-filesystem,版本 2.0.0,可执行名 mcp-server-filesystem。
在 2026 年 3 月的全球搜索量榜单上它排第 29(约 4.9k/月)。排名不高,但它是最基础的骨干——很多更复杂的 MCP 服务器本质上也是在帮模型更安全地碰到文件。
为什么 dsh 已经有了还要装
dsh 自带文件系统能力(见 DeepSeek Harness 完全使用指南),那这个服务器是不是多余?分场景看:
| 场景 | 用 dsh 自带 | 用 filesystem MCP |
|---|---|---|
| 当前工作区内的文件 | ✅ 更顺 | 多余 |
| 需要限定只读某个目录 | 靠审批 | ✅ 白名单硬约束 |
| 跨多个 agent 工具复用同一套约束 | ❌ | ✅ |
| 给不信任的任务划安全边界 | 靠配置 | ✅ 服务端强制 |
| 让外部 agent 只碰指定资料夹 | ❌ | ✅ |
核心差异在最后两列:白名单是服务端强制的,不依赖使用者记得开审批,也不会因为某次会话忘了切目录就扩大范围。审批是“请你确认”,白名单是“它物理上就进不去别的目录”——后者更省心。
安装与配置
先装:
npm install --global @modelcontextprotocol/server-filesystem
接进 dsh 的标准写法:
- insert:
- id: fs-docs
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: filesystem
transport: stdio
command: mcp-server-filesystem
args:
- /Users/you/allowed-docs
cwd: !!js process.cwd()
args 里列出的就是允许访问的目录。几个必须注意的点:
| 想达到的效果 | args 写法 |
|---|---|
| 只开放一个目录 | ["/abs/path"] |
| 开放多个目录 | ["/abs/a", "/abs/b"] |
| 可读仓库但只写临时目录 | 装两个实例:一个只读工程根、一个只写 /tmp |
- 路径必须是绝对路径,相对路径会被直接拒绝;
- 每一项单独成行,别用空格把多个目录拼进同一个字符串;
- 符号链接按解析后的真实路径判定,指不出去也白搭。
进阶:用多实例做读写隔离
白名单是按“实例”生效的。如果你既想让 agent 读工程代码、又想让它把产物写进别处,不要在一个白名单里把两个都放开——那样“读代码”和“写产物”就共享了同一个大范围。更干净的做法是挂两个 filesystem 实例:
- insert:
- id: fs-readonly-code
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: fs_code
transport: stdio
command: mcp-server-filesystem
args: ["/Users/you/project"]
cwd: !!js process.cwd()
- id: fs-write-tmp
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: fs_tmp
transport: stdio
command: mcp-server-filesystem
args: ["/tmp/scratch"]
cwd: !!js process.cwd()
这样模型手里有 mcp__fs_code__... 和 mcp__fs_tmp__... 两套工具,写操作天然被限制在 /tmp/scratch,读代码始终在 project。权限边界写在配置里,不靠临时叮嘱。
一条安全纪律
只开放真正需要的目录。给整个 home 目录等于没限制——那不如直接用 dsh 自带能力,至少还有审批兜底。
实务上的分层做法:
~/.dsh/skills/ 技能目录(按需)
/Users/you/allowed-docs 资料目录(常用)
/tmp/scratch 临时目录(可放开)
典型场景:你要让 agent 处理一批下载文件,但绝不想让它碰到代码仓库。把下载目录加进白名单、把仓库排除在外,就是 filesystem MCP 比“全局审批”更省心的用法。
常见用法
批量整理:
用 filesystem 服务器列出 allowed-docs 下所有 .md,
按文件名前缀分到对应子目录,列出移动清单先别动。
内容检索:
用 filesystem 服务器在 allowed-docs 里搜索包含"迁移"的文件,
返回路径与匹配行号。
受控写入:
用 filesystem 服务器把会议纪要写入 /tmp/scratch/notes/,
文件名用今天日期,写前先确认目录存在。
排错
| 现象 | 原因 |
|---|---|
| 路径不存在报错 | args 里的目录没建 |
| 明明在目录里却读不到 | 给了相对路径,需绝对路径 |
| 符号链接指不出去 | 白名单按解析后路径判定 |
| 写入被拒 | 目标不在白名单目录内 |
| 中文路径乱码 | 终端编码问题,避免路径含非常规字符 |
它和另外几个服务器的关系
- 需要跨会话记住文件元数据或笔记 → 看 2.2 memory;
- 需要操作 Git 仓库(提交、分支、对比)→ 看 2.3 git;
- 需要读网页再落盘 → 看 2.4 fetch。
小结
- filesystem 的价值在服务端强制的路径白名单,不是能力本身。
- 只开放真正需要的目录,给整个 home 等于没限制。
- 适合跨工具复用约束、给不信任任务划边界、让外部 agent 只碰指定资料夹。
- 多目录与读写分离,靠多实例而非一个大白名单实现。
延伸阅读
- 本章其余小节:2.2 memory、2.3 git
- 官方参考服务器仓库:modelcontextprotocol/servers
- 相关书籍:DeepSeek Harness 完全使用指南
- 姊妹篇:《DeepSeek Harness 必装的 skill 推荐》
