Skip to content
opencode-build-tool logo

OpenCode SDK Development

opencode-build-tool

Build custom tools and plugins using the OpenCode SDK packages. Use when creating custom tools, implementing tool schemas, building plugins, managing OpenCode sessions, using opencode-ai/sdk or opencode-ai/plugin, creating a tool.ts file, writing an execute handler, using tool.schema, ToolContext...

pantheon-org/tekhne0installs10stars

SKILL.md

Full skill instructions

OpenCode SDK Development

Quick Start: Custom Tool in 3 Steps

  1. Create .opencode/​tool/​my-tool.ts:
import { tool } from "@opencode-ai/​plugin"

export default tool({
  description: "Brief description of what the tool does",
  args: {
    query: tool.schema.string().describe("Search query")
  },
  async execute({ query }, { abort }) {
    return `Result for: ${query}`
  }
})
  1. Restart OpenCode — tools are auto-discovered.
  2. Filename = tool name: my-tool.ts → my-tool.

Mindset

  • @opencode-ai/​plugin — Tools + Hooks: Add LLM-callable tools, intercept execution, react to events
  • @opencode-ai/​sdk — Client API: Control OpenCode from external scripts, session management

When to use: Adding new capabilities the agent doesn't have, automating OpenCode from CI/​scripts, or integrating external APIs as first-class tools. When NOT to use: Built-in tool exists (client.tool.list()). Need shell (use $ from Bun). Logic belongs in system prompt (AGENTS.md). Need MCP.

Full decision guide in advanced-patterns.md.

Tool File Locations

.opencode/​tool/​*.ts          # project-specific
~/​.config/​opencode/​tool/​*.ts  # global

Multiple named exports per file → multiple tools: filename_exportname

Anti-Patterns

NEVER return non-string from execute. WHY: The tool runtime expects a string response; returning an object throws at runtime.

// BAD - throws at runtime
async execute({ query }) { return { results: [] } }
// GOOD - serialize first
async execute({ query }) { return JSON.stringify({ results: [] }) }

NEVER import { tool } from "@opencode-ai/​sdk". WHY: The SDK package has no tool() export. The tool factory is only in @opencode-ai/​plugin.

// BAD - import error at runtime
import { tool } from "@opencode-ai/​sdk"
// GOOD
import { tool } from "@opencode-ai/​plugin"

NEVER ignore the abort signal in long-running operations. WHY: Ignoring abort leaves processes dangling after the user cancels, consuming resources.

// BAD - dangling fetch after cancel
async execute({ url }) { return await fetch(url).then(r => r.text()) }
// GOOD - propagate abort
async execute({ url }, { abort }) { return await fetch(url, { signal: abort }).then(r => r.text()) }

Reference Files

Run bun run opencode run "list tools" to verify your tool is discovered and named correctly.

---
description: custom tools opencode SDK plugin execute handler abort signal Zod schema session management event streaming
---

Eval Scenarios