# How MCP Formatters Like NetworkFormatter Transform DevTools Data

> Discover how MCP formatters like NetworkFormatter transform raw DevTools data into human-readable strings markdown reports and JSON using lazy loading binary detection and configurable output.

- Repository: [ChromeDevTools/chrome-devtools-mcp](https://github.com/chromedevtools/chrome-devtools-mcp)
- Tags: internals
- Published: 2026-02-16

---

**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`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/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`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/formatters/NetworkFormatter.ts). This factory accepts an `HTTPRequest` and an options object, returning a configured formatter instance.

```typescript
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

```typescript
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

```typescript
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

```typescript
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`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/formatters/NetworkFormatter.ts)** — Core implementation containing `from()`, `#loadDetailedData()`, `toString()`, `toStringDetailed()`, and JSON export methods.
- **[`src/third_party/index.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/third_party/index.ts)** — Re-exports CDP types (`HTTPRequest`, `HTTPResponse`) consumed by formatters.
- **[`tests/formatters/NetworkFormatter.test.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/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`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/formatters/SnapshotFormatter.ts)** — Transforms accessibility snapshots into readable tree structures.
- **[`src/formatters/ConsoleFormatter.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/formatters/ConsoleFormatter.ts)** — Formats console messages and stack traces.
- **[`src/formatters/IssueFormatter.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/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.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/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 flavors**—`toString()`, `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`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/formatters/SnapshotFormatter.ts)) for accessibility tree snapshots, **ConsoleFormatter** ([`src/formatters/ConsoleFormatter.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/src/formatters/ConsoleFormatter.ts)) for console messages and stack traces, and **IssueFormatter** ([`src/formatters/IssueFormatter.ts`](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/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.