工具投毒
描述本身就是提示词的一部分
一个工具的名称、描述和参数 schema 都会以文本形式送进模型。服务器可以写一段描述,让模型去读某个文件并把内容当作参数传过去,而模型没有任何办法把它和一条正当指令区分开。
MCP 服务器
它在你的机器上以你的权限运行,和任何一个包一样。和包不一样的是,它的工具描述会落进模型的上下文里,在那里被当成指令来读。MCP 服务器安全必须把这两半都算进去。
没有一条是稀奇的。它们都源自协议本身的工作方式,以及人们把凭据放在了什么地方。
工具投毒
一个工具的名称、描述和参数 schema 都会以文本形式送进模型。服务器可以写一段描述,让模型去读某个文件并把内容当作参数传过去,而模型没有任何办法把它和一条正当指令区分开。
凭据交接
本地 MCP 服务器以子进程的形式跑在 stdio 上。它们拿到的是你交给它们的环境,而那通常意味着某个变量里一个长期有效的令牌。从那一刻起,凭据就不在你手上了。
schema 漂移
工具描述和 schema 可以在下一次更新时改变。你三月审过的那个服务器,未必就是六月在跑的那个,而协议里没有任何机制会把这种变化叫出声来。
令牌范围
远程服务器代你保管访问令牌。一个愿意接受或转发为其他资源签发的令牌的服务器,就成了一个很顺手的代理人,去够你从未授权过的东西。
没有记录
绝大多数配置根本没有记录:哪个 MCP 工具跑过、带了什么参数、打向哪个主机。麻烦按别人的时间表浮出水面,通常是在一张账单上,或者一封数据泄露通知里。
共同的线索是:读工具描述的是模型,而凭据正躺在服务器够得着的地方。把后半截解决掉,前半截就再也兑现不了。
本地 MCP 服务器和远程 MCP 服务器的认证方式完全不同,而这个差别比大多数安装指南暗示的要重要得多。
根本没有认证这一步。服务器是你启动的一个子进程,你用什么样的环境启动它,它就有多可信。你导出的那个令牌,现在也是它的令牌。
信任 = 你的环境
MCP 的授权规范建立在 OAuth 2.1 之上:客户端发现授权服务器,你在浏览器里同意,然后客户端持有一个短期访问令牌,背后跟着一个刷新令牌。
信任 = 令牌的范围
这里没有一条需要买产品。它就是你会给任何依赖做的那套审查,外加两个 MCP 特有的问题。
装一个确定的版本,而不是 latest 今天恰好指向的那个。会自我更新的服务器,可以在你批准之后改掉它的工具描述。
README 是写给你看的,描述是写给模型看的。去读模型真正会收到的那一份。
stdio 服务器是一个带着你权限的子进程。给它能把活干完的最小范围凭据,绝不要给一个恰好在旁边的 root 令牌。
一个可吊销的短期令牌,胜过环境变量里的个人访问令牌,哪怕配置要多花五分钟。
一个资源,一个受众。一个想要宽范围令牌、或者接受为别的东西签发的令牌的服务器,已经在告诉你它会怎么做了。
把五个服务聚合到一个 MCP 服务器后面,等于把所有凭据集中到一个进程里。拆开,最坏情况就变小了。
如果你说不出某个服务器上周二做了什么,那你是在信任它,不是在核验它。日志正是把一次事故变成一次有边界的事故的东西。
把工具 schema 的变化当成一次依赖升级来对待:去看 diff。这是几乎没人做的检查,也正因如此它作为攻击手段特别管用。
Sallyport 站在你的智能体所连的那些 MCP 服务器前面。它们的调用和其他一切一样爬同一道阶梯,落进同一份审计日志。
01
Claude Code、Cursor 或任何 MCP 客户端连到 Sallyport。Sallyport 向它提供 http.request、ssh.exec,以及你配置的那些上游服务器。
02
本地 stdio 服务器和远程 OAuth 2.1 服务器都会被代理。Sallyport 把它们的令牌封进密钥库,并在 App 内部完成刷新。
03
密钥库锁着就拒绝这次调用。标记为逐次调用批准的密钥会弹出卡片。其余情况下,你已经给过的会话批准覆盖这次运行。
04
在事情发生的同时加密并做哈希链接,这样「那个服务器上周二做了什么」的答案,在你需要之前就已经存在了。
一个诚实的边界:你自己配置的本地 stdio MCP 服务器,确实会拿到你绑定给它的凭据。代理让你对它的调用有批准和记录;它没法把密钥从一个你自己选择启动的进程里再拿回来。
免费下载。需要 Apple Silicon、macOS 14 或更高版本。永远无需账户。
brew install --cask olegsotnikov/tap/sallyportmacOS 14+ · Apple Silicon