Back to Blog

OpenCode MCP + Plugin:无限扩展 AI 能力

2026-04-27T10:50:00+08:00
OpenCode MCP Plugin 扩展 自定义工具

OpenCode MCP + Plugin:无限扩展 AI 能力

你有没有遇到这种情况:AI Agent 正在帮你写代码,突然你想让它查一下数据库里的某条记录,或者调一下公司的内部 API——然后发现,它做不到。Agent 再聪明,手里没有工具也就是个光杆司令。

OpenCode 从设计之初就想到了这个问题。它通过两套扩展机制——MCP ServerPlugin 系统——让 AI Agent 的能力几乎可以无限扩展。我用了几个月,最大的感受是:这套扩展体系设计得相当优雅,上手简单但上限极高。

今天咱们就聊聊这两套系统怎么玩,以及怎么用它们打造属于你自己的超级 Agent。

本文提纲

  1. MCP 协议:让 Agent 连接一切
  2. Local MCP Server:命令行启动,即插即用
  3. Remote MCP Server:连接云端服务
  4. OAuth 认证:安全的第三方接入
  5. MCP 工具发现与管理
  6. Plugin 系统:更深度的定制
  7. Plugin 生命周期:从安装到运行
  8. 通过 Plugin 创建自定义工具
  9. 实战案例:Database MCP + Search MCP + 自定义 Plugin

MCP 协议:让 Agent 连接一切

MCP(Model Context Protocol)是 Anthropic 提出的一种开放协议,目的是让 LLM 能够统一地调用外部工具。你可以把它理解成 AI 世界的 USB 接口——不管你后面接的是数据库、搜索引擎还是企业内部系统,Agent 都用同一套标准来调用。

OpenCode 对 MCP 的支持非常完整,覆盖了三种接入方式:

  • Local MCP:在本机启动一个进程,通过 stdin/stdout 通信
  • Remote MCP:连接一个远程 HTTP 服务器(SSE)
  • OAuth MCP:基于 OAuth 2.0 的安全认证接入

所有 MCP 工具在被添加后,会自动出现在 LLM 的工具列表里,跟 OpenCode 内置的 basheditread 等工具平起平坐。也就是说,LLM 会根据任务需要,自主决定调用哪个 MCP 工具,你不需要手动干预。

有一点需要注意:每个 MCP Server 注册的工具会占用 context 窗口的 token。如果你挂了一堆 MCP 但平时只用其中一两个,反而会拖慢 Agent 的响应速度。所以别贪多,用到什么加什么。

Local MCP Server:命令行启动,即插即用

Local MCP 是最简单的接入方式。你只需要告诉 OpenCode 怎么启动这个 Server,剩下的通信协议它全包了。

配置写在 opencode.json 里:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "my-local-server": {
      "type": "local",
      "command": ["npx", "-y", "@modelcontextprotocol/server-everything"],
      "enabled": true,
      "environment": {
        "MY_ENV_VAR": "my_env_var_value"
      }
    }
  }
}

command 是一个数组,第一个元素是可执行文件,后面的是参数。最常见的方式就是用 npxbunx 直接运行 npm 包里的 MCP Server。environment 可以传环境变量,比如 API Key 之类的敏感信息可以从这里注入。

启动之后,OpenCode 会通过 stdio 跟这个进程通信,自动发现它提供的所有工具。你在对话里加一句 use the my-local-server tool,Agent 就知道该调哪个 MCP 了。

可配置的选项:

选项 类型 说明
type string 必须是 "local"
command array 启动命令和参数
environment object 环境变量
enabled boolean 是否启用
timeout number 工具发现超时(毫秒),默认 5000

Remote MCP Server:连接云端服务

Remote MCP 让你直接连接一个远端的 HTTP 端点。对于那些已经部署好的 MCP 服务(比如 Sentry、Context7、Grep),这种方式最方便——不用在本机装任何东西。

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "my-remote-mcp": {
      "type": "remote",
      "url": "https://my-mcp-server.com",
      "enabled": true,
      "headers": {
        "Authorization": "Bearer MY_API_KEY"
      }
    }
  }
}

url 指向 MCP Server 的端点地址,headers 用来传认证信息。如果你有 API Key,可以通过 headers 直接带上;如果需要 OAuth 认证,那就用下面要讲的 OAuth 方式。

Remote MCP 的可配置选项:

