Skip to content
DocsHaarness CLI
On this page

Add a custom tool with an extension

An extension is a small TypeScript or JavaScript module that gives the agent your own tools, or hooks into what Haarness does: adding context to a prompt, answering a permission prompt, or setting environment variables for shell commands.

Extensions run with Bun. From the command line and the terminal UI you need Bun on your PATH, or HAARNESS_BUN set to its full path; Haarness does not install it for you. Desktop brings its own copy.

Create your first extension

haarness extension init ./my-extension

This creates index.ts, its type definitions and a package.json. The extension is named after its folder, here my_extension; pass --name to choose another. The starter adds a greet tool (shortened here):

import type { Extension } from "./api";

export default {
  apiVersion: 1,
  name: "my_extension",
  tools: {
    greet: {
      description: "Greet someone by name",
      parameters: {
        type: "object",
        properties: { name: { type: "string" } },
        required: ["name"],
      },
      execute(args: { name: string }) {
        return `Hello, ${args.name}!`;
      },
    },
  },
} satisfies Extension;

Edit it in any editor; the type definitions give you autocomplete for every tool and hook.

Install it

haarness extension install ./my-extension
haarness extension check

install runs the extension once to validate it, then turns it on. check loads every enabled extension and reports problems with names, hooks or tool schemas; add --verbose to see the extension's console output.

The agent sees your tool as ext__my_extension__greet, and /tools lists it. An extension tool cannot replace a built-in or MCP tool.

Haarness reloads a changed local extension once no turn is running. Run install again to check the new version first; if it fails to load, the previous version keeps running.

Pass settings to an extension with --options '{"greeting":"Hello"}'. Add --project to any extension command to manage this project's extensions instead of your own.

Install one from npm

haarness extension install npm:[email protected]

Haarness installs the exact version into its own folder, with package install scripts turned off. It never downloads missing dependencies while running.

A package says where its extension is in package.json:

{
  "name": "haarness-extension-example",
  "version": "1.2.3",
  "type": "module",
  "haarness": { "extension": "src/index.ts" }
}

Without that field, Haarness uses main or module, or an index.ts, index.mjs or index.js file.

To move to a newer npm version, remove the extension and install the new version.

Turn extensions off or remove one

haarness extension list             # what is installed; runs no extension code
haarness extension disable my_extension   # one extension, by name or list number
haarness extension disable                # all extensions
haarness extension enable my_extension
haarness extension remove my_extension    # unregister it; your files stay

Naming rules

Extension and tool names are 1 to 24 lowercase letters, digits or single underscores. Double underscores are not allowed.

Before you install

Installing an extension is a decision to trust it: its code runs on your machine with your user's access. Extensions run in a separate process and can be cancelled. Changes take effect when no turn is running. A project's extensions load only while project configuration is on. Limits on what an extension may use are in Execution budgets.