MCP Server Alias(本地可信)、MCP Connection(运行时状态)、Published MCP Tool(外部数据)、MCP Tool Policy(本地可信 effect)、Exposed MCP Tool(本地能力)、Registry Snapshot(不可变工具集合)。
Published = 外部数据(能做什么未知)
Exposed = 经 alias + 名称隔离 + policy 转换后的本地能力
connect_mcp 接收 alias,不接收 command/args/cwd。alias 必须匹配 ^[a-z][a-z0-9_]{0,31}$。如果允许任意 command,connect_mcp 就退化成另一种 Shell 入口。
const servers = [
new McpServerSpec({
alias: "demo_alpha",
command: process.execPath,
args: [tsxCli, demoScript, "--label", "alpha"],
toolPolicies: policies,
startupTimeoutSeconds: 5, toolTimeoutSeconds: 5,
}),
];
即使远程描述写着 (readOnly),本地 policy 若定义为 EXTERNAL,权限看到的仍是 EXTERNAL。权限只读 ToolDefinition.source 与 effect,不解析远程 annotation。且 policy 集合与远程申报集合必须精确相等(不多不少,不是取交集):远程少发或多发一个都拒绝整条连接,否则就会出现「半连接」状态。
本地分类:
lookup READ -> ALLOW
delay READ -> ALLOW
terminate EXTERNAL-> ASK
((readOnly) 描述不能覆盖本地 EXTERNAL effect)
mcp__{alias}__{normalized}:远程名转小写、连续非字母数字下划线替换为 _。JSON Schema 顶层必须 type:object,拒绝外部 $ref(只能 # 开头 fragment),用 Ajv 编译 validator 在 handler 前校验参数。
demo_alpha lookup -> mcp__demo_alpha__lookup
demo_beta lookup -> mcp__demo_beta__lookup
lookup-one/ lookup_one -> 都变 lookup_one → 碰撞拒绝
模型回复可能同时含 connect_mcp 与 mcp__xxx__lookup。所有调用仍用请求时的 snapshot v7 prepare/execute;提前调用稳定得到 unknown_tool;下一次请求再取 live registry v8。registerMany/unregisterMany 整批原子:一批要么全成功要么一个不留;撤销按对象身份而不只是名字——同名工具被另一连接重新注册过,按名撤销会误删别人的。
live registry v7 → snapshot v7 → request.tools
模型返回多个 tool calls → 全部用 snapshot v7
下一次请求再取 live registry v8
(证明:不是 eventual consistency,是请求一致性)
远程 isError:true → mcp_remote_error,连接保留;调用超时 → mcp_timeout,连接丢弃;子进程退出/transport 断 → mcp_connection_lost,连接丢弃,空闲时由 Watchdog 主动撤销该 alias 全部工具。错误信封字段全部本地构造,远程私有细节(如 server-private-detail)一律不进模型上下文;prepare() 先 JSON 解析 + Ajv 校验,坏参数不出网。不承诺 exactly-once:超时判失败后远程副作用可能已发生。
校验 allowlist → 启动 stdio → Client.connect → tools/list 全量分页。
Published 集合与 policy 精确比对 → 校验名称/schema/碰撞 → registerMany 原子发布。
调用用当时的 Registry Snapshot;Ajv 校验参数后再 callTool。
先原子撤销该 alias 全部工具,再关闭 connection;超时/进程退出也撤销。