把 AI-agent 做成 “可扩展操作系统” 的关键,是让用户像装 App 一样装工具,且随时插拔、不崩主进程。Block 开源的 Rust 版 Goose 给出的答案是:把插件翻译成 MCP Server,用进程级生命周期管理实现热插拔,用白名单 + WASM 做沙箱。本文拆给你看:从发现、加载、 capability 协商到崩溃重启,每一步都有可直接落地的参数。
一、Goose 的 “插件” 到底是什么
Goose 主进程完全用 Rust 写成,只干四件事:
- 调度 LLM 请求;
- 维护一个 MCP Client Router;
- 守护所有子进程(即 MCP Server);
- 把 stdout/sse 回来的 JSON-RPC 结果喂给 LLM。
因此,“写插件”=“写一个 MCP Server”,语言不限:官方仓库里既有 Node 也有 Python 例子,只要实现 MCP Specification 的 tools/list、tools/call 两组方法即可。Goose 在运行时把它当黑盒子 spawn,用完即走,天然隔离。
二、热插拔生命周期:五条命令就能复现
-
发现
用户通过 UI 或 CLI 输入扩展名,Goose 先去本地$GOOSE_REGISTRY/json/<name>.json查元数据;若无,则回退到远程 registry。元数据里唯一必填的是启动命令:["npx", "@mcp/github"]或"wasm://./figma.wasm"。 -
拉取 & 校验
如果指定了checksum,Goose 用sha256校验;若走 WASM,还会验证 runtime 版本兼容性(wasmtime >= 17.0)。 -
spawn + capability 协商
Goose 以RUST_LOG=mcp=debug启动子进程,先发送mcp.initialize,子进程返回capabilities.tools.list=true即表示 “我能提供工具”。这一步失败直接回滚,不会写入配置。 -
注册到 Router
成功后,把<tool_name, server_id>写进内存哈希表;LLM 后续看到tools/github.create_issue就会路由到对应子进程。 -
崩溃重启 & 卸载
子进程退出码非 0 视为 crash。Goose 立即重启,连续 5 次失败则永久卸载,并写$GOOSE_DATA/crashes/<id>.log供排查。用户可手动goose ext reload <id>清零计数器。
以上五步全部在 1 秒内完成,用户侧感知就是 “点一下开关→工具列表里出现新工具”。
三、沙箱隔离三板斧
| 维度 | 默认策略 | 可调参数 |
|---|---|---|
| 网络 | 仅允许 localhost:8000–9000 |
GOOSE_MCP_ALLOW_NET=host:port,host:port |
| 文件 | 只读工作区,写操作需显式声明 | `{ |