Tools Reference
Victauri exposes 35 MCP tools organized into standalone tools (one action per call) and compound tools (multiple actions via an action parameter).
All tools are accessible via MCP at /mcp or REST at POST /api/tools/{tool_name}.
Common parameters. Every tool that touches a webview accepts
webview_label(aliaseswindow,window_label) to pick the window; without it the default window is used (main, else the first visible one). Every compound tool takes a requiredactionstring naming the operation.
Backend Tools
These tools access the Rust backend directly — no webview proxy, no JavaScript evaluation.
app_info
Get application configuration, directory paths, environment, discovered databases, and process info.
Parameters: None required.
Returns: {config, paths, databases, process, environment}
list_app_dir
Browse files in app backend directories (data, config, log, local_data).
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
directory | string | no | One of: data, config, log, local_data (default: data) |
path | string | no | Subdirectory to list within the chosen directory |
pattern | string | no | Glob to filter entries (e.g. *.db) |
max_depth | number | no | Recursion depth (default: 1) |
Returns: {root, entries: [{name, path, is_dir, size, modified}]}
A listing stops (and reports truncated) at 10,000 returned entries, 100,000 examined entries, or
5 s — whichever comes first — so a pattern that matches nothing in a huge tree still returns.
read_app_file
Read a file from one of the app’s backend directories.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
path | string | yes | File path relative to the directory root |
directory | string | no | One of: data, config, log, local_data (default: data) |
max_bytes | number | no | Max bytes to read (default: 1 MB) |
binary | boolean | no | Return base64 instead of UTF-8 text |
Returns: UTF-8 text, or base64-encoded bytes when binary is true. Path-traversal-guarded.
A read truncated at max_bytes in the middle of a multi-byte character drops the partial
character and stays UTF-8 text.
query_db
Execute a read-only SQL query against a SQLite database in the app’s data directory.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
query | string | yes | SQL query — SELECT/PRAGMA(read)/EXPLAIN/WITH only |
path | string | no | Path to the database file (auto-discovers if omitted) |
params | array | no | Positional bind parameters |
max_rows | number | no | Max rows to return (default 100) |
Examples:
{"query": "SELECT * FROM users WHERE active = ?", "params": [true]}
{"query": "SELECT count(*) FROM items", "path": "app.db"}
Returns: {columns, rows, row_count, truncated, max_rows}
Limits: a 5 MB result budget charged cell by cell (the result is truncated rather than built
past it), at most 256 result columns, 1 MB per value, LIKE/GLOB patterns up to 1000 bytes, a
5 s CPU deadline, a 1 s wait for a lock held by the app, and at most two database calls
(query_db / db_health) running at once — a third gets a “busy” error. Duplicate result
column names get :N keys (a, a:1) so no value is lost; columns lists exactly the keys
each row carries. A single string function (LIKE, replace, instr) is one uninterruptible
SQLite step, so a pathological call can overrun the deadline by seconds on its worker thread;
the tool call itself returns at the deadline.
Read-only and path-traversal-guarded: writes (INSERT/UPDATE/…), stacked
queries, ATTACH, and the write form of PRAGMA (PRAGMA x = y) are rejected.
By default only the OS app-data directories are searched; if your app stores its
DB elsewhere (a project/working dir or custom path), register the directory via
VictauriBuilder::db_search_paths(["../data", "/abs/path"]) — then relative names
and absolute paths within those roots become reachable.
Webview & IPC Tools
eval_js
Evaluate JavaScript in the webview and return the result.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
code | string | yes | JavaScript code to evaluate (expressions, statements, or async/await) |
webview_label | string | no | Target webview (defaults to “main” or first visible) |
Examples:
{"code": "document.title"}
{"code": "document.querySelectorAll('button').length"}
{"code": "await fetch('/api/data').then(r => r.json())"}
Auto-return: a single bare expression is auto-wrapped with return
(document.title → return document.title). Multi-statement code must include
an explicit return — e.g. localStorage.setItem('k','v'); return localStorage.getItem('k') —
or be wrapped in an IIFE; otherwise only the first statement runs. async/await is
supported.
JavaScript errors (thrown exceptions) return an MCP error with isError: true.
undefined returns "undefined", null returns null. A syntax error is
reported at once as a JavaScript parse error (“the code did not begin executing”) — code
reported that way never runs later. A result JSON cannot represent (a circular object, a
BigInt, a function) is reported as “the code ran, but its result could not be serialized”,
never as a JavaScript error. A page reload under the call fails it promptly. Targeting a hidden or unresponsive window fails fast (~2s); and if a prior
eval timed out, the next call re-probes and fails fast if the webview reloaded or
the app stopped responding.
dom_snapshot
Capture a full accessible DOM tree with ref handles for every element.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
webview_label | string | no | Target webview |
format | string | no | compact (default, accessible text, ~70-80% fewer tokens) or json (full tree) |
Returns: Tree of elements with ref, role, name, children, and bounding box data. Descends into open shadow DOM and same-origin iframes (cross-origin frames are marked and skipped).
find_elements
Search for elements by CSS selector or text content. Returns an MCP error for invalid CSS selectors. Searches into open shadow roots and same-origin iframes — frame elements get ref handles and are fully interactable.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
selector | string | no | CSS selector (alias: css). Invalid selectors return an error. |
text | string | no | Text content to search for |
role | string | no | ARIA role to filter by |
webview_label | string | no | Target webview |
test_id | string | no | Exact data-testid value |
name | string | no | Accessible name (aria-label, title, placeholder; case-insensitive substring) |
tag | string | no | Tag name, e.g. button |
placeholder | string | no | Placeholder text (case-insensitive substring) |
alt | string | no | Alt text (case-insensitive substring) |
title_attr | string | no | title attribute (case-insensitive substring) |
label | string | no | Associated <label> text (finds inputs by their label) |
exact | boolean | no | Exact instead of substring text matching |
enabled | boolean | no | Filter by enabled state |
max_results | integer | no | Maximum results (default 10) |
Examples:
{"selector": "button.primary"}
{"text": "Submit"}
{"role": "heading"}
invoke_command
Invoke a Tauri command from the backend.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
command | string | yes | Command name |
args | object | no | Arguments to pass |
timeout_ms | integer | no | How long to wait for the result (default: the plugin eval timeout, 30 s; max 300000). Raise it for legitimately slow commands. victauri-test: invoke_command_with_timeout |
Example:
{"command": "get_settings", "args": {}}
{"command": "search_context", "args": {"query": "hello"}}
screenshot
Capture a PNG screenshot of the application window.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
window_label | string | no | Target window (defaults to main) |
Returns: Base64-encoded PNG image data.
Linux: capture requires X11 or XWayland (the common case, including CI under
xvfb). A pure-Wayland session exposes no safe per-window capture path, soscreenshotreturns a clear error there rather than a wrong or blank image. Windows and macOS capture natively.
verify_state
Compare frontend and backend state to detect drift.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
frontend_expr | string | no | JS expression for frontend state |
backend_state | object | no | Expected backend state to compare |
backend_command | string | no | Tauri command whose result is the backend state (instead of backend_state) |
backend_args | object | no | Arguments for backend_command |
Example:
{
"frontend_expr": "document.title",
"backend_state": {"title": "My App"}
}
detect_ghost_commands
Classify every command the frontend invoked by its observed outcome: a command that succeeded at least once provably has a handler (counted in verified_handlers); one that only ever failed with “not found” is listed in confirmed_ghosts; plugin:* built-ins are excluded_builtins; frontend_only is the weak tier (absent from the #[inspectable] registry, which is a subset of the real handler set). Read reliability and note before treating frontend_only as a bug list.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
since_ms | integer | no | Only consider commands invoked within the last N ms (scopes detection to the current test without clearing the IPC log) |
Returns: confirmed_ghosts (each {name, error}), verified_handlers (a count), frontend_only, excluded_builtins, registry_only, total_frontend_commands, total_registry_commands, reliability (none/low/high) and note.
check_ipc_integrity
Verify the health of IPC communication.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
stale_threshold_ms | integer | no | Age after which a pending IPC call counts as stale (default 5000) |
Returns: {healthy, total_calls, pending_count, stale_count, error_count, stale_calls, errored_calls, warning}
wait_for
Wait for a condition to become true, polling until timeout. Use the expression
and event conditions to await async backend work to true completion instead
of guessing with a fixed sleep.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
condition | string | yes | One of: selector, selector_gone, text, text_gone, url, ipc_idle, network_idle, expression, event |
value | string | no | Selector/text/URL to match; the JS expression (expression); or the Tauri event name (event). Not needed for ipc_idle/network_idle |
expected | any | no | For expression: the JSON value the expression must equal. Omit to wait for the expression to become truthy |
since_ms | number | no | For event: how far back (ms) to accept an already-fired event when the wait begins (default: 2000) |
timeout_ms | number | no | Max wait time in ms (default: 10000) |
poll_ms | number | no | Poll interval in ms (default: 200) |
Conditions for async completion:
expression— polls a JS expression until truthy (or== expected). It mayawait, so you can await a fire-and-forget command’s status directly. Level-triggered and race-free; needs no app changes. Returns{ ok, value, elapsed_ms }.event— blocks until a named Tauri event fires (evaluated against the captured event bus, with asince_mslook-back). The app must emit the event and Victauri must capture it viaVictauriBuilder::listen_events. Returns{ ok, event, elapsed_ms }.
Example:
{"condition": "selector", "value": ".modal.open", "timeout_ms": 3000}
{"condition": "url", "value": "/dashboard"}
{"condition": "expression", "value": "(await window.__TAURI_INTERNALS__.invoke('get_status')).running === false", "timeout_ms": 30000}
{"condition": "event", "value": "analysis-complete", "timeout_ms": 30000}
The robust async pattern is invoke_command(...) then wait_for(expression|event, ...).
app_state
Read application-defined backend state through a registered probe. Probes give an
agent first-class, discoverable access to domain state (a scoring pipeline’s version
and stale-item count, a queue’s depth, cache stats) that would otherwise require
query_db + log-grepping. A probe runs in the Rust process with no IPC round-trip
and no frontend involvement — direct-backend introspection a browser-external tool
cannot do.
Apps register probes via VictauriBuilder::probe("name", || serde_json::json!({ … }))
(build your shared state as an Arc once, clone it into both .manage() and the probe).
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
probe | string | no | Name of the probe to run. Omit to list all available probe names |
Example:
{} // → { "probes": ["scoring", "queue"] }
{"probe": "scoring"} // → { "pipeline_version": 5, "stale_items": 0 }
assert_semantic
Assert a condition about the application state using JS expressions.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
expression | string | yes | JS expression to evaluate |
condition | string | yes | One of: equals, not_equals, contains, greater_than, less_than, truthy, falsy |
expected | any | no | Expected value (not needed for truthy/falsy) |
label | string | no | Human-readable name for the assertion (default empty) |
Example:
{
"expression": "document.title",
"condition": "equals",
"expected": "My App"
}
resolve_command
Resolve a natural language description to registered commands.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
query | string | yes | Natural language description |
limit | integer | no | Maximum results (default 5) |
Example:
{"query": "show settings"}
get_registry
List all registered commands with their metadata.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
query | string | no | Filter commands by name or description |
Each argument lists its Rust name, and — when it differs — the key the frontend invokes it
by: Tauri camelCases argument names by default (size_kb → sizeKb), so pass key (or
name when there is no key) in invoke_command’s args.
get_memory_stats
Get real OS process memory usage.
Parameters: None.
Returns: {working_set_bytes, peak_working_set_bytes, page_fault_count, page_file_bytes}
get_plugin_info
Get plugin version, uptime, configuration, and capabilities.
Parameters: None.
get_diagnostics
Get detailed diagnostic information about the plugin state.
Parameters: None.
Compound Tools
Compound tools use an action parameter to select the specific operation.
interact
Element interactions with Playwright-grade actionability checks. Works on elements inside same-origin iframes too (refs from a snapshot/find resolve across frame boundaries).
An action the page refuses — element covered, disabled, hidden, detached, not a
<select>, or ref not found — is a tool error (MCP isError: true; REST
{"error": …}) whose text is the reason followed by [hint: RETRY_LATER] (the UI
may still settle) or [hint: CHECK_INPUT] (wrong target). The same applies to
input, inspect (e.g. get_styles on an unknown ref) and route add. (Before
0.9.0 these came back as a successful {"ok": false, "error": …} result.)
| Action | Parameters | Description |
|---|---|---|
click | ref_id | Click an element |
double_click | ref_id | Double-click an element |
hover | ref_id | Hover over an element |
focus | ref_id | Focus an element |
scroll_into_view | ref_id | Scroll element into viewport |
select_option | ref_id, value or values | Select option(s) in a <select> |
trusted | boolean | no |
x | number | no |
y | number | no |
Trusted (OS-level) clicks: add "trusted": true to click to deliver a
real OS mouse event (isTrusted: true) at the element’s center, instead of a
synthetic DOM event — for app handlers that gate on event.isTrusted or browser
features needing user activation. Implemented on Windows (Win32 SendInput);
on macOS/Linux it returns a clear “not implemented” error so you fall back to the
default synthetic click. The window is brought to the foreground first.
Example:
{"action": "click", "ref_id": "e3"}
{"action": "click", "ref_id": "e3", "trusted": true}
{"action": "select_option", "ref_id": "e9", "value": "us"}
input
Text input and keyboard operations.
| Action | Parameters | Description |
|---|---|---|
fill | ref_id, value | Set input value directly |
type_text | ref_id, text | Type character-by-character |
press_key | key | Press a keyboard key |
trusted | boolean | no |
Trusted (OS-level) input: add "trusted": true to type_text or press_key to
deliver real OS keystrokes (isTrusted: true) into the focused element instead
of synthetic DOM events. The target element (ref_id) is focused first.
Implemented on Windows (Win32 SendInput); macOS/Linux fall back to synthetic
input with a clear error.
Example:
{"action": "fill", "ref_id": "e5", "value": "hello@example.com"}
{"action": "type_text", "ref_id": "e5", "text": "Hello"}
{"action": "type_text", "ref_id": "e5", "text": "Hello", "trusted": true}
{"action": "press_key", "key": "Enter"}
Supported keys: Tab, Escape, Enter, ArrowUp, ArrowDown, ArrowLeft, ArrowRight, F1-F12, and any single character.
window
Window management operations.
| Action | Parameters | Description |
|---|---|---|
get_state | label | Get window state (position, size, visibility) |
list | — | List all window labels |
manage | label, manage_action | minimize, unminimize, maximize, unmaximize, close, focus, show, hide, fullscreen, unfullscreen, always_on_top, not_always_on_top |
resize | label, width, height | Resize a window |
move_to | label, x, y | Move a window |
set_title | label, title | Change window title |
introspectability | — | Probe every window’s JS bridge and report which are introspectable vs. blind |
introspectability answers “which windows can Victauri actually see?” A window that returns introspectable: false while visible: true is almost always missing the victauri:default capability — Tauri’s per-window permission ACL silently blocks the bridge’s callback IPC, so eval_js/dom_snapshot/animation/find_elements see nothing with no error. The diagnostic names the exact capability file to edit. Required per window (not just main).
Example:
{"action": "list"}
{"action": "get_state", "label": "main"}
{"action": "resize", "label": "main", "width": 1200, "height": 800}
{"action": "introspectability"}
storage
Browser storage operations.
| Action | Parameters | Description |
|---|---|---|
get | key, storage_type | Get a value (storage_type: local (default) or session) |
set | key, value, storage_type | Set a value |
delete | key, storage_type | Delete a key |
get_cookies | — | Get the page’s cookies (non-httpOnly only — read via document.cookie) |
Example:
{"action": "set", "key": "theme", "value": "dark"}
{"action": "get", "key": "theme"}
navigate
Navigation and history operations.
| Action | Parameters | Description |
|---|---|---|
go_to | url | Navigate to a URL (http/https only) |
go_back | — | Go back in history |
get_history | — | Get navigation history log |
set_dialog_response | dialog_type, dialog_action, text | Auto-respond to future alert/confirm/prompt dialogs (accept / dismiss) |
get_dialog_log | — | Get dialog log (alerts, confirms, prompts) |
Example:
{"action": "go_to", "url": "https://example.com"}
{"action": "get_history"}
{"action": "set_dialog_response", "dialog_type": "confirm", "dialog_action": "accept"}
recording
Time-travel recording for session capture and replay.
| Action | Parameters | Description |
|---|---|---|
start | session_id (optional) | Start recording events |
stop | — | Stop recording and return session |
checkpoint | checkpoint_id (optional, auto-generated), checkpoint_label (alias: label), state | Create a checkpoint |
list_checkpoints | — | List checkpoints |
get_events | since_index | Get recorded events |
events_between | from, to (checkpoint ids) | Events between two checkpoints |
get_replay | — | The recorded IPC call sequence |
export | — | Export full session data (works after stop) |
import | session_json | Import a session (becomes the active recording; refused while one is in progress) |
replay | webview_label | Re-invoke recorded IPC commands (see below; works after stop) |
flush | webview_label | Pull pending bridge events into the active recording now |
replay semantics. Recordings do not capture call arguments, so replay re-invokes
only calls that succeeded with no arguments. Calls that had arguments, failed, or
never completed are reported as skipped with a reason; commands blocked by the privacy
allow/blocklist are reported as blocked. passed means the re-invocation succeeded —
the response is not diffed against the original (only its JSON type is reported).
Re-invoked commands run for real, so their side effects happen again.
Each call is re-invoked in the window that recorded it — a window’s Tauri capabilities are
its own — and is skipped if that window no longer exists (it never falls back to main);
webview_label only filters which recorded calls are replayed. Calls that were fulfilled or
blocked by a network route (route add, which page script can also use) are recorded as
mocked and never replayed: they never reached the backend. (Script in the recording window
can still rewrite what the recording captures from that window — see
Security.)
Example:
{"action": "start"}
{"action": "checkpoint", "label": "after-login"}
{"action": "get_events", "since_index": 0}
{"action": "stop"}
{"action": "replay"}
inspect
CSS inspection, accessibility, and performance profiling.
| Action | Parameters | Description |
|---|---|---|
get_styles | ref_id, properties | Get computed CSS styles |
get_bounding_boxes | ref_ids | Get bounding boxes with box model |
highlight | ref_id, color, highlight_label | Draw debug overlay on element |
clear_highlights | — | Remove all debug overlays |
audit_accessibility | — | Run WCAG accessibility audit |
get_performance | — | Get performance metrics |
label | string | no |
Example:
{"action": "get_styles", "ref_id": "e3", "properties": ["color", "font-size"]}
{"action": "get_bounding_boxes", "ref_ids": ["e1", "e2", "e3"]}
{"action": "audit_accessibility"}
{"action": "get_performance"}
The accessibility audit checks: missing alt text, unlabeled form inputs, empty buttons/links, heading hierarchy, color contrast (WCAG AA), ARIA role validity, positive tabindex, and missing document language/title.
Performance metrics include: navigation timing, resource summary, paint timing (FP/FCP), JS heap usage, long task count, and DOM statistics.
css
CSS injection for debugging and prototyping.
| Action | Parameters | Description |
|---|---|---|
inject | css | Inject custom CSS (replaces previous) |
remove | — | Remove injected CSS |
allow_remote | boolean | no |
Example:
{"action": "inject", "css": "* { outline: 1px solid red; }"}
{"action": "remove"}
logs
Access all captured logs from the application.
| Action | Parameters | Description |
|---|---|---|
console | since, level | Console log entries |
network | since | Network request log |
ipc | since, limit | IPC command log |
navigation | — | Navigation history |
dialogs | — | Dialog interactions |
events | since | Event stream |
slow_ipc | threshold_ms | IPC calls slower than threshold |
clear | — | Clear the IPC + network logs (per-test isolation) |
filter | string | no |
wait_for_capture | boolean | no |
Example:
{"action": "console", "level": "error"}
{"action": "network"}
{"action": "slow_ipc", "threshold_ms": 100}
ipc/network/slow_ipcreturn at most 100 entries by default and truncate large per-entry bodies (4 KB) — pass an explicitlimitfor more.
route
Network request interception — the Playwright route() equivalent, implemented
purely in the JS bridge (no CDP; the same behaviour on all three Tauri webviews, within the fetch/XHR limits below).
Matches webview fetch/XHR by URL and blocks, mocks, or delays them. Rules are
page-scoped (cleared on reload).
| Action | Parameters | Description |
|---|---|---|
add | pattern, behavior, … | Add a rule (see below) |
list | — | List active rules |
clear | id | Remove a rule by id |
clear_all | — | Remove all rules |
matches | limit | Log of intercepted requests |
status_text | string | no |
add parameters:
| Name | Type | Required | Description |
|---|---|---|---|
pattern | string | yes | URL pattern to match. Tested against the request’s absolute URL (resolved against the page’s base URL), its path + query + fragment when same-origin, and the URL string as the app passed it — so a rule hits the same request whether the app calls fetch('/api/x'), fetch(new URL(…)) or fetch(new Request(…)), and likewise for XHR |
match_type | string | no | substring (default), glob (* matches any run of characters; every other character, including ?, is literal), regex, exact |
method | string | no | Restrict to one HTTP method |
behavior | string | no | block (abort), fulfill (mock — default), delay |
status | number | no | Mock response status (fulfill, default 200) |
headers | object | no | Mock response headers (fulfill) |
body | any | no | Mock response body (fulfill) |
content_type | string | no | Mock content-type (fulfill, default application/json) |
delay_ms | number | no | Latency to inject (delay; also delays a fulfill) |
times | number | no | Max times the rule fires (0 = unlimited) |
Example:
{"action": "add", "pattern": "/api/users", "behavior": "fulfill", "status": 200, "body": {"users": []}}
{"action": "add", "pattern": "analytics", "behavior": "block"}
{"action": "add", "pattern": "*/slow", "match_type": "glob", "behavior": "delay", "delay_ms": 2000}
{"action": "matches"}
Scope: fetch supports all behaviors; XHR supports
block/delay(fulfillis fetch-only). Top-level navigation, sub-resources (img/css), and WebSocket frames are not intercepted.Tauri IPC is observe-only. The app’s real
invoketraffic (http://ipc.localhost/…on Windows,ipc://localhost/…on macOS/Linux) is logged (logs ipc, and a rule may even show up inmatches), but a rule does not control it — Tauri serves real IPC below the JSfetchlayer the bridge can reach, so the call still goes through, undelayed. Thefaulttool does not touch the app’s real frontend IPC either: it only applies to commands you drive throughinvoke_command. Neither tool can reproduce a failure a user clicking the UI would hit.
trace
Screencast / visual timeline — captures the window at a fixed interval into a
ring buffer via the native screenshot path (no CDP). Pairs with recording
(events) and logs (network/console) to form a Playwright-trace-style bundle.
| Action | Parameters | Description |
|---|---|---|
start | interval_ms, max_frames, with_events | Begin capturing |
stop | — | Stop and return a summary |
status | — | Active flag + buffered frame count |
frames | limit | Return captured frames as base64 PNGs |
start defaults: interval_ms 500 (min 50, max 60000), max_frames 60 (max 600). Set
with_events: true to also start the event recorder so the trace bundles the
IPC/DOM/console timeline alongside the screencast.
Limits and lifecycle:
- A trace auto-stops after 30 minutes (an abandoned trace must not capture forever).
- The frame buffer is a ring (
max_frames) and is also capped at 256 MB total; the oldest frames are dropped first. framesreturns the newest frames that fit a 25 MB response cap (with a note when it had to drop older ones) — pass a smallerlimitto page.- Hidden windows are skipped: a frame is only captured while the target window is visible (a native capture of a hidden window would return stale or another window’s pixels), so a hidden period shows up as a gap, not a wrong image.
stop(or the 30-minute auto-stop) also ends the recording thatwith_eventsstarted; a recording you started separately withrecording startis left alone. The stopped session stays readable viarecording get_events/export.- Frames come from the native screenshot path, so the platform limits of
screenshotapply (Linux needs X11/XWayland).
Example:
{"action": "start", "interval_ms": 250, "max_frames": 40, "with_events": true}
{"action": "status"}
{"action": "stop"}
{"action": "frames", "limit": 10}
Backend Introspection (Victauri-Exclusive)
These tools exploit Victauri’s position inside the Rust process to provide insights and control that browser-external tools like CDP cannot access.
introspect
Deep backend introspection — command performance profiling, IPC contract testing, coverage analysis, startup timing, capability auditing, process enumeration, and event bus monitoring.
| Action | Parameters | Description |
|---|---|---|
command_timings | slow_threshold_ms | Per-command execution timing stats (min/max/avg/p95) |
coverage | — | Which registered commands have been called this session |
command_catalog | — | Per-command argument + result shapes mined from the live IPC log, merged with the registry — real call/return schemas even when the app doesn’t use #[inspectable] (where get_registry is names-only) |
contract_record | command, args | Record a command’s response shape as baseline |
contract_check | — | Check all recorded contracts for schema drift |
contract_list | — | List all recorded contract baselines |
contract_clear | — | Clear all recorded contract baselines |
startup_timing | — | Victauri plugin initialization phase-by-phase timing breakdown |
capabilities | — | Tauri v2 capabilities, security config (CSP, freeze_prototype), plugins, and window definitions |
db_health | db_path | Bounded, read-only SQLite diagnostics (see below) |
plugin_state | — | Victauri plugin internal state: event counts, registry, recording, faults, timings, uptime |
processes | — | Host process + child processes (sidecars, background workers) with PID, name, and memory |
plugin_tasks | — | Victauri’s spawned async tasks (MCP server, event drain) with active/finished counts |
event_bus | — | All captured Tauri events (automatically intercepted) + app events from EventLog |
event_bus_clear | — | Clear both event bus and event log |
db_health report. Opens the database read-only and returns: journal_mode,
page_count, page_size, db_size_mb, freelist_count; the table list with
per-table row_count (counted under a 5 s budget — tables not reached get
row_count: null and row_counts_complete: false; the listing is capped and flagged by
tables_truncated); and integrity_check from SQLite PRAGMA quick_check under its own
5 s budget. When the file may contain virtual tables and the host app has registered a
non-built-in virtual-table module, the check runs table by table over ordinary tables only
(integrity_check_kind: "quick_check (per table)", reason in integrity_check_note), so no
app-registered module code runs; otherwise integrity_check_note is null. On a large
database quick_check may report
"not completed: … exceeded its budget …" — that means unknown, not corrupt. It never
checkpoints or inspects the -wal file (wal_checkpoint is reported as “not run
(read-only diagnostics)” in WAL mode).
Examples:
{"action": "command_timings", "slow_threshold_ms": 100}
{"action": "coverage"}
{"action": "command_catalog"}
{"action": "contract_record", "command": "get_settings"}
{"action": "contract_check"}
{"action": "startup_timing"}
{"action": "capabilities"}
{"action": "db_health"}
{"action": "plugin_state"}
{"action": "processes"}
{"action": "plugin_tasks"}
{"action": "event_bus"}
fault
Probe a backend command handler under failure for chaos engineering.
Scope: faults apply only to commands you run via the
invoke_commandtool — they do not intercept the app’s real user-driven IPC (window.__TAURI_INTERNALS__.invoke), which Tauri serves below the JS layer Victauri can reach. Usefaultto test a handler’s error path when you drive it (e.g. “does my error branch return the right shape on a DB failure?”). It does not reproduce a failure a user clicking the UI would experience — that path is not interceptable cross-platform without CDP.
| Action | Parameters | Description |
|---|---|---|
inject | command, fault_type, delay_ms, error_message, max_triggers | Add a fault rule |
list | — | List all active fault injection rules |
clear | command | Remove a specific fault rule |
clear_all | — | Remove all fault rules |
Fault types:
delay— sleepdelay_ms(max 120 000), then run the command normally.error— return[FAULT INJECTED] command '<name>': <error_message>without running the command.drop— return{}without running the command.corrupt— run the command for real (side effects happen), then return a fixed marker instead of its result:{"__corrupted":true,"original_length":<n>,"fault":"corrupt"}.
Rules auto-expire 15 minutes after they are injected (so a forgotten fault cannot
sabotage a later run — re-inject to refresh) or after max_triggers hits
(0 = unlimited).
Examples:
{"action": "inject", "command": "get_settings", "fault_type": "delay", "delay_ms": 2000}
{"action": "inject", "command": "save_data", "fault_type": "error", "error_message": "disk full"}
{"action": "inject", "command": "fetch_feed", "fault_type": "drop", "max_triggers": 3}
{"action": "list"}
{"action": "clear_all"}
explain
Natural-language narration of what happened in the app. Aggregates recent events over a time window and produces human-readable summaries, causal chains, or diffs.
Event source. While a recording is active, explain reads the recorder-fed event
log. When nothing is recording, it reads the JS bridge’s own event stream on demand
(IPC, DOM mutations, console, network, navigation) for the requested window — without
storing it, so a later recording never double-counts. Victauri’s own internal traffic is
filtered out. Network requests are counted as state changes and navigations as
window events in the tallies.
| Action | Parameters | Description |
|---|---|---|
summary | seconds | Aggregate events over N seconds (default: 30) into a narrative with type counts |
last_action | seconds | Map recent events to a causal chain with “ → “ separators (default: 5s) |
diff | seconds | Count IPC calls, DOM changes, errors, and interactions over N seconds (default: 10) |
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | One of: summary, last_action, diff |
seconds | integer | no | How many seconds to look back |
webview_label | string | no | Target webview window |
Examples:
{"action": "summary"}
{"action": "summary", "seconds": 60}
{"action": "last_action"}
{"action": "diff", "seconds": 15}
animation
Quantitative, deterministic, cross-platform access to the webview’s animation engine via the Web Animations API. Works identically on WebView2 / WKWebView / WebKitGTK with no CDP. Closes the last blind spot in agent perception — screenshots are frozen instants; this lets an agent perceive time-based behaviour.
| Action | Parameters | Description |
|---|---|---|
list | webview_label | getAnimations() introspection: declared timing (duration/delay/easing/iterations), computed progress, keyframes, play state, and the animating target. An animation only appears while running/pending — trigger it first. |
scrub | selector, points, capture, webview_label | Pauses the target’s animation and seeks it to N evenly-spaced points (await animation.ready + double-rAF freezes each frame), returning the exact geometry curve (rect + transform + opacity per point). With capture: true, also returns a single contact-sheet filmstrip PNG of the whole arc plus a manifest. CSS-driven animations only (JS/rAF animations are not seekable — errors clearly and suggests sample). |
sample | record, selector, webview_label | Real-time requestAnimationFrame recorder, decoupled from the blocking eval so event-triggered sweeps are catchable: record: true arms a watcher, trigger the animation, then record: false reads the measured per-frame curve, jank stats (dropped frames, max frame gap), and declared-vs-measured duration. Works for any animation including JS/rAF-driven ones. |
restore | boolean | no |
cols | integer | no |
clear | boolean | no |
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
action | string | yes | One of: list, scrub, sample |
selector | string | for scrub/sample | CSS selector of the animating element |
points | integer | no | Number of evenly-spaced seek points for scrub (default: 6) |
capture | boolean | no | For scrub: also return a filmstrip PNG of the arc |
record | boolean | for sample | true to arm the recorder, false to read results |
webview_label | string | no | Target webview window |
Filmstrip + transparent windows:
scrub’s filmstrip uses native window capture, which cannot see transparent / GPU-composited windows (no DWM redirection surface). On such a window the capture now fails with an actionable error rather than returning a blank frame — use an opaque window, orlist/sample/scrubwithoutcapture.
Examples:
{"action": "list"}
{"action": "scrub", "selector": "#sweep-toast", "points": 6, "capture": true}
{"action": "sample", "selector": "#sweep-toast", "record": true}
{"action": "sample", "record": false}