Writing tools for agents¶
Tools are how a Replio agent acts. The model plans, the ToolRegistry dispatches, and the agent loop feeds every tool result back into the conversation until the model answers. A tool is a contract between a deterministic system (your tool implementation) and a non-deterministic agent (the model). The model may call the right tool with the wrong parameters, call the wrong tool, call too few tools, or misread a result. This guide is about writing tools that hold up under that uncertainty, across any OpenAI-compatible provider, weak backends included.
The registry mechanics (registration metadata, policy, aliases, clean_args) are the reference in tools.md. This page is the craft: which tools to build, how to name them, what to return, and how to describe them. The principles are adapted from Anthropic's "Writing effective tools for agents" and shaped to Replio's single-loop, zero-dependency design.
Choosing the right tools¶
More tools does not mean a better agent. Every tool definition is loaded into the model's context on every turn, and overlapping tools actively mislead selection. Build a few thoughtful tools that target high-impact workflows, and scale up only when a real gap shows up.
- Prefer search and point tools over dump-everything tools. An agent wastes its limited context reading irrelevant results.
grepreturns only matchingfile:line: textlines.globlocates a path before it is read.file_readreportsN lines, M charsin its header so the agent can probe a file's size withlimit=0before committing to a read. - Consolidate operations that are usually chained.
file_writefolds create, overwrite, and append into one call behind amodeenum.delegateruns a whole task under an agent type as a single call. A consolidated tool offloads multi-step reasoning from the model's context into the tool itself. - Do not wrap a function or API endpoint just because you can. A tool needs a clear agent affordance: the model must be able to decide from its description alone when to call it and what to do with the result.
- Avoid near-duplicate tools. Two tools that fetched a web page (
openandfetch_page) confused agents until they were merged into oneweb_fetchthat takesurlor a search-resultid. If two tools overlap, merge them or delete one.
Naming and namespacing¶
Tool names are how the model selects behavior, so they should be distinct, purposeful, and grouped by domain.
- Give every tool a clear, distinct purpose and a name that states it.
web_search,web_fetch,file_read,list_dir,file_write,glob,grep,run_commandall read as one distinct action each. - Namespace by domain when a surface grows. The bundled plugins already do this by module (
replio-core-webexposesweb_search,web_fetch), and MCP tools use themcp_prefix. Prefixing (web_,file_,mcp_) helps the model pick the right tool when many are loaded. - Advertise canonical names only, and absorb model dialect with
aliases. The provider schema showsrun_command, notbashorexec, andweb_fetch, notopenorfetch_page. Aliases resolve at call time, so a model that still saysopen,search, orfindgets the real tool anyway. Advertising both spellings doubles the tool list in every request for no benefit. - Name parameters unambiguously. Prefer
includeoverglob,patternoverqueryinsidegrep, and concrete nouns over pronouns. A parameter namedpathwith a description is clearer than a barefile.
Returning meaningful context¶
Tool results are fed back verbatim into the model's context. Return high-signal information that directly informs the next step, and prefer interpretable language over technical identifiers.
- Resolve and report natural identifiers.
file_readandfile_writeresolve absolute paths.grepreturnsfile:line: textso the model can act on the location. No raw UUIDs or opaque handles appear in Replio's tools. - Report sizes and boundaries so the agent can decide.
file_readputs the total line and char count in its header and marks partial windows with(showing a-b).web_fetchreports[offset N of M chars]when content continues. - Keep results deterministic and complete for the task. The same input should produce the same output, and a result should not depend on unrelated state.
Optimizing tool responses for token efficiency¶
Context is the model's scarcest resource. Give every potentially large response pagination, filtering, or a sensible cap, and make the truncation itself steer the agent.
- Page large reads.
file_read(path, offset=1, limit=200)thenoffset=201walks a file window by window.web_fetchaccepts theoffsetits marker reports. Both let the agent pull only what it needs. - Cap with sensible defaults.
globstops at 200 matches andgrepat 100, each with a... (showing first N matches)marker.list_dircaps entries vialist_dir_max_entries(default 200) with a... (showing first N of M entries)marker. Thetool_max_result_charsconfig key is the hard cap for every tool result (default 100000,0= unlimited), so tools should still self-cap below it. - Offer narrowing parameters.
grep'sglobfilter limits which files are searched.list_dir'sdepthbounds a tree. Filters and ranges let the agent ask for less. - Make markers actionable. A marker that says how to continue (
send "continue",use cursor=N,showing 5-7) converts a truncated response into the next tool call instead of a dead end. - Steer toward small targeted calls. A description that says "use many small searches" or "use offset to page" changes behavior more reliably than a bigger cap.
Prompt-engineering descriptions and schemas¶
Descriptions are loaded into the model's context on every turn. Write each one the way you would describe the tool to a new hire, and make the schema the single source of truth.
- State the purpose, the output format, and the alternatives in the description.
file_readexplains the header, thelimit=0size probe, and paging.globshows example patterns ("**/*.py","src/**/chat.py").grepstates itsfile:line:result format.run_commanddocuments stdout, stderr, and exit code. - Say when NOT to use a tool, and point at the right one. Error text already does this at runtime (see below). A description that preempts the confusion ("use list_dir for directories") saves calls.
- Enforce the contract with types, enums, and
required. The registry passes only declared, non-null arguments to the handler, so a strict schema is the model's only input surface. Use an enum for closed sets (file_write'smode), and always describe each parameter. - Keep descriptions and implementations self-consistent. When a tool's behavior changes, update its description in the same change.
Registration metadata that shapes behavior¶
Registration metadata drives more than the schema. It shapes confirmation, display, refinement, and permissions without special-casing tool names in the loop.
| Metadata | What it does for the agent |
|---|---|
refine |
A short query is rewritten by a lightweight model call before web_search runs, so vague queries still work |
permission / permission_fn |
Gates the call with allow (no prompt), ask (y/N confirm), or deny. permission_fn resolves per invocation from the arguments (see delegate) |
path_arg |
Marks the filesystem path parameter so worktree escalation applies |
key_arg |
The argument shown in confirm prompts and activity lines, so the human sees what will happen |
aliases / param_aliases |
Absorb model-dialect tool and argument names at call time without advertising them |
note |
A predicate marking soft one-line results ((no matches), (empty file)) so they render as a dimmed note, not an error |
status / echo / glyph / verb |
Ephemeral REPL activity lines (* Grep, ↓ Fetch) and result previews that never reach the session log |
Full reference in tools.md. The policy and worktree rules that gate permission are in tools.md and security.md.
Errors and soft results¶
Error responses are guidance, not telemetry. When a call fails, tell the model what it can do instead.
- Start with
Error:and a one-line actionable message. Replio's filesystem tools set the pattern:Error: X is a directory (use list_dir instead),Error: X is not a directory (use file_read instead),Error: file not found: X. Each suggests the correct tool. - Do not return tracebacks or opaque codes. The first line of an
Error:result is echoed dimmed in the REPL, so the model and the human see the same guidance. - Use a
notepredicate for informational one-liners that are not failures.(no matches for "x"),(empty file),(end of content), andNo search results found.are normal outcomes, and a dimmed note beats a red error line.
Evaluating your tools¶
Measure how well a model uses a tool before you trust it. Replio ships an agent-level tool-evaluation harness - replio eval - that runs task fixtures through the headless agent loop and reports tool-call accuracy, redundant calls, errors, and tokens, against a mock or a real provider (see eval.md). The replio-core-eval bundled plugin contributes a small fixture catalog. Author your own fixtures under .replio/eval/*.json.
- Build a mock-provider loop test. The test suite runs the full agent loop against a stubbed
provider.chatwith no network and no API key.tests/test_tool_calling.py,tests/test_agent_loop.py, and themake_chat/make_enginehelpers intests/are the pattern. Drive the loop with a tool-call event and assert the model-visible result and the session messages. - Run
replio evalagainst a real provider before and after a description change. A fixture whosepassflips (or whose accuracy moves) quantifies the effect of the change. - Exercise a tool directly with
/tool <name> {"args": ...}from the REPL. It routes through the same policy,clean_args, and display as a loop call, so it is a fast sanity check for argument handling and result format. - Read the session logs for misbehavior. A session that shows wrong-tool selection, repeated parameter errors, or the same search re-run means the description, naming, or schema is unclear. Models trained on tool-use data often reach for
opento fetch a URL. Theopenalias absorbs that habit while the schema advertisesweb_fetch. - Compare providers. Replio standardizes on OpenAI-compatible tool calling, but models differ sharply in how they parse schemas. Run the same task against Ollama and a hosted OpenAI-compatible endpoint and watch which tools each reaches for. Weak backends are exactly where naming, caps, and description quality pay off.
Metrics worth watching: redundant tool calls (pagination or caps too tight), invalid-parameter errors (descriptions unclear or the schema too loose), and context bloat (a tool returning more than the task needs).
Checklist¶
- The tool has one distinct purpose and a name that states it.
- The description says what it does, the output format, and when not to use it, with an example.
- The schema uses explicit parameter names, types, enums, and
required. - The result is high-signal, deterministic, and interpretable, with no raw identifiers.
- Large results paginate or cap with an actionable marker.
- Errors start with
Error:and suggest the correct alternative. - Soft outcomes use a
notepredicate. - Dialect names are
aliases, not advertised schemas. - The tool was exercised via
/tooland a mock-provider loop test.