Skip to main content
Glama

ios_take_screenshot

Capture screenshots from iOS simulators by specifying device UDID and save path for development testing and debugging workflows.

Instructions

Take a screenshot of an iOS simulator

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
udidYesSimulator UDID
pathYesAbsolute path to save screenshot

Implementation Reference

  • Handler function that validates inputs, performs security and path checks, executes xcrun simctl to take screenshot, verifies file creation, and returns structured result.
    handler: async (args: any) => { checkMacOS(); const validation = IosSimulatorScreenshotSchema.safeParse(args); if (!validation.success) { throw new Error(`Invalid request: ${validation.error.message}`); } const { udid, path: screenshotPath } = validation.data; // Validate UDID format if (!validateUDID(udid)) { throw new Error(`Invalid simulator UDID format. UDID must be in format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX: ${udid}`); } // Validate screenshot path - must be absolute and not contain dangerous patterns if (!path.isAbsolute(screenshotPath)) { throw new Error(`Screenshot path must be absolute. Path must start with /: ${screenshotPath}`); } // Security check - prevent path traversal and access to sensitive directories const normalizedPath = path.normalize(screenshotPath); const dangerousPaths = ['/etc', '/usr', '/System', '/private', '/var']; if (dangerousPaths.some(dangerous => normalizedPath.startsWith(dangerous))) { throw new Error(`Access to this path is not allowed. Path access denied for security reasons: ${normalizedPath}`); } // Ensure file has image extension const allowedExtensions = ['.png', '.jpg', '.jpeg']; const extension = path.extname(screenshotPath).toLowerCase(); if (!allowedExtensions.includes(extension)) { throw new Error(`Screenshot file must have image extension. Allowed extensions: ${allowedExtensions.join(', ')}. Got: ${extension}`); } // Create directory if it doesn't exist const directory = path.dirname(screenshotPath); try { await fs.mkdir(directory, { recursive: true }); } catch (mkdirError) { throw new Error(`Failed to create screenshot directory: ${mkdirError}`); } const result = await processExecutor.execute('xcrun', ['simctl', 'io', udid, 'screenshot', screenshotPath]); if (result.exitCode !== 0) { throw new Error(`Failed to capture screenshot: ${result.stderr}`); } // Verify screenshot was created let fileStats; try { fileStats = await fs.stat(screenshotPath); } catch { throw new Error(`Screenshot file was not created. Expected file: ${screenshotPath}`); } return { success: true, data: { udid, screenshotPath, fileSize: fileStats.size, status: 'captured', message: 'Screenshot captured successfully', output: result.stdout, }, }; }
  • Zod schema used for input validation in the handler, defining udid and path parameters.
    * Zod validation schema for ios_simulator_screenshot tool. * * @type {z.ZodObject} * @property {string} udid - Simulator UDID * @property {string} path - Output file path for screenshot */ const IosSimulatorScreenshotSchema = z.object({ udid: z.string().min(1), path: z.string().min(1), });
  • Registration of the tool in the createIOSTools function, including name, description, inputSchema (JSON schema for MCP), and reference to handler.
    tools.set('ios_take_screenshot', { name: 'ios_take_screenshot', description: 'Take a screenshot of an iOS simulator', inputSchema: { type: 'object', properties: { udid: { type: 'string', minLength: 1, description: 'Simulator UDID' }, path: { type: 'string', minLength: 1, description: 'Absolute path to save screenshot' } }, required: ['udid', 'path'] }, handler: async (args: any) => { checkMacOS(); const validation = IosSimulatorScreenshotSchema.safeParse(args); if (!validation.success) { throw new Error(`Invalid request: ${validation.error.message}`); } const { udid, path: screenshotPath } = validation.data; // Validate UDID format if (!validateUDID(udid)) { throw new Error(`Invalid simulator UDID format. UDID must be in format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX: ${udid}`); } // Validate screenshot path - must be absolute and not contain dangerous patterns if (!path.isAbsolute(screenshotPath)) { throw new Error(`Screenshot path must be absolute. Path must start with /: ${screenshotPath}`); } // Security check - prevent path traversal and access to sensitive directories const normalizedPath = path.normalize(screenshotPath); const dangerousPaths = ['/etc', '/usr', '/System', '/private', '/var']; if (dangerousPaths.some(dangerous => normalizedPath.startsWith(dangerous))) { throw new Error(`Access to this path is not allowed. Path access denied for security reasons: ${normalizedPath}`); } // Ensure file has image extension const allowedExtensions = ['.png', '.jpg', '.jpeg']; const extension = path.extname(screenshotPath).toLowerCase(); if (!allowedExtensions.includes(extension)) { throw new Error(`Screenshot file must have image extension. Allowed extensions: ${allowedExtensions.join(', ')}. Got: ${extension}`); } // Create directory if it doesn't exist const directory = path.dirname(screenshotPath); try { await fs.mkdir(directory, { recursive: true }); } catch (mkdirError) { throw new Error(`Failed to create screenshot directory: ${mkdirError}`); } const result = await processExecutor.execute('xcrun', ['simctl', 'io', udid, 'screenshot', screenshotPath]); if (result.exitCode !== 0) { throw new Error(`Failed to capture screenshot: ${result.stderr}`); } // Verify screenshot was created let fileStats; try { fileStats = await fs.stat(screenshotPath); } catch { throw new Error(`Screenshot file was not created. Expected file: ${screenshotPath}`); } return { success: true, data: { udid, screenshotPath, fileSize: fileStats.size, status: 'captured', message: 'Screenshot captured successfully', output: result.stdout, }, }; } });
  • Helper function to validate UDID format, used in the handler.
    const validateUDID = (udid: string): boolean => { const uuidPattern = /^[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$/i; return uuidPattern.test(udid); };
  • Helper function to ensure running on macOS, called at start of handler.
    const checkMacOS = (): void => { if (process.platform !== 'darwin') { throw new Error(`iOS development tools only work on macOS. Current platform: ${process.platform}`); } };

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/cristianoaredes/mcp-mobile-server'

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