选项 类型 说明
type string 必须是 "remote"
url string 远端 MCP Server 地址
enabled boolean 是否启用
headers object 请求头
oauth object/false OAuth 配置,或 false 禁用自动检测
timeout number 工具发现超时(毫秒),默认 5000

OAuth 认证:安全的第三方接入

OpenCode 对 OAuth 的支持做得相当到位,基于 RFC 7591 的 Dynamic Client Registration 实现。大部分情况下你不需要手动配置任何 OAuth 参数——OpenCode 会自动处理整个流程。

自动 OAuth

只需要配置一个普通的 Remote MCP:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "my-oauth-server": {
      "type": "remote",
      "url": "https://mcp.example.com/mcp"
    }
  }
}

当你第一次使用这个 MCP 时,OpenCode 会检测到 401 响应,自动发起 OAuth 流程,打开浏览器让你授权,然后把 token 安全地存储在 ~/.local/share/opencode/mcp-auth.json

预注册 OAuth

如果服务提供商给了你 Client ID 和 Client Secret,可以直接配置:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "my-oauth-server": {
      "type": "remote",
      "url": "https://mcp.example.com/mcp",
      "oauth": {
        "clientId": "{env:MY_MCP_CLIENT_ID}",
        "clientSecret": "{env:MY_MCP_CLIENT_SECRET}",
        "scope": "tools:read tools:execute"
      }
    }
  }
}

{env:XXX} 语法会自动从环境变量中读取值,避免在配置文件里硬编码敏感信息。

OAuth 管理命令

OpenCode 提供了几个 CLI 命令来管理 OAuth:

# 手动触发某个 MCP 的认证流程
opencode mcp auth my-oauth-server

# 列出所有 MCP Server 及其认证状态
opencode mcp list

# 移除已存储的凭据
opencode mcp logout my-oauth-server

# 调试连接和 OAuth 流程
opencode mcp debug my-oauth-server

如果你的 MCP 不需要 OAuth(用 API Key 就够了),可以显式禁用:

{
  "type": "remote",
  "url": "https://mcp.example.com/mcp",
  "oauth": false,
  "headers": {
    "Authorization": "Bearer {env:MY_API_KEY}"
  }
}

MCP 工具发现与管理

MCP Server 启动后,OpenCode 会自动发现它提供的所有工具,并以 {server-name}_{tool-name} 的格式注册。比如你有一个叫 sentry 的 MCP Server,它提供了 list_issuesget_issue 两个工具,那在 OpenCode 里就变成 sentry_list_issuessentry_get_issue

全局管理

你可以在 opencode.jsontools 字段里控制 MCP 工具的启用状态:

{
  "mcp": {
    "my-mcp-foo": { "type": "local", "command": ["bun", "x", "foo"] },
    "my-mcp-bar": { "type": "local", "command": ["bun", "x", "bar"] }
  },
  "tools": {
    "my-mcp-foo": false
  }
}

甚至支持 glob 模式批量控制:

{
  "tools": {
    "my-mcp*": false
  }
}

按 Agent 分配

如果你有很多 MCP Server 但不是每个 Agent 都需要,可以全局禁用、按 Agent 开启:

{
  "mcp": {
    "my-mcp": {
      "type": "local",
      "command": ["bun", "x", "my-mcp"],
      "enabled": true
    }
  },
  "tools": {
    "my-mcp*": false
  },
  "agent": {
    "my-agent": {
      "tools": {
        "my-mcp*": true
      }
    }
  }
}

这样 my-mcp 的工具只对 my-agent 这个 Agent 可见。这种按需分配的方式对管理大型项目特别有用——避免给每个 Agent 都塞一堆它用不到的工具,既浪费 context 又拖慢速度。

Plugin 系统:更深度的定制

如果说 MCP 是"连接外部工具",那 Plugin 就是"改造 OpenCode 本身"。

Plugin 系统让你可以钩入 OpenCode 的内部事件流,修改默认行为,甚至注入新的工具。它比 MCP 的粒度更细,控制力更强。

两种加载方式

1. 从 npm 安装

opencode.json 里声明:

{
  "$schema": "https://opencode.ai/config.json",
  "plugin": [
    "opencode-helicone-session",
    "opencode-wakatime",
    "@my-org/custom-plugin"
  ]
}

OpenCode 启动时会用 Bun 自动安装这些包,缓存在 ~/.cache/opencode/node_modules/。支持 scoped package,跟平时装 npm 包没有区别。

