pilot_responsive
Capture full-page screenshots at mobile, tablet, and desktop breakpoints to verify responsive design. Viewport is restored after each capture.
Instructions
Capture full-page screenshots at three standard responsive breakpoints — mobile (375x812), tablet (768x1024), and desktop (1280x720). Use when the user wants to preview how a page looks across different screen sizes, test responsive design, or generate viewport comparison screenshots. The browser viewport is restored to its original size after capture.
Parameters:
output_prefix: File path prefix for the saved screenshots (default: /tmp/pilot-responsive). Files are saved as {prefix}-mobile.png, {prefix}-tablet.png, {prefix}-desktop.png
Returns: List of viewport names, dimensions, and file paths for each screenshot.
Errors:
"Output path must be within ...": The prefix path is outside the allowed directory.
Timeout: The page took too long to render at one of the viewports.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| output_prefix | No | File path prefix for screenshots |
Implementation Reference
- src/tools/visual.ts:152-193 (handler)The tool registration and handler for 'pilot_responsive'. It captures full-page screenshots at three responsive breakpoints (mobile 375x812, tablet 768x1024, desktop 1280x720) and restores the original viewport afterwards.
server.tool( 'pilot_responsive', `Capture full-page screenshots at three standard responsive breakpoints — mobile (375x812), tablet (768x1024), and desktop (1280x720). Use when the user wants to preview how a page looks across different screen sizes, test responsive design, or generate viewport comparison screenshots. The browser viewport is restored to its original size after capture. Parameters: - output_prefix: File path prefix for the saved screenshots (default: /tmp/pilot-responsive). Files are saved as {prefix}-mobile.png, {prefix}-tablet.png, {prefix}-desktop.png Returns: List of viewport names, dimensions, and file paths for each screenshot. Errors: - "Output path must be within ...": The prefix path is outside the allowed directory. - Timeout: The page took too long to render at one of the viewports.`, { output_prefix: z.string().optional().describe('File path prefix for screenshots') }, async ({ output_prefix }) => { await bm.ensureBrowser(); try { const page = bm.getPage(); const prefix = validateOutputPath(output_prefix || path.join(TEMP_DIR, 'pilot-responsive')); const viewports = [ { name: 'mobile', width: 375, height: 812 }, { name: 'tablet', width: 768, height: 1024 }, { name: 'desktop', width: 1280, height: 720 }, ]; const originalViewport = page.viewportSize(); const results: string[] = []; for (const vp of viewports) { await page.setViewportSize({ width: vp.width, height: vp.height }); const filePath = `${prefix}-${vp.name}.png`; await page.screenshot({ path: filePath, fullPage: true }); results.push(`${vp.name} (${vp.width}x${vp.height}): ${filePath}`); } if (originalViewport) await page.setViewportSize(originalViewport); return { content: [{ type: 'text' as const, text: results.join('\n') }] }; } catch (err) { return { content: [{ type: 'text' as const, text: wrapError(err) }], isError: true }; } } ); - src/tools/visual.ts:165-165 (schema)The input schema for pilot_responsive, accepting an optional 'output_prefix' string describing the file path prefix for screenshots.
{ output_prefix: z.string().optional().describe('File path prefix for screenshots') }, - src/tools/register.ts:82-82 (registration)Where registerVisualTools (which contains pilot_responsive) is called during tool registration.
registerVisualTools(effectiveServer, bm); - src/tools/visual.ts:55-55 (registration)The exported function that registers all visual tools including pilot_responsive.
export function registerVisualTools(server: McpServer, bm: BrowserManager) { - src/tools/visual.ts:13-30 (helper)The validateOutputPath helper used by pilot_responsive to validate and resolve the output prefix path.
export function validateOutputPath(outputPath: string): string { const allowed = process.env.PILOT_OUTPUT_DIR || os.tmpdir(); let normalizedAllowed: string; try { normalizedAllowed = fs.realpathSync(path.resolve(allowed)); } catch { normalizedAllowed = path.resolve(allowed); } try { const parentDir = path.dirname(outputPath); const realParent = fs.realpathSync(parentDir); const resolved = path.resolve(realParent, path.basename(outputPath)); if (!resolved.startsWith(normalizedAllowed + path.sep) && resolved !== normalizedAllowed) { throw new Error(`Output path must be within ${normalizedAllowed}: ${outputPath}`); } return resolved; } catch (err) { if (err instanceof Error && err.message.includes('Output path must be within')) {