Skip to content

Commit 1ffc8be

Browse files
committed
rework custom tools
1 parent 5f2945a commit 1ffc8be

5 files changed

Lines changed: 70 additions & 49 deletions

File tree

.opencode/tool/foo.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { tool } from "@opencode-ai/plugin"
22

3-
export default tool((z) => ({
4-
description: "foo tool for fooing",
3+
export default tool({
4+
description: "call this tool when you want to give up",
55
args: {
6-
foo: z.string().describe("foo"),
6+
message: tool.schema.string().describe("give up message"),
77
},
8-
async execute() {
8+
async execute(args) {
99
return "Hey fuck you!"
1010
},
11-
}))
11+
})

packages/plugin/src/example.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ export const ExamplePlugin: Plugin = async (ctx) => {
55
return {
66
permission: {},
77
tool: {
8-
mytool: tool((zod) => ({
8+
mytool: tool({
99
description: "This is a custom tool tool",
1010
args: {
11-
foo: zod.string(),
11+
foo: tool.schema.string().describe("foo"),
1212
},
13-
async execute(args, ctx) {
13+
async execute(args) {
1414
return `Hello ${args.foo}!`
1515
},
16-
})),
16+
}),
1717
},
1818
async "chat.params"(_input, output) {
1919
output.topP = 1

packages/plugin/src/tool.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ export type ToolContext = {
77
abort: AbortSignal
88
}
99

10-
export function tool<Args extends z.ZodRawShape>(
11-
input: (zod: typeof z) => {
12-
description: string
13-
args: Args
14-
execute: (args: z.infer<z.ZodObject<Args>>, ctx: ToolContext) => Promise<string>
15-
},
16-
) {
17-
return input(z)
10+
export function tool<Args extends z.ZodRawShape>(input: {
11+
description: string
12+
args: Args
13+
execute(args: z.infer<z.ZodObject<Args>>, context: ToolContext): Promise<string>
14+
}) {
15+
return input
1816
}
17+
tool.schema = z
1918

2019
export type ToolDefinition = ReturnType<typeof tool>

packages/web/src/content/docs/custom-tools.mdx

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,30 @@ Custom tools are functions you create that the LLM can call during conversations
99

1010
## Tool structure
1111

12-
The easiest way to create tools is using the `tool()` helper which provides type safety and validation:
12+
Tools are defined as `.ts/.js` files in the `.opencode/tool/` directory. They
13+
can also be defined globally in `~/.config/opencode/tool/`.
14+
15+
The easiest way to create tools is using the `tool()` helper which provides type safety and validation. Use `tool.schema` (which is just [Zod](https://zod.dev)) to define argument types:
1316

1417
```ts title=".opencode/tool/database.ts"
1518
import { tool } from "@opencode-ai/plugin"
1619

17-
export default tool((z) => ({
20+
export default tool({
1821
description: "Query the project database",
1922
args: {
20-
query: z.string().describe("SQL query to execute"),
23+
query: tool.schema.string().describe("SQL query to execute"),
2124
},
2225
async execute(args) {
2326
// Your database logic here
2427
return `Executed query: ${args.query}`
2528
},
26-
}))
29+
})
2730
```
2831

29-
You can also import Zod directly and return a plain object:
32+
You can also import [Zod](https://zod.dev) directly and return a plain object:
3033

3134
```ts
32-
import z from "zod/v4"
35+
import { z } from "zod"
3336

3437
export default {
3538
description: "Tool description",
@@ -42,29 +45,62 @@ export default {
4245
},
4346
}
4447
```
45-
4648
The filename becomes the tool name. This creates a `database` tool.
4749

4850
---
4951

52+
## Multiple tools per file
53+
54+
You can export multiple tools from a single file. Each export becomes a separate tool with the name `<filename>_<exportname>`:
55+
56+
```ts title=".opencode/tool/math.ts"
57+
import { tool } from "@opencode-ai/plugin"
58+
59+
export const add = tool({
60+
description: "Add two numbers",
61+
args: {
62+
a: tool.schema.number().describe("First number"),
63+
b: tool.schema.number().describe("Second number"),
64+
},
65+
async execute(args) {
66+
return args.a + args.b
67+
},
68+
})
69+
70+
export const multiply = tool({
71+
description: "Multiply two numbers",
72+
args: {
73+
a: tool.schema.number().describe("First number"),
74+
b: tool.schema.number().describe("Second number"),
75+
},
76+
async execute(args) {
77+
return args.a * args.b
78+
},
79+
})
80+
```
81+
82+
This creates two tools: `math_add` and `math_multiply`.
83+
84+
---
85+
5086
## Arguments
5187

52-
Use the `z` parameter to define tool arguments with validation and descriptions:
88+
Use `tool.schema` (which is just [Zod](https://zod.dev)) to define tool arguments with validation and descriptions:
5389

5490
```ts title=".opencode/tool/calculator.ts"
5591
import { tool } from "@opencode-ai/plugin"
5692

57-
export default tool((z) => ({
93+
export default tool({
5894
description: "Perform mathematical calculations",
5995
args: {
60-
expression: z.string().describe("Mathematical expression to evaluate"),
61-
precision: z.number().optional().describe("Decimal precision"),
96+
expression: tool.schema.string().describe("Mathematical expression to evaluate"),
97+
precision: tool.schema.number().optional().describe("Decimal precision"),
6298
},
6399
async execute(args) {
64100
// Your calculation logic here
65101
return `Result: ${eval(args.expression).toFixed(args.precision || 2)}`
66102
},
67-
}))
103+
})
68104
```
69105

70106
---
@@ -76,30 +112,16 @@ Tools receive context about the current session:
76112
```ts title=".opencode/tool/project.ts"
77113
import { tool } from "@opencode-ai/plugin"
78114

79-
export default tool((z) => ({
115+
export default tool({
80116
description: "Get project information",
81117
args: {},
82118
async execute(args, context) {
83119
// Access context information
84120
const { project, directory, worktree } = context
85121
return `Project: ${project.name}, Directory: ${directory}`
86122
},
87-
}))
123+
})
88124
```
89125

90-
---
91-
92-
## Tool locations
93-
94-
Custom tools are loaded from:
95-
96-
- Project: `.opencode/tool/`
97-
- Global: `~/.config/opencode/tool/`
98-
99-
Files must use `.js` or `.ts` extensions.
100-
101-
---
102126

103-
## Built-in tools
104127

105-
opencode includes several built-in tools: `read`, `write`, `edit`, `bash`, `glob`, `grep`, `list`, `patch`, `todo`, and `task`. [Learn more](/docs/tui#tools).

packages/web/src/content/docs/plugins.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,23 +108,23 @@ export const EnvProtection = async ({ project, client, $, directory, worktree })
108108

109109
### Custom tools
110110

111-
Create custom tools that opencode can use:
111+
Plugins can also add custom tools to opencode:
112112

113113
```ts title=".opencode/plugin/custom-tools.ts"
114114
import type { Plugin, tool } from "@opencode-ai/plugin"
115115

116116
export const CustomToolsPlugin: Plugin = async (ctx) => {
117117
return {
118118
tool: {
119-
mytool: tool((zod) => ({
119+
mytool: tool({
120120
description: "This is a custom tool",
121121
args: {
122-
foo: zod.string(),
122+
foo: tool.schema.string(),
123123
},
124124
async execute(args, ctx) {
125125
return `Hello ${args.foo}!`
126126
},
127-
})),
127+
}),
128128
},
129129
}
130130
}

0 commit comments

Comments
 (0)