# MCP 工具列表变化如何改变实时会话

实时 MCP 会话不应继承你批准它时尚不存在的工具所带来的信任。工具发现看起来像无害的底层工作，直到智能体可以凭借同样的对话惯性、使用背后的同一组凭据，调用一个新出现的操作。到了这一步，操作范围已经发生变化，即使没有人修改提示词也是如此。

我见过工程师批准一次用于处理代码仓库的编码运行，然后在连接器新增部署或工单操作后，仍让它继续运行。常见的辩解是：「智能体只有完成工作所需的工具。」工具列表一变，这句话就不成立了。长时间运行的智能体应当比短暂运行的命令行进程获得更少的随意信任，而不是更多。

我采用的规则很简单：工具清单一旦变化就进行比较，判断权限发生了什么变化；如果变更新增或实质性改变了操作范围，就启动一次新的智能体运行。不要把这件事变成一场策略语言工程。关键在于保留人工决定的原意，让它在之后仍然代表你当初批准的内容。

## 工具列表就是权限清单

MCP 工具列表不是菜单，而是一组智能体可以提议，并且根据客户端不同可能实际调用的操作。每个条目都包含名称、描述、输入架构和行为，而这些行为往往会触及模型无法直接检查的服务。

模型上下文协议规范将工具描述为服务器向模型公开的、由模型控制的函数。这种表述很重要。客户端可以把描述展示给模型，但描述不是安全边界。调用能够执行什么，取决于架构和服务器端行为。

我检查工具清单时，会针对每个工具提出五个问题：

- 这个调用可以读取哪些数据？
- 这个调用可以改变哪些状态？
- 它的输出可以发送到哪里？
- 它使用哪项凭据或哪台主机？
- 它的参数是否能把范围扩大到名称暗示之外？

名为 `get_build_status` 的工具可能是只读且范围明确的。名为 `request` 的工具则可能在凭据允许的范围内读取、写入数据，并将数据发送到任意位置。名称让第一个工具容易批准，也让人容易低估第二个工具。

工具发现和权限经常被混为一谈。发现告诉智能体有哪些工具可用，权限决定智能体是否可以使用它。如果把这两个概念合并，会话中途刷新列表就会变成一次未经审查的权限变更。

## 新增工具应接受最严格的审查

当新增工具创建了通往数据、命令或外部人员的新路径时，就需要重新启动运行。即使新工具看起来与智能体已经执行的工作有关，也同样如此。

假设智能体一开始拥有 `repo_search`、`read_issue` 和 `create_branch`。会话进行到一半时，服务器新增了 `post_comment`。有人可能因为智能体本来就会读取工单，而把这看成一个小扩展。它并不小。读取工单属于私密检索，发布评论则会把模型生成的文字发送给可能据此采取行动的人，还可能暴露对话或工作区中的细节。

默认情况下，应将以下新增操作视为实质性变化：

- 任何写入、删除、部署、发布或发送消息的操作。
- 任何目的地由参数决定的通用请求工具。
- 任何 shell 或 SSH 操作，包括标称为诊断用途的操作。
- 任何读取新仓库、新账户、新主机或新类别数据的操作。
- 任何使用范围比现有工具更广的凭据的操作。

新增的读取操作也可能需要重新启动。工程师有时只关注写入操作，却暴露了一个可以读取客户记录、构建日志或配置中保存的秘密的导出工具。数据外泄始于读取。

也有范围很窄的例外。如果只是为已经批准的固定操作增加第二个名称，并且你已经确认它访问相同的服务，使用相同的参数和凭据，那么可以让它留在当前运行中。但这种情况很少见，不能仅凭一份发布说明就授予例外。

## 工具被移除是信号，不代表权限已干净撤销

工具被移除后，应检查客户端，因为从新列表中消失，并不能证明运行中的智能体已经失去了访问权限。服务器可能更新了对外公布的列表，而客户端内存中仍保留着早先的列表。另一个客户端则可能立即刷新。你无法仅凭移除这一点安全地推断实际行为。

这在事件复盘中很重要。团队看到服务器移除了 `delete_environment`，以为危险已经过去，于是让旧会话继续运行。如果客户端已经解析了该工具定义，它可能仍会尝试调用。只要实现已经移除了处理程序，服务器就应该拒绝调用，但公布的发现结果和实际执行是两回事。两者都要验证。

暂停智能体，并记录变更前后的工具清单。如果你负责服务器，就在非生产环境中测试被移除的操作；如果不负责，就结束会话。不要询问模型它是否还拥有该工具。模型对自身上下文的描述并不可靠，不能用来解决授权问题。

移除还会带来一个更普通的后果：工具重命名可能首先表现为一个工具被移除、另一个工具被新增。因此，应比较定义，而不是只统计名称数量。

## 重命名必须证明等价

重命名可能只是外观变化，也可能隐藏了更宽泛的契约。危险情况往往出现在普通的维护工作中：`search_logs` 变成 `query_logs`，架构新增一个可选的 `project` 字段，服务开始接受远程项目标识符。名称几乎没变，权限却变了。

建立一份足够枯燥、能够在压力下使用的比较记录。对每个旧工具和新工具，记录名称、描述、JSON Schema、声明的只读或破坏性注解（如果有）、后端端点或命令、凭据身份，以及已知副作用。JSON Schema 无法揭示服务器的全部行为，因此应把它当作证据，而不是完整答案。

下面是一种实用的最小工具清单格式：

```json
{
  "name": "query_logs",
  "inputSchema": {
    "type": "object",
    "properties": {
      "project": {"type": "string"},
      "query": {"type": "string"}
    },
    "required": ["query"]
  },
  "destination": "logs-api",
  "credential": "logs-read",
  "effects": ["read"]
}
```

