Skip to main content
Glama
mzxrai

MCP Web Research Server

by mzxrai

take_screenshot

Capture a screenshot of the current webpage using the MCP Web Research Server, enabling quick visual documentation for research and analysis tasks.

Instructions

Take a screenshot of the current page

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Main handler for executing the 'take_screenshot' tool. Captures screenshot of current page, optimizes and saves it, stores in research session as a resource, notifies clients, and returns resource URI.
    case "take_screenshot": {
        try {
            // Step 1: Capture screenshot with retry mechanism
            const screenshot = await withRetry(async () => {
                // Take and optimize screenshot with default size limits
                return await takeScreenshotWithSizeLimit(page);
            });
    
            // Step 2: Initialize session if needed
            if (!currentSession) {
                currentSession = {
                    query: "Screenshot Session",            // Session identifier
                    results: [],                            // Empty results array
                    lastUpdated: new Date().toISOString(),  // Current timestamp
                };
            }
    
            // Step 3: Get current page information
            const pageUrl = await page.url();      // Current page URL
            const pageTitle = await page.title();  // Current page title
    
            // Step 4: Save screenshot to disk
            const screenshotPath = await saveScreenshot(screenshot, pageTitle || 'untitled');
    
            // Step 5: Create and store screenshot result
            const resultIndex = currentSession ? currentSession.results.length : 0;
            addResult({
                url: pageUrl,
                title: pageTitle || "Untitled Page",  // Fallback title if none available
                content: "Screenshot taken",          // Simple content description
                timestamp: new Date().toISOString(),  // Capture time
                screenshotPath                        // Path to screenshot file
            });
    
            // Step 6: Notify clients about new screenshot resource
            server.notification({
                method: "notifications/resources/list_changed"
            });
    
            // Step 7: Return success message with resource URI
            const resourceUri = `research://screenshots/${resultIndex}`;
            return {
                content: [{
                    type: "text" as const,
                    text: `Screenshot taken successfully. You can view it via *MCP Resources* (Paperclip icon) @ URI: ${resourceUri}`
                }]
            };
        } catch (error) {
            // Handle and format screenshot errors
            return {
                content: [{
                    type: "text" as const,
                    text: `Failed to take screenshot: ${(error as Error).message}`
                }],
                isError: true
            };
        }
    }
  • Core helper function that captures the screenshot using Playwright page.screenshot(), automatically resizing the viewport to ensure the image is under 5MB, and returns base64-encoded PNG data.
    async function takeScreenshotWithSizeLimit(page: Page): Promise<string> {
        const MAX_SIZE = 5 * 1024 * 1024;
        const MAX_DIMENSION = 1920;
        const MIN_DIMENSION = 800;
    
        // Set viewport size
        await page.setViewportSize({
            width: 1600,
            height: 900
        });
    
        // Take initial screenshot
        let screenshot = await page.screenshot({
            type: 'png',
            fullPage: false
        });
    
        // Handle buffer conversion
        let buffer = screenshot;
        let attempts = 0;
        const MAX_ATTEMPTS = 3;
    
        // While screenshot is too large, reduce size
        while (buffer.length > MAX_SIZE && attempts < MAX_ATTEMPTS) {
            // Get current viewport size
            const viewport = page.viewportSize();
            if (!viewport) continue;
    
            // Calculate new dimensions
            const scaleFactor = Math.pow(0.75, attempts + 1);
            let newWidth = Math.round(viewport.width * scaleFactor);
            let newHeight = Math.round(viewport.height * scaleFactor);
    
            // Ensure dimensions are within bounds
            newWidth = Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, newWidth));
            newHeight = Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, newHeight));
    
            // Update viewport with new dimensions
            await page.setViewportSize({
                width: newWidth,
                height: newHeight
            });
    
            // Take new screenshot
            screenshot = await page.screenshot({
                type: 'png',
                fullPage: false
            });
    
            // Update buffer with new screenshot
            buffer = screenshot;
    
            // Increment retry attempts
            attempts++;
        }
    
        // Final attempt with minimum settings
        if (buffer.length > MAX_SIZE) {
            await page.setViewportSize({
                width: MIN_DIMENSION,
                height: MIN_DIMENSION
            });
    
            // Take final screenshot
            screenshot = await page.screenshot({
                type: 'png',
                fullPage: false
            });
    
            // Update buffer with final screenshot
            buffer = screenshot;
    
            // Throw error if final screenshot is still too large
            if (buffer.length > MAX_SIZE) {
                throw new McpError(
                    ErrorCode.InvalidRequest,
                    `Failed to reduce screenshot to under 5MB even with minimum settings`
                );
            }
        }
    
        // Convert Buffer to base64 string before returning
        return buffer.toString('base64');
    }
  • index.ts:156-164 (registration)
    Tool registration in the TOOLS array, including name, description, and empty input schema (no parameters required). This is returned by the list_tools handler.
        {
            name: "take_screenshot",
            description: "Take a screenshot of the current page",
            inputSchema: {
                type: "object",
                properties: {},  // No parameters needed
            },
        },
    ];
  • Helper function to save the base64 screenshot to disk in a temporary directory, with size validation and safe filename generation.
    async function saveScreenshot(screenshot: string, title: string): Promise<string> {
        // Convert screenshot from base64 to buffer
        const buffer = Buffer.from(screenshot, 'base64');
    
        // Check size before saving
        const MAX_SIZE = 5 * 1024 * 1024;  // 5MB
        if (buffer.length > MAX_SIZE) {
            throw new McpError(
                ErrorCode.InvalidRequest,
                `Screenshot too large: ${Math.round(buffer.length / (1024 * 1024))}MB exceeds ${MAX_SIZE / (1024 * 1024)}MB limit`
            );
        }
    
        // Generate a safe filename
        const timestamp = new Date().getTime();
        const safeTitle = title.replace(/[^a-z0-9]/gi, '_').toLowerCase();
        const filename = `${safeTitle}-${timestamp}.png`;
        const filepath = path.join(SCREENSHOTS_DIR, filename);
    
        // Save the validated screenshot
        await fs.promises.writeFile(filepath, buffer);
    
        // Return the filepath to the saved screenshot
        return filepath;
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the action ('take a screenshot') but doesn't explain what happens—e.g., whether it saves the screenshot, returns it as data, requires permissions, or has side effects. This leaves significant gaps in understanding the tool's behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, clear sentence with no wasted words, making it highly efficient and easy to parse. It's front-loaded with the core action, which is ideal for quick understanding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of annotations and output schema, the description is incomplete for a tool that performs an action like taking a screenshot. It doesn't cover what the tool returns, how the screenshot is handled, or any behavioral nuances, leaving the agent with insufficient context to use it effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has 0 parameters with 100% coverage, so no parameter documentation is needed. The description doesn't add parameter details, which is appropriate here, as there are no parameters to describe, aligning with the baseline for zero parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('take') and target resource ('screenshot of the current page'), making the tool's purpose immediately understandable. It doesn't explicitly differentiate from sibling tools like 'visit_page' or 'search_google', which serve different functions, but the purpose is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives or in what context it should be applied. It lacks any mention of prerequisites, such as needing a page to be loaded first, or exclusions, leaving usage entirely implicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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/mzxrai/mcp-webresearch'

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