Built-in tools
Each name enables one or more tools and brings its own prompt guidance, so enabling it is the only switch you need.- Bash
- Editor
- WebSearch
- Browser
- DataBase
- Canvas and Apps
- Suggest and Ask
Runs inside a Network access is on by default so
bubblewrap sandbox with the user’s workspace bound at
/workspace and a sanitized environment. The working directory is the
workspace, scratch belongs in .tmp/, and /tmp is per-command and discarded.curl, pip and git work. A prompt
injection can exfiltrate anything the sandbox can read, so turn it off when the
agent does not need it:A user can switch a tool off in Settings.
LLM.run() drops those tools for the
turn. A switch can never enable a tool the operator did not allow.Document parser
read can send documents to a parser you deploy as a function,
and give the model the Markdown it returns. The parser receives the file’s bytes and
name, and returns three keys.
doc_parser.py
Deploy it, then name it in the agent container’s environment:
.providers.env
cycls.remote, and a deployment answers only the
key that deployed it, so the agent’s container needs that key. read sends PDF,
Word, PowerPoint, Excel, OpenDocument, RTF and EPUB files up to 50MB, and waits up
to 120 seconds. A read with pages skips the parser. A call that fails falls back
to reading without it and writes a level=parser log record with the error.
The default, cycls-parser, is the parser Cycls runs for its own agents and
answers only Cycls’s key. Set CYCLS_PARSER to your deployment, or to an empty
string to skip the call.
Custom tools
A tool schema is a plain dict. A handler is an async function. The return value goes to the UI stream and to the model as thetool_result, so one return covers
both destinations.
Handler signature
A handler takes the tool input, and optionally a second argument with request context.ctx.secret(name) reads what was stored for the connector
name, the user’s own grant before the workspace’s. An API key comes back as
{"key": ...}, and an OAuth grant as stored, with access_token and expires_at.
ctx.secret does not refresh an expired token.
A handler that raises sends Error: and the exception to the model as the result,
and the turn goes on.
Presentation
.on() controls how the call appears in the UI.
Returning UI components
A handler can return a component dict instead of text. It renders in the conversation, and the model reads it as JSON._model
becomes the tool result, and _ui is one event or a list of events for the chat.
sources and the files it opens are kept with the chat, so they come
back after a reload. A link in the answer to a source’s url becomes its chip,
and a source can say more than its title:
A file a tool opens with an
open_canvas event in _ui also stands in the
conversation as its card, and a picture as itself.
A handler can also return a client action
with an ack. The client gets the action, and the model reads ack in its place.
A confirm or connect action ends the turn, because the user has to answer
first.
Reserved names
skill and find_tools are reserved by the loop. skill loads a
skill on demand and publishes one with
action: "publish". It is offered every turn, even before any skill exists, unless
you set .skills(None). find_tools loads a connector’s tools when the model
needs them. A built-in tool’s name, such as bash, read or database, is taken
too: a custom tool with that name runs the built-in.
Next steps
Custom tool guide
A complete tool with a handler, a label and a UI return.
Connectors
OAuth grants, API keys, MCP servers and per-tool approvals.