13 · 1 patch
Write your first plugin
Declare what you need, describe the tool and write what it does. There is no fourth step.
Adding a tool the model can use is the most common case. The structure is always the same.
export const name = 'example-changelog'
export const inject = ['tools']
export function apply(ctx) {
ctx.tools.register(defineTool({
name: 'read_changelog',
description: 'Returns the recent entries from the project changelog.',
parameters: {
version: { type: 'string', required: true, description: 'Version to look up' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args, exec) {
// exec.signal lets the work be cancelled if the turn is aborted
return await findEntry(args.version, exec.signal)
},
}))
}
Four rules that avoid surprises
- Rule one Arguments arrive validated Against what you declared, so inside the body they have the right type. What your description does not express, such as a number being positive, is still on you.
- Rule two Return data, not a message The human readable explanation goes separately. If you return prose, you force whoever receives it to parse it.
- Rule three Exceptions are for real failures Throwing marks the result as an error, and that is for infrastructure failures. A correct but unwanted outcome, such as a command exiting badly, is represented in the data.
- Rule four Cancellation has to be respected When the signal fires, work in progress stops.
Two things you get for free
- Free one It works in Code mode Any registered tool is available with no extra integration, with types derived from the same description
- Free two It has a default interface If you do not define how it looks, a generic card appears. There are specific formats for terminal output, file changes and search results
Technical detail optional
The functions that draw the card have to be pure, because they run both live and when replaying a stored session. No reading files, checking the clock or using randomness. If you need the previous contents of a file inside the presenter, that belongs in the result metadata, not there.
Publishing it
A plugin is distributed as a package that contributes a configuration layer. The recommended flow is to test first in Creator mode, where it can be mounted and unmounted in memory, and to package afterwards.
For others to find it, the repository is tagged with dsh-plugin.