Skip to main content
Custom tools are functions you create that the LLM can call during conversations. They work alongside opencode’s built-in tools like read, write, and bash.

Creating a tool

Tools are defined as TypeScript or JavaScript files. However, the tool definition can invoke scripts written in any language — TypeScript or JavaScript is only used for the tool definition itself.

Location

They can be defined:
  • Locally by placing them in the .opencode/tools/ directory of your project.
  • Or globally, by placing them in ~/.config/opencode/tools/.

Structure

The easiest way to create tools is using the tool() helper which provides type-safety and validation.
.opencode/tools/database.ts
The filename becomes the tool name. The above creates a database tool.

Multiple tools per file

You can also export multiple tools from a single file. Each export becomes a separate tool with the name <filename>_<exportname>:
.opencode/tools/math.ts
This creates two tools: math_add and math_multiply.

Tool API

Tool definition

The tool() function accepts an object with the following properties:
  • description (required): A clear description of what the tool does. The LLM uses this to decide when to call your tool.
  • args (required): A Zod schema object defining the tool’s parameters. Use tool.schema (which is Zod) to define types.
  • execute (required): An async function that implements the tool’s logic. Must return a string that will be shown to the LLM.

Arguments

You can use tool.schema, which is just Zod, to define argument types.
You can also import Zod directly and return a plain object:

Available schema types

Since tool.schema is Zod, you have access to all Zod types:
Always add .describe() to help the LLM understand what each parameter is for:

Context

Tools receive context about the current session:
.opencode/tools/project.ts

Context properties

  • directory: Use this instead of process.cwd() when resolving relative paths
  • worktree: Useful for generating stable relative paths with path.relative(worktree, absPath)
  • abort: Check abort.aborted to detect if the user cancelled the operation
  • metadata(): Update the tool’s title or add custom metadata shown in the UI
  • ask(): Request user permission during tool execution

Using metadata

Handling cancellation


Examples

Write a tool in Python

You can write your tools in any language you want. Here’s an example that adds two numbers using Python. First, create the tool as a Python script:
.opencode/tools/add.py
Then create the tool definition that invokes it:
.opencode/tools/python-add.ts
Here we are using the Bun.$ utility to run the Python script.

Database query tool

Create a tool that executes SQL queries:
.opencode/tools/db-query.ts

API client tool

Create a tool that calls an external API:
.opencode/tools/github-search.ts

File system tool

Create a tool that performs custom file operations:
.opencode/tools/count-lines.ts

Shell command tool

Create a tool that wraps shell commands:
.opencode/tools/docker-ps.ts

Tool registration via plugins

You can also register tools through plugins instead of separate files:
.opencode/plugins/my-tools.ts
This approach is useful when:
  • You want to group related tools together
  • Your tools need shared state or initialization
  • You want to conditionally register tools based on plugin configuration

Best practices

Write clear descriptions

The LLM relies on your tool’s description to decide when to use it. Be specific:

Validate inputs

Use Zod’s validation features to ensure correct inputs:

Return structured output

Return well-formatted output that’s easy for the LLM to parse:

Handle errors gracefully

Catch errors and return helpful messages:

Use context appropriately

Respect cancellation

For long-running operations, check the abort signal: