关于 ShieldFive
@shieldfive/mcp 是用于整理 ShieldFive 保险库文件和获准本地文件夹的 MCP 服务。它在本机解密保险库数据,上传验证后才将原件移入回收区,并支持撤销更改。
主要功能
- 01浏览和搜索保险库文件
- 02查看存储用量并查找重复项
- 03重命名、移动和移入回收区
- 04验证上传后再移动原件
- 05离线整理获准的本地文件夹
- 06预览更改并支持撤销
适用场景
- 01将验证过的文件移入 ShieldFive 保险库
- 02查找重复文件和大文件
- 03整理授权文件夹中的文件
- 04让助手读取选定的文本文件
MCP 相关
该服务通过 MCP 工具向兼容的助手提供保险库和本地文件操作。破坏性操作采用预览与确认流程;客户端支持时,还会通过 MCP elicitation 请求批准。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @shieldfive/mcpSHIELDFIVE_GRANT密钥Connection string from ShieldFive → Settings → AI assistants. Optional: `npx @shieldfive/mcp login` stores it in the OS keychain instead.SHIELDFIVE_MCP_ROOTSLocal directories the local tools may use, separated by the platform path separator. Optional.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
10收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | 否 | 要扫描的目录。不指定则扫描所有已配置的根目录。 |
| limit | integer | 否 | 返回的最大行数,最多 10,000。 |
| sort_by | enum | 否 | path | size | modified |
| max_files | integer | 否 | 达到此文件数后停止,最多 1,000,000。达到上限时,结果会注明。 |
| include_hidden | boolean | 否 | 包含点文件和点目录。 |
详细介绍
@shieldfive/mcp
一个 Model Context Protocol 服务,可让 Claude、ChatGPT、Cursor 或本地模型处理两类内容:
- 你的 ShieldFive 保险库,采用端到端加密:将文件从电脑移入保险库——每个文件都会在本机加密、上传、读回并逐字节核对,然后才将原文件移入本地废纸篓文件夹,而非删除;还可查找重复文件、查看空间占用情况、重命名、移动文件以及将文件移入回收站。它只处理你授予访问权限的文件夹,在你的电脑上解密,且每项更改都可以撤销;
- 你自己磁盘上的文件夹:执行相同的整理任务,完全不需要网络访问。
ShieldFive 的服务器永远不会看到明文文件名或文件内容字节,即使运行此服务时也是如此。解密在你的电脑上的此进程中进行。之后助手如何处理读取到的内容,则是另一回事,详见安全模型。
在 60 秒内连接你的保险库
需要 Node 20 或更高版本。
-
将此服务添加到你的助手。对于 Claude Desktop,将以下内容添加到
claude_desktop_config.json(Cursor 使用~/.cursor/mcp.json中的相同配置块):{ "mcpServers": { "shieldfive": { "command": "npx", "args": ["-y", "@shieldfive/[email protected]"] } } }对于 Claude Code:
claude mcp add shieldfive -- npx -y @shieldfive/[email protected]版本号是有意固定的:此服务可以读取你的连接,因此请固定为你已审核的版本;决定升级时再更改版本号,而不是运行最新发布的版本。
-
重启助手,并让它整理我的 ShieldFive 保险库。它会调用
vault_connect,在浏览器中打开 ShieldFive。 -
在该标签页中,选择文件夹、只读或读取并整理,以及过期时间(1 小时至 90 天),然后点击授权。
设置就这么简单:连接会直接传送到运行在你电脑上的服务——通过 127.0.0.1,绝不会经过 ShieldFive——并存储在系统钥匙串中。无需手动复制任何内容。
若要在开始对话前连接,请运行 npx -y @shieldfive/[email protected] login:
打开同一个浏览器页面,得到相同结果。对于没有浏览器的机器,login --paste 可使用你从“设置”→“AI 助手”复制的连接字符串。
npx @shieldfive/[email protected] status 会显示已配置的连接,以及 ShieldFive 是否仍接受该连接。npx @shieldfive/[email protected] logout 会从钥匙串和下方的备用文件中移除连接。在 ShieldFive 中撤销连接,才会在所有位置切断访问权限。
如果无法使用系统钥匙串(例如没有 Secret Service 的 Linux 机器),连接会改为保存到只有你的用户帐户可读取的文件中:macOS 上为 ~/Library/Application Support/shieldfive-mcp/connection,Windows 上为 %APPDATA%\shieldfive-mcp\connection,Linux 上为 $XDG_CONFIG_HOME/shieldfive-mcp/connection(默认值为 ~/.config),或 $SHIELDFIVE_MCP_CONFIG_DIR/connection。服务会在采用此方式时告知你。该文件未经加密,因为用于加密它的任何密钥都会存放在同一磁盘上;它通过文件权限(0600)进行保护,就像 SSH 密钥一样。如果没有此文件,每次重启都需要重新建立连接,而 ShieldFive 会限制同时有效的连接数量。之后成功保存到钥匙串时,该文件会被删除。
对于 CI,请改为将连接字符串设置为 SHIELDFIVE_GRANT。任何能够读取服务环境的程序都能读取该连接,因此只要有钥匙串可用,就优先使用钥匙串。在钥匙串中保存了另一个连接的机器上,设置 SHIELDFIVE_GRANT=none 可让一个客户端仅在本地运行。
如何确保浏览器交接过程安全可靠
- 页面只接受端口号,不接受回调 URL,并自行构建
http://127.0.0.1:<port>/callback。精心构造的链接无法将你的连接发送到本机以外的任何地方。 - 监听器只接受一次请求:向
/callback发送 POST 请求,Host必须恰好是环回地址(因此会拒绝重新绑定的 DNS 名称),不能带有 ShieldFive 以外的Origin,并且会以恒定时间比较 256 位状态值。之后监听器便会关闭。 - 连接字符串通过表单正文传递,绝不会出现在 URL 中,因此不会留在浏览器历史记录里。
- 监听器仅在授权连接期间运行,最长不超过 10 分钟。
安全模型
简单来说:
- 连接字符串包含两项内容。 一项是服务器在每次请求时都会检查的 令牌,另一项是永不离开你设备的 密钥。ShieldFive 只存储令牌的哈希值,从未见过该密钥。
- 该密钥只能打开你选择的文件夹。 创建连接时,浏览器会使用由该密钥派生的密钥来封装这些文件夹的密钥,不会封装其他任何内容:既不封装你的保险库根密钥,也不封装你的密码或后量子密钥。子文件夹通过保险库常规的文件夹密钥链打开。未选择的文件夹无法通过此服务器持有的任何内容打开。
- ShieldFive 会在每次请求时强制执行范围、过期时间和撤销规则。 此进程中的检查只用于提供更清晰的错误信息;服务器才是真正的安全边界。撤销连接后,该连接的下一次请求就会失败。不会缓存任何可能在撤销后仍然有效的内容。
- 解密在此处的内存中完成。 不会将明文、密钥或密文写入磁盘。保险库模块不会导入文件系统,并且有测试对此作出断言。
- 不会删除任何内容。
vault_trash会将项目移入回收站中属于该连接的文件夹。没有永久删除工具,并且该连接可访问的 API 不提供删除路由。每次重命名、移动和移入回收站的操作都会显示在 设置 → AI 助手 → 活动记录 中,并附带“撤销”按钮。 - 每项更改都会先预览。 修改类工具会报告操作计划,确认调用时必须携带该计划的令牌;如果期间项目发生了变化,请求就会被拒绝。
- 在客户端支持询问的情况下,由你自行批准破坏性更改。 对于标记为破坏性的工具(
vault_trash、vault_move_in、move_local、trash_local),确认调用时会先通过 MCP elicitation,在客户端自己的提示中向你展示已批准的计划;只有你接受后才会继续。拒绝不会产生任何更改。不支持 elicitation 的客户端仍会使用上述预览和令牌流程。 - 列出文件与读取内容相互独立。
vault_list_files、vault_search_files和vault_storage_stats只返回名称和元数据。只有vault_read_file会返回文件内容,另一个会解密文件内容的工具是vault_find_duplicates(它会返回哈希值)。使用--no-contents(或SHIELDFIVE_MCP_CONTENTS=off)启动服务后,这两个工具都不会注册;没有“读取”权限的连接也会被拒绝使用这两个工具。
此功能无法提供以下保护:
- 你的 AI 服务提供商能看到助手读取的内容。 文件名以及助手打开的文件内容都会发送给助手。对于云端助手,这意味着这些内容会像对话的其他部分一样发送给其服务提供商。避免这种情况的唯一方法是使用本地模型。
- 撤销无法让助手忘记。 助手已经读取的内容仍然会被记住。除此之外,不会留下任何其他内容:每次请求时,文件内容都会通过 ShieldFive 流式传输,因此撤销授权后不会有下载链接继续有效。
- 连接根据你创建连接时服务器向浏览器显示的内容建立。 此后每次扩展连接范围时,都会使用你自己的密钥进行检查,因此遭到入侵的服务器之后无法扩大连接范围。不过,在创建连接时,遭到入侵的服务器可能会错误标记你选择的文件夹。
- 复制的连接字符串是实时密钥,在过期或被撤销之前,可以访问其涵盖的文件夹。请将其保存在密钥链中(如果没有密钥链,则保存在服务器写入的权限为 0600 的备用文件中)。
- 文件中可能包含针对助手的指令。 此服务会将每个名称和文件内容都标记为数据,将文件内容围在文件本身无法关闭的代码块中,将
vault_trash限制为每次调用最多处理 50 个项目,要求每项更改都先预览,并确保每项更改都可以撤销。模型仍可能被诱导,在你授权的文件夹中犯下可撤销的错误。
完整设计(包括威胁模型以及每项决策背后的原因)见
docs/mcp-grants-design.md。
Vault 工具
vault_connect 始终可用。其余工具会在建立连接后注册——在对话过程中连接时,会通过
notifications/tools/list_changed 通知。下表按 ID 列出各项;路径仅供人查看。
| 工具 | 所需权限 | 功能 |
|---|---|---|
vault_connect | — | 在浏览器中打开 ShieldFive 以授权连接,并将连接信息存储在钥匙串中 |
vault_list_files | 读取 | 列出范围内的文件和文件夹,包括解密后的名称、路径、大小和日期 |
vault_search_files | 读取 | 按名称、路径、扩展名、大小或日期搜索;在本地对解密后的名称进行搜索 |
vault_storage_stats | 读取 | 提供总量、最大的文件夹和文件,以及按类型分类的统计 |
vault_find_duplicates | 读取(内容) | 在内存中解密并通过 SHA-256 比较大小相同的文件;设有预算限制,并会说明结果是否仅为下限 |
vault_read_file | 读取(内容) | 唯一会返回内容的工具:文本文件以围栏代码块形式返回,并标记为不可信内容(最多 1 M 个字符);其他类型仅返回详情 |
vault_rename | 整理 | 重命名文件或文件夹 |
vault_move | 整理 | 将项目移动到范围内的另一个文件夹 |
vault_create_folder | 整理 | 在范围内创建文件夹 |
vault_upload | 写入 | 加密本地文件并将其上传到 Vault,然后读回文件并进行比较,确认安全后才会告知你可以删除原文件 |
vault_trash | 整理 | 最多将 50 个项目移至此连接在回收站中的文件夹 |
vault_move_in | 写入 | 释放空间:最多逐个上传并验证 50 个本地文件;只有在副本读回内容完全一致后,才会将各原文件移至本地回收站——只需一次授权,不会删除任何内容 |
用户可能遇到的限制:
- 从手机或 CLI 上传的后量子文件会显示为
readable: false,直到你下次在网页上打开 ShieldFive,为连接添加所需的密钥。在网页应用中上传的文件则可立即使用。 - 首次列出大型 Vault 需要一些时间。 每个名称的 Argon2id 运算约需 70 ms,并由 CPU 核心分担(在 8 核 CPU 上处理 2,000 个名称约需 20 秒)。名称会缓存在内存中,供本次会话后续使用。
- 整个 Vault 连接最顶层的项目可以读取并移入文件夹,但不能在原处重命名,也不能将任何项目移到最顶层。它们的名称由 Vault 根密钥加密,而连接不会持有该密钥。
- 上传需要允许添加文件的连接(授权时选择“读取、整理和添加文件”),以及上传额度(届时由你选择)。每个文件最多 512 MB。上传和移动操作会读取本地文件,因此服务器除了需要连接外,还需要根目录。
- 移入文件本身不会释放空间。
vault_move_in会将每个已验证的原文件放入.shieldfive-mcp-trash,并附上列明 Vault 副本的清单;清空该目录后,空间才会恢复。
本地文件
软件包名称后列出的每个路径都是一个根目录。本地工具只能在这些目录中读取和写入,不能访问其他位置,也不会发出网络请求。
npx @shieldfive/[email protected] ~/Documents ~/Downloads
在 claude_desktop_config.json 中:
{
"mcpServers": {
"shieldfive": {
"command": "npx",
"args": ["-y", "@shieldfive/[email protected]", "/Users/you/Documents", "/Volumes/Archive"]
}
}
}
配置了连接但未配置根目录时,只会注册 Vault 工具。配置了根目录但未配置连接时,只会注册本地工具,服务器的行为与 0.2.0 完全相同。两者都配置时,两类工具都会提供。
SHIELDFIVE_MCP_ROOTS 也可以添加根目录——两者会合并,而不是互为替代。根目录列表使用平台的路径分隔符分隔(macOS 和 Linux 使用 :,Windows 使用 ;):
SHIELDFIVE_MCP_ROOTS="/Users/you/Documents:/Volumes/Archive" npx @shieldfive/[email protected]
根目录前后的空白会被忽略。但在传给工具的路径中,空白不会被忽略:其中的每个字符都是路径的一部分。
先看看它的效果
npm run demo
demo/run-demo.mjs 会在临时目录中创建五个文件:两个内容相同但名称不同的文件、一个大小相同的干扰文件、一个 12 MB 的归档文件,以及一个两年前的 PDF;随后对这些文件运行读取工具,预览一次移入回收站的调用,再确认操作并显示清单。它不会触碰该目录之外的任何内容,并会在最后删除该目录(--keep 会保留该目录)。
本地工具无法做到的事
它们无法判断某个本地文件是否已存在于你的保管库中。 根据名称和大小将本地文件与保管库列表进行匹配,可能会导致工具删除某项内容的唯一副本,因此本服务不会猜测。
它们无法阻止结果传送给你的 AI 服务提供商。 本地工具不会发起网络请求,这一事实仅代表它本身所说明的内容: 它们返回的所有信息——路径、文件名、大小、日期以及报告的摘要——都会发送给调用它的 AI 客户端;如果该客户端是云端助手,这些名称就会像对话中的其他内容一样传送给助手的服务提供商。请据此选择根目录。
它绝不会仅凭文件名和大小推断两个文件相同。 重复文件检测会读取两个文件,并比较其内容的完整 SHA-256。按名称匹配可能会导致去重工具删除某项内容的唯一副本;而确保判断正确所需的代价,只是几秒钟的磁盘 I/O。
哈希计算有预算限制,因此预算可能导致结果不完整。候选文件会按大小分组;对于大于 64 KiB 的文件,会先根据前 64 KiB 的哈希进行筛选,然后再通过完整摘要确认。每次读取(无论是哪种读取)都会计入 max_files_hashed,默认值为 20,000。预算用尽后,剩余文件将不再计算哈希——预算耗尽所在的组会部分计算,优先处理最旧的副本——结果会说明有多少文件以及多少空间未经检查。各组按大小从大到小处理,因此预算紧张时保留下来的,是价值最高的文件。
可回收空间按磁盘上的单个文件计算。硬链接到同一个文件的多个名称只算一份,因为移除其中一个不会释放空间。APFS 克隆——也就是 Finder 在 APFS 卷上使用“重复”功能创建的文件——也共享存储空间;但本服务无法通过读取任何信息来区分克隆和真实副本,因此如果将其移入废纸篓几乎或完全不释放空间,仍会报告为可回收。建议保留的副本,是修改时间最早的副本;如果时间相同,则选择路径较短的副本,然后按代码单元顺序选择路径,因此同一目录树总会建议保留同一份副本。
除一种情况外,它不会删除你的任何内容。 trash_local 会将文件移入其所在卷上的 .shieldfive-mcp-trash 目录,并写入 manifest.json,记录每个文件原本的位置。在你使用自己的文件管理器亲自删除该目录并自行撤销之前,不会释放任何磁盘空间。 工具会在输出中明确说明这一点,因此助手无法将这些空间报告为已回收。例外情况是跨卷执行 move_local,因为这必须通过复制完成:源文件会被移除,但只有在复制经过验证后才会移除——请参阅跨卷移动。
覆盖文件时也适用这一规则。使用 overwrite: true 调用 move_local 时,会先将目标位置已有的项目移入废纸篓,再由新项目取代;它不会将其删除。预览会告知将被替换的文件数和字节数,而不仅仅是正在移动的数量。如果随后移动失败,被替换的项目会被放回原处。
默认情况下,所有会更改内容的工具都不会执行任何操作。 如果调用时没有 confirm: true,工具会解析路径、检查是否位于允许范围内、准确报告将要执行的操作,然后停止。预览会执行与实际操作相同的检查,因此计划一旦报告拒绝,就意味着操作会被拒绝。
确认调用必须与预览中的计划一致。 预览会返回 plan_token;没有该令牌却设置 confirm: true 的调用会被拒绝。确认调用会根据当前文件系统重新制定计划,并将其与令牌批准的计划进行比较——包括路径、每个条目的类型、大小和修改时间,以及其下的文件数和字节数;如果任何内容发生变化,就会拒绝执行并指出变更之处。一个令牌只能执行一次更改,并会在十分钟后失效。因此,如果目录中新增了内容、出现了目标文件,或某个路径现在指向了不同的文件,调用就会被阻止,而不会悄悄扩大操作范围。
本地工具
| 工具 | 读取 | 写入 |
|---|---|---|
list_local | 文件、大小、日期 | — |
find_duplicates | 文件内容(SHA-256) | — |
find_large_files | 大小 | — |
find_old_files | 修改时间 | — |
storage_summary | 大小,按扩展名和目录分类 | — |
move_local | 源文件及其可能替换的目标内容的大小 | 移动文件、文件夹或符号链接;将被替换的目标移入回收站;跨卷时,先复制并验证,再删除源文件 |
rename_local | — | 原地重命名,绝不覆盖已有名称 |
create_local_folder | — | 创建目录 |
trash_local | 待移入回收站的子树大小 | 将内容移入项目所在卷上的回收站目录,并写入清单 |
默认值均可在每次调用时覆盖:list_local 返回 200 行,其他列表工具返回 100 行。find_large_files 的 min_bytes 默认为 100,000,000(100 MB)。find_old_files 的 older_than_days 默认为 365。find_duplicates 会跳过空文件(min_bytes 为 1),最多为其中 max_files_hashed 个文件(20,000)计算哈希,并返回 100 组;每组最多列出 50 个副本。storage_summary 会报告排名前 15 的扩展名和排名前 15 的目录。每次扫描在所有根目录中扫描到 max_files 个文件(200,000)后就会停止,并遍历根目录及其下方 64 层子目录。
每项覆盖值都有上限,由 MCP 架构和工具本身分别强制执行:limit 为 10,000 行,max_files 为 1,000,000,max_files_hashed 为 1,000,000,每次 trash_local 调用的 paths 为 1,000,路径长度为 4,096 个字符,new_name 长度为 255 字节。拒绝操作时,只会引用超长值的开头部分。
find_old_files 报告的是修改时间,这只是一个较弱的判断依据:某些复制操作会将其重置为复制日期,而未被触碰的文件也不一定是不需要的文件。工具会在自身的结果中说明这一点,避免助手把候选列表当作最终结论。
遍历并不完整
所有读取工具都以相同的方式遍历,并且默认跳过以下内容:
- 隐藏条目,除非传入
include_hidden: true。 - 按名称跳过的 19 个构建和缓存目录,无论它们出现在哪里:
node_modules、.git、.svn、.hg、.cache、.venv、venv、__pycache__、.next、.turbo、dist、build、target、Pods、.gradle、.tox、.mypy_cache、.pytest_cache,以及此服务自己的回收站。在代码目录之外,build、dist和target也是普通文件夹名称,因此这可能会排除真实数据——目前无法覆盖此列表。 - 符号链接,始终跳过,且无法覆盖。
这三类都会计数,并在结果的警告中报告,因此总数看起来过小时,你可以知道原因。但这也意味着 storage_summary 并不是磁盘使用情况工具:如果将它指向开发者的主目录,它会告知你这一点,但不会告诉你空间都用在了哪里。
如果在遍历完所有根目录之前就耗尽 max_files 预算,结果会在 scanned 下列出已遍历的根目录,在 not_scanned 下列出其余根目录,并在警告中点明这些目录。
清空回收站
此服务不会清空回收站,也无法做到这一点。trash_local 会将每个项目移入 .shieldfive-mcp-trash/<batch>/,该目录位于项目与其根目录之间、且处于项目所在卷上的最高目录中:通常是根目录本身;如果项目位于根目录内挂载的驱动器上,则是该驱动器的顶层目录。<batch> 由时间戳、进程 ID 和计数组成,因此不会有两次调用共用同一个值。项目旁的 manifest.json 会在移动任何项目之前写入,并列出每个项目的来源;如果某条记录中的 trashed_to 指向的位置不存在,则说明该项目已计划移入回收站,但尚未移动。要真正删除这些项目,需要你在查看其中内容后自行运行 rm -rf。这里的任何操作都不会自行释放磁盘空间。
挂载点无法移入回收站,因为根目录内不存在位于其自身卷上、能够容纳该挂载点的目录。如果 .shieldfive-mcp-trash 是符号链接或文件,trash_local 和会覆盖目标的 move_local 都会拒绝操作,而不会跟随它。
跨卷移动
rename(2) 无法跨卷,因此跨卷移动需要先复制,再移除源文件——这是本服务唯一会移除你创建内容的情况。这样做是为了确保任何阶段发生故障都不会丢失内容:
- 复制会先使用一个新的隐藏名称(
.shieldfive-mcp-incoming-<pid>-<n>)在目标位置旁创建,采用独占方式创建,并在不替换任何内容的情况下放置到位。原有内容不会受到影响。 - 如果文件夹中包含符号链接、FIFO、套接字或设备文件,则会在移除任何内容之前拒绝操作,因为复制无法忠实保留这些内容。
- 每个文件都会刷新到磁盘并与源文件进行比较——大小和 SHA-256 必须相同,且源文件自复制后不得有变化。如果任一检查失败,副本会被丢弃,源文件会保留。
- 源文件会逐个移除,且仅当每个文件仍是被复制的那个文件时才会移除;文件夹则只会在变为空文件夹后移除。移动期间发生变化或新出现的任何内容都会保留原处,并列在
source_left_in_place中。
如果中途崩溃,可能会在该隐藏名称下留下不完整的副本。副本放置到位之前,源文件始终完整保留。
取消
已取消的请求不会开始任何更改。trash_local 会在条目之间停止,不会在处理单个条目时停止,因此每个条目要么已移动并记录,要么完全未动;错误信息会说明具体情况。在源文件开始移除之前取消的 move_local 会回滚,包括恢复被其替换的内容;超过这一阶段后则会继续完成,因为停止会导致两边都只留下部分目录树。MCP SDK 不会对已取消的请求发送响应,因此已取消调用的执行情况会写入服务的 stderr 日志;对于回收操作,还会写入清单。
路径包含机制的工作原理
助手提供的每个路径都会按原样使用,因此 "report " 永远不会变成 "report",并且在任何内容读取它或通过它写入之前,都会先使用 realpath 解析——沿着每一条符号链接。结果必须位于已配置的根目录内。基于分隔符的边界检查意味着,/data/roots-evil 不会被视为位于根目录 /data/root 内。
顺序正是关键所在。对提供的路径进行字符串检查会被 .. 绕过;在调用 path.resolve 后检查仍会被符号链接绕过,因为 /allowed/link -> /etc 解析后虽然是 /allowed 下的字符串,读取的却是 /etc。先解析链接就能同时堵住这两种情况,这也是目录遍历使用 lstat 且绝不跟随链接的原因——遍历过程中跟随的链接,是路径包含检查根本无从发现的。
对于尚不存在的目标——例如移动目标、新文件夹——会先解析最近的现存祖先,再将其余路径重新拼接,以便在写入前而不是写入后,发现通过符号链接父目录写入的情况。无论何处,只要写入操作会经过目标不存在的符号链接,就会拒绝该链接:realpath 会像处理不存在的路径一样报告它,而如果相信这个结果,就可能让复制操作落到链接指向的位置。
唯一不会跟随的是变更工具操作的项目本身。传给 move_local、rename_local 或 trash_local 的符号链接会像 mv 处理它那样,被移动、重命名或移入回收站;其指向的内容不会受到影响,只有链接自身所在的位置必须位于根目录内。移动目标也一样:其中的符号链接,无论是否悬空,都是一个现存条目;overwrite: true 会将其移入回收站,而不会把它当成要移入的文件夹。请提供该文件夹的真实路径。
rename_local 和 move_local 不会替换检查目标后才出现的内容。文件会先通过硬链接获得新名称,然后才从旧名称解除链接;符号链接会重新创建;文件夹则会重命名到刚刚创建的空占位项上。因此,如果中间出现了内容,操作就会失败,而不是覆盖它。在不支持这类操作的情况下——例如 FIFO、套接字和设备文件、不支持硬链接的文件系统(如 FAT 和 exFAT),以及 Windows 上的文件夹——工具会检查后再重命名,因此在那一瞬间创建的文件会被替换。
测试断言的内容
npm test 会运行 256 项测试。以下是值得了解的测试:
- 如果服务器将某个文件的密文和密钥与同级文件的互换,该文件会被拒绝(后量子格式和 AES-GCM v1 格式):文件头中的文件 ID 必须与记录中的一致。将 v4 名称复制到使用 UUIDv7 ID 创建的记录上时,该名称不会显示;上传时,系统会根据新建记录的 ID 对文件名和文件头进行封装。
- 指向根目录之外的符号链接会被拒绝,无论是读取还是写入时都会如此;写入路径上的悬空符号链接也会被拒绝。
- 如果
.shieldfive-mcp-trash是指向根目录之外的符号链接,就会被拒绝,并且不会通过它写入任何内容。 - 两个文件即使名称和大小相同,只要内容不同,就不会被报告为重复文件;一个文件的两个名称也不会被计为可回收空间。
trash_local会让字节数据在同一卷的新位置仍可读取,并报告space_freed_bytes: 0。- 对于挂载在根目录内的 RAM 磁盘,跨卷移动时,如果副本损坏或复制过程中源文件发生变化,系统会保留源文件;如果现有文件使用旧的暂存名称,也不会动它;如果文件夹中含有 FIFO,则会拒绝移动;绝不会通过悬空符号链接写入。这些测试会在 macOS 上运行,其他平台会跳过。
- 如果在检查与重命名之间,有文件出现在新名称处,则不会替换该文件。
- 取消请求时不会移动任何内容;中途取消批量移入垃圾箱时,会准确说明已移动的内容。
- 只有
src/vault/api.mjs会调用fetch,且只会访问 https ShieldFive 源。没有任何本地工具模块会从 vault 部分导入内容,读取的环境变量也仅有SHIELDFIVE_MCP_ROOTS、SHIELDFIVE_MCP_CONTENTS、SHIELDFIVE_GRANT和SHIELDFIVE_API_URL。 - 上传时会先打开文件(使用
O_NOFOLLOW),再通过fstat检查打开的文件描述符:如果是符号链接,或者自计划生成后文件被替换或改写,就会拒绝上传,确保加密的内容与检查过的内容完全一致。 - 对于已确认的破坏性调用,如果客户端支持询问功能,系统会通过该功能向用户确认;用户拒绝后不会有任何变化。
- 使用
--no-contents时不会启用内容工具;仅支持列表操作的连接也无法使用这些工具。 - 浏览器交接使用的是此软件包中唯一的入站套接字和唯一的子进程:
src/vault/connect.mjs会在127.0.0.1上绑定一个随机端口,不会自行打开任何连接,并且会通过固定命令启动浏览器,不使用 shell。若请求来自其他源、使用其他状态、发往其他Host、使用其他方法,或是在第一次请求之后到达,都会被拒绝。 - vault 模块不会导入任何文件系统模块,因此解密后的数据无法写入磁盘。没有任何模块会自行使用密码、HMAC 或 KDF。所有加密操作都来自
@shieldfive/crypto。 - 使用真实 MCP 客户端,针对一个能以三种 vault 格式提供真实密文的内存中 ShieldFive 服务进行测试:
- 文件只通过 grant 的密钥解密,作用域之外的内容不会被列出、读取,甚至不会被请求;
- 连接被撤销或过期后,紧接着的下一次调用就会失败;
- 重命名和移动会重新封装名称与密钥,因此所有者自己的密钥仍可打开这些文件;
- grant 密钥和令牌绝不会出现在任何请求正文或路径中。
- 如果文件内容要求助手将所有内容移入垃圾箱,返回的内容会被包在助手无法闭合的围栏内。读取它只会发出读取操作,而包含 51 项的移入垃圾箱调用会被拒绝。
- 没有配置连接时,真实 MCP 客户端通过真实的 stdio 传输恰好能看到 9 个本地工具,包括服务器通过 npm 安装时使用的符号链接启动方式。
网络断言有一项值得说明的限制:它证明的是 src/ 的行为,而不是依赖树可能采取的行为。@modelcontextprotocol/sdk 提供了供其他人的服务使用的 HTTP 传输。弥补这一差距的方式是让 server.mjs 只导入 stdio 传输、不导入任何 HTTP 传输;这一点也有断言覆盖。
限制
- 计划检查缩小了预览与执行之间的差距,但无法消除差距。 比较发生在已确认的调用中,因此在检查完成到实际写入之间仍可能发生变化。每个工具都会在写入前立即重新检查自己的目标位置,因此这段时间很短,但并非不存在,而且基于路径的工具都无法做得更好。
- 计划会绑定其中指定的内容。 对于目录,绑定的是目录项本身,以及其下的文件数和字节数——这足以发现内容新增、消失或大小变化,但无法发现同一秒内原地编辑且长度完全相同的文件。
- 大小指的是文件内容大小。 不包括目录开销,也不考虑文件系统压缩、稀疏文件、硬链接和 APFS 克隆,因此总数不会与磁盘工具完全一致;
storage_summary会统计硬链接文件的每个名称。 - APFS 克隆看起来就像副本。
find_duplicates会将其报告为可回收内容,而将其中一个移入废纸篓几乎不会释放空间,甚至完全不会释放。 - 扫描默认有上限:最多 200,000 个文件,且仅扫描根目录及其下 64 层子目录。达到上限时,结果会在摘要行和字段中说明:否则,达到上限而停止的扫描结果看起来会与已完成的扫描完全一样,助手也会把部分列表当成完整列表来报告。
- **跨卷行为仅在 macOS 上测试过,**测试使用的是挂载在测试自身临时目录中的 RAM 磁盘。
- Windows 未经测试。 代码没有使用仅限 POSIX 的 API,路径处理通过
node:path完成,但没有人在 Windows 上运行过它。
SECURITY.md 介绍其余内容:检查时与使用时之间的时间差、重命名仍可能替换内容的时间窗口、硬链接、继承环境的含义与不含义,以及无网络声明的适用范围。
隐私政策
除非配置了 ShieldFive 连接,否则此服务不会向任何地方发送数据;配置后,也只会向 ShieldFive 的 API(https://shieldfive.com,或 SHIELDFIVE_API_URL 中的源)发送数据:
- 发送的内容: 保管库工具所需的请求——列表、读取和按 ID 进行的更改;上传时,还会发送此机器上生成的密文和封装密钥。文件名和内容在离开设备前都会加密;存储上传会直接发送到预签名存储 URL,绝不会携带连接凭据。
- 绝不会发送的内容: 明文文件名或内容、本地路径、有关本地文件夹的任何信息、遥测数据或分析数据。本地工具完全不会发起网络请求。
- ShieldFive 保留的内容: 连接记录和每个请求的记录(ID、操作、结果、时间——不含文件名、内容、IP 地址或用户代理),在账户存续期间保留,并随账户删除。请参阅 ShieldFive 隐私政策 第 3.9 节,了解这些记录、保留期限及相关权利。
- 助手收到的内容: 通过此服务读取的任何内容都会在此处解密,并发送给助手的运营方,受其条款约束——与对话中的其他内容相同。
- 凭据: 保存在操作系统密钥链中(或
SHIELDFIVE_GRANT),绝不会记录到日志或返回。只有在无法使用密钥链时,才会将其写入文件,且仅你的用户账户可读取(请参阅连接保管库)。
联系方式:[email protected];漏洞请联系 [email protected]。
安全
请将漏洞报告至 [email protected]。请参阅
SECURITY.md。
许可证
MIT。版本 0.6.3 及更早版本均以 Apache-2.0 发布。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。