Files
pocketpaw/docs/tools/custom-tools.mdx
Prakash Dalai 427d702623 feat(tools): cap oversized tool output before it reaches agent context (#1166)
A tool that returned a large blob used to drop the raw blob straight
into the agent's context window with nothing capping it. A long pytest
run, a build log, a big HTTP response body, verbose command stdout --
the whole thing went in. That wasted tokens and buried the lines the
agent needed.

Add output_budget.cap_tool_output. Output within the cap is returned
unchanged. An oversized blob gets a deterministic head+tail slice with
an elision marker. A recognized structured format (pytest run, ruff or
flake8 lint output) gets a salient-lines extract instead, keeping the
failures and the summary line and dropping the PASSED noise.

Wire it at two boundaries: BaseTool._success/_error, and
ToolRegistry.execute plus the tool_bridge wrappers. Two boundaries
because shell and run_python return strings directly and never touch
_success -- the registry is the universal chokepoint that still catches
them. The transform is deterministic and idempotent, so a result
already capped by _success passes through the registry unchanged.

The cap defaults to 12000 chars and is configurable through the new
tool_output_char_cap setting.

Closes #1160
2026-05-21 16:49:50 +05:30

113 lines
3.6 KiB
Plaintext

---
title: "Custom Tools: Build Your Own PocketPaw Extensions"
description: "Build custom tools for PocketPaw using the ToolProtocol interface. Define tool schemas compatible with both Anthropic and OpenAI APIs, register them in the ToolRegistry, and use them immediately."
section: Tools
ogType: article
keywords:
["custom tools", "tool protocol", "tool development", "plugin", "extensible"]
tags: ["tools", "development"]
---
# Custom Tools: Build Your Own PocketPaw Extensions
PocketPaw's tool system is extensible. You can create custom tools by implementing the `ToolProtocol`.
## ToolProtocol Interface
```python
from pocketpaw.tools.registry import ToolProtocol, ToolDefinition
class MyCustomTool:
@property
def name(self) -> str:
return "my_tool"
@property
def description(self) -> str:
return "Does something useful"
@property
def definition(self) -> ToolDefinition:
return ToolDefinition(
name=self.name,
description=self.description,
input_schema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The input query"
}
},
"required": ["query"]
}
)
async def execute(self, **kwargs) -> str:
query = kwargs.get("query", "")
# Your tool logic here
return f"Result for: {query}"
```
## Registering Custom Tools
Register your tool with the `ToolRegistry`:
```python
from pocketpaw.tools.registry import ToolRegistry
registry = ToolRegistry()
registry.register(MyCustomTool())
```
## Schema Export
`ToolDefinition` supports exporting to both Anthropic and OpenAI formats:
```python
tool = MyCustomTool()
# Anthropic format
anthropic_schema = tool.definition.to_anthropic()
# OpenAI format
openai_schema = tool.definition.to_openai()
```
## Best Practices
1. **Keep tools focused** - Each tool should do one thing well
2. **Validate inputs** - Check required parameters before execution
3. **Return strings** - Tool results are always strings
4. **Handle errors** - Return error messages instead of raising exceptions
5. **Be async** - The `execute` method must be async
6. **Document inputs** - Provide clear descriptions in the schema so the LLM knows how to use the tool
## Output Size
You do not need to truncate large output yourself. Every tool result passes
through an [output budget](/concepts/tool-system#output-budget) before it
reaches the agent: oversized blobs are shortened to a head + tail slice, or a
salient-lines extract for recognised test and lint formats. This runs at
`BaseTool._success` / `_error` and again at `ToolRegistry.execute`, so it
applies whether your tool returns through `_success` or returns a string
directly. The cap defaults to 12,000 characters (`tool_output_char_cap`).
Return the full, accurate result from `execute` and let the budget handle
size. Avoid ad hoc per-tool slicing — it loses the salient-lines treatment
and is harder to tune than one central cap.
## Related
<CardGroup>
<Card title="Tool Policy" icon="lucide:shield-check" href="/tools/tool-policy">
Control which tools are available with profiles and allow/deny lists.
</Card>
<Card title="Skill Generator" icon="lucide:wand-2" href="/tools/skill-generator">
Create persistent skill definitions that extend agent capabilities.
</Card>
<Card title="Tools Overview" icon="lucide:wrench" href="/tools">
Browse all 50+ built-in tools available in PocketPaw.
</Card>
</CardGroup>