How MCP Formatters Like NetworkFormatter Transform DevTools Data
MCP formatters convert raw Chrome DevTools Protocol objects into human-readable strings, detailed markdown reports, and JSON structures through lazy loading, binary detection, and configurable output methods.
The ChromeDevTools/chrome-devtools-mcp repository provides a set of MCP formatters designed to bridge the gap between raw Chrome DevTools Protocol (CDP) data and consumable output formats. These formatters handle the complex transformation of network requests, console messages, and accessibility snapshots into structured representations suitable for CLI tools, automated testing pipelines, and AI agent consumption.
Understanding the MCP Formatter Architecture
Core Design Principles
MCP formatters follow a consistent three-tier output pattern. Every formatter implements methods for compact string representation (toString()), detailed markdown documentation (toStringDetailed()), and programmatic JSON export (toJSON() and toJSONDetailed()). This design allows the same underlying CDP object to serve both human-readable interfaces and automated data pipelines without redundant processing.
The architecture emphasizes lazy evaluation by default. Formatters only fetch heavy payloads like request bodies or response buffers when explicitly requested via the fetchData option, ensuring that simple logging operations remain performant even when processing large network traces.
Input Objects and CDP Types
Formatters consume standardized CDP objects exported from src/third_party/index.ts. The NetworkFormatter specifically operates on HTTPRequest and HTTPResponse instances, which encapsulate raw protocol events including headers, post data, response bodies, and failure information. These input objects maintain references to the underlying CDP session, allowing formatters to asynchronously fetch additional data as needed.
NetworkFormatter Transformation Pipeline
Step 1: Formatter Instantiation with Lazy Loading
The transformation begins with the static from() method in src/formatters/NetworkFormatter.ts. This factory accepts an HTTPRequest and an options object, returning a configured formatter instance.
const fmt = await NetworkFormatter.from(request, {
requestId: 42,
selectedInDevToolsUI: true,
fetchData: false, // lazy loading - bodies not fetched yet
});
When options.fetchData is set to true, the constructor immediately triggers #loadDetailedData(), eagerly loading request bodies, response buffers, and post data. Otherwise, the formatter retains only metadata (method, URL, status) until detailed output is requested.
Step 2: Fetching Request and Response Bodies
The private #loadDetailedData() method (lines 50-84) handles the heavy lifting of data retrieval. For requests containing post data, it invokes request.postData() or request.fetchPostData() depending on availability. When a requestFilePath is provided in options, raw bytes pass through the user-supplied saveFile callback for external storage.
Response processing occurs via #getFormattedResponseBody(). The formatter calls response.buffer() to obtain raw bytes, then applies isUtf8 detection to determine if content is textual or binary. All bodies undergo truncation via getSizeLimitedString(), enforcing a BODY_CONTEXT_SIZE_LIMIT of 10 KB to prevent memory bloat from large assets.
Step 3: Generating Compact String Summaries
The toString() method (lines 16-20) produces a single-line summary optimized for logging and CLI output. It constructs a string containing the request ID, HTTP method, URL, and a status token generated by #getStatusFromRequest().
Status tokens follow a strict format: [success - 200] for successful responses, [failed - ...] for error conditions, or [pending] for incomplete requests. When selectedInDevToolsUI is true, the output appends [selected in the DevTools Network panel], indicating the user's current focus in the browser interface.
Step 4: Building Detailed Markdown Reports
For comprehensive documentation, toStringDetailed() assembles a multi-section markdown document. The method organizes output into hierarchical sections using ### headers for request details, headers, and bodies.
Header formatting relies on #getFormattedHeaderValue(), which converts header objects into - name:value markdown list items. Request and response bodies display inline when textual and under the size limit, or reference external file paths when saved via the saveFile callback.
The method handles redirect chains by recursively creating temporary formatter instances for each redirect entry, reusing the same saveFile callback to maintain consistency across the entire navigation history.
Step 5: Exporting Structured JSON
The JSON export methods provide machine-readable alternatives to string output. toJSON() (lines 82-90) returns a lightweight object containing requestId, method, url, status, and the UI selection flag.
toJSONDetailed() (lines 92-118) enriches this structure with complete header maps, full request/response bodies (or file paths when externalized), failure text, and recursively formatted redirect chains. This enables downstream tools to consume network data without parsing markdown or regex extraction.
Practical Implementation Examples
Simple One-Liner for Logging
import {NetworkFormatter} from './src/formatters/NetworkFormatter.js';
// `request` is an HTTPRequest obtained from MCP context
const fmt = await NetworkFormatter.from(request, {
requestId: 42,
selectedInDevToolsUI: true,
});
console.log(fmt.toString());
// → "reqid=42 GET https://example.com/ [success - 200] [selected in the DevTools Network panel]"
Full Markdown Report with File Persistence
import {NetworkFormatter} from './src/formatters/NetworkFormatter.js';
import {writeFile} from 'node:fs/promises';
async function saveFile(data: Uint8Array<ArrayBufferLike>, filename: string) {
await writeFile(`artifacts/${filename}`, Buffer.from(data));
return {filename};
}
const fmt = await NetworkFormatter.from(request, {
requestId: 'req-123',
fetchData: true, // pull bodies
requestFilePath: 'req-123-body.txt', // store request body
responseFilePath: 'req-123-resp.txt',// store response body
saveFile,
});
console.log(fmt.toStringDetailed());
/* Example output:
## Request https://example.com/api
Status: [success - 200]
### Request Headers
- :method:GET
- :path:/api
...
### Request Body
Saved to req-123-body.txt.
### Response Headers
- :status:200
...
### Response Body
Saved to req-123-resp.txt.
*/
JSON Export for Automated Processing
import {NetworkFormatter} from './src/formatters/NetworkFormatter.js';
const fmt = await NetworkFormatter.from(request, {requestId: 7});
const json = fmt.toJSONDetailed();
console.log(JSON.stringify(json, null, 2));
/* {
"requestId":7,
"method":"POST",
"url":"https://api.example.com/submit",
"status":"[success - 201]",
"requestHeaders":{"content-type":"application/json"},
"requestBody":"{\"name\":\"test\"}",
"responseHeaders":{"content-type":"application/json"},
"responseBody":"{\"id\":42}",
"failure":null,
"redirectChain":null
}
*/
Key Files and Sibling Formatters
The MCP formatter architecture is implemented across several key files in the ChromeDevTools/chrome-devtools-mcp repository:
src/formatters/NetworkFormatter.ts— Core implementation containingfrom(),#loadDetailedData(),toString(),toStringDetailed(), and JSON export methods.src/third_party/index.ts— Re-exports CDP types (HTTPRequest,HTTPResponse) consumed by formatters.tests/formatters/NetworkFormatter.test.ts— Test suite verifying transformation logic and output formats.
The repository implements sibling formatters following the same three-tier output pattern:
src/formatters/SnapshotFormatter.ts— Transforms accessibility snapshots into readable tree structures.src/formatters/ConsoleFormatter.ts— Formats console messages and stack traces.src/formatters/IssueFormatter.ts— Converts DevTools issues and warnings into structured reports.
Each formatter maintains the same architectural contract: lazy data loading, configurable persistence via saveFile callbacks, and parallel string/markdown/JSON output methods.
Summary
- MCP formatters act as transformation layers between raw Chrome DevTools Protocol objects and consumable output formats.
- NetworkFormatter in
src/formatters/NetworkFormatter.tsimplements a five-step pipeline: instantiation, data loading, compact string generation, detailed markdown assembly, and JSON export. - Lazy loading via
fetchDataoptions ensures performance by fetching heavy payloads only when necessary, with automatic truncation atBODY_CONTEXT_SIZE_LIMIT(10 KB). - Three output flavors—
toString(),toStringDetailed(), andtoJSON()/toJSONDetailed()—support both human-readable CLI output and machine-readable data pipelines. - Extensible architecture allows custom file persistence via
saveFilecallbacks and supports complex scenarios like redirect chains through recursive formatter instantiation.
Frequently Asked Questions
What is the difference between toString() and toStringDetailed() in MCP formatters?
The toString() method generates a single-line summary containing the request ID, HTTP method, URL, and status token (e.g., [success - 200]), optimized for logging and CLI output. In contrast, toStringDetailed() assembles a comprehensive markdown document with hierarchical sections for request headers, response headers, bodies, failure messages, and redirect chains, making it suitable for documentation and debugging reports.
How does NetworkFormatter handle binary response data?
NetworkFormatter detects binary content using the isUtf8 utility after fetching the response buffer via response.buffer(). When binary data is detected, or when text exceeds the BODY_CONTEXT_SIZE_LIMIT of 10 KB, the formatter truncates the content using getSizeLimitedString() and appends an ellipsis with a truncation notice. For external storage scenarios, binary data passes unchanged to the user-supplied saveFile callback.
Can MCP formatters save request bodies to external files?
Yes, the NetworkFormatter.from() method accepts requestFilePath and responseFilePath options alongside a saveFile callback function. When these paths are provided and fetchData is enabled, the private #loadDetailedData() method streams raw bytes to the callback instead of holding them in memory. This pattern allows processing of large assets without memory bloat while maintaining references to the saved file paths in the formatted output.
What other formatters exist besides NetworkFormatter in the chrome-devtools-mcp repository?
The repository implements several sibling formatters following the same architectural pattern: SnapshotFormatter (src/formatters/SnapshotFormatter.ts) for accessibility tree snapshots, ConsoleFormatter (src/formatters/ConsoleFormatter.ts) for console messages and stack traces, and IssueFormatter (src/formatters/IssueFormatter.ts) for DevTools warnings and audit issues. Each implements the from() factory method and the three-tier output system of string, detailed markdown, and JSON exports.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →