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:
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