Tools — computations and actions
🔵 Engines that execute something: scripts, shell commands, subagents, workflows, as well as result delivery (email, files). For a summary table of all engines, see «Tool Reference».
5.10 script_tool
Rhai or JavaScript script.
kind: script_tool
name: currency
description: Конвертация валют.
script: currency.rhai # .rhai → Rhai, .js → JavaScript (QuickJS)
parameters: { ... }
max_operations: 10000000
The script defines fn run(args) (Rhai) or function run(args) (JS). Full
reference — 07-scripts.
5.14 subagent_tool
Delegate a task to a subagent. Spawns a child agent process, hands over the task, waits for completion and returns its final response. The child's spending is charged to the parent's session (inherits the budget group), followed by cleanup.
kind: subagent_tool
name: delegate_research
description: Делегировать исследование субагенту.
template: trace-agent # или inline instructions + tools:
# instructions: "You are a research assistant."
# tools: [http_fetch, fs_read]
# budget: { max_per_turn: 2000, max_per_minute: 20000, max_total: 20000 }
| Field | Description |
|---|---|
template |
template for delegation (takes priority over instructions) |
instructions |
inline instructions without a template |
tools |
names of the subagent's tools (overrides the template) |
budget |
token limit { max_per_turn, max_per_minute, max_total } |
output_schema |
JSON Schema of the structured result (the subagent finishes with job_done; the validated payload — in data) |
params |
string pairs passed to the subagent as template_vars (visible to its tools via ToolContext), e.g. code_root for the code module |
allow_params |
keys that the LLM can set in the params call argument (empty = static only) |
Runtime arguments: task (required), optional template/instructions/
params.
ask_user
Asks the user a form (several questions at once) and returns
structured answers { "answers": { "<id>": <value> } }. The turn is blocked until
a response or timeout.
Types: text, textarea, number, select (radio), multiselect (checkbox),
confirm (yes/no), date. For select/multiselect — options: [...];
allow_other: true adds a free-form "custom option".
{ "title": "Настройки проекта",
"questions": [
{ "id": "os", "type": "select", "label": "ОС?", "options": ["Linux","macOS","Windows"], "allow_other": true },
{ "id": "features", "type": "multiselect", "label": "Фичи", "options": ["a","b"] },
{ "id": "proceed", "type": "confirm", "label": "Продолжить?" }
] }
The widget renders native controls; in messengers the questions are shown as text.
Module agent_tools.
notify_tool
Draws the user's attention via notification channels. The
level level sets the persistence: silent — a quiet line, notify — sound,
alert — heads-up, alarm/call — repeat until acknowledged (on Android —
alarm / ringtone). channels — any sinks (as in notify: for cron); empty =
the event is saved to the message store.
kind: notify_tool
name: notify
description: Привлеки внимание пользователя.
level: alert
require_ack: false
# channels: [ { type: yandex_messenger, chat_id: "user@ya" } ]
Call arguments: title?, body (required), level?, require_ack?.
Module agent_tools.
5.16 email_tool
Send an email (SMTP).
kind: email_tool
name: send_report
description: Отправить отчёт оператору.
smtp:
host: smtp.example.com
username: agent@example.com
password_env: SMTP_PASSWORD # или password: "..."
from: agent@example.com
tls: starttls # starttls (по умолч., 587) | ssl (465) | none (25)
port: 587
to: ops@example.com
subject: "Agent report"
| Field | Description |
|---|---|
smtp.host / username / from |
server, login, From (required) |
smtp.password / password_env |
password inline or env name |
smtp.tls |
starttls (default) | ssl | none |
to / subject |
defaults overridden by arguments |
Arguments: body (required), optional to / subject.
5.17 vk_tool
Calling the VK API on behalf of a community (group access token) — a universal gateway plus convenient actions for typical business tasks.
kind: vk_tool
name: vk_api
description: Вызвать VK API от имени сообщества.
token_env: VK_GROUP_TOKEN # env с group access token (по умолч. VK_GROUP_TOKEN)
service_token_env: VK_SERVICE_TOKEN # env с сервисным ключом (по умолч. VK_SERVICE_TOKEN)
api_version: "5.199" # версия API (по умолч. "5.199")
owner_id: -123456789 # дефолтный владелец (для wall_post)
| Field | Description |
|---|---|
token_env |
env name with the community token (group actions: send_message, wall_post, generic gateway) |
service_token_env |
env name with the service key — for search/read methods (search) unavailable to group tokens |
api_version |
VK API version (default 5.199) |
owner_id |
default owner for wall_post (negative = group) |
Two call modes. Generic gateway — any VK API method:
{ "method": "wall.get", "params": { "owner_id": -123456789, "count": 5 } }
Convenient actions (inside, the engine itself substitutes the required random_id
and default parameters):
{ "action": "wall_post", "message": "Новый пост от агента" }
{ "action": "send_message", "peer_id": 123, "message": "Привет" }
{ "action": "search", "q": "бренд", "count": 10 }
| Action | VK method | Token | What it does |
|---|---|---|---|
send_message |
messages.send |
group | message to a dialog (peer_id, message, opt. attachment) |
wall_post |
wall.post |
group | post to the wall (owner_id, message, opt. attachments) |
search |
newsfeed.search |
service | search for posts/mentions (q, count) |
Result: content — pretty-JSON of the response, data — parsed response
(for composition with visualize_tool). The group token is needed with the permissions messages,
wall, photos, docs, market, groups. Read/search methods
(newsfeed.search, wall.get, groups.search) are unavailable to group
tokens — they require a service key (service_token_env).
5.17.1 ym_tool
Sending messages and working with chats via the Yandex Messenger Bot API
(Yandex 360) on behalf of a bot — the outgoing "twin" of the
channel.yandex_messenger channel.
kind: ym_tool
name: yandex_messenger
description: Отправить сообщение в Яндекс Мессенджер.
token_env: YANDEX_MESSENGER_BOT_TOKEN # env с токеном бота (по умолч.)
default_chat_id: ivan # опц.: логин или id чата по умолчанию
| Field | Description |
|---|---|
token_env |
env name with the bot's OAuth token (default YANDEX_MESSENGER_BOT_TOKEN); the token is issued in Yandex 360 → "Bots in Messenger" |
default_chat_id |
default recipient: login (personal chat) or group/channel id with a slash / |
The recipient is set in the arguments: login (personal chat), chat_id
(group/channel) or to (login, or chat id with a slash /); otherwise
default_chat_id is used.
| Action | What it does |
|---|---|
send |
text to a chat (text; markdown: true converts CommonMark to Messenger markup) |
send_markdown |
same as send with markdown: true |
send_file |
file-document (file — path, base64 or content; name, mime) |
send_image |
image to the feed (file/base64/content) |
send_typing |
show "typing…" |
get_updates |
fetch updates (offset, limit), return updates and next_offset |
get_file |
download a file by file_id (text — inline, binary — base64 + to the session draft) |
{ "action": "send", "login": "ivan", "text": "Привет!" }
{ "action": "send", "chat_id": "0/0/4f24b544", "text": "**Отчёт** готов", "markdown": true }
{ "action": "get_updates", "offset": 0 }
Result: content — pretty-JSON, data — structured response
({ "sent": n, "recipient": "..." } for sending).
5.18 shell_tool
Run a CLI command. Universal launch of an external program: the command, flags,
working directory and environment are fixed in YAML by the agent's author; the LLM merely
substitutes values into input. Unlike an arbitrary fs_read, the LLM does not
choose the command — it only chooses the arguments according to a given JSON Schema.
kind: shell_tool
name: git_log
description: Показать последние коммиты репозитория.
command: git
args: ["log", "--oneline", "-n", "{{ input.limit | default(10) }}"]
cwd: /srv/agent-os # рабочая папка (по умолч. — папка сервера)
env:
GIT_PAGER: "cat" # доп. переменные окружения
stdin: "{{ input.text }}" # текст в stdin (опционально)
timeout_secs: 30 # жёсткий таймаут (по умолч. 30)
capture: both # stdout | stderr | both (по умолч.)
parse_json: false # распарсить stdout как JSON в data.parsed
parameters: { ... } # JSON Schema входа агента
| Field | Required | Description |
|---|---|---|
kind |
yes | shell_tool |
name |
yes | name for the LLM |
description |
no | description for the LLM |
command |
yes | program (MiniJinja template over input) |
args |
no | list of arguments (each is a template) |
cwd |
no | working directory (template) |
env |
no | additional environment variables (values are templates) |
stdin |
no | text fed to stdin (template) |
timeout_secs |
no | timeout (default 30) |
capture |
no | which streams to return: stdout | stderr | both |
parse_json |
no | parse stdout as JSON into data.parsed |
The command inherits the server's environment + env, and is executed with a time limit.
Result: content — captured output, success — zero exit code,
data — { exit_code, stdout, stderr, parsed? }.
All string fields are MiniJinja templates over input. If logging.temp_dir is set on the server,
the variable {{ temp_dir }} is available in templates — the scratch
directory of the current session, where you can generate a file (see
file_tool with source temp).
5.19 workflow_tool
Run a workflow.
kind: workflow_tool
name: run_triage
description: Запустить воркфлоу триажа.
workflow: triage # имя воркфлоу по умолчанию
Runs a workflow and returns its run id. The workflow name can be overridden
with the workflow argument; input — the input payload. Requires that WorkflowService be
connected in the core (done at server startup). See
12-workflows.
5.20 file_tool
Send a file to the user. Sends the user a file (report, document,
data export) as a downloadable file block. The agent usually does not have
access to the file system, so the content is taken from one of the sources:
kind: file_tool
name: send_file
description: Отправить файл пользователю.
max_bytes: 10485760 # макс. размер файла в байтах (по умолч. 10 МиБ)
| Argument | Description |
|---|---|
name |
file name for download (required), e.g. report.html |
mime |
MIME type (optional; inferred from the extension name) |
content |
content as a string (markdown / HTML / CSV / JSON…) |
data |
any JSON — serialized to a file (pretty JSON) |
path |
read bytes from a local file (if the agent can write files) |
temp |
{ name } — read a file that another tool wrote to the session's scratch directory |
state |
{ key, scope?, path? } — a value from the state store (DB). scope: session/user/agent (default resolves user → session → agent); path — dot path into the value |
source |
{ tool, index, field, path } — the result of another tool in this session. field: content (default)/data/visual; index: 0 = last |
The source is selected by priority content → data → path → temp →
state → source. Example of a report assembled from the result of a previous
tool:
# вызвать: send_file { "name": "report.csv", "source": { "tool": "build_report" } }
The LLM sees only a compact summary in content; the bytes themselves (base64) travel in
visual and reach the web widget (download link), Android and Telegram
(document).
Session scratch storage
If logging.temp_dir is set in the configuration, the core allocates each session a
scratch directory <temp_dir>/<session_id>/. A generator tool (e.g.
shell_tool, which runs an office CLI) writes a file there via
{{ temp_dir }}, and file_tool retrieves it by name:
# shell_tool: нагенерить .xlsx в scratch сессии
kind: shell_tool
name: export_xlsx
description: Сгенерировать отчёт в Excel.
command: libreoffice
args: ["--headless", "--convert-to", "xlsx", "--outdir", "{{ temp_dir }}", "{{ input.docx }}"]
# вызвать: send_file { "name": "report.xlsx", "temp": { "name": "report.xlsx" } }
The directory is deleted when the session is closed/expires.
5.21 office_tool
An engine wrapper over OfficeCLI (a single
self-contained binary, MS Office not needed). On first use the binary
is downloaded automatically into data/officecli/ and verified by SHA-256;
after that it is cached. The finished file is written to the session's scratch and returned
as a file block (like file_tool) — no external shell_tool wrappers are needed.
kind: office_tool
name: make_docx
description: Собрать Word-документ из Markdown.
operation: create # create | fill | extract | convert | edit | merge
format: docx # docx | xlsx | pptx (для create/merge)
# template: templates/contract.docx # для fill: путь к шаблону (отн. YAML)
# binary: /path/to/officecli # опц. явный путь к бинарю
# download_url: "https://..." # опц. переопределение
# sha256: "..." # опц. (пусто = не проверять)
# auto_download: true # по умолч. true
# pdf_bin: soffice # fallback для PDF (если нет плагина)
# pdf_exporter: /path/to/officecli_pdf_exporter # опц. явный путь к плагину
# timeout_secs: 120 # таймаут одной команды
Operations
operation |
What it does | Arguments |
|---|---|---|
create |
build a document from scratch | name + content (docx, Markdown) / rows+columns or csv (xlsx) / slides (pptx) |
fill |
fill a template with {{key}} placeholders |
name + data (object) or rows (array → mail merge, result is a zip) |
extract |
read a document as text/CSV for the LLM | temp/path + optional mode (text/outline/annotated/issues/csv) |
convert |
export to HTML/SVG/PDF | name (.html/.svg/.pdf) + temp/path |
edit |
apply edits set/add/remove/replace |
name + temp/path + ops: [{ action, path, type?, props?, find?, replace? }] |
merge |
merge several documents into one | name + `files: [{ temp |
Template placeholders (fill)
The template uses double curly braces {{key}} — in paragraphs, tables,
headers/footers, and chart titles. Filling is a officecli merge:
# вызвать: fill_contract { "name": "dogovor.docx", "data": { "client": "ООО Ромашка", "sum": "150000" } }
# вызвать (mail merge): fill_contract { "name": "dogovor.docx", "rows": [ {...}, {...} ] }
# → вернёт dogovor.zip с N заполненными файлами
A template .docx with placeholders can be produced yourself from Markdown:
make_docx → contract.md with text of the form {{client}}.
Editing (edit)
edit copies the input document to the output and applies the list of edits (the source
is not modified). Each edit is a { action, path, ... }:
# вызвать: edit_doc { "name": "edited.docx", "temp": { "name": "in.docx" },
# "ops": [
# { "action": "replace", "path": "/body", "find": "ООО Старое", "replace": "ООО Новое" },
# { "action": "set", "path": "/body/p[1]/r[1]", "props": { "bold": true } },
# { "action": "add", "path": "/body", "type": "paragraph", "props": { "text": "Новый абзац" } },
# { "action": "remove", "path": "/body/p[3]" }
# ] }
Actions: set (properties via --prop), add (element --type), remove,
replace (find/replace text via --find/--replace). path — the address
of an OfficeCLI element (/body/p[1], /Sheet1/A1, /slide[1]/shape[1], …).
Examples (full set)
A ready-made "secretary" agent is in config/agents/office/ — it also contains all these
tools and the contract template templates/contract.docx. Below are excerpts.
1. create — document from scratch
# tools/make_docx.yaml
kind: office_tool
name: make_docx
description: Собрать Word из Markdown.
operation: create
format: docx
// вызов: { "name": "report.docx", "content": "# Отчёт\n\nТекст..." }
# tools/make_xlsx.yaml
kind: office_tool
name: make_xlsx
description: Собрать Excel из таблицы.
operation: create
format: xlsx
// вызов: { "name": "price.xlsx", "columns": ["Name","Price"], "rows": [["Apple",3],["Banana",5]] }
2. fill — template with {{key}} + mail merge
# tools/fill_contract.yaml
kind: office_tool
name: fill_contract
description: Заполнить договор данными.
operation: fill
format: docx
template: ../templates/contract.docx # путь отн. этого YAML
// один документ: { "name": "dogovor.docx", "data": { "client": "ООО Ромашка", "sum": "150000" } }
// mail merge: { "name": "dogovor.docx", "rows": [ { "client": "A" }, { "client": "B" } ] }
// → вернёт dogovor.zip с N заполненными файлами
3. extract — reading (text or CSV)
kind: office_tool
name: extract_text
description: Прочитать документ текстом.
operation: extract
// { "temp": { "name": "dogovor.docx" } } → текст
// { "temp": { "name": "price.xlsx" }, "mode": "csv" } → CSV
4. convert — HTML / SVG / PDF
kind: office_tool
name: convert_pdf
description: Экспорт в PDF.
operation: convert
// { "name": "out.html", "temp": { "name": "in.docx" } }
// { "name": "out.pdf", "temp": { "name": "in.docx" } } // плагин + headless Chromium
5. edit — targeted edits
kind: office_tool
name: edit_doc
description: Внести правки в документ.
operation: edit
// { "name": "out.docx", "temp": { "name": "in.docx" }, "ops": [
// { "action": "replace", "path": "/body", "find": "старое", "replace": "новое" } ] }
6. merge — merge documents
kind: office_tool
name: merge_docs
description: Объединить документы в один.
operation: merge
format: docx # docx — склейка текста; xlsx — конкатенация строк
// { "name": "all.docx", "files": [ { "temp": { "name": "a.docx" } }, { "temp": { "name": "b.docx" } } ] }
Formats and limitations
createsupportsdocx(from Markdown),xlsx(from strings/CSV), andpptx(fromslides: [{ title, bullets[] }]).convertproduceshtml,svg, andpdf. Forpdf, the engine uses an exporter plugin (officecli_pdf_exporter) — a separate binary that OfficeCLI locates viaOFFICECLI_PLUGIN_EXPORTER_PDF. The plugin renders the document to HTML using the OfficeCLI engine, and then prints it to PDF with a headless Chromium browser. If the browser is absent, it is downloaded automatically (chrome-headless-shellfrom Chrome for Testing) to~/.cache/agent-os/browser(Linux) /%LOCALAPPDATA%\agent-os\browser(Windows). It works headless on a Linux server.- If the plugin is absent,
convert pdffalls back tosoffice(LibreOffice) — the path to the binary is set by thepdf_binfield. - OfficeCLI itself is downloaded on first invocation: access to GitHub Releases is required
(or set
download_url/binary).
5.24 storage_tool
Access to external file storage: S3-compatible object storage
(AWS S3, MinIO, Yandex Object Storage, Cloud.ru), WebDAV (Nextcloud,
ownCloud), and FTP/FTPS. Operations list / get / put / delete, scope —
optional prefix. Secrets — from env.
kind: storage_tool
name: upload_report
description: Выгрузить отчёт в объектное хранилище.
storage:
backend: s3 # s3 | webdav | ftp
endpoint: https://s3.example.com
bucket: reports
region: us-east-1
access_key_env: S3_ACCESS_KEY
secret_key_env: S3_SECRET_KEY
path_style: true # по умолч. true (MinIO-стиль)
prefix: uploads/ # опц. базовый префикс (скоуп агента)
operation: put # опц.: list | get | put | delete
Backends
backend |
Fields | Note |
|---|---|---|
s3 |
endpoint, bucket, region, access_key_env, secret_key_env, path_style? |
any S3-compatible service |
webdav |
base_url, username_env, password_env |
base_url — file root (e.g. …/remote.php/dav/files/user/) |
ftp |
host, port?, username_env, password_env, tls? |
tls: none | explicit (FTPS, default) |
Operations
| operation | Purpose | Parameters |
|---|---|---|
list |
list files under a prefix | prefix (additional sub-prefix) |
get |
download a file | key → text in content + file block |
put |
upload a file | key + content/data/path/temp |
delete |
delete a file | key |
Without operation, the operation is selected by the operation argument (default list).
get returns the content as text (if it is text ≤ 20 thousand characters) and always
as a file block in visual; list returns an array of {key, size, last_modified} in
data + a table.
5.25 calendar_tool
Reading and managing Google Calendar on behalf of a service account (2-legged OAuth — JWT bearer, without interactive consent). The engine itself exchanges the service account key for an access token (with a TTL cache) and calls the Google Calendar API v3.
kind: calendar_tool
name: calendar
description: Читать и управлять календарём.
credentials_file: data/google-calendar/service_account.json # путь к JSON-ключу
# credentials_env: GOOGLE_SERVICE_ACCOUNT_JSON # или env с JSON-текстом (по умолч.)
# credentials: { ... } # или inline-объект (не рекомендуется)
calendar_id: petrochenko.pavel.a@gmail.com # дефолтный календарь (перекрывается аргументом)
| Field | Description |
|---|---|
credentials_file |
path to the service account JSON key (takes priority over the others) |
credentials_env |
name of the env with the key JSON text (default GOOGLE_SERVICE_ACCOUNT_JSON) |
credentials |
inline key (not recommended — keep secrets in a file/env) |
calendar_id |
default calendar id (overridden by the calendar_id argument) |
Secrets are not kept in YAML. Key resolution order: credentials_file →
credentials → env credentials_env.
Actions (the action argument):
| Action | API method | What it does |
|---|---|---|
list_calendars |
calendarList.list |
list the service account's calendars |
list |
events.list |
events in a range (time_min/time_max, max_results, q) |
get |
events.get |
a single event (event_id) |
create |
events.insert |
create an event (summary, start, end, location, description, attendees) |
update |
events.update |
modify an event (event_id + the same fields) |
delete |
events.delete |
delete an event (event_id) |
quick_add |
events.quickAdd |
create an event from free text (text) |
{ "action": "list", "time_min": "2026-09-07T00:00:00Z", "max_results": 20 }
{ "action": "create", "summary": "Созвон", "start": "2026-09-10T15:00:00+03:00", "end": "2026-09-10T16:00:00+03:00" }
{ "action": "quick_add", "text": "Ужин завтра в 19:00" }
start/end accept an RFC3339 string (→ dateTime) or an object
{ dateTime, timeZone } / { date } (all-day event). A full event can be
passed as an event object. Generic gateway: method (in ресурс.действие notation)
params(query) +body:
{ "method": "events.list", "params": { "timeMin": "2026-09-07T00:00:00Z" } }
{ "method": "calendars.insert", "body": { "summary": "Рабочий календарь" } }
Supported methods: calendarList.list, events.list/get/insert/
update/delete/quickAdd, calendars.insert/delete, freebusy.query.
Result: content — pretty-JSON of the response, data — the parsed response (for
composition with visualize_tool).
Setting up access
- Create a service account (or take an existing JSON key) and enable the Google Calendar API in the project (Cloud Console → API Library).
- Give the service account access to the calendar: calendar settings →
"Share with specific people" → add
client_emailwith "Make changes to events" permission. - Specify
calendar_id— usually the owner's email (name@gmail.com) orxxx@group.calendar.google.com(the "Calendar ID" field in the settings).
Nuance: list_calendars for a service account may return an empty list — it
has no calendars "of its own", and calendars shared into calendarList are not added
automatically. Work directly via calendar_id (set in the config or
the argument).
5.26 google_tool
Reading and managing Google Drive / Sheets / Docs / Slides. Two authorization
modes: service account (2-legged OAuth / JWT) — suitable only for
reading; and user OAuth — for reading and writing (with the real
account's quota). One google_tool instance works with one service
(the service field); for multiple services, connect multiple tools.
kind: google_tool
name: gdrive
description: Обзор и управление Google Drive.
service: drive # drive | sheets | docs | slides
auth: oauth # oauth (рекомендуется) | service_account
oauth:
client_id: 123-abc.apps.googleusercontent.com
client_secret_env: GOOGLE_OAUTH_CLIENT_SECRET
token_file: data/google-oauth/drive_token.json
# для service_account (только чтение):
# credentials_file: data/google-calendar/service_account.json
# scopes: ["https://www.googleapis.com/auth/drive.file"] # опц. переопределение
| Field | Description |
|---|---|
service |
target service: drive | sheets | docs | slides |
auth |
oauth (not set by default — service_account) | service_account |
oauth.client_id / client_id_env |
OAuth client id (inline or env) |
oauth.client_secret / client_secret_env |
OAuth client secret (env) |
oauth.token_file |
file where the refresh token is stored after authorize_code |
oauth.redirect_uri |
redirect URI (default — OOB copy-paste) |
credentials_file / credentials_env / credentials |
service account key (same as calendar_tool) |
scopes |
optional override of OAuth scopes |
User authorization (OAuth)
A one-time step, performed through the tool itself:
- Call the
authorizeaction — a consent URL will be returned. - Open the URL, sign in to Google, and grant access.
- Copy the code and call
authorize_codewith thecodefield — the refresh token will be saved totoken_file.
{ "action": "authorize" }
{ "action": "authorize_code", "code": "4/0A..." }
After that, the tool refreshes the access token automatically using the refresh token.
Each service supports a generic gateway method (notation ресурс.действие)
params/body, and a set of convenient actionsaction.
Actions by service
Drive (service: drive):
| Action | API method | What it does |
|---|---|---|
list |
files.list |
list files (q, page_size) |
get |
files.get |
file metadata (file_id) |
create_folder |
files.create |
create folder (name, opt. parents) |
upload |
files.create (multipart) |
upload file (name, content, mime_type, parents) |
export |
files.export |
export a Google file to a format (file_id, mime_type, opt. name) |
download |
files.get?alt=media |
download file contents (file_id, opt. name) |
delete |
files.delete |
delete file (file_id) |
export/download return text (text) for text formats, and binary
(PDF/DOCX/XLSX) — as base64 + a file in the session scratch directory (for file_tool).
Sheets (service: sheets):
| Action | API method | What it does |
|---|---|---|
create |
spreadsheets.create |
create spreadsheet (title) |
get |
spreadsheets.get |
spreadsheet metadata (spreadsheet_id) |
read |
spreadsheets.values.get |
read range (spreadsheet_id, range) |
write |
spreadsheets.values.update |
write range (spreadsheet_id, range, values) |
append |
spreadsheets.values.append |
append rows (spreadsheet_id, range, values) |
import |
values.update |
write CSV (csv) or an array (values) into a range |
export |
values.get |
export a range as csv or json (format) |
add_chart |
batchUpdate |
embedded chart (range, title, chart_type: COLUMN/BAR/LINE/PIE/AREA) |
Docs (service: docs):
| Action | API method | What it does |
|---|---|---|
create |
documents.create |
create document (title) |
import |
create + batchUpdate | create a document with text (title, content) |
build |
create + batchUpdate | build a document from blocks (title, blocks) |
export |
documents.get |
extract document text (document_id) |
get |
documents.get |
raw document JSON (document_id) |
insert_text |
documents.batchUpdate |
insert text (document_id, text, opt. index) |
build builds a document from structured blocks with styles:
{ "action": "build", "title": "Отчёт", "blocks": [
{ "type": "heading", "text": "Раздел", "level": 1 },
{ "type": "paragraph", "text": "Жирный синий", "style": { "bold": true, "color": "#0000FF", "fontSize": 14, "align": "CENTER" } },
{ "type": "bullet", "items": ["a", "b"] },
{ "type": "numbered", "items": ["раз", "два"] },
{ "type": "table", "rows": [["Имя", "Значение"], ["alpha", 1]] },
{ "type": "image", "url": "https://…/img.png" },
{ "type": "page_break" }
] }
style supports bold/italic/underline/strikethrough, color
(#RRGGBB), background_color (#RRGGBB), font (font name, e.g. Arial,
Times New Roman), weight (400=normal, 700=bold), fontSize (pt),
align (START/CENTER/END/JUSTIFIED), lineSpacing.
Slides (service: slides):
| Action | API method | What it does |
|---|---|---|
create |
presentations.create |
create presentation (title) |
build |
create + batchUpdate | build a presentation from slides (title, slides) |
add_slide |
batchUpdate | add slide (presentation_id, title, bullets) |
outline |
presentations.get |
simplified text outline of slides |
export |
files.export |
export to PDF (presentation_id, opt. mime_type) |
get |
presentations.get |
raw presentation JSON |
{ "action": "build", "title": "Презентация", "slides": [
{ "title": "Титул", "bullets": ["пункт 1", "пункт 2"] },
{ "title": "Картинка", "image": { "url": "https://…/img.png" } }
] }
Universal request batching
The batch_update action is a universal primitive: pass a raw list of
requests and it will be applied to the target resource (document_id /
presentation_id / spreadsheet_id):
{ "action": "batch_update", "document_id": "<id>", "requests": [
{ "insertText": { "text": "hi\n", "location": { "index": 1 } } },
{ "updateTextStyle": { "range": { "startIndex": 1, "endIndex": 3 }, "textStyle": { "bold": true }, "fields": "*" } }
] }
This is an escape hatch for any operation not covered by the convenient actions (the same
effect is achieved by method: documents.batchUpdate + body: {requests: [...]}).
API limitations
- Columns (multi-column layout) are not supported in the Docs API — this is a UI-only feature.
- Tables in Slides are not supported by the API (UI only) — use tables in Docs or Sheets.
- There is no direct API for rendering an embedded chart to PNG;
add_chartcreates an embedded chart visible in the UI.
Example calls:
{ "action": "list", "q": "name contains 'report'", "page_size": 20 }
{ "action": "upload", "name": "notes.txt", "content": "hello", "mime_type": "text/plain", "parents": ["<folder_id>"] }
{ "action": "export", "file_id": "<id>", "mime_type": "application/pdf", "name": "report.pdf" }
{ "action": "import", "spreadsheet_id": "<id>", "range": "A1:C4", "csv": "a,b,c\n1,2,3\n" }
{ "action": "export", "document_id": "<id>" }
{ "action": "build", "title": "Отчёт", "blocks": [ ... ] }
Generic gateway (for non-standard methods):
{ "method": "files.list", "params": { "q": "mimeType='application/vnd.google-apps.folder'" } }
{ "method": "spreadsheets.values.get", "spreadsheet_id": "<id>", "range": "Sheet1!A1:D10" }
{ "method": "documents.get", "document_id": "<id>" }
{ "method": "presentations.get", "presentation_id": "<id>" }
Supported methods: files.list/get/create/delete/copy,
permissions.list/create, spreadsheets.create/get/values.get/
values.update/values.append/batchUpdate, documents.create/get/
batchUpdate, presentations.create/get/batchUpdate.
Result: content — pretty JSON of the response, data — the parsed response.
Setting up access
- Enable the required APIs (Drive / Sheets / Docs / Slides) in the Cloud Console.
- For OAuth mode (write): create an OAuth client ID (type "Desktop app"),
specify
client_id/client_secret, go throughauthorize→authorize_code.
Important about the service account. It has no Drive quota of its own — it
cannot upload/create files (storage quota exceeded / caller does not have permission) even in shared folders (the service account still becomes
the file owner). Therefore, for writing, use OAuth mode;
a service account is only suitable for reading shared files.
{ "action": "upload", "name": "report.csv", "content": "a,b\n1,2", "mime_type": "text/csv" }
{ "action": "create", "title": "Отчёт" }
5.26.1 yandex_tool
Working with Yandex services (Disk and Calendar) on behalf of a real
Yandex account via 3-legged OAuth2. Yandex has no service accounts, so
authorization is always user-based: a one-time authorize step → authorize_code
saves the refresh token to token_file, after which access tokens are refreshed automatically.
One instance of yandex_tool works with one service (the service field).
kind: yandex_tool
name: yandex_disk
description: Обзор и управление Яндекс.Диском.
service: disk # disk | calendar
oauth:
client_id: "…" # id приложения (inline или client_id_env)
client_secret_env: YANDEX_OAUTH_CLIENT_SECRET
token_file: data/yandex-oauth/disk_token.json
# email: you@yandex.ru # обязателен для calendar (путь CalDAV)
# scopes: ["cloud_api:disk.read", ...] # опц. переопределение
| Field | Description |
|---|---|
service |
target service: disk | calendar |
email |
Yandex email of the account — required for calendar (path …/calendars/{email}/events-default/) |
calendar |
name of the calendar collection for CalDAV (default events-default) |
oauth.client_id / client_id_env |
OAuth client id (inline or env) |
oauth.client_secret / client_secret_env |
OAuth client secret (env; may be omitted for public apps) |
oauth.token_file |
file where the refresh token is stored after authorize_code |
oauth.redirect_uri |
redirect URI — must match the app's Callback URL (for "API access" apps this is https://oauth.yandex.ru/verification_code) |
scopes |
optional override of OAuth scopes |
Authorization (one-time step, via the tool itself):
{ "action": "authorize" }
{ "action": "authorize_code", "code": "…" }
Disk (service: disk)
Base URL https://cloud-api.yandex.net/v1/disk, header
Authorization: OAuth <token> (not Bearer). Actions:
| Action | API method | What it does |
|---|---|---|
info |
GET /disk |
Disk info (capacity, limits) |
list |
GET /resources |
folder contents (path, default /; limit/offset/sort) |
get |
GET /resources |
resource metadata (path) |
create_folder |
PUT /resources |
create folder (path) |
delete |
DELETE /resources |
delete (path, opt. permanently) |
copy |
POST /resources/copy |
copy (from, path, opt. overwrite) |
move |
POST /resources/move |
move (from, path, opt. overwrite) |
upload |
GET /resources/upload → PUT href |
upload file (path, content) |
download |
GET /resources/download → GET href |
download file (path, opt. name) |
publish / unpublish |
`PUT /resources/publish | unpublish` |
files |
GET /resources/files |
flat list of all files (limit/offset) |
last_uploaded |
GET /resources/last-uploaded |
most recently uploaded files |
upload performs a two-step upload (first obtains a temporary href from the
API, then PUT the contents there). download is likewise two-step and returns
text (text) for text formats, and binary ones as base64 + a file in the
session's scratch directory.
Calendar (service: calendar)
Yandex has no public Calendar REST API — it works over the CalDAV
protocol (RFC 4791) at https://caldav.yandex.ru. The token is passed in the same
header Authorization: OAuth <token>; for REPORT/PROPFIND you need
Content-Type: text/xml, for events — text/calendar. The email field is required
(the path …/calendars/{email}/events-default/ is built from it).
| Action | CalDAV | What it does |
|---|---|---|
list_calendars |
— | default calendar (Yandex has only one) |
list |
REPORT (calendar-query) |
events in a range (from/to RFC3339, opt. limit) |
get |
GET …/{uid}.ics |
a single event (uid) |
create |
PUT …/{uid}.ics |
create event (summary, start, end, location, description; uid is auto-generated) |
update |
PUT …/{uid}.ics |
overwrite event (uid + fields) |
delete |
DELETE …/{uid}.ics |
delete event (uid) |
start/end accept an RFC3339 string (→ UTC), a YYYY-MM-DD string (all-day
event), or a { dateTime, timeZone } object. An event is identified by the
uid field (or the event_id alias).
{ "action": "list", "from": "2026-09-01T00:00:00Z" }
{ "action": "create", "summary": "Созвон", "start": "2026-09-10T15:00:00+03:00", "end": "2026-09-10T16:00:00+03:00" }
{ "action": "delete", "uid": "agent-os-abc123" }
Generic gateway
For non-standard Disk calls: method + params (query) + body.
Examples: resources, resources.download, resources.upload, resources.copy,
resources.move, resources.files.
{ "action": "list", "path": "/documents" }
{ "action": "upload", "path": "/report.csv", "content": "a,b\n1,2" }
{ "action": "create", "summary": "Созвон", "start": "2026-09-10T15:00:00+03:00", "end": "2026-09-10T16:00:00+03:00" }
{ "method": "resources", "params": { "path": "/" } }
Setting up access
- Register an application at oauth.yandex.ru (type "Web services" for a client_secret, or "For API access").
- Specify the required permissions: for Disk —
cloud_api:disk.read/write/info, for Calendar —calendar:all. redirect_uri(oauth.redirect_uri) must match the application's Callback URL. For "For API access" applications this is a fixed valuehttps://oauth.yandex.ru/verification_code.- Complete
authorize→authorize_code, the refresh token will be saved intoken_file.
5.27 vision_tool
Image analysis by a vision model (deepseek-v4-flash-vision-exp): OCR,
description, answering a question about an image. The tool accepts one or more
images, sends them to the model, and returns text. Tokens (including image tokens)
are charged to the calling agent's budget through the core's LLM service.
kind: vision_tool
name: vision
description: Прочитать текст / описать / ответить на вопрос по картинке(ам).
model: vision # именованная модель из server.yaml → models:
prompt: "Transcribe all text visible in the image(s) verbatim, in reading order."
detail: auto # low | high | original | auto
max_tokens: 2048
| Field | Description |
|---|---|
model |
named model (provider) from server.yaml → models: (default vision) |
prompt |
default prompt (overridden by the prompt argument) |
detail |
low | high | original | auto |
max_tokens |
response limit (default 2048) |
Arguments: images (array of strings, required), prompt?, detail?.
Each element of images is an http(s) URL, a data:image/...;base64,..., or a path to
a local file (absolute or relative to the session's scratch directory; it is read
and encoded in base64). Example call:
{ "images": ["https://example.com/shot.png"], "prompt": "Что за ошибка на скрине?" }
The result is content with the model's response.
5.28 request_attachment
Requesting an attachment from the user right during the agent's operation (not via widget buttons):
the agent calls the tool, the core sends an input_request event to the channel, the channel shows
a prompt, the user attaches a file / takes a screenshot / photo — and the run
continues. The uploaded file lands in the session's scratch directory, and the tool returns
its name — after that it can be read (vision_tool by name, file_tool, etc.).
Works in interactive channels: web chat (the widget shows a prompt card), Telegram, VK, and Yandex Messenger (the bot sends a message "send a file/photo", and the user's reply with media resolves the request). In batch channels (cron/email/webhook) there is no UI — the call will end with a timeout.
kind: request_attachment
name: request_image
description: Попросить пользователя прислать файл, скриншот или фото.
prompt: "Пожалуйста, прикрепите файл, сделайте скриншот или фото."
timeout_secs: 300
| Field | Description |
|---|---|
prompt |
default text shown to the user (overridden by the description argument) |
timeout_secs |
how long to wait for a response (default 300) |
Arguments: kind (file | screenshot | photo), description?, timeout_secs?.
The result is content with JSON of the form
{"kind":"screenshot","files":[{"name":"screenshot-....png","mime":"image/png","size":12345}]}.
5.28.1 oauth_connect
Asks the user to authorize access to a connector (an OAuth-protected external
API — Google Drive, Yandex.Disk, etc., config connectors: in §3). The agent calls
the tool, the core sends an input_request event to the channel with the authorization URL, the user
opens it and grants consent; the callback /v1/connectors/callback exchanges the code,
saves the token (per-user, encrypted), and resolves the request — the run continues.
kind: oauth_connect
name: oauth_connect
description: Попросить пользователя авторизовать доступ к коннектору.
timeout_secs: 300
Arguments: connector (connector name), timeout_secs?. The result is JSON
{"connector":"gdrive","granted":true}.
5.28.2 connector_request
An authenticated HTTP request through the connector on behalf of the user (the token is looked up by user_id, and if expired — auto-refreshed via refresh_token).
kind: connector_request
name: connector_request
description: Сделать авторизованный запрос через коннектор.
Arguments: connector, method (GET/POST/PATCH/DELETE/…), url,
body? (JSON/text). The result is the response body (content); on error —
Error: <status>: <body>.
5.28.3 await_event
Suspend the run and wait for an external event: a callback POST /v1/events/callback
(source webhook) or a timer (source cron). This is a generalized primitive
"wait until something happens externally" — unlike request_attachment
(waits for a file) and oauth_connect (waits for OAuth), here an arbitrary external
trigger is awaited.
kind: await_event
name: wait_payment
description: Дождаться подтверждения оплаты по correlation id.
timeout_secs: 600
event:
source: webhook # webhook (default) | cron
id_pointer: /object/id # где id в теле коллбэка (default /id)
# id_query: payment_id # либо query-параметр (взаимоисключает id_pointer)
| Field | Description |
|---|---|
event.source |
webhook (default) — wait for a callback; cron — timer |
event.id_pointer |
JSON-pointer → correlation id in the callback body (default /id) |
event.id_query |
name of the query parameter (mutually exclusive with id_pointer) |
event.delay_secs |
for source: cron — how many seconds to wait |
timeout_secs |
how long to wait for the event (default 600) |
source: webhook: argument event_id (required) — the id we are waiting for. While
the run is suspended, input_request is sent to the channel; the external system sends
POST /v1/events/callback { ... } (auth — events.token_env), the core matches the
callback by id (via event.id_pointer/id_query) and resolves the run. The result is
{"event_id":"...","payload":{...}}.
source: cron: argument delay_secs — simply wait N seconds and continue.
5.28.4 async_action
A complete declarative asynchronous flow start → prompt → suspend → callback → resume: first an HTTP request creates a resource (a payment, etc.), then from its response
the correlation id and the URL to display are extracted, and the run is suspended until the callback.
kind: async_action
name: sbp_payment
description: Принять оплату по СБП и дождаться подтверждения.
parameters:
type: object
properties: { amount: { type: string } }
required: [amount]
start: # поля как у rest_tool (method/url/json/headers/oauth2/sign)
method: POST
url: "https://api.yookassa.ru/v3/payments"
json: { amount: { value: "{{ amount }}", currency: "RUB" }, capture: true }
headers:
Authorization: "Basic {{ (env_var('YOOKASSA_SHOP_ID') ~ ':' ~ env_var('YOOKASSA_SECRET_KEY')) | base64 }}"
prompt:
message: "Оплатите по QR:"
url_pointer: /confirmation/confirmation_url
resolve:
id_pointer: /id
callback:
id_pointer: /object/id
timeout_secs: 600
| Field | Description |
|---|---|
start |
HTTP initiator request (fields as in rest_tool/site_tool) |
prompt.message |
text shown to the user |
prompt.url_pointer |
JSON-pointer → URL in the response start (the channel shows a button/link) |
resolve.id_pointer |
JSON-pointer → correlation id in the response start |
callback.id_pointer / callback.id_query |
where the id is in the callback (default /id) |
Flow: start is executed by the site_tool engine (and its template rendering/authorization),
the id/url are extracted from the response, then the same suspend/resume as in await_event
(route /v1/events/callback). The result is {"event_id":"...","payload":{...}}.
5.28.5 show_link
Show the user a link as a button (in the widget, clicking opens a window with the link; in messengers, a link in the message/button). By itself it waits for nothing and does nothing — it simply presents a URL.
kind: show_link
name: open_link
description: Показать пользователю ссылку.
wait: false # true — показать «Открыть» + «Готово» и ждать подтверждения
timeout_secs: 300
| Field | Description |
|---|---|
wait |
false (default) — show and return control; true — wait for user confirmation ("Done") |
timeout_secs |
how long to wait for confirmation (only with wait: true) |
Arguments: url (required), message?, wait? (overrides the config).
The result is {"shown":true} (or {"shown":true,"confirmed":true} with wait: true).
5.28.6 escalate_to_operator
Hand the dialog over to a live operator (agent-initiated escalation). The agent
calls the tool, the core opens an escalation and sends a card to the operator in
the configured channels (approval.operator_notify in server.yaml — Yandex
Messenger / Telegram / webhook). The operator writes /accept, communicates with
the user, and finishes with the /release command; if idle for longer than
timeout_secs, control returns to the agent with timeout_message. The same
mechanism exists in the escalate hook (see hooks).
kind: escalate_to_operator
name: escalate_to_operator
description: Передать диалог живому оператору.
contact: |
📞 +7 (383) 363-40-37
✉️ priem@nsu.ru
connecting_message: "Соединяю вас с оператором, пожалуйста, подождите…"
unavailable_message: "Оператор сейчас недоступен.\n{contact}"
context_messages: 12
| Field | Description |
|---|---|
contact |
contact that the agent gives the user if the operator is unavailable |
connecting_message |
message to the user when the escalation opens |
unavailable_message |
text for the unavailable operator; {contact} is substituted from contact |
context_messages |
how many recent session messages to attach to the operator card (0 = no history) |
Arguments: reason (off_topic | empty_answer | user_requested | other),
question?. The result is content with JSON
{"status":"escalated","request_id":"…","message":"…"} or
{"status":"unavailable","contact":"…"} (then the agent itself gives the contact).
Works in interactive channels (web widget, Telegram, VK, Yandex Messenger);
the operator's replies reach the user in their channel (web — in the same SSE stream).
Operator channels
The card is sent to the channels from config/server.yaml → approval.operator_notify
(the same list as for action approvals — see
approvals). Empty = operators are not notified.
# config/server.yaml
approval:
operator_notify:
- type: yandex_messenger
chat_id: "pawelpetrochenko" # логин оператора; для группового чата — id со слэшем
token_env: YANDEX_MESSENGER_BOT_TOKEN
- type: telegram
chat_id: -100123456789 # приватный чат или группа операторов
token_env: TG_OPERATOR_TOKEN
- type: webhook
url: https://ops.example.com/escalations
token_env: OPS_WEBHOOK_TOKEN # необязательно
Secrets — only via token_env (variables from .env), not in YAML.
| Channel | Card delivery | How the operator replies |
|---|---|---|
yandex_messenger |
sendText to a personal chat/chat_id |
text in the chat: /accept, messages, /release |
telegram |
card with inline buttons "✅ Take over" / "↩️ Return to bot" | buttons or text /accept, /release |
webhook |
POST JSON of the escalation event (bearer from token_env) |
notification only; reply — via the messenger |
Operator commands: /accept [id] — take over the dialog; /release [id]
(also /agent, !bot) — return control to the agent; any other text
goes to the user. The operator's replies are delivered to the user's channel
(web — in the same SSE stream). If idle for longer than timeout_secs, the escalation
is closed and timeout_message is sent to the user.
Approvals and escalations share a single
approval.operator_notify: one bot serves both, a shared long-pollgetUpdates. If the operator has previously written to the bot, they will receive the card in the same chat.
5.29 image_tool
Image processing directly on the server (without a vision model): cropping, resizing,
grayscale, rotate/flip, brightness/contrast/blur, and format conversion.
The source is http(s) URL, data: URL, a local path (absolute or relative to
the session's scratch directory), or a file from scratch via temp: { name }. Operations
are submitted in a batch to ops and applied in order.
kind: image_tool
name: image_edit
description: Обрезать / изменить размер / ч/б / повернуть / отконвертировать картинку.
max_bytes: 26214400 # лимит размера входного/выходного файла (по умолч. 25 MiB)
| Operation | Arguments | Effect |
|---|---|---|
crop |
{ x, y, width, height } or { left, top, right, bottom } |
crop a rectangle |
resize |
{ width, height, fit?, filter? } |
fit: contain (default) / exact / cover; filter: nearest / triangle / catmullrom / gaussian / lanczos3 |
grayscale |
— | desaturate |
rotate |
{ degrees } (90 / 180 / 270) |
rotate losslessly |
flip |
{ direction } (horizontal / vertical / both) |
flip |
brightness |
{ value } |
add to pixels (negative — darken) |
contrast |
{ factor } |
1.0 = no change |
blur |
{ sigma } |
gaussian blur |
format |
{ format, quality? } |
convert to png / jpeg / webp / gif / bmp / tiff; quality (1–100) for jpeg |
info |
— | record current dimensions/format (no changes) |
Call arguments: input (string — URL / data: URL / path) or temp: { name },
ops (array of operations), output? (filename for writing to scratch), send?
(explicitly controls delivery: by default false when output is set — only
to scratch; otherwise true).
Output. The result is encoded into bytes and:
- written to the session scratch directory if
outputis set (available later viatemp: { name },file_tool, etc.); - returned to the user as a
fileblock (base64) ifsend: true— the image is shown with an inline preview everywhere: web widget (<img>), Telegram, VK and Yandex Messenger (photo, forimage/jpeg|png|gif|webp; other formats are sent as a document), Android (inline preview + "Open"/"Save"). You can do both at the same time, or only one of the two.
Without ops the tool returns only the image metadata (width/height/format/size).
A ready-made recipe for generating images from text on top of
image_tool(free, no API key) — section 5.32.
Example (URL → crop → resize → grayscale → jpeg, return to the user and save to scratch):
{
"input": "https://example.com/photo.jpg",
"ops": [
{ "op": "crop", "x": 0, "y": 0, "width": 800, "height": 600 },
{ "op": "resize", "width": 400, "height": 400, "fit": "cover" },
{ "op": "grayscale" },
{ "op": "format", "format": "jpeg", "quality": 85 }
],
"output": "avatar.jpg",
"send": true
}
5.30 mcp_tool
Connect a single tool from an external MCP server (Model Context Protocol).
The agent hosts MCP tools, but is not itself an MCP server — mcp_tool
acts as a client that, using the tool: field, calls a specific tool on the server.
Transports:
stdio— local process (command+args+env), newline-delimited JSON-RPC;http/sse— Streamable HTTP (POST JSON-RPC, responseapplication/jsonortext/event-stream;sse— alias forhttp).
description and parameters (JSON Schema) are taken from the server's tools/list if
not overridden in YAML, so the LLM sees exactly the schema that the
server declares. The schema is fetched at config load time (not at runtime); if
the server is unavailable at load time — a fallback schema is registered, and the connection
error will be returned at call time.
kind: mcp_tool
name: read_file # имя, под которым тул виден агенту
description: Прочитать файл # необязательно — по умолчанию из схемы сервера
server:
transport: stdio # stdio | http | sse
command: npx # только stdio
args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]
env: {} # только stdio, доп. переменные для процесса
# — или удалённый сервер —
# transport: http
# url: https://example.com/mcp
# headers: { Authorization: "Bearer ${MCP_TOKEN}" }
timeout_secs: 30
tool: read_file # имя тула на MCP-сервере (обязательно)
parameters: {} # необязательное перекрытие JSON Schema
ui: {} # необязательный UI-конфиг
# arguments: '{ "path": "{{ input.path }}", "encoding": "utf-8" }'
| Field | Required | Description |
|---|---|---|
kind |
yes | mcp_tool |
name |
yes | local tool name for the LLM |
server |
yes | server config (see below) |
tool |
yes | tool name on the MCP server |
description |
no | description override (otherwise from tools/list) |
parameters |
no | JSON Schema override (otherwise from tools/list) |
arguments |
no | call argument template (see below) |
ui |
no | UI config (icons, output templates) |
Fields of the server block:
| Field | Transport | Description |
|---|---|---|
transport |
— | stdio | http | sse |
command |
stdio | program (required) |
args |
stdio | arguments |
env |
stdio | additional process environment variables |
url |
http/sse | MCP endpoint URL (required) |
headers |
http/sse | additional request headers |
timeout_secs |
— | request/handshake timeout (default 30) |
${ENV_VAR} in command, args, env, url and headers is expanded from
the process environment — secrets are not hardcoded into YAML.
Call templating. Without arguments in tools/call, the raw input
from the LLM is passed. The arguments field is a MiniJinja template string that is rendered into a JSON
document; the scope provides input, temp_dir, session_id, user_id,
template. This way the agent author can inject fixed fields, remap
the LLM input to the server schema, or substitute the user identifier:
arguments: '{ "query": "{{ input.query }}", "user": "{{ user_id }}", "limit": 10 }'
Connections to the same server are reused (pool by server), so several
mcp_tool referencing the same server keep a single live process / HTTP session.
5.31 wait_tool
Pause for a specified number of seconds — to pace polling of a long operation (for example, a benchmark), rather than calling the tool dozens of times in a row.
kind: wait_tool
name: wait
description: "Подождать N секунд."
max_seconds: 120 # потолок на один вызов (по умолчанию 120)
Arguments: seconds (integer, required; clamped to 1..max_seconds),
reason? (string). Response — Waited Ns (reason). Continue now. The tool
is universal (module agent_tools), available to any agent via
tool_files.
5.32 Image generation (text-to-image)
A ready-made "text → image" recipe without an API key, on the free service
Pollinations. This is not a separate kind, but a combination of
two tools (the script_tool + image_tool pattern).
It is easier to use the native engine
image_gen_tool(section 5.33) — it does the same thing with a single tool. The recipe is kept as an example of composingscript_tool+image_toolviacall_tool.
generate_image(script_tool) — builds the image URL from the prompt and passes it to the service tool viacall_tool;image_fetch(image_tool) — downloads the image by URL and returns afileblock; the script passes it into its ownvisual, so the image is shown in all channels (widget, Telegram, VK, Yandex Messenger, Android).
Both live in config/tools/ and are reused by any agent. In tool_files
the agent needs just two lines (paths are relative to the agent directory, hence
../../tools/):
tool_files:
- ../../tools/generate_image.yaml # генерация
- ../../tools/image_fetch.yaml # служебная доставка картинки в чат
After that the LLM sees
generate_image(prompt, width?, height?, seed?, model?, style?).
Agent's own style (thin wrapper)
The implementation is a shared config/tools/generate_image.js. To set the default style and
sizes, create a thin wrapper in the agent that references the same
script, with the fields style / width / height / deliver:
# config/agents/<agent>/tools/generate_image.yaml
kind: script_tool
name: generate_image
description: Нарисовать иллюстрацию в стиле агента.
script: ../../../tools/generate_image.js # общая реализация
style: "dark historical oil painting, 16th century, muted palette, storm light"
width: 1024
height: 768
parameters:
type: object
properties:
prompt: { type: string }
required: [prompt]
style from the wrapper is appended to the prompt and overridden by the call (style in
the arguments); deliver changes the name of the delivery tool (by default
image_fetch). Ready-made examples: the image agent (no style) and egidia
(gloomy historical painting).
../../tools/…links work in the config file tree (and in the git config, whereconfig/tools/exists). The admin agent builder ("Builder") operates on a self-contained agent bundle and does not copy files outside its directory — when actively using Builder, keep a copy of the tools inside<agent>/tools/.
How it works
The script encodes the prompt, builds the URL
https://image.pollinations.ai/prompt/<prompt>?width=…&height=…&seed=…&nologo=true
and calls
call_tool("image_fetch", { input: url, ops: [{ "op": "info" }], send: true }).
image_fetch, like any image_tool, downloads the URL and on
send: true returns an file block; the script returns this block in visual, and it
is rendered in the UI (see common fields).
Without the tool-calling service (offline
agent-os tool)call_toolthrows — the script catches this and returns the ready-made URL incontent(handy for debugging the config without a server).
Limitations
- The service is public and free: request limits and periodic
delays are possible. The list of models (
model, e.g.flux,turbo) changes; by defaultfluxis used. - The free tier adds a
pollinations.aiwatermark;nologo=truedoes not always help. - For private/commercial generation, the recipe switches to local Stable Diffusion/ComfyUI or another API — only the URL construction in the script changes.
5.33 image_gen_tool
A native "text → image" engine. Unlike the recipe in
5.32, this is a separate kind: it renders the
backend URL itself, downloads the bytes and returns an file block — without script_tool,
call_tool and a second tool.
kind: image_gen_tool
name: generate_image
description: Сгенерировать картинку по описанию.
url_template: "https://image.pollinations.ai/prompt/{prompt}?width={width}&height={height}&seed={seed}&nologo=true"
model: flux # добавляется как &model=…, если шаблон без {model}
style: "dark oil painting, 16th century" # дописывается к промпту
width: 1024
height: 768
max_bytes: 26214400 # лимит размера ответа (по умолчанию 25 МиБ)
# send: true # отдавать пользователю (по умолчанию true, если нет output)
# output: image.jpg # имя файла для записи в scratch сессии
# headers: { Authorization: "Bearer ${IMAGE_API_KEY}" } # ${ENV} раскрывается
Call arguments: prompt (required), width, height, seed, model,
style, output, send.
| YAML field | Purpose |
|---|---|
url_template |
request template; placeholders (URL-encoded): {prompt}, {style}, {model}, {width}, {height}, {seed}. Empty → Pollinations |
model |
default model (appended as &model=… if the template has no {model}) |
style |
default style, appended to the prompt via , |
width / height |
default sizes (clamped to 64..2048) |
output / send |
scratch file and delivery (same as image_tool) |
max_bytes |
response size limit (default 25 MiB) |
headers |
extra headers; ${ENV_VAR} is expanded from the environment |
How it works. The engine builds the URL from url_template, downloads the response,
checks that it is a recognizable image (image::guess_format), and
returns an file block (base64) — the image is shown in all channels
(widget, Telegram, VK, Yandex Messenger, Android). A backend error
(e.g. HTTP 402 on a limit) is returned as success: false with the text.
Another provider. Only url_template changes (and headers if needed):
local Stable Diffusion/ComfyUI, an OpenAI-compatible API, etc.
Connection examples: the image agent (no style) and egidia (gloomy
historical painting).