A comprehensive Tauri plugin and MCP server that bridges AI agents (Claude Code, Cursor, Cline, etc.) with your Tauri desktop applications, enabling intelligent debugging, testing, and automation capabilities.
The Model Context Protocol (MCP) is an open protocol that standardizes how AI assistants interact with external systems. This plugin implements MCP for Tauri applications, allowing AI agents to:
- Debug visually by taking screenshots and analyzing UI state
- Automate testing through simulated user interactions
- Inspect application state via DOM access and storage inspection
- Execute JavaScript in the application context for advanced debugging
- Control windows programmatically for multi-window testing scenarios
Traditional debugging requires manual reproduction of issues and visual inspection. With this MCP plugin, AI agents can:
- See what you see - Take screenshots to understand visual bugs
- Do what you do - Simulate clicks, typing, and navigation
- Know what's inside - Access DOM, localStorage, and application state
- Fix autonomously - Execute JavaScript to test fixes in real-time
This is particularly powerful for:
- Debugging visual regressions - AI can compare screenshots before/after changes
- Automated UI testing - Generate and execute test scenarios
- Cross-platform validation - Verify behavior across different OS windows
- State inspection - Diagnose issues by examining storage and DOM structure
This plugin is based on the original tauri-plugin-mcp by P3GLEG, and has been heavily modified and improved with:
- Console & Error Tracking: New tools for capturing console logs and exceptions
- Comprehensive Documentation:
- Complete Quick Start guide
- Tool Parameters Reference
- AI Agent Usage Guide with debugging workflows
- Detailed Development Workflow guide
- Common debugging patterns and best practices
- Enhanced Developer Experience:
- Pre-commit checklist
- Step-by-step guide for adding new tools
- Code templates for Rust and TypeScript
- Testing strategies and examples
- Production-Ready: Extensive troubleshooting guide and error handling
- Better Organization: Structured documentation with clear examples
Original work: P3GLEG/tauri-plugin-mcp License: MIT (see LICENSE)
We're grateful to P3GLEG for creating the foundation of this plugin. This fork aims to provide a more comprehensive, production-ready solution with extensive documentation for both AI agents and human developers.
New to MCP? Start with our comprehensive guides:
- Quick Start Guide - Get running in 15 minutes
- Integration Guide - Complete setup and configuration
- Testing Guide - Master AI-powered testing
- Docs Overview - Documentation hub
Below: Quick reference for the main README. See the guides above for step-by-step instructions.
Want to get started immediately? Here's the minimal setup:
# 1. Add to your Tauri app's Cargo.toml
tauri-plugin-mcp = { path = "../.tauri-plugin-mcp" }
# 2. Register in src-tauri/src/main.rs (debug builds only!)
#[cfg(debug_assertions)]
{
use tauri_plugin_mcp::PluginConfig;
builder = builder.plugin(tauri_plugin_mcp::init_with_config(
PluginConfig::new("YourApp".to_string())
.start_socket_server(true)
.socket_path("/tmp/tauri-mcp.sock") // macOS/Linux
));
}
# 3. Build the MCP server
cd .tauri-plugin-mcp/mcp-server-ts
pnpm install && pnpm build
# 4. Configure your AI agent (Claude Code/Cursor/Cline)
# Add to ~/.config/claude/claude_code_config.json:
{
"mcpServers": {
"tauri-mcp": {
"command": "node",
"args": ["/absolute/path/to/.tauri-plugin-mcp/mcp-server-ts/build/index.js"]
}
}
}
# 5. Start your Tauri app and test!
pnpm run tauri devNow AI agents can debug your app! See Getting Started for detailed setup.
| Tool | Purpose | Common Use Case |
|---|---|---|
| take_screenshot | Capture window or element images | Visual regression testing, bug reporting |
| get_dom | Retrieve HTML structure | Debugging dynamic content, state inspection |
| execute_js | Run JavaScript in webview | State inspection, API calls, framework access |
| get_element_position | Find element coordinates | Preparing for mouse clicks, layout debugging |
| inject_console_capture | Enable console log collection | Capture console.log/error/warn messages |
| get_console_logs | Retrieve captured logs | Debugging runtime errors, log analysis |
| inject_error_tracker | Enable exception tracking | Capture unhandled errors, promise rejections |
| get_exceptions | Retrieve tracked errors | Understanding crash causes, error patterns |
| local_storage_get | Read localStorage item | Session debugging, auth token inspection |
| local_storage_set | Write localStorage item | Testing state persistence, setting up test data |
| local_storage_remove | Delete localStorage item | Cleanup, testing deletion flows |
| local_storage_clear | Clear all localStorage | Reset to clean state |
| local_storage_get_all | Retrieve all storage | Complete state inspection |
| manage_window | Control window properties | Multi-window testing, positioning, focus |
| health_check | Verify plugin connectivity | Connection diagnostics, startup verification |
| ping | Simple connectivity test | Basic health check |
See Features for detailed documentation of each tool.
┌─────────────────────────────────────────────────────────────┐
│ AI Agent (Claude/Cursor) │
│ (MCP Client via stdio/SSE) │
└───────────────────────────┬─────────────────────────────────┘
│
│ MCP Protocol
│ (JSON-RPC)
│
┌───────────────────────────▼─────────────────────────────────┐
│ MCP Server (TypeScript - Node.js) │
│ • Implements MCP protocol │
│ • Exposes tools to AI agents │
│ • Manages socket connection lifecycle │
└───────────────────────────┬─────────────────────────────────┘
│
│ IPC Socket or TCP
│ (JSON commands)
│
┌───────────────────────────▼─────────────────────────────────┐
│ Tauri Plugin (Rust - Socket Server) │
│ • Listens on Unix socket/Named pipe/TCP │
│ • Processes JSON commands │
│ • Executes Tauri API calls │
└───────────────────────────┬─────────────────────────────────┘
│
│ Tauri IPC
│
┌───────────────────────────▼─────────────────────────────────┐
│ Your Tauri Application │
│ • Frontend (React/Vue/Svelte/etc.) │
│ • Webview rendering │
│ • Application logic │
└─────────────────────────────────────────────────────────────┘
- Socket Server (
socket_server.rs): Manages persistent connections via IPC or TCP - Tool Implementations (
src/tools/*.rs): Individual Rust modules for each capability - Tauri Integration: Hooks into Tauri's window and webview APIs
- Client Connection (
client.ts): Connects to the Tauri plugin's socket - Tool Registry (
src/tools/*.ts): Maps MCP tool calls to socket commands - Protocol Handler: Implements MCP specification for AI agent communication
AI Request → MCP Server → Socket → Tauri Plugin → Tauri API → App
← ← ← ← ←
AI Response JSON Rust Handler Result Effect
Capture high-quality images of any Tauri window with pixel-perfect accuracy.
Use Cases:
- Visual regression testing
- Bug reporting with context
- UI state verification
- Automated documentation generation
Capabilities:
- Configure JPEG quality (1-100)
- Specify exact dimensions or use window size
- Target specific windows in multi-window apps
- Base64 or file output
Example: AI can take a screenshot, analyze the UI, and tell you "The submit button is misaligned 3px to the right."
Full programmatic control over window lifecycle and properties.
Capabilities:
- Position: Set x, y coordinates
- Size: Resize width and height
- State: Minimize, maximize, restore, focus
- Multi-window: Target specific windows by label
- Visibility: Show, hide, bring to front
Use Cases:
- Multi-window testing scenarios
- Window positioning tests
- Focus management debugging
- Screen layout automation
Retrieve the complete HTML structure and content from any webview.
Capabilities:
- Full DOM tree extraction
- JavaScript evaluation context
- Element inspection
- Computed styles and properties (via JS execution)
Use Cases:
- Debugging dynamic content
- Validating data-binding
- Analyzing generated markup
- State inspection without DevTools
Example: AI can read the DOM, find a specific element by selector, and verify its content or attributes.
Simulate realistic mouse interactions with pixel-perfect accuracy.
Capabilities:
- Click: Left, right, middle button clicks
- Double-click and triple-click
- Movement: Absolute and relative positioning
- Scrolling: Vertical and horizontal with delta control
- Hold and drag operations
Use Cases:
- Automated UI testing
- Click-through flow validation
- Hover state testing
- Drag-and-drop testing
Example: AI can click a button, verify the result via screenshot, and continue a multi-step workflow.
Programmatically input text into focused elements with keyboard simulation.
Capabilities:
- Type into any focused input/textarea
- Simulate keyboard events
- Special characters and modifiers
- Paste large text blocks
Use Cases:
- Form filling automation
- Input validation testing
- Search functionality testing
- Text editor interaction
Example: AI can fill out a form, submit it, and verify the submission success.
Run arbitrary JavaScript code directly in your application's webview context.
Capabilities:
- Full access to window scope
- Return values to the plugin
- Async/await support
- Error handling and reporting
Use Cases:
- Advanced state inspection
- Dynamic testing scenarios
- Direct API calls
- Framework-specific interactions (React state, Vue store, etc.)
Example: AI can execute window.store.getState() to inspect Redux state, or call application methods directly.
Complete CRUD operations on browser localStorage.
Capabilities:
- Get: Retrieve individual items or all entries
- Set: Add or update key-value pairs
- Remove: Delete specific keys
- Clear: Wipe all storage
Use Cases:
- State persistence testing
- Cache debugging
- User preferences inspection
- Session data validation
Example: AI can check localStorage for authentication tokens, verify expiration, and test refresh flows.
Simple connectivity test to verify the plugin is responsive.
Use Cases:
- Connection health monitoring
- Startup verification
- Debugging connection issues
- Integration testing
Detailed parameter specifications for each MCP tool:
{
quality?: number; // JPEG quality 1-100 (default: 90)
width?: number; // Target width in pixels (optional)
height?: number; // Target height in pixels (optional)
window_label?: string; // Target window (default: main window)
}{
window_label?: string; // Target window (default: main window)
}{
selector: string; // CSS selector (required)
window_label?: string; // Target window (default: main window)
}{
script: string; // JavaScript code to execute (required)
window_label?: string; // Target window (default: main window)
}{
window_label?: string; // Target window (default: main window)
}{
level?: "log" | "warn" | "error" | "info" | "debug"; // Filter by level
since?: number; // Unix timestamp - only logs after this time
limit?: number; // Max number of logs to return
window_label?: string; // Target window
}{
window_label?: string; // Target window (default: main window)
}{
since?: number; // Unix timestamp - only errors after this time
limit?: number; // Max number of errors to return
window_label?: string; // Target window
}{
key: string; // Storage key (required)
window_label?: string; // Target window
}{
key: string; // Storage key (required)
value: string; // Value to store (required)
window_label?: string; // Target window
}{
key: string; // Storage key (required)
window_label?: string; // Target window
}{
window_label?: string; // Target window
}{
window_label?: string; // Target window
}{
action: "resize" | "move" | "focus" | "minimize" | "maximize" | "restore";
window_label?: string; // Target window (default: main window)
x?: number; // X position for move action
y?: number; // Y position for move action
width?: number; // Width for resize action
height?: number; // Height for resize action
}{} // No parameters required{} // No parameters required- Rust (latest stable): For Tauri development
- Node.js 18+: For the MCP server
- pnpm (recommended) or npm: Package management
- Tauri CLI:
cargo install tauri-cli
First, build both the Rust plugin and TypeScript MCP server:
# Install dependencies
pnpm install
# Build the Rust plugin and TypeScript server
pnpm run build && pnpm run build-pluginThis will:
- Compile the Rust plugin (
tauri-plugin-mcp) - Build the TypeScript MCP server (
mcp-server-ts/build/)
If you don't have a Tauri app yet, follow Tauri's quickstart guide.
In your app's src-tauri/Cargo.toml, add the plugin dependency:
[dependencies]
tauri-plugin-mcp = { path = "../path/to/tauri-plugin-mcp" }
# Or from a git repository:
# tauri-plugin-mcp = { git = "https://github.com/yourusername/tauri-plugin-mcp" }In your app's package.json, add the guest bindings:
{
"dependencies": {
"tauri-plugin-mcp": "file:../path/to/tauri-plugin-mcp"
}
}IMPORTANT SECURITY NOTE: Only enable MCP in development builds. This plugin provides deep access to your application and should NEVER be included in production.
In your src-tauri/src/main.rs (or lib.rs for mobile):
use tauri_plugin_mcp;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let mut builder = tauri::Builder::default();
// Only enable MCP in development builds
#[cfg(debug_assertions)]
{
use tauri_plugin_mcp::PluginConfig;
builder = builder.plugin(tauri_plugin_mcp::init_with_config(
PluginConfig::new("YourAppName".to_string()) // Must match your app's window name
.start_socket_server(true)
// Choose ONE connection mode:
// Option 1: IPC Socket (Default - Recommended)
.socket_path("/tmp/tauri-mcp.sock") // macOS/Linux
// .socket_path("\\\\.\\pipe\\tauri-mcp") // Windows
// Option 2: TCP Socket (Useful for Docker/Remote debugging)
// .tcp("127.0.0.1".to_string(), 4000)
));
log::info!("MCP plugin enabled for development");
}
builder
.run(tauri::generate_context!())
.expect("error while running tauri application");
}Configuration Options:
PluginConfig::new(app_name): The application name used to identify windows (must match yourtauri.conf.jsontitle).start_socket_server(true): Enables the socket server (required).socket_path(path): IPC socket location (Unix socket on macOS/Linux, Named Pipe on Windows).tcp(host, port): TCP socket configuration (alternative to IPC)
Platform-Specific Socket Paths:
- macOS/Linux:
/tmp/tauri-mcp.sock(or any path in/tmp) - Windows:
\\\\.\\pipe\\tauri-mcp(Named Pipe format)
The MCP server acts as a bridge between AI agents (Claude Code, Cursor, Cline) and your Tauri application.
cd mcp-server-ts
pnpm install
pnpm buildThis creates mcp-server-ts/build/index.js, the entry point for AI agents.
Add the MCP server to your AI agent's configuration file:
For Claude Code (~/.config/claude/claude_code_config.json or claude_desktop_config.json):
For Cursor (.cursor/mcp-config.json):
For Cline (VSCode settings):
{
"mcpServers": {
"tauri-mcp": {
"command": "node",
"args": ["/absolute/path/to/tauri-plugin-mcp/mcp-server-ts/build/index.js"]
}
}
}Replace /absolute/path/to/ with the actual path on your system.
Best for local development with lowest overhead. Uses platform-specific inter-process communication.
Default Configuration (no env vars needed):
{
"mcpServers": {
"tauri-mcp": {
"command": "node",
"args": ["/path/to/mcp-server-ts/build/index.js"]
}
}
}Custom Socket Path:
{
"mcpServers": {
"tauri-mcp": {
"command": "node",
"args": ["/path/to/mcp-server-ts/build/index.js"],
"env": {
"TAURI_MCP_IPC_PATH": "/custom/path/to/socket"
}
}
}
}Platform-specific defaults:
- macOS/Linux:
/tmp/tauri-mcp.sock - Windows:
\\\\.\\pipe\\tauri-mcp
Use TCP when:
- Running Tauri app in Docker
- Remote debugging across network
- IPC socket permissions issues
- Testing from multiple machines
Configuration:
{
"mcpServers": {
"tauri-mcp": {
"command": "node",
"args": ["/path/to/mcp-server-ts/build/index.js"],
"env": {
"TAURI_MCP_CONNECTION_TYPE": "tcp",
"TAURI_MCP_TCP_HOST": "127.0.0.1",
"TAURI_MCP_TCP_PORT": "4000"
}
}
}
}Corresponding Tauri plugin configuration:
#[cfg(debug_assertions)]
{
builder = builder.plugin(tauri_plugin_mcp::init_with_config(
PluginConfig::new("YourApp".to_string())
.start_socket_server(true)
.tcp("127.0.0.1".to_string(), 4000) // Must match MCP server config
));
}Security Warning: TCP sockets expose your application to network connections. Use 127.0.0.1 (localhost) to prevent external access. Never use 0.0.0.0 in production-like environments.
| Variable | Default | Description |
|---|---|---|
TAURI_MCP_CONNECTION_TYPE |
ipc |
Connection mode: ipc or tcp |
TAURI_MCP_IPC_PATH |
Platform-specific | Custom IPC socket path |
TAURI_MCP_TCP_HOST |
127.0.0.1 |
TCP server host (TCP mode only) |
TAURI_MCP_TCP_PORT |
3000 |
TCP server port (TCP mode only) |
Understanding the communication flow helps with debugging and extending the plugin.
1. AI Agent sends MCP request
↓
2. MCP Server receives tool call (e.g., "take_screenshot")
↓
3. MCP Server constructs JSON command
{
"action": "take_screenshot",
"params": { "quality": 80 }
}
↓
4. Socket Client sends command to Socket Server (IPC or TCP)
↓
5. Socket Server (Rust) receives JSON command
↓
6. Router dispatches to appropriate tool handler
↓
7. Tool handler calls Tauri API
(e.g., window.screenshot())
↓
8. Result returned as JSON
{
"success": true,
"data": { "image": "base64..." }
}
↓
9. Socket Client receives response
↓
10. MCP Server formats response per MCP protocol
↓
11. AI Agent receives result
Responsibilities:
- Creates and manages socket listeners (IPC or TCP)
- Accepts incoming connections with persistent support
- Deserializes JSON commands from clients
- Routes commands to appropriate tool handlers
- Serializes responses back to JSON
- Handles errors and connection lifecycle
Key Features:
- Persistent Connections: Supports multiple requests per connection
- Concurrent Clients: Handle multiple AI agents simultaneously
- Error Recovery: Graceful handling of malformed requests
- Type Safety: Strongly-typed command/response structures
Implementation:
// Simplified example
match command.action.as_str() {
"take_screenshot" => {
let params: ScreenshotParams = serde_json::from_value(command.params)?;
let result = tools::take_screenshot(&app, params).await?;
Ok(json!({ "success": true, "data": result }))
}
// ... other actions
}Responsibilities:
- Establishes connection to socket (IPC or TCP)
- Sends JSON-serialized commands
- Awaits and deserializes responses
- Implements retry logic for connection failures
- Manages connection pooling/reuse
Key Features:
- Promise-based API: Async/await support
- Automatic Reconnection: Retry failed connections
- Timeout Handling: Prevent hung requests
- Type Definitions: TypeScript interfaces for all commands
Implementation:
// Simplified example
async function sendCommand(action: string, params: any): Promise<any> {
const command = { action, params };
await socket.write(JSON.stringify(command) + '\n');
const response = await socket.readLine();
return JSON.parse(response);
}Responsibilities:
- Implements MCP protocol specification
- Registers available tools with descriptions
- Translates MCP tool calls to socket commands
- Formats responses according to MCP schema
- Handles stdio communication with AI agents
Tool Registration Example:
server.registerTool({
name: "take_screenshot",
description: "Capture a screenshot of the Tauri window",
inputSchema: {
type: "object",
properties: {
quality: { type: "number", minimum: 1, maximum: 100 }
}
}
});Before diving into specific issues, follow this diagnostic checklist:
-
Verify Tauri app is running in debug mode
pnpm run tauri dev # Look for log: "MCP plugin enabled for development" -
Check socket file exists (IPC mode only)
# macOS/Linux ls -l /tmp/tauri-mcp.sock # Windows PowerShell Get-ChildItem \\.\pipe\ | Select-String tauri-mcp
-
Test socket connectivity (TCP mode only)
# macOS/Linux nc -zv 127.0.0.1 4000 # Windows Test-NetConnection -ComputerName 127.0.0.1 -Port 4000
-
Check MCP server logs
- AI agent logs usually show MCP server stdout/stderr
- Look for connection attempts and errors
Symptoms: MCP server cannot connect to the Tauri plugin.
Causes & Solutions:
- Tauri app not running: Start your app with
pnpm run tauri dev - Socket server disabled: Verify
.start_socket_server(true)in plugin config - Mismatched connection modes: Ensure both MCP server and Tauri plugin use the same mode (IPC or TCP)
- Port mismatch (TCP): Verify port numbers match exactly:
// Tauri: .tcp("127.0.0.1".to_string(), 4000) // MCP Server env: TAURI_MCP_TCP_PORT=4000
Debug commands:
# Check if socket server is listening (TCP mode)
lsof -i :4000 # macOS/Linux
netstat -an | findstr :4000 # Windows
# Check Tauri app logs
# Look for: "Socket server started on..."Symptoms: Error mentioning socket path doesn't exist.
Causes & Solutions:
-
Socket not created: Tauri app may have failed to start the socket server
- Check Tauri logs for socket creation errors
- Verify path has write permissions (try
/tmpon Unix)
-
Wrong socket path: Ensure paths match exactly:
// Tauri .socket_path("/tmp/tauri-mcp.sock")
// MCP Server (if custom path) "env": { "TAURI_MCP_IPC_PATH": "/tmp/tauri-mcp.sock" }
-
Path cleared on reboot:
/tmpmay be cleared on system restart- Restart your Tauri app to recreate the socket
Workaround: Switch to TCP mode which doesn't use file system.
Symptoms: Socket file exists but cannot be accessed.
Causes & Solutions:
-
File permissions: Check socket file permissions
ls -l /tmp/tauri-mcp.sock # Should be readable/writable by your user -
SELinux/AppArmor (Linux): Security modules may block socket access
# Temporary disable SELinux (for testing only) sudo setenforce 0 -
Windows named pipe permissions: Ensure correct pipe name format
.socket_path("\\\\.\\pipe\\tauri-mcp") // Correct format
Workaround: Use TCP mode to avoid file system permissions.
Symptoms: Each tool call requires reconnection; slow responses.
Causes & Solutions:
- Outdated plugin version: Ensure you're using the latest version with persistent connection support
- Short timeouts: Increase client timeout settings
- Server-side errors: Check Tauri logs for panics or errors that might kill connections
Fix:
cd tauri-plugin-mcp
git pull
pnpm run build && pnpm run build-pluginSymptoms: AI agent says tool doesn't exist or returns errors.
Causes & Solutions:
-
MCP server not built: Rebuild the MCP server
cd mcp-server-ts pnpm build -
Tool not registered: Check
mcp-server-ts/src/tools/index.tsincludes the tool -
Schema mismatch: Ensure tool parameters match the expected schema
Debug:
# List available tools using MCP Inspector
cd mcp-server-ts
npx @modelcontextprotocol/inspector node build/index.js
# Click "List Tools" to see registered toolsSymptoms: Screenshot tool returns blank or all-black images.
Causes & Solutions:
-
Wrong window name: Application name must match window title
PluginConfig::new("ExactAppName".to_string()) // Must match tauri.conf.json
-
Window not focused/visible: Ensure window is visible and not minimized
-
Webview not loaded: Wait for app to fully load before taking screenshots
-
macOS permissions: Grant screen recording permission to your terminal/IDE
- System Preferences → Security & Privacy → Screen Recording
Symptoms: execute_js tool returns errors or undefined.
Causes & Solutions:
- Webview not ready: Ensure DOM is loaded before executing JS
- Syntax errors: Validate JavaScript syntax
- CSP restrictions: Content Security Policy may block inline scripts
- Return value serialization: Ensure returned values are JSON-serializable
Example:
// Bad: Returns DOM element (not serializable)
execute_js({ script: "document.getElementById('app')" })
// Good: Returns serializable data
execute_js({ script: "document.getElementById('app').textContent" })The official MCP Inspector provides a GUI for testing your server:
cd mcp-server-ts
# IPC mode (default)
npx @modelcontextprotocol/inspector node build/index.js
# TCP mode
TAURI_MCP_CONNECTION_TYPE=tcp \
TAURI_MCP_TCP_HOST=127.0.0.1 \
TAURI_MCP_TCP_PORT=4000 \
npx @modelcontextprotocol/inspector node build/index.js
# Windows (TCP mode)
set TAURI_MCP_CONNECTION_TYPE=tcp&& set TAURI_MCP_TCP_HOST=127.0.0.1&& set TAURI_MCP_TCP_PORT=4000&& npx @modelcontextprotocol/inspector node build\index.jsThe Inspector allows you to:
- List all available tools
- View tool schemas and descriptions
- Execute tools with custom parameters
- See real-time request/response logs
Test the socket connection directly:
# Test TCP socket
echo '{"action":"ping","params":{}}' | nc 127.0.0.1 4000
# Test Unix socket (macOS/Linux)
echo '{"action":"ping","params":{}}' | nc -U /tmp/tauri-mcp.sockExpected response:
{"success":true,"data":"pong"}If you're still stuck after trying these solutions:
- Check logs: Collect logs from both Tauri app and MCP server
- Minimal reproduction: Create a minimal Tauri app that reproduces the issue
- Open an issue: Include:
- Operating system and version
- Tauri version (
cargo tauri info) - Connection mode (IPC or TCP)
- Full error messages and stack traces
- Configuration files (plugin config and MCP server config)
This section provides guidance for AI agents (like Claude Code, Cursor, Cline) on how to effectively use this plugin for debugging Tauri applications.
When debugging a Tauri application, follow this systematic approach:
Always start by verifying the plugin is accessible:
// Step 1: Health check
await health_check({});
// Step 2: Ping test
await ping({});What to check:
- Plugin is loaded and responsive
- Socket connection is working
- MCP server can communicate with Tauri app
Before investigating issues, set up monitoring for runtime errors and logs:
// Enable console log capture
await inject_console_capture({});
// Enable error tracking
await inject_error_tracker({});Why this matters:
- Captures errors that occur during your investigation
- Logs provide context about what the app is doing
- Prevents missing important diagnostic information
Take a screenshot to understand what the user is seeing:
// Capture current visual state
const screenshot = await take_screenshot({ quality: 80 });
// Analyze the screenshot to understand:
// - Is the UI rendered correctly?
// - Are there visual errors?
// - Which elements are visible?Use screenshots for:
- Visual regression detection
- Confirming UI state before/after actions
- Identifying layout issues
- Documenting bugs
Examine the HTML structure to understand the application state:
// Get full DOM
const dom = await get_dom({});
// Or find specific elements
const buttonPos = await get_element_position({
selector: "button.submit"
});Look for:
- Missing or unexpected elements
- Incorrect attributes or classes
- Dynamic content issues
- Framework-specific data attributes
Inspect application state using JavaScript execution:
// Check React state (if using React)
const state = await execute_js({
script: `
const app = document.getElementById('root');
const fiber = app._reactRootContainer?._internalRoot?.current;
// Return serializable state information
JSON.stringify({ /* state data */ });
`
});
// Check localStorage for session data
const storage = await local_storage_get_all({});
// Check specific configuration
const config = await execute_js({
script: "JSON.stringify(window.__APP_CONFIG__ || {})"
});Common state sources:
- Redux store:
window.store?.getState() - Zustand:
window.useStore?.getState() - Vue store:
window.app?.$store?.state - Local/session storage
- Global configuration objects
Check console logs and errors for diagnostic information:
// Get recent error messages
const errors = await get_exceptions({
since: Date.now() - 60000, // Last minute
limit: 50
});
// Get console logs
const logs = await get_console_logs({
level: "error",
since: Date.now() - 60000
});
// Analyze patterns:
// - Are there recurring errors?
// - What was the sequence of events?
// - Are there unhandled promise rejections?Based on the information gathered, form hypotheses and test them:
// Example: Testing if a specific function exists
const hasFunction = await execute_js({
script: "typeof window.myFunction === 'function'"
});
// Example: Testing if data loaded
const dataLoaded = await execute_js({
script: `
const data = window.myData;
JSON.stringify({
exists: !!data,
length: data?.length || 0,
isEmpty: !data || data.length === 0
});
`
});
// Example: Testing localStorage state
const authState = await local_storage_get({ key: "auth_token" });-
Always verify connectivity first - Use
health_check()orping()before attempting other operations -
Set up monitoring early - Call
inject_console_capture()andinject_error_tracker()at the start of debugging sessions -
Take screenshots before and after actions - Document visual changes to understand impact
-
Use execute_js for complex queries - Batch multiple checks into a single JavaScript execution to reduce round trips
-
Check logs after each significant action - Use
get_console_logs()andget_exceptions()to catch errors immediately -
Clean up test data - Use
local_storage_clear()orlocal_storage_remove()to reset state between tests -
Be specific with selectors - Use precise CSS selectors in
get_element_position()to avoid ambiguity -
Handle errors gracefully - Wrap tool calls in try-catch and provide helpful context when operations fail
-
Don't assume the plugin is always available - Always verify connectivity, especially in new sessions
-
Don't skip visual verification - Screenshots provide critical context that logs cannot
-
Don't execute untrusted code - Only run JavaScript that you've verified is safe
-
Don't ignore TypeScript types - Use the parameter types defined in the Tool Parameters Reference
-
Don't overwhelm with requests - Batch operations when possible to reduce overhead
-
Don't forget about multi-window apps - Use
window_labelparameter to target specific windows -
Don't assume synchronous execution - All operations are async; use await properly
-
Don't leave monitoring enabled in production - Console/error tracking is for debugging only
// 1. Verify form is visible
const screenshot = await take_screenshot({});
// 2. Check form state
const formData = await execute_js({
script: `
const form = document.querySelector('form');
const formData = new FormData(form);
const obj = {};
formData.forEach((value, key) => obj[key] = value);
JSON.stringify({
action: form.action,
method: form.method,
data: obj
});
`
});
// 3. Check for validation errors
const logs = await get_console_logs({ level: "error" });
// 4. Check network-related errors (if applicable)
const exceptions = await get_exceptions({});// 1. Inspect current state
const currentState = await execute_js({
script: "JSON.stringify(window.store?.getState() || {})"
});
// 2. Check localStorage for persisted state
const persistedState = await local_storage_get({ key: "redux_state" });
// 3. Compare expected vs actual
// 4. Check for state update errors in logs
const logs = await get_console_logs({ level: "warn" });// 1. Take screenshot
const screenshot = await take_screenshot({});
// 2. Get DOM to check element attributes
const dom = await get_dom({});
// 3. Check element positioning
const position = await get_element_position({
selector: ".problematic-element"
});
// 4. Check computed styles
const styles = await execute_js({
script: `
const el = document.querySelector('.problematic-element');
const styles = window.getComputedStyle(el);
JSON.stringify({
display: styles.display,
visibility: styles.visibility,
opacity: styles.opacity,
position: styles.position,
zIndex: styles.zIndex
});
`
});// 1. Check for network errors in console
const logs = await get_console_logs({ level: "error" });
// 2. Check application state for API responses
const apiState = await execute_js({
script: `
JSON.stringify({
baseURL: window.API_BASE_URL,
lastResponse: window.__lastAPIResponse__,
pendingRequests: window.__pendingRequests__?.length || 0
});
`
});
// 3. Check localStorage for tokens
const authToken = await local_storage_get({ key: "auth_token" });
// 4. Verify token format and expiration
const tokenInfo = await execute_js({
script: `
try {
const token = localStorage.getItem('auth_token');
if (!token) throw new Error('No token');
const parts = token.split('.');
const payload = JSON.parse(atob(parts[1]));
JSON.stringify({
isExpired: payload.exp * 1000 < Date.now(),
expiresAt: new Date(payload.exp * 1000).toISOString()
});
} catch (e) {
JSON.stringify({ error: e.message });
}
`
});When tool calls fail, handle errors systematically:
try {
const result = await some_tool({ params });
} catch (error) {
// 1. Check if it's a connectivity issue
try {
await ping({});
} catch (pingError) {
// Plugin is not accessible - inform user
// Suggest: Check if Tauri app is running
// Suggest: Verify socket configuration
return;
}
// 2. Check if it's a parameter validation issue
if (error.message.includes('validation')) {
// Review parameter types in Tool Parameters Reference
// Provide corrected parameters
}
// 3. Check if it's a window-specific issue
if (error.message.includes('window')) {
// Verify window_label is correct
// Check if window still exists
}
// 4. Provide helpful context to user
// Include: What you were trying to do
// Include: The specific error message
// Include: Suggested next steps
}-
Batch JavaScript execution:
// Good: Single execution const allData = await execute_js({ script: ` JSON.stringify({ title: document.title, url: location.href, userCount: document.querySelectorAll('.user').length, isLoggedIn: !!localStorage.getItem('auth_token') }); ` }); // Bad: Multiple round trips const title = await execute_js({ script: "document.title" }); const url = await execute_js({ script: "location.href" }); const userCount = await execute_js({ script: "document.querySelectorAll('.user').length" });
-
Use appropriate screenshot quality:
- Use
quality: 60-70for quick checks - Use
quality: 90-100for detailed analysis - Lower quality = faster transfer, smaller size
- Use
-
Limit log retrieval:
- Use
limitparameter to avoid retrieving thousands of logs - Use
sinceparameter to get recent logs only - Filter by
levelto focus on errors
- Use
-
Cache DOM if analyzing multiple times:
- Get DOM once and analyze it multiple times in your code
- Only fetch again if you've made changes to the app
Have AI agents automatically detect UI changes:
Prompt to AI:
"Take a screenshot of the main window, then click the 'Theme' button and take another screenshot. Compare the two and tell me what changed visually."
What happens:
- AI takes initial screenshot
- AI simulates mouse click on theme button
- AI takes second screenshot
- AI analyzes both images and reports differences (colors, layout, etc.)
Test form validation and submission:
Prompt to AI:
"Fill out the registration form with invalid data and verify the error messages are displayed correctly."
What happens:
- AI uses
text_inputto type into form fields - AI uses
mouse_movementto click submit button - AI uses
take_screenshotto capture error state - AI uses
get_domto verify error messages in DOM - AI reports whether validation works correctly
Debug application state without opening DevTools:
Prompt to AI:
"Check the current Redux store state and tell me if the user is authenticated."
What happens:
- AI uses
execute_jsto run:window.store.getState() - AI examines the returned state object
- AI reports authentication status and related data
Test multi-window scenarios:
Prompt to AI:
"Open the settings window, change the theme to dark, take a screenshot, then switch back to the main window and verify the theme changed there too."
What happens:
- AI uses
window_managerto focus settings window - AI interacts with theme controls
- AI takes screenshot of settings window
- AI switches to main window using
window_manager - AI verifies theme consistency across windows
Inspect and modify stored data:
Prompt to AI:
"Check what's stored in localStorage and clear any expired session tokens."
What happens:
- AI uses
local_storage_get_allto retrieve all entries - AI examines token expiration dates
- AI uses
local_storage_removeto clear expired tokens - AI confirms cleanup was successful
CRITICAL: This plugin provides powerful access to your application and should NEVER be included in production builds.
Always wrap plugin registration in debug assertions:
#[cfg(debug_assertions)]
{
builder = builder.plugin(tauri_plugin_mcp::init_with_config(...));
}The plugin allows:
- Arbitrary JavaScript execution in your webview
- Full DOM access including sensitive data
- Screenshot capture of potentially sensitive UI
- Storage access including tokens and credentials
- Input simulation that could trigger unintended actions
Verify the plugin is excluded from production:
# Build for production
pnpm run tauri build
# Check the binary doesn't include MCP symbols (Linux/macOS)
nm -a ./target/release/myapp | grep -i mcp
# Should return nothing if properly excluded
# Or check Cargo features
cargo tree --features | grep mcpWhen using TCP mode:
-
Bind to localhost only: Never use
0.0.0.0.tcp("127.0.0.1".to_string(), 4000) // Safe .tcp("0.0.0.0".to_string(), 4000) // DANGEROUS
-
Firewall protection: Ensure firewall blocks external access to MCP port
# macOS - block external access sudo pfctl -e -
Use IPC instead: Prefer IPC sockets which can't be accessed remotely
Be aware that AI agents can access:
- User credentials in localStorage/sessionStorage
- API tokens in application state
- Personal data displayed in the UI
- Business logic via JavaScript execution
Mitigation:
- Use the plugin only with trusted AI agents
- Review AI agent prompts before execution
- Clear sensitive data from development environments
- Use test accounts, not production credentials
Add custom tools for your specific needs:
Create src/tools/custom_tool.rs:
use tauri::{AppHandle, Runtime};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
pub struct CustomToolParams {
pub param1: String,
}
#[derive(Serialize)]
pub struct CustomToolResult {
pub result: String,
}
pub async fn custom_tool<R: Runtime>(
app: &AppHandle<R>,
params: CustomToolParams,
) -> Result<CustomToolResult, String> {
// Your custom logic here
Ok(CustomToolResult {
result: format!("Processed: {}", params.param1),
})
}In src/tools/mod.rs:
pub mod custom_tool;
// In the command router
match action {
"custom_tool" => {
let params = serde_json::from_value(command.params)?;
let result = custom_tool::custom_tool(&app, params).await?;
Ok(json!({ "success": true, "data": result }))
}
// ... other actions
}In mcp-server-ts/src/tools/custom_tool.ts:
export const customToolDefinition = {
name: "custom_tool",
description: "Your custom tool description",
inputSchema: {
type: "object",
properties: {
param1: {
type: "string",
description: "Parameter description"
}
},
required: ["param1"]
}
};
export async function customTool(params: { param1: string }) {
return await client.sendCommand("custom_tool", params);
}In mcp-server-ts/src/tools/index.ts:
import { customToolDefinition, customTool } from './custom_tool';
server.registerTool(customToolDefinition, customTool);Reduce screenshot size for faster transmission:
PluginConfig::new("App".to_string())
.screenshot_quality(60) // Lower quality = smaller sizeThe plugin supports persistent connections. Ensure your client reuses connections:
// Good: Reuse connection
const client = await createClient();
await client.sendCommand("ping", {});
await client.sendCommand("take_screenshot", {});
// Bad: New connection each time
await (await createClient()).sendCommand("ping", {});
await (await createClient()).sendCommand("take_screenshot", {});When possible, batch operations into single JavaScript executions:
// Good: Single JS execution
execute_js({
script: `
const data = {
title: document.title,
url: window.location.href,
userCount: document.querySelectorAll('.user').length
};
JSON.stringify(data);
`
});
// Bad: Multiple roundtrips
execute_js({ script: "document.title" });
execute_js({ script: "window.location.href" });
execute_js({ script: "document.querySelectorAll('.user').length" });Different platforms may report window titles differently:
// macOS: Usually exact match
PluginConfig::new("MyApp".to_string())
// Windows: May include additional decorations
PluginConfig::new("MyApp - Window Name".to_string())
// Linux: Depends on window manager
// Test with: wmctrl -lPlatform-specific default paths:
#[cfg(target_os = "macos")]
const DEFAULT_SOCKET: &str = "/tmp/tauri-mcp.sock";
#[cfg(target_os = "linux")]
const DEFAULT_SOCKET: &str = "/tmp/tauri-mcp.sock";
#[cfg(target_os = "windows")]
const DEFAULT_SOCKET: &str = "\\\\.\\pipe\\tauri-mcp";- macOS: Requires Screen Recording permission for screenshots
- Windows: May need admin rights for certain window operations
- Linux: Depends on X11/Wayland and window manager permissions
Contributions are welcome! Here's how to get started:
# Clone the repository
git clone https://github.com/yourusername/tauri-plugin-mcp
cd tauri-plugin-mcp
# Install dependencies
pnpm install
# Build plugin and server
pnpm run build && pnpm run build-plugin
# Run tests
cargo test
cd mcp-server-ts && pnpm testRequired Tools:
- Rust toolchain (latest stable)
- Node.js 18+ and pnpm
- A Tauri test application
- MCP Inspector for testing:
npx @modelcontextprotocol/inspector
IDE Setup (Recommended):
- VS Code with extensions:
- rust-analyzer
- Tauri
- ESLint
- Prettier
- IntelliJ IDEA/RustRover with Rust and Tauri plugins
For Rust Changes (Plugin Core):
# 1. Make changes in src/
vim src/tools/my_tool.rs
# 2. Run format and lint
cargo fmt
cargo clippy
# 3. Build
cargo build
# 4. Test
cargo test
# 5. Test in real app
cd ../your-tauri-app
pnpm run tauri devFor TypeScript Changes (MCP Server):
cd mcp-server-ts
# 1. Make changes in src/
vim src/tools/my_tool.ts
# 2. Run format and lint
pnpm run lint
pnpm run format
# 3. Build
pnpm build
# 4. Test
pnpm test
# 5. Test with MCP Inspector
npx @modelcontextprotocol/inspector node build/index.jsUnit Tests:
# Rust unit tests
cargo test
# TypeScript unit tests
cd mcp-server-ts && pnpm testIntegration Testing:
-
Start your Tauri test app with the plugin:
cd your-test-app pnpm run tauri dev -
Use MCP Inspector to test tools:
cd mcp-server-ts npx @modelcontextprotocol/inspector node build/index.js -
Or test with your AI agent (Claude Code, Cursor, Cline)
Manual Socket Testing:
# Test ping command
echo '{"action":"ping","params":{}}' | nc -U /tmp/tauri-mcp.sock
# Test health check
echo '{"action":"health_check","params":{}}' | nc -U /tmp/tauri-mcp.sockRust Debugging:
Enable verbose logging in your test app:
#[cfg(debug_assertions)]
{
env_logger::init(); // Add this
builder = builder.plugin(tauri_plugin_mcp::init_with_config(...));
}Then run with:
RUST_LOG=debug pnpm run tauri devTypeScript Debugging:
Add debug logging in mcp-server-ts/src/client.ts:
console.error('[MCP] Sending command:', command);
console.error('[MCP] Received response:', response);Run with Node debugging:
node --inspect build/index.js- Code formatted (
cargo fmt,pnpm run format) - Lints pass (
cargo clippy,pnpm run lint) - Tests pass (
cargo test,pnpm test) - Changes tested in real Tauri app
- Documentation updated (README, doc comments)
- CHANGELOG.md updated (if applicable)
Complete workflow for adding a new MCP tool:
Define:
- Purpose: What problem does it solve?
- Parameters: What inputs does it need?
- Return Type: What data does it return?
- Use Case: When would AI agents use it?
Example:
Tool: get_network_requests
Purpose: Retrieve all network requests made by the app
Parameters: { since?: timestamp, filter?: regex }
Returns: Array of { url, method, status, headers, body, timing }
Use Case: Debugging API integration issues, analyzing performance
Create src/tools/my_tool.rs:
use tauri::{AppHandle, Runtime};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
pub struct MyToolParams {
pub param1: String,
pub optional_param: Option<i32>,
}
#[derive(Serialize)]
pub struct MyToolResult {
pub data: String,
}
pub async fn my_tool<R: Runtime>(
app: &AppHandle<R>,
params: MyToolParams,
) -> Result<MyToolResult, String> {
// Implementation here
Ok(MyToolResult {
data: format!("Processed: {}", params.param1),
})
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_my_tool() {
// Add unit tests
}
}Add to src/tools/mod.rs:
pub mod my_tool;
// In the command router match statement
"my_tool" => {
let params = serde_json::from_value(command.params)?;
let result = my_tool::my_tool(&app, params).await?;
Ok(json!({ "success": true, "data": result }))
}Create mcp-server-ts/src/tools/my_tool.ts:
import { client } from './client';
export const myToolDefinition = {
name: "my_tool",
description: "Clear description of what the tool does",
inputSchema: {
type: "object",
properties: {
param1: {
type: "string",
description: "Parameter description"
},
optional_param: {
type: "number",
description: "Optional parameter"
}
},
required: ["param1"]
}
};
export async function myTool(params: {
param1: string;
optional_param?: number;
}) {
return await client.sendCommand("my_tool", params);
}Add to mcp-server-ts/src/tools/index.ts:
import { myToolDefinition, myTool } from './my_tool';
// In the setup function
server.registerTool(myToolDefinition, myTool);Rust test:
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_my_tool_success() {
// Test implementation
}
#[tokio::test]
async fn test_my_tool_validation() {
// Test error cases
}
}TypeScript test:
// mcp-server-ts/tests/my_tool.test.ts
import { myTool } from '../src/tools/my_tool';
describe('myTool', () => {
it('should process valid input', async () => {
// Test implementation
});
});Add to README:
- Add entry in Quick Reference table
- Add to Tool Parameters Reference
- Add example usage in Usage Examples section
- Rust: Follow
rustfmtandclippysuggestions- Run:
cargo fmt && cargo clippy - Fix all clippy warnings before committing
- Run:
- TypeScript: Use ESLint and Prettier configurations
- Run:
pnpm run lint && pnpm run format
- Run:
- Commits: Use conventional commit format
feat:for new featuresfix:for bug fixesdocs:for documentationrefactor:for code refactoringtest:for adding tests
# Rust tests
cargo test
# TypeScript tests
cd mcp-server-ts
pnpm test
# Integration tests with MCP Inspector
cd mcp-server-ts
npx @modelcontextprotocol/inspector node build/index.js
# Integration tests with real app
cd examples/test-app
pnpm run tauri dev
# Then test with your AI agentRebuilding after changes:
# Full rebuild
pnpm run build && pnpm run build-plugin
# Rust only
cargo build
# TypeScript only
cd mcp-server-ts && pnpm buildRunning with verbose logging:
# Tauri app with debug logs
RUST_LOG=debug pnpm run tauri dev
# MCP server with debug logs
DEBUG=* node build/index.jsTesting socket connectivity:
# Check if socket exists
ls -l /tmp/tauri-mcp.sock
# Test with netcat
echo '{"action":"ping","params":{}}' | nc -U /tmp/tauri-mcp.sock[Specify your license here - e.g., MIT, Apache 2.0, etc.]
- Built on Tauri - Secure desktop application framework
- Implements Model Context Protocol - Standard for AI-application integration
- Inspired by browser automation tools like Selenium and Playwright
- Quick Start Guide - Get up and running in 15 minutes
- Integration Guide - Comprehensive setup and troubleshooting
- Testing Guide - AI-powered testing scenarios and patterns
- Documentation Hub - Complete documentation overview
- Tauri Documentation: https://tauri.app/v2/
- MCP Specification: https://spec.modelcontextprotocol.io/
- Original Plugin: https://github.com/P3GLEG/tauri-plugin-mcp
- Claude Code: https://claude.com/claude-code
- Issue Tracker: GitHub Issues
- Discussions: GitHub Discussions
- Tauri Discord: https://discord.gg/tauri