@hypothesi/tauri-mcp-server 0.11.0 → 0.11.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.
@@ -0,0 +1 @@
1
+ export declare const SETUP_INSTRUCTIONS: string;
@@ -0,0 +1,67 @@
1
+ import { PLUGIN_VERSION_CARGO } from './version.js';
2
+ export const SETUP_INSTRUCTIONS = `Help me set up or update the MCP Bridge plugin in my Tauri project.
3
+
4
+ ## IMPORTANT: Do Not Act Without Permission
5
+
6
+ **You must NOT make any changes to files without my explicit approval.**
7
+
8
+ 1. First, examine my project to understand its current state
9
+ 2. Then, present a clear summary of what changes are needed
10
+ 3. Wait for my approval before making ANY modifications
11
+ 4. Only proceed with changes after I confirm
12
+
13
+ ## Prerequisites Check
14
+
15
+ First, verify this is a Tauri v2 project:
16
+ - Look for \`src-tauri/\` directory and \`tauri.conf.json\`
17
+ - If this is NOT a Tauri project, stop and let me know this setup only applies to Tauri apps
18
+
19
+ ## What to Check
20
+
21
+ Examine these files and report what needs to be added or updated:
22
+
23
+ ### 1. Rust Plugin Dependency
24
+ Check \`src-tauri/Cargo.toml\` for \`tauri-plugin-mcp-bridge\`. If missing or outdated, note that it needs:
25
+ \`\`\`toml
26
+ [dependencies]
27
+ tauri-plugin-mcp-bridge = "${PLUGIN_VERSION_CARGO}"
28
+ \`\`\`
29
+
30
+ ### 2. Plugin Registration
31
+ Check \`src-tauri/src/lib.rs\` or \`src-tauri/src/main.rs\` for plugin registration. It should have:
32
+ \`\`\`rust
33
+ #[cfg(debug_assertions)]
34
+ {
35
+ builder = builder.plugin(tauri_plugin_mcp_bridge::init());
36
+ }
37
+ \`\`\`
38
+
39
+ ### 3. Global Tauri Setting
40
+ Check \`src-tauri/tauri.conf.json\` for \`withGlobalTauri: true\` under the \`app\` section.
41
+ **This is required** - without it, the MCP bridge cannot communicate with the webview.
42
+
43
+ ### 4. Plugin Permissions
44
+ Check \`src-tauri/capabilities/default.json\` (or similar) for \`"mcp-bridge:default"\` permission.
45
+
46
+ ## Your Response Format
47
+
48
+ After examining the project, respond with:
49
+
50
+ 1. **Current State**: What's already configured correctly
51
+ 2. **Changes Needed**: A numbered list of specific changes required
52
+ 3. **Ask for Permission**: "May I proceed with these changes?"
53
+
54
+ Only after I say yes should you make any modifications.
55
+
56
+ ## After Setup
57
+
58
+ Once changes are approved and made:
59
+ 1. Run the Tauri app in development mode (\`cargo tauri dev\`)
60
+ 2. Use \`driver_session\` with action "start" to connect
61
+ 3. Use \`driver_session\` with action "status" to verify
62
+
63
+ ## Notes
64
+
65
+ - The plugin only runs in debug builds so it won't affect production
66
+ - The WebSocket server binds to \`0.0.0.0:9223\` by default
67
+ - For localhost-only access, use \`Builder::new().bind_address("127.0.0.1").build()\``;
@@ -10,7 +10,7 @@ import { executeInWebview, executeAsyncInWebview } from './webview-executor.js';
10
10
  import { ensureSessionAndConnect } from './plugin-client.js';
11
11
  import { SCRIPTS, buildScript } from './scripts/index.js';
12
12
  import { WindowTargetSchema } from './webview-interactions.js';
13
- import { getHtml2CanvasSource, HTML2CANVAS_SCRIPT_ID, } from './scripts/html2canvas-loader.js';
13
+ import { getHtml2CanvasSource, HTML2CANVAS_SCRIPT_ID, HTML2CANVAS_RESOLVER_SCRIPT, HTML2CANVAS_OPTIONS_SCRIPT, } from './scripts/html2canvas-loader.js';
14
14
  import { registerScript, isScriptRegistered } from './script-manager.js';
15
15
  // ============================================================================
16
16
  // Schemas
