@laststance/electron-mcp-server 1.5.2 → 1.6.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.
package/dist/index.js CHANGED
@@ -2122,6 +2122,14 @@ const CommandArgsSchema = external_zod_namespaceObject.z
2122
2122
  const SendCommandToElectronSchema = external_zod_namespaceObject.z.object({
2123
2123
  command: external_zod_namespaceObject.z.string().describe('Command to send to the Electron process'),
2124
2124
  args: CommandArgsSchema.optional().describe('Arguments for the command - must be an object with appropriate properties based on the command type'),
2125
+ targetId: external_zod_namespaceObject.z
2126
+ .string()
2127
+ .optional()
2128
+ .describe('CDP target ID to send the command to a specific window (exact match)'),
2129
+ windowTitle: external_zod_namespaceObject.z
2130
+ .string()
2131
+ .optional()
2132
+ .describe('Window title to target (case-insensitive partial match). Use list_electron_windows to see available windows.'),
2125
2133
  });
2126
2134
  const TakeScreenshotSchema = external_zod_namespaceObject.z.object({
2127
2135
  outputPath: external_zod_namespaceObject.z
@@ -2141,6 +2149,12 @@ const ReadElectronLogsSchema = external_zod_namespaceObject.z.object({
2141
2149
  const GetElectronWindowInfoSchema = external_zod_namespaceObject.z.object({
2142
2150
  includeChildren: external_zod_namespaceObject.z.boolean().optional().describe('Include child windows information'),
2143
2151
  });
2152
+ const ListElectronWindowsSchema = external_zod_namespaceObject.z.object({
2153
+ includeDevTools: external_zod_namespaceObject.z
2154
+ .boolean()
2155
+ .optional()
2156
+ .describe('Include DevTools windows in the list (default: false)'),
2157
+ });
2144
2158
 
2145
2159
  ;// ./src/tools.ts
2146
2160
 
@@ -2152,6 +2166,7 @@ var ToolName;
2152
2166
  ToolName["TAKE_SCREENSHOT"] = "take_screenshot";
2153
2167
  ToolName["READ_ELECTRON_LOGS"] = "read_electron_logs";
2154
2168
  ToolName["GET_ELECTRON_WINDOW_INFO"] = "get_electron_window_info";
2169
+ ToolName["LIST_ELECTRON_WINDOWS"] = "list_electron_windows";
2155
2170
  })(ToolName || (ToolName = {}));
2156
2171
  // Define tools available to the MCP server
2157
2172
  const tools = [
@@ -2193,9 +2208,20 @@ Examples:
2193
2208
  - send_keyboard_shortcut: {"text": "Enter"}
2194
2209
  - eval: {"code": "document.title"}
2195
2210
 
2196
- Use 'get_page_structure' or 'debug_elements' first to understand available elements, then use specific interaction commands.`,
2211
+ Use 'get_page_structure' or 'debug_elements' first to understand available elements, then use specific interaction commands.
2212
+
2213
+ Multi-window support:
2214
+ - targetId: Specify a CDP target ID to send commands to a specific window (exact match)
2215
+ - windowTitle: Specify a window title to target (case-insensitive partial match)
2216
+ - If neither is specified, commands are sent to the first available main window (backward compatible)
2217
+ - Use 'list_electron_windows' to see available windows and their IDs`,
2197
2218
  inputSchema: (0,external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(SendCommandToElectronSchema),
2198
2219
  },
2220
+ {
2221
+ name: ToolName.LIST_ELECTRON_WINDOWS,
2222
+ description: "List all available Electron window targets across all detected applications. Returns window IDs, titles, URLs, and ports. Use the returned IDs with send_command_to_electron's targetId parameter to target specific windows.",
2223
+ inputSchema: (0,external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(ListElectronWindowsSchema),
2224
+ },
2199
2225
  {
2200
2226
  name: ToolName.READ_ELECTRON_LOGS,
2201
2227
  description: 'Read console logs and output from running Electron applications. Useful for debugging and monitoring app behavior.',
@@ -2371,6 +2397,31 @@ function findMainTarget(targets) {
2371
2397
  return (targets.find((target) => target.type === 'page' && !target.title.includes('DevTools')) ||
2372
2398
  targets.find((target) => target.type === 'page'));
2373
2399
  }
2400
+ /**
2401
+ * List all available Electron window targets across all detected apps.
2402
+ * @param includeDevTools - Whether to include DevTools windows (default: false)
2403
+ * @returns Array of window targets with id, title, url, port, and type
2404
+ */
2405
+ async function listElectronWindows(includeDevTools = false) {
2406
+ const foundApps = await scanForElectronApps();
2407
+ const windows = [];
2408
+ for (const app of foundApps) {
2409
+ for (const target of app.targets) {
2410
+ // Filter out DevTools windows unless explicitly requested
2411
+ if (!includeDevTools && target.url && target.url.startsWith('devtools://')) {
2412
+ continue;
2413
+ }
2414
+ windows.push({
2415
+ id: target.id,
2416
+ title: target.title || '',
2417
+ url: target.url || '',
2418
+ port: app.port,
2419
+ type: target.type || 'page',
2420
+ });
2421
+ }
2422
+ }
2423
+ return windows;
2424
+ }
2374
2425
  /**
2375
2426
  * Get window information from any running Electron app
2376
2427
  */
@@ -2430,14 +2481,56 @@ async function getElectronWindowInfo(includeChildren = false) {
2430
2481
 
2431
2482
 
2432
2483
  /**
2433
- * Find and connect to a running Electron application
2484
+ * Find and connect to a running Electron application.
2485
+ * @param options - Optional targeting options to select a specific window
2486
+ * @returns The DevTools target matching the given options
2487
+ * @example
2488
+ * findElectronTarget() // first available main window
2489
+ * findElectronTarget({ targetId: 'ABC123' }) // exact ID match
2490
+ * findElectronTarget({ windowTitle: 'Settings' }) // partial title match
2434
2491
  */
2435
- async function findElectronTarget() {
2492
+ async function findElectronTarget(options) {
2436
2493
  logger.debug('Looking for running Electron applications...');
2437
2494
  const foundApps = await scanForElectronApps();
2438
2495
  if (foundApps.length === 0) {
2439
2496
  throw new Error('No running Electron application found with remote debugging enabled. Start your app with: electron . --remote-debugging-port=9222');
2440
2497
  }
2498
+ // If targetId is specified, search all apps for exact ID match
2499
+ if (options?.targetId) {
2500
+ for (const app of foundApps) {
2501
+ const match = app.targets.find((t) => t.id === options.targetId);
2502
+ if (match) {
2503
+ logger.debug(`Found target by ID "${options.targetId}" on port ${app.port}`);
2504
+ return {
2505
+ id: match.id,
2506
+ title: match.title,
2507
+ url: match.url,
2508
+ webSocketDebuggerUrl: match.webSocketDebuggerUrl,
2509
+ type: match.type,
2510
+ };
2511
+ }
2512
+ }
2513
+ throw new Error(`No window found with targetId "${options.targetId}". Use list_electron_windows to see available targets.`);
2514
+ }
2515
+ // If windowTitle is specified, search all apps for case-insensitive partial match
2516
+ if (options?.windowTitle) {
2517
+ const searchTitle = options.windowTitle.toLowerCase();
2518
+ for (const app of foundApps) {
2519
+ const match = app.targets.find((t) => t.title && t.title.toLowerCase().includes(searchTitle));
2520
+ if (match) {
2521
+ logger.debug(`Found target by title "${options.windowTitle}" on port ${app.port}`);
2522
+ return {
2523
+ id: match.id,
2524
+ title: match.title,
2525
+ url: match.url,
2526
+ webSocketDebuggerUrl: match.webSocketDebuggerUrl,
2527
+ type: match.type,
2528
+ };
2529
+ }
2530
+ }
2531
+ throw new Error(`No window found with title matching "${options.windowTitle}". Use list_electron_windows to see available targets.`);
2532
+ }
2533
+ // Default: use first app's main target (backward compatible)
2441
2534
  const app = foundApps[0];
2442
2535
  const mainTarget = findMainTarget(app.targets);
2443
2536
  if (!mainTarget) {
@@ -3610,11 +3703,14 @@ function generatePageStructureCommand() {
3610
3703
 
3611
3704
 
3612
3705
  /**
3613
- * Enhanced command executor with improved React support
3706
+ * Enhanced command executor with improved React support.
3707
+ * @param command - The command to execute
3708
+ * @param args - Command-specific arguments
3709
+ * @param windowOptions - Optional window targeting (targetId or windowTitle)
3614
3710
  */
3615
- async function sendCommandToElectron(command, args) {
3711
+ async function sendCommandToElectron(command, args, windowOptions) {
3616
3712
  try {
3617
- const target = await findElectronTarget();
3713
+ const target = await findElectronTarget(windowOptions);
3618
3714
  let javascriptCode;
3619
3715
  switch (command.toLowerCase()) {
3620
3716
  case 'get_title':
@@ -5661,7 +5757,7 @@ async function handleToolCall(request) {
5661
5757
  return { content, isError: false };
5662
5758
  }
5663
5759
  case ToolName.SEND_COMMAND_TO_ELECTRON: {
5664
- const { command, args: commandArgs } = SendCommandToElectronSchema.parse(args);
5760
+ const { command, args: commandArgs, targetId, windowTitle, } = SendCommandToElectronSchema.parse(args);
5665
5761
  // Execute command through security manager
5666
5762
  const securityResult = await securityManager.executeSecurely({
5667
5763
  command,
@@ -5692,8 +5788,10 @@ async function handleToolCall(request) {
5692
5788
  isError: true,
5693
5789
  };
5694
5790
  }
5791
+ // Build window target options if specified
5792
+ const windowOptions = targetId || windowTitle ? { targetId, windowTitle } : undefined;
5695
5793
  // Execute the actual command if security checks pass
5696
- const result = await sendCommandToElectron(command, commandArgs);
5794
+ const result = await sendCommandToElectron(command, commandArgs, windowOptions);
5697
5795
  return {
5698
5796
  content: [{ type: 'text', text: result }],
5699
5797
  isError: false,
@@ -5723,6 +5821,51 @@ async function handleToolCall(request) {
5723
5821
  isError: false,
5724
5822
  };
5725
5823
  }
5824
+ case ToolName.LIST_ELECTRON_WINDOWS: {
5825
+ const { includeDevTools } = ListElectronWindowsSchema.parse(args);
5826
+ const securityResult = await securityManager.executeSecurely({
5827
+ command: 'list_windows',
5828
+ args,
5829
+ sourceIP,
5830
+ userAgent,
5831
+ operationType: 'window_info',
5832
+ });
5833
+ if (securityResult.blocked) {
5834
+ return {
5835
+ content: [
5836
+ {
5837
+ type: 'text',
5838
+ text: `Operation blocked: ${securityResult.error}`,
5839
+ },
5840
+ ],
5841
+ isError: true,
5842
+ };
5843
+ }
5844
+ const windows = await listElectronWindows(includeDevTools);
5845
+ if (windows.length === 0) {
5846
+ return {
5847
+ content: [
5848
+ {
5849
+ type: 'text',
5850
+ text: 'No Electron windows found. Ensure your app is running with --remote-debugging-port=9222',
5851
+ },
5852
+ ],
5853
+ isError: false,
5854
+ };
5855
+ }
5856
+ const formatted = windows
5857
+ .map((w) => `- [${w.id}] "${w.title}" (port: ${w.port}, type: ${w.type})\n URL: ${w.url}`)
5858
+ .join('\n');
5859
+ return {
5860
+ content: [
5861
+ {
5862
+ type: 'text',
5863
+ text: `Available Electron windows (${windows.length}):\n\n${formatted}`,
5864
+ },
5865
+ ],
5866
+ isError: false,
5867
+ };
5868
+ }
5726
5869
  default:
5727
5870
  return {
5728
5871
  content: [
@@ -5765,7 +5908,7 @@ async function handleToolCall(request) {
5765
5908
 
5766
5909
 
5767
5910
 
5768
- (0,external_dotenv_namespaceObject.config)();
5911
+ (0,external_dotenv_namespaceObject.config)({ quiet: true });
5769
5912
  // Create MCP server instance
5770
5913
  const server = new Server({
5771
5914
  name: 'electron-mcp-server',