Plugins
Plugins extend OpenCode with custom tools, lifecycle hooks, middleware, and third-party integrations. They can be loaded from npm packages, local file paths, or installed interactively using the plugin CLI commands.
Overview
A plugin can:
- Register custom tools for the AI agent to use
- Hook into tool execution lifecycle (before/after)
- Add middleware for message processing
- Provide configuration schemas and defaults
- Integrate with external APIs and services
Configuration
Plugins are declared in opencode.json under the plugin key:
{
"plugin": [
"opencode-helicone-session",
"@my-org/custom-plugin",
"./.opencode/plugins/internal-tools.mjs"
]
}
Each entry is a module specifier: an npm package name, a scoped npm package, or a relative file path.
Plugin Locations
Plugins can reside in several places:
| Location | Scope | Description |
|---|---|---|
.opencode/plugins/ | Project | Per-project plugins, version-controlled |
~/.config/opencode/plugins/ | User | Global plugins available to all projects |
| npm packages | System | Installed via package manager |
Local Plugin Example
Create a plugin file at .opencode/plugins/custom-tools.mjs:
import { tool } from 'opencode-ai/sdk';
export default {
name: 'project-tools',
version: '0.1.0',
tools: [
tool({
name: 'query_internal_api',
description: 'Query the internal company API',
parameters: {
type: 'object',
properties: {
endpoint: { type: 'string' },
params: { type: 'object' }
}
},
execute: async ({ endpoint, params }) => {
const res = await fetch(`https://api.internal/${endpoint}`, {
headers: { Authorization: `Bearer ${process.env.INTERNAL_TOKEN}` }
});
return res.json();
}
})
]
};
Installing Plugins
Use the opencode plugin command to install plugins from npm:
# Install a plugin
opencode plugin opencode-helicone-session
# Install with a specific version
opencode plugin opencode-helicone-session@1.2.0
# Install globally (available across all projects)
opencode plugin opencode-helicone-session --global
# Force reinstall, replacing existing version
opencode plugin opencode-helicone-session --force
The plugin command can also be invoked with the shorthand plug:
opencode plug @my-org/custom-plugin
opencode plug @my-org/custom-plugin -g # global
opencode plug @my-org/custom-plugin -f # force
Writing a Plugin
Plugins export a default object with the following structure:
import { definePlugin } from 'opencode-ai/sdk';
export default definePlugin({
name: 'my-plugin',
version: '1.0.0',
description: 'Description of what this plugin does',
tools: [
// Custom tool definitions
],
hooks: {
'tool.execute.before': async ({ tool, args, session }) => {
// Runs before every tool execution
console.log(`Executing ${tool.name}`);
return args; // optionally modify args
},
'tool.execute.after': async ({ tool, args, result, session }) => {
// Runs after every tool execution
console.log(`Completed ${tool.name}`);
return result; // optionally modify result
},
'session.create': async ({ session }) => {
// Runs when a new session is created
},
'message.send.before': async ({ message, session }) => {
// Runs before a message is sent to the model
return message;
},
'message.send.after': async ({ message, response, session }) => {
// Runs after a model response is received
}
}
});
Hook Reference
| Hook | Timing | Payload |
|---|---|---|
tool.execute.before | Before a tool runs | { tool, args, session } |
tool.execute.after | After a tool completes | { tool, args, result, session } |
session.create | New session created | { session } |
message.send.before | Message sent to model | { message, session } |
message.send.after | Response received from model | { message, response, session } |
Example: Helicone Session Plugin
The opencode-helicone-session plugin integrates OpenCode with Helicone for observability, logging, and analytics:
opencode plugin opencode-helicone-session
After installation, configure the Helicone API key:
export HELICONE_API_KEY="sk-helicone-..."
The plugin automatically wraps all LLM calls with Helicone session tracking, providing request/response logging, latency monitoring, and cost analysis through the Helicone dashboard.
Example: GitLab Plugin
A GitLab integration plugin might provide tools for repository operations:
export default definePlugin({
name: 'gitlab-tools',
version: '1.0.0',
tools: [
tool({
name: 'gitlab_create_merge_request',
description: 'Create a merge request on GitLab',
parameters: {
type: 'object',
properties: {
projectId: { type: 'string' },
title: { type: 'string' },
sourceBranch: { type: 'string' },
targetBranch: { type: 'string' }
}
},
execute: async (args) => {
const res = await fetch(
`https://gitlab.com/api/v4/projects/${args.projectId}/merge_requests`,
{
method: 'POST',
headers: {
'PRIVATE-TOKEN': process.env.GITLAB_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: args.title,
source_branch: args.sourceBranch,
target_branch: args.targetBranch
})
}
);
return res.json();
}
})
]
});
Plugin Resolution Order
When multiple plugins register the same hook or tool, they execute in this order:
- Built-in plugins (OpenCode internal)
- npm package plugins (in order listed in
pluginarray) .opencode/plugins/local files (alphabetical)~/.config/opencode/plugins/global files (alphabetical)
Best Practices
- Name uniquely: Prefix plugin namespaces (e.g.,
@org/plugin-name) to avoid conflicts. - Version strictly: Pin plugin versions in production to prevent unexpected behavior from updates.
- Minimize hooks: Only use hooks you need. Each hook adds overhead to every matching operation.
- Fail gracefully: Plugins should catch errors internally and log rather than crash the host process.
- Document tools: Provide clear descriptions and parameter schemas so the AI agent uses tools correctly.
- Test locally: Use
.opencode/plugins/during development, then publish to npm for team-wide distribution.