Can I extend OpenCode with my own tools?
can i extend opencode with my own tools?
I now have a complete picture. Here's a comprehensive answer:
##Yes! You can extend OpenCode with your own tools in two main ways:
###1. Custom Tools (Simpler Approach)
Place TypeScript or JavaScript files in these directories:
- Project-level:
.opencode/tool/ - Global:
~/.config/opencode/tool/
Use the tool() helper from @opencode-ai/plugin:
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, context) {
// Your logic here
// context provides: sessionID, messageID, agent, abort signal
return `Executed query: ${args.query}`
},
})
The filename becomes the tool name. You can also export multiple tools from one file (named exports become <filename>_<exportname>).
###2. Plugins (Full Extension System)
Plugins let you add tools and hook into OpenCode events. Place them in:
- Project-level:
.opencode/plugin/ - Global:
~/.config/opencode/plugin/
Or install from npm via opencode.json:
{
"plugin": ["my-plugin-package"]
}
A plugin can provide tools via the tool hook:
import { type Plugin, tool } from "@opencode-ai/plugin"
export const MyToolsPlugin: Plugin = async (ctx) => {
return {
tool: {
mytool: tool({
description: "My custom tool",
args: {
foo: tool.schema.string(),
},
async execute(args) {
return `Hello ${args.foo}!`
},
}),
},
}
}
###Available Hooks
Plugins can also hook into:
tool.execute.before/tool.execute.after- intercept tool callsevent- react to events likesession.idle,file.edited, etc.permission.ask- customize permission handlingchat.message/chat.params- modify chat behaviorexperimental.session.compacting- customize compaction prompts- And more (see
packages/plugin/src/index.tsfor all hooks)
###Using External Dependencies
Add a package.json to your .opencode/ directory:
{
"dependencies": {
"some-package": "^1.0.0"
}
}
OpenCode runs bun install at startup automatically.
tell me more about Plugins. and their relationship with skills
Now I have a complete picture. Here's a comprehensive explanation:
##Plugins vs Skills: Understanding the Relationship
Plugins and Skills are two different extension mechanisms in OpenCode, and they serve distinct purposes:
###Skills — Declarative Knowledge for Agents
Skills are markdown files (SKILL.md) that provide reusable instructions the LLM can load on-demand. They're essentially "prompt libraries" that agents can pull in when needed.
Key characteristics:
- Passive content — just markdown with instructions
- On-demand loading — agents use the built-in
skilltool to load them - No code execution — pure text/documentation
- Discovery-based — agents see available skills and choose when to load them
Locations:
.opencode/skill/<name>/SKILL.md # Project-level
~/.config/opencode/skill/<name>/SKILL.md # Global
.claude/skills/<name>/SKILL.md # Claude-compatible (project)
~/.claude/skills/<name>/SKILL.md # Claude-compatible (global)
Example skill:
---
name: git-release
description: Create consistent releases and changelogs
---
## What I do
- Draft release notes from merged PRs
- Propose a version bump
- Provide a copy-pasteable `gh release create` command
The agent sees this in the skill tool's description as <available_skills> and can call skill({ name: "git-release" }) to load the full content.
###Plugins — Active Code Extensions
Plugins are JavaScript/TypeScript modules that can:
- Add custom tools the LLM can call
- Hook into events (session.idle, file.edited, tool.execute.before, etc.)
- Modify behavior (intercept tool calls, transform messages, customize compaction)
- Provide authentication for providers
Key characteristics:
- Active code — runs JavaScript/TypeScript
- Event-driven — can react to OpenCode events
- Tool providers — can register new tools
- Lifecycle hooks — can intercept and modify operations
Locations:
.opencode/plugin/*.{ts,js} # Project-level
~/.config/opencode/plugin/*.{ts,js} # Global
opencode.json: { "plugin": ["npm-package"] } # npm packages
###The Relationship
They're complementary but independent:
| Aspect | Skills | Plugins |
|---|---|---|
| Nature | Markdown content | Executable code |
| Purpose | Provide instructions to agent | Extend OpenCode functionality |
| Execution | Loaded into context as text | Runs as JavaScript |
| Tools | Loaded via built-in skill tool | Can create new tools |
| Events | None | Can hook into all events |
How they interact:
- A plugin could technically create a custom tool that works with skills
- The built-in
skilltool is just another tool in the registry (seepackages/opencode/src/tool/skill.ts) - Plugins can intercept skill loading via
tool.execute.beforehook
###Plugin Architecture Deep Dive
From packages/opencode/src/plugin/index.ts, plugins are loaded in this order:
- Internal plugins (built-in, like
CodexAuthPlugin) - npm plugins from config (
opencode.json→pluginarray) - Built-in auth plugins (
opencode-copilot-auth,opencode-anthropic-auth) - Local plugin files (
.opencode/plugin/*.ts)
Each plugin receives a context:
type PluginInput = {
client: OpencodeClient // SDK client for API calls
project: Project // Current project info
directory: string // Working directory
worktree: string // Git worktree path
serverUrl: URL // OpenCode server URL
$: BunShell // Bun's shell for commands
}
And returns hooks:
type Hooks = {
event?: (input: { event: Event }) => Promise<void>
tool?: { [key: string]: ToolDefinition }
auth?: AuthHook
"tool.execute.before"?: (input, output) => Promise<void>
"tool.execute.after"?: (input, output) => Promise<void>
"experimental.session.compacting"?: (input, output) => Promise<void>
// ... more hooks
}
###When to Use Which?
Use Skills when:
- You want to give agents reusable instructions/guidelines
- You need domain-specific knowledge (e.g., "how to release in this repo")
- You want Claude-compatible
.claude/skills/format
Use Plugins when:
- You need to execute code (API calls, database queries, etc.)
- You want to react to events (notifications, logging)
- You need to intercept/modify tool behavior
- You're adding authentication for a provider
END OF POST