Skip to main content
Glama
Radek44

MCP Tauri Automation

by Radek44
README.md
# MCP Tauri Automation

A small local MCP server for testing Tauri desktop apps. Connect to a test app,
inspect a bounded UI snapshot, click/type/wait, and capture a screenshot.

**2.0.0-rc.2** requires Node.js 22 or newer. It supports an already running
embedded WebDriver server (including macOS) and the external tauri-driver flow.
This is a release candidate; see [CHANGELOG.md](CHANGELOG.md) for compatibility
changes and the distinction between automated protocol tests and native proof.
The rc.2 candidate passed synthetic native acceptance on Linux ARM64 with both
embedded and external drivers; see [native evidence](https://github.com/Radek44/mcp-tauri-automation/blob/main/test/native-fixture/ACCEPTANCE.md).
The earlier rc.1 embedded flow passed a macOS arm64 Saga smoke on September 7,
2026. Windows and the new rc.2 capabilities on macOS remain unverified.

## Install

From source:

~~~bash
git clone https://github.com/Radek44/mcp-tauri-automation.git
cd mcp-tauri-automation
npm ci
npm test
~~~

The server uses stdio. Start it with Node and an absolute path:

~~~bash
node /absolute/path/to/mcp-tauri-automation/dist/index.js
~~~

Register with Codex ([official configuration guide](https://learn.chatgpt.com/docs/extend/mcp)):

~~~bash
codex mcp add tauri-automation -- node /absolute/path/to/mcp-tauri-automation/dist/index.js
~~~

Or Claude Code:

~~~bash
claude mcp add --transport stdio tauri-automation --scope user -- node /absolute/path/to/mcp-tauri-automation/dist/index.js
~~~

Any stdio MCP client can use the same command and arguments. No provider API key
or additional model service is needed. Restart/reconnect the MCP client after
changing its configuration.

## Connect a test app

### Embedded WebDriver — macOS, Linux, Windows

Build your app with the optional tauri-plugin-wdio-webdriver test plugin.
Launch that build yourself with a dedicated port and isolated test data:

~~~bash
TAURI_WEBDRIVER_PORT=4445 WDIO_EMBEDDED_SERVER=true /absolute/path/to/test-app
~~~

Then call connect_app with {"port":4445}. It creates a new session on
127.0.0.1; it does not discover apps, take over an existing session, or launch
a process. close_app deletes that session; your externally launched app remains
running. The launcher remains responsible for stopping the exact process.

The WebDriver plugin belongs in test builds, and visible UI tests need an
unlocked desktop. See the [Tauri WebDriver guide](https://v2.tauri.app/develop/tests/webdriver/)
and [plugin setup](https://webdriver.io/docs/desktop-testing/tauri/plugin-setup/).

### External tauri-driver — Linux and Windows

Install and run the platform driver as described in
[Tauri's manual setup](https://v2.tauri.app/develop/tests/webdriver/manual/).
Then call launch_app with {"appPath":"/absolute/path/to/app"}.
The driver owns launching the app; deleting its session requests teardown.
Native process behavior ultimately belongs to that driver.

## Tools

| Tool | Inputs / behavior |
| --- | --- |
| connect_app | Optional port; create a session on a running embedded server. |
| launch_app | Optional appPath, args, env; use TAURI_APP_PATH when path is omitted. |
| close_app | Delete the owned session. |
| get_app_state | Session metadata and a live title/location probe; reports failed health checks. |
| inspect_ui | Optional CSS selector, limit (1–100, default 20), maxTextLength (0–1000, default 160). |
| click_element | CSS selector only; standard primary WebDriver click. Button overrides are rejected. |
| focus_element | One unique CSS target; scroll, focus and verify without typing or activation. |
| type_text | selector, text, optional clear (false appends, true clears then types). |
| wait_for_element | selector, optional timeout and state: attached (default), visible, or hidden; optional conditions described below. |
| get_element_text | CSS selector; maximum 12,000 characters, otherwise use a narrower selector. |
| capture_screenshot | Native MCP PNG by default; returnBase64:false saves a local file. Optional basename filename and timeout (1–60,000 ms). |
| execute_tauri_command | Compatibility/debug tool: command, optional args; requires an enabled global Tauri invoke bridge. |

inspect_ui returns bounded text, role/name hints, enabled/visible/focused state,
viewport intersection, rectangles, and own/effective opacity. Truncation flags
disclose omitted results. It does not return HTML, input values, application
stores, or an accessibility-tree conformance claim. A screenshot is still
needed for visual judgment; geometry does not prove appearance or interaction.

Start with inspect_ui, narrow the selector if necessary, perform the action,
then use wait_for_element or a targeted read to confirm the result.
Capture images when they answer a visual question. This reduces repetitive
diagnostic calls and large DOM dumps; no measured model-token saving is claimed.

For state waits, `conditions` accepts `textEquals`, `enabled`, and `ariaBusy`;
all supplied conditions must match. Conditions cannot accompany `hidden`.
Text concatenates public visible text nodes in DOM order, preserving inline
adjacency, then normalizes whitespace. It excludes form/editable contents and
is not a full rendered-text or accessibility algorithm. Each observation is
bounded to 2,000 text nodes and 12,000 raw text characters, including excluded
nodes; narrow the selector when that budget is insufficient. `ariaBusy:false`
requires an explicit `aria-busy="false"`. Each poll reacquires the element,
and all observations share one deadline.

A screenshot timeout overrides the configured request timeout for that call
only. Omitting it uses the configuration; zero does not mean unlimited.
Right/middle clicks remain deferred: the tested native WebKit driver can emit
an unintended primary click. See [native acceptance](test/native-fixture/ACCEPTANCE.md).

All calls execute in arrival order, including schema validation. Cancellation
while queued prevents execution; an input operation already started is allowed
to finish and is never replayed. Action requests are never automatically retried.
If an action times out, it may already have happened: inspect before retrying.
If session creation has an unknown outcome, restart the isolated app/driver
and this MCP server before creating another session. A failed session deletion
retains ownership so close_app can retry cleanup.

## Configuration

All variables are optional. Invalid numeric configuration fails startup.

| Variable | Default |
| --- | --- |
| TAURI_APP_PATH | No default |
| TAURI_WEBDRIVER_PORT | 4444; connect_app.port overrides it |
| TAURI_DEFAULT_TIMEOUT | 5000 ms, range 1–60,000; per WebDriver request |
| TAURI_SCREENSHOT_DIR | ./screenshots, resolved when the MCP process starts |

Only 127.0.0.1 is used; redirects are rejected. Responses are capped at 1 MiB,
or 16 MiB for screenshot JSON. Screenshot filenames cannot include directories;
existing files are never overwritten. TAURI_DRIVER_PATH was never used to
spawn a driver and remains unnecessary.

UI text, names, titles, and images can contain user content. Use disposable test
data and expose only the tools appropriate to the task. Page locations omit
credentials, query strings, and fragments; this is not a general secret scanner.

For Saga/Vuea, use this server for native UI evidence with a test-owned data
directory. Use SagaDoc's own validated MCP tools for document authoring and its
normal review controls for human decisions. Exclude execute_tauri_command
from that automation profile; it must not become a shortcut around the
document service or human Finish Session boundary.
See [the local example](examples/codex.toml) and [Saga smoke recipe](examples/saga.md).

## Development

~~~bash
npm ci
npm run verify
~~~

The gate builds TypeScript, runs Node's test runner against local fake WebDriver
servers and real MCP transports, audits runtime dependencies, and checks the
package contents. It does not require a desktop session. Native testing is
separate and must identify the app build, isolated data, platform, and results.

The runtime uses Node fetch for the narrow W3C command set; it does not download
browsers or require WebdriverIO. Diagnostics go to stderr; stdout carries MCP.
The package contains the compiled runtime, docs/examples, and native probe.

MIT license. Contributions welcome; keep additions tied to a demonstrated
workflow and include a deterministic failure/success oracle.

TDQS

A3.6/5.0

Scored across 9 tools

Disambiguation5/5

Each tool has a clearly distinct purpose with no significant overlap. The tools cover different aspects of Tauri automation: launching/closing apps, UI interaction (clicking, typing, waiting), element inspection, screenshot capture, and command execution. The descriptions make it easy to distinguish between similar tools like click_element and wait_for_element.

Naming Consistency5/5

All tools follow a consistent verb_noun naming pattern with snake_case throughout. The naming is predictable and readable: capture_screenshot, click_element, close_app, execute_tauri_command, get_app_state, get_element_text, launch_app, type_text, wait_for_element. There are no deviations in naming conventions.

Tool Count5/5

With 9 tools, this is well-scoped for Tauri automation. Each tool earns its place by covering essential operations: app lifecycle management, UI interaction, element inspection, and command execution. The count is neither too sparse nor bloated, providing comprehensive coverage without redundancy.

Completeness4/5

The tool set provides excellent coverage for core Tauri automation workflows including app control, UI interaction, and state inspection. Minor gaps exist such as no explicit tool for navigating between pages/windows or handling file operations, but agents can work around these using existing tools like execute_tauri_command for extended functionality.

Maintenance

ActivityMaintained
ResponsivenessNo issues