它要避免的不是请求格式错误，而是防止审查者在接受重命名时，忽略新增的 `project` 选择器。这个选择器可能把本地查询变成跨项目检索。

如果旧记录和新记录在目的地、凭据、影响或参数范围上存在差异，就把它视为新增能力并重新启动。如果无法确认后端行为，也应重新启动。「大概一样」不算审查结论。

## 架构变化比描述变化更重要

工具可以保留原来的名称，却变得更加危险。输入架构能帮助你发现问题通常出现在哪里：新的 URL 字段、路径字段、账户选择器、自由格式命令字符串、收件人列表，以及会改变执行模式的可选标记。

假设某个工具最初接受这样的输入：

```json
{
  "type": "object",
  "properties": {"issue_id": {"type": "string"}},
  "required": ["issue_id"]
}
```

后来它接受了 `include_private_notes` 或 `destination_url`。第一个字段改变了工具读取的内容，第二个字段引入了一条向外发送数据的路径。它们都不需要配上一个引人注目的新工具名称，才会触发新的批准边界。

描述也会变化。服务器可能把「获取发布详情」改成「获取并更新发布详情」，却不改变架构。你应该阅读描述，但不要止步于此。询问负责人实际执行的是哪个处理程序、使用什么主体，以及是否会发起出站请求。可靠的回答应当指出具体命令、端点或服务账户。「这只是一个内部 helper」并不能提供有用信息。

MCP 规范允许工具元数据和注解，但客户端是否支持，以及如何解释它们，都可能不同。可以把它们当作有帮助的标签，但不要仅凭「只读」这样的标签做出批准决定，尤其是在调用最终会触及通用端点时。

## 新的运行会创建一个能够解释清楚的边界

当工具清单发生变化后，原先的批准会代表不同的含义，就必须启动新的运行。这个边界具有实际价值：它会停止当前进程，让下一次批准对应到一个明确的可执行对象，并在记录中区分新旧两组调用。

流程很短：

1. 工具清单意外变化时，停止后续工具调用。
2. 保存新旧清单，包括架构和可用的服务器版本信息。
3. 将每项差异标记为移除、重命名、修改或新增。
4. 如果差异扩大了数据访问、影响范围、目的地范围或凭据范围，就重新启动。
5. 只有在能够用通俗语言说明新运行允许执行哪些操作后，才批准它。

不要为了完成仪式而重新启动。重新启动的原因应是某项具体变化使早先的批准失效。这个区别能让规则真正可用。每次拼写修正都重启的团队最终会忽略规则，而会因新增权限而重启的团队会逐渐学会识别这类变化。

按会话授权在批准绑定到一个会退出的进程时效果很好。Sallyport 采用的就是这种模式：它会显示新智能体进程的代码签名权限，然后在进程退出前保持这次运行的批准。如果工具清单发生变化，且智能体能够请求的操作范围扩大，仍然应结束该进程。

## 应让高风险操作保持逐次批准

对于后果太大、不适合隐藏在宽泛会话批准中的操作，应采用逐次调用批准。它适用于生产环境变更、破坏性操作、公开消息、金融操作，以及可能将敏感数据发送到运行时选择的目的地的请求。

有些团队试图用一长串自然语言条件解决这个问题。这种做法一直很流行，因为它承诺减少中断，同时也让审查者在智能体运行时面对一个脆弱、必须理解的规则引擎。更安全的设计是保留少量清晰可见的选择：保险库锁定时拒绝，针对有限运行批准一个已知进程，或要求每次使用特定凭据时点击确认。

Sallyport 的逐次调用密钥设置就属于最后一种情况。智能体可以请求执行操作，但永远不会以明文形式收到 API 密钥或 SSH 密钥。这样能减少凭据泄露，同时由用户决定这一次请求是否应离开设备。

不要把每个操作都设置为逐次批准。如果一次无害的读取操作也不断弹出提示，人们会不看内容就批准。把阻力放在能够保留判断力的地方。

## 审计记录必须经得起变更争议

审计轨迹应能回答：运行的是哪个智能体进程，它调用了什么，以及有人是否在调用前改变了可用的操作范围。当争议是「批准会话时，这个工具是否可用」时，只记录最后一次请求并不够。

在会话记录旁保存带有稳定摘要的工具清单快照。记录工具名称、规范化架构、服务器身份，以及客户端观察到它的时间。发生变化时，保留两份快照和分类决定。开始时不需要复杂的数据库，一份带签名或具备防篡改能力、包含原始发现响应的记录，就远胜于第二天早上凭口头回忆重建的经过。

Sallyport 会在 Sessions 日志中记录智能体运行，并在 Activity 日志中记录单独的调用。这两者都来自同一份采用加密哈希链的审计日志。它的 `sp audit verify` 命令可以在离线状态下对密文验证哈希链，无需保险库密钥。这些记录在发生变化后很有价值，但前提是团队同时记录了该运行看到的是哪份工具清单。

哈希链可以检测保留条目的篡改，但不能证明每个组件记录了每个事件，也不能告诉你某次批准是否合理。要把这些结论分开。让一种机制回答它无法回答的问题，会削弱安全审查。

## 把动态发现当作部署事件

如果你的 MCP 服务器可以在不重启客户端的情况下改变工具，那么团队实际上已经把新的操作范围部署到了实时控制通道中。应为这类事件建立发布纪律：明确负责人，审查差异，说明预期权限，并留下日后调查人员能够读懂的记录。

第一项实际措施，是在每次智能体运行开始时加入工具清单快照，并在任何刷新生效前进行比较。不要等到发生重大事件才开始。悄无声息的重命名，只增加一个可选目的地字段，正是最容易从忙碌而又称职的人眼皮底下溜走的变化。
