Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Getting Started

Get Victauri running in your Tauri app in under 5 minutes.

Prerequisites

  • A Tauri 2.0+ application
  • Rust toolchain (stable). Victauri’s own MSRV is 1.88 (checked in CI against the committed lockfile). Your app’s dependency tree can raise that: a fresh project resolving tauri 2.12 needs Rust 1.90.
  • An MCP client (Claude Code, VS Code, or any MCP-compatible tool)

Step 1: Add the Dependency

Add victauri-plugin to your app’s src-tauri/Cargo.toml:

[dependencies]
victauri-plugin = "0.9"

The plugin runs inside your app process. In release builds, init() returns a no-op plugin (zero runtime cost) thanks to the #[cfg(debug_assertions)] gate — no feature flags needed. The crate still compiles into your binary (inert in release). For a zero compiled-footprint release, make it an optional dependency behind a Cargo feature (victauri-plugin = { version = "0.9", optional = true } + [features] victauri = ["dep:victauri-plugin"]) and gate the .plugin(...) call with #[cfg(feature = "victauri")]. Do not move it to [dev-dependencies] — dev-dependencies are only visible to tests/examples/benches, so the app binary would no longer compile.

Step 2: Initialize the Plugin

Add victauri_plugin::init() to your Tauri builder in src-tauri/src/main.rs:

fn main() {
    tauri::Builder::default()
        .plugin(victauri_plugin::init())
        // ... your other plugins and setup
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

That’s it. In debug builds, this starts an MCP server on 127.0.0.1:7373. In release builds, it’s a no-op.

Step 3: Add Capabilities

Add the victauri:default capability to your app’s capabilities file. Create or edit src-tauri/capabilities/default.json:

{
  "identifier": "default",
  "windows": ["*"],
  "permissions": [
    "core:default",
    "victauri:default"
  ]
}

Without this capability, the Tauri permission system silently blocks IPC callbacks and the plugin cannot interact with your webviews.

Step 4: Connect via MCP

Create a .mcp.json file in your project root (victauri init writes this for you):

{
  "mcpServers": {
    "victauri": {
      "command": "victauri",
      "args": ["bridge"]
    }
  }
}

This connects through the victauri bridge — a stdio proxy that discovers the running app’s port at connect time, re-discovers it across restarts, and reads the auth token automatically from the discovery directory. Because the port is resolved dynamically, the agent always reaches the right app — even after a rebuild, or when several Victauri apps are running. (Requires the CLI on your PATH: cargo install victauri-cli.)

Multiple apps running at once

Pin the bridge to a specific app by its Tauri bundle identifier — victauri init bakes this in automatically when it can read your tauri.conf.json:

{
  "mcpServers": {
    "victauri": {
      "command": "victauri",
      "args": ["bridge", "--app", "com.your.app"]
    }
  }
}

Authentication

Auth is enabled by default (the token is auto-generated and auto-discovered — the bridge reads it for you, so no manual header is needed). To pin a fixed token for CI, set it on the builder with .auth_token("…") (or the VICTAURI_AUTH_TOKEN env var).

Connecting with a raw "url": "http://127.0.0.1:7373/mcp" also works, but it hardcodes a port — if that port is taken (another app, a leftover instance) your agent can silently bind the wrong app. Prefer the bridge.

Step 5: Verify It Works

With your app running, check the health endpoint:

Auth is on by default, so every endpoint except /health needs the Bearer token. The plugin writes it (and the bound port) to a per-process, per-user discovery directory <root>/<pid>/token (and .../port), where <pid> is the app’s process id and <root> is %TEMP%\victauri on Windows; on macOS/Linux it is $XDG_RUNTIME_DIR/victauri when that directory is private to you, else <temp>/victauri-<uid> (<temp> = $TMPDIR or /tmp), or ~/.local/state/victauri ($XDG_STATE_HOME/victauri) if that one is unusable.

curl http://127.0.0.1:7373/health
# Returns: ok  (unauthenticated liveness probe)

TOKEN=$(cat "${TMPDIR:-/tmp}"/victauri/*/token | head -n1)   # single running app
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:7373/info
# Returns: {"name":"victauri","port":7373,"protocol":"mcp","version":"0.9.0",...}

Or use the Victauri CLI:

cargo install victauri-cli
victauri check

Optional: Register Commands

To enable command discovery and ghost command detection, annotate your Tauri commands with #[inspectable] and register them.

The code #[inspectable] generates refers to victauri_core::... by path, so your app crate must also depend on victauri-core directly (victauri init adds it for you) — otherwise every annotated command fails to compile with E0433: failed to resolve: use of undeclared crate victauri_core:

[dependencies]
victauri-plugin = "0.9"
victauri-core = "0.9"
use victauri_plugin::inspectable;

#[inspectable(description = "Greet a user", intent = "greeting")]
#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}!", name)
}

fn main() {
    tauri::Builder::default()
        .plugin(victauri_plugin::init())
        .invoke_handler(tauri::generate_handler![greet])
        .setup(|app| {
            victauri_plugin::register_commands!(app, greet__schema());
            Ok(())
        })
        .run(tauri::generate_context!())
        .unwrap();
}

Optional: REST API

All 35 tools are also available via a REST API with no MCP handshake at all:

# List available tools
curl -H "Authorization: Bearer $TOKEN" http://127.0.0.1:7373/api/tools

# Execute a tool directly
curl -X POST http://127.0.0.1:7373/api/tools/eval_js \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"code": "document.title"}'

Next Steps