screenshot
Capture screenshots from iOS Simulator to document app states, debug visual issues, or create visual records. Specify output path, format, display, and mask options.
Instructions
Takes a screenshot of the iOS Simulator
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| udid | No | Udid of target, can also be set with the IDB_UDID env var | |
| output_path | Yes | File path where the screenshot will be saved. If relative, it uses the directory specified by the `IOS_SIMULATOR_MCP_DEFAULT_OUTPUT_DIR` env var, or `~/Downloads` if not set. | |
| type | No | Image format (png, tiff, bmp, gif, or jpeg). Default is png. | |
| display | No | Display to capture (internal or external). Default depends on device type. | |
| mask | No | For non-rectangular displays, handle the mask by policy (ignored, alpha, or black) |
Implementation Reference
- src/index.ts:677-725 (handler)The handler function for the 'screenshot' tool. It resolves the device UDID, ensures the output path is absolute, executes the 'xcrun simctl io screenshot' command with provided options, checks for success message, and returns appropriate content or error.async ({ udid, output_path, type, display, mask }) => { try { const actualUdid = await getBootedDeviceId(udid); const absolutePath = ensureAbsolutePath(output_path); // command is weird, it responds with stderr on success and stdout is blank const { stderr: stdout } = await run("xcrun", [ "simctl", "io", actualUdid, "screenshot", ...(type ? [`--type=${type}`] : []), ...(display ? [`--display=${display}`] : []), ...(mask ? [`--mask=${mask}`] : []), // When passing user-provided values to a command, it's crucial to use `--` // to separate the command's options from positional arguments. // This prevents the shell from misinterpreting the arguments as options. "--", absolutePath, ]); // throw if we don't get the expected success message if (stdout && !stdout.includes("Wrote screenshot to")) { throw new Error(stdout); } return { isError: false, content: [ { type: "text", text: stdout, }, ], }; } catch (error) { return { isError: true, content: [ { type: "text", text: errorWithTroubleshooting( `Error taking screenshot: ${toError(error).message}` ), }, ], }; } }
- src/index.ts:646-676 (schema)Input schema validation using Zod for the 'screenshot' tool parameters: udid (optional UDID), output_path (required path), type (optional format), display (optional), mask (optional).{ udid: z .string() .regex(UDID_REGEX) .optional() .describe("Udid of target, can also be set with the IDB_UDID env var"), output_path: z .string() .max(1024) .describe( "File path where the screenshot will be saved. If relative, it uses the directory specified by the `IOS_SIMULATOR_MCP_DEFAULT_OUTPUT_DIR` env var, or `~/Downloads` if not set." ), type: z .enum(["png", "tiff", "bmp", "gif", "jpeg"]) .optional() .describe( "Image format (png, tiff, bmp, gif, or jpeg). Default is png." ), display: z .enum(["internal", "external"]) .optional() .describe( "Display to capture (internal or external). Default depends on device type." ), mask: z .enum(["ignored", "alpha", "black"]) .optional() .describe( "For non-rectangular displays, handle the mask by policy (ignored, alpha, or black)" ), },
- src/index.ts:643-726 (registration)Registration of the 'screenshot' tool on the MCP server, including name, description, input schema, and handler reference. Wrapped in a filter check at line 642.server.tool( "screenshot", "Takes a screenshot of the iOS Simulator", { udid: z .string() .regex(UDID_REGEX) .optional() .describe("Udid of target, can also be set with the IDB_UDID env var"), output_path: z .string() .max(1024) .describe( "File path where the screenshot will be saved. If relative, it uses the directory specified by the `IOS_SIMULATOR_MCP_DEFAULT_OUTPUT_DIR` env var, or `~/Downloads` if not set." ), type: z .enum(["png", "tiff", "bmp", "gif", "jpeg"]) .optional() .describe( "Image format (png, tiff, bmp, gif, or jpeg). Default is png." ), display: z .enum(["internal", "external"]) .optional() .describe( "Display to capture (internal or external). Default depends on device type." ), mask: z .enum(["ignored", "alpha", "black"]) .optional() .describe( "For non-rectangular displays, handle the mask by policy (ignored, alpha, or black)" ), }, async ({ udid, output_path, type, display, mask }) => { try { const actualUdid = await getBootedDeviceId(udid); const absolutePath = ensureAbsolutePath(output_path); // command is weird, it responds with stderr on success and stdout is blank const { stderr: stdout } = await run("xcrun", [ "simctl", "io", actualUdid, "screenshot", ...(type ? [`--type=${type}`] : []), ...(display ? [`--display=${display}`] : []), ...(mask ? [`--mask=${mask}`] : []), // When passing user-provided values to a command, it's crucial to use `--` // to separate the command's options from positional arguments. // This prevents the shell from misinterpreting the arguments as options. "--", absolutePath, ]); // throw if we don't get the expected success message if (stdout && !stdout.includes("Wrote screenshot to")) { throw new Error(stdout); } return { isError: false, content: [ { type: "text", text: stdout, }, ], }; } catch (error) { return { isError: true, content: [ { type: "text", text: errorWithTroubleshooting( `Error taking screenshot: ${toError(error).message}` ), }, ], }; } } );
- src/index.ts:615-640 (helper)Helper function used in the 'screenshot' handler to convert relative output paths to absolute paths, respecting environment variables and tilde expansion.function ensureAbsolutePath(filePath: string): string { if (path.isAbsolute(filePath)) { return filePath; } // Handle ~/something paths in the provided filePath if (filePath.startsWith("~/")) { return path.join(os.homedir(), filePath.slice(2)); } // Determine the default directory from env var or fallback to ~/Downloads let defaultDir = path.join(os.homedir(), "Downloads"); const customDefaultDir = process.env.IOS_SIMULATOR_MCP_DEFAULT_OUTPUT_DIR; if (customDefaultDir) { // also expand tilde for the custom directory path if (customDefaultDir.startsWith("~/")) { defaultDir = path.join(os.homedir(), customDefaultDir.slice(2)); } else { defaultDir = customDefaultDir; } } // Join the relative filePath with the resolved default directory return path.join(defaultDir, filePath); }