OpenCode 扩展与集成:Plugins、MCP、权限与快照

Posted on Mon 05 October 2026 in AI 编程 • Sucre Liu

权限:有序规则 + 最后匹配者胜

OpenCode 的权限不是一堆开关,而是有序规则列表,每条三个字段:

{
  "permissions": [
    { "action": "shell", "resource": "*", "effect": "ask" },
    { "action": "shell", "resource": "git status *", "effect": "allow" },
    { "action": "shell", "resource": "git diff *", "effect": "allow" },
    { "action": "shell", "resource": "git push *", "effect": "deny" }
  ]
}

规则是"最后匹配者胜",所以宽规则在前、例外在后。写成反过来,deny 会被后面的 allow 盖掉。

匹配是整值通配:* 匹配任意字符(含 /),? 匹配单字符,其余按字面量。一个以 * 结尾的 shell 规则也会匹配"无参数"形式——git status * 同时命中 git status 和 git status --short。

可用的 action 和它们的 resource:

action resource
read 路径
edit edit/write/patch 的目标路径
glob 请求的 glob 模式
grep 请求的正则(不是搜索路径)
shell 扫描器产出的命令串(复合命令可能产生多条)
subagent 目标 agent ID
skill skill ID
webfetch / websearch URL / 查询
external_directory 外部目录边界
<server>_<tool> MCP 工具

一次操作可能检查多个资源(比如一个补丁改多个文件)。判定规则:任一 deny 就 deny;否则任一 ask 就 ask;否则 allow。

默认策略与"为什么它动了我的 .env"

每个 agent(包括自定义的)都从这套基础策略起步:

[
  { "action": "*", "resource": "*", "effect": "allow" },
  { "action": "external_directory", "resource": "*", "effect": "ask" },
  { "action": "read", "resource": "*.env", "effect": "ask" },
  { "action": "read", "resource": "*.env.*", "effect": "ask" },
  { "action": "read", "resource": "*.env.example", "effect": "allow" }
]

默认"全放",但对外部目录和.env 读取会问。内置 agent 在此之上各有追加:Build 允许提问;Plan 允许提问、除 ~/.opencode/plan 外拒绝编辑;General 拒绝提问和派生子代理;Explore 除读取/glob/grep/网页外基本全拒。

这也解释了那个经典困惑:为什么它读 .env 时有时不问你?如果你之前选过 "Allow always",那条批准会被存成项目级的持久 allow 规则。而保存的批准永远压不过你配置的 deny——这是有意的。

三条硬经验

  1. shell 是最高危的 action。 它以宿主用户的文件系统、进程和网络权限运行,且"从命令文本推断目录"只是尽力而为。官方建议也很直接:用窄的 shell 允许清单,别指望写一条模式去识别所有危险命令。
  2. deny 优先用在这些地方:read 命中 *.env*、read/edit 命中密钥目录、shell 命中 git push *、subagent 命中不想要的子代理、skill 命中内部技能。
  3. 需要"任何配置都不能开"的规则,用 policy(experimental.policies),它能把 allow/ask 强制变成 deny,且全局与 Console 托管策略压过项目配置——这正是组织阻止某个仓库自行放开命令的机制。

批准交互里,客户端可以回复 once(只批准本次)、always(批准并为项目保存建议的模式)、reject(拒绝并拒绝该会话其余待批准的请求)。保存的模式因工具而异:shell 存命令前缀,skill/subagent 存 ID,有的工具建议 *。定期清理过宽的已保存批准,它们不会自己过期。

MCP:连接外部能力

命令行添加最省心:

opencode mcp add context7 --url https://mcp.context7.com/mcp
opencode mcp add context7 --global --url https://mcp.context7.com/mcp
opencode mcp list

V2 的结构是 mcp.servers,服务器名不直接挂在 mcp 下:

{
  "mcp": {
    "servers": {
      "my-server": { "type": "local", "command": ["npx", "-y", "example-mcp-server"] },
      "context7": { "type": "remote", "url": "https://mcp.context7.com/mcp" }
    }
  }
}

