Skip to main content
Glama
adamlj
by adamlj

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
NameRequiredDescriptionDefault
debugPortNoWireless debugging port (different from pairing port)
deviceIPNoIP address of the Android device. Can include port like "192.168.1.100:12345"
outputPathNoPath where to save the screenshot (optional)
pairingCodeNoPairing code shown on device for initial pairing
pairingPortNoPairing port shown on device for initial pairing

Implementation Reference

  • 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; } }
  • 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}`, }, ], }; } }
  • 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);

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/adamlj/android-screenshot-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server