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

Plugin Tools

OpenCode registers every custom tool through a plugin. A published plugin can bundle tools with commands, skills, agents, or hooks so other developers can install the whole extension at once.

Use a skill or agent when a workflow needs only instructions. Add a tool when it needs code, for example to query an API or database.

To register a tool in a plugin, call tools.add() inside a ctx.tool.transform() callback. Here's a plugin that fetches the top comment on a Hacker News post:

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

type HNItem = {
  text?: string;
  kids?: number[];
};

async function getItem(id: number): Promise<HNItem | null> {
  const res = await fetch(
    `https://hacker-news.firebaseio.com/v0/item/${id}.json`,
  );
  if (!res.ok) {
    throw new Error(`failed to fetch Hacker News item ${id}`);
  }
  return (await res.json()) as HNItem | null;
}

export default Plugin.define({
  id: "bootdev.hn-top-comment",
  setup: async (ctx) => {
    await ctx.tool.transform((tools) => {
      tools.add({
        name: "hn_top_comment",
        description: "Get the top comment for a Hacker News post by ID",
        input: {
          type: "object",
          properties: {
            post_id: { type: "number" },
          },
          required: ["post_id"],
          additionalProperties: false,
        },
        output: {
          type: "object",
          properties: {
            comment: { type: "string" },
          },
          required: ["comment"],
          additionalProperties: false,
        },
        execute: async (input) => {
          const { post_id } = input as { post_id: number };
          const post = await getItem(post_id);
          if (!post) {
            throw new Error(`Hacker News post ${post_id} was not found`);
          }
          const topCommentID = post.kids?.[0];
          const item = topCommentID ? await getItem(topCommentID) : null;
          const comment =
            item?.text ?? "The top comment's text is unavailable.";
          return {
            output: { comment },
            content: comment,
          };
        },
        options: { codemode: false },
      });
    });
  },
});

Tool names are normalized, and duplicate names can collide. Use a specific name or namespace instead of trying to replace a built-in tool.

Assignment

Create a tool that reports the results of running every example.

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