Skip to main content
Glama
take-screenshot.ts7.5 kB
/** * Module for taking screenshots of a specific screen */ import { exec } from "node:child_process" import * as fs from "node:fs" import * as os from "node:os" import * as path from "node:path" import { promisify } from "node:util" import sharp from "sharp" const execAsync = promisify(exec) export async function takeScreenshot( url: string, options: { width?: number height?: number fullPage?: boolean waitForSelector?: string timeout?: number screenId?: number } = {} ): Promise<string> { try { const screenId = options.screenId !== undefined ? options.screenId : 1 // Create a temporary file path const tmpDir = os.tmpdir() const screenshotPath = path.join(tmpDir, `screenshot-${Date.now()}.png`) // Determine which command to use based on OS const platform = os.platform() let cmd = "" if (platform === "darwin") { // Get display info first to check if the requested display exists const screens = await getAvailableScreens() const displayExists = screens.some( (screen) => screen.id === screenId ) if (displayExists || screenId === 1) { // On macOS, use screencapture with -D flag to specify display ID cmd = `screencapture -D ${screenId} -x "${screenshotPath}"` } else { // Default to main display if the specified one doesn't exist console.warn( `Display ID ${screenId} not found, defaulting to main display` ) cmd = `screencapture -x "${screenshotPath}"` } } else if (platform === "win32") { // Windows implementation - doesn't support multiple screens yet cmd = `powershell -command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('{PRTSC}'); Start-Sleep -Milliseconds 500; $img = [System.Windows.Forms.Clipboard]::GetImage(); $img.Save('${screenshotPath}')"` } else if (platform === "linux") { // Linux implementation - doesn't support multiple screens yet cmd = `import -window root "${screenshotPath}"` } else { throw new Error(`Unsupported platform: ${platform}`) } // Execute the command await execAsync(cmd, { timeout: options.timeout || 0 }) // If screenId is 2, resize the image to 819x1456 px if (screenId === 2) { const resizedPath = path.join( tmpDir, `screenshot-resized-${Date.now()}.png` ) await sharp(screenshotPath) .resize(819, 1456, { fit: "contain", background: { r: 255, g: 255, b: 255, alpha: 1 } }) .toFile(resizedPath) // Delete the original screenshot and use the resized one await fs.promises.unlink(screenshotPath) // Read the resized screenshot file and convert to base64 const screenshotData = await fs.promises.readFile(resizedPath) const base64Data = screenshotData.toString("base64") // Clean up the temporary file await fs.promises.unlink(resizedPath) return base64Data } // For other screenIds, continue with the original behavior const screenshotData = await fs.promises.readFile(screenshotPath) const base64Data = screenshotData.toString("base64") // Clean up the temporary file await fs.promises.unlink(screenshotPath) return base64Data } catch (error) { console.error("Error taking screenshot:", error) throw new Error( `Failed to capture screenshot: ${error instanceof Error ? error.message : String(error)}` ) } } // Add a helper function to list available screens (macOS only) export async function getAvailableScreens(): Promise< { id: number; description: string }[] > { try { const platform = os.platform() if (platform === "darwin") { // Use system_profiler to get display information const { stdout } = await execAsync( "system_profiler SPDisplaysDataType" ) // Parse the system_profiler output to properly identify displays // First, separate the output into sections for each display const displayData = stdout .split("Graphics/Displays:") .filter((section) => section.trim().length > 0) if (displayData.length === 0) { // Fallback if no displays were found return [{ id: 1, description: "Main Display" }] } // Initial display list with Main Display const displays: { id: number; description: string }[] = [] // Scan through raw output and extract display sections const displaySections = stdout .split(/^\s*\w+:/m) .filter( (section) => section.includes("Display Type") || section.includes("Resolution") || section.includes("Type:") ) // Check if we're dealing with a more specific format if (displaySections.length === 0) { // Fallback to main display only return [{ id: 1, description: "Main Display" }] } // Process each section to extract display info for (let i = 0; i < displaySections.length; i++) { const section = displaySections[i] const lines = section.split("\n").map((line) => line.trim()) let displayName = "" let resolution = "" let displayType = "" // Extract relevant display information for (const line of lines) { if ( line.includes("Display Type:") || line.includes("Type:") ) { displayType = line.split(":")[1]?.trim() || "Unknown" } else if (line.includes("Resolution:")) { resolution = line.split(":")[1]?.trim() || "" } else if (line.includes("Name:")) { displayName = line.split(":")[1]?.trim() || "" } } // Combine information for a descriptive name let description = displayName || displayType || "Display" if (resolution) { description += ` (${resolution})` } // Add to display list displays.push({ id: i + 1, // Display IDs in screencapture start at 1 description }) } // If we found displays but none matched our parsing, add a fallback if (displays.length === 0) { displays.push({ id: 1, description: "Main Display" }) } // Use an alternative method: Check output from "system_profiler SPDisplaysDataType" if (displays.length <= 1) { // Try running a different command to get screen info try { const { stdout: screenInfo } = await execAsync( "system_profiler SPDisplaysDataType | grep Resolution" ) const resolutions = screenInfo .split("\n") .filter((line) => line.includes("Resolution")) // If we have multiple resolutions, we likely have multiple screens if (resolutions.length > 1) { displays.length = 0 // Clear existing displays // Add each detected display for (let i = 0; i < resolutions.length; i++) { const resolution = resolutions[i].split(":")[1]?.trim() || "" displays.push({ id: i + 1, description: i === 0 ? `Main Display (${resolution})` : `External Display ${i} (${resolution})` }) } } } catch (err) { console.error("Error getting alternative screen info:", err) } } return displays } if (platform === "win32") { // Windows implementation (placeholder) return [ { id: 0, description: "Primary Screen (multi-screen selection not supported yet)" } ] } if (platform === "linux") { // Linux implementation (placeholder) return [ { id: 0, description: "Primary Screen (multi-screen selection not supported yet)" } ] } throw new Error(`Unsupported platform: ${platform}`) } catch (error) { console.error("Error listing screens:", error) return [{ id: 1, description: "Main Display" }] } }

Implementation Reference

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/zueai/webdev-mcp'

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