内容
# restic-defensive-mcp
结构体只读 [MCP](https://modelcontextprotocol.io) 服务器(标准输入输出)用于
通过不受信任的 MCP 客户端检查 [restic](https://restic.net) 仓库 **而不会** 暴露 shell、自由形式的 CLI 或 mutation 路径。
> 我的备份是否存在且最近,元数据是否可读,snapshot 是否包含我期望的文件?
## 为什么不直接暴露 `restic`?
Restic 是一个强大的备份工具。 unrestricted 调用者可以:
- 删除保留历史记录(`forget` / `prune`)
- 覆盖或提取数据(`backup` / `restore` / `dump`)
- 解锁或重写仓库状态
- 接受任意仓库 URL 和密码命令
薄包装层暴露“运行 restic 并带有这些参数”或每次调用仓库覆盖,都会在 MCP 中重现这种爆炸半径。
这个项目在构建时是不同的:
1. 仓库在**进程启动时声明和密封**
2. MCP 接口**仅只读**(无写入功能标志)
3. 只有**编译后的子命令子集**可以构建
4. **没有 shell** — 仅使用固定 argv 的 `exec.CommandContext`
5. 主机、标签、路径和结果大小受到**限制**
6. 机密来自**权限检查的文件**,而非 `RESTIC_PASSWORD_COMMAND`
7. 错误是**结构化和编辑过的**
8. 每个工具都报告一个明确的**成本类别**(`light` / `moderate` / `expensive`)
## 要求
- Go 1.25+(构建)
- restic **0.17.1+** 在 `PATH` 上(或配置中的 `restic_binary`);已测试 0.19.x
- 支持本地标准输入输出服务器的 MCP 客户端
## 安装(从源代码)
```bash
git clone https://github.com/ThomasCrouzet/restic-defensive-mcp.git
cd restic-defensive-mcp
make build
./bin/restic-defensive-mcp --version
```
## 快速开始
1. 创建密码和仓库位置文件(常规文件,非符号链接)。
避免将密码放在 shell 历史记录中:
```bash
secret_dir="${XDG_CONFIG_HOME:-$HOME/.config}/restic-defensive-mcp"
install -d -m 700 "$secret_dir"
install -m 600 /dev/null "$secret_dir/repository"
printf '%s\n' '/path/to/your/repo' > "$secret_dir/repository"
install -m 600 /dev/null "$secret_dir/password"
"${EDITOR:-vi}" "$secret_dir/password"
```
2. 复制并编辑 `config.example.yaml`,包括上述两个文件路径。
3. 运行新构建的二进制文件:
```bash
./bin/restic-defensive-mcp --config /path/to/config.yaml
```
4. 使用 `--config` 将您的 MCP 主机指向二进制文件(仅标准输入输出传输)。
示例 MCP 主机代码段(说明性):
```json
{
"mcpServers": {
"restic-defensive": {
"command": "/absolute/path/to/restic-defensive-mcp",
"args": ["--config", "/etc/restic-defensive-mcp/config.yaml"]
}
}
}
```
## 工具(确切地七个)
| 工具 | 成本 | 目的 |
|------|------|---------|
| `restic_capabilities` | 轻量 | 版本、限制、后端、警告 |
| `list_repositories` | 轻量 | 配置的 ID 和允许列表(无 URL) |
| `list_snapshots` | 轻量 | 有限的 snapshot 列表 |
| `get_snapshot` | 轻量 | 一个 snapshot 元数据 |
| `browse_snapshot` | 中等 | 允许列表路径内的目录列表 |
| `find_files` | 中等-昂贵 | 简单的 glob 查找(非任意正则表达式) |
| `repository_stats` | 中等-昂贵 | 允许列表模式的大小/计数统计 |
**没有** `run_restic`、`backup`、`restore`、`forget`、`prune`、`unlock`、
`check --read-data` 或仓库 URL 参数。
完整的请求/响应形状:[docs/tool-contract.md](docs/tool-contract.md)。
## 配置
参见 [config.example.yaml](config.example.yaml)。原则:
- MCP 调用者仅传递 `repository_id`
- 优先使用 `repository_file` + `password_file` 而非内联机密
- 仓库位置和后端凭证在启动时加载和密封;
更改其源文件不会重新定位正在运行的服务器
- `allowed_hosts` / `allowed_tags` / `allowed_paths` 是**强制**,而非装饰
- 空允许列表意味着**无限制**该维度;服务器在启动时记录警告(`empty_allowlist`),以便操作员注意到
- 未知 YAML 键和多个 YAML 文档被拒绝
- 机密文件必须是常规的非符号链接文件;Unix 需要模式 `0600`
或更严格,而 Windows 部署必须强制执行等效的 NTFS ACL
- `find_files` 需要显式 `path`,当允许多个根目录被允许时
- 运行时限制:零变为默认值;超出绝对最小/最大值的值被**拒绝**(而非静默钳制)。默认值是推荐的起点;
值可能仅提升至这些上限
## 支持的后端(v0.1)
| 后端 | 支持 |
|---------|-----------|
| 本地文件系统 | 是(仅绝对路径) |
| S3 | 是(通过允许列表环境文件进行凭证管理) |
| Backblaze B2 | 是 |
| REST 服务器 | 是 |
| SFTP | **否**(启动 `ssh`) |
| rclone | **否**(启动 `rclone`) |
| 其他 | **否** |
兼容性说明:[docs/restic-compatibility.md](docs/restic-compatibility.md)。
## 本地影响:缓存和锁
检查**不是**磁盘或仓库锁定表上的纯 no-op:
- restic 可能创建或更新**本地缓存**(`cache_dir` 或默认值)
- restic 可能在 snapshots/ls/find/stats 期间获取**仓库锁**
此服务器从不故意改变备份内容或 snapshot 集。
它也从不运行完整的数据验证(`restic check --read-data`)。
`repository_stats` 仅回答大小/计数问题。
详情:[SECURITY.md](SECURITY.md)。
## 隐私限制
snapshot 文件名、路径、主机、标签、大小和时间戳是**敏感的**。
此服务器:
- 从不返回文件**内容**
- 从不返回仓库 URL 或机密文件路径
- 限制列表和查找结果
- 清除名称中的控制字符
- 默认情况下将审计日志保持在不包含完整路径的状态
## 演示(本地临时仓库)
```bash
make demo
```
针对 `internal/testrepo` 创建的临时 restic 仓库运行端到端测试框架。
它演练每个 MCP 工具,证明路径拒绝和 mutation 工具的缺失,然后删除夹具。
仓库操作保持本地;
在全新 checkout 时,可能会发生正常的 Go 模块下载。
## 开发
```bash
make fmt
make lint
make test # 单元测试
make race # 竞争检测器
make fuzz-smoke # 每个模糊目标的短时间运行
make integration # 真实的 restic 临时仓库(需要 restic)
make vet
```
## 依赖项
| 模块 | 原因 |
|--------|-----|
| `github.com/modelcontextprotocol/go-sdk` | 官方 MCP Go SDK(标准输入输出服务器/客户端) |
| `gopkg.in/yaml.v3` | 配置文件解析 |
Restic 本身是一个外部二进制文件,而非 Go 模块。
## 非目标
- 备份编排或调度
- 恢复 / 删除 / 修剪 / 解锁 / 修复
- 通用 restic CLI 代理
- Autorestic 配置导入
- 远程多租户 API
- 作为常规工具的完整 `restic check --read-data`
- 遥测或自动更新
## 许可证
MIT — 参见 [LICENSE](LICENSE)。