browser.console.read
Read and filter browser console logs to capture JavaScript errors, warnings, and network errors with configurable level, time, and search filters.
Instructions
Read browser console logs with filters; returns formatted summary + stats. Captures JS errors/warnings/logs and browser-generated network errors (e.g., 'Failed to load resource'). For full HTTP payloads and headers, use 'browser.network.inspect'.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| level | No | Filter by console message level. Default: 'all' | |
| limit | No | Maximum number of entries to return. Default: no limit | |
| timeOffset | No | Time offset in seconds from current time. Use this for relative time filtering (e.g., 10 = last 10 seconds, 300 = last 5 minutes). Maximum allowed: 24 hours (86400 seconds). | |
| search | No | Search for specific text in console messages |
Implementation Reference
- browser-tools-mcp/mcp-server.ts:1709-1875 (registration)Registration of the MCP tool 'browser.console.read' with Zod input schema and inline handler function. The handler validates parameters, discovers the browser tools server if needed, constructs query params, and fetches from the backend /console-inspection endpoint, formatting the response.server.tool( "browser.console.read", "Read browser console logs with filters; returns formatted summary + stats. Captures JS errors/warnings/logs and browser-generated network errors (e.g., 'Failed to load resource'). For full HTTP payloads and headers, use 'browser.network.inspect'.", { level: z .enum(["log", "error", "warn", "info", "debug", "all"]) .optional() .describe("Filter by console message level. Default: 'all'"), limit: z .number() .optional() .describe("Maximum number of entries to return. Default: no limit"), timeOffset: z .number() .optional() .describe( "Time offset in seconds from current time. Use this for relative time filtering (e.g., 10 = last 10 seconds, 300 = last 5 minutes). Maximum allowed: 24 hours (86400 seconds)." ), search: z .string() .optional() .describe("Search for specific text in console messages"), }, async (args) => { if (!serverDiscovered) { console.error("Server not discovered, attempting discovery..."); await discoverServer(); if (!serverDiscovered) { return { content: [ { type: "text", text: "β Browser Tools Server not found. Please ensure the server is running and the Chrome extension is connected.", }, ], isError: true, }; } } try { console.error(`Inspecting browser console with filters:`, args); // Capture current time when tool is called const currentTime = Date.now(); let finalSince: number | undefined; // Handle timeOffset parameter - calculate relative time if (args.timeOffset !== undefined) { // Validate timeOffset if (args.timeOffset <= 0) { throw new Error("timeOffset must be a positive number"); } if (args.timeOffset > 86400) { throw new Error("timeOffset cannot exceed 24 hours (86400 seconds)"); } // Calculate since timestamp based on offset finalSince = currentTime - args.timeOffset * 1000; console.log( `Time offset calculation: ${args.timeOffset}s ago = ${new Date( finalSince ).toISOString()}` ); } // Build query parameters const queryParams = new URLSearchParams(); if (args.level) queryParams.append("level", args.level); if (args.limit) queryParams.append("limit", args.limit.toString()); if (finalSince) queryParams.append("since", finalSince.toString()); if (args.search) queryParams.append("search", args.search); const url = `http://${discoveredHost}:${discoveredPort}/console-inspection?${queryParams.toString()}`; console.error(`Making request to: ${url}`); const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); console.error( `Console inspection completed. Found ${ result.logs?.length || 0 } entries` ); // Format the response for the AI agent let responseText = `π **Browser Console Inspection Results**\n\n`; responseText += `π **Summary**: ${result.summary}\n\n`; if (result.stats && result.stats.total > 0) { responseText += `π **Statistics**:\n`; responseText += `- Total entries: ${result.stats.total}\n`; if (result.stats.byLevel) { responseText += `- By level: `; const levelStats = Object.entries(result.stats.byLevel) .map( ([level, count]) => `${count} ${level}${count !== 1 ? "s" : ""}` ) .join(", "); responseText += levelStats + "\n"; } if (result.stats.timeRange?.oldest && result.stats.timeRange?.newest) { const oldestDate = new Date( result.stats.timeRange.oldest ).toISOString(); const newestDate = new Date( result.stats.timeRange.newest ).toISOString(); responseText += `- Time range: ${oldestDate} to ${newestDate}\n`; } responseText += "\n"; } if (args.level || args.search || args.timeOffset || args.limit) { responseText += `π§ **Applied Filters**:\n`; if (args.level) responseText += `- Level: ${args.level}\n`; if (args.search) responseText += `- Search: "${args.search}"\n`; if (args.timeOffset) responseText += `- Time Offset: ${args.timeOffset} seconds ago\n`; if (args.limit) responseText += `- Limit: ${args.limit} entries\n`; responseText += "\n"; } if (result.formatted && result.logs?.length > 0) { responseText += `π **Console Messages**:\n\n`; responseText += result.formatted; } else { responseText += `βΉοΈ No console messages found matching the specified criteria.`; } return { content: [ { type: "text", text: responseText, }, ], }; } catch (error: any) { const errorMessage = error instanceof Error ? error.message : String(error); console.error("Console inspection failed:", errorMessage); return { content: [ { type: "text", text: `β Failed to inspect browser console: ${errorMessage}`, }, ], isError: true, }; } } );
- Backend HTTP endpoint handler for GET /console-inspection. Parses query filters (level, limit, since, search), calls buildConsoleInspectionResponse helper on stored consoleLogs/errors/warnings arrays, and returns formatted JSON response.app.get("/console-inspection", (req, res) => { logInfo("Browser Connector: Received console inspection request"); // Parse query parameters for filtering const filters: ConsoleFilterParams = { level: (req.query.level as any) || "all", limit: req.query.limit ? parseInt(req.query.limit as string) : undefined, since: req.query.since ? parseInt(req.query.since as string) : undefined, search: (req.query.search as string) || undefined, }; logDebug("Browser Connector: Console inspection filters:", filters); try { // Build comprehensive console inspection response const response = buildConsoleInspectionResponse( consoleLogs, consoleErrors, consoleWarnings, filters ); logInfo( `Browser Connector: Returning ${response.logs.length} console entries` ); logDebug(`Browser Connector: Stats:`, response.stats); res.json(response); } catch (error) { console.error("Browser Connector: Error in console inspection:", error); res.status(500).json({ error: error instanceof Error ? error.message : "Unknown error occurred", }); } });
- Core helper function that combines console logs/errors/warnings, applies filters/sorting/limiting, computes stats, formats output, used by backend /console-inspection handler.export function buildConsoleInspectionResponse( consoleLogs: ConsoleLogEntry[], consoleErrors: ConsoleLogEntry[], consoleWarnings: ConsoleLogEntry[], filters: ConsoleFilterParams = {} ): { logs: ConsoleLogEntry[]; stats: ReturnType<typeof getConsoleLogStats>; formatted: string; summary: string; filters: ConsoleFilterParams; } { // Combine all console entries let allLogs: ConsoleLogEntry[] = []; // Add logs with proper typing allLogs.push(...consoleLogs.map(log => ({ ...log, level: 'log' }))); allLogs.push(...consoleErrors.map(log => ({ ...log, level: 'error' }))); allLogs.push(...consoleWarnings.map(log => ({ ...log, level: 'warn' }))); // Apply filters let filteredLogs = filterConsoleLogs(allLogs, filters); // Sort by timestamp filteredLogs = sortConsoleLogs(filteredLogs); // Apply limit filteredLogs = limitConsoleResults(filteredLogs, filters.limit); // Get statistics const stats = getConsoleLogStats(filteredLogs); // Format for display const { formatted, summary } = formatConsoleLogsForDisplay(filteredLogs); return { logs: filteredLogs, stats, formatted, summary, filters }; }
- Code in /extension-log POST endpoint that receives console events from Chrome extension (console-log/error/warn) and stores them in in-memory arrays consoleLogs, consoleErrors, consoleWarnings (with size limiting). These arrays are used by /console-inspection.case "console-log": logDebug("Adding console log:", { level: data.level, message: data.message?.substring(0, 100) + (data.message?.length > 100 ? "..." : ""), timestamp: data.timestamp, }); consoleLogs.push(data); if (consoleLogs.length > currentSettings.logLimit) { logDebug( `Console logs exceeded limit (${currentSettings.logLimit}), removing oldest entry` ); consoleLogs.shift(); } break; case "console-error": logDebug("Adding console error:", { level: data.level, message: data.message?.substring(0, 100) + (data.message?.length > 100 ? "..." : ""), timestamp: data.timestamp, }); consoleErrors.push(data); if (consoleErrors.length > currentSettings.logLimit) { logDebug( `Console errors exceeded limit (${currentSettings.logLimit}), removing oldest entry` ); consoleErrors.shift(); } break; case "console-warn": logDebug("Adding console warning:", { level: data.level, message: data.message?.substring(0, 100) + (data.message?.length > 100 ? "..." : ""), timestamp: data.timestamp, }); consoleWarnings.push(data); if (consoleWarnings.length > currentSettings.logLimit) { logDebug( `Console warnings exceeded limit (${currentSettings.logLimit}), removing oldest entry` ); consoleWarnings.shift(); } break;
- Zod input schema defining parameters for the browser.console.read tool: level (enum), limit (number), timeOffset (number), search (string).{ level: z .enum(["log", "error", "warn", "info", "debug", "all"]) .optional() .describe("Filter by console message level. Default: 'all'"), limit: z .number() .optional() .describe("Maximum number of entries to return. Default: no limit"), timeOffset: z .number() .optional() .describe( "Time offset in seconds from current time. Use this for relative time filtering (e.g., 10 = last 10 seconds, 300 = last 5 minutes). Maximum allowed: 24 hours (86400 seconds)." ), search: z .string() .optional() .describe("Search for specific text in console messages"), },