2. 从本地文件加载

.js.ts 文件放到指定目录即可:

  • 项目级:.opencode/plugins/
  • 全局级:~/.config/opencode/plugins/

不需要任何配置,放进目录就会被自动加载。

加载顺序

OpenCode 按 4 个层级依次加载:

  1. 全局配置(~/.config/opencode/opencode.json)里的 npm plugin
  2. 项目配置(opencode.json)里的 npm plugin
  3. 全局 plugin 目录(~/.config/opencode/plugins/
  4. 项目 plugin 目录(.opencode/plugins/

所有 hook 按顺序执行,同名同版本的 npm package 只加载一次,但本地 plugin 和 npm plugin 即使名字一样也会分别加载。

Plugin 生命周期:从安装到运行

写一个 Plugin 很简单——导出一个异步函数,接收 context,返回一个 hooks 对象:

export const MyPlugin = async ({ project, client, $, directory, worktree }) => {
  console.log("Plugin initialized!")

  return {
    // 各种 hook 在这里注册
  }
}

Plugin 函数接收的 context 包含:

  • project:当前项目信息
  • directory:当前工作目录
  • worktree:git worktree 路径
  • client:OpenCode SDK 客户端,可以调用 AI
  • $:Bun 的 Shell API,用来执行命令

可以订阅的事件

Plugin 可以订阅的事件类型非常丰富,涵盖了 OpenCode 运行的方方面面:

工具事件tool.execute.beforetool.execute.after——在工具执行前后插入自定义逻辑,比如拦截 bash 命令、记录执行日志。

会话事件session.createdsession.idlesession.errorsession.compacted——跟踪会话状态变化,比如会话完成时发通知。

文件事件file.editedfile.watcher.updated——监控文件变化。

Shell 事件shell.env——注入环境变量到所有 shell 执行中。

TUI 事件tui.prompt.appendtui.command.executetui.toast.show——自定义终端界面行为。

还有 LSP、权限、TODO 等各种事件,总共 20 多个 hook 点。

TypeScript 支持

如果你用 TypeScript,可以从 @opencode-ai/plugin 导入类型:

import type { Plugin } from "@opencode-ai/plugin"

export const MyPlugin: Plugin = async (ctx) => {
  return {
    // 类型安全的 hook 实现
  }
}

依赖管理

本地 Plugin 如果需要外部依赖,在 .opencode/ 目录下创建一个 package.json

{
  "dependencies": {
    "shescape": "^2.1.0"
  }
}

OpenCode 启动时会自动 bun install,你的 Plugin 就可以直接 import 了。

通过 Plugin 创建自定义工具

这是 Plugin 系统最强大的能力之一——你可以直接给 Agent 添加新工具。

import { type Plugin, tool } from "@opencode-ai/plugin"

export const CustomToolsPlugin: Plugin = async (ctx) => {
  return {
    tool: {
      mytool: tool({
        description: "This is a custom tool",
        args: {
          foo: tool.schema.string(),
        },
        async execute(args, context) {
          const { directory, worktree } = context
          return `Hello ${args.foo} from ${directory} (worktree: ${worktree})`
        },
      }),
    },
  }
}

tool() 是一个 helper 函数,接收三个参数:

  • description:告诉 LLM 这个工具干什么
  • args:用 Zod schema 定义参数类型(tool.schema 就是 Zod)
  • execute:工具被调用时执行的函数

这些自定义工具跟内置工具完全平级,LLM 会自动识别并在需要时调用。如果自定义工具和内置工具同名,自定义工具会覆盖内置工具。

Custom Tools 的另一种写法

除了通过 Plugin,你还可以直接在 .opencode/tools/ 目录下创建工具文件:

// .opencode/tools/database.ts
import { tool } from "@opencode-ai/plugin"

export default tool({
  description: "Query the project database",
  args: {
    query: tool.schema.string().describe("SQL query to execute"),
  },
  async execute(args) {
    return `Executed query: ${args.query}`
  },
})

文件名就是工具名。这个文件会创建一个叫 database 的工具。也可以一个文件导出多个工具,它们会以 {filename}_{exportname} 命名。

这种方式比 Plugin 更轻量,适合只需要加一两个独立工具的场景。

实战案例:Database MCP + Search MCP + 自定义 Plugin

光说理论不够过瘾,来看几个真实场景。

场景一:Database MCP Server

假设你的项目用 PostgreSQL,想让 Agent 能直接查数据库。你可以用一个 PostgreSQL MCP Server:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "postgres": {
      "type": "local",
      "command": ["npx", "-y", "@modelcontextprotocol/server-postgres"],
      "environment": {
        "POSTGRES_CONNECTION_STRING": "postgresql://user:pass@localhost:5432/mydb"
      }
    }
  }
}