@@ -114,28 +114,14 @@ async function captureElementScreenshot(cssSelector, windowId, appIdentifier) {
114
114
  const escapedSelector = cssSelector.replace(/\\/g, '\\\\').replace(/'/g, '\\\'');
115
115
  // Build a script that captures just the element with html2canvas
116
116
  const captureScript = `
117
- const html2canvasFn = typeof html2canvas !== 'undefined' ? html2canvas :
118
- (typeof window !== 'undefined' && window.html2canvas) ? window.html2canvas :
119
- (typeof self !== 'undefined' && self.html2canvas) ? self.html2canvas :
120
- (typeof globalThis !== 'undefined' && globalThis.html2canvas) ? globalThis.html2canvas : null;
121
-
122
- if (!html2canvasFn) {
123
- throw new Error('html2canvas not loaded');
124
- }
117
+ ${HTML2CANVAS_RESOLVER_SCRIPT}
125
118
 
126
119
  const el = document.querySelector('${escapedSelector}');
127
120
  if (!el) {
128
121
  throw new Error('Element not found for screenshot');
129
122
  }
130
123
 
131
- const canvas = await html2canvasFn(el, {
132
- backgroundColor: null,
133
- scale: window.devicePixelRatio || 1,
134
- logging: false,
135
- useCORS: true,
136
- allowTaint: false,
137
- imageTimeout: 5000,
138
- });
124
+ const canvas = await html2canvasFn(el, ${HTML2CANVAS_OPTIONS_SCRIPT});
139
125
 
140
126
  if (!canvas) {
141
127
  throw new Error('html2canvas returned null canvas');
@@ -168,6 +154,23 @@ async function captureElementScreenshot(cssSelector, windowId, appIdentifier) {
168
154
  return null;
169
155
  }
170
156
  }
157
+ /**
158
+ * Common helper to format an element and capture its screenshot.
159
+ */
160
+ async function buildElementContent(element, windowId, appIdentifier) {
161
+ const content = [];
162
+ // Add formatted metadata
163
+ content.push({ type: 'text', text: formatElementMetadata(element) });
164
+ // Capture element-only screenshot (no picker overlays visible)
165
+ const screenshot = await captureElementScreenshot(element.cssSelector, windowId, appIdentifier);
166
+ if (screenshot) {
167
+ content.push(screenshot);
168
+ }
169
+ else {
170
+ content.push({ type: 'text', text: '(Element screenshot capture failed)' });
171
+ }
172
+ return content;
173
+ }
171
174
  // ============================================================================
172
175
  // selectElement - Agent-initiated picker
173
176
  // ============================================================================
@@ -217,18 +220,7 @@ export async function selectElement(options) {
217
220
  }
218
221
  // Clean up all picker UI BEFORE taking the screenshot
219
222
  await cleanupPickerHighlights(windowId, appIdentifier);
220
- const content = [];
221
- // Add formatted metadata
222
- content.push({ type: 'text', text: formatElementMetadata(element) });
223
- // Capture element-only screenshot (no picker overlays visible)
224
- const screenshot = await captureElementScreenshot(element.cssSelector, windowId, appIdentifier);
225
- if (screenshot) {
226
- content.push(screenshot);
227
- }
228
- else {
229
- content.push({ type: 'text', text: '(Element screenshot capture failed)' });
230
- }
231
- return content;
223
+ return buildElementContent(element, windowId, appIdentifier);
232
224
  }
233
225
  // ============================================================================
234
226
  // getPointedElement - Retrieve user-pointed element
@@ -257,16 +249,5 @@ export async function getPointedElement(options) {
257
249
  catch {
258
250
  return [{ type: 'text', text: `Failed to parse pointed element data: ${raw.substring(0, 200)}` }];
259
251
  }
260
- const content = [];
261
- // Add formatted metadata
262
- content.push({ type: 'text', text: formatElementMetadata(element) });
263
- // Capture element-only screenshot (no overlays)
264
- const screenshot = await captureElementScreenshot(element.cssSelector, windowId, appIdentifier);
265
- if (screenshot) {
266
- content.push(screenshot);
267
- }
268
- else {
269
- content.push({ type: 'text', text: '(Element screenshot capture failed)' });
270
- }
271
- return content;
252
+ return buildElementContent(element, windowId, appIdentifier);
272
253
  }
@@ -13,6 +13,8 @@ export declare const HTML2CANVAS_SCRIPT_ID = "__mcp_html2canvas__";
13
13
  * Loaded lazily and cached.
14
14
  */
15
15
  export declare function getHtml2CanvasSource(): string;
16
+ export declare const HTML2CANVAS_RESOLVER_SCRIPT = "\n // Get the html2canvas function (may be on window, self, or globalThis)\n const html2canvasFn = typeof html2canvas !== 'undefined' ? html2canvas :\n (typeof window !== 'undefined' && window.html2canvas) ? window.html2canvas :\n (typeof self !== 'undefined' && self.html2canvas) ? self.html2canvas :\n (typeof globalThis !== 'undefined' && globalThis.html2canvas) ? globalThis.html2canvas : null;\n\n if (!html2canvasFn) {\n throw new Error('html2canvas not loaded');\n }\n";
17
+ export declare const HTML2CANVAS_OPTIONS_SCRIPT = "{\n backgroundColor: null,\n scale: window.devicePixelRatio || 1,\n logging: false,\n useCORS: true,\n allowTaint: false,\n imageTimeout: 5000,\n }";
16
18
  /**
17
19
  * Build a script that captures a screenshot using html2canvas.
18
20
  * Assumes html2canvas is already loaded (either via script manager or inline).
@@ -30,13 +30,7 @@ export function getHtml2CanvasSource() {
30
30
  }
31
31
  return html2canvasProSource;
32
32
  }
33
- /**
34
- * Build a script that captures a screenshot using html2canvas.
35
- * Assumes html2canvas is already loaded (either via script manager or inline).
36
- */
37
- export function buildScreenshotCaptureScript(format, quality) {
38
- // Note: This script is wrapped by executeAsyncInWebview, so we don't need an IIFE
39
- return `
33
+ export const HTML2CANVAS_RESOLVER_SCRIPT = `
40
34
  // Get the html2canvas function (may be on window, self, or globalThis)
41
35
  const html2canvasFn = typeof html2canvas !== 'undefined' ? html2canvas :
42
36
  (typeof window !== 'undefined' && window.html2canvas) ? window.html2canvas :
@@ -44,27 +38,34 @@ export function buildScreenshotCaptureScript(format, quality) {
44
38
  (typeof globalThis !== 'undefined' && globalThis.html2canvas) ? globalThis.html2canvas : null;
45
39
 
46
40
  if (!html2canvasFn) {
47
- throw new Error('html2canvas not loaded - function not found on any global');
41
+ throw new Error('html2canvas not loaded');
48
42
  }
49
-
50
- // Capture the entire document
51
- const element = document.documentElement;
52
- if (!element) {
53
- throw new Error('document.documentElement is null');
54
- }
55
-
56
- // Configure html2canvas options
57
- const options = {
43
+ `;
44
+ export const HTML2CANVAS_OPTIONS_SCRIPT = `{
58
45
  backgroundColor: null,
59
46
  scale: window.devicePixelRatio || 1,
60
47
  logging: false,
61
48
  useCORS: true,
62
49
  allowTaint: false,
63
50
  imageTimeout: 5000,
64
- };
51
+ }`;
52
+ /**
53
+ * Build a script that captures a screenshot using html2canvas.
54
+ * Assumes html2canvas is already loaded (either via script manager or inline).
55
+ */
56
+ export function buildScreenshotCaptureScript(format, quality) {
57
+ // Note: This script is wrapped by executeAsyncInWebview, so we don't need an IIFE
58
+ return `
59
+ ${HTML2CANVAS_RESOLVER_SCRIPT}
60
+
61
+ // Capture the entire document
62
+ const element = document.documentElement;
63
+ if (!element) {
64
+ throw new Error('document.documentElement is null');
65
+ }
65
66
 
66
67
  // Capture the webview
67
- const canvas = await html2canvasFn(element, options);
68
+ const canvas = await html2canvasFn(element, ${HTML2CANVAS_OPTIONS_SCRIPT});
68
69
  if (!canvas) {
69
70
  throw new Error('html2canvas returned null canvas');
70
71
  }
@@ -106,17 +106,20 @@ export function buildKeyEventScript(action, key, modifiers = []) {
106
106
 
107
107
  const activeElement = document.activeElement || document.body;
108
108
 
109
+ const modStr = modifiers.length ? ' with ' + modifiers.join('+') : '';
110
+ const dispatch = (type) => activeElement.dispatchEvent(new KeyboardEvent(type, eventOptions));
111
+
109
112
  if (action === 'press') {
110
- activeElement.dispatchEvent(new KeyboardEvent('keydown', eventOptions));
111
- activeElement.dispatchEvent(new KeyboardEvent('keypress', eventOptions));
112
- activeElement.dispatchEvent(new KeyboardEvent('keyup', eventOptions));
113
- return 'Pressed key: ' + key + (modifiers.length ? ' with ' + modifiers.join('+') : '');
113
+ dispatch('keydown');
114
+ dispatch('keypress');
115
+ dispatch('keyup');
116
+ return 'Pressed key: ' + key + modStr;
114
117
  } else if (action === 'down') {
115
- activeElement.dispatchEvent(new KeyboardEvent('keydown', eventOptions));
116
- return 'Key down: ' + key + (modifiers.length ? ' with ' + modifiers.join('+') : '');
118
+ dispatch('keydown');
119
+ return 'Key down: ' + key + modStr;
117
120
  } else if (action === 'up') {
118
- activeElement.dispatchEvent(new KeyboardEvent('keyup', eventOptions));
119
- return 'Key up: ' + key + (modifiers.length ? ' with ' + modifiers.join('+') : '');
121
+ dispatch('keyup');
122
+ return 'Key up: ' + key + modStr;
120
123
  }
121
124
 
122
125
  throw new Error('Unknown action: ' + action);
@@ -2,7 +2,7 @@
2
2
  * Single source of truth for all MCP prompt definitions
3
3
  * Prompts are user-controlled templates that appear as slash commands in MCP clients
4
4
  */
5
- import { PLUGIN_VERSION_CARGO } from './version.js';
5
+ import { SETUP_INSTRUCTIONS as SETUP_PROMPT } from './constants.js';
6
6
  const FIX_WEBVIEW_ERRORS_PROMPT = `I need help finding and fixing JavaScript errors in my Tauri app's webview.
7
7
 
8
8
  Please follow these steps:
@@ -25,72 +25,6 @@ Please follow these steps:
25
25
  If no errors are found, let me know the app is running cleanly.
26
26
 
27
27
  If the session fails to start, help me troubleshoot the connection (is the app running? is the MCP bridge plugin installed?).`;
28
- const SETUP_PROMPT = `Help me set up or update the MCP Bridge plugin in my Tauri project.
29
-
30
- ## IMPORTANT: Do Not Act Without Permission
31
-
32
- **You must NOT make any changes to files without my explicit approval.**
33
-
34
- 1. First, examine my project to understand its current state
35
- 2. Then, present a clear summary of what changes are needed
36
- 3. Wait for my approval before making ANY modifications
37
- 4. Only proceed with changes after I confirm
38
-
39
- ## Prerequisites Check
40
-
41
- First, verify this is a Tauri v2 project:
42
- - Look for \`src-tauri/\` directory and \`tauri.conf.json\`
43
- - If this is NOT a Tauri project, stop and let me know this setup only applies to Tauri apps
44
-
45
- ## What to Check
46
-
47
- Examine these files and report what needs to be added or updated:
48
-
49
- ### 1. Rust Plugin Dependency
50
- Check \`src-tauri/Cargo.toml\` for \`tauri-plugin-mcp-bridge\`. If missing or outdated, note that it needs:
51
- \`\`\`toml
52
- [dependencies]
53
- tauri-plugin-mcp-bridge = "${PLUGIN_VERSION_CARGO}"
54
- \`\`\`
55
-
56
- ### 2. Plugin Registration
57
- Check \`src-tauri/src/lib.rs\` or \`src-tauri/src/main.rs\` for plugin registration. It should have:
58
- \`\`\`rust
59
- #[cfg(debug_assertions)]
60
- {
61
- builder = builder.plugin(tauri_plugin_mcp_bridge::init());
62
- }
63
- \`\`\`
64
-
65
- ### 3. Global Tauri Setting
66
- Check \`src-tauri/tauri.conf.json\` for \`withGlobalTauri: true\` under the \`app\` section.
67
- **This is required** - without it, the MCP bridge cannot communicate with the webview.
68
-
69
- ### 4. Plugin Permissions
70
- Check \`src-tauri/capabilities/default.json\` (or similar) for \`"mcp-bridge:default"\` permission.
71
-
72
- ## Your Response Format
73
-
74
- After examining the project, respond with:
75
-
76
- 1. **Current State**: What's already configured correctly
77
- 2. **Changes Needed**: A numbered list of specific changes required
78
- 3. **Ask for Permission**: "May I proceed with these changes?"
79
-
80
- Only after I say yes should you make any modifications.
81
-
82
- ## After Setup
83
-
84
- Once changes are approved and made:
85
- 1. Run the Tauri app in development mode (\`cargo tauri dev\`)
86
- 2. Use \`driver_session\` with action "start" to connect
87
- 3. Use \`driver_session\` with action "status" to verify
88
-
89
- ## Notes
90
-
91
- - The plugin only runs in debug builds so it won't affect production
92
- - The WebSocket server binds to \`0.0.0.0:9223\` by default
93
- - For localhost-only access, use \`Builder::new().bind_address("127.0.0.1").build()\``;
94
28
  const SELECT_ELEMENT_PROMPT = (message) => {
95
29
  const lines = [
96
30
  'The user wants to visually select an element in their running Tauri app so they can discuss it with you.',
@@ -9,7 +9,7 @@ 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
10
  import { interact, screenshot, keyboard, waitFor, getStyles, executeJavaScript, findElement, domSnapshot, InteractSchema, ScreenshotSchema, KeyboardSchema, WaitForSchema, GetStylesSchema, ExecuteJavaScriptSchema, FindElementSchema, DomSnapshotSchema, } from './driver/webview-interactions.js';
11
11
  import { selectElement, getPointedElement, SelectElementSchema, GetPointedElementSchema, } from './driver/element-picker.js';
12
- import { PLUGIN_VERSION_CARGO } from './version.js';
12
+ import { SETUP_INSTRUCTIONS } from './constants.js';
13
13
  /**
14
14
  * Standard multi-app description for webview tools.
15
15
  */
@@ -24,76 +24,6 @@ export const TOOL_CATEGORIES = {
24
24
  UI_AUTOMATION: 'UI Automation & WebView Interaction',
25
25
  IPC_PLUGIN: 'IPC & Plugin Tools (via MCP Bridge)',
26
26
  };
27
- // Setup instructions for the MCP Bridge plugin
28
- const SETUP_INSTRUCTIONS = `# MCP Bridge Plugin Setup Instructions
29
-
30
- Use these instructions to set up or update the MCP Bridge plugin in a Tauri v2 project.
31
-
32
- ## IMPORTANT: Do Not Act Without Permission
33
-
34
- **You must NOT make any changes to files without the user's explicit approval.**
35
-
36
- 1. First, examine the project to understand its current state
37
- 2. Then, present a clear summary of what changes are needed
38
- 3. Wait for user approval before making ANY modifications
39
- 4. Only proceed with changes after they confirm
40
-
41
- ## Prerequisites Check
42
-
43
- First, verify this is a Tauri v2 project:
44
- - Look for \`src-tauri/\` directory and \`tauri.conf.json\`
45
- - If this is NOT a Tauri project, stop and let the user know this setup only applies to Tauri apps
46
-
47
- ## What to Check
48
-
49
- Examine these files and report what needs to be added or updated:
50
-
51
- ### 1. Rust Plugin Dependency
52
- Check \`src-tauri/Cargo.toml\` for \`tauri-plugin-mcp-bridge\`. If missing or outdated, note that it needs:
53
- \`\`\`toml
54
- [dependencies]
55
- tauri-plugin-mcp-bridge = "${PLUGIN_VERSION_CARGO}"
56
- \`\`\`
57
-
58
- ### 2. Plugin Registration
59
- Check \`src-tauri/src/lib.rs\` or \`src-tauri/src/main.rs\` for plugin registration. It should have:
60
- \`\`\`rust
61
- #[cfg(debug_assertions)]
62
- {
63
- builder = builder.plugin(tauri_plugin_mcp_bridge::init());
64
- }
65
- \`\`\`
66
-
67
- ### 3. Global Tauri Setting
68
- Check \`src-tauri/tauri.conf.json\` for \`withGlobalTauri: true\` under the \`app\` section.
69
- **This is required** - without it, the MCP bridge cannot communicate with the webview.
70
-
71
- ### 4. Plugin Permissions
72
- Check \`src-tauri/capabilities/default.json\` (or similar) for \`"mcp-bridge:default"\` permission.
73
-
74
- ## Response Format
75
-
76
- After examining the project, respond with:
77
-
78
- 1. **Current State**: What's already configured correctly
79
- 2. **Changes Needed**: A numbered list of specific changes required
80
- 3. **Ask for Permission**: "May I proceed with these changes?"
81
-
82
- Only after the user says yes should you make any modifications.
83
-
84
- ## After Setup
85
-
86
- Once changes are approved and made:
87
- 1. Run the Tauri app in development mode (\`cargo tauri dev\`)
88
- 2. Use \`driver_session\` with action "start" to connect
89
- 3. Use \`driver_session\` with action "status" to verify
90
-
91
- ## Notes
92
-
93
- - The plugin only runs in debug builds so it won't affect production
94
- - The WebSocket server binds to \`0.0.0.0:9223\` by default
95
- - For localhost-only access, use \`Builder::new().bind_address("127.0.0.1").build()\`
96
- `;
97
27
  /**
98
28
  * Complete registry of all available tools
99
29
  * This is the single source of truth for tool definitions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hypothesi/tauri-mcp-server",
3
- "version": "0.11.0",
3
+ "version": "0.11.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",