关于 Octofs
Octofs 是面向编码助手的独立 Rust MCP 服务,可查看和修改本地或 SSH/SFTP 文件。它整合文件操作、Shell 命令与工作目录管理,并通过行哈希校验避免基于过期内容编辑。
主要功能
- 01读取、列出和搜索文件
- 02创建、编辑及撤销文件更改
- 03原子批量编辑与过期写入校验
- 04执行 Shell 命令并管理工作目录
- 05通过 SSH/SFTP 访问文件
- 06支持 STDIO 和 Streamable HTTP
适用场景
- 01检查项目并搜索代码
- 02批量修改源文件
- 03操作远程主机上的文件
- 04从 MCP 客户端运行项目命令
MCP 相关
向 MCP 客户端提供文件系统、编辑、Shell 和工作目录工具,并支持 STDIO 与 Streamable HTTP 传输。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
🐙 Octofs
赋予 AI 助手文件系统超能力
独立的 Rust 二进制程序,通过模型上下文协议提供文件系统工具。基于 rmcp 3.x、tokio 和 axum 构建。
安装 · 快速开始 · 功能 · 工具参考 · 文档 · 架构 · 更新日志 · 贡献 · 适合新手的问题
MCP Registry:io.github.Muvon/octofs
为什么选择 Octofs?
你的 AI 编码助手(Cursor、Claude、Windsurf 等)很聪明,却看不到你的文件系统。Octofs 弥合了这一差距,为 AI 提供:
- 双眼 — 读取文件、搜索内容、浏览目录
- 双手 — 创建、编辑文件,并以原子方式批量修改
- 上下文 — 执行命令、管理工作目录
- 触达 — 透明支持 SSH/SFTP:每个文件工具都接受
ssh://URL
┌─────────────────────────────────────────────────────────────┐
│ You: "Refactor all error handling to use anyhow::Context" │
├─────────────────────────────────────────────────────────────┤
│ AI without Octofs: │
│ • "I can't see your project structure" │
│ • "Please paste the relevant files" │
│ • *Wastes 10 minutes on back-and-forth* │
├─────────────────────────────────────────────────────────────┤
│ AI with Octofs: │
│ • Reads your codebase directly │
│ • Finds all error handling patterns │
│ • Suggests atomic batch edits │
│ • Applies changes with your approval │
└─────────────────────────────────────────────────────────────┘
有何不同
| 功能 | Octofs | 常见替代方案 |
|---|---|---|
| 实现方式 | 编译后的 Rust 二进制程序,无需运行时 | Python/Node 脚本 |
| 内容搜索 | 内置搜索,提供上下文行 | 仅字符串匹配 |
| 批量操作 | 对单个文件执行原子多项编辑 | 一次一项 |
| 行定位 | 复合 N:hh ID — 应用时进行哈希验证;过时的编辑会失败并提供重新定位提示 | 仅数字 |
| 传输方式 | STDIO + Streamable HTTP | 仅 STDIO |
| Shell 集成 | 自动将前台进程切换到后台 | 有限支持或不支持 |
| 远程文件 | 每个文件工具都透明支持 SSH/SFTP | 无 |
| 重新读取 | 增量视图 — 再次查看文件时,只返回自上次以来发生变化的区块(适用于会报告上下文 epoch 的客户端) | 每次返回完整文件 |
| 安全性 | 感知 Gitignore、检测过时写入、验证路径 | 完整文件系统访问权限 |
安装
Cargo(crates.io)
cargo install octofs
Homebrew
brew install muvon/tap/octofs
npm(无需 Rust 工具链)
npx -y @muvon/octofs --version
该封装程序会在首次运行时下载匹配的预构建二进制文件。MCP 配置:
{
"mcpServers": {
"octofs": {
"command": "npx",
"args": ["-y", "@muvon/octofs", "mcp"]
}
}
}
预构建二进制文件
从 GitHub Releases 下载适用于你平台的版本:
| 平台 | 目标 |
|---|---|
| Linux(x86_64) | x86_64-unknown-linux-musl |
| Linux(ARM64) | aarch64-unknown-linux-musl |
| Windows(x86_64) | x86_64-pc-windows-msvc |
| Windows(ARM64) | aarch64-pc-windows-msvc |
| macOS(Intel) | x86_64-apple-darwin |
| macOS(Apple Silicon) | aarch64-apple-darwin |
每个版本还会提供 .mcpb 包,可在支持 MCP 包的客户端(例如 Claude Desktop)中一键安装。
MCP Registry
已作为 io.github.Muvon/octofs 发布到 MCP Registry。
从源代码构建
需要 Rust 1.95+。
git clone https://github.com/muvon/octofs
cd octofs
cargo build --release
# Binary at ./target/release/octofs
快速开始
1. 配置你的 AI 助手
CLI 使用 mcp 子命令——你的配置必须将 ["mcp"] 作为 args。
Cursor(~/.cursor/mcp.json):
{
"mcpServers": {
"octofs": {
"command": "octofs",
"args": ["mcp"]
}
}
}
Claude Desktop(macOS 上的 ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"octofs": {
"command": "octofs",
"args": ["mcp"]
}
}
}
Windsurf(~/.windsurf/mcp.json):
{
"mcpServers": {
"octofs": {
"command": "octofs",
"args": ["mcp"]
}
}
}
如果 Octofs 不在你的
PATH中,请使用二进制文件的完整路径(例如/usr/local/bin/octofs或./target/release/octofs)。
2. 重启你的 AI 助手
当你的 AI 助手连接时,MCP 服务会自动启动。
3. 试用
请你的 AI 助手:
- “显示项目结构”
- “读取 main.rs 文件”
- “搜索代码库中所有
unwrap()的用法” - “创建一个名为
test.rs的新文件”
功能
📁 文件系统操作
- 查看文件和目录 — 读取单个文件(同时读取多个文件时,可并行调用
view),使用 glob 模式列出目录,搜索内容 - 智能截断 — 智能截断大型文件,避免上下文窗口不堪重负
- 遵循 Gitignore — 遍历目录时遵循
.gitignore模式 - 行范围 — 使用负索引读取指定行范围(
-1= 最后一行) - 远程文件(SSH/SFTP) — 每个文件工具都接受
ssh://user@host:port/pathURL(参见远程文件系统)
✏️ 文本编辑
- 创建文件 — 创建新文件,并自动创建父目录
- 字符串替换 — 替换完全匹配的字符串;空白不匹配时会回退到模糊匹配
- 删除 — 删除文件(可通过撤销恢复)
- 撤销 — 恢复上一次编辑(每个文件最多支持 10 级撤销,保存在内存中)
- 批量编辑 — 对单个文件原子性地执行多个插入/替换操作
- 过期写入保护 — 每个编辑目标的
N:hhID 都会在应用时与文件内容进行验证;如果 ID 已过期,操作会失败,并返回当前内容及目标内容的新位置,避免编辑错误的行
🔍 代码智能
- 内容搜索 — 在文件中搜索字符串,并显示上下文行
- 提取行 — 将一个文件中的指定行范围复制到另一个文件
🖥️ Shell 和系统
- 命令执行 — 运行 shell 命令并捕获输出
- 后台进程 — 长时间运行的命令会自动在后台继续执行,并在完成时发送通知
- 工作目录 — 设置、获取或重置操作的工作目录上下文
配置
行标识符
每一行都由组合 ID N:hh 标识——其中包含从 1 开始计数的位置,以及根据行内容生成的 2 字符十六进制哈希(FNV-1a)。view 会将行显示为 N:hh|content:
1:a3|fn main() {
2:f1| println!("Hello");
3:0e|}
编辑工具会将这些 ID 作为目标传回,并在应用任何更改前验证文件中的哈希。如果 ID 已过期(即文件在查看后发生了变化),操作会失败,并返回目标附近的当前内容以及预期内容的新位置——这样模型可以根据错误信息重新定位,而无需重新读取文件。编辑结果会提供带有新 ID 的差异,因此可以连续进行编辑而无需重新查看。被删除的行会合并为其旧 ID 范围(-12:a3..15:f1 (4 lines));末尾的 shift: lines after 40:b2 +3 行则会说明每次编辑后、指定行号之后的原始行号发生了什么变化,因此模型仍持有的 ID 可以相应调整,无需因调用失败而重新定位。
行 ID 只有这一种格式——没有模式切换。在仅需位置且安全的情况下,仍接受普通行号:view 范围(负数从末尾计数),以及插入锚点 0(文件开头)和 -1(追加到末尾)。
Shell 误用拦截
Octofs 会检测 shell 误用——例如 cat、grep、find 或 sed 这类本应使用专用 MCP 工具的命令——并拒绝执行,同时说明应使用哪个工具。调用会失败;不会执行任何操作。这是有意为之,且无法配置:专用工具能为模型提供行 ID、感知 gitignore 的能力以及远程主机支持,而原始 shell 输出无法做到这些。管道命令(cargo build 2>&1 | grep error)仍然允许——只有单独调用这些程序时才会被拦截。
其他提示(范围超出边界、模糊匹配通知)会作为 ⚠️ 提示附加到成功的响应中,不会导致调用失败。
传输模式
STDIO(默认)
标准输入/输出传输。适用于所有 MCP 客户端。
octofs mcp
HTTP
用于远程访问或多客户端场景的可流式 HTTP 传输。所有工具(包括 shell)都可供任何能够连接的人使用,因此需要设置访问门控:
# Loopback, no token: requests carrying a browser Origin header are rejected
octofs mcp --bind 127.0.0.1:12345
# Any other address requires a bearer token; octofs refuses to start without one
OCTOFS_AUTH_TOKEN="$(openssl rand -hex 32)" octofs mcp --bind 0.0.0.0:12345
客户端连接到 http://localhost:12345/mcp;使用令牌时,客户端会发送 Authorization: Bearer <token>。服务使用纯 HTTP,因此当令牌会经过网络传输时,请将其置于 TLS 或 SSH 隧道之后。也可以使用 --auth-token <TOKEN>,但命令行参数会对其他本地用户可见。
工作目录
默认情况下,Octofs 在当前目录中运行。指定其他根目录:
{
"mcpServers": {
"octofs": {
"command": "octofs",
"args": ["mcp", "--path", "/path/to/your/project"]
}
}
}
远程文件系统(SSH/SFTP)
所有路径参数(包括 --path 本身)都接受 ssh:// 或 sftp:// URL:
# Remote session root: relative paths resolve on the remote host
octofs mcp --path ssh://[email protected]/var/www/app --ssh-key ~/.ssh/id_ed25519
view path="ssh://[email protected]/etc/nginx/nginx.conf"
- 登录主目录——
ssh://host(不带路径)和ssh://host/~/dir会相对于登录用户的主目录解析,与ssh host/scp host:dir的行为一致。ssh://host/dir则保持为绝对路径。 - OpenSSH 配置——主机别名会通过本地 OpenSSH 配置解析(包括
Include和Match),并遵循HostName、User、Port和单个ProxyJump。URL 中明确指定的值优先,因此ssh://dev/path和ssh://[email protected]:2222/path都会按预期工作。不支持多跳ProxyJump和ProxyCommand路由,并会返回明确的错误。 - 身份验证——使用主机的
IdentityAgent(例如 1Password)或$SSH_AUTH_SOCK,然后使用密钥文件——如果指定了--ssh-key,则优先使用该文件;接着使用主机的IdentityFile条目;最后使用~/.ssh中的默认密钥(id_ed25519、id_ecdsa)。不直接支持受密码保护的密钥文件;请改用代理。 - 不支持 RSA 密钥——Rust
rsacrate 存在尚未修复的定时侧信道漏洞(Marvin 攻击,RUSTSEC-2023-0071),因此 octofs 完全不包含 RSA 支持。请改用 ed25519 密钥(ssh-keygen -t ed25519);ecdsa 也可用。仅使用 RSA 的配置会失败,并显示指出密钥的明确错误。 - 主机密钥——使用 OpenSSH 的
accept-new策略,根据~/.ssh/known_hosts验证:首次使用时会记录未知主机;密钥发生变化时则会拒绝连接。 --ssh-timeout SECS——连接超时(默认值为 30)。连接会按主机建立连接池,通过传输层 keepalive 保持活跃,并在连接中断时自动重新连接。shell保持本地运行——命令始终在运行 Octofs 的机器上执行;只有文件工具(view、text_editor、batch_edit、extract_lines、workdir)会访问远程主机。
MCP 工具参考
view — 读取文件、列出目录、搜索内容
读取文件:(path 是单个路径;start/end 是行号或行 ID)
{"path": "src/main.rs"} // whole file
{"path": "src/main.rs", "start": 10, "end": 20} // lines 10–20
{"path": "src/main.rs", "start": 42, "end": 42} // single line
{"path": "src/main.rs", "start": 80} // line 80 → end of file
{"path": "src/main.rs", "start": -20} // last 20 lines
{"path": "src/main.rs", "start": "12:a3", "end": "20:f1"} // line ids from prior output
输出会将每一行显示为 N:hh|content——N:hh 前缀是 batch_edit 和 extract_lines 用作目标的行 ID。
要读取多个文件,请多次调用 view——这些调用会并行运行。
复用已返回的内容和行 ID。在一次调用中读取完整的相关函数或代码块,并合并相邻的窗口。如果位置未知,请使用 content 并提供足够的 context 来理解匹配项,然后只获取缺少的周边代码。不要重新读取重叠范围,也不要为了选取编辑目标而反复缩小范围。当文件可能已更改、输出被截断或无法获取之前的上下文时,请重新读取。首次读取或强制完整读取空文件时,会返回 [empty file, 0 lines]。
增量视图: 如果对本次会话已提供过内容的文件进行全文件视图,只会返回自上次以来更改的代码块——格式与编辑结果中的 .../-/+ 相同,删除的行会合并为其旧行 ID 范围——或者返回 [unchanged since you last viewed or edited it: N lines. Pass full: true to re-read.]。Octofs 自身的编辑会使缓存保持最新,因此在你自己的 batch_edit 后重新查看只需一行。范围视图和内容搜索始终会准确显示所请求的内容。full 对范围或搜索无效;省略 start/end 和内容搜索即可强制完整读取文件。它不会更改输出限制。
{"path": "src/main.rs", "full": true} // force the complete file (e.g. after context compaction)
目录列表:
{"path": "src/"}
{"path": "src/", "pattern": "*.rs"}
{"path": "src/", "max_depth": 2, "include_hidden": true}
对远程目录(ssh://host/path,没有 pattern/content)进行裸列表时,默认使用 max_depth: 1——仅列出根目录条目——因为每个子目录都需要一次 SFTP 往返;传入 max_depth 可显式遍历更深层级。使用 pattern 或 content 的搜索始终会遍历整个目录树,与 rg 一样。
pattern 使用与 ripgrep 的 -g/--glob 相同的 gitignore 风格 glob 语法:不含 / 时,会匹配任意深度的文件名;包含 / 的模式则匹配返回的相对路径。它支持单个路径组件内的 *、跨目录的 **、?、[abc] 等字符类、*.{rs,toml} 等花括号选项,以及前导 ! 排除项。使用 | 可在一个 MCP 字符串中传入有序 glob;后面的 glob 优先级更高:
{"path": ".", "pattern": "**/*.{rs,toml}|!target/**"}
{"path": ".", "content": "unsafe", "pattern": "src/**/*.rs|!src/generated/**"}
glob 会筛选 Octofs 的 gitignore 感知遍历所发现的文件。使用 include_hidden: true 可包含隐藏路径;被 gitignore 排除的路径仍会排除。模式最长为 4096 UTF-8 字节,最多包含 64 条以 | 分隔的规则,以及 16 层嵌套花括号。模式必须为单行,并会在遍历前进行验证。将字面量前导 # 或 ! 转义为 \# 或 \!;否则会拒绝 #,而不是静默地将其视作 gitignore 注释。不支持空花括号选项(例如 {,rs}),因为 ripgrep 不支持这种写法。
内容搜索:(默认按字面内容匹配;设置 regex: true 可使用 Rust 正则表达式,(?i) = 不区分大小写)
{"path": "src", "content": "fn main"}
{"path": "src", "content": "unwrap()", "context": 3}
{"path": "src", "content": "(?i)error", "regex": true}
{"path": "docs|scripts|BENCHMARK.md", "content": "update_benchmark.py", "context": 4}
要像 rg 一样跨多个根目录搜索,设置 content 时,path 可接受以 | 分隔的字面量文件和目录。这些根路径不是正则表达式:与 rg 的位置路径参数一样,每个路径都必须存在。如果真实路径中包含 |,则优先使用该路径,而不是这种简写形式。根路径列表必须为单行、不得包含重复项,且最多可包含 32 个根路径,总长度不超过 8192 UTF-8 字节。
目录列表会将每个文件标注为 path<TAB>NL<TAB>~Nt(行数 + 估算 token 数),便于你在打开文件前规划读取量;二进制文件会显示为 path<TAB>(binary)。
text_editor — 创建、编辑、替换文本
创建文件:
{"command": "create", "path": "src/new.rs", "content": "pub fn new() {}"}
替换字符串:(old_text 必须恰好匹配一次)
{
"command": "str_replace",
"path": "src/main.rs",
"old_text": "fn old()",
"new_text": "fn new()"
}
替换所有匹配项(类似重命名的编辑):
{
"command": "str_replace",
"path": "src/main.rs",
"old_text": "old_name",
"new_text": "new_name",
"replace_all": true
}
匹配会逐步进行:精确匹配 → 转义字面量恢复(当结果唯一匹配时,将双重转义的 \n/\t 解释为换行符/制表符)→ 规范化空白并调整缩进的模糊匹配 → 提供最接近候选项及其行 ID 的详细诊断。CRLF 文件会在 LF 格式下进行匹配,写入时保留原有行尾格式。
删除文件:(可通过 undo_edit 恢复)
{"command": "delete", "path": "src/old.rs"}
撤销上次编辑:
{"command": "undo_edit", "path": "src/main.rs"}
batch_edit — 原子化执行多项编辑
在单个文件上原子化执行多项插入/替换操作。
每项操作都有一个 start。对于 replace,它是范围首行的行 ID,该 ID 从 view 输出中复制(指定范围时添加 end;仅替换单行时省略)。对于 insert,它是插入位置的锚点,可以是行 ID,也可以是整数 0(文件开头)/-1(最后一行之后)。所有 ID 都会在应用任何操作前根据文件进行验证;如果 ID 已过期,则会返回当前内容,方便模型无需重新查看即可重新指定位置。结果是带有新 ID 的差异。
在开头插入:
{
"path": "src/main.rs",
"operations": [
{"operation": "insert", "start": 0, "content": "// Header\n"}
]
}
替换行:
{
"path": "src/main.rs",
"operations": [
{"operation": "replace", "start": "10:4b", "end": "15:c2", "content": "new code here"}
]
}
extract_lines — 在文件间复制行
{
"from_path": "src/utils.rs",
"from_start": 10,
"from_end": 25,
"append_path": "src/new.rs",
"append_line": -1
}
from_end 是可选的(省略则只复制单行)。from_start、from_end 和 append_line 均接受行号或行 ID(例如 "12:a3",并根据文件进行验证)。append_line 用于指定复制内容在目标文件中的位置:0 = 开头,-1 = 末尾,N = 第 N 行之后。
shell — 执行命令
命令会先在前台启动;如果在约 10 秒内完成,通常会直接返回输出:
{"command": "cargo test"}
{"command": "cd foo && cargo build"}
如果命令在此时仍在运行,同一进程会继续在后台运行。响应会返回其 PID 和关联的输出资源,并在命令退出时发送完成通知。此处没有 background 标志,交接过程中也绝不会终止或重启命令。多个不同命令可以并发运行,包括在同一工作目录中运行。同一工作目录中已有某条命令正在运行时,会拒绝完全相同的命令。
声明 MCP tasks 扩展(io.modelcontextprotocol/tasks,SEP-2663)的客户端会将转入后台的命令作为任务处理,任务 ID 即作业 ID。客户端通过轮询 tasks/get 获取结果,其内容与前台调用返回的结果相同(命令失败时,错误结果会以退出代码开头);tasks/cancel 则会停止该命令的进程组。命令运行期间,任务状态消息会列出其 PID 和终止命令(kill -- -<pid>)、已运行时间以及最新输出行。
其他客户端会通过作业资源的 resources/updated 收到完成通知(如果客户端已打开 subscriptions/listen 流,则会在该流中收到)。Claude Code 不会向模型显示此通知,因此在 2026-07-28 之前建立的连接上,octofs 还会将其作为 Claude Code channel 消息(notifications/claude/channel)发送——退出代码和输出末尾内容会唤醒会话。Claude Code 仅在交互模式下,且仅对会话已启用的服务端发送的消息,以及 2026-07-28 之前建立的连接,接受 channel 消息。Claude Code 默认会对 stdio 服务端协商使用 2026-07-28,因此在声明 tasks 扩展之前,还需退出该协商:
MCP_PROTOCOL_NEGOTIATION=legacy claude --dangerously-load-development-channels server:octofs
否则,需要结果时,请读取作业的资源链接。
在 Windows 上,关闭时的清理操作只会终止直接子进程(不具备 Unix 进程组语义);对于进程树,请使用
taskkill /PID <pid> /T。
workdir — 管理工作目录
获取当前目录:
{}
设置新目录:
{"path": "/path/to/project"}
重置为会话根目录:
{"reset": true}
架构
octofs/
├── src/
│ ├── main.rs # Entry point, STDIO/HTTP server setup, signal handling
│ ├── cli.rs # CLI argument parsing (clap): octofs mcp [OPTIONS]
│ ├── mcp/
│ │ ├── mod.rs # McpToolCall, SessionRoot
│ │ ├── server.rs # OctofsServer (rmcp tool impl), Params structs, SessionWorkdir
│ │ ├── request_ctx.rs # Per-request hint queue + stale-file stamps
│ │ └── fs/ # Filesystem tool implementations
│ │ ├── mod.rs # Re-exports
│ │ ├── core.rs # view, text_editor, batch_edit, extract_lines
│ │ ├── file_ops.rs # view_file_spec, create_file_spec
│ │ ├── text_editing.rs # str_replace, batch_edit, undo, per-file locking
│ │ ├── directory.rs # Directory listing + content search
│ │ ├── search.rs # search_content
│ │ ├── shell.rs # Command execution, background, process cleanup
│ │ ├── workdir.rs # Working directory management
│ │ ├── remote.rs # SSH/SFTP path abstraction, SshHandler, SFTP pool
│ │ └── fs_tests.rs # Integration tests (cfg(test))
│ └── utils/
│ ├── mod.rs # Module re-exports
│ ├── line_hash.rs # Composite line ids (N:hh), FNV-1a hashes, endpoint parsing
│ └── truncation.rs # Token estimation, smart truncation
关键设计决策:
- rmcp SDK — 使用官方 Rust MCP SDK(
rmcp3.x)处理协议 - Tokio — 异步运行时;所有文件 I/O 均使用
tokio::fs,绝不使用阻塞式std::fs - 文件锁定 — 每个文件使用
tokio::sync::Mutex,防止并发写入冲突 - 撤销历史记录 — 每个文件最多保留 10 个快照,存储在内存中(重启后会丢失)
- 路径解析 — 相对路径基于会话工作目录解析;不进行规范化(文件可能尚不存在)
- 过期写入检测 — 应用时会根据文件验证每个编辑目标的
N:hhID;过期 ID 会返回当前内容,而不是编辑错误的行
开发
# Build
cargo build --release
# Run tests
cargo test
# Lint (zero warnings policy)
cargo clippy
# Format
cargo fmt
# Run locally
cargo run -- mcp
运行测试
# All tests
cargo test
# Specific test
cargo test test_view_file
# With output
cargo test -- --nocapture
贡献
欢迎贡献!请参阅 CONTRIBUTING.md 了解指南。
快速检查清单:
- 提交前运行
cargo fmt - 确保
cargo clippy通过且没有任何警告 - 为新功能添加测试
- 根据需要更新文档
安全
安全政策和漏洞报告方式请参阅 SECURITY.md。
许可证
Apache-2.0 — 参见 LICENSE
致谢
- rmcp — 官方 Rust MCP SDK
- Model Context Protocol — 协议规范
由 Muvon 使用 🦀 构建
如果 Octofs 帮助你更快交付,请在 GitHub 上为我们点个 Star!⭐
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。