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
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query |
Implementation Reference
- index.ts:839-956 (handler)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 }; } }
- index.ts:133-144 (schema)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}` ); } });
- index.ts:1180-1183 (helper)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: