browser.network.inspect
Analyze and debug browser network requests by inspecting HTTP errors (4xx/5xx), payloads, and request sequences. Filter by URL, specify details like headers and timestamps, and troubleshoot issues missed by console tools.
Instructions
Inspect recent browser network requests (DevTools-like). Use for debugging HTTP failures (4xx/5xx), payloads, and request sequences. Note: This captures network errors that console tools miss.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| details | Yes | Fields to include for each entry. 'timestamp' is useful for chronological ordering. | |
| limit | No | Max entries to return | |
| orderBy | No | Sort field | timestamp |
| orderDirection | No | Sort direction | desc |
| timeOffset | No | Relative window in seconds (e.g., 300 = last 5 minutes, max 86400). | |
| urlFilter | Yes | Substring or pattern to filter request URLs. Tips: Use partial matches; try singular/plural variants if empty. |
Implementation Reference
- browser-tools-mcp/mcp-server.ts:572-617 (registration)Registers the browser.network.inspect MCP tool, including schema definition and handler reference.server.tool( "browser.network.inspect", "Inspect recent browser network requests (DevTools-like). Use for debugging HTTP failures (4xx/5xx), payloads, and request sequences. Note: This captures network errors that console tools miss.", { urlFilter: z .string() .describe( "Substring or pattern to filter request URLs. Tips: Use partial matches; try singular/plural variants if empty." ), details: z .array( z.enum([ "url", "method", "status", "timestamp", "requestHeaders", "responseHeaders", "requestBody", "responseBody", ]) ) .min(1) .describe( "Fields to include for each entry. 'timestamp' is useful for chronological ordering." ), timeOffset: z .number() .optional() .describe( "Relative window in seconds (e.g., 300 = last 5 minutes, max 86400)." ), orderBy: z .enum(["timestamp", "url"]) .optional() .default("timestamp") .describe("Sort field"), orderDirection: z .enum(["asc", "desc"]) .optional() .default("desc") .describe("Sort direction"), limit: z.number().optional().default(20).describe("Max entries to return"), }, handleInspectBrowserNetworkActivity );
- browser-tools-mcp/mcp-server.ts:487-569 (handler)Primary MCP tool handler for browser.network.inspect: parses params, builds query for /network-request-details, fetches and formats response.async function handleInspectBrowserNetworkActivity(params: any) { const { urlFilter, details, timeOffset, orderBy, orderDirection, limit } = params; const currentTime = Date.now(); let finalTimeStart: number | undefined; let finalTimeEnd: number | undefined; if (timeOffset !== undefined) { if (timeOffset <= 0) { throw new Error("timeOffset must be a positive number"); } if (timeOffset > 86400) { throw new Error("timeOffset cannot exceed 24 hours (86400 seconds)"); } finalTimeStart = currentTime - timeOffset * 1000; finalTimeEnd = currentTime; console.log( `Time offset calculation: ${timeOffset}s ago = ${new Date( finalTimeStart ).toISOString()} to ${new Date(finalTimeEnd).toISOString()}` ); } const queryString = `?urlFilter=${encodeURIComponent( urlFilter )}&details=${details.join(",")}&includeTimestamp=true${ finalTimeStart ? `&timeStart=${finalTimeStart}` : "" }${finalTimeEnd ? `&timeEnd=${finalTimeEnd}` : ""}&orderBy=${ orderBy || "timestamp" }&orderDirection=${orderDirection || "desc"}&limit=${limit || 20}`; const targetUrl = `http://${discoveredHost}:${discoveredPort}/network-request-details${queryString}`; console.log(`MCP Tool: Fetching network details from ${targetUrl}`); return await withServerConnection(async () => { try { const response = await fetch(targetUrl); if (!response.ok) { const errorText = await response.text(); throw new Error( `Server returned ${response.status}: ${ errorText || response.statusText }` ); } const json = await response.json(); const results = json; if (Array.isArray(results) && results.length === 0) { const suggestions = generateSearchSuggestions(urlFilter); return { content: [ { type: "text", text: `No API calls found matching '${urlFilter}'. Try these search strategies:\n\n${suggestions.join( "\n" )}`, }, ], } as any; } return { content: [ { type: "text", text: JSON.stringify(results, null, 2), }, ], } as any; } catch (error: any) { console.error("Error fetching network request details:", error); return { content: [ { type: "text", text: `Failed to get network request details: ${error.message}`, }, ], isError: true, } as any; } }); }
- Browser server endpoint /network-request-details: parses query params, applies filter/sort/project/limit using helpers on detailedNetworkLogCache.app.get("/network-request-details", (req, res) => { if ((process.env.LOG_LEVEL || "info").toLowerCase() === "debug") { console.log("[debug] /network-request-details hit", req.query); } try { const urlFilter = String(req.query.urlFilter ?? ""); const detailsCsv = String(req.query.details ?? "url,method,status"); const details = detailsCsv .split(",") .filter(Boolean) as any as NetworkFilterParams["details"]; const includeTimestamp = String(req.query.includeTimestamp ?? "true") === "true"; const timeStart = req.query.timeStart ? Number(req.query.timeStart) : undefined; const timeEnd = req.query.timeEnd ? Number(req.query.timeEnd) : undefined; const orderBy = String( req.query.orderBy ?? "timestamp" ) as any as NetworkFilterParams["orderBy"]; const orderDirection = String( req.query.orderDirection ?? "desc" ) as any as NetworkFilterParams["orderDirection"]; const limit = req.query.limit ? Number(req.query.limit) : 20; let results = filterNetworkLogs(detailedNetworkLogCache, { urlFilter, timeStart, timeEnd, }); results = sortNetworkLogs(results, orderBy, orderDirection); const projected = projectNetworkLogDetails( results, details, includeTimestamp ); const limited = limitResults(projected, limit); res.json(limited); } catch (e: any) { res .status(500) .json({ error: e?.message || "Failed to read network details" }); } });
- Supporting helper functions: filterNetworkLogs, sortNetworkLogs, projectNetworkLogDetails, limitResults used by the network endpoint./** * Refactor Temp: Tool — inspectBrowserNetworkActivity * Stateless helpers to filter, sort, and format network logs captured from the extension. * Keep HTTP bindings and in-memory caches in browser-connector.ts. */ export type OrderByField = "timestamp" | "url"; export type OrderDirection = "asc" | "desc"; export type DetailKey = | "url" | "method" | "status" | "timestamp" | "requestHeaders" | "responseHeaders" | "requestBody" | "responseBody"; export interface NetworkFilterParams { urlFilter: string; details: DetailKey[]; includeTimestamp?: boolean; timeStart?: number; timeEnd?: number; orderBy?: OrderByField; orderDirection?: OrderDirection; limit?: number; } /** * Apply URL substring filter and optional time window to logs. */ export function filterNetworkLogs< T extends { url?: string; timestamp?: number } >( logs: T[], params: Pick<NetworkFilterParams, "urlFilter" | "timeStart" | "timeEnd"> ): T[] { const term = (params.urlFilter || "").toLowerCase(); return logs.filter((log) => { const urlMatch = term ? (log.url || "").toLowerCase().includes(term) : true; const ts = typeof log.timestamp === "number" ? log.timestamp : undefined; const afterStart = params.timeStart ? ts ? ts >= params.timeStart : false : true; const beforeEnd = params.timeEnd ? ts ? ts <= params.timeEnd : false : true; return urlMatch && afterStart && beforeEnd; }); } /** * Sort logs by timestamp or url with direction. */ export function sortNetworkLogs<T extends { timestamp?: number; url?: string }>( logs: T[], orderBy: OrderByField = "timestamp", orderDirection: OrderDirection = "desc" ): T[] { const sorted = [...logs]; sorted.sort((a, b) => { if (orderBy === "url") { const ua = (a.url || "").toLowerCase(); const ub = (b.url || "").toLowerCase(); const cmp = ua.localeCompare(ub); return orderDirection === "asc" ? cmp : -cmp; } else { const ta = typeof a.timestamp === "number" ? a.timestamp : 0; const tb = typeof b.timestamp === "number" ? b.timestamp : 0; const cmp = ta - tb; return orderDirection === "asc" ? cmp : -cmp; } }); return sorted; } /** * Project only requested details to reduce payload size. */ export function projectNetworkLogDetails( logs: any[], details: DetailKey[], includeTimestamp: boolean = true ): any[] { const want = new Set(details); return logs.map((log) => { const out: any = {}; if (want.has("url")) out.url = log.url; if (want.has("method")) out.method = log.method; if (want.has("status")) out.status = log.status; if (includeTimestamp) out.timestamp = log.timestamp; if (want.has("requestHeaders")) out.requestHeaders = log.requestHeaders; if (want.has("responseHeaders")) out.responseHeaders = log.responseHeaders; if (want.has("requestBody")) out.requestBody = log.requestBody; if (want.has("responseBody")) out.responseBody = log.responseBody; return out; }); } /** * Enforce a hard limit on number of results. */ export function limitResults<T>(logs: T[], limit: number = 20): T[] { if (limit <= 0) return []; return logs.slice(0, limit); }