# Best Practices for Developing New Tools for PrimeAgent: A Complete Guide

> Discover best practices for developing new tools for PrimeAgent. Learn to register tools, implement execution logic, and provide UI hints for seamless integration.

- Repository: [Prime Intellect/prime-agent](https://github.com/PrimeIntellect-ai/prime-agent)
- Tags: best-practices
- Published: 2026-08-09

---

**To add a new tool to PrimeAgent, register it via `pi.registerTool` with a strict Typebox JSON-Schema, implement abort-aware `execute` logic using the `signal` and `onUpdate` callbacks, provide clear UI hints via `promptSnippet` and `promptGuidelines`, and return a standardized result object containing `content`, optional `details`, and an `isError` flag.**

PrimeAgent’s architecture is built for extensibility through custom tools that large language models can invoke during task execution. Following the conventions established in the `PrimeIntellect-ai/prime-agent` repository ensures your tools integrate seamlessly with the parallel execution engine, remain type-safe, and provide a consistent user experience across TUI, RPC, and print modes.

## Registering Tools Through the Extension API

All custom tools must be registered using the `pi.registerTool` method available through the Extension API. According to the source code 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), registration can occur at extension load time, during the `session_start` event, or within a command handler.

The registration object requires a unique, lowercase `name`, a human-readable `label`, and a `description`. These fields are indexed by the agent’s tool discovery system to populate the system prompt available to the LLM.

## Defining Strict Parameter Schemas with Typebox

PrimeAgent enforces type safety at the boundary between the LLM and your code by requiring a JSON-Schema definition for all parameters. The codebase uses **Typebox** to build these schemas programmatically.

In [`packages/coding-agent/docs/extensions.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/extensions.md), the documentation emphasizes avoiding `any` types and instead using `Type.Object` to define required and optional fields with validation constraints. This ensures that invalid tool invocations are caught before reaching your execution logic.

For example, a tool requiring a string and an optional integer would define:

```typescript
parameters: Type.Object({
  query: Type.String({ description: "The search query." }),
  limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 }))
})

```

## Implementing Abort-Aware Execution Logic

The `execute` function signature defined in [`src/core/tools/tool-definition-wrapper.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/src/core/tools/tool-definition-wrapper.ts) is:

```typescript
async execute(toolCallId, params, signal, onUpdate, ctx)

```

**Abort handling** is critical for PrimeAgent’s parallel tool execution model. Your implementation must respect the `signal` (an `AbortSignal`) to cancel long-running operations when the user interrupts the session or when the agent decides to halt execution.

**Streaming updates** are provided via the `onUpdate` callback, which accepts partial `MessageContent` arrays. This allows the TUI to display progress for tools that perform multi-step operations, such as the Bash tool implementation found in [`src/core/tools/bash.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/src/core/tools/bash.ts).

## Providing UI Hints for LLM Discoverability

To ensure the LLM knows when and how to invoke your tool, provide explicit UI hints:

- **`promptSnippet`**: A short string shown in the system prompt demonstrating basic usage (e.g., `"summarize <text>"`).
- **`promptGuidelines`**: An array of bulleted strings providing specific usage guidance. Always include the tool name explicitly in these guidelines to improve model recall.

These hints are parsed from the tool definition and injected into the system prompt context, as documented in [`packages/coding-agent/docs/extensions.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/docs/extensions.md).

## Returning Well-Formed Tool Results

All tools must return an object conforming to the `ToolResult` interface:

```typescript
{
  content: MessageContent[];
  details?: object;
  isError?: boolean;
}

```

The `content` array contains the primary output visible to the user and LLM. The optional `details` field can include structured metadata for debugging or downstream processing. Crucially, **errors must be signaled with `isError: true`** rather than thrown, allowing PrimeAgent to distinguish between execution failures and successful completions with informative output.

## Adding Custom Rendering for the TUI

For advanced user experiences, optionally implement `renderCall` and `renderResult` methods. These functions control how tool invocations and their outputs appear in the Terminal User Interface (TUI).

- **`renderCall(args, theme, ctx)`**: Returns a string representing the tool call in the chat history.
- **`renderResult(result, options, theme, ctx)`**: Returns a formatted string for the result display.

The built-in IPython tool in [`src/core/tools/ipython.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/src/core/tools/ipython.ts) demonstrates how to use theme colors (via `theme.fg()`) to distinguish different tool states visually.

## Testing Your Custom Tools

New tools should include unit tests placed under `packages/coding-agent/test/`, following the pattern established in [`tools.test.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/tools.test.ts). Tests should exercise:

1. Parameter validation against the JSON-Schema.
2. Execution logic with mocked `signal` and `onUpdate` callbacks.
3. Error handling paths where `isError` is returned.

