# How to Define Custom Tools for PrimeAgent: A Complete Extension Guide

> Learn how to define custom tools for PrimeAgent using the pi.registerTool() API. Extend LLM capabilities with your own tools in this comprehensive guide.

- Repository: [Prime Intellect/prime-agent](https://github.com/PrimeIntellect-ai/prime-agent)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Use the `pi.registerTool()` API in a PrimeAgent extension to register custom tools that the LLM can invoke alongside built‑in tools like `bash` and `edit`.**

Custom tools are the primary way to extend PrimeAgent's capabilities. According to the PrimeIntellect-ai/prime-agent source code, you define tools through an **extension**—a TypeScript module loaded at runtime that receives an `ExtensionAPI` object with full access to the tool registry.

## How PrimeAgent Extensions Work

Extensions are discovered from two locations:

- `~/.prime/agent/extensions/` — user‑global extensions
- `.prime/agent/extensions/` — project‑local extensions

The loader in [`packages/coding-agent/src/core/extensions/loader.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/extensions/loader.ts) uses **jiti** to execute these files as native TypeScript, eliminating the need for a build step. When PrimeAgent starts, every `.ts` file in these directories is evaluated, and each default export function receives the `pi` API object.

## The Extension API (pi)

The `pi` object implements `ExtensionAPI` as defined in [`packages/coding-agent/src/core/extensions/types.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/extensions/types.ts). It provides three primary methods:

| Method | Purpose |
|--------|---------|
| `pi.on(event, handler)` | Subscribe to lifecycle events |
| `pi.registerCommand(def)` | Add CLI commands |
| `pi.registerTool(def)` | **Register custom tools for the LLM** |

The `registerTool` signature appears at **line 1081** of [`types.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/types.ts) and accepts a `ToolDefinition<TParams, TDetails, TState>` object.

## Tool Definition Structure

A complete tool definition requires five fields:

```typescript
{
  name: string;           // LLM-facing identifier
  label: string;          // Human-readable UI label
  description: string;    // Help text for the model
  parameters: TSchema;    // Typebox JSON schema
  execute: Function;      // Async execution handler
}

```

### Parameter Schema with Typebox

PrimeAgent uses **typebox** for runtime validation. Import `Type` from `"typebox"` and construct schemas that describe the JSON structure the LLM should emit:

```typescript
import { Type } from "typebox";

// Required string parameter
Type.String({ description: "..." })

// Object with multiple fields
Type.Object({
  path: Type.String({ description: "File path" }),
  content: Type.String({ description: "File content" }),
});

```

### Execute Function Signature

The `execute` callback receives five arguments as implemented in [`packages/coding-agent/src/core/tools/tool-definition-wrapper.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/tools/tool-definition-wrapper.ts):

```typescript
async execute(
  toolCallId: string,      // Unique ID for this invocation
  params: TParams,         // Validated parameters from schema
  signal: AbortSignal,     // Cancellation signal
  onUpdate: Function,      // Stream partial results to UI
  ctx: ExecutionContext    // Agent context and utilities
): Promise<ToolResult>

```

The return value must contain a `content` array and optional `details`:

```typescript
return {
  content: [{ type: "text", text: "Result here" }],
  details: {}, // Arbitrary structured data
};

```

## Complete Custom Tool Example

Create `~/.prime/agent/extensions/greet.ts` with this implementation based on the official example in [`packages/coding-agent/docs/extensions.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/extensions.md) (lines 76‑90):

```typescript
// ~/.prime/agent/extensions/greet.ts
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";

export default function (pi: ExtensionAPI) {
  pi.registerTool({
    name: "greet",
    label: "Greet",
    description: "Greet someone by name",
    parameters: Type.Object({
      name: Type.String({ description: "Name to greet" }),
    }),
    async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
      return {
        content: [{ type: "text", text: `Hello, ${params.name}!` }],
        details: {},
      };
    },
  });
}

```

This matches the concrete example found in [`packages/coding-agent/examples/extensions/hello.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/examples/extensions/hello.ts).

