Tool inventory¶
This inventory explains which tools are available, when they appear, and how their switches work. An administrator configures the required service and grants access; people can then choose tools for a conversation. See the tool contracts reference for arguments, results and limits, or tools and integrations for the browser workflow.
How to read the columns¶
- Gate — the configuration or service that must be available before the tool appears.
- Chat-only — the tool needs an AIplane conversation and is unavailable to direct model API requests.
- Toggle key — the switch an administrator can grant and a person can enable for their conversation. Related tools may share one switch.
The /tools UI shows admins read-only rows for optional image, GeoIP, and
sandbox tools when their service is absent. Their switches are disabled and
link to the relevant setup page. Tools that need storage, knowledge indexing,
or notification setup also show when that prerequisite is missing.
Always registered¶
No configuration required. A tool here can still fail at runtime when its storage is unconfigured (e.g. S3 attachment storage), but it fails with a clear message rather than being absent.
| Tool | Chat-only | Toggle key | What it does |
|---|---|---|---|
enable_tools |
— | (hidden, always on) | The lazy-disclosure bootstrap: turns other tool groups on for the rest of the conversation. Not presented as a toggle — allowed_tools_for_session force-keeps BOOTSTRAP_TOOL_ID, so a switch for it would be inert. |
search_gateway_tools |
— | (hidden, /v1 only) |
Searches the Auto capabilities allowed by an API token and exposes at most five matching tool schemas in the next model round. |
company_echo |
— | (hidden) | Diagnostic echo tool for checking a custom installation's tool connection. It is hidden from normal tool selection. |
get_current_timestamp |
— | get_current_timestamp |
Timezone-aware current date/time, from the caller's users.timezone. |
convert_currency |
— | convert_currency |
Currency conversion at daily ECB reference rates. |
ask_user |
yes | ask_user |
Ask the user a short question mid-turn and wait for the answer, rather than guessing. Needs a live chat turn and someone watching it; times out and reports answered: false otherwise. |
forward_request |
yes | forward_request |
An active personal Dot sends an explicit task to another active Dot it may address: a personal Dot belonging to its owner (both directed consents), or an organization Dot the caller's member organization consented to receive from (cross-boundary consent plus the caller's current membership). The receiver uses its own tools, memory, credentials and approval rules. Ordinary chats and stateless /v1 requests do not offer this personal variant. See Dot communication and cross-organization delegation. |
notify_user |
— | notify_user |
Send the user a Web Push notification (long work finished, a scheduled action found something). Deliberately not chat-only — a notification lands on a device, not in a conversation, so it also works from the headless scheduler. Hard limit of one per turn, latched in PushNotifier; errors clearly when Web Push is unconfigured or the user has no subscribed device. |
get_user_location |
— | get_user_location |
Caller's location: a browser GPS prompt when a live chat turn is watching, else coarse GeoIP. |
generate_qr_code |
yes | generate_qr_code |
QR codes (URL, WiFi, vCard, SEPA) as PNG/SVG, rendered in-process. |
search_web |
— | search_web |
Web search via SearXNG, Brave, or Tavily, with optional domain and recency filters. A confirmed exhausted provider quota triggers another configured provider; the result identifies the one used. When every configured provider is exhausted, the error lists each one's quota failure and marks the configured provider. Tavily has a separate Enable switch and is eligible only when enabled with a stored key. Configured on /admin/settings?tab=web-search. |
browser_control |
yes | browser_control |
Act in the user's own logged-in browser (navigate, read, click, type, screenshot) through the extension paired with the open chat page. Actions travel as a batch and run in order. The extension, not AIplane, decides what may run; nothing happens until the user switches it on. Users set it up at /tools/browser. See tools and integrations. |
show_screenshot |
yes | show_screenshot |
Show the user a capture of their browser page — viewport, whole page, one element (ref) or a rectangle (region) — as an inline attachment in the reply. Browser-control screenshots are for the assistant; this tool shows a capture to the user. It uses the browser extension and needs attachment storage. See tools and integrations. |
fetch_url |
— | fetch_url |
HTTP GET through outbound_guard (public hosts only unless $AIPLANE_ALLOW_PRIVATE_NETWORKS; every redirect re-checked; 32 MiB read cap, 4 MiB returned-text cap). HTML is reduced to readable text unless raw is set; images come back viewable; other binary returns metadata. |
wikipedia |
— | wikipedia |
Summary of the best-matching Wikipedia article. |
dns_lookup |
— | dns_lookup |
DNS records over DoH. |
whois_lookup |
— | whois_lookup |
Domain registration via RDAP. |
tls_cert |
— | tls_cert |
TLS certificate inspection (issuer, validity, days to expiry, SANs). The host is checked through outbound_guard like a fetch_url URL. |
fetch_attachment |
— | fetch_attachment |
Read any file of the conversation — an attachment or (by id/title) a canvas document, which comes back as text plus its version. Tiered PDF reading — text layer, rasterised pages, mode="ocr" (gateway OCR, images too), mode="auto" (text-or-OCR) — with page_from/page_to; Office files return structured content. |
upload_attachment |
yes | upload_attachment |
Attach a model-generated file to the reply. |
offer_download |
yes | upload_attachment |
Hand a file the conversation already holds to the user as a download chip on the current reply — an attachment, or a canvas document (its current version is written out as a file, named from the document's title) — including objects with no chip of their own (a typst render's hidden .json data base, an intermediate artifact) and files from earlier turns. Takes a reference, never content: the object is copied inside S3, so a large payload never round-trips through the model as prose. Session-scoped twice over — a marker-backed id is proven in-session by the enumeration, an unlisted <turn_id>/<filename> by turn_in_session. Shares the upload_attachment toggle: one switch for "let the assistant hand me files". |
zip_attachments |
yes | upload_attachment |
Bundle several files the conversation already holds into one .zip and attach it as a single download chip. Takes the same reference spellings as offer_download (<turn_id>/<filename>, canvas document_id, bare filename) in ids, plus an optional archive filename (.zip appended when missing). Unlike offer_download the bytes cannot stay inside S3 — the archive is assembled in memory — so it is capped at 64 entries and 256 MiB of uncompressed members, and members are read sequentially so the first bad id is the one reported. Colliding entry names are suffixed (report.md, report-2.md) rather than overwriting each other; a canvas document contributes a snapshot of its current version. Shares the upload_attachment toggle. |
list_attachments |
yes | list_attachments |
Inventory of the conversation's files, so assets get reused rather than regenerated. |
load_image_url |
yes | load_image_url |
Fetch an image from a URL (through outbound_guard, like fetch_url) and keep it as a reusable conversation attachment. |
import_file |
yes | document |
Turn a text attachment (upload or produced artifact) into an editable, versioned canvas document — server-side, so the content never round-trips through the model. The on-ramp that makes an uploaded .typ/.csv/.json/.md editable a passage at a time (and hand-editable by the user); offer_download is the exit ramp. Text formats only: binary attachments stay attachments, already usable by id (att: refs, sandbox staging, fetch_attachment). Capped at the same 512 KB the document tools can write. |
create_document |
yes | document |
Open a canvas document. |
edit_document |
yes | document |
Apply passage edits to text-format canvas documents, or JSON Patch to JSON/TOML documents; every edit creates a version. |
edit_document_section |
yes | document |
Edit one section of a canvas document. |
read_document |
yes | document |
Read a canvas document back. |
list_documents |
yes | document |
List the conversation's canvas documents. |
list_document_versions |
yes | document |
Version history of a canvas document. |
restore_document_version |
yes | document |
Roll a canvas document back to an earlier version. |
delete_document |
yes | document |
Soft-delete a canvas document: hidden from the canvas and from list_documents, version history kept, reversible. |
undelete_document |
yes | document |
Undo a delete_document. Deliberately not named restore_document — that would sit one suffix away from restore_document_version, which does something else entirely. |
schedule_action |
yes | schedule |
Create a recurring prompt (5-field cron + IANA timezone, defaulting to users.timezone). Validates with Cron::parse and returns describe() + the next 3 run times, so a wrong-but-valid expression is caught before the first run. Inherits the turn's model from ToolContext.model; always creates the action with tools off. |
list_scheduled_actions |
— | schedule |
The caller's own scheduled actions, each with the same human schedule preview. Read-only, so it works off-chat. |
delete_scheduled_action |
yes | schedule |
Delete one of the caller's actions. Another user's id is reported as missing, not forbidden (no existence leak). |
rag_search |
— | rag_search |
Hybrid search (dense kNN fused with FTS5/BM25) over an indexed collection. Returns chunks with path, line range, score. Optional path_glob scopes it to part of the corpus — filtered inside the query on the lexical side, and after kNN candidate generation on the dense side (which is why the candidate pool widens when it is set). |
rag_grep |
— | rag_grep |
Regex scan over an indexed collection's chunk text: matching lines with file, line number and context. For patterns BM25 cannot express (TODO\(.*\), impl .* for Tool). Full scan with no index behind it, so it is bounded by result / row / wall-clock limits and reports which one it hit. |
rag_list_collections |
— | rag_list_collections |
Discover which collections exist before searching them. |
rag_query_documents |
— | rag_query_documents |
Filter, sort and total the documents of a collection by the fields its extraction profile pulled out (vendor, date, amount, project). The answer to any question about a set of documents — the latest, the largest, how many, how much — which top-k passage retrieval structurally cannot give: it returns five similar chunks and no way to know whether that was all of them. Always reports total_matches alongside the returned page, and surfaces the distinct values a text filter matched so ACME hitting both ACME GmbH and ACME Deutschland AG is visible rather than silently resolved. |
rag_list_documents |
— | rag_query_documents |
Folder-scoped document listing with the stored per-document summaries written at index time. Turns "find everything about project X and summarise it" into one call over ~200 tokens per document rather than re-reading every file. |
rag_fetch_document |
— | rag_query_documents |
Full extracted text of one indexed document by document_id. The drill-down after a hit; truncates long documents with a note rather than flooding the context. |
remember |
— | memory |
Persist a durable fact for the user or acting Dot. Personal and organization Dot writes remain in that Dot's own scope; new content is bounded to 2,000 UTF-8 bytes. |
recall |
— | memory |
Read at most 200 recent notes with explicit scopes. A personal Dot reads own and user notes; an organization Dot reads own and shared organization notes, without employee profiles. |
update_memory |
— | memory |
Correct an authorized fact by its returned ID and scope, within 2,000 UTF-8 bytes. Dots can change only their own notes. |
forget |
— | memory |
Delete an authorized fact by ID and scope. Dots cannot delete user or shared organization notes. |
The canvas tools, the four memory tools and the three scheduling tools
collapse to the document, memory and schedule keys respectively — see
DOCUMENT_IDS / MEMORY_IDS / SCHEDULE_IDS in catalog. Turning memory
off has to remove the mutating tools too, not just the read/write pair; the
same reasoning groups list_scheduled_actions with the two tools that change
a schedule, since "can see my schedule but not change it" is not a
distinction anyone configures.
schedule_action and delete_scheduled_action are chat-only for a reason
that isn't about attaching output: both wait for the person's approval,
because the action they write later runs as the user, unattended, until
removed. That makes a scheduled action a persistent prompt-injection vector,
so a human has to approve it. The approval is the durable pause every
approval uses (ask_first::approval): the call shows a preview of the
schedule, the person answers in the chat or the inbox, and it survives a
restart. A scheduled run that calls them pauses too, and nothing is written
unless its owner approves from the inbox.
Conditionally registered¶
Gated so the model is never offered a tool whose every call could only answer "not configured".
| Tool | Gate | Chat-only | Toggle key |
|---|---|---|---|
lookup_ip |
GeoIP configured (geoip.enabled) |
— | lookup_ip |
generate_image |
an image-kind upstream pool exists |
yes | generate_image |
edit_image |
an image backend advertises supports_edit |
yes | edit_image |
read_skill |
skills configured (skills.enabled) |
— | (always on when the caller has a permitted skill) |
run_in_sandbox |
sandbox.enabled |
— | run_in_sandbox |
generate_document |
sandbox.enabled |
— | generate_document |
export_document |
sandbox.enabled |
yes | document |
convert_document |
sandbox.enabled |
— | convert_document |
edit_presentation |
sandbox.enabled |
— | edit_presentation |
capture_webpage |
sandbox.enabled and the runner reports egress |
— | capture_webpage |
browse_page |
sandbox.enabled and the runner reports egress |
— | browse_page |
render_typst |
sandbox.enabled |
— | render_typst |
render_excalidraw |
sandbox.enabled |
— | render_excalidraw |
render_video |
sandbox.enabled |
— | render_video |
read_sandbox_output |
sandbox.enabled |
yes | read_sandbox_output |
export_document is the one sandbox tool that rides the canvas document
toggle: it exports a canvas document, so it belongs to that capability from
the user's point of view even though it needs the sandbox to run.
Egress is a runner capability, not gateway config¶
Whether a sandbox can reach the network is decided by the runner's
deployment (SANDBOX_EGRESS_NETWORK + SANDBOX_EGRESS_PROXY), which nothing
in AIplane's own config can see. So AIplane asks: at boot it reads
GET /healthz on the runner, which reports egress: true|false
(shared::sandbox::RunnerHealth), and remembers the answer for the process
lifetime.
That answer changes what the model is offered:
- No egress →
capture_webpageandbrowse_pageare not registered at all, andrun_in_sandbox's schema has nonetworkproperty, with a description that states plainly there is no network rather than implying a permission the model could ask for. This is the "absent beats always-failing" rule below. - Egress → both tools register and
networkappears as an option. - Unknown (runner unreachable at boot or health response without the egress field) → treated as available. Withdrawing capabilities needs positive evidence; an unreachable runner breaks every sandbox tool anyway, so hiding a subset would turn a transient outage into an apparent permanent capability loss.
Not re-probed: egress changes when an operator edits a unit file and restarts things, and a capability set that shifted under a running conversation would be worse than a slightly stale one.
render_typst is deliberately not chat-only, even though part of it needs a
session. It renders either inline source or a document_id from the canvas.
The inline path is the common one and works fine on /v1; only the
document_id path needs a chat session, and it fails there with a message
naming the reason ("canvas documents are only available inside a chat session")
rather than a generic error. Marking the whole tool chat-only would remove a
working capability from the proxy paths to protect an argument the model
wouldn't have a use for there — a /v1 caller has no canvas to reference. Same
reasoning applies to run_in_sandbox's optional canvas-document staging, which
degrades to a note rather than failing the run. export_document is different
and is chat-only: a canvas document is its only possible input.
The two shapes a conversation's files come in¶
A conversation holds files in two stores, on purpose, and the tools cross between them rather than duplicating either:
| Attachments | Canvas documents | |
|---|---|---|
| Address | <turn_id>/<filename> |
doc_… |
| Mutable | no — one immutable blob per write | yes — every change appends a version |
| Content | any bytes (images, PDFs, archives) | UTF-8 text, ≤ 512 KB |
| User can edit | no | yes (the document panel) |
| Reached by | fetch_attachment, att: refs, sandbox attachments, offer_download |
fetch_attachment, read_document/edit_document, sandbox documents or attachments, typst_* document_id/base, export_document, offer_download |
One reference syntax, every tool. A model should not have to know which
tool takes which way of naming a file. Every file-taking tool resolves through
file_refs::resolve, which accepts all of them — an <turn_id>/<filename> id, a bare filename (newest match wins), a
doc_… id, an unambiguous document title or its materialised filename, and a
leading att: / doc: / file: that some models add unprompted. So a
reference the model got from any result works in any argument, and a wrong
one produces the same message (naming both inventories) wherever it is passed.
Session scoping is part of resolution rather than a check each caller
remembers: a marker-backed attachment is proven in-session by the enumeration,
an unlisted <turn>/<file> by turn_in_session, a document by the
session-scoped get_version. Another conversation's real id resolves to the
same "not found" as a typo, so nothing leaks. A deleted document is its own
error, because the fix is undelete_document rather than a different id.
What is not a reference: a path inside the sandbox's working directory.
docs/backend.md is the same shape as an attachment id, so a model that just
wrote it in /work passes exactly that — and the generic "no file named that in
this conversation" reads as your file is gone rather than you named the wrong
store. A non-UUID first segment is therefore detected and answered with the
real explanation (file_refs::looks_like_sandbox_path): /work is unreachable
from outside a run_in_sandbox call, and a produced file becomes referenceable
only once that call returned it in artifacts, under the flattened name shown
there.
Crossing over: import_file turns a text attachment into a document;
offer_download writes a document's current version back out as a
downloadable file. Both copy inside AIplane — content never round-trips
through the model, which is what made "give me that file" cost two passes of
the whole payload and invite retyping drift.
The user can hand-edit a document in the panel (PUT
/api/v0/chat/sessions/{id}/documents/{doc_id}, owner-only, newest version only). That save
is a normal new version, marked as authored by the user — the panel's version
switcher labels those revisions (v3 · you) so a history stops reading as
interchangeable numbers, and read_document / list_document_versions /
list_documents report the author too — and the request
context then tells the model which documents were hand-edited and at what
version, because nothing in the transcript would: its history still holds the
content it wrote, so an unwarned edit reverts the correction. The warning
stops once the model writes on top (it has seen the change by then).
A typst render parks its field data in a canvas document (its
document_id comes back in the result) rather than a hidden
<turn>/<basename>.json attachment. That data is the file the model works on
constantly, so it must be visible: in the panel, downloadable, stageable, and
editable by the user. The _read / _edit / _pptx
tools take either id — a slash means an attachment — so a conversation whose
base is a <turn>/<basename>.json attachment keeps editing it, and
_edit writes back to whichever surface it read from. The render deliberately
does not push the panel open for a data document: the deliverable is the PDF.
All three refuse a soft-deleted document explicitly. documents::get
resolves deleted rows on purpose (see delete_document), so without that check
a stale id would quietly produce an export, a PDF, or a staged file from work
the user threw away.
Dynamic families¶
These have no fixed id — one tool per discovered template / workflow / connected server. The drift guard matches them by prefix. Auditing, approval and live personal-Dot or organization-principal authorization wrappers retain the wrapped tool's actual ID and schema; they do not add another tool or toggle.
| Tool IDs | When they are available | Toggle key |
|---|---|---|
typst_<id> plus _edit / _read / _pptx |
Each installed Typst template provides its own document and conversion actions. | typst_<id> |
comfyui_<id> |
Each enabled ComfyUI workflow provides a matching action. | comfyui |
mcp__<server>__<tool> |
Actions exposed by a connected personal or shared MCP integration. | mcp__<server> |
| Agent builder actions | Available to the assistant while creating or editing an agent, according to the builder user's permissions. | Managed by the agent builder |
set_<slot> |
Available during an agent run for slots the agent is allowed to fill. | Managed by the agent |
request_human |
Available when an agent can hand work to a person. | Managed by the agent |
finish |
Available when an agent has a result contract to satisfy. | Managed by the agent |
verify_<id>_request_code, verify_<id>_submit_code |
Available when an agent requires one-time-code verification. | Managed by the agent |
verify_<id> |
Available when an agent requires lookup-based verification. | Managed by the agent |
Published agents with configured sub-agent routes use a run-scoped
forward_request variant with the same ID. Its argument-free schema renders
the request from the agent's state and dispatches the configured route. That
variant is supplied by the existing agent run tool source; it does not use the
personal receiver/message schema.
Some document conversions also need the sandbox service and an enabled output format. Connector-returned files become conversation attachments. For how to configure these services, see integrations.
Availability and access¶
Some tools need a configured service, such as image generation, geolocation, knowledge indexing or the sandbox. Those tools appear when the service is available. Other tools remain visible when setup is incomplete and show which prerequisite is missing.
An administrator must grant access to a tool before it is available to a person. The conversation switch controls whether the assistant can use an available tool during that conversation. Tools that act through a connected service can also require approval before they make a change. See agent permissions and approvals.
Documentation build: 2610.1.108 · commit b774e1f9e2c2