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:

The repository implements sibling formatters following the same three-tier output pattern:

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.ts implements a five-step pipeline: instantiation, data loading, compact string generation, detailed markdown assembly, and JSON export.
  • Lazy loading via fetchData options ensures performance by fetching heavy payloads only when necessary, with automatic truncation at BODY_CONTEXT_SIZE_LIMIT (10 KB).
  • Three output flavorstoString(), toStringDetailed(), and toJSON()/toJSONDetailed()—support both human-readable CLI output and machine-readable data pipelines.
  • Extensible architecture allows custom file persistence via saveFile callbacks 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →