screenshot
Capture screenshots from Android devices using ADB or wireless connections. Supports debugging and visual inspection during app development by saving images directly to a specified path.
Instructions
Take a screenshot of any connected Android device. Uses existing ADB connections or connects wirelessly if device info provided.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| debugPort | No | Wireless debugging port (different from pairing port) | |
| deviceIP | No | IP address of the Android device. Can include port like "192.168.1.100:12345" | |
| outputPath | No | Path where to save the screenshot (optional) | |
| pairingCode | No | Pairing code shown on device for initial pairing | |
| pairingPort | No | Pairing port shown on device for initial pairing |
Implementation Reference
- src/index.js:289-349 (handler)Main handler function for the 'screenshot' tool. Manages screenshot operation overlaps, device connection checks, reconnection attempts, and delegates actual capture.async function takeScreenshot(args) { // Prevent overlapping screenshot operations if (isScreenshotInProgress) { return { content: [ { type: 'text', text: 'Screenshot operation already in progress. Please wait for it to complete.', }, ], }; } isScreenshotInProgress = true; try { const outputPath = args?.outputPath; // Check for existing connections const devices = await getConnectedDevices(); // Handle offline devices by disconnecting them await cleanupOfflineDevices(devices); // Check for online devices after cleanup const onlineDevices = devices.filter(d => d.status === 'device'); if (onlineDevices.length === 0) { // Try to reconnect to saved device const reconnected = await tryReconnectToSaved(); if (reconnected) { // Successfully reconnected, take screenshot return await captureScreenshotFromDevice(outputPath); } return { content: [ { type: 'text', text: 'No Android device connected. I need your device connection details to take a screenshot. Please provide:\n\n1. IP address and debug port from Settings > Developer Options > Wireless debugging\n2. Pairing code (6 digits) and pairing port from "Pair device with pairing code"\n\nOnce you give me all these details, I\'ll automatically connect and take the screenshot.', }, ], }; } // Capture screenshot return await captureScreenshotFromDevice(outputPath); } catch (error) { return { isError: true, content: [ { type: 'text', text: `Failed to check device status: ${error.message}`, }, ], }; } finally { isScreenshotInProgress = false; } }
- src/index.js:465-581 (helper)Core helper function that executes ADB commands to capture screenshot (screencap), pull file locally, handle base64 encoding for small images, and cleanup temp files.async function captureScreenshotFromDevice(outputPath) { let tempPath = null; let finalOutputPath = null; let targetDevice = null; try { // Get the first connected device const devices = await getConnectedDevices(); const onlineDevices = devices.filter(d => d.status === 'device'); if (onlineDevices.length === 0) { throw new Error('No connected Android devices found'); } // Use the first available device targetDevice = onlineDevices[0].address; const timestamp = Date.now(); tempPath = `/sdcard/temp_screenshot_${timestamp}.png`; const defaultOutputPath = path.join(os.tmpdir(), `screenshot-${timestamp}.png`); finalOutputPath = outputPath || defaultOutputPath; // Take screenshot with unique temp filename, specifying the device await execAsync(`adb -s ${targetDevice} shell screencap -p ${tempPath}`); // Pull screenshot to local machine, specifying the device await execAsync(`adb -s ${targetDevice} pull ${tempPath} "${finalOutputPath}"`); // Get file stats first const stats = await fs.stat(finalOutputPath); const fileSizeKB = (stats.size / 1024).toFixed(2); // Clean up temp file on device immediately try { await execAsync(`adb -s ${targetDevice} shell rm ${tempPath}`); } catch (cleanupError) { } // For images over 2MB, skip base64 encoding entirely to prevent call stack overflow if (stats.size > 2 * 1024 * 1024) { // 2MB threshold return { content: [ { type: 'text', text: `Screenshot captured (${fileSizeKB} KB) and saved to: ${finalOutputPath}\n\nImage too large for inline display (>2MB). File saved to disk. This typically happens with photo wallpapers - consider using a simpler background.`, }, ], }; } // For smaller images, try base64 encoding with error handling let base64Image; try { const imageBuffer = await fs.readFile(finalOutputPath); base64Image = imageBuffer.toString('base64'); // Clear buffer immediately imageBuffer.fill(0); } catch (readError) { // If base64 conversion fails, just return file path return { content: [ { type: 'text', text: `Screenshot captured (${fileSizeKB} KB) and saved to: ${finalOutputPath}\n\nCould not display image inline: ${readError.message}`, }, ], }; } // Clean up temp file if it was auto-generated if (!outputPath) { try { await fs.unlink(finalOutputPath); } catch (unlinkError) { } } return { content: [ { type: 'text', text: `Screenshot captured (${fileSizeKB} KB) and saved to: ${finalOutputPath}`, }, { type: 'image', data: base64Image, mimeType: 'image/png', }, ], }; } catch (error) { // Emergency cleanup on error if (tempPath && targetDevice) { try { await execAsync(`adb -s ${targetDevice} shell rm ${tempPath}`); } catch (e) { } } if (finalOutputPath && !outputPath) { try { await fs.unlink(finalOutputPath); } catch (e) { } } return { isError: true, content: [ { type: 'text', text: `Failed to capture screenshot: ${error.message}`, }, ], }; } }
- src/index.js:113-144 (schema)Input schema for the 'screenshot' tool, validating optional parameters for device IP/port, pairing info, and output path.inputSchema: { type: 'object', properties: { deviceIP: { type: 'string', description: 'IP address of the Android device. Can include port like "192.168.1.100:12345"', pattern: '^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(:\\d{1,5})?$', }, debugPort: { type: 'number', description: 'Wireless debugging port (different from pairing port)', minimum: 1, maximum: 65535, }, pairingPort: { type: 'number', description: 'Pairing port shown on device for initial pairing', minimum: 1, maximum: 65535, }, pairingCode: { type: 'string', description: 'Pairing code shown on device for initial pairing', pattern: '^\\d{6}$', }, outputPath: { type: 'string', description: 'Path where to save the screenshot (optional)', }, }, required: [], },
- src/index.js:110-145 (registration)Registration of the 'screenshot' tool in the ListToolsRequestSchema handler, including name, description, and reference to input schema.{ name: 'screenshot', description: 'Take a screenshot of any connected Android device. Uses existing ADB connections or connects wirelessly if device info provided.', inputSchema: { type: 'object', properties: { deviceIP: { type: 'string', description: 'IP address of the Android device. Can include port like "192.168.1.100:12345"', pattern: '^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(:\\d{1,5})?$', }, debugPort: { type: 'number', description: 'Wireless debugging port (different from pairing port)', minimum: 1, maximum: 65535, }, pairingPort: { type: 'number', description: 'Pairing port shown on device for initial pairing', minimum: 1, maximum: 65535, }, pairingCode: { type: 'string', description: 'Pairing code shown on device for initial pairing', pattern: '^\\d{6}$', }, outputPath: { type: 'string', description: 'Path where to save the screenshot (optional)', }, }, required: [], }, },
- src/index.js:204-205 (registration)Registration of the 'screenshot' tool handler in the CallToolRequestSchema switch statement, routing to takeScreenshot function.case 'screenshot': return await takeScreenshot(toolArgs);