Description
Custom tools defined via @opencode-ai/plugin receive undefined for optional args when the AI model omits them. This is the same root cause as #9020, which was fixed for MCP tools in #11203 but not for custom plugin tools.
Steps to Reproduce
- Define a custom tool with an optional parameter that has a Zod
.default():
import { tool } from "@opencode-ai/plugin";
import { randomUUID } from "crypto";
export default tool({
description: "Generate one or more UUID v4s.",
args: {
count: tool.schema.number().min(1).max(10).default(1).describe("Number of UUIDs to generate."),
},
async execute(args) {
return Array.from({ length: args.count }, () => randomUUID()).join("\n");
},
});
- When the AI calls this tool without specifying
count, the tool receives { count: undefined } (or {})
- The Zod
.default(1) is never applied, so args.count is undefined
Array.from({ length: undefined }) produces an empty array, and the tool returns ""
Expected Behavior
args.count should be 1 (the Zod default), or at minimum the args should be coerced from undefined to {} so Zod can apply defaults during parsing.
Actual Behavior
args.count is undefined. The Zod .default() is never triggered because the args are passed directly to execute() without being parsed through the Zod schema.
Context
The fix in #11203 patched this for MCP tools in packages/opencode/src/mcp/index.ts:
- arguments: args as Record<string, unknown>,
+ arguments: (args || {}) as Record<string, unknown>,
But custom plugin tools go through a different code path (likely packages/opencode/src/tool/) where the same coercion was not applied.
Workaround
Handle undefined manually in the execute function:
async execute(args) {
const count = args.count ?? 1;
return Array.from({ length: count }, () => randomUUID()).join("\n");
},
Environment
- OpenCode version: 1.2.27
- Model: Claude Sonnet 4.6
Description
Custom tools defined via
@opencode-ai/pluginreceiveundefinedfor optional args when the AI model omits them. This is the same root cause as #9020, which was fixed for MCP tools in #11203 but not for custom plugin tools.Steps to Reproduce
.default():count, the tool receives{ count: undefined }(or{}).default(1)is never applied, soargs.countisundefinedArray.from({ length: undefined })produces an empty array, and the tool returns""Expected Behavior
args.countshould be1(the Zod default), or at minimum the args should be coerced fromundefinedto{}so Zod can apply defaults during parsing.Actual Behavior
args.countisundefined. The Zod.default()is never triggered because the args are passed directly toexecute()without being parsed through the Zod schema.Context
The fix in #11203 patched this for MCP tools in
packages/opencode/src/mcp/index.ts:But custom plugin tools go through a different code path (likely
packages/opencode/src/tool/) where the same coercion was not applied.Workaround
Handle
undefinedmanually in the execute function:Environment