Skip to main content
Glama
adamlj
by adamlj

screenshot

Capture screenshots from Android devices over WiFi for UI debugging and visual inspection during app development.

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

Implementation Reference

  • Main handler function for the 'screenshot' tool. Manages device connections, reconnections, offline cleanup, and delegates to screenshot 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; } }
  • Input schema defining parameters for the 'screenshot' tool: deviceIP, debugPort, pairingPort, pairingCode, outputPath.
    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)
    Tool registration in ListTools response, including name, description, and 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:203-205 (registration)
    Dispatch/registration in CallToolRequestSchema handler switch statement calling takeScreenshot for 'screenshot' tool.
    switch (toolName) { case 'screenshot': return await takeScreenshot(toolArgs);
  • Core helper function that performs the actual ADB screencap, pull, base64 encoding (if small), and returns image content or file path.
    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}`, }, ], }; } }
Install Server

Other 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