# Charles MCP cuts its tool surface to 31 and splits peek from read

> An MCP server that puts Charles Proxy behind an agent so it can read live traffic incrementally, analyse recorded sessions structurally, and expand one request at a time. The design decisions worth knowing are all about not filling the context window.

**heizaheiza/Charles-mcp** — Charles Proxy MCP server for AI agents with live capture, structured traffic analysis, and agent-friendly tool contracts

- Repository: https://github.com/heizaheiza/Charles-mcp
- Website: https://pypi.org/project/charles-mcp/
- Stars: 316 · Forks: 33
- Language: Python
- License: MIT
- Published: 2026-09-15 · Updated: 2026-09-15 · Language: en
- Canonical page: https://hysenlabs.com/projects/heizaheiza-charles-mcp

## The documented Charles password is 123456 and it ships in every config snippet

The first setup step is to enable Charles's own web interface, through Proxy and then Web Interface Settings. The instructions then say to tick the enable box, set the username to `admin`, and set the password to `123456`.

Those two values are not the project's choice. They are the Charles factory default, and the MCP server simply reads whatever the web interface expects. But they appear in the example environment file, in the Claude Code snippet, in the generic JSON configuration, and in the Codex TOML block, so a reader copying any of them has a working server and a guessable credential.

The control-plane connection details are configurable alongside them. The proxy host defaults to `127.0.0.1` and the port to `8888`, the control-plane HTTP timeout is 10 seconds, and the Charles configuration file is auto-detected rather than required, with a commented Windows example path in the environment file for the cases where auto-detection fails.

The one setting with a behavioural consequence rather than a connection one is lifecycle management. It defaults to false, and the recommendation is to keep it that way: with it enabled the MCP server owns starting and stopping Charles, which means it will shut down your proxy when it exits. That is convenient for a controlled test harness and unwelcome on the machine you actually use.

The Codex CLI configuration is the shortest of the three forms, and it shows the same values:

```toml
[mcp_servers.charles]
command = "uvx"
args = ["charles-mcp"]

[mcp_servers.charles.env]
CHARLES_USER = "admin"
CHARLES_PASS = "123456"
CHARLES_MANAGE_LIFECYCLE = "false"
```

Because the server is a plain stdio process invoked through uvx, nothing about it depends on being launched from the client that configured it.

## peek and query were split apart so the cursor stops being consumed

The live capture tools are five, and three of them exist because of how a streaming cursor behaves.

`read_live_capture` reads new traffic from the cursor. `peek_live_capture` shows the same new traffic without advancing the cursor. `query_live_capture_entries` produces a structured summary of the live capture without advancing the cursor either, and accepts a `since_seconds` argument to restrict the window.

That separation was a fix. Querying used to be a read, so asking a second question about the same capture consumed the incremental history and the first answer could not be re-derived under different filters. Now the same `capture_id` can be queried repeatedly with different conditions, and the cursor only moves when you mean it to.

Both of the compact readers also stopped returning the raw entry. They return routing-level fields only, the host, the method, the path, and the status. During live polling that difference is the whole game: a full Charles entry per request fills a context window in seconds, and the raw body is almost never what you want while you are still deciding what to look at.

Start and stop are the remaining two. `start_live_capture` returns a `capture_id`, and `stop_live_capture` ends the capture and persists a snapshot when one is wanted.

## Detail defaults to 2048 body characters and warns at roughly twelve thousand

Drill-down happens through one tool, and it is bounded by default. `get_traffic_entry_detail` defaults to `include_full_body=false` and a `max_body_chars` of 2048.

Above roughly 12,000 characters of estimated output, the response carries a warning telling you to narrow the scope or turn the full body off. The estimate rather than the exact length is what triggers it, so the warning arrives before the response does rather than after.

The same restraint applies to the shape of the payload. Null values are stripped automatically, and a set of internal fields is hidden: `header_map`, `parsed_json`, `parsed_form`, and `lower_name`. If you want the headers, the documented route is the `headers` list rather than the raw map.

The summary layer does something similar for explainability. Summary items returned by the analysis tools explicitly include `matched_fields` and `match_reasons`, so an agent can say why a particular request was selected rather than only that it matched. Given that the recommended workflow starts from a grouped summary and narrows down, having the reasoning attached at the summary stage is what makes the narrowing auditable.

## start_live_capture adopts the existing session instead of clearing it

Starting a live capture does not wipe what Charles has already recorded. `start_live_capture` defaults to `adopt_existing=true` and `reset_session=false`, and the documentation is explicit that it does not clear the traffic Charles has already captured.

That default is the difference between a usable tool and an annoying one. An agent asked to look at a request the user just reproduced in a browser would, with a resetting default, destroy the evidence before reading it. Adoption means the capture joins whatever is already in the session.

The flip side is that you cannot rely on a fresh capture to mean fresh data. If you want a clean window you have to clear Charles yourself, or use `reset_environment`, which restores Charles configuration and cleans up the current environment.

That tool is in the status and control group alongside `charles_status`, which reports the connection state and whether a live capture is still active, and `throttling`, which applies a weak-network preset. The presets named are 3G, 4G, 5G, and off, which is the interesting one for reproducing a request that behaves differently under latency.

Recording is bounded too, with a maximum stop time defaulting to 3600 seconds, so a forgotten capture does not run indefinitely.

## The tool surface was cut to 31 and the old aliases need a switch

The default public tool surface is a canonical set of 31. Three legacy aliases are no longer exposed by default: `filter_func`, `proxy_by_time`, and `list_sessions`.

They are not gone. There is an explicit compatibility switch, either the constructor argument `create_server(expose_legacy_tools=True)` or the environment variable `CHARLES_EXPOSE_LEGACY_TOOLS=true`, and a migration document at `docs/migrations/legacy-tools.md` is named as the authoritative reference for moving off them.

