Skip to main content

Writing tools

The agent can call custom tools during a conversation. Two places register them, and both are pi extensions: a module that calls pi.registerTool(...) with a name, a description, a Typebox parameter schema, and an execute function.

  • Platform extensions in src/pi-agent/extensions/: charts, tables, forms, and memory. Pi loads every file passed via --extension.
  • Core agents in src/pi-agent/core-agents/: google (Gmail and Sheets), hubspot, reports, and edb-modeller. They load on the parent pi process (adrs/0004.md, adrs/0005.md).

How a tool returns results​

A tool result has two channels:

  • content — text that goes back into the model's context.
  • details — data handed to the frontend for rendering and state, and never sent to the model.

This split is why charts and forms can appear in the UI without their raw rows passing through the conversation.

The patterns​

  • Vendor HTTP via the nono proxy — HubSpot tools (hubspot_whoami, hubspot_list, hubspot_create, hubspot_update, hubspot_owners, hubspot_create_task) and the Google tools (send_email, fetch_sheet, search_spreadsheets) call {VENDOR}_BASE_URL with a phantom {VENDOR}_API_KEY. nono injects the real token; Django is not on the hot path. See HubSpot Tools and adding a vendor.
  • Deterministic rendering — render_chart, render_table, and render_form call no model and no network. They validate and shape data into the frontend's json-render components and hand it back on the details channel. rasterize_chart takes the same chart arguments and produces a PNG, for sending a chart outside the chat, for example by email.
  • Memory access — fetch_memory reads rows that a tool staged in chat_memoryentry, so large tables are pulled into context only when the agent asks, not on every fetch. The model and the API were renamed off their old MCP names (adrs/0006.md).

Adding a tool​

Drop a new .ts file in src/pi-agent/extensions/ that default-exports a (pi: ExtensionAPI) => void and calls pi.registerTool(...). Register it with --extension in pi_runtime.py and k8s_runtime.py. Add any shared logic as a plain sibling module (like memory.ts or chartShape.ts). Pi only turns files passed via --extension into tools, so imported helpers stay ordinary modules.

A tool that belongs to one platform agent goes in that agent's folder under src/pi-agent/core-agents/ and is loaded from that agent's index.ts, not as another loose file in extensions/.