Tools and sub-agents
The built-in tools an agent run can use, the standalone /v1/tools endpoints, and how spawn_agent fans work out to typed sub-agents.
Inside an agent run — or a chat completion with x-codai-server-tools: 1 — the model gets a set of gateway-executed tools. It calls them like any function tool; the gateway runs every call of a turn in parallel and feeds the results back. You never see the tool traffic unless you read the run's steps.
Built-in tools
| Tool | What it does | Available when |
|---|---|---|
http_fetch | Fetch a URL server-side. | Your key's tool level allows HTTP (l1_http or higher). |
json_query | Query a JSON document. | Same as http_fetch. |
exec_python | Run Python in a sandbox — the "code interpreter" capability. | Sandbox enabled on the gateway. |
read_artifact | Read back a large tool result the gateway spilled to storage instead of inlining. | Spilling enabled and at least one tool active. |
memory_recall, memory_remember | Long-term memory scoped to your account. | Your plan includes persistent memory. |
use_skill | Load one of your skills into the loop. | At least one skill visible to the key. |
web_search | Native web search; { query, max_results 1–10 }. | Web search configured on the gateway. |
| MCP tools | Tools exposed by the MCP servers attached to your key. | Tool level is not none. |
delegate_to_model | codai's internal single-completion router across the models you may call. | Always, when ≥ 2 models are allowed. |
spawn_agent | A typed sub-agent with its own context and tool loop. | See below. |
Tool level is a property of your key. none disables every executed tool; l1_http allows network reads; l3_shell and l4_browser unlock the heavier sub-agent roles.
spawn_agent
When server-side tools are on, the model also gets spawn_agent. Unlike delegate_to_model (one completion), a spawned child runs its own loop with its own context. The model picks a role; the gateway picks the tier, tool level and step budget for it.
Prop
Type
| Role | Tier | Max steps | Max tool level | Purpose |
|---|---|---|---|---|
explore | small | 6 | l1_http | Read-only facts. The default. |
research | small | 8 | l1_http | web_search + http_fetch, returns a sourced summary. |
code | large | 12 | caller's level (up to l4_browser) | Implement and verify; returns diff or output. |
verify | small | 6 | l3_shell | Run checks, report PASS/FAIL per check. |
summarize | small | 3 | l1_http | Digest one source. |
Semantics:
- Parallel by design. Every
spawn_agentcall in one assistant turn runs concurrently, and concurrently with the turn's other tool calls. The fan-out is capped per turn (default 4); calls past the cap receive{"error":"delegation_fanout_capped"}as their tool result. - Children never spawn. A child has
spawn_agentremoved, runs on the parent's alias withmax_tokens: 4096, and bills as a continuation of the parent turn — it does not start a new task. - Cost ceiling. Child spend counts toward the request's cost ceiling (default 5 USD); once reached, further spawns return
{"error":"request_cost_ceiling"}. - Result shape. The child's answer comes back to the parent as an ordinary
toolmessage prefixedSUB-AGENT[<role>] RESULT:. - Progress frames. On a streamed chat completion, the gateway emits vendor frames inside the SSE delta so a UI can render live sub-agent cards:
{"codai":{"event":"subagent_started","id","role","task"}}and{"codai":{"event":"subagent_done","id","role","ok","preview"}}. Ignore them if you only want text. - Invalid arguments →
{"error":"invalid_arguments"}; a child that fails →{"error":"sub_agent_failed"}.
spawn_agent is injected only when the request carries no client-side tools of its own — if you send tools, you own the loop and the gateway stays out.
Standalone search and fetch — /v1/tools
The same search and fetch engines are exposed as plain endpoints, so a client-side agent can use them without going through a model. Both need only your codai_ key, count against your per-minute / per-hour request limits (X-RateLimit-* headers, 429 rate_limit_exceeded), and do not create a usage row — they are not model calls.
POST /v1/tools/search
Prop
Type
curl https://ai.codai.ro/v1/tools/search \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": "curs BNR euro azi", "max_results": 3, "freshness": "day" }'Response — every result is verified: the snippet was found on the live page before it was returned.
{
"results": [
{ "title": "Curs valutar BNR – 23 septembrie 2026", "url": "https://www.bnr.ro/…", "snippet": "1 EUR = 5,0912 RON …", "source": "bnr", "verified": true, "page_age": "2026-09-23" }
],
"provider": "codai",
"took_ms": 412,
"cached": false,
"coverage": 1,
"sources_tried": ["bnr", "index"],
"degraded": [],
"intent": "currency"
}If the search service is unreachable the gateway answers 503 search_unavailable (type: server_error) — retry later; nothing was charged.
POST /v1/tools/fetch
Prop
Type
curl https://ai.codai.ro/v1/tools/fetch \
-H "Authorization: Bearer $CODAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://nodejs.org/en/blog/release/v24.0.0", "max_chars": 4000, "focus": "ESM loader hooks" }'{
"url": "https://nodejs.org/en/blog/release/v24.0.0",
"final_url": "https://nodejs.org/en/blog/release/v24.0.0",
"title": "Node.js 24.0.0 (Current)",
"byline": "The Node.js Project",
"description": "…",
"canonical": "https://nodejs.org/en/blog/release/v24.0.0",
"lang": "en",
"content_type": "text/html",
"content": "## Module customization hooks\n\n…",
"chars": 3811,
"truncated": true,
"took_ms": 688,
"cached": false
}Limits: 10 s timeout, 3 MB body cap, at most 3 redirects, and an SSRF guard on every hop — private, loopback and credential-bearing URLs, or non-HTTP schemes, are 400 bad_request. Accepted content types are HTML, plain text, JSON, XHTML, Markdown and XML; a non-2xx upstream is 502 upstream_error with details: { status, url }. Fetches are cached for 15 minutes, searches for 10.
Both endpoints fetch from the public internet on your behalf. Do not pass them user-supplied URLs you have not validated yourself — the SSRF guard protects codai's network, not your intent.