That is a deliberate reduction in tool count rather than a removal of capability, and it follows the same logic as the summary-first output: every tool an agent can see is a tool it may call, and a surface padded with redundant entry points costs context on every turn regardless of whether they get used.

The documentation was reorganised to match. The entry point is consolidated into the docs README, an `AGENTS.md` file was added at the repository root, and an agent workflows guide was added as a task-oriented call manual. Paths in the README and in the tool contract are repository-relative so they resolve in any environment.

One detail worth naming because it is rare to see: contract tests were added specifically to stop the documentation drifting from the tool descriptions. The high-frequency entry points now carry minimum semantics, including identity retention, summary-first behaviour, and the difference between peek and read, and a test asserts the two stay in agreement.

## A Beta classifier on a release candidate, with no 3.13 classifier

The packaging metadata is small and mostly conventional. The build backend is setuptools, the project name is `charles-mcp`, the license is MIT, the minimum Python is 3.10, and the console script maps `charles-mcp` to the package's main entry point.

Two fields do not obviously agree with each other. The development status classifier says Beta, while the version field reads `3.1.0rc1`, which is a release candidate. PyPI will show both, and the classifier is the one people filter on.

The Python classifier list stops at 3.12 even though `requires-python` is an open-ended lower bound of 3.10. So a 3.13 interpreter satisfies the install requirement and then is not claimed by the classifiers. Nothing breaks; it just means the supported-version badge on the package index is narrower than the actual gate.

The dependency set is nine packages with lower bounds only and no upper bounds anywhere. Three of them exist for reasons specific to this domain: an XML parser in its hardened form, because Charles sessions arrive as XML and should not be parsed the naive way; and two compression libraries, because recorded bodies come back brotli or zstandard encoded depending on the request.

A typing extensions backport is declared for interpreters below 3.11, and the development extras add pytest with its asyncio plugin, mypy, and ruff.

## Two README languages, a lock file the backend ignores, and rewritten history

Three things about the repository layout are worth knowing before you file an issue.

First, there are two READMEs. The primary one is in Chinese and the English one is the translation. The packaging metadata points its long description at the English file, so what you read on the package index is not what you read when you land on the repository.

Second, the root holds a lock file while the build backend is setuptools. A lock file of that kind is used to pin a development environment; it has no effect on what pip installs, which resolves dependencies fresh. So reproducibility of the local environment and reproducibility of an install are two different things here, and only one of them is locked.

The launcher scripts show the same asymmetry in miniature. There is a batch file for Windows and no shell script for macOS or Linux, in a package that classifies itself as operating system independent. Nothing stops you running the console script directly, since the whole install path is a single `uvx` invocation, but the convenience wrapper is Windows-only.

Third, and most disruptive for contributors: the public Git history was reorganised on 2026-04-21. The maintenance notice says anyone who cloned before that date should re-clone before contributing, and specifically warns against merging or pushing from an old local clone, because that can reintroduce the discarded history. The related release carries a corresponding migration document for the legacy tool names, which suggests the rewrite and the tool-surface cut happened together.

## Conclusion

Charles MCP is worth adopting if you want an agent to reason about captured HTTP traffic without drowning in it, because the output discipline here is unusually well thought out: routing-level summaries for polling, a cursor that queries do not advance, bounded detail reads, and a warning before the context is full. Two things to settle before you start. The documented Charles Web Interface credentials are a username of admin and a password of 123456, which is Charles's factory default rather than something this project chooses, and you should change it before enabling the web interface on anything but a loopback-bound machine. Second, the Git history was rewritten on 2026-04-21, so any clone older than that must be discarded rather than merged. Keep `CHARLES_MANAGE_LIFECYCLE` false unless you truly want the server to stop Charles on exit, and expect the legacy aliases to be hidden until you opt back in.

## FAQ

### What does the Charles MCP server actually do?

It puts Charles Proxy behind an MCP client so an agent can read live capture traffic incrementally, analyse recorded sessions through structured summaries rather than raw capture dictionaries, and expand a single request's detail only when it needs to.

### How do I install the Charles MCP server?

Install uv and add the server with `uvx charles-mcp`, no clone and no manual virtual environment. Configuration is given for the Claude Code CLI as JSON, for Claude Desktop and Cursor as an mcpServers block, and for the Codex CLI as a TOML mcp_servers table.

### How many tools does the Charles MCP server expose?

The default public surface is a canonical set of 31 tools. Three legacy aliases, filter_func, proxy_by_time, and list_sessions, are hidden by default and can be brought back with create_server(expose_legacy_tools=True) or CHARLES_EXPOSE_LEGACY_TOOLS=true.

### Why does query_live_capture_entries not move the cursor?

It was changed to a read-only analysis entry so the same capture_id can be queried repeatedly with different filters without consuming the incremental history. It also accepts since_seconds to restrict the window to recent traffic.

### What Charles Web Interface credentials does Charles MCP use?

The documented values are the username admin and the password 123456, which are Charles's factory defaults rather than project settings. The proxy host defaults to 127.0.0.1 on port 8888, with a 10 second control-plane timeout and a 3600 second maximum recording duration.

## Sources

- [heizaheiza/Charles-mcp on GitHub](https://github.com/heizaheiza/Charles-mcp)
- [License: MIT](https://github.com/heizaheiza/Charles-mcp/blob/main/LICENSE)
- [Project website](https://pypi.org/project/charles-mcp/)
- [README](https://github.com/heizaheiza/Charles-mcp/blob/main/README.md)
- [Releases](https://github.com/heizaheiza/Charles-mcp/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/heizaheiza-charles-mcp
