skills/agentic-harness/opencode-toolkit/build-tool/SKILL.md
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, createOpencode, plugin hooks, custom auth, automating OpenCode from a script, building a CI tool, or integrating external APIs. Keywords: Zod schema, execute handler, event streaming, session management, abort signal, tool discovery, multiple tools per file, tool factory.
npx skillsauth add pantheon-org/tekhne opencode-build-toolInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
.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}`
}
})
my-tool.ts → my-tool.@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 managementWhen 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.
.opencode/tool/*.ts # project-specific
~/.config/opencode/tool/*.ts # global
Multiple named exports per file → multiple tools: filename_exportname
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()) }
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
---
tools
A skill that produces warnings but no errors.
testing
A well-formed example skill for testing the validator.
development
A skill with code blocks and imperative instructions for testing content and contamination analysis.
tools