Skip to main content
Glama

MCP Browser Screenshot Server

index.jsโ€ข11.8 kB
#!/usr/bin/env node import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js"; import puppeteer from "puppeteer"; const browserState = { browser: null, page: null, }; const viewportPresets = { mobile: { width: 375, height: 812, deviceScaleFactor: 3, isMobile: true, hasTouch: true, }, tablet: { width: 768, height: 1024, deviceScaleFactor: 2, isMobile: true, hasTouch: true, }, desktop: { width: 1920, height: 1080, deviceScaleFactor: 1, }, laptop: { width: 1366, height: 768, deviceScaleFactor: 1, }, }; const server = new Server({ name: "mcp-browser-screenshot", version: "1.0.0", }, { capabilities: { tools: {}, }, }); async function ensureBrowser() { if (!browserState.browser || !browserState.browser.isConnected()) { const headless = process.env.HEADLESS !== "false"; browserState.browser = await puppeteer.launch({ headless, args: ["--no-sandbox", "--disable-setuid-sandbox"], }); browserState.page = await browserState.browser.newPage(); } if (!browserState.page || browserState.page.isClosed()) { browserState.page = await browserState.browser.newPage(); } return { browser: browserState.browser, page: browserState.page, }; } server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "browser_launch", description: "Launch a new browser instance", inputSchema: { type: "object", properties: { headless: { type: "boolean", description: "Run browser in headless mode", default: true, }, }, }, }, { name: "browser_navigate", description: "Navigate to a URL", inputSchema: { type: "object", properties: { url: { type: "string", description: "URL to navigate to", }, waitUntil: { type: "string", enum: ["load", "domcontentloaded", "networkidle0", "networkidle2"], description: "When to consider navigation complete", default: "networkidle2", }, }, required: ["url"], }, }, { name: "browser_close", description: "Close the browser instance", inputSchema: { type: "object", properties: {}, }, }, { name: "screenshot_capture", description: "Take a screenshot of the current page", inputSchema: { type: "object", properties: { fullPage: { type: "boolean", description: "Capture full page screenshot", default: false, }, selector: { type: "string", description: "CSS selector of element to screenshot", }, format: { type: "string", enum: ["base64", "binary"], description: "Output format for the screenshot", default: "base64", }, }, }, }, { name: "screenshot_viewport", description: "Take a screenshot with specific viewport settings", inputSchema: { type: "object", properties: { preset: { type: "string", enum: ["mobile", "tablet", "desktop", "laptop"], description: "Viewport preset to use", }, width: { type: "number", description: "Custom viewport width", }, height: { type: "number", description: "Custom viewport height", }, fullPage: { type: "boolean", description: "Capture full page screenshot", default: false, }, }, }, }, { name: "browser_execute_script", description: "Execute JavaScript in the browser context", inputSchema: { type: "object", properties: { script: { type: "string", description: "JavaScript code to execute", }, }, required: ["script"], }, }, ], }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case "browser_launch": { const headless = args?.headless !== false; if (browserState.browser) { await browserState.browser.close(); } browserState.browser = await puppeteer.launch({ headless, args: ["--no-sandbox", "--disable-setuid-sandbox"], }); browserState.page = await browserState.browser.newPage(); return { content: [ { type: "text", text: "Browser launched successfully", }, ], }; } case "browser_navigate": { const { page } = await ensureBrowser(); const url = args?.url; const waitUntil = args?.waitUntil || "networkidle2"; await page.goto(url, { waitUntil }); return { content: [ { type: "text", text: `Navigated to ${url}`, }, ], }; } case "browser_close": { if (browserState.browser) { await browserState.browser.close(); browserState.browser = null; browserState.page = null; } return { content: [ { type: "text", text: "Browser closed", }, ], }; } case "screenshot_capture": { const { page } = await ensureBrowser(); const fullPage = args?.fullPage === true; const selector = args?.selector; const format = args?.format || "base64"; let screenshot; if (selector) { const element = await page.$(selector); if (!element) { throw new McpError(ErrorCode.InvalidParams, `Element with selector "${selector}" not found`); } screenshot = (await element.screenshot()); } else { screenshot = (await page.screenshot({ fullPage })); } if (format === "base64") { return { content: [ { type: "text", text: `data:image/png;base64,${screenshot.toString("base64")}`, }, ], }; } else { return { content: [ { type: "text", text: "Screenshot captured as binary data", }, ], }; } } case "screenshot_viewport": { const { page } = await ensureBrowser(); const preset = args?.preset; const width = args?.width; const height = args?.height; const fullPage = args?.fullPage === true; if (preset && viewportPresets[preset]) { await page.setViewport(viewportPresets[preset]); } else if (width && height) { await page.setViewport({ width, height }); } else { throw new McpError(ErrorCode.InvalidParams, "Either preset or width/height must be provided"); } const screenshot = (await page.screenshot({ fullPage })); return { content: [ { type: "text", text: `data:image/png;base64,${screenshot.toString("base64")}`, }, ], }; } case "browser_execute_script": { const { page } = await ensureBrowser(); const script = args?.script; const result = await page.evaluate((scriptToRun) => { return eval(scriptToRun); }, script); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } default: throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`); } } catch (error) { if (error instanceof McpError) { throw error; } throw new McpError(ErrorCode.InternalError, `Tool execution failed: ${error}`); } }); async function cleanup() { if (browserState.browser) { await browserState.browser.close(); } process.exit(0); } process.on("SIGINT", cleanup); process.on("SIGTERM", cleanup); async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("MCP Browser Screenshot Server running on stdio"); } main().catch((error) => { console.error("Server error:", error); process.exit(1); }); //# sourceMappingURL=index.js.map

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/seabassgonzalez/mcp-browser-screenshot'

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