Skip to main content
Glama
mzxrai

MCP Web Research Server

by mzxrai

search_google

Perform real-time Google searches directly through the MCP Web Research Server to retrieve up-to-date information for any query, enhancing research and knowledge integration.

Instructions

Search Google for a query

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query

Implementation Reference

  • The main handler for the 'search_google' tool within the CallToolRequestSchema handler. It uses Playwright to automate Google search: navigates to google.com, inputs the query, submits, extracts top results (title, url, snippet), stores in session, and returns JSON.
    case "search_google": {
        // Extract search query from request parameters
        const { query } = request.params.arguments as { query: string };
    
        try {
            // Execute search with retry mechanism
            const results = await withRetry(async () => {
                // Step 1: Navigate to Google search page
                await safePageNavigation(page, 'https://www.google.com');
                await dismissGoogleConsent(page);
    
                // Step 2: Find and interact with search input
                await withRetry(async () => {
                    // Wait for any search input element to appear
                    await Promise.race([
                        // Try multiple possible selectors for search input
                        page.waitForSelector('input[name="q"]', { timeout: 5000 }),
                        page.waitForSelector('textarea[name="q"]', { timeout: 5000 }),
                        page.waitForSelector('input[type="text"]', { timeout: 5000 })
                    ]).catch(() => {
                        throw new Error('Search input not found - no matching selectors');
                    });
    
                    // Find the actual search input element
                    const searchInput = await page.$('input[name="q"]') ||
                        await page.$('textarea[name="q"]') ||
                        await page.$('input[type="text"]');
    
                    // Verify search input was found
                    if (!searchInput) {
                        throw new Error('Search input element not found after waiting');
                    }
    
                    // Step 3: Enter search query
                    await searchInput.click({ clickCount: 3 });  // Select all existing text
                    await searchInput.press('Backspace');        // Clear selected text
                    await searchInput.type(query);               // Type new query
                }, 3, 2000);  // Allow 3 retries with 2s delay
    
                // Step 4: Submit search and wait for results
                await withRetry(async () => {
                    await Promise.all([
                        page.keyboard.press('Enter'),
                        page.waitForLoadState('networkidle', { timeout: 15000 }),
                    ]);
                });
    
                // Step 5: Extract search results
                const searchResults = await withRetry(async () => {
                    const results = await page.evaluate(() => {
                        // Find all search result containers
                        const elements = document.querySelectorAll('div.g');
                        if (!elements || elements.length === 0) {
                            throw new Error('No search results found');
                        }
    
                        // Extract data from each result
                        return Array.from(elements).map((el) => {
                            // Find required elements within result container
                            const titleEl = el.querySelector('h3');            // Title element
                            const linkEl = el.querySelector('a');              // Link element
                            const snippetEl = el.querySelector('div.VwiC3b');  // Snippet element
    
                            // Skip results missing required elements
                            if (!titleEl || !linkEl || !snippetEl) {
                                return null;
                            }
    
                            // Return structured result data
                            return {
                                title: titleEl.textContent || '',        // Result title
                                url: linkEl.getAttribute('href') || '',  // Result URL
                                snippet: snippetEl.textContent || '',    // Result description
                            };
                        }).filter(result => result !== null);  // Remove invalid results
                    });
    
                    // Verify we found valid results
                    if (!results || results.length === 0) {
                        throw new Error('No valid search results found');
                    }
    
                    // Return compiled list of results
                    return results;
                });
    
                // Step 6: Store results in session
                searchResults.forEach((result) => {
                    addResult({
                        url: result.url,
                        title: result.title,
                        content: result.snippet,
                        timestamp: new Date().toISOString(),
                    });
                });
    
                // Return compiled list of results
                return searchResults;
            });
    
            // Step 7: Return formatted results
            return {
                content: [{
                    type: "text",
                    text: JSON.stringify(results, null, 2)  // Pretty-print JSON results
                }]
            };
        } catch (error) {
            // Handle and format search errors
            return {
                content: [{
                    type: "text",
                    text: `Failed to perform search: ${(error as Error).message}`
                }],
                isError: true
            };
        }
    }
  • The schema definition for the 'search_google' tool, including name, description, and input schema requiring a 'query' string.
    {
        name: "search_google",
        description: "Search Google for a query",
        inputSchema: {
            type: "object",
            properties: {
                query: { type: "string", description: "Search query" },
            },
            required: ["query"],
        },
    },
    {
  • index.ts:580-582 (registration)
    Registration of the tool list handler that exposes the 'search_google' tool (via TOOLS array) to MCP clients.
    server.setRequestHandler(ListToolsRequestSchema, async () => ({
        tools: TOOLS  // Return list of available research tools
    }));
  • index.ts:833-1122 (registration)
    The CallToolRequestSchema handler registration that includes the switch dispatching to search_google implementation.
    server.setRequestHandler(CallToolRequestSchema, async (request): Promise<ToolResult> => {
        // Initialize browser for tool operations
        const page = await ensureBrowser();
    
        switch (request.params.name) {
            // Handle Google search operations
            case "search_google": {
                // Extract search query from request parameters
                const { query } = request.params.arguments as { query: string };
    
                try {
                    // Execute search with retry mechanism
                    const results = await withRetry(async () => {
                        // Step 1: Navigate to Google search page
                        await safePageNavigation(page, 'https://www.google.com');
                        await dismissGoogleConsent(page);
    
                        // Step 2: Find and interact with search input
                        await withRetry(async () => {
                            // Wait for any search input element to appear
                            await Promise.race([
                                // Try multiple possible selectors for search input
                                page.waitForSelector('input[name="q"]', { timeout: 5000 }),
                                page.waitForSelector('textarea[name="q"]', { timeout: 5000 }),
                                page.waitForSelector('input[type="text"]', { timeout: 5000 })
                            ]).catch(() => {
                                throw new Error('Search input not found - no matching selectors');
                            });
    
                            // Find the actual search input element
                            const searchInput = await page.$('input[name="q"]') ||
                                await page.$('textarea[name="q"]') ||
                                await page.$('input[type="text"]');
    
                            // Verify search input was found
                            if (!searchInput) {
                                throw new Error('Search input element not found after waiting');
                            }
    
                            // Step 3: Enter search query
                            await searchInput.click({ clickCount: 3 });  // Select all existing text
                            await searchInput.press('Backspace');        // Clear selected text
                            await searchInput.type(query);               // Type new query
                        }, 3, 2000);  // Allow 3 retries with 2s delay
    
                        // Step 4: Submit search and wait for results
                        await withRetry(async () => {
                            await Promise.all([
                                page.keyboard.press('Enter'),
                                page.waitForLoadState('networkidle', { timeout: 15000 }),
                            ]);
                        });
    
                        // Step 5: Extract search results
                        const searchResults = await withRetry(async () => {
                            const results = await page.evaluate(() => {
                                // Find all search result containers
                                const elements = document.querySelectorAll('div.g');
                                if (!elements || elements.length === 0) {
                                    throw new Error('No search results found');
                                }
    
                                // Extract data from each result
                                return Array.from(elements).map((el) => {
                                    // Find required elements within result container
                                    const titleEl = el.querySelector('h3');            // Title element
                                    const linkEl = el.querySelector('a');              // Link element
                                    const snippetEl = el.querySelector('div.VwiC3b');  // Snippet element
    
                                    // Skip results missing required elements
                                    if (!titleEl || !linkEl || !snippetEl) {
                                        return null;
                                    }
    
                                    // Return structured result data
                                    return {
                                        title: titleEl.textContent || '',        // Result title
                                        url: linkEl.getAttribute('href') || '',  // Result URL
                                        snippet: snippetEl.textContent || '',    // Result description
                                    };
                                }).filter(result => result !== null);  // Remove invalid results
                            });
    
                            // Verify we found valid results
                            if (!results || results.length === 0) {
                                throw new Error('No valid search results found');
                            }
    
                            // Return compiled list of results
                            return results;
                        });
    
                        // Step 6: Store results in session
                        searchResults.forEach((result) => {
                            addResult({
                                url: result.url,
                                title: result.title,
                                content: result.snippet,
                                timestamp: new Date().toISOString(),
                            });
                        });
    
                        // Return compiled list of results
                        return searchResults;
                    });
    
                    // Step 7: Return formatted results
                    return {
                        content: [{
                            type: "text",
                            text: JSON.stringify(results, null, 2)  // Pretty-print JSON results
                        }]
                    };
                } catch (error) {
                    // Handle and format search errors
                    return {
                        content: [{
                            type: "text",
                            text: `Failed to perform search: ${(error as Error).message}`
                        }],
                        isError: true
                    };
                }
            }
    
            // Handle webpage visit and content extraction
            case "visit_page": {
                // Extract URL and screenshot flag from request
                const { url, takeScreenshot } = request.params.arguments as {
                    url: string;                    // Target URL to visit
                    takeScreenshot?: boolean;       // Optional screenshot flag
                };
    
                // Step 1: Validate URL format and security
                if (!isValidUrl(url)) {
                    return {
                        content: [{
                            type: "text" as const,
                            text: `Invalid URL: ${url}. Only http and https protocols are supported.`
                        }],
                        isError: true
                    };
                }
    
                try {
                    // Step 2: Visit page and extract content with retry mechanism
                    const result = await withRetry(async () => {
                        // Navigate to target URL safely
                        await safePageNavigation(page, url);
                        const title = await page.title();
    
                        // Step 3: Extract and process page content
                        const content = await withRetry(async () => {
                            // Convert page content to markdown
                            const extractedContent = await extractContentAsMarkdown(page);
    
                            // If no content is extracted, throw an error
                            if (!extractedContent) {
                                throw new Error('Failed to extract content');
                            }
    
                            // Return the extracted content
                            return extractedContent;
                        });
    
                        // Step 4: Create result object with page data
                        const pageResult: ResearchResult = {
                            url,      // Original URL
                            title,    // Page title
                            content,  // Markdown content
                            timestamp: new Date().toISOString(),  // Capture time
                        };
    
                        // Step 5: Take screenshot if requested
                        let screenshotUri: string | undefined;
                        if (takeScreenshot) {
                            // Capture and process screenshot
                            const screenshot = await takeScreenshotWithSizeLimit(page);
                            pageResult.screenshotPath = await saveScreenshot(screenshot, title);
    
                            // Get the index for the resource URI
                            const resultIndex = currentSession ? currentSession.results.length : 0;
                            screenshotUri = `research://screenshots/${resultIndex}`;
    
                            // Notify clients about new screenshot resource
                            server.notification({
                                method: "notifications/resources/list_changed"
                            });
                        }
    
                        // Step 6: Store result in session
                        addResult(pageResult);
                        return { pageResult, screenshotUri };
                    });
    
                    // Step 7: Return formatted result with screenshot URI if taken
                    const response: ToolResult = {
                        content: [{
                            type: "text" as const,
                            text: JSON.stringify({
                                url: result.pageResult.url,
                                title: result.pageResult.title,
                                content: result.pageResult.content,
                                timestamp: result.pageResult.timestamp,
                                screenshot: result.screenshotUri ? `View screenshot via *MCP Resources* (Paperclip icon) @ URI: ${result.screenshotUri}` : undefined
                            }, null, 2)
                        }]
                    };
    
                    return response;
                } catch (error) {
                    // Handle and format page visit errors
                    return {
                        content: [{
                            type: "text" as const,
                            text: `Failed to visit page: ${(error as Error).message}`
                        }],
                        isError: true
                    };
                }
            }
    
            // Handle standalone screenshot requests
            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
                    };
                }
            }
    
            // Handle unknown tool requests
            default:
                throw new McpError(
                    ErrorCode.MethodNotFound,
                    `Unknown tool: ${request.params.name}`
                );
        }
    });
  • Reference to 'search_google' tool in the agentic-research prompt instructions.
    - search_google: Search for information
    - visit_page: Visit and extract content from web pages
    
    Do *NOT* use the following tools:

Tool Definition Quality

Score is being calculated. Check back soon.

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