Run `npm run check` to ensure type safety across the extension boundary before submitting changes.

## Complete Implementation Example

Below is a minimal yet complete custom tool that summarizes text. It demonstrates schema definition, execution with streaming updates, error handling, and UI hints as implemented in the PrimeAgent codebase.

```typescript
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { Type } from "typebox";

export default function (pi: ExtensionAPI) {
  // Register the tool at load time
  pi.registerTool({
    name: "summarize",
    label: "Summarize",
    description: "Generate a concise summary of the provided text.",
    // Short snippet shown in the system prompt
    promptSnippet: "summarize <text>",
    // Explicit usage guidance for the model
    promptGuidelines: [
      "Use summarize when the user asks for a brief recap of a longer passage."
    ],
    // Parameter schema – required `text` field, optional `maxLength`
    parameters: Type.Object({
      text: Type.String({ description: "The text to summarize." }),
      maxLength: Type.Optional(Type.Integer({ minimum: 10, maximum: 500 }))
    }),

    async execute(_toolCallId, params, signal, onUpdate) {
      // Simulate a streaming summarizer (replace with real logic)
      const chunks = [
        "Analyzing input…",
        "Generating summary…",
        "Done."
      ];
      for (const chunk of chunks) {
        if (signal?.aborted) break;
        // Emit partial output so the UI can show progress
        onUpdate?.({ content: [{ type: "text", text: chunk }] });
        await new Promise(r => setTimeout(r, 200));
      }

      // Simple truncation implementation
      const summary = params.maxLength
        ? params.text.slice(0, params.maxLength) + "…"
        : params.text.slice(0, 200) + "…";

      return {
        content: [{ type: "text", text: summary }],
        details: { originalLength: params.text.length }
      };
    },

    // Optional custom rendering – makes the call visually distinct
    renderCall(args, theme, ctx) {
      const title = theme.fg("accent", "Summarize");
      return `${title}: ${args.text.slice(0, 30)}…`;
    },

    renderResult(result, _options, theme, _ctx) {
      const success = theme.fg("success", "✔");
      return `${success} ${result.content[0].text}`;
    }
  });
}

```

## Key Source Files to Reference

When developing new tools, consult these reference implementations in the `PrimeIntellect-ai/prime-agent` repository:

- **[`src/core/tools/bash.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/src/core/tools/bash.ts)**: Reference for command execution, error handling, and streaming output.
- **[`src/core/tools/tool-definition-wrapper.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/src/core/tools/tool-definition-wrapper.ts)**: Core type definitions for `ToolDefinition` and the execution contract.
- **[`src/core/tools/ipython.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/src/core/tools/ipython.ts)**: Demonstrates wrapping a persistent Python kernel as a tool, including `onUpdate` usage for streaming.
- **[`test/tools.test.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/test/tools.test.ts)**: Test suite covering built-in tools; serves as a template for new tool tests.
- **[`docs/extensions.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/docs/extensions.md)**: Full documentation for custom tools, UI hints, and event flow.

## Summary

- **Register** tools via `pi.registerTool` at load time or during session initialization.
- **Define strict schemas** using Typebox to validate LLM-generated parameters before execution.
- **Handle cancellation** by checking `signal.aborted` in long-running `execute` implementations.
- **Stream progress** using `onUpdate` to provide real-time feedback in the TUI.
- **Guide the LLM** with `promptSnippet` and `promptGuidelines` containing explicit tool names and usage patterns.
- **Return standardized results** with `content`, optional `details`, and `isError` flags for proper error signaling.
- **Test thoroughly** under `packages/coding-agent/test/` and validate with `npm run check`.

## Frequently Asked Questions

### What is the correct way to handle errors in a PrimeAgent tool?

Return an object with `isError: true` and include error details in the `content` array. Do not throw exceptions, as uncaught errors can disrupt the agent's event pipeline and parallel execution flow.

### How do I make my tool parameters optional in the schema?

Use `Type.Optional()` from Typebox to wrap the field definition. For example: `Type.Optional(Type.Integer({ minimum: 1 }))`. This generates the correct JSON-Schema to indicate the field is not required while maintaining type safety.

### Can I update the UI while my tool is still running?

Yes. Call the `onUpdate` callback within your `execute` function to stream partial results. This is essential for long-running operations like file searches or API calls, allowing users to see progress before the final result is returned.

### Where should I place tests for my custom tool?

Add test files under `packages/coding-agent/test/`, following the naming convention and patterns found in [`tools.test.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/tools.test.ts). Ensure your tests mock the `signal` and `onUpdate` parameters to verify both successful execution and cancellation paths.