配好之后,在对话里你可以这样说:

查一下 users 表里最近注册的 10 个用户,use postgres

Agent 会自动调用 postgres_query 工具,执行 SQL,把结果返回给你。调试数据的时候再也不用手动切换到数据库客户端了。

场景二:Search MCP Server

Context7 是一个文档搜索 MCP,可以让 Agent 实时查找技术文档:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "context7": {
      "type": "remote",
      "url": "https://mcp.context7.com/mcp"
    }
  }
}

用的时候:

怎么用 Cloudflare Worker 缓存 JSON API 响应 5 分钟?use context7

Agent 会搜索最新的文档并给出准确的配置方案。再也不用担心 LLM 的训练数据过时了。

更聪明的做法是在 AGENTS.md 里加一条规则:

When you need to search docs, use `context7` tools.

这样 Agent 在遇到不确定的问题时会主动去查文档,你甚至不需要提醒它。

场景三:自定义通知 Plugin

写一个 Plugin,在 Agent 完成任务后给你发系统通知:

// .opencode/plugins/notification.js
export const NotificationPlugin = async ({ $ }) => {
  return {
    event: async ({ event }) => {
      if (event.type === "session.idle") {
        await $`osascript -e 'display notification "Task done!" with title "OpenCode"'`
      }
    },
  }
}

把这个文件放到 .opencode/plugins/ 目录,OpenCode 启动时自动加载。当你让 Agent 跑一个长时间任务,去做别的事,完成后 macOS 会弹出通知。

场景四:.env 文件保护 Plugin

防止 Agent 读取你的 .env 文件:

// .opencode/plugins/env-protection.js
export const EnvProtection = async () => {
  return {
    "tool.execute.before": async (input, output) => {
      if (input.tool === "read" && output.args.filePath.includes(".env")) {
        throw new Error("Do not read .env files")
      }
    },
  }
}

几行代码就给 Agent 加了一道安全护栏。这个思路可以扩展到任何你想要限制的操作——比如禁止删除某些目录、限制网络请求等等。

场景五:环境变量注入 Plugin

给所有 shell 执行注入环境变量:

// .opencode/plugins/inject-env.js
export const InjectEnvPlugin = async () => {
  return {
    "shell.env": async (input, output) => {
      output.env.MY_API_KEY = "secret"
      output.env.PROJECT_ROOT = input.cwd
    },
  }
}

这样不管是 Agent 执行的 bash 命令还是你自己开的终端,都会有这些环境变量。特别适合在团队里统一配置开发环境。

社区生态

OpenCode 的社区已经有不少现成的 Plugin 可以直接用。比较有意思的几个:

  • opencode-wakatime:自动记录编程时间到 WakaTime
  • opencode-supermemory:跨会话持久化记忆
  • opencode-firecrawl:网页抓取和搜索
  • opencode-background-agents:Claude Code 风格的后台 Agent
  • opencode-vibeguard:在 LLM 调用前自动脱敏敏感信息
  • opencode-pty:让 Agent 能在 PTY 里跑交互式程序

完整的列表可以在 OpenCode Ecosystem 页面找到。如果你写了好的 Plugin,也可以提交 PR 加进去。

MCP 和 Plugin 这两套系统搭配起来,基本能覆盖你能想到的所有扩展需求。MCP 负责连接外部服务,Plugin 负责改造 Agent 行为,各司其职又相互补充。开始的话,建议先试几个 Remote MCP(比如 Context7 和 Grep),体验一下工具发现和自动调用的感觉;然后再写一两个本地 Plugin(比如通知或环境变量注入),熟悉一下 hook 机制。搞清楚这两样,你的 OpenCode 就不再是开箱即用的标准版,而是专为你量身定制的专属 Agent。


作者: itech001 来源: 公众号:AI人工智能时代 主页: https://www.theaiera.cn(每日分享最前沿的AI新闻和技术)

本文首发于 AI人工智能时代,转载请注明出处。

Enjoyed this article? Share it with others!