Agent skill
OpenCode SDK Development
This skill should be used when the user asks to "create an OpenCode tool", "build an OpenCode plugin", "write a custom tool for OpenCode", "use @opencode-ai/sdk", "use @opencode-ai/plugin", "integrate with OpenCode", "create OpenCode hooks", "define tool schema", "use tool.schema", "work with OpenCode sessions", or needs guidance on OpenCode SDK patterns, plugin development, or custom tool creation.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/opencode-sdk-development
SKILL.md
OpenCode SDK Development
Guide for creating custom tools and plugins using the OpenCode SDK.
Overview
OpenCode provides two main packages for SDK development:
| Package | Purpose |
|---|---|
@opencode-ai/sdk |
Client SDK for interacting with OpenCode server (sessions, messages, files) |
@opencode-ai/plugin |
Plugin system for creating custom tools with schema validation |
Quick Start: Custom Tools
Custom tools extend OpenCode's capabilities. Tools are TypeScript/JavaScript files auto-discovered from:
- Local:
.opencode/tool/in project directory - Global:
~/.config/opencode/tool/
The filename becomes the tool name.
Basic Tool Structure
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Brief description of what the tool does",
args: {
paramName: tool.schema.string().describe("Parameter description")
},
async execute(args, context) {
// context provides: sessionID, messageID, agent, abort
return "Result string returned to the AI"
}
})
Schema Definition
Use tool.schema (which is Zod) for argument validation:
args: {
// String with description
query: tool.schema.string().describe("Search query"),
// Optional string
path: tool.schema.string().optional().describe("File path"),
// Number with constraints
limit: tool.schema.number().min(1).max(100).default(10).describe("Max results"),
// Enum/literal union
format: tool.schema.enum(["json", "text"]).describe("Output format"),
// Boolean
recursive: tool.schema.boolean().default(false).describe("Search recursively")
}
Tool Context
The execute function receives a context object:
type ToolContext = {
sessionID: string // Current session ID
messageID: string // Current message ID
agent: string // Current agent identifier
abort: AbortSignal // Signal for cancellation
}
Example: File Search Tool
import { tool } from "@opencode-ai/plugin"
import { $ } from "bun"
export default tool({
description: "Search for files matching a pattern",
args: {
pattern: tool.schema.string().describe("Glob pattern to match"),
directory: tool.schema.string().default(".").describe("Directory to search")
},
async execute({ pattern, directory }) {
const result = await $`find ${directory} -name "${pattern}"`.text()
return result || "No files found"
}
})
Plugin Development
Plugins provide more comprehensive integrations with hooks for events, authentication, and tool modification.
Plugin Structure
import type { Plugin } from "@opencode-ai/plugin"
const plugin: Plugin = async (input) => {
const { client, project, directory, worktree, $ } = input
return {
// Custom tools
tool: {
myTool: tool({ /* definition */ })
},
// Event hooks
event: async ({ event }) => { /* handle events */ },
// Configuration hooks
config: async (config) => { /* modify config */ },
// Message hooks
"chat.message": async (input, output) => { /* modify messages */ },
// Tool execution hooks
"tool.execute.before": async (input, output) => { /* pre-processing */ },
"tool.execute.after": async (input, output) => { /* post-processing */ }
}
}
export default plugin
Available Hooks
| Hook | Purpose |
|---|---|
event |
Handle real-time events from server |
config |
Modify configuration on load |
tool |
Register custom tools |
auth |
Custom authentication providers |
chat.message |
Modify messages before sending |
chat.params |
Modify LLM parameters (temperature, topP) |
permission.ask |
Handle permission requests |
tool.execute.before |
Pre-process tool arguments |
tool.execute.after |
Post-process tool output |
SDK Client Usage
The SDK client provides programmatic access to OpenCode functionality.
Initialization
import { createOpencode, createOpencodeClient } from "@opencode-ai/sdk"
// Create both client and server
const { client, server } = await createOpencode({
hostname: "127.0.0.1",
port: 4096,
timeout: 5000
})
// Or just the client
const client = createOpencodeClient({
baseUrl: "http://127.0.0.1:4096"
})
Client API Categories
| Category | Methods |
|---|---|
client.session |
list, create, get, delete, prompt, messages, fork, share |
client.project |
list, current |
client.file |
list, read, status |
client.find |
text, files, symbols |
client.tool |
ids, list |
client.event |
subscribe (SSE streaming) |
client.mcp |
status, add |
client.tui |
appendPrompt, submitPrompt, showToast |
Session Management
// List sessions
const { data: sessions } = await client.session.list()
// Create session
const { data: session } = await client.session.create()
// Send prompt
const { data: response } = await client.session.prompt({
path: { id: sessionId },
body: {
parts: [{ type: "text", text: "Your message here" }]
}
})
// Get messages
const { data: messages } = await client.session.messages({
path: { id: sessionId }
})
Event Streaming
const result = await client.event.subscribe()
for await (const event of result.events) {
console.log("Event:", event.type, event.data)
}
Installation
# Install SDK
npm install @opencode-ai/sdk
# Install plugin package (for tools)
npm install @opencode-ai/plugin
Requires TypeScript >= 4.9.
Tool File Location
| Location | Scope |
|---|---|
.opencode/tool/*.ts |
Project-specific tools |
~/.config/opencode/tool/*.ts |
Global tools for all projects |
Multiple exports create multiple tools: filename_exportname.
Best Practices
- Clear Descriptions: Write concise, action-oriented descriptions for tools and parameters
- Schema Validation: Use Zod schemas to validate all inputs before processing
- Error Handling: Return meaningful error messages as strings
- Abort Signal: Check
context.abortfor long-running operations - Type Safety: Use TypeScript for full type inference from schemas
- Minimal Dependencies: Keep tools lightweight and focused
Common Patterns
Cross-Language Tool
import { tool } from "@opencode-ai/plugin"
import { $ } from "bun"
export default tool({
description: "Run Python analysis script",
args: {
file: tool.schema.string().describe("File to analyze")
},
async execute({ file }) {
return await $`python3 analyze.py ${file}`.text()
}
})
Tool with Context
import { tool } from "@opencode-ai/plugin"
export default tool({
description: "Get current session info",
args: {},
async execute(args, context) {
return JSON.stringify({
session: context.sessionID,
message: context.messageID,
agent: context.agent
}, null, 2)
}
})
Troubleshooting
Tool not appearing:
- Verify file is in
.opencode/tool/or~/.config/opencode/tool/ - Check file exports a valid tool definition
- Restart OpenCode to reload tools
Schema errors:
- Ensure all required args are provided
- Check type constraints (string vs number)
- Verify optional fields use
.optional()
Execution errors:
- Check
executereturns a string - Verify async operations complete
- Handle errors and return error messages as strings
Additional Resources
Reference Files
For detailed API documentation:
references/sdk-api.md- Complete SDK client API referencereferences/plugin-api.md- Full plugin hooks and types
Example Files
Working examples in examples/:
examples/basic-tool.ts- Simple tool implementationexamples/full-plugin.ts- Complete plugin with hooks
External Documentation
Didn't find tool you were looking for?