Skip to main content
Glama
chuanmingliu

MCP Web Research Server

by chuanmingliu

take_screenshot

Capture webpage screenshots for documentation, analysis, or verification during web research tasks.

Instructions

Take a screenshot of the current page

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The primary handler for the 'take_screenshot' tool. It captures a screenshot of the current page using takeScreenshotWithSizeLimit, saves it to disk, adds it to the research session, notifies about resource changes, and returns a message with the 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
            };
        }
    }
  • Helper function that implements the screenshot capture logic with viewport resizing to ensure the image stays under 5MB size limit. Returns base64 encoded PNG.
    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-163 (registration)
    Tool registration definition including name, description, and input schema (empty, no parameters required). This is part of the TOOLS array returned by listTools.
    {
        name: "take_screenshot",
        description: "Take a screenshot of the current page",
        inputSchema: {
            type: "object",
            properties: {},  // No parameters needed
        },
    },
  • Helper function to save base64 screenshot to disk with size check 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;
    }
  • Input schema for the take_screenshot tool, which requires no parameters.
    inputSchema: {
        type: "object",
        properties: {},  // No parameters needed
    },
Behavior2/5

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

With no annotations provided, the description carries full burden but only states the basic action without disclosing behavioral traits. It doesn't mention what happens after the screenshot (e.g., saved location, format, permissions needed, or error conditions), which is critical for a tool with potential side effects.

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, efficient sentence that front-loads the core action without any wasted words. It's appropriately sized for a simple tool with no parameters, making it easy for an agent to parse quickly.

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 tool's complexity (simple action but with behavioral implications) and lack of annotations/output schema, the description is incomplete. It doesn't address what the tool returns (e.g., file path, base64 data) or error handling, leaving significant gaps for agent invocation.

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 tool has 0 parameters with 100% schema description coverage, so the baseline is 4. The description doesn't need to add parameter details, and it correctly implies no inputs are required for this operation.

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 purpose immediately understandable. However, it doesn't differentiate from potential siblings like 'capture_region' or 'record_screen', which would require explicit comparison to achieve a perfect score.

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 like 'search_google' or 'visit_page'. It lacks context about prerequisites (e.g., needing an active page) or exclusions (e.g., not working on certain page types), leaving the agent with minimal usage direction.

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

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

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