@hypothesi/tauri-mcp-server 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -67,7 +67,7 @@ Supported clients: `claude-code`, `cursor`, `windsurf`, `vscode`, `cline`, `roo-
67
67
  | `tauri_webview_wait_for` | Wait for elements, text, or events |
68
68
  | `tauri_webview_get_styles` | Get computed CSS styles |
69
69
  | `tauri_webview_execute_js` | Execute JavaScript in webview |
70
- | `tauri_list_windows` | List all open webview windows |
70
+ | `tauri_manage_window` | List windows, get info, or resize |
71
71
 
72
72
  ### IPC & Plugin
73
73
 
@@ -110,6 +110,21 @@ export async function emitTestEvent(eventName, payload) {
110
110
  throw new Error(`Failed to emit event: ${message}`);
111
111
  }
112
112
  }
113
+ export const GetWindowInfoSchema = z.object({});
114
+ export async function getWindowInfo() {
115
+ try {
116
+ const result = await executeIPCCommand('plugin:mcp-bridge|get_window_info');
117
+ const parsed = JSON.parse(result);
118
+ if (!parsed.success) {
119
+ throw new Error(parsed.error || 'Unknown error');
120
+ }
121
+ return JSON.stringify(parsed.result);
122
+ }
123
+ catch (error) {
124
+ const message = error instanceof Error ? error.message : String(error);
125
+ throw new Error(`Failed to get window info: ${message}`);
126
+ }
127
+ }
113
128
  export const GetBackendStateSchema = z.object({});
114
129
  export async function getBackendState() {
115
130
  try {
@@ -154,3 +169,97 @@ export async function listWindows() {
154
169
  throw new Error(`Failed to list windows: ${message}`);
155
170
  }
156
171
  }
172
+ export const ResizeWindowSchema = z.object({
173
+ width: z.number().int().positive().describe('Width in pixels'),
174
+ height: z.number().int().positive().describe('Height in pixels'),
175
+ windowId: z.string().optional().describe('Window label to resize (defaults to "main")'),
176
+ logical: z.boolean().optional().default(true)
177
+ .describe('Use logical pixels (true, default) or physical pixels (false)'),
178
+ });
179
+ /**
180
+ * Resizes a window to the specified dimensions.
181
+ *
182
+ * @param options - Resize options including width, height, and optional windowId
183
+ * @returns JSON string with the result of the resize operation
184
+ */
185
+ export async function resizeWindow(options) {
186
+ try {
187
+ await connectPlugin();
188
+ const client = getPluginClient();
189
+ const response = await client.sendCommand({
190
+ command: 'resize_window',
191
+ args: {
192
+ width: options.width,
193
+ height: options.height,
194
+ windowId: options.windowId,
195
+ logical: options.logical ?? true,
196
+ },
197
+ });
198
+ if (!response.success) {
199
+ throw new Error(response.error || 'Unknown error');
200
+ }
201
+ return JSON.stringify(response.data);
202
+ }
203
+ catch (error) {
204
+ const message = error instanceof Error ? error.message : String(error);
205
+ throw new Error(`Failed to resize window: ${message}`);
206
+ }
207
+ }
208
+ export const ManageWindowSchema = z.object({
209
+ action: z.enum(['list', 'info', 'resize'])
210
+ .describe('Action: "list" all windows, get "info" for one window, or "resize" a window'),
211
+ windowId: z.string().optional()
212
+ .describe('Window label to target (defaults to "main"). Required for "info", optional for "resize"'),
213
+ width: z.number().int().positive().optional()
214
+ .describe('Width in pixels (required for "resize" action)'),
215
+ height: z.number().int().positive().optional()
216
+ .describe('Height in pixels (required for "resize" action)'),
217
+ logical: z.boolean().optional().default(true)
218
+ .describe('Use logical pixels (true, default) or physical pixels (false). Only for "resize"'),
219
+ });
220
+ /**
221
+ * Unified window management function.
222
+ *
223
+ * Actions:
224
+ * - `list`: List all open webview windows with their labels, titles, URLs, and state
225
+ * - `info`: Get detailed info for a window (size, position, title, focus, visibility)
226
+ * - `resize`: Resize a window to specified dimensions
227
+ *
228
+ * @param options - Action and parameters
229
+ * @returns JSON string with the result
230
+ */
231
+ export async function manageWindow(options) {
232
+ const { action, windowId, width, height, logical } = options;
233
+ switch (action) {
234
+ case 'list': {
235
+ return listWindows();
236
+ }
237
+ case 'info': {
238
+ try {
239
+ await connectPlugin();
240
+ const client = getPluginClient();
241
+ const response = await client.sendCommand({
242
+ command: 'get_window_info',
243
+ args: { windowId },
244
+ });
245
+ if (!response.success) {
246
+ throw new Error(response.error || 'Unknown error');
247
+ }
248
+ return JSON.stringify(response.data);
249
+ }
250
+ catch (error) {
251
+ const message = error instanceof Error ? error.message : String(error);
252
+ throw new Error(`Failed to get window info: ${message}`);
253
+ }
254
+ }
255
+ case 'resize': {
256
+ if (width === undefined || height === undefined) {
257
+ throw new Error('width and height are required for resize action');
258
+ }
259
+ return resizeWindow({ width, height, windowId, logical });
260
+ }
261
+ default: {
262
+ throw new Error(`Unknown action: ${action}`);
263
+ }
264
+ }
265
+ }
@@ -1,7 +1,8 @@
1
1
  import { z } from 'zod';
2
2
  import { getDefaultHost, getDefaultPort } from '../config.js';
3
3
  import { AppDiscovery } from './app-discovery.js';
4
- import { resetPluginClient, getPluginClient } from './plugin-client.js';
4
+ import { resetPluginClient, getPluginClient, connectPlugin } from './plugin-client.js';
5
+ import { getBackendState } from './plugin-commands.js';
5
6
  import { resetInitialization } from './webview-executor.js';
6
7
  /**
7
8
  * Session Manager - Native IPC-based session management
@@ -26,7 +27,7 @@ export const ManageDriverSessionSchema = z.object({
26
27
  // Module State
27
28
  // ============================================================================
28
29
  // AppDiscovery instance - recreated when host changes
29
- // Track current session info
30
+ // Track current session info including app identifier for session reuse
30
31
  let appDiscovery = null, currentSession = null;
31
32
  function getAppDiscovery(host) {
32
33
  if (!appDiscovery || appDiscovery.host !== host) {
@@ -51,7 +52,28 @@ async function tryConnect(host, port) {
51
52
  };
52
53
  }
53
54
  /**
54
- * Manage session lifecycle (start or stop).
55
+ * Fetch the app identifier from the backend state.
56
+ * Must be called after the singleton pluginClient is connected.
57
+ *
58
+ * @returns The app identifier (bundle ID) or null if not available. Returns null when:
59
+ * - The plugin doesn't support the identifier field (older versions)
60
+ * - The backend state request fails
61
+ * - The identifier field is missing from the response
62
+ */
63
+ async function fetchAppIdentifier() {
64
+ try {
65
+ const stateJson = await getBackendState();
66
+ const state = JSON.parse(stateJson);
67
+ // Return null if identifier is not present (backward compat with older plugins)
68
+ return state.app?.identifier ?? null;
69
+ }
70
+ catch {
71
+ // Return null on any error (e.g., older plugin version that doesn't support this)
72
+ return null;
73
+ }
74
+ }
75
+ /**
76
+ * Manage session lifecycle (start, stop, or status).
55
77
  *
56
78
  * Connection strategy for 'start':
57
79
  * 1. Try localhost:{port} first (most reliable for simulators/emulators/desktop)
@@ -59,9 +81,16 @@ async function tryConnect(host, port) {
59
81
  * 3. If both fail, try auto-discovery on localhost
60
82
  * 4. Return error if all attempts fail
61
83
  *
62
- * @param action - 'start' or 'stop'
84
+ * @param action - 'start', 'stop', or 'status'
63
85
  * @param host - Optional host address (defaults to env var or localhost)
64
86
  * @param port - Optional port number (defaults to 9223)
87
+ * @returns For 'start'/'stop': A message string describing the result.
88
+ * For 'status': A JSON string with connection details including:
89
+ * - `connected`: boolean indicating if connected
90
+ * - `app`: app name (or null if not connected)
91
+ * - `identifier`: app bundle ID (e.g., "com.example.app"), or null
92
+ * - `host`: connected host (or null)
93
+ * - `port`: connected port (or null)
65
94
  */
66
95
  export async function manageDriverSession(action, host, port) {
67
96
  // Handle status action
@@ -71,6 +100,7 @@ export async function manageDriverSession(action, host, port) {
71
100
  return JSON.stringify({
72
101
  connected: true,
73
102
  app: currentSession.name,
103
+ identifier: currentSession.identifier,
74
104
  host: currentSession.host,
75
105
  port: currentSession.port,
76
106
  });
@@ -78,12 +108,16 @@ export async function manageDriverSession(action, host, port) {
78
108
  return JSON.stringify({
79
109
  connected: false,
80
110
  app: null,
111
+ identifier: null,
81
112
  host: null,
82
113
  port: null,
83
114
  });
84
115
  }
85
116
  if (action === 'start') {
86
- // Reset any existing plugin client to ensure fresh connection
117
+ // Reset any existing connections to ensure fresh connection
118
+ if (appDiscovery) {
119
+ await appDiscovery.disconnectAll();
120
+ }
87
121
  resetPluginClient();
88
122
  const configuredHost = host ?? getDefaultHost();
89
123
  const configuredPort = port ?? getDefaultPort();
@@ -91,7 +125,11 @@ export async function manageDriverSession(action, host, port) {
91
125
  if (configuredHost !== 'localhost' && configuredHost !== '127.0.0.1') {
92
126
  try {
93
127
  const session = await tryConnect('localhost', configuredPort);
94
- currentSession = session;
128
+ // Connect the singleton pluginClient so status checks work
129
+ await connectPlugin(session.host, session.port);
130
+ // Fetch app identifier after singleton is connected
131
+ const identifier = await fetchAppIdentifier();
132
+ currentSession = { ...session, identifier };
95
133
  return `Session started with app: ${session.name} (localhost:${session.port})`;
96
134
  }
97
135
  catch {
@@ -101,7 +139,11 @@ export async function manageDriverSession(action, host, port) {
101
139
  // Strategy 2: Try the configured/provided host
102
140
  try {
103
141
  const session = await tryConnect(configuredHost, configuredPort);
104
- currentSession = session;
142
+ // Connect the singleton pluginClient so status checks work
143
+ await connectPlugin(session.host, session.port);
144
+ // Fetch app identifier after singleton is connected
145
+ const identifier = await fetchAppIdentifier();
146
+ currentSession = { ...session, identifier };
105
147
  return `Session started with app: ${session.name} (${session.host}:${session.port})`;
106
148
  }
107
149
  catch {
@@ -115,7 +157,11 @@ export async function manageDriverSession(action, host, port) {
115
157
  // Reset client again to connect to discovered port
116
158
  resetPluginClient();
117
159
  const session = await tryConnect('localhost', firstApp.port);
118
- currentSession = session;
160
+ // Connect the singleton pluginClient so status checks work
161
+ await connectPlugin(session.host, session.port);
162
+ // Fetch app identifier after singleton is connected
163
+ const identifier = await fetchAppIdentifier();
164
+ currentSession = { ...session, identifier };
119
165
  return `Session started with app: ${session.name} (localhost:${session.port})`;
120
166
  }
121
167
  catch {
@@ -126,7 +172,11 @@ export async function manageDriverSession(action, host, port) {
126
172
  try {
127
173
  resetPluginClient();
128
174
  const session = await tryConnect(configuredHost, configuredPort);
129
- currentSession = session;
175
+ // Connect the singleton pluginClient so status checks work
176
+ await connectPlugin(session.host, session.port);
177
+ // Fetch app identifier after singleton is connected
178
+ const identifier = await fetchAppIdentifier();
179
+ currentSession = { ...session, identifier };
130
180
  return `Session started with app: ${session.name} (${session.host}:${session.port})`;
131
181
  }
132
182
  catch {
@@ -24,96 +24,72 @@ Please follow these steps:
24
24
  If no errors are found, let me know the app is running cleanly.
25
25
 
26
26
  If the session fails to start, help me troubleshoot the connection (is the app running? is the MCP bridge plugin installed?).`;
27
- const SETUP_PROMPT = `Help me set up the MCP Bridge plugin in my Tauri project so I can use these AI development tools.
27
+ const SETUP_PROMPT = `Help me set up or update the MCP Bridge plugin in my Tauri project.
28
28
 
29
- ## Prerequisites
29
+ ## IMPORTANT: Do Not Act Without Permission
30
30
 
31
- - This is a **Tauri v2** project (check for \`src-tauri/\` directory and \`tauri.conf.json\`)
32
- - If this is NOT a Tauri project, stop and let the user know this setup only applies to Tauri apps
31
+ **You must NOT make any changes to files without my explicit approval.**
33
32
 
34
- ## Setup Steps
33
+ 1. First, examine my project to understand its current state
34
+ 2. Then, present a clear summary of what changes are needed
35
+ 3. Wait for my approval before making ANY modifications
36
+ 4. Only proceed with changes after I confirm
35
37
 
36
- ### Step 1: Add the Rust Plugin
38
+ ## Prerequisites Check
37
39
 
38
- Add the plugin to \`src-tauri/Cargo.toml\` dependencies:
40
+ First, verify this is a Tauri v2 project:
41
+ - Look for \`src-tauri/\` directory and \`tauri.conf.json\`
42
+ - If this is NOT a Tauri project, stop and let me know this setup only applies to Tauri apps
39
43
 
44
+ ## What to Check
45
+
46
+ Examine these files and report what needs to be added or updated:
47
+
48
+ ### 1. Rust Plugin Dependency
49
+ Check \`src-tauri/Cargo.toml\` for \`tauri-plugin-mcp-bridge\`. If missing or outdated, note that it needs:
40
50
  \`\`\`toml
41
51
  [dependencies]
42
- tauri-plugin-mcp-bridge = "0.2"
52
+ tauri-plugin-mcp-bridge = "0.4"
43
53
  \`\`\`
44
54
 
45
- Or run from the \`src-tauri\` directory:
46
- \`\`\`bash
47
- cargo add tauri-plugin-mcp-bridge
48
- \`\`\`
49
-
50
- ### Step 2: Register the Plugin
51
-
52
- In the Tauri app's entry point (usually \`src-tauri/src/lib.rs\` or \`src-tauri/src/main.rs\`), register the plugin.
53
-
54
- Find the \`tauri::Builder\` and add the plugin (only in debug builds):
55
-
55
+ ### 2. Plugin Registration
56
+ Check \`src-tauri/src/lib.rs\` or \`src-tauri/src/main.rs\` for plugin registration. It should have:
56
57
  \`\`\`rust
57
- let mut builder = tauri::Builder::default();
58
- // ... existing plugins ...
59
-
60
58
  #[cfg(debug_assertions)]
61
59
  {
62
60
  builder = builder.plugin(tauri_plugin_mcp_bridge::init());
63
61
  }
64
-
65
- builder
66
- .run(tauri::generate_context!())
67
- .expect("error while running tauri application");
68
- \`\`\`
69
-
70
- ### Step 3: Enable Global Tauri (REQUIRED)
71
-
72
- In \`src-tauri/tauri.conf.json\`, ensure \`withGlobalTauri\` is enabled:
73
-
74
- \`\`\`json
75
- {
76
- "app": {
77
- "withGlobalTauri": true
78
- }
79
- }
80
62
  \`\`\`
81
63
 
64
+ ### 3. Global Tauri Setting
65
+ Check \`src-tauri/tauri.conf.json\` for \`withGlobalTauri: true\` under the \`app\` section.
82
66
  **This is required** - without it, the MCP bridge cannot communicate with the webview.
83
67
 
84
- ### Step 4: Add Plugin Permissions
68
+ ### 4. Plugin Permissions
69
+ Check \`src-tauri/capabilities/default.json\` (or similar) for \`"mcp-bridge:default"\` permission.
85
70
 
86
- Add the plugin permission to \`src-tauri/capabilities/default.json\` (create the file if it doesn't exist):
71
+ ## Your Response Format
87
72
 
88
- \`\`\`json
89
- {
90
- "$schema": "../gen/schemas/desktop-schema.json",
91
- "identifier": "default",
92
- "description": "Default capabilities",
93
- "windows": ["main"],
94
- "permissions": [
95
- "mcp-bridge:default"
96
- ]
97
- }
98
- \`\`\`
73
+ After examining the project, respond with:
99
74
 
100
- If the file already exists, just add \`"mcp-bridge:default"\` to the existing permissions array.
75
+ 1. **Current State**: What's already configured correctly
76
+ 2. **Changes Needed**: A numbered list of specific changes required
77
+ 3. **Ask for Permission**: "May I proceed with these changes?"
101
78
 
102
- ## Verification
79
+ Only after I say yes should you make any modifications.
103
80
 
104
- After setup:
105
- 1. Run the Tauri app in development mode (\`cargo tauri dev\` or \`npm run tauri dev\`)
106
- 2. The MCP bridge will start a WebSocket server on port 9223
107
- 3. Use \`tauri_driver_session\` with action "start" to connect
108
- 4. Use \`tauri_driver_session\` with action "status" to verify the connection
81
+ ## After Setup
109
82
 
110
- ## Notes
83
+ Once changes are approved and made:
84
+ 1. Run the Tauri app in development mode (\`cargo tauri dev\`)
85
+ 2. Use \`tauri_driver_session\` with action "start" to connect
86
+ 3. Use \`tauri_driver_session\` with action "status" to verify
111
87
 
112
- - The plugin only runs in debug builds (\`#[cfg(debug_assertions)]\`) so it won't affect production
113
- - The WebSocket server binds to \`0.0.0.0\` by default to support mobile device testing
114
- - For localhost-only access, use \`Builder::new().bind_address("127.0.0.1").build()\` instead of \`init()\`
88
+ ## Notes
115
89
 
116
- Please examine the project structure and make the necessary changes to set up the MCP bridge plugin.`;
90
+ - The plugin only runs in debug builds so it won't affect production
91
+ - The WebSocket server binds to \`0.0.0.0:9223\` by default
92
+ - For localhost-only access, use \`Builder::new().bind_address("127.0.0.1").build()\``;
117
93
  /**
118
94
  * Complete registry of all available prompts
119
95
  */
@@ -139,9 +115,9 @@ export const PROMPTS = [
139
115
  },
140
116
  {
141
117
  name: 'setup',
142
- description: 'Set up the MCP Bridge plugin in a Tauri project. ' +
143
- 'Guides through adding the Rust crate, registering the plugin, enabling withGlobalTauri, ' +
144
- 'and adding permissions. Use this when starting with a new Tauri project.',
118
+ description: 'Set up or update the MCP Bridge plugin in a Tauri project. ' +
119
+ 'Examines the project, reports what changes are needed, and asks for permission before ' +
120
+ 'making any modifications. Use for initial setup or to update to the latest version.',
145
121
  arguments: [],
146
122
  handler: () => {
147
123
  return [
@@ -2,24 +2,118 @@
2
2
  * Single source of truth for all MCP tool definitions
3
3
  * This file defines all available tools and their metadata
4
4
  */
5
+ import { z } from 'zod';
5
6
  import { listDevices, ListDevicesSchema } from './manager/mobile.js';
6
7
  import { manageDriverSession, ManageDriverSessionSchema, } from './driver/session-manager.js';
7
8
  import { readLogs, ReadLogsSchema } from './monitor/logs.js';
8
- import { executeIPCCommand, manageIPCMonitoring, getIPCEvents, emitTestEvent, getBackendState, listWindows, ExecuteIPCCommandSchema, ManageIPCMonitoringSchema, GetIPCEventsSchema, EmitTestEventSchema, GetBackendStateSchema, ListWindowsSchema, } from './driver/plugin-commands.js';
9
+ import { executeIPCCommand, manageIPCMonitoring, getIPCEvents, emitTestEvent, getBackendState, manageWindow, ExecuteIPCCommandSchema, ManageIPCMonitoringSchema, GetIPCEventsSchema, EmitTestEventSchema, GetBackendStateSchema, ManageWindowSchema, } from './driver/plugin-commands.js';
9
10
  import { interact, screenshot, keyboard, waitFor, getStyles, executeJavaScript, findElement, InteractSchema, ScreenshotSchema, KeyboardSchema, WaitForSchema, GetStylesSchema, ExecuteJavaScriptSchema, FindElementSchema, } from './driver/webview-interactions.js';
10
11
  /**
11
12
  * Tool categories for organization
12
13
  */
13
14
  export const TOOL_CATEGORIES = {
15
+ SETUP: 'Setup & Configuration',
14
16
  MOBILE_DEVELOPMENT: 'Mobile Development',
15
17
  UI_AUTOMATION: 'UI Automation & WebView Interaction',
16
18
  IPC_PLUGIN: 'IPC & Plugin Tools (via MCP Bridge)',
17
19
  };
20
+ // Setup instructions for the MCP Bridge plugin
21
+ const SETUP_INSTRUCTIONS = `# MCP Bridge Plugin Setup Instructions
22
+
23
+ Use these instructions to set up or update the MCP Bridge plugin in a Tauri v2 project.
24
+
25
+ ## IMPORTANT: Do Not Act Without Permission
26
+
27
+ **You must NOT make any changes to files without the user's explicit approval.**
28
+
29
+ 1. First, examine the project to understand its current state
30
+ 2. Then, present a clear summary of what changes are needed
31
+ 3. Wait for user approval before making ANY modifications
32
+ 4. Only proceed with changes after they confirm
33
+
34
+ ## Prerequisites Check
35
+
36
+ First, verify this is a Tauri v2 project:
37
+ - Look for \`src-tauri/\` directory and \`tauri.conf.json\`
38
+ - If this is NOT a Tauri project, stop and let the user know this setup only applies to Tauri apps
39
+
40
+ ## What to Check
41
+
42
+ Examine these files and report what needs to be added or updated:
43
+
44
+ ### 1. Rust Plugin Dependency
45
+ Check \`src-tauri/Cargo.toml\` for \`tauri-plugin-mcp-bridge\`. If missing or outdated, note that it needs:
46
+ \`\`\`toml
47
+ [dependencies]
48
+ tauri-plugin-mcp-bridge = "0.4"
49
+ \`\`\`
50
+
51
+ ### 2. Plugin Registration
52
+ Check \`src-tauri/src/lib.rs\` or \`src-tauri/src/main.rs\` for plugin registration. It should have:
53
+ \`\`\`rust
54
+ #[cfg(debug_assertions)]
55
+ {
56
+ builder = builder.plugin(tauri_plugin_mcp_bridge::init());
57
+ }
58
+ \`\`\`
59
+
60
+ ### 3. Global Tauri Setting
61
+ Check \`src-tauri/tauri.conf.json\` for \`withGlobalTauri: true\` under the \`app\` section.
62
+ **This is required** - without it, the MCP bridge cannot communicate with the webview.
63
+
64
+ ### 4. Plugin Permissions
65
+ Check \`src-tauri/capabilities/default.json\` (or similar) for \`"mcp-bridge:default"\` permission.
66
+
67
+ ## Response Format
68
+
69
+ After examining the project, respond with:
70
+
71
+ 1. **Current State**: What's already configured correctly
72
+ 2. **Changes Needed**: A numbered list of specific changes required
73
+ 3. **Ask for Permission**: "May I proceed with these changes?"
74
+
75
+ Only after the user says yes should you make any modifications.
76
+
77
+ ## After Setup
78
+
79
+ Once changes are approved and made:
80
+ 1. Run the Tauri app in development mode (\`cargo tauri dev\`)
81
+ 2. Use \`tauri_driver_session\` with action "start" to connect
82
+ 3. Use \`tauri_driver_session\` with action "status" to verify
83
+
84
+ ## Notes
85
+
86
+ - The plugin only runs in debug builds so it won't affect production
87
+ - The WebSocket server binds to \`0.0.0.0:9223\` by default
88
+ - For localhost-only access, use \`Builder::new().bind_address("127.0.0.1").build()\`
89
+ `;
18
90
  /**
19
91
  * Complete registry of all available tools
20
92
  * This is the single source of truth for tool definitions
21
93
  */
22
94
  export const TOOLS = [
95
+ // Setup & Configuration Tools
96
+ {
97
+ name: 'tauri_get_setup_instructions',
98
+ description: 'Get instructions for setting up or updating the MCP Bridge plugin in a Tauri project. ' +
99
+ 'Call this tool when: (1) tauri_driver_session fails to connect, (2) you detect the plugin ' +
100
+ 'is not installed or outdated, or (3) the user asks about setup. ' +
101
+ 'Returns step-by-step guidance that you should follow to help the user configure their project. ' +
102
+ 'IMPORTANT: The instructions require you to examine the project first and ask for permission ' +
103
+ 'before making any changes.',
104
+ category: TOOL_CATEGORIES.SETUP,
105
+ schema: z.object({}),
106
+ annotations: {
107
+ title: 'Get Setup Instructions',
108
+ readOnlyHint: true,
109
+ destructiveHint: false,
110
+ idempotentHint: true,
111
+ openWorldHint: false,
112
+ },
113
+ handler: async () => {
114
+ return SETUP_INSTRUCTIONS;
115
+ },
116
+ },
23
117
  // Mobile Development Tools
24
118
  {
25
119
  name: 'tauri_list_devices',
@@ -42,7 +136,11 @@ export const TOOLS = [
42
136
  {
43
137
  name: 'tauri_driver_session',
44
138
  description: '[Tauri Apps Only] Start/stop automation session to connect to a RUNNING Tauri app. ' +
45
- 'Use action "status" to check current connection state. ' +
139
+ 'Use action "status" to check current connection state and get the app identifier. ' +
140
+ 'The status response includes an "identifier" field (e.g., "com.example.myapp") that uniquely identifies the connected app. ' +
141
+ 'The identifier may be null if the Tauri app uses an older plugin version that does not provide it. ' +
142
+ 'Before starting a new session, check status first - if already connected to the correct app (matching identifier), ' +
143
+ 'reuse the existing session. If identifier is null, you cannot verify the app identity. ' +
46
144
  'REQUIRED before using other tauri_webview_* or tauri_plugin_* tools. ' +
47
145
  'Connects via WebSocket to the MCP Bridge plugin in the Tauri app. ' +
48
146
  'For browser automation, use Chrome DevTools MCP instead. ' +
@@ -339,20 +437,25 @@ export const TOOLS = [
339
437
  },
340
438
  // Window Management Tools
341
439
  {
342
- name: 'tauri_list_windows',
343
- description: '[Tauri Apps Only] List all Tauri webview windows with details including ' +
344
- 'labels, titles, URLs, and state (focused, visible, isMain). ' +
345
- 'Requires active tauri_driver_session. Use to discover windows before targeting them. ' +
346
- 'For browser tabs/windows, use Chrome DevTools MCP instead.',
440
+ name: 'tauri_manage_window',
441
+ description: '[Tauri Apps Only] Manage Tauri windows. Actions: ' +
442
+ '"list" - List all windows with labels, titles, URLs, and state. ' +
443
+ '"info" - Get detailed info for a window (size, position, title, focus, visibility). ' +
444
+ '"resize" - Resize a window (requires width/height, uses logical pixels by default). ' +
445
+ 'Requires active tauri_driver_session. ' +
446
+ 'For browser windows, use Chrome DevTools MCP instead.',
347
447
  category: TOOL_CATEGORIES.UI_AUTOMATION,
348
- schema: ListWindowsSchema,
448
+ schema: ManageWindowSchema,
349
449
  annotations: {
350
- title: 'List Tauri Windows',
351
- readOnlyHint: true,
450
+ title: 'Manage Tauri Window',
451
+ readOnlyHint: false,
452
+ destructiveHint: false,
453
+ idempotentHint: true,
352
454
  openWorldHint: false,
353
455
  },
354
- handler: async () => {
355
- return await listWindows();
456
+ handler: async (args) => {
457
+ const parsed = ManageWindowSchema.parse(args);
458
+ return await manageWindow(parsed);
356
459
  },
357
460
  },
358
461
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypothesi/tauri-mcp-server",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A Model Context Protocol server for Tauri v2 development",
5
5
  "type": "module",
6
6
  "bin": {