

0 / 2 embers
0 / 3000 xp
click for more info
Complete a lesson to start your streak
click for more info
Still calibrating
click for more info
Not enough gems
Cost: 6 gems
1: Plugins
incomplete
2: Loading Plugins
incomplete
3: Install Plugins
incomplete
4: Plugin Transforms
incomplete
5: Plugin Hooks
incomplete
6: Plugin Tools
incomplete
7: Publishing Plugins
incomplete
Back
ctrl+,
Next
ctrl+.
This lesson's interactive features are locked, please to keep using them
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.
Create a tool that reports the results of running every example.
Tell OpenCode to run and submit the CLI tests from the project root.