@hypothesi/tauri-mcp-server 0.12.0 → 0.13.1

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.
@@ -7,7 +7,7 @@ import { listDevices, ListDevicesSchema } from './manager/mobile.js';
7
7
  import { manageDriverSession, ManageDriverSessionSchema, } from './driver/session-manager.js';
8
8
  import { readLogs, ReadLogsSchema } from './monitor/logs.js';
9
9
  import { executeIPCCommand, manageIPCMonitoring, getIPCEvents, emitTestEvent, getBackendState, manageWindow, ExecuteIPCCommandSchema, ManageIPCMonitoringSchema, GetIPCEventsSchema, EmitTestEventSchema, GetBackendStateSchema, ManageWindowSchema, } from './driver/plugin-commands.js';
10
- import { interact, screenshot, keyboard, waitFor, getStyles, executeJavaScript, findElement, domSnapshot, InteractSchema, ScreenshotSchema, KeyboardSchema, WaitForSchema, GetStylesSchema, ExecuteJavaScriptSchema, FindElementSchema, DomSnapshotSchema, } from './driver/webview-interactions.js';
10
+ import { interact, screenshot, keyboard, waitFor, executeJavaScript, findElement, domSnapshot, InteractSchema, ScreenshotSchema, KeyboardSchema, WaitForSchema, ExecuteJavaScriptSchema, FindElementSchema, DomSnapshotSchema, } from './driver/webview-interactions.js';
11
11
  import { selectElement, getPointedElement, SelectElementSchema, GetPointedElementSchema, } from './driver/element-picker.js';
12
12
  import { SETUP_INSTRUCTIONS } from './constants.js';
13
13
  /**
@@ -99,10 +99,16 @@ export const TOOLS = [
99
99
  },
100
100
  {
101
101
  name: 'webview_find_element',
102
- description: '[Tauri Apps Only] Find DOM elements in a running Tauri app\'s webview. ' +
102
+ description: '[Tauri Apps Only] Find elements in a running Tauri app\'s webview and return their geometry, text, ' +
103
+ 'accessible name, attributes, visibility, and optionally their computed styles. ' +
103
104
  'Supports CSS selectors (default), XPath expressions, and text content matching via the strategy parameter. ' +
104
- 'The "text" strategy first searches element text content, then falls back to placeholder, aria-label, and title attributes. ' +
105
- 'Returns the element\'s HTML. ' +
105
+ 'The "text" strategy searches element text content (not just direct text nodes), prefers exact matches over ' +
106
+ 'substring matches, prefers innermost elements, and prefers interactive ancestors. ' +
107
+ 'Returns structured JSON with totalMatches, returned count, and array of matches each containing: ' +
108
+ 'nth index, ref ID, tag, trimmed text, accessible name, attributes, bounding rect, visibility, interactability, ' +
109
+ 'optional computed styles, and optional truncated outerHTML. ' +
110
+ 'Use this instead of webview_execute_js for querySelector, getBoundingClientRect, textContent, ' +
111
+ 'and getComputedStyle reads. ' +
106
112
  'Requires active driver_session. ' +
107
113
  MULTI_APP_DESC + ' ' +
108
114
  'For browser pages or documentation sites, use Chrome DevTools MCP instead.',
@@ -118,6 +124,11 @@ export const TOOLS = [
118
124
  return await findElement({
119
125
  selector: parsed.selector,
120
126
  strategy: parsed.strategy,
127
+ nth: parsed.nth,
128
+ limit: parsed.limit,
129
+ properties: parsed.properties,
130
+ visibleOnly: parsed.visibleOnly,
131
+ includeHtml: parsed.includeHtml,
121
132
  windowId: parsed.windowId,
122
133
  appIdentifier: parsed.appIdentifier,
123
134
  });
@@ -144,9 +155,11 @@ export const TOOLS = [
144
155
  // WebView Interaction Tools
145
156
  {
146
157
  name: 'webview_interact',
147
- description: '[Tauri Apps Only] Click, scroll, swipe, focus, or perform gestures in a Tauri app webview. ' +
148
- 'Supported actions: click, double-click, long-press, scroll, swipe, focus. ' +
158
+ description: '[Tauri Apps Only] Click, hover, right-click, scroll, swipe, focus, or perform gestures in a Tauri app webview. ' +
159
+ 'Supported actions: click, double-click, long-press, scroll, swipe, focus, hover, right-click. ' +
149
160
  'Supports CSS selectors (default), XPath, and text content matching via the strategy parameter. ' +
161
+ 'When using a text selector that matches multiple visible elements, you must provide the nth parameter ' +
162
+ 'to disambiguate, or the tool will throw an error listing the candidates. ' +
150
163
  'Requires active driver_session. ' +
151
164
  'For browser interaction, use Chrome DevTools MCP instead.',
152
165
  category: TOOL_CATEGORIES.UI_AUTOMATION,
@@ -185,6 +198,7 @@ export const TOOLS = [
185
198
  filePath: parsed.filePath,
186
199
  appIdentifier: parsed.appIdentifier,
187
200
  maxWidth: parsed.maxWidth,
201
+ allowScreenCapture: parsed.allowScreenCapture,
188
202
  });
189
203
  // If saved to file, return text confirmation
190
204
  if ('filePath' in result) {
@@ -217,6 +231,7 @@ export const TOOLS = [
217
231
  action: parsed.action,
218
232
  selectorOrKey: parsed.selector,
219
233
  strategy: parsed.strategy,
234
+ nth: parsed.nth,
220
235
  textOrModifiers: parsed.text,
221
236
  windowId: parsed.windowId,
222
237
  appIdentifier: parsed.appIdentifier,
@@ -257,38 +272,14 @@ export const TOOLS = [
257
272
  });
258
273
  },
259
274
  },
260
- {
261
- name: 'webview_get_styles',
262
- description: '[Tauri Apps Only] Get computed CSS styles from elements in a Tauri app. ' +
263
- 'Supports CSS selectors (default), XPath, and text content matching via the strategy parameter. ' +
264
- 'Requires active driver_session. ' +
265
- MULTI_APP_DESC + ' ' +
266
- 'For browser style inspection, use Chrome DevTools MCP instead.',
267
- category: TOOL_CATEGORIES.UI_AUTOMATION,
268
- schema: GetStylesSchema,
269
- annotations: {
270
- title: 'Get Styles in Tauri Webview',
271
- readOnlyHint: true,
272
- openWorldHint: false,
273
- },
274
- handler: async (args) => {
275
- const parsed = GetStylesSchema.parse(args);
276
- return await getStyles({
277
- selector: parsed.selector,
278
- strategy: parsed.strategy,
279
- properties: parsed.properties,
280
- multiple: parsed.multiple,
281
- windowId: parsed.windowId,
282
- appIdentifier: parsed.appIdentifier,
283
- });
284
- },
285
- },
286
275
  {
287
276
  name: 'webview_execute_js',
288
277
  description: '[Tauri Apps Only] Execute JavaScript in a Tauri app\'s webview context. ' +
289
278
  'Requires active driver_session. Has access to window.__TAURI__. ' +
290
279
  'If you need a return value, it must be JSON-serializable. ' +
291
280
  'For functions that return values, use an IIFE: "(() => { return 5; })()" not "() => { return 5; }". ' +
281
+ 'Accepts an optional timeout parameter (milliseconds) for long-running scripts. ' +
282
+ 'Timeout errors will identify which layer timed out (transport, script self-race, or Rust eval). ' +
292
283
  MULTI_APP_DESC + ' ' +
293
284
  'For browser JS execution, use Chrome DevTools MCP instead.',
294
285
  category: TOOL_CATEGORIES.UI_AUTOMATION,
@@ -304,6 +295,7 @@ export const TOOLS = [
304
295
  return await executeJavaScript({
305
296
  script: parsed.script,
306
297
  args: parsed.args,
298
+ timeout: parsed.timeout,
307
299
  windowId: parsed.windowId,
308
300
  appIdentifier: parsed.appIdentifier,
309
301
  });
@@ -396,6 +388,9 @@ export const TOOLS = [
396
388
  name: 'ipc_execute_command',
397
389
  description: '[Tauri Apps Only] Execute Tauri IPC commands (invoke Rust backend functions). ' +
398
390
  'Requires active driver_session. This is Tauri-specific IPC, not browser APIs. ' +
391
+ 'Plugin commands (plugin:mcp-bridge|*) are routed through the bridge; all other commands ' +
392
+ 'are executed via the webview using window.__TAURI_INTERNALS__.invoke, which keeps ' +
393
+ 'the application\'s Tauri capability rules in force. ' +
399
394
  'For Electron IPC or browser APIs, use appropriate tools for those frameworks.',
400
395
  category: TOOL_CATEGORIES.IPC_PLUGIN,
401
396
  schema: ExecuteIPCCommandSchema,
@@ -410,6 +405,7 @@ export const TOOLS = [
410
405
  return await executeIPCCommand({
411
406
  command: parsed.command,
412
407
  args: parsed.args,
408
+ windowId: parsed.windowId,
413
409
  appIdentifier: parsed.appIdentifier,
414
410
  });
415
411
  },
@@ -417,7 +413,8 @@ export const TOOLS = [
417
413
  {
418
414
  name: 'ipc_monitor',
419
415
  description: '[Tauri Apps Only] Monitor Tauri IPC calls between frontend and Rust backend. ' +
420
- 'Requires active driver_session. Captures invoke() calls and responses. ' +
416
+ 'Requires active driver_session. Captures invoke() calls made through window.__TAURI__.core. ' +
417
+ 'Bridge-internal commands (plugin:mcp-bridge|*) are excluded from capture. ' +
421
418
  'This is Tauri-specific; for browser network monitoring, use Chrome DevTools MCP.',
422
419
  category: TOOL_CATEGORIES.IPC_PLUGIN,
423
420
  schema: ManageIPCMonitoringSchema,
@@ -492,6 +489,11 @@ export const TOOLS = [
492
489
  '"list" - List all windows with labels, titles, URLs, and state. ' +
493
490
  '"info" - Get detailed info for a window (size, position, title, focus, visibility). ' +
494
491
  '"resize" - Resize a window (requires width/height, uses logical pixels by default). ' +
492
+ '"focus" - Focus a window (bring to front). ' +
493
+ '"minimize" - Minimize a window. ' +
494
+ '"maximize" - Maximize a window. ' +
495
+ 'Desktop-only actions (focus, minimize, maximize) require tauri-plugin-mcp-bridge 0.13.0+ ' +
496
+ 'and return a version error on older plugins. ' +
495
497
  'Requires active driver_session. ' +
496
498
  'For browser windows, use Chrome DevTools MCP instead.',
497
499
  category: TOOL_CATEGORIES.UI_AUTOMATION,
package/dist/version.d.ts CHANGED
@@ -13,3 +13,8 @@ export declare const PLUGIN_VERSION_FULL: string;
13
13
  * This is the major.minor version used in Cargo.toml dependency specifications.
14
14
  */
15
15
  export declare const PLUGIN_VERSION_CARGO: string;
16
+ /**
17
+ * Describe version skew between plugin and server.
18
+ * Returns null if versions match (major.minor), otherwise returns an upgrade message.
19
+ */
20
+ export declare function describeVersionSkew(pluginVersion: string | null): string | null;
package/dist/version.js CHANGED
@@ -15,3 +15,19 @@ export const PLUGIN_VERSION_FULL = pkg.version;
15
15
  * This is the major.minor version used in Cargo.toml dependency specifications.
16
16
  */
17
17
  export const PLUGIN_VERSION_CARGO = `${major}.${minor}`;
18
+ /**
19
+ * Describe version skew between plugin and server.
20
+ * Returns null if versions match (major.minor), otherwise returns an upgrade message.
21
+ */
22
+ export function describeVersionSkew(pluginVersion) {
23
+ if (!pluginVersion) {
24
+ return 'The connected app cannot report its plugin version. ' +
25
+ `Update tauri-plugin-mcp-bridge to ${PLUGIN_VERSION_CARGO} and rebuild the app.`;
26
+ }
27
+ const [pMajor, pMinor] = pluginVersion.split('.'), [sMajor, sMinor] = PLUGIN_VERSION_FULL.split('.');
28
+ if (pMajor === sMajor && pMinor === sMinor) {
29
+ return null;
30
+ }
31
+ return `Plugin ${pluginVersion} does not match server ${PLUGIN_VERSION_FULL}. ` +
32
+ `Set tauri-plugin-mcp-bridge = "${PLUGIN_VERSION_CARGO}" in Cargo.toml and rebuild the app.`;
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypothesi/tauri-mcp-server",
3
- "version": "0.12.0",
3
+ "version": "0.13.1",
4
4
  "mcpName": "io.github.hypothesi/mcp-server-tauri",
5
5
  "description": "A Model Context Protocol server for use with Tauri v2 applications",
6
6
  "type": "module",
@@ -1,47 +0,0 @@
1
- /**
2
- * Get computed CSS styles for elements
3
- *
4
- * @param {Object} params
5
- * @param {string} params.selector - CSS selector, XPath, text, or ref ID (e.g., "ref=e3") for element(s)
6
- * @param {string} params.strategy - Selector strategy: 'css', 'xpath', or 'text'
7
- * @param {string[]} params.properties - Specific CSS properties to retrieve
8
- * @param {boolean} params.multiple - Whether to get styles for all matching elements
9
- */
10
- (function(params) {
11
- const { selector, strategy, properties, multiple } = params;
12
-
13
- var elements;
14
-
15
- if (multiple) {
16
- elements = window.__MCP__.resolveAll(selector, strategy);
17
- } else {
18
- var el = window.__MCP__.resolveRef(selector, strategy);
19
- elements = el ? [el] : [];
20
- }
21
-
22
- if (!elements[0]) {
23
- throw new Error('Element not found: ' + selector);
24
- }
25
-
26
- const results = elements.map(function(element) {
27
- const styles = window.getComputedStyle(element);
28
-
29
- if (properties.length > 0) {
30
- const result = {};
31
- properties.forEach(function(prop) {
32
- result[prop] = styles.getPropertyValue(prop);
33
- });
34
- return result;
35
- }
36
-
37
- // Return all styles
38
- const allStyles = {};
39
- for (let i = 0; i < styles.length; i++) {
40
- const prop = styles[i];
41
- allStyles[prop] = styles.getPropertyValue(prop);
42
- }
43
- return allStyles;
44
- });
45
-
46
- return JSON.stringify(multiple ? results : results[0]);
47
- })
@@ -1,76 +0,0 @@
1
- /**
2
- * Keyboard interaction scripts
3
- */
4
-
5
- /**
6
- * Type text into an element
7
- * @param {Object} params
8
- * @param {string} params.selector - CSS selector for the element
9
- * @param {string} params.text - Text to type
10
- */
11
- function typeText(params) {
12
- const { selector, text } = params;
13
-
14
- const element = document.querySelector(selector);
15
- if (!element) {
16
- throw new Error(`Element not found: ${selector}`);
17
- }
18
-
19
- // Focus the element
20
- element.focus();
21
-
22
- // Set the value
23
- element.value = text;
24
-
25
- // Trigger input event for frameworks that listen to it
26
- element.dispatchEvent(new Event('input', { bubbles: true }));
27
- element.dispatchEvent(new Event('change', { bubbles: true }));
28
-
29
- return `Typed "${text}" into ${selector}`;
30
- }
31
-
32
- /**
33
- * Send keyboard events (press, down, up)
34
- * @param {Object} params
35
- * @param {string} params.action - 'press', 'down', or 'up'
36
- * @param {string} params.key - Key to press
37
- * @param {string[]} params.modifiers - Modifier keys
38
- */
39
- function keyEvent(params) {
40
- const { action, key, modifiers } = params;
41
-
42
- const eventOptions = {
43
- key: key,
44
- code: key,
45
- bubbles: true,
46
- cancelable: true,
47
- ctrlKey: modifiers.includes('Control'),
48
- altKey: modifiers.includes('Alt'),
49
- shiftKey: modifiers.includes('Shift'),
50
- metaKey: modifiers.includes('Meta'),
51
- };
52
-
53
- const activeElement = document.activeElement || document.body;
54
-
55
- if (action === 'press') {
56
- activeElement.dispatchEvent(new KeyboardEvent('keydown', eventOptions));
57
- activeElement.dispatchEvent(new KeyboardEvent('keypress', eventOptions));
58
- activeElement.dispatchEvent(new KeyboardEvent('keyup', eventOptions));
59
- return `Pressed key: ${key}${modifiers.length ? ' with ' + modifiers.join('+') : ''}`;
60
- }
61
-
62
- if (action === 'down') {
63
- activeElement.dispatchEvent(new KeyboardEvent('keydown', eventOptions));
64
- return `Key down: ${key}${modifiers.length ? ' with ' + modifiers.join('+') : ''}`;
65
- }
66
-
67
- if (action === 'up') {
68
- activeElement.dispatchEvent(new KeyboardEvent('keyup', eventOptions));
69
- return `Key up: ${key}${modifiers.length ? ' with ' + modifiers.join('+') : ''}`;
70
- }
71
-
72
- throw new Error(`Unknown action: ${action}`);
73
- }
74
-
75
- // Export for use
76
- ({ typeText, keyEvent })