Can I extend OpenCode with my own tools?

dev
20b52cachore: generate
This post might have stale content, as dev is 2009 commits ahead.

can i extend opencode with my own tools?

Avatar of anthropic/claude-opus-4.5
Claude Opus 4.5
commented

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 calls
  • event - react to events like session.idle, file.edited, etc.
  • permission.ask - customize permission handling
  • chat.message / chat.params - modify chat behavior
  • experimental.session.compacting - customize compaction prompts
  • And more (see packages/plugin/src/index.ts for 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.

Avatar of julianbenegas
Julian Benegas
commented

tell me more about Plugins. and their relationship with skills

Avatar of anthropic/claude-opus-4.5
Claude Opus 4.5
commented

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 skill tool 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:

  1. Add custom tools the LLM can call
  2. Hook into events (session.idle, file.edited, tool.execute.before, etc.)
  3. Modify behavior (intercept tool calls, transform messages, customize compaction)
  4. 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:

AspectSkillsPlugins
NatureMarkdown contentExecutable code
PurposeProvide instructions to agentExtend OpenCode functionality
ExecutionLoaded into context as textRuns as JavaScript
ToolsLoaded via built-in skill toolCan create new tools
EventsNoneCan hook into all events

How they interact:

  • A plugin could technically create a custom tool that works with skills
  • The built-in skill tool is just another tool in the registry (see packages/opencode/src/tool/skill.ts)
  • Plugins can intercept skill loading via tool.execute.before hook

###Plugin Architecture Deep Dive

From packages/opencode/src/plugin/index.ts, plugins are loaded in this order:

  1. Internal plugins (built-in, like CodexAuthPlugin)
  2. npm plugins from config (opencode.jsonplugin array)
  3. Built-in auth plugins (opencode-copilot-auth, opencode-anthropic-auth)
  4. 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

Can I extend OpenCode with my own tools? — anomalyco/opencode