## Running and Testing Custom Tools

Launch PrimeAgent with your extension loaded:

```bash
prime-agent -e ~/.prime/agent/extensions/greet.ts

```

Once running, the LLM will see `greet` in its tool catalog. When the model emits a tool call like:

```json
{
  "toolCallId": "tc-1",
  "toolName": "greet",
  "arguments": { "name": "Alice" }
}

```

The runtime in [`packages/coding-agent/src/modes/interactive/components/tool-execution.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/modes/interactive/components/tool-execution.ts):

1. Looks up `greet` in the registry populated by [`loader.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/loader.ts)
2. Validates `arguments` against the Typebox schema
3. Executes your `execute` function
4. Streams the result back to the model and renders it in the TUI

## Advanced Patterns for Custom Tools

### Streaming Progress Updates

Use `onUpdate` to send partial results before completion—useful for long‑running operations:

```typescript
async execute(toolCallId, params, signal, onUpdate, ctx) {
  onUpdate({ content: [{ type: "text", text: "Starting..." }] });
  
  const result = await expensiveOperation(params);
  
  return {
    content: [{ type: "text", text: result }],
    details: { duration: Date.now() - start },
  };
}

```

### Accessing Context and State

The `ctx` parameter provides access to the session state, file system abstractions, and other agent services. This enables tools that interact with the codebase or persist data across invocations.

### Cancellation Handling

Respect the `signal` parameter to abort operations when the user interrupts:

```typescript
async execute(_toolCallId, params, signal, _onUpdate, _ctx) {
  const controller = new AbortController();
  signal.addEventListener("abort", () => controller.abort());
  
  return await fetchData(params, controller.signal);
}

```

## Key Source Files Reference

| File | Role in Custom Tool Lifecycle |
|------|------------------------------|
| [`packages/coding-agent/docs/extensions.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/extensions.md) | User guide with examples (lines 76‑90) |
| [`packages/coding-agent/src/core/extensions/types.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/extensions/types.ts) | `ExtensionAPI` interface and `registerTool` signature (line 1081) |
| [`packages/coding-agent/src/core/extensions/loader.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/extensions/loader.ts) | Extension discovery and registration |
| [`packages/coding-agent/src/core/tools/tool-definition-wrapper.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/tools/tool-definition-wrapper.ts) | Tool result formatting and runtime adapter |
| [`packages/coding-agent/examples/extensions/hello.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/examples/extensions/hello.ts) | Working example extension |

## Summary

- **PrimeAgent custom tools** are defined in TypeScript extensions loaded from `~/.prime/agent/extensions/` or project‑local paths.
- Use **`pi.registerTool()`** from the `ExtensionAPI` to add tools, passing a `ToolDefinition` with name, label, description, **typebox parameters**, and **async execute function**.
- The runtime validates parameters, executes your handler, and streams results back to the LLM and UI.
- Extensions execute without compilation thanks to **jiti**, enabling rapid iteration.

## Frequently Asked Questions

### What programming language should I use for PrimeAgent extensions?

Write extensions in **TypeScript**. The loader uses jiti to execute `.ts` files directly without a build step, giving you full type safety and modern JavaScript features.

### Where should I place my extension files?

Place them in `~/.prime/agent/extensions/` for global access, or `.prime/agent/extensions/` in your project root for repository‑specific tools. The discovery mechanism checks both locations as documented in [`packages/coding-agent/docs/extensions.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/extensions.md).

### How does parameter validation work for custom tools?

PrimeAgent uses **typebox** schemas defined in the `parameters` field. The runtime validates the LLM's JSON arguments against this schema before executing your `execute` function, ensuring type safety without manual parsing.

### Can I use external npm packages in my custom tools?

Yes. Extensions run in a Node.js context with access to npm dependencies. Install packages in your extension directory or reference globally available modules—PrimeAgent's jiti loader resolves imports normally.