# Directory Structure ``` ├── .gitattributes ├── .gitignore ├── LICENSE ├── package.json ├── README.md ├── src │ ├── index.ts │ ├── prompts │ │ └── index.ts │ ├── resources │ │ └── index.ts │ ├── server │ │ └── server.ts │ ├── tools │ │ ├── control.ts │ │ ├── index.ts │ │ ├── keyboard.ts │ │ ├── mouse.ts │ │ ├── process.ts │ │ └── window.ts │ └── utils │ ├── logger │ │ └── logger.ts │ └── types.ts └── tsconfig.json ``` # Files -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- ``` # Auto detect text files and perform LF normalization * text=auto ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp .cache # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown # MCP Windows Desktop Automation A Model Context Protocol (MCP) server for Windows desktop automation using AutoIt. ## Overview This project provides a TypeScript MCP server that wraps the [node-autoit-koffi](https://www.npmjs.com/package/node-autoit-koffi) package, allowing LLM applications to automate Windows desktop tasks through the MCP protocol. The server exposes: - **Tools**: All AutoIt functions as MCP tools - **Resources**: File access and screenshot capabilities - **Prompts**: Templates for common automation tasks ## Features - Full wrapping of all AutoIt functions as MCP tools - Support for both stdio and WebSocket transports - File access resources for reading files and directories - Screenshot resources for capturing the screen or specific windows - Prompt templates for common automation tasks - Strict TypeScript typing throughout ## Installation ```bash # Clone the repository git clone https://github.com/yourusername/mcp-windows-desktop-automation.git cd mcp-windows-desktop-automation # Install dependencies npm install # Build the project npm run build ``` ## Usage ### Starting the Server ```bash # Start with stdio transport (default) npm start # Start with WebSocket transport npm start -- --transport=websocket --port=3000 # Enable verbose logging npm start -- --verbose ``` ### Command Line Options - `--transport=stdio|websocket`: Specify the transport protocol (default: stdio) - `--port=<number>`: Specify the port for WebSocket transport (default: 3000) - `--verbose`: Enable verbose logging ## Tools The server provides tools for: - **Mouse operations**: Move, click, drag, etc. - **Keyboard operations**: Send keystrokes, clipboard operations, etc. - **Window management**: Find, activate, close, resize windows, etc. - **Control manipulation**: Interact with UI controls, buttons, text fields, etc. - **Process management**: Start, stop, and monitor processes - **System operations**: Shutdown, sleep, etc. ## Resources The server provides resources for: - **File access**: Read files and list directories - **Screenshots**: Capture the screen or specific windows ## Prompts The server provides prompt templates for: - **Window interaction**: Find and interact with windows - **Form filling**: Automate form filling tasks - **Automation tasks**: Create scripts for repetitive tasks - **Monitoring**: Wait for specific conditions ## Development ```bash # Run in development mode npm run dev # Lint the code npm run lint # Run tests npm run test ``` ## License MIT ``` -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- ```json { "compilerOptions": { "target": "ES2020", "module": "NodeNext", "moduleResolution": "NodeNext", "esModuleInterop": true, "strict": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictPropertyInitialization": true, "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "outDir": "./dist", "sourceMap": true, "declaration": true, "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } ``` -------------------------------------------------------------------------------- /src/tools/index.ts: -------------------------------------------------------------------------------- ```typescript /** * Tools module for MCP Windows Desktop Automation */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { registerMouseTools } from './mouse'; import { registerKeyboardTools } from './keyboard'; import { registerWindowTools } from './window'; import { registerProcessTools } from './process'; import { registerControlTools } from './control'; /** * Register all AutoIt tools with the MCP server */ export function registerAllTools(server: McpServer): void { registerMouseTools(server); registerKeyboardTools(server); registerWindowTools(server); registerProcessTools(server); registerControlTools(server); } ``` -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- ```json { "name": "mcp-windows-desktop-automation", "version": "0.1.0", "description": "MCP server for Windows desktop automation", "main": "dist/index.js", "scripts": { "build": "tsc", "start": "node dist/index.js", "dev": "ts-node src/index.ts", "lint": "eslint src/**/*.ts", "test": "jest" }, "keywords": [ "mcp", "windows", "automation", "autoit" ], "author": "", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.7.0", "node-autoit-koffi": "^1.0.5", "ws": "^8.18.1" }, "devDependencies": { "@types/express": "^4.17.17", "@types/node": "^18.15.11", "@types/ws": "^8.18.0", "@typescript-eslint/eslint-plugin": "^5.57.1", "@typescript-eslint/parser": "^5.57.1", "eslint": "^8.38.0", "jest": "^29.5.0", "ts-jest": "^29.1.0", "ts-node": "^10.9.1", "typescript": "^5.0.4" } } ``` -------------------------------------------------------------------------------- /src/utils/logger/logger.ts: -------------------------------------------------------------------------------- ```typescript /** * Logger utility for MCP Windows Desktop Automation */ enum LogLevel { VERBOSE = 0, DEBUG = 1, INFO = 2, WARN = 3, ERROR = 4 } class Logger { private level: LogLevel = LogLevel.INFO; /** * Set the minimum log level * @param level The minimum level to log */ setLevel(level: LogLevel): void { this.level = level; } /** * Log verbose information * @param message The message to log * @param data Additional data to log (will be JSON stringified) */ verbose(message: string, data?: any): void { if (this.level <= LogLevel.VERBOSE) { console.log(`[VERBOSE] ${message}`, data ? JSON.stringify(data) : ''); } } /** * Log debug information * @param message The message to log * @param data Additional data to log */ debug(message: string, data?: any): void { if (this.level <= LogLevel.DEBUG) { console.log(`[DEBUG] ${message}`, data || ''); } } /** * Log general information * @param message The message to log * @param data Additional data to log */ info(message: string, data?: any): void { if (this.level <= LogLevel.INFO) { console.log(`[INFO] ${message}`, data || ''); } } /** * Log warnings * @param message The message to log * @param data Additional data to log */ warn(message: string, data?: any): void { if (this.level <= LogLevel.WARN) { console.warn(`[WARN] ${message}`, data || ''); } } /** * Log errors * @param message The message to log * @param data Additional data to log */ error(message: string, data?: any): void { if (this.level <= LogLevel.ERROR) { console.error(`[ERROR] ${message}`, data || ''); } } } export const log = new Logger(); ``` -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- ```typescript /** * Shared type definitions for MCP Windows Desktop Automation */ import { z } from 'zod'; import { CallToolResult, TextContent } from '@modelcontextprotocol/sdk/types.js'; /** * Point coordinates */ export interface Point { x: number; y: number; } /** * Rectangle coordinates */ export interface Rect { left: number; top: number; right: number; bottom: number; } /** * Standard tool response creator */ export function createToolResponse(message: string): CallToolResult { return { content: [ { type: 'text', text: message } as TextContent ] }; } /** * Standard error response creator */ export function createErrorResponse(error: Error | string): CallToolResult { const errorMessage = typeof error === 'string' ? error : error.message; return { content: [ { type: 'text', text: `Error: ${errorMessage}` } as TextContent ], isError: true }; } /** * Common Zod schemas for tool parameters */ export const schemas = { // Window identification windowTitle: z.string().describe('Window title'), windowText: z.string().optional().describe('Window text'), // Mouse parameters mouseButton: z.enum(['left', 'right', 'middle']).optional().default('left').describe('Mouse button'), mouseSpeed: z.number().min(1).max(100).optional().default(10).describe('Mouse movement speed (1-100)'), mouseX: z.number().describe('X coordinate'), mouseY: z.number().describe('Y coordinate'), mouseClicks: z.number().min(1).optional().default(1).describe('Number of clicks'), // Control parameters controlName: z.string().describe('Control identifier'), controlText: z.string().describe('Text to set/send to control'), // Process parameters processName: z.string().describe('Process name or executable path'), processTimeout: z.number().optional().describe('Timeout in milliseconds'), // Common parameters handle: z.number().describe('Window or control handle'), bufferSize: z.number().optional().describe('Buffer size for string operations') }; ``` -------------------------------------------------------------------------------- /src/server/server.ts: -------------------------------------------------------------------------------- ```typescript /** * MCP Server configuration for Windows Desktop Automation */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { WebSocket, WebSocketServer, RawData } from 'ws'; import { createServer as createHttpServer, IncomingMessage, Server as HttpServer } from 'http'; import { log } from '../utils/logger/logger'; import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js'; /** * Custom WebSocket transport for MCP */ class WebSocketServerTransport implements Transport { private ws: WebSocket | null = null; sessionId?: string; constructor(ws: WebSocket) { this.ws = ws; this.sessionId = Math.random().toString(36).substring(2, 15); ws.on('message', (data: RawData) => { try { const message = JSON.parse(data.toString()) as JSONRPCMessage; this.onmessage?.(message); } catch (error) { this.onerror?.(new Error(`Failed to parse message: ${error}`)); } }); ws.on('close', () => { this.ws = null; this.onclose?.(); }); ws.on('error', (error: Error) => { this.onerror?.(error); }); } onclose?: () => void; onerror?: (error: Error) => void; onmessage?: (message: JSONRPCMessage) => void; async start(): Promise<void> { log.info('WebSocket transport started'); } async send(message: JSONRPCMessage): Promise<void> { if (!this.ws) { throw new Error('WebSocket not connected'); } return new Promise((resolve, reject) => { this.ws!.send(JSON.stringify(message), (error?: Error) => { if (error) { reject(error); } else { resolve(); } }); }); } async close(): Promise<void> { if (this.ws) { this.ws.close(); this.ws = null; } } } /** * Server configuration options */ export interface ServerConfig { name: string; version: string; transport: 'stdio' | 'websocket'; port?: number; } /** * Create and configure an MCP server */ export async function setupServer(config: ServerConfig): Promise<{ server: McpServer; httpServer?: HttpServer; }> { // Create the MCP server const server = new McpServer({ name: config.name, version: config.version }, { capabilities: { tools: {}, resources: { subscribe: true, listChanged: true }, prompts: { listChanged: true } } }); // Configure the transport if (config.transport === 'stdio') { log.info('Using stdio transport'); const transport = new StdioServerTransport(); await server.connect(transport); return { server }; } else if (config.transport === 'websocket') { log.info(`Using WebSocket transport on port ${config.port}`); // Create HTTP server const httpServer = createHttpServer(); const wss = new WebSocketServer({ server: httpServer }); // Handle WebSocket connections wss.on('connection', async (ws: WebSocket) => { log.info('New WebSocket connection'); const transport = new WebSocketServerTransport(ws); await server.connect(transport); }); // Start HTTP server httpServer.listen(config.port || 3000); return { server, httpServer }; } else { throw new Error(`Unsupported transport: ${config.transport}`); } } ``` -------------------------------------------------------------------------------- /src/tools/keyboard.ts: -------------------------------------------------------------------------------- ```typescript /** * Keyboard-related tools for MCP Windows Desktop Automation */ import * as autoIt from 'node-autoit-koffi'; import { z } from 'zod'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { createToolResponse, createErrorResponse } from '../utils/types'; import { log } from '../utils/logger/logger'; /** * Register keyboard-related tools with the MCP server */ export function registerKeyboardTools(server: McpServer): void { // send - Send keystrokes to the active window server.tool( 'send', { text: z.string().describe('Text or keys to send'), mode: z.number().optional().default(0).describe('Send mode flag') }, async ({ text, mode }) => { try { log.verbose('send called', { text, mode }); await autoIt.init(); await autoIt.send(text, mode); return createToolResponse(`Sent keystrokes: "${text}" with mode ${mode}`); } catch (error) { log.error('send failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // clipGet - Get the text from the clipboard server.tool( 'clipGet', { bufSize: z.number().optional().describe('Buffer size for clipboard content') }, async ({ bufSize }) => { try { log.verbose('clipGet called', { bufSize }); await autoIt.init(); const clipboardContent = await autoIt.clipGet(bufSize); log.verbose('clipGet result', JSON.stringify({ clipboardContent })); return createToolResponse(`Clipboard content: "${clipboardContent}"`); } catch (error) { log.error('clipGet failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // clipPut - Put text into the clipboard server.tool( 'clipPut', { text: z.string().describe('Text to put in the clipboard') }, async ({ text }) => { try { log.verbose('clipPut called', { text }); await autoIt.init(); await autoIt.clipPut(text); return createToolResponse(`Text set to clipboard: "${text}"`); } catch (error) { log.error('clipPut failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // autoItSetOption - Set AutoIt options server.tool( 'autoItSetOption', { option: z.string().describe('Option name'), value: z.number().describe('Option value') }, async ({ option, value }) => { try { log.verbose('autoItSetOption called', { option, value }); await autoIt.init(); const result = await autoIt.autoItSetOption(option, value); return createToolResponse(`AutoIt option "${option}" set to ${value} with result: ${result}`); } catch (error) { log.error('autoItSetOption failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // opt - Alias for autoItSetOption server.tool( 'opt', { option: z.string().describe('Option name'), value: z.number().describe('Option value') }, async ({ option, value }) => { try { log.verbose('opt called', { option, value }); await autoIt.init(); const result = await autoIt.opt(option, value); return createToolResponse(`AutoIt option "${option}" set to ${value} with result: ${result}`); } catch (error) { log.error('opt failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // toolTip - Display a tooltip server.tool( 'toolTip', { text: z.string().describe('Tooltip text'), x: z.number().optional().describe('X coordinate'), y: z.number().optional().describe('Y coordinate') }, async ({ text, x, y }) => { try { log.verbose('toolTip called', { text, x, y }); await autoIt.init(); await autoIt.toolTip(text, x, y); const position = x !== undefined && y !== undefined ? ` at position (${x}, ${y})` : ''; return createToolResponse(`Tooltip displayed: "${text}"${position}`); } catch (error) { log.error('toolTip failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); } ``` -------------------------------------------------------------------------------- /src/tools/mouse.ts: -------------------------------------------------------------------------------- ```typescript /** * Mouse-related tools for MCP Windows Desktop Automation */ import * as autoIt from 'node-autoit-koffi'; import { z } from 'zod'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { createToolResponse, createErrorResponse, schemas } from '../utils/types'; import { log } from '../utils/logger/logger'; /** * Register mouse-related tools with the MCP server */ export function registerMouseTools(server: McpServer): void { // mouseMove - Move the mouse cursor to the specified coordinates server.tool( 'mouseMove', { x: schemas.mouseX, y: schemas.mouseY, speed: schemas.mouseSpeed }, async ({ x, y, speed }) => { try { log.verbose('mouseMove called', { x, y, speed }); await autoIt.init(); const result = await autoIt.mouseMove(x, y, speed); return createToolResponse(`Mouse moved to (${x}, ${y}) with result: ${result}`); } catch (error) { log.error('mouseMove failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // mouseClick - Click the mouse at the current or specified position server.tool( 'mouseClick', { button: schemas.mouseButton, x: schemas.mouseX.optional(), y: schemas.mouseY.optional(), clicks: schemas.mouseClicks, speed: schemas.mouseSpeed }, async ({ button, x, y, clicks, speed }) => { try { log.verbose('mouseClick called', { button, x, y, clicks, speed }); await autoIt.init(); const result = await autoIt.mouseClick(button, x, y, clicks, speed); return createToolResponse(`Mouse clicked ${button} button ${clicks} time(s) with result: ${result}`); } catch (error) { log.error('mouseClick failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // mouseClickDrag - Click and drag the mouse from one position to another server.tool( 'mouseClickDrag', { button: schemas.mouseButton, x1: schemas.mouseX, y1: schemas.mouseY, x2: schemas.mouseX, y2: schemas.mouseY, speed: schemas.mouseSpeed }, async ({ button, x1, y1, x2, y2, speed }) => { try { log.verbose('mouseClickDrag called', { button, x1, y1, x2, y2, speed }); await autoIt.init(); const result = await autoIt.mouseClickDrag(button, x1, y1, x2, y2, speed); return createToolResponse(`Mouse dragged from (${x1}, ${y1}) to (${x2}, ${y2}) with result: ${result}`); } catch (error) { log.error('mouseClickDrag failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // mouseDown - Press and hold the specified mouse button server.tool( 'mouseDown', { button: schemas.mouseButton }, async ({ button }) => { try { log.verbose('mouseDown called', { button }); await autoIt.init(); await autoIt.mouseDown(button); return createToolResponse(`Mouse ${button} button pressed down`); } catch (error) { log.error('mouseDown failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // mouseUp - Release the specified mouse button server.tool( 'mouseUp', { button: schemas.mouseButton }, async ({ button }) => { try { log.verbose('mouseUp called', { button }); await autoIt.init(); await autoIt.mouseUp(button); return createToolResponse(`Mouse ${button} button released`); } catch (error) { log.error('mouseUp failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // mouseGetPos - Get the current mouse cursor position server.tool( 'mouseGetPos', {}, async () => { try { log.verbose('mouseGetPos called'); await autoIt.init(); const position = await autoIt.mouseGetPos(); log.verbose('mouseGetPos result', JSON.stringify(position)); return createToolResponse(`Mouse position: (${position.x}, ${position.y})`); } catch (error) { log.error('mouseGetPos failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // mouseGetCursor - Get the current mouse cursor type server.tool( 'mouseGetCursor', {}, async () => { try { log.verbose('mouseGetCursor called'); await autoIt.init(); const cursor = await autoIt.mouseGetCursor(); log.verbose('mouseGetCursor result', JSON.stringify(cursor)); return createToolResponse(`Mouse cursor type: ${cursor}`); } catch (error) { log.error('mouseGetCursor failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // mouseWheel - Scroll the mouse wheel server.tool( 'mouseWheel', { direction: z.enum(['up', 'down']).describe('Scroll direction'), clicks: z.number().min(1).describe('Number of clicks to scroll') }, async ({ direction, clicks }) => { try { log.verbose('mouseWheel called', { direction, clicks }); await autoIt.init(); await autoIt.mouseWheel(direction, clicks); return createToolResponse(`Mouse wheel scrolled ${direction} ${clicks} click(s)`); } catch (error) { log.error('mouseWheel failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); } ``` -------------------------------------------------------------------------------- /src/resources/index.ts: -------------------------------------------------------------------------------- ```typescript /** * Resources module for MCP Windows Desktop Automation */ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js'; import * as fs from 'fs/promises'; import * as path from 'path'; import * as autoIt from 'node-autoit-koffi'; import { log } from '../utils/logger/logger'; /** * Register all resources with the MCP server */ export function registerAllResources(server: McpServer): void { // Register file resources registerFileResources(server); // Register screenshot resources registerScreenshotResources(server); } /** * Register file resources */ function registerFileResources(server: McpServer): void { server.resource( 'file', new ResourceTemplate('file://{path*}', { list: async () => { return { resources: [] }; // Empty list by default } }), async (uri, params) => { try { // Ensure filePath is a string const filePath = Array.isArray(params.path) ? params.path.join('/') : params.path; log.verbose('Reading file resource', JSON.stringify({ uri: uri.href, filePath })); // Check if file exists const stats = await fs.stat(filePath); if (stats.isDirectory()) { // List directory contents const files = await fs.readdir(filePath); const resources = await Promise.all( files.map(async (file) => { const fullPath = path.join(filePath, file); const fileStats = await fs.stat(fullPath); return { uri: `file://${fullPath.replace(/\\/g, '/')}`, name: file, description: fileStats.isDirectory() ? 'Directory' : 'File' }; }) ); return { contents: [{ uri: uri.href, text: `Directory: ${filePath}\n${files.join('\n')}`, mimeType: 'text/plain' }] }; } else { // Read file content const content = await fs.readFile(filePath); const extension = path.extname(filePath).toLowerCase(); // Determine if it's a text or binary file const isTextFile = [ '.txt', '.md', '.js', '.ts', '.html', '.css', '.json', '.xml', '.csv', '.log', '.ini', '.cfg', '.conf', '.py', '.c', '.cpp', '.h', '.java', '.sh', '.bat', '.ps1' ].includes(extension); if (isTextFile) { return { contents: [{ uri: uri.href, text: content.toString('utf-8'), mimeType: getMimeType(extension) }] }; } else { return { contents: [{ uri: uri.href, blob: content.toString('base64'), mimeType: getMimeType(extension) }] }; } } } catch (error) { log.error('Error reading file resource', error); throw new Error(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`); } } ); } /** * Register screenshot resources */ function registerScreenshotResources(server: McpServer): void { server.resource( 'screenshot', new ResourceTemplate('screenshot://{window?}', { list: async () => { return { resources: [] }; // Empty list by default } }), async (uri, params) => { try { await autoIt.init(); // Ensure window is a string if provided const windowName = params.window ? String(params.window) : undefined; log.verbose('Taking screenshot', JSON.stringify({ uri: uri.href, window: windowName })); // If window parameter is provided, activate that window first if (windowName) { const windowExists = await autoIt.winExists(windowName); if (windowExists) { await autoIt.winActivate(windowName); // Wait a moment for the window to activate await new Promise(resolve => setTimeout(resolve, 500)); } else { throw new Error(`Window "${windowName}" not found`); } } // TODO: Implement actual screenshot capture // This is a placeholder - in a real implementation, you would use // a library like 'screenshot-desktop' or other Windows API bindings // to capture the screen or specific window // For now, we'll return a placeholder message return { contents: [{ uri: uri.href, text: `Screenshot of ${windowName || 'full screen'} would be captured here`, mimeType: 'text/plain' }] }; // In a real implementation, you would return something like: /* return { contents: [{ uri: uri.href, blob: screenshotBase64Data, mimeType: 'image/png' }] }; */ } catch (error) { log.error('Error taking screenshot', error); throw new Error(`Failed to take screenshot: ${error instanceof Error ? error.message : String(error)}`); } } ); } /** * Get MIME type based on file extension */ function getMimeType(extension: string): string { const mimeTypes: Record<string, string> = { '.txt': 'text/plain', '.html': 'text/html', '.css': 'text/css', '.js': 'application/javascript', '.ts': 'application/typescript', '.json': 'application/json', '.xml': 'application/xml', '.md': 'text/markdown', '.csv': 'text/csv', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml', '.pdf': 'application/pdf', '.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', '.xls': 'application/vnd.ms-excel', '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', '.ppt': 'application/vnd.ms-powerpoint', '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation', '.zip': 'application/zip', '.rar': 'application/x-rar-compressed', '.7z': 'application/x-7z-compressed', '.tar': 'application/x-tar', '.gz': 'application/gzip' }; return mimeTypes[extension] || 'application/octet-stream'; } ``` -------------------------------------------------------------------------------- /src/prompts/index.ts: -------------------------------------------------------------------------------- ```typescript /** * Prompts module for MCP Windows Desktop Automation */ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { z } from 'zod'; import { log } from '../utils/logger/logger'; /** * Register all prompts with the MCP server */ export function registerAllPrompts(server: McpServer): void { // Register window interaction prompts registerWindowPrompts(server); // Register form filling prompts registerFormPrompts(server); // Register automation task prompts registerAutomationPrompts(server); } /** * Register window interaction prompts */ function registerWindowPrompts(server: McpServer): void { // Prompt for finding and interacting with a window server.prompt( 'findWindow', { windowTitle: z.string().describe('Title or partial title of the window to find'), action: z.enum(['activate', 'close', 'minimize', 'maximize']).describe('Action to perform on the window') }, ({ windowTitle, action }) => { log.verbose('findWindow prompt called', { windowTitle, action }); let actionDescription: string; switch (action) { case 'activate': actionDescription = 'activate (bring to front)'; break; case 'close': actionDescription = 'close'; break; case 'minimize': actionDescription = 'minimize'; break; case 'maximize': actionDescription = 'maximize'; break; } return { description: `Find a window with title "${windowTitle}" and ${actionDescription} it`, messages: [ { role: 'user', content: { type: 'text', text: `I need to find a window with the title "${windowTitle}" and ${actionDescription} it. Can you help me with the steps to do this using AutoIt functions?` } } ] }; } ); // Prompt for getting information about a window server.prompt( 'windowInfo', { windowTitle: z.string().describe('Title or partial title of the window') }, ({ windowTitle }) => { log.verbose('windowInfo prompt called', { windowTitle }); return { description: `Get information about a window with title "${windowTitle}"`, messages: [ { role: 'user', content: { type: 'text', text: `I need to get information about a window with the title "${windowTitle}". Can you help me retrieve details like its position, size, state, and text content using AutoIt functions?` } } ] }; } ); } /** * Register form filling prompts */ function registerFormPrompts(server: McpServer): void { // Prompt for filling out a form server.prompt( 'fillForm', { windowTitle: z.string().describe('Title of the window containing the form'), formFields: z.string().describe('Description of form fields and values to fill in') }, ({ windowTitle, formFields }) => { log.verbose('fillForm prompt called', { windowTitle, formFields }); return { description: `Fill out a form in window "${windowTitle}"`, messages: [ { role: 'user', content: { type: 'text', text: `I need to fill out a form in a window with the title "${windowTitle}". The form has the following fields that need to be filled:\n\n${formFields}\n\nCan you help me automate filling out this form using AutoIt functions?` } } ] }; } ); // Prompt for submitting a form server.prompt( 'submitForm', { windowTitle: z.string().describe('Title of the window containing the form'), submitButtonText: z.string().describe('Text on the submit button') }, ({ windowTitle, submitButtonText }) => { log.verbose('submitForm prompt called', { windowTitle, submitButtonText }); return { description: `Submit a form in window "${windowTitle}"`, messages: [ { role: 'user', content: { type: 'text', text: `I need to submit a form in a window with the title "${windowTitle}" by clicking the "${submitButtonText}" button. Can you help me automate this using AutoIt functions?` } } ] }; } ); } /** * Register automation task prompts */ function registerAutomationPrompts(server: McpServer): void { // Prompt for automating a repetitive task server.prompt( 'automateTask', { taskDescription: z.string().describe('Description of the repetitive task to automate'), repetitions: z.string().describe('Number of times to repeat the task') }, ({ taskDescription, repetitions }) => { log.verbose('automateTask prompt called', { taskDescription, repetitions }); return { description: `Automate a repetitive task: ${taskDescription}`, messages: [ { role: 'user', content: { type: 'text', text: `I need to automate the following repetitive task ${repetitions} times:\n\n${taskDescription}\n\nCan you help me create an automation script using AutoIt functions to accomplish this?` } } ] }; } ); // Prompt for monitoring a window or process server.prompt( 'monitorWindow', { windowTitle: z.string().describe('Title of the window to monitor'), condition: z.string().describe('Condition to monitor for (e.g., "appears", "disappears", "contains text X")') }, ({ windowTitle, condition }) => { log.verbose('monitorWindow prompt called', { windowTitle, condition }); return { description: `Monitor window "${windowTitle}" for condition: ${condition}`, messages: [ { role: 'user', content: { type: 'text', text: `I need to monitor a window with the title "${windowTitle}" and wait until the following condition is met: ${condition}. Can you help me create an automation script using AutoIt functions to accomplish this?` } } ] }; } ); // Prompt for taking a screenshot server.prompt( 'takeScreenshot', { target: z.enum(['fullscreen', 'window', 'region']).describe('What to capture in the screenshot'), windowTitle: z.string().optional().describe('Title of the window to capture (if target is "window")') }, ({ target, windowTitle }) => { log.verbose('takeScreenshot prompt called', { target, windowTitle }); let promptText: string; if (target === 'fullscreen') { promptText = 'I need to take a screenshot of the entire screen.'; } else if (target === 'window' && windowTitle) { promptText = `I need to take a screenshot of a window with the title "${windowTitle}".`; } else if (target === 'region') { promptText = 'I need to take a screenshot of a specific region of the screen.'; } else { promptText = 'I need to take a screenshot.'; } return { description: `Take a screenshot of ${target === 'window' ? `window "${windowTitle}"` : target}`, messages: [ { role: 'user', content: { type: 'text', text: `${promptText} Can you help me do this using AutoIt functions?` } } ] }; } ); } ``` -------------------------------------------------------------------------------- /src/tools/process.ts: -------------------------------------------------------------------------------- ```typescript /** * Process-related tools for MCP Windows Desktop Automation */ import * as autoIt from 'node-autoit-koffi'; import { z } from 'zod'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { createToolResponse, createErrorResponse, schemas } from '../utils/types'; import { log } from '../utils/logger/logger'; /** * Register process-related tools with the MCP server */ export function registerProcessTools(server: McpServer): void { // run - Run a program server.tool( 'run', { program: z.string().describe('Program path or command'), workingDir: z.string().optional().describe('Working directory'), showFlag: z.number().optional().describe('Window show flag') }, async ({ program, workingDir, showFlag }) => { try { log.verbose('run called', { program, workingDir, showFlag }); await autoIt.init(); const result = await autoIt.run(program, workingDir, showFlag); return createToolResponse(`Program "${program}" started with process ID: ${result}`); } catch (error) { log.error('run failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // runWait - Run a program and wait for it to complete server.tool( 'runWait', { program: z.string().describe('Program path or command'), workingDir: z.string().optional().describe('Working directory'), showFlag: z.number().optional().describe('Window show flag') }, async ({ program, workingDir, showFlag }) => { try { log.verbose('runWait called', { program, workingDir, showFlag }); await autoIt.init(); const result = await autoIt.runWait(program, workingDir, showFlag); return createToolResponse(`Program "${program}" completed with exit code: ${result}`); } catch (error) { log.error('runWait failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // runAs - Run a program as a different user server.tool( 'runAs', { user: z.string().describe('Username'), domain: z.string().describe('Domain'), password: z.string().describe('Password'), logonFlag: z.number().describe('Logon flag'), program: z.string().describe('Program path or command'), workingDir: z.string().optional().describe('Working directory'), showFlag: z.number().optional().describe('Window show flag') }, async ({ user, domain, password, logonFlag, program, workingDir, showFlag }) => { try { log.verbose('runAs called', { user, domain, logonFlag, program, workingDir, showFlag }); await autoIt.init(); const result = await autoIt.runAs(user, domain, password, logonFlag, program, workingDir, showFlag); return createToolResponse(`Program "${program}" started as user "${user}" with process ID: ${result}`); } catch (error) { log.error('runAs failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // runAsWait - Run a program as a different user and wait for it to complete server.tool( 'runAsWait', { user: z.string().describe('Username'), domain: z.string().describe('Domain'), password: z.string().describe('Password'), logonFlag: z.number().describe('Logon flag'), program: z.string().describe('Program path or command'), workingDir: z.string().optional().describe('Working directory'), showFlag: z.number().optional().describe('Window show flag') }, async ({ user, domain, password, logonFlag, program, workingDir, showFlag }) => { try { log.verbose('runAsWait called', { user, domain, logonFlag, program, workingDir, showFlag }); await autoIt.init(); const result = await autoIt.runAsWait(user, domain, password, logonFlag, program, workingDir, showFlag); return createToolResponse(`Program "${program}" completed as user "${user}" with exit code: ${result}`); } catch (error) { log.error('runAsWait failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // processExists - Check if a process exists server.tool( 'processExists', { process: schemas.processName }, async ({ process }) => { try { log.verbose('processExists called', { process }); await autoIt.init(); const result = await autoIt.processExists(process); const exists = result !== 0; return createToolResponse( exists ? `Process "${process}" exists with PID: ${result}` : `Process "${process}" does not exist` ); } catch (error) { log.error('processExists failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // processClose - Close a process server.tool( 'processClose', { process: schemas.processName }, async ({ process }) => { try { log.verbose('processClose called', { process }); await autoIt.init(); const result = await autoIt.processClose(process); const success = result === 1; return createToolResponse( success ? `Process "${process}" closed successfully` : `Failed to close process "${process}"` ); } catch (error) { log.error('processClose failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // processSetPriority - Set process priority server.tool( 'processSetPriority', { process: schemas.processName, priority: z.number().describe('Priority level (0-4)') }, async ({ process, priority }) => { try { log.verbose('processSetPriority called', { process, priority }); await autoIt.init(); const result = await autoIt.processSetPriority(process, priority); const success = result === 1; return createToolResponse( success ? `Priority for process "${process}" set to ${priority}` : `Failed to set priority for process "${process}"` ); } catch (error) { log.error('processSetPriority failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // processWait - Wait for a process to exist server.tool( 'processWait', { process: schemas.processName, timeout: schemas.processTimeout }, async ({ process, timeout }) => { try { log.verbose('processWait called', { process, timeout }); await autoIt.init(); const result = await autoIt.processWait(process, timeout); const success = result !== 0; return createToolResponse( success ? `Process "${process}" exists with PID: ${result}` : `Timed out waiting for process "${process}"` ); } catch (error) { log.error('processWait failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // processWaitClose - Wait for a process to close server.tool( 'processWaitClose', { process: schemas.processName, timeout: schemas.processTimeout }, async ({ process, timeout }) => { try { log.verbose('processWaitClose called', { process, timeout }); await autoIt.init(); const result = await autoIt.processWaitClose(process, timeout); const success = result === 1; return createToolResponse( success ? `Process "${process}" closed within the timeout` : `Timed out waiting for process "${process}" to close` ); } catch (error) { log.error('processWaitClose failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // shutdown - Shut down the system server.tool( 'shutdown', { flags: z.number().describe('Shutdown flags') }, async ({ flags }) => { try { log.verbose('shutdown called', { flags }); await autoIt.init(); const result = await autoIt.shutdown(flags); const success = result === 1; return createToolResponse( success ? `System shutdown initiated with flags: ${flags}` : `Failed to initiate system shutdown` ); } catch (error) { log.error('shutdown failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); } ``` -------------------------------------------------------------------------------- /src/tools/window.ts: -------------------------------------------------------------------------------- ```typescript /** * Window-related tools for MCP Windows Desktop Automation */ import * as autoIt from 'node-autoit-koffi'; import { z } from 'zod'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { createToolResponse, createErrorResponse, schemas } from '../utils/types'; import { log } from '../utils/logger/logger'; /** * Register window-related tools with the MCP server */ export function registerWindowTools(server: McpServer): void { // winActivate - Activate a window server.tool( 'winActivate', { title: schemas.windowTitle, text: schemas.windowText }, async ({ title, text }) => { try { log.verbose('winActivate called', { title, text }); await autoIt.init(); const result = await autoIt.winActivate(title, text); return createToolResponse(`Window "${title}" activated with result: ${result}`); } catch (error) { log.error('winActivate failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winActivateByHandle - Activate a window by handle server.tool( 'winActivateByHandle', { handle: schemas.handle }, async ({ handle }) => { try { log.verbose('winActivateByHandle called', { handle }); await autoIt.init(); const result = await autoIt.winActivateByHandle(handle); return createToolResponse(`Window with handle ${handle} activated with result: ${result}`); } catch (error) { log.error('winActivateByHandle failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winActive - Check if a window is active server.tool( 'winActive', { title: schemas.windowTitle, text: z.string().describe('Window text') }, async ({ title, text }) => { try { log.verbose('winActive called', { title, text }); await autoIt.init(); const result = await autoIt.winActive(title, text); const isActive = result === 1; return createToolResponse(`Window "${title}" is ${isActive ? 'active' : 'not active'}`); } catch (error) { log.error('winActive failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winClose - Close a window server.tool( 'winClose', { title: schemas.windowTitle, text: schemas.windowText }, async ({ title, text }) => { try { log.verbose('winClose called', { title, text }); await autoIt.init(); const result = await autoIt.winClose(title, text); return createToolResponse(`Window "${title}" closed with result: ${result}`); } catch (error) { log.error('winClose failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winExists - Check if a window exists server.tool( 'winExists', { title: schemas.windowTitle, text: schemas.windowText }, async ({ title, text }) => { try { log.verbose('winExists called', { title, text }); await autoIt.init(); const result = await autoIt.winExists(title, text); const exists = result === 1; return createToolResponse(`Window "${title}" ${exists ? 'exists' : 'does not exist'}`); } catch (error) { log.error('winExists failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winGetHandle - Get a window handle server.tool( 'winGetHandle', { title: schemas.windowTitle, text: schemas.windowText }, async ({ title, text }) => { try { log.verbose('winGetHandle called', { title, text }); await autoIt.init(); const handle = await autoIt.winGetHandle(title, text); log.verbose('winGetHandle result', JSON.stringify({ handle })); return createToolResponse(`Window "${title}" handle: ${handle}`); } catch (error) { log.error('winGetHandle failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winGetPos - Get window position and size server.tool( 'winGetPos', { title: schemas.windowTitle, text: schemas.windowText }, async ({ title, text }) => { try { log.verbose('winGetPos called', { title, text }); await autoIt.init(); const rect = await autoIt.winGetPos(title, text); log.verbose('winGetPos result', JSON.stringify(rect)); const width = rect.right - rect.left; const height = rect.bottom - rect.top; return createToolResponse( `Window "${title}" position: Left=${rect.left}, Top=${rect.top}, Width=${width}, Height=${height}` ); } catch (error) { log.error('winGetPos failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winGetText - Get window text server.tool( 'winGetText', { title: schemas.windowTitle, text: schemas.windowText, bufSize: schemas.bufferSize }, async ({ title, text, bufSize }) => { try { log.verbose('winGetText called', { title, text, bufSize }); await autoIt.init(); const windowText = await autoIt.winGetText(title, text, bufSize); log.verbose('winGetText result', JSON.stringify({ windowText })); return createToolResponse(`Window "${title}" text: "${windowText}"`); } catch (error) { log.error('winGetText failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winGetTitle - Get window title server.tool( 'winGetTitle', { title: schemas.windowTitle, text: schemas.windowText, bufSize: schemas.bufferSize }, async ({ title, text, bufSize }) => { try { log.verbose('winGetTitle called', { title, text, bufSize }); await autoIt.init(); const windowTitle = await autoIt.winGetTitle(title, text, bufSize); log.verbose('winGetTitle result', JSON.stringify({ windowTitle })); return createToolResponse(`Window "${title}" title: "${windowTitle}"`); } catch (error) { log.error('winGetTitle failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winMove - Move and resize a window server.tool( 'winMove', { title: schemas.windowTitle, text: schemas.windowText, x: z.number().describe('X coordinate'), y: z.number().describe('Y coordinate'), width: z.number().optional().describe('Window width'), height: z.number().optional().describe('Window height') }, async ({ title, text, x, y, width, height }) => { try { log.verbose('winMove called', { title, text, x, y, width, height }); await autoIt.init(); const result = await autoIt.winMove(title, text, x, y, width, height); const sizeInfo = width && height ? ` and resized to ${width}x${height}` : ''; return createToolResponse(`Window "${title}" moved to (${x}, ${y})${sizeInfo} with result: ${result}`); } catch (error) { log.error('winMove failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winSetState - Set window state (minimized, maximized, etc.) server.tool( 'winSetState', { title: schemas.windowTitle, text: schemas.windowText, flags: z.number().describe('State flags') }, async ({ title, text, flags }) => { try { log.verbose('winSetState called', { title, text, flags }); await autoIt.init(); const result = await autoIt.winSetState(title, text, flags); return createToolResponse(`Window "${title}" state set to ${flags} with result: ${result}`); } catch (error) { log.error('winSetState failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winWait - Wait for a window to exist server.tool( 'winWait', { title: schemas.windowTitle, text: schemas.windowText, timeout: z.number().optional().describe('Timeout in seconds') }, async ({ title, text, timeout }) => { try { log.verbose('winWait called', { title, text, timeout }); await autoIt.init(); const result = await autoIt.winWait(title, text, timeout); const success = result === 1; return createToolResponse( success ? `Window "${title}" appeared within the timeout` : `Window "${title}" did not appear within the timeout` ); } catch (error) { log.error('winWait failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winWaitActive - Wait for a window to be active server.tool( 'winWaitActive', { title: schemas.windowTitle, text: schemas.windowText, timeout: z.number().optional().describe('Timeout in seconds') }, async ({ title, text, timeout }) => { try { log.verbose('winWaitActive called', { title, text, timeout }); await autoIt.init(); const result = await autoIt.winWaitActive(title, text, timeout); const success = result === 1; return createToolResponse( success ? `Window "${title}" became active within the timeout` : `Window "${title}" did not become active within the timeout` ); } catch (error) { log.error('winWaitActive failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // winWaitClose - Wait for a window to close server.tool( 'winWaitClose', { title: schemas.windowTitle, text: schemas.windowText, timeout: z.number().optional().describe('Timeout in seconds') }, async ({ title, text, timeout }) => { try { log.verbose('winWaitClose called', { title, text, timeout }); await autoIt.init(); const result = await autoIt.winWaitClose(title, text, timeout); const success = result === 1; return createToolResponse( success ? `Window "${title}" closed within the timeout` : `Window "${title}" did not close within the timeout` ); } catch (error) { log.error('winWaitClose failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); } ``` -------------------------------------------------------------------------------- /src/tools/control.ts: -------------------------------------------------------------------------------- ```typescript /** * Control-related tools for MCP Windows Desktop Automation */ import * as autoIt from 'node-autoit-koffi'; import { z } from 'zod'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { createToolResponse, createErrorResponse, schemas } from '../utils/types'; import { log } from '../utils/logger/logger'; /** * Register control-related tools with the MCP server */ export function registerControlTools(server: McpServer): void { // controlClick - Click on a control server.tool( 'controlClick', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName, button: schemas.mouseButton, clicks: schemas.mouseClicks, x: z.number().optional().describe('X coordinate within control'), y: z.number().optional().describe('Y coordinate within control') }, async ({ title, text, control, button, clicks, x, y }) => { try { log.verbose('controlClick called', { title, text, control, button, clicks, x, y }); await autoIt.init(); const result = await autoIt.controlClick(title, text, control, button, clicks, x, y); const success = result === 1; return createToolResponse( success ? `Clicked on control "${control}" in window "${title}"` : `Failed to click on control "${control}" in window "${title}"` ); } catch (error) { log.error('controlClick failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlClickByHandle - Click on a control by handle server.tool( 'controlClickByHandle', { windowHandle: schemas.handle, controlHandle: schemas.handle, button: schemas.mouseButton, clicks: schemas.mouseClicks, x: z.number().optional().describe('X coordinate within control'), y: z.number().optional().describe('Y coordinate within control') }, async ({ windowHandle, controlHandle, button, clicks, x, y }) => { try { log.verbose('controlClickByHandle called', { windowHandle, controlHandle, button, clicks, x, y }); await autoIt.init(); const result = await autoIt.controlClickByHandle(windowHandle, controlHandle, button, clicks, x, y); const success = result === 1; return createToolResponse( success ? `Clicked on control handle ${controlHandle} in window handle ${windowHandle}` : `Failed to click on control handle ${controlHandle} in window handle ${windowHandle}` ); } catch (error) { log.error('controlClickByHandle failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlCommand - Send a command to a control server.tool( 'controlCommand', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName, command: z.string().describe('Command to send'), extra: z.string().optional().describe('Extra parameter for the command'), bufSize: schemas.bufferSize }, async ({ title, text, control, command, extra, bufSize }) => { try { log.verbose('controlCommand called', { title, text, control, command, extra, bufSize }); await autoIt.init(); const result = await autoIt.controlCommand(title, text, control, command, extra, bufSize); log.verbose('controlCommand result', JSON.stringify({ result })); return createToolResponse(`Command "${command}" sent to control "${control}" with result: ${result}`); } catch (error) { log.error('controlCommand failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlGetText - Get text from a control server.tool( 'controlGetText', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName, bufSize: schemas.bufferSize }, async ({ title, text, control, bufSize }) => { try { log.verbose('controlGetText called', { title, text, control, bufSize }); await autoIt.init(); const controlText = await autoIt.controlGetText(title, text, control, bufSize); log.verbose('controlGetText result', JSON.stringify({ controlText })); return createToolResponse(`Text from control "${control}": "${controlText}"`); } catch (error) { log.error('controlGetText failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlSetText - Set text in a control server.tool( 'controlSetText', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName, controlText: schemas.controlText }, async ({ title, text, control, controlText }) => { try { log.verbose('controlSetText called', { title, text, control, controlText }); await autoIt.init(); const result = await autoIt.controlSetText(title, text, control, controlText); const success = result === 1; return createToolResponse( success ? `Text set in control "${control}" to "${controlText}"` : `Failed to set text in control "${control}"` ); } catch (error) { log.error('controlSetText failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlSend - Send keystrokes to a control server.tool( 'controlSend', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName, sendText: schemas.controlText, mode: z.number().optional().describe('Send mode flag') }, async ({ title, text, control, sendText, mode }) => { try { log.verbose('controlSend called', { title, text, control, sendText, mode }); await autoIt.init(); const result = await autoIt.controlSend(title, text, control, sendText, mode); const success = result === 1; return createToolResponse( success ? `Keystrokes "${sendText}" sent to control "${control}"` : `Failed to send keystrokes to control "${control}"` ); } catch (error) { log.error('controlSend failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlFocus - Set focus to a control server.tool( 'controlFocus', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName }, async ({ title, text, control }) => { try { log.verbose('controlFocus called', { title, text, control }); await autoIt.init(); const result = await autoIt.controlFocus(title, text, control); const success = result === 1; return createToolResponse( success ? `Focus set to control "${control}"` : `Failed to set focus to control "${control}"` ); } catch (error) { log.error('controlFocus failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlGetHandle - Get a control handle server.tool( 'controlGetHandle', { windowHandle: schemas.handle, control: schemas.controlName }, async ({ windowHandle, control }) => { try { log.verbose('controlGetHandle called', { windowHandle, control }); await autoIt.init(); const handle = await autoIt.controlGetHandle(windowHandle, control); log.verbose('controlGetHandle result', JSON.stringify({ handle })); return createToolResponse(`Control "${control}" handle: ${handle}`); } catch (error) { log.error('controlGetHandle failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlGetPos - Get control position and size server.tool( 'controlGetPos', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName }, async ({ title, text, control }) => { try { log.verbose('controlGetPos called', { title, text, control }); await autoIt.init(); const rect = await autoIt.controlGetPos(title, text, control); log.verbose('controlGetPos result', JSON.stringify(rect)); const width = rect.right - rect.left; const height = rect.bottom - rect.top; return createToolResponse( `Control "${control}" position: Left=${rect.left}, Top=${rect.top}, Width=${width}, Height=${height}` ); } catch (error) { log.error('controlGetPos failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlMove - Move and resize a control server.tool( 'controlMove', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName, x: z.number().describe('X coordinate'), y: z.number().describe('Y coordinate'), width: z.number().optional().describe('Control width'), height: z.number().optional().describe('Control height') }, async ({ title, text, control, x, y, width, height }) => { try { log.verbose('controlMove called', { title, text, control, x, y, width, height }); await autoIt.init(); const result = await autoIt.controlMove(title, text, control, x, y, width, height); const success = result === 1; const sizeInfo = width && height ? ` and resized to ${width}x${height}` : ''; return createToolResponse( success ? `Control "${control}" moved to (${x}, ${y})${sizeInfo}` : `Failed to move control "${control}"` ); } catch (error) { log.error('controlMove failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlShow - Show a control server.tool( 'controlShow', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName }, async ({ title, text, control }) => { try { log.verbose('controlShow called', { title, text, control }); await autoIt.init(); const result = await autoIt.controlShow(title, text, control); const success = result === 1; return createToolResponse( success ? `Control "${control}" shown` : `Failed to show control "${control}"` ); } catch (error) { log.error('controlShow failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); // controlHide - Hide a control server.tool( 'controlHide', { title: schemas.windowTitle, text: schemas.windowText, control: schemas.controlName }, async ({ title, text, control }) => { try { log.verbose('controlHide called', { title, text, control }); await autoIt.init(); const result = await autoIt.controlHide(title, text, control); const success = result === 1; return createToolResponse( success ? `Control "${control}" hidden` : `Failed to hide control "${control}"` ); } catch (error) { log.error('controlHide failed', error); return createErrorResponse(error instanceof Error ? error : String(error)); } } ); } ```