We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Create a Custom Tool

Like other OpenCode config, tools (and the plugins that define them) can be project-specific or apply to all your personal projects:

  • Project-specific tools go in .opencode/plugins/ in the repo. Assuming you allow that folder to be tracked, the tools will also be shared among collaborators.
  • Personal tools go in ~/.config/opencode/plugins/ for use across many projects.

Plugins Are TypeScript

OpenCode loads TypeScript and JavaScript plugins directly, but we recommend using TypeScript so that Plugin.define() has nice type information.

Now, local plugin dependencies are not installed automatically. Plugin.define() needs to have @opencode/plugin listed in a package.json, for example:

cd .opencode
bun add @opencode/[email protected]

With that dependency available, you create a plugin file and register the tool with ctx.tool.transform(). For example:

import { Plugin } from "@opencode/plugin";

export default Plugin.define({
  id: "applymaxxing.database-tool",
  setup: async (ctx) => {
    await ctx.tool.transform((tools) => {
      tools.add({
        name: "database",
        description: "Query the project database",
        input: {
          type: "object",
          properties: {
            query: { type: "string" },
          },
          required: ["query"],
          additionalProperties: false,
        },
        options: { codemode: false },
        execute: async (input) => {
          const { query } = input as { query: string };
          return { content: `Executed query: ${query}` };
        },
      });
    });
  },
});

If that file lives at .opencode/plugins/database.ts, OpenCode discovers the plugin automatically and registers the database tool. The id names the plugin, and the name of the tool is what OpenCode exposes to the model.

Tool Definitions

Each tool needs a name, description, JSON-compatible input schema, and execute function. It can also declare an output schema. If you omit that schema, you'll need to return model-visible content (plain text) as the example does.

The optional second argument to execute contains session metadata like sessionID, agent, and messageID, plus a progress function for reporting status.

By default, custom tools use Code Mode, meaning the model writes JavaScript that calls tools, and OpenCode runs it through the execute tool. This lets the model combine tool calls and process their results in code.

That said, this example sets options: { codemode: false } so the model will call database directly, just to show that you can.

Tool names are normalized, so duplicate names can collide. Give your tool a specific name instead of trying to replace a built-in... unless you really want to override one.

Assignment

Give OpenCode a tool for running SlopLang programs.

  1. curl -fsSL https://bun.com/install | bash
    

Tell OpenCode to run and submit the CLI tests from the project root.