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.
These tools access the Rust backend directly — no webview proxy, no JavaScript evaluation.
Get application configuration, directory paths, environment, discovered databases, and process info.
Parameters: None required.
Returns: {config, paths, databases, process, environment}
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 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.
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.
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.
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).
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 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"}}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.
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"}
}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.
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 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, ...).
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 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 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"}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 real OS process memory usage.
Parameters: None.
Returns: {working_set_bytes, peak_working_set_bytes, page_fault_count, page_file_bytes}
Get plugin version, uptime, configuration, and capabilities.
Parameters: None.
Environment diagnostics for a webview: service workers, closed shadow DOM, iframes, DOM size, CSP, and IPC capture completeness.
Parameters: webview_label (optional).
info.ipc_capture says which Tauri IPC transport the page uses and how much of it Victauri sees:
| Field | Values |
|---|---|
transport |
fetch (custom protocol, desktop default) · post_message_fallback (Tauri switched after a custom-protocol failure) · post_message (always postMessage, e.g. Android) · unknown (no IPC yet) |
capture |
full · requests_only (responses unobservable; calls logged with status unknown) · none (switch detected, traffic invisible) · unknown |
fetch_calls, post_message_calls, post_message_responses |
counters for this page |
fallback_detected_via, fallback_at |
console_warning / custom_protocol_blocked, and when |
note |
plain-language explanation |
When Tauri has switched to postMessage, warnings includes ipc-postmessage-fallback. The same
object is attached as ipc_capture to detect_ghost_commands, check_ipc_integrity, and
introspect coverage / command_catalog / command_timings.
Compound tools use an action parameter to select the specific operation.
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"}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 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"}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"}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"}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"}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 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"}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 (calls captured on Tauri's postMessage fallback carry transport: "post_message") |
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.
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.A
blockrule on a Tauri IPC URL switches the page to postMessage IPC. The blocked fetch rejects, which Tauri treats as a broken custom protocol: it retries that call overwindow.ipc.postMessageand uses postMessage for the rest of the page's life (until a reload). Victauri still captures those calls (transport: "post_message") andget_diagnosticsreports the switch.
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}These tools exploit Victauri's position inside the Rust process to provide insights and control that browser-external tools like CDP cannot access.
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"}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"}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}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}