How to Write Custom Tools or Extensions for the OMP Coding Agent: A Complete Guide

To write custom tools for the OMP coding agent, create a TypeScript module that exports a CustomTool object conforming to the interface defined in types.ts, drop it into the ~/.omp/custom-tools directory (or a path specified by OMP_CUSTOM_TOOLS_PATH), and the agent will automatically discover and register it via discoverAndLoadCustomTools() at startup.

The OMP (Oh-My-Pi) coding agent from the can1357/oh-my-pi repository provides a flexible plugin system that allows developers to extend its capabilities without modifying core agent code. By writing custom tools or extensions for the OMP coding agent, you can integrate external APIs, custom scripts, or domain-specific logic that the LLM can invoke dynamically during conversations. This guide walks through the architecture, implementation patterns, and registration process based on the actual source code structure.

Understanding the Custom Tool Architecture

The OMP coding agent treats tools as first-class plugins that follow a strict runtime contract. Understanding these four key stages—definition, discovery, registration, and execution—ensures your extensions integrate seamlessly with the agent's tooling pipeline.

Tool Definition and Contract

Every custom tool must export a CustomTool object (or factory function) that describes its metadata, input schema, and execution logic. The type definitions reside in packages/coding-agent/src/extensibility/custom-tools/types.ts, which specifies:

  • name: The unique identifier the model uses to invoke the tool.
  • parameters: A JSON Schema object defining valid arguments.
  • run: An async function receiving validated arguments and a CustomToolContext (containing logger, signal, and helpers).
  • renderResult (optional): A function for custom TUI formatting.

Discovery and Loading

At launch, the agent calls discoverAndLoadCustomTools() from packages/coding-agent/src/sdk.ts. This routine scans the default omp-tools directory or any folder added via the OMP_CUSTOM_TOOLS_PATH environment variable for .ts and .js files. It dynamically imports each module using import(), validates the exported shape against the CustomTool interface, and prepares them for registration.

Registration and Execution

Each discovered tool is wrapped by CustomToolAdapter in packages/coding-agent/src/extensibility/custom-tools/wrapper.ts. This adapter normalizes the tool for the internal pipeline, handling async execution, abort signals, and result rendering. When the model decides to call a tool, the agent looks up the tool by name in the registry, invokes its run method, and streams the result back to the conversation.

MCP Integration

Tools are exposed to the Multi-Channel Protocol (MCP) layer via packages/coding-agent/src/mcp/tool-bridge.ts. This bridge makes custom tools available to both the CLI interface and remote clients, ensuring consistent behavior across all interfaces.

Creating Your First Custom Tool

A valid custom tool is a TypeScript module that exports a single CustomTool object. Below is a complete example that fetches weather data from a public API.

// File: ~/.omp/custom-tools/weather.ts
import type { CustomTool, CustomToolContext, RenderResultOptions } from "@oh-my-pi/pi-coding-agent/extensibility/custom-tools/types";

export const weather: CustomTool = {
  // 1️⃣ Tool identifier used by the model
  name: "weather",

  // 2️⃣ JSON Schema for input validation
  description: "Get the current weather for a city.",
  parameters: {
    type: "object",
    properties: {
      city: { type: "string", description: "Name of the city, e.g. 'Berlin'" },
    },
    required: ["city"],
  },

  // 3️⃣ Execution function with automatic abort signal support
  async run(args: { city: string }, ctx: CustomToolContext): Promise<string> {
    const { logger, signal } = ctx;
    const url = `https://wttr.in/${encodeURIComponent(args.city)}?format=%C+%t`;
    
    const resp = await fetch(url, { signal });
    if (!resp.ok) {
      logger.error("Weather API error", { status: resp.status });
      throw new Error(`Unable to fetch weather for ${args.city}`);
    }
    const text = await resp.text();
    return `Current weather in ${args.city}: ${text}`;
  },

  // 4️⃣ Optional TUI rendering for colored output
  renderResult: (result: string, opts: RenderResultOptions) => {
    return opts.theme.fg("toolResult", result);
  },
};

Key implementation details from this example:

  • Type Safety: Import types from @oh-my-pi/pi-coding-agent/extensibility/custom-tools/types to ensure compile-time validation.
  • Abort Handling: The signal property in CustomToolContext allows the tool to respect cancellation requests from the agent.
  • Logging: Use ctx.logger for structured logging that appears in the agent's debug output.
  • Rendering: The optional renderResult function receives theme helpers for consistent terminal styling.

Installing and Registering Custom Tools

The OMP coding agent uses a zero-configuration discovery system. Simply place your tool file in the correct location and restart the agent.

Default Installation Path

Create the standard directory and place your TypeScript file inside:

mkdir -p ~/.omp/custom-tools
cp weather.ts ~/.omp/custom-tools/

Custom Tool Paths

Alternatively, specify additional directories via the environment variable:

export OMP_CUSTOM_TOOLS_PATH="/path/to/company-tools:/path/to/personal-tools"
omp start

The agent recursively scans these paths for .ts and .js files at startup. No manual registration steps are required—discoverAndLoadCustomTools() handles all loading and validation automatically.

Using Custom Tools in Conversations

Once registered, the model automatically recognizes when to invoke your tool based on its description and the conversation context.

$ omp ask "What's the weather in Tokyo?"
🛠️  Calling tool: weather
Current weather in Tokyo: Clear +26°C

The agent handles the entire execution lifecycle: validation of arguments against your JSON Schema, invocation of the run method with proper context, and streaming of results back to the model. If your tool exports a renderResult function, the TUI applies the formatting before displaying the output to the user.

Summary

Writing custom tools or extensions for the OMP coding agent involves these core concepts:

Frequently Asked Questions

What is the minimum required structure for a custom tool?

A valid custom tool must export an object with at least three properties: name (string), parameters (JSON Schema object), and run (async function). The run function receives validated arguments and a CustomToolContext object containing logger and signal. While renderResult is optional for custom UI formatting, the tool will function with just these three core fields as defined in packages/coding-agent/src/extensibility/custom-tools/types.ts.

Can I use JavaScript instead of TypeScript for custom tools?

Yes. The discovery mechanism in packages/coding-agent/src/sdk.ts accepts both .ts and .js files. While the examples use TypeScript for type safety, plain JavaScript modules that export a CustomTool-shaped object will load and execute correctly, though you lose compile-time validation against the interface definitions.

How does the agent handle errors in custom tools?

Errors thrown within the run method are caught by the CustomToolAdapter in packages/coding-agent/src/extensibility/custom-tools/wrapper.ts and converted into structured error responses for the model. Use ctx.logger.error() for debuggable stack traces, and throw descriptive Error objects to help the LLM understand what went wrong and potentially retry with corrected parameters.

Where should I place custom tools for team-wide or CI/CD usage?

For shared environments, set the OMP_CUSTOM_TOOLS_PATH environment variable to point to a version-controlled directory (e.g., export OMP_CUSTOM_TOOLS_PATH="./tools"). The agent scans all paths listed in this variable at startup, allowing you to commit custom tools to your repository and share them across team members or CI pipelines without modifying the default ~/.omp/custom-tools user directory.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →