Skip to main content
Glama
Akarda

mcp-snapshot-filter

by Akarda
README.md
# mcp-snapshot-filter

MCP proxy that sits between [Claude Code](https://claude.com/claude-code) and [`chrome-devtools-mcp`](https://github.com/nichochar/chrome-devtools-mcp), intercepting content-heavy responses and applying smart filtering to reduce token usage by **40-60%**.

```
Claude Code  <--stdio-->  mcp-snapshot-filter  <--stdio-->  chrome-devtools-mcp
```

## Why?

Browser MCP tools (especially `take_snapshot`) return massive accessibility trees that fill up the context window fast. A single snapshot of a data-heavy page can be 30-40KB of text. This proxy transparently filters that down without breaking any tool interactions.

**Real-world results:**
| Page | Original | Filtered | Reduction |
|---|---|---|---|
| GitHub repo page | 29.5KB | 22.8KB | 23% |
| Portal with 36-row data table | 38.5KB | 17.3KB | 55% |

## Setup

```bash
# Clone and build
git clone https://github.com/Akarda/mcp-snapshot-filter.git
cd mcp-snapshot-filter
npm install
npm run build
```

Add to `~/.claude.json` (replace any existing `chrome-devtools` entry):

```json
{
  "mcpServers": {
    "chrome-devtools": {
      "type": "stdio",
      "command": "node",
      "args": [
        "/path/to/mcp-snapshot-filter/dist/index.js",
        "--filter-level=moderate",
        "--",
        "npx", "-y", "chrome-devtools-mcp@latest"
      ]
    }
  }
}
```

Restart Claude Code to pick up the new config.

## Filter Levels

| Setting | Light | Moderate (default) | Aggressive |
|---|---|---|---|
| maxNodes | 1000 | 500 | 300 |
| maxDepth | 20 | 15 | 10 |
| maxSimilarSiblings | 5 | 3 | 2 |
| Strip decorative nodes | yes | yes | yes |
| Collapse navigation | no | yes | yes |
| Focus main content only | no | no | yes |
| Est. reduction | 20-30% | 40-60% | 60-80% |

Use `--filter-level=off` to disable all filtering and pass through raw responses.

## What Gets Filtered

### Snapshot filtering (biggest impact)
- **Decorative nodes removed** — `role=none/presentation/separator`, empty text nodes, unnamed generic wrappers are stripped (children promoted up)
- **Similar siblings collapsed** — after N consecutive siblings with the same role (e.g., 50 `StaticText` nodes in a table), keeps first N and shows `... [47 more StaticText elements collapsed]`
- **Navigation collapsed** — `navigation/banner/contentinfo/menu` subtrees reduced to a single summary: `[navigation: 21 items, 9 links]`
- **Main content focus** (aggressive only) — when `role=main` exists, sibling subtrees are summarized
- **Depth/node limits** — prunes beyond maxDepth, caps total node count

### Network request filtering
Strips `image`, `font`, `stylesheet` (and `media` in aggressive) entries from `list_network_requests` output. All `xhr`, `fetch`, `document` requests remain visible. `get_network_request` is **never filtered** — individual request details always pass through in full.

### Console message filtering
Strips `debug`, `verbose` (and `dir`, `trace` at higher levels) from `list_console_messages`. Error and warning messages are always preserved.

### Evaluate script & memory snapshot filtering
Large responses from `evaluate_script` and `take_memory_snapshot` are truncated:
- **JSON arrays** — kept to first N items (100/30/10 depending on level)
- **HTML dumps** — truncated at max text length
- **Plain text** — truncated at max text length (100KB/50KB/20KB depending on level)

## What Is NOT Filtered

- **Error responses** — always passed through raw
- **Image content blocks** — screenshots etc. are never touched
- **Unknown/new tools** — only explicitly listed tools get filtered, everything else passes through
- **Individual request/message details** — `get_network_request`, `get_console_message` are unfiltered
- **Non-interactive tools** — `emulate`, `performance_*`, `screencast_*`, etc. pass through as-is

## UID Integrity

All UIDs of visible nodes are preserved in filtered output. After taking a filtered snapshot, you can `click`, `fill`, or `hover` any UID shown in the output — the proxy forwards these calls to the upstream server unchanged.

## Session Stats

Each filtered response includes a stats line:
```
[cdp-filter-proxy: 20.7KB saved (55%) | session: 22.4KB saved across 2 calls (52%)]
```

## How It Works

The proxy uses the MCP SDK's low-level `Server` class (not `McpServer`) to avoid JSON Schema to Zod conversion issues when proxying tool definitions. On startup it:

1. Spawns the upstream `chrome-devtools-mcp` as a subprocess via `StdioClientTransport`
2. Discovers all upstream tools via `listTools()`
3. Re-exposes them via `setRequestHandler(ListToolsRequestSchema)` and `setRequestHandler(CallToolRequestSchema)`
4. For calls to content-heavy tools, parses the response text into markdown sections (`## Latest page snapshot`, etc.), applies the appropriate filter to each section, and returns the filtered result

## Compatibility with `--slim` mode

chrome-devtools-mcp supports a `--slim` flag that reduces the server to just 3 tools (`navigate`, `evaluate`, `screenshot`) for maximum token savings (~368 tokens). If the proxy detects `--slim` in the upstream args, it will warn that filtering has no effect — slim mode responses are already minimal. The proxy is designed for full mode where it preserves all tools while reducing response sizes by 40-60%.

## Development

```bash
npm run build    # compile TypeScript to dist/
```

After rebuilding, restart Claude Code to pick up changes (MCP servers are long-lived processes).

TDQS

B3.3/5.0

Scored across 29 tools

Disambiguation4/5

Most tools target a clear, distinct browser action or data source, such as navigation, input, console messages, network requests, or performance tracing. The main ambiguity is between fill, fill_form, and type_text, though the descriptions reduce the risk of misselection.

Naming Consistency3/5

Most names follow a readable snake_case verb_noun pattern like list_pages, navigate_page, and take_snapshot. However, bare verbs such as click, fill, hover, and drag, plus deviations like lighthouse_audit, new_page, and wait_for, break the otherwise consistent style.

Tool Count2/5

At 29 tools, the surface is too large for a tightly scoped server and crosses the 25+ threshold. Several specialized debugging capabilities like heap snapshots, Lighthouse audits, and performance tracing could be separated into their own servers or grouped more compactly.

Completeness4/5

The tool set covers the full browser workflow well: page lifecycle, navigation, interaction, forms, dialogs, console and network inspection, screenshots, accessibility audits, performance tracing, and memory analysis. Minor gaps like direct cookie/storage manipulation or an explicit scroll helper can be worked around with evaluate_script or emulate.

Maintenance

ActivityInactive
ResponsivenessNo issues