@hypothesi/tauri-mcp-server 0.3.1 → 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 +1 -1
- package/dist/driver/plugin-commands.js +109 -0
- package/dist/driver/session-manager.js +59 -9
- package/dist/driver/webview-executor.js +3 -1
- package/dist/index.js +3 -1
- package/dist/logger.js +13 -0
- package/dist/prompts-registry.js +42 -66
- package/dist/tools-registry.js +115 -12
- package/package.json +1 -1
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
|
-
| `
|
|
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
|
-
*
|
|
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 '
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { getPluginClient, connectPlugin } from './plugin-client.js';
|
|
3
|
+
import { createMcpLogger } from '../logger.js';
|
|
3
4
|
import { buildScreenshotScript, buildScreenshotCaptureScript, getHtml2CanvasSource, HTML2CANVAS_SCRIPT_ID, } from './scripts/html2canvas-loader.js';
|
|
4
5
|
import { registerScript, isScriptRegistered } from './script-manager.js';
|
|
5
6
|
/**
|
|
@@ -15,6 +16,7 @@ import { registerScript, isScriptRegistered } from './script-manager.js';
|
|
|
15
16
|
// Auto-Initialization System
|
|
16
17
|
// ============================================================================
|
|
17
18
|
let isInitialized = false;
|
|
19
|
+
const driverLogger = createMcpLogger('DRIVER');
|
|
18
20
|
/**
|
|
19
21
|
* Ensures the MCP server is fully initialized and ready to use.
|
|
20
22
|
* This is called automatically by all tool functions.
|
|
@@ -302,7 +304,7 @@ export async function captureScreenshot(options = {}) {
|
|
|
302
304
|
catch (nativeError) {
|
|
303
305
|
// Log the native error for debugging, then fall back
|
|
304
306
|
const nativeMsg = nativeError instanceof Error ? nativeError.message : String(nativeError);
|
|
305
|
-
|
|
307
|
+
driverLogger.error(`Native screenshot failed: ${nativeMsg}, falling back to html2canvas`);
|
|
306
308
|
}
|
|
307
309
|
// Fallback 1: Use html2canvas library for high-quality DOM rendering
|
|
308
310
|
// Try to use the script manager to register html2canvas for persistence
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,13 @@ import { dirname, join } from 'path';
|
|
|
9
9
|
// Import the single source of truth for all tools and prompts
|
|
10
10
|
import { TOOLS, TOOL_MAP } from './tools-registry.js';
|
|
11
11
|
import { PROMPTS, PROMPT_MAP } from './prompts-registry.js';
|
|
12
|
+
import { createMcpLogger } from './logger.js';
|
|
12
13
|
/* eslint-disable no-process-exit */
|
|
13
14
|
// Read version from package.json
|
|
14
15
|
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
const packageJson = JSON.parse(readFileSync(join(currentDir, '..', 'package.json'), 'utf-8'));
|
|
16
17
|
const VERSION = packageJson.version;
|
|
18
|
+
const serverLogger = createMcpLogger('SERVER');
|
|
17
19
|
// Initialize server
|
|
18
20
|
const server = new Server({
|
|
19
21
|
name: 'mcp-server-tauri',
|
|
@@ -33,7 +35,7 @@ server.onerror = (error) => {
|
|
|
33
35
|
process.exit(0);
|
|
34
36
|
}
|
|
35
37
|
// For other errors, log to stderr (will be captured by MCP client)
|
|
36
|
-
|
|
38
|
+
serverLogger.error(message);
|
|
37
39
|
};
|
|
38
40
|
// Handle connection close - exit gracefully
|
|
39
41
|
server.onclose = () => {
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function createMcpLogger(scope) {
|
|
2
|
+
return {
|
|
3
|
+
info: (...args) => {
|
|
4
|
+
console.log('[MCP][' + scope + '][INFO]', ...args);
|
|
5
|
+
},
|
|
6
|
+
warn: (...args) => {
|
|
7
|
+
console.warn('[MCP][' + scope + '][WARN]', ...args);
|
|
8
|
+
},
|
|
9
|
+
error: (...args) => {
|
|
10
|
+
console.error('[MCP][' + scope + '][ERROR]', ...args);
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
package/dist/prompts-registry.js
CHANGED
|
@@ -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
|
|
27
|
+
const SETUP_PROMPT = `Help me set up or update the MCP Bridge plugin in my Tauri project.
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## IMPORTANT: Do Not Act Without Permission
|
|
30
30
|
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
38
|
+
## Prerequisites Check
|
|
37
39
|
|
|
38
|
-
|
|
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.
|
|
52
|
+
tauri-plugin-mcp-bridge = "0.4"
|
|
43
53
|
\`\`\`
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
###
|
|
68
|
+
### 4. Plugin Permissions
|
|
69
|
+
Check \`src-tauri/capabilities/default.json\` (or similar) for \`"mcp-bridge:default"\` permission.
|
|
85
70
|
|
|
86
|
-
|
|
71
|
+
## Your Response Format
|
|
87
72
|
|
|
88
|
-
|
|
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
|
-
|
|
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
|
-
|
|
79
|
+
Only after I say yes should you make any modifications.
|
|
103
80
|
|
|
104
|
-
After
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
'
|
|
144
|
-
'
|
|
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 [
|
package/dist/tools-registry.js
CHANGED
|
@@ -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,
|
|
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: '
|
|
343
|
-
description: '[Tauri Apps Only]
|
|
344
|
-
'labels, titles, URLs, and state
|
|
345
|
-
'
|
|
346
|
-
'
|
|
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:
|
|
448
|
+
schema: ManageWindowSchema,
|
|
349
449
|
annotations: {
|
|
350
|
-
title: '
|
|
351
|
-
readOnlyHint:
|
|
450
|
+
title: 'Manage Tauri Window',
|
|
451
|
+
readOnlyHint: false,
|
|
452
|
+
destructiveHint: false,
|
|
453
|
+
idempotentHint: true,
|
|
352
454
|
openWorldHint: false,
|
|
353
455
|
},
|
|
354
|
-
handler: async () => {
|
|
355
|
-
|
|
456
|
+
handler: async (args) => {
|
|
457
|
+
const parsed = ManageWindowSchema.parse(args);
|
|
458
|
+
return await manageWindow(parsed);
|
|
356
459
|
},
|
|
357
460
|
},
|
|
358
461
|
];
|