How Pi's tool use works (and how to add your own tools)
Pi, the agent bundled with openclawOS, has a tool loop with browser, shell, file system, vector memory, and more. Here's how it works and how to extend it.
- pi
- tool use
- skills
- deep dive
Pi is the agent that ships with openclawOS. Its job is “respond usefully to messages”. The interesting part is that it can do more than just write text — it has tools.
This is how Pi’s tool loop works under the hood, and how you’d add your own.
The loop in five sentences
- A message arrives, binding routes it to Pi.
- Pi calls the LLM with the conversation context, the available tools, and a system prompt.
- The LLM either emits a final reply (Pi sends it) or emits a tool call.
- Pi executes the tool call, sends the result back to the LLM, loops.
- Loops bound at some configurable max-iterations (default 20) to prevent runaways.
Every modern tool-using agent works roughly this way. The fun is in the details.
What ships in the box
Pi’s stock toolset:
- shell — execute a command, capture stdout/stderr. Sandboxed by default.
- fs — read/write/list files in a configurable workspace.
- browser — Playwright-backed headless browser; navigate, click, scrape.
- search — pluggable web search (Brave, Perplexity, Tavily).
- memory — read/write the vector store and structured facts.
- canvas — emit structured UI for iOS/Android nodes.
- mcp — call any Model Context Protocol server you’ve registered.
- subagent — delegate to a nested agent loop.
Each is a JSON schema + a TypeScript handler. The LLM sees the schema; Pi runs the handler.
How tools are picked per binding
Tools are scoped per binding. A coding Pi binding might enable shell, fs, browser. A research Pi binding might enable search, browser, memory. A child-friendly Pi binding might disable shell and fs entirely.
agent: pi
tools:
enable: [search, browser, memory, subagent]
disable: [shell, fs]
Custom tools — the minimal version
Add ~/.openclaw/skills/weather.ts:
import { defineSkill } from "openclawos/skills";
export default defineSkill({
name: "weather",
description: "Get current weather for a city.",
input: {
type: "object",
properties: { city: { type: "string" } },
required: ["city"],
},
async run({ city }) {
const res = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);
const data = await res.json();
const current = data.current_condition[0];
return `${current.temp_C}°C, ${current.weatherDesc[0].value} in ${city}`;
},
});
The Gateway hot-reloads the file. Pi sees weather in its tools list on the next call. The model can now invoke it.
Sandboxing
For tools that touch the file system or shell, openclawOS provides a sandboxing layer. Run the tool in a worker subprocess with restricted permissions:
export default defineSkill({
name: "untrusted_shell",
runtime: { isolation: "worker", capabilities: { fs: "readonly", net: false } },
// ...
});
This matters when Pi’s loop is exposed to untrusted users (e.g., a public Discord bot).
MCP servers
Anthropic’s Model Context Protocol gives a standard way for external processes to expose tools. openclawOS speaks MCP — register a server, its tools appear in Pi’s toolset:
mcp:
servers:
- name: filesystem
command: npx
args: ["@modelcontextprotocol/server-filesystem", "/Users/dipankar/notes"]
Anything the MCP community ships is now usable by Pi.
Sub-agents
Pi can delegate to nested agents — a “researcher” sub-agent with its own toolset, called from Pi’s main loop with a focused prompt, returning a summary. Useful for keeping the main loop’s context clean while doing deep exploration in a side loop.
Streaming and ACK
Tool calls can take seconds. Pi streams partial results to the channel:
- For channels that support “typing…” (Telegram, WhatsApp, Discord), Pi indicates active.
- For channels with reactions (Discord, Slack), Pi adds an ACK reaction immediately.
- Final reply lands when the loop finishes.
Cost control
Each tool call burns tokens (the tool call goes back as part of the context). For high-volume use, two heuristics:
- Cap iterations on cheap models, escalate to expensive models for long loops.
- Use a
summarisetool that compresses a tool’s output before re-injecting into context.
Pi has both knobs.
What this gives you
Tool use is the difference between a chatbot (“I think the answer might be…”) and an agent (“I checked, the answer is…”). Pi’s loop is conventional but the integration with channels, sessions and memory is what makes it useful day-to-day.
The Skill API is small enough that any TypeScript developer can write a useful tool in 20 minutes. The lobster tank is full of community skills — weather, smart home, journaling, calendar, ticketing — most of them under 30 lines.
Frequently asked
In openclawOS, "skill" and "tool" are synonyms. The model sees them as "tools" in the API sense (an OpenAI/Anthropic JSON schema); humans tend to call them "skills" when discussing what they do.
Related reading
How openclawOS uses sqlite-vec for agent memory
An honest look at how openclawOS stores Pi's memory: sessions in normal SQLite tables, vector recall via sqlite-vec, no Pinecone or Weaviate required.
How multi-channel AI agents actually work under the hood
A deep dive into the openclawOS kernel: routing, sessions, identity, memory and the tricky parts of making one agent feel coherent across WhatsApp, Telegram, Discord and the rest.