mcpField
S
ShieldFive

ShieldFive

本地服务
shieldfive.com文件与存储01 周前更新

关于 ShieldFive

@shieldfive/mcp 是用于整理 ShieldFive 保险库文件和获准本地文件夹的 MCP 服务。它在本机解密保险库数据,上传验证后才将原件移入回收区,并支持撤销更改。

主要功能

  1. 01浏览和搜索保险库文件
  2. 02查看存储用量并查找重复项
  3. 03重命名、移动和移入回收区
  4. 04验证上传后再移动原件
  5. 05离线整理获准的本地文件夹
  6. 06预览更改并支持撤销

适用场景

  1. 01将验证过的文件移入 ShieldFive 保险库
  2. 02查找重复文件和大文件
  3. 03整理授权文件夹中的文件
  4. 04让助手读取选定的文本文件

MCP 相关

该服务通过 MCP 工具向兼容的助手提供保险库和本地文件操作。破坏性操作采用预览与确认流程;客户端支持时,还会通过 MCP elicitation 请求批准。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"shieldfive": {
"command": "npx",
"args": [
"-y",
"@shieldfive/mcp"
],
"env": {
"SHIELDFIVE_GRANT": "<SHIELDFIVE_GRANT>",
"SHIELDFIVE_MCP_ROOTS": "<SHIELDFIVE_MCP_ROOTS>"
}
}
}
}
启动命令npx -y @shieldfive/mcp
需要配置的环境变量
SHIELDFIVE_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+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@shieldfive/mcp0.7.1

工具

10

收录的工具列表 · 更新于 2 周前

参数类型必填说明
pathstring否要扫描的目录。不指定则扫描所有已配置的根目录。
limitinteger否返回的最大行数,最多 10,000。
sort_byenum否path | size | modified
max_filesinteger否达到此文件数后停止,最多 1,000,000。达到上限时,结果会注明。
include_hiddenboolean否包含点文件和点目录。

详细介绍

@shieldfive/mcp

一个 Model Context Protocol 服务,可让 Claude、ChatGPT、Cursor 或本地模型处理两类内容:

  • 你的 ShieldFive 保险库,采用端到端加密:将文件从电脑移入保险库——每个文件都会在本机加密、上传、读回并逐字节核对,然后才将原文件移入本地废纸篓文件夹,而非删除;还可查找重复文件、查看空间占用情况、重命名、移动文件以及将文件移入回收站。它只处理你授予访问权限的文件夹,在你的电脑上解密,且每项更改都可以撤销;
  • 你自己磁盘上的文件夹:执行相同的整理任务,完全不需要网络访问。

ShieldFive 的服务器永远不会看到明文文件名或文件内容字节,即使运行此服务时也是如此。解密在你的电脑上的此进程中进行。之后助手如何处理读取到的内容,则是另一回事,详见安全模型。

在 60 秒内连接你的保险库

需要 Node 20 或更高版本。

  1. 将此服务添加到你的助手。对于 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]

    版本号是有意固定的:此服务可以读取你的连接,因此请固定为你已审核的版本;决定升级时再更改版本号,而不是运行最新发布的版本。

  2. 重启助手,并让它整理我的 ShieldFive 保险库。它会调用 vault_connect,在浏览器中打开 ShieldFive。

  3. 在该标签页中,选择文件夹、只读或读取并整理,以及过期时间(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 发布。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。