要点:

  • 远程默认启用 OAuth(PKCE、刷新令牌、支持时做动态客户端注册)。需要登录就跑 /mcps 选它授权,或 opencode mcp auth <name>;换账号用 mcp logout。
  • 只有服务器只用 API key/自定义头时才设 oauth: false,且密钥用环境变量替换 {env:NAME},别写进文件。
  • 关闭但保留配置用 disabled: true,没有 enabled 字段。
  • 工具名会规范化为 <server>_<tool>(非字母数字下划线连字符转 _),server 名要起得短且规范化后仍唯一。
  • 想隐藏而非断开某 server 的工具,用权限:{ "action": "context7_*", "resource": "*", "effect": "deny" }。
  • 超时有 startup(默认 30s)、catalog(30s)、execution(12h),可按 server 覆盖。
  • codemode 默认 true,把工具按 server 分组暴露;需要让工具留在 provider 原生工具列表时设 false。

MCP 会消耗模型上下文,所以只加你需要的 server——这点和"插件越多越强"的直觉相反。

插件:能力大,但要有边界

插件从包或本地目录加载:

{
  "plugins": [
    "opencode-acme-plugin@1.2.0",
    "@acme/opencode-plugin",
    "./plugins/local",
    { "package": "@acme/opencode-plugin", "options": { "strict": true } }
  ]
}

几个机制:

  • 本地发现:每个 .opencode/plugins/ 里的 .ts/.js 文件和直接的插件包目录会被加载。注意:项目根 opencode.json 旁边的 plugins/ 目录不会被自动发现,要么显式配置,要么放进 .opencode/。
  • 控制:条目按顺序处理,用 - 前缀禁用、* 匹配全部、.* 匹配前缀;后面的 ID 可以重新启用。
  • 有两个内置插件忽略删除:opencode.config.policy 和 opencode.provider.opencode——保证仓库无法关掉策略执行。
  • CLI 管理包插件:plugin add/list/check/update/remove,支持 npm 版本/标签/范围,以及 Git 规格(含私有仓库、子目录选择器)。
  • 配置目录下的改动会热加载;未监视的本地依赖可能要 opencode service restart。

快照:能撤销,但不等于备份

/undo 会回滚最近的对话和文件改动:它把回滚暂存起来,恢复受影响文件,并把被你删掉的那条提示放回输入框让你改:

/undo
# 改写提示后重新提交,才真正提交这次回滚
/redo   # 提交前可取消

它也提供快捷键(<leader>u 撤销、<leader>r 取消),或选中较早消息选 Revert。

范围是关键,别高估:

内容 是否捕获
活动目录内已跟踪文件 是
未忽略的未跟踪文件(单个 ≤ 2 MiB) 是
被 gitignore 的文件 否
单个 > 2 MiB 的未跟踪文件 否
活动目录之外的文件 否
git 元数据(提交、分支)、数据库、进程 否

而且快照需要 git 仓库;不在仓库里,/undo 只能回滚对话,不能恢复文件。存储用的是数据目录下独立的内部 git 对象库,不会创建提交或移动分支。

官方对安全的表述值得原样记住:快照是便利功能,不是事务,也不是备份。 shell 可以改服务、进程、网络、数据库、git 状态、被忽略的产物或活动目录外的文件,恢复快照不会逆转这些。另外,恢复会覆盖受影响路径的当前内容——所以在脏工作区上用 /undo 前,先提交或备份。

小结

  • 权限是有序规则、"最后匹配者胜";宽规则在前,例外在后;任一 deny 即 deny。
  • 默认对 .env 和外部目录会问;保存的 "always" 是项目级持久 allow,但压不过 deny。
  • MCP 只加需要的;远程默认 OAuth;密钥走 {env:...}。
  • 内置策略插件无法被仓库移除,这是有意的防线。
  • 快照能救"最近的文件改动",救不了远端副作用——重要工作先提交。

下一篇收尾:HTTP API、客户端与 SDK、上下文压缩,以及一套排障流程。