Content
# Charles MCP Server
[English README](README.en.md) | [Tool Contract](docs/contracts/tools.md)
Charles MCP Server is used to connect Charles Proxy to the MCP client, allowing the agent to stably read real-time traffic, analyze historical recordings, and expand single request details when needed.
It solves three core problems:
- While recording is still in progress, the agent can continuously read the incremental traffic of the current session.
- Live and history are unified in structured analysis, no longer letting the agent directly consume the original captured packet dictionary.
- By default, use summary-first output, first looking at hotspots and summaries, then drilling down to single details.
## Quick Start
### 1. Enable Charles Web Interface
In Charles, go to: `Proxy -> Web Interface Settings`
Please confirm:
- Check `Enable web interface`
- Username is `admin`
- Password is `123456`
Menu location indication:

Settings window indication:

### 2. Install and Configure to MCP Client
No need to clone the repository or manually create a virtual environment. First, install [uv](https://docs.astral.sh/uv/getting-started/installation/).
#### Claude Code CLI
```bash
claude mcp add-json charles '{
"type": "stdio",
"command": "uvx",
"args": ["charles-mcp"],
"env": {
"CHARLES_USER": "admin",
"CHARLES_PASS": "123456",
"CHARLES_MANAGE_LIFECYCLE": "false"
}
}'
```
#### Claude Desktop / Cursor / General JSON Configuration
```json
{
"mcpServers": {
"charles": {
"command": "uvx",
"args": ["charles-mcp"],
"env": {
"CHARLES_USER": "admin",
"CHARLES_PASS": "123456",
"CHARLES_MANAGE_LIFECYCLE": "false"
}
}
}
}
```
#### Codex CLI
```toml
[mcp_servers.charles]
command = "uvx"
args = ["charles-mcp"]
[mcp_servers.charles.env]
CHARLES_USER = "admin"
CHARLES_PASS = "123456"
CHARLES_MANAGE_LIFECYCLE = "false"
```
### Let AI Automatically Install
Copy and paste the following prompt into any AI agent (Claude Code, ChatGPT, Gemini CLI, Cursor Agent, etc.), and the agent will automatically complete the installation and configuration:
[](#让-ai-自动安装)
<details>
<summary><strong>🔴 Click to Expand Automatic Installation Prompt (Recommended)</strong></summary>
```text
Install the "charles-mcp" MCP server and configure it for my MCP client. Follow these steps exactly:
Step 1 — Detect OS:
Determine if this machine runs Windows, macOS, or Linux.
Step 2 — Ensure uv is installed:
Run: uv --version
If the command fails (uv not found):
- macOS/Linux: run: curl -LsSf https://astral.sh/uv/install.sh | sh
- Windows: run: powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
After installing, verify uv works: uv --version
Step 3 — Detect which MCP client is installed:
Check in this order and use the FIRST match:
a) Claude Code — run: claude --version
If it succeeds, run this command and skip to Step 5:
claude mcp add-json charles '{"type":"stdio","command":"uvx","args":["charles-mcp"],"env":{"CHARLES_USER":"admin","CHARLES_PASS":"123456","CHARLES_MANAGE_LIFECYCLE":"false"}}'
b) Claude Desktop — check if config file exists:
- macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
- Windows: %APPDATA%\Claude\claude_desktop_config.json
- Linux: ~/.config/Claude/claude_desktop_config.json
c) Cursor — check if any of these exist:
- ~/.cursor/mcp.json
- .cursor/mcp.json (in current project)
d) Windsurf — check if exists:
- ~/.codeium/windsurf/mcp_config.json
If none detected, ask the user which client they use.
Step 4 — Write config (for clients b/c/d):
The config entry to add is:
"charles": {
"command": "uvx",
"args": ["charles-mcp"],
"env": {
"CHARLES_USER": "admin",
"CHARLES_PASS": "123456",
"CHARLES_MANAGE_LIFECYCLE": "false"
}
}
IMPORTANT: If the config file already exists, read it first, parse the JSON,
add the "charles" key inside "mcpServers" (create "mcpServers" if absent),
and write back. Do NOT overwrite other existing MCP server entries.
If the file does not exist, create it with:
{ "mcpServers": { "charles": { ... } } }
Step 5 — Verify:
Run: uvx charles-mcp
Wait 3 seconds then terminate the process.
If it starts without import errors, the installation is successful.
Step 6 — Report:
Print: "charles-mcp installed successfully. Please restart your MCP client to load the new server."
Also mention that Charles Proxy must be running with Web Interface enabled (Proxy → Web Interface Settings, username: admin, password: 123456).
```
</details>
## Prerequisites
- Python 3.10+
- Charles Proxy is running on the local machine
- Charles Web Interface is enabled
- Charles Proxy is listening on `127.0.0.1:8888` by default
It is recommended to keep `CHARLES_MANAGE_LIFECYCLE=false` by default. Unless you explicitly want the MCP server to manage the Charles lifecycle, do not let it shut down your Charles process when it exits.
## Environment Variables
| Variable | Default Value | Description |
| --- | --- | --- |
| `CHARLES_USER` | `admin` | Charles Web Interface username |
| `CHARLES_PASS` | `123456` | Charles Web Interface password |
| `CHARLES_PROXY_HOST` | `127.0.0.1` | Charles Proxy host |
| `CHARLES_PROXY_PORT` | `8888` | Charles Proxy port |
| `CHARLES_CONFIG_PATH` | Automatically detected | Charles configuration file path |
| `CHARLES_REQUEST_TIMEOUT` | `10` | Control surface HTTP timeout in seconds |
| `CHARLES_MAX_STOPTIME` | `3600` | Bounded recording maximum duration |
| `CHARLES_MANAGE_LIFECYCLE` | `false` | Whether the MCP server manages Charles lifecycle |
## Recommended Usage Paths
### Real-time Analysis
1. `start_live_capture`
2. `group_capture_analysis`
3. `query_live_capture_entries`
4. `get_traffic_entry_detail`
5. `stop_live_capture`
The goal of this path is to find hotspots with minimal tokens and then expand single requests as needed.
### Historical Analysis
1. `list_recordings`
2. `analyze_recorded_traffic`
3. `group_capture_analysis(source="history")`
4. `get_traffic_entry_detail`
This path is suitable for browsing recordings, filtering and drilling down on structured summaries.
## Current Version Key Changes
- `read_live_capture` and `peek_live_capture` now only return route-level summary fields, such as `host`, `method`, `path`, `status`, and no longer directly throw the complete original Charles entry to avoid quickly filling up the context during real-time polling.
- `query_live_capture_entries` is changed to only read analysis entries, not advancing the live cursor. You can query with different filtering conditions based on the same `capture_id` without consuming historical increments.
- `analyze_recorded_traffic` and `query_live_capture_entries` summary items will explicitly return `matched_fields` and `match_reasons` to help the agent explain "why this traffic was selected".
- `get_traffic_entry_detail` defaults to `include_full_body=false` and `max_body_chars=2048`. If the detail output exceeds about 12,000 characters, a warning will be prompted to narrow the scope or turn off full body.
- Detail/summary output will automatically strip `null` values and hide internal fields like `header_map`, `parsed_json`, `parsed_form`, and `lower_name`; please use the `headers` list when you need to view header information.
## Tool Overview
The README only covers the main process tools recommended for use. Compatible but not recommended entries are not expanded in this document.
### Live Capture Tools
| Tool | Function | When to Use |
| --- | --- | --- |
| `start_live_capture` | Start or take over the current live capture and return `capture_id` | Before real-time observation |
| `read_live_capture` | Read live capture incrementally by cursor and return compact route summaries | Continuously consume new traffic, only want to see host/path/status |
| `peek_live_capture` | Preview new traffic without advancing cursor and return compact route summaries | Want to take a look without changing read progress |
| `stop_live_capture` | End capture and persist snapshot if needed | At the end or export of this real-time capture |
| `query_live_capture_entries` | Output structured summary for live capture without advancing cursor | Want to filter key requests from real-time traffic |
### Analysis Tools
| Tool | Function | When to Use |
| --- | --- | --- |
| `group_capture_analysis` | Aggregate and group live or history results | Want to see hotspots host, path, status |
| `get_capture_analysis_stats` | Return classification statistics | Want to quickly know API, static resources, error traffic ratio |
| `get_traffic_entry_detail` | Read single entry details and give warnings if response is too large | Already have target `entry_id`, prepare to drill down |
| `analyze_recorded_traffic` | Output structured summary for specified recording or latest recording with matching reasons | Want to analyze historical `.chlsj` |
### History Tools
| Tool | Function | When to Use |
| --- | --- | --- |
| `list_recordings` | List saved recording files | Want to know which historical recordings are available |
| `get_recording_snapshot` | Read a recording's raw snapshot | Need to fully view a saved snapshot |
| `query_recorded_traffic` | Directly filter the latest recording with lightweight filtering | Need to quickly find host, method, regex hits |
### Status and Control Tools
| Tool | Function | When to Use |
| --- | --- | --- |
| `charles_status` | View Charles connection status and current live capture status | Suspect connection abnormal or want to confirm capture is still active |
| `throttling` | Set Charles weak network preset | Need to simulate 3G / 4G / 5G / off network conditions |
| `reset_environment` | Restore Charles configuration and clean up current environment | Need to return to a clean environment |
## Key Usage Agreements
### 1. Default Return Raw Data
All tools return complete raw content by default without desensitization. If masking is needed, it should be handled by the MCP client or agent.
### 2. Summary First, Then Detail
It is recommended to use `group_capture_analysis`, `query_live_capture_entries`, or `analyze_recorded_traffic` to confirm the target, then call `get_traffic_entry_detail`.
Do not request `include_full_body=true` at the beginning by default.
### 3. Output Optimized for Token Budget
All summary and detail outputs have been serialized and slimmed down:
- Internal fields like `header_map`, `parsed_json`, `parsed_form`, and `lower_name` no longer appear in output.
- Fields with a value of `null` are automatically stripped during serialization.
- Redundant `preview_text` is removed if `full_text` exists in detail view.
Default parameters have been adjusted to protect the context window:
| Parameter | Old Default | New Default |
| --- | --- | --- |
| `max_items` | 20 | 10 |
| `max_preview_chars` | 256 | 128 |
| `max_headers_per_side` | 8 | 6 |
| `max_body_chars` | 4096 | 2048 |
You can still manually pass higher values if you need to view a larger range.
### 4. History Detail Needs Stable Source Identity
History summary will return `recording_path`, and live summary will return `capture_id`.
For `get_traffic_entry_detail`:
- History scenario, prefer to pass `recording_path`.
- Live scenario, prefer to pass `capture_id`.
### 5. `stop_live_capture` Failure is Recoverable
`stop_live_capture` has two stable end states:
- `status="stopped"`: Truly closed and completed.
- `status="stop_failed"`: Failed after short retry, but capture is still retained.
When returning `stop_failed`, pay attention to:
- `recoverable`.
- `active_capture_preserved`.
If the result is:
```json
{
"status": "stop_failed",
"recoverable": true,
"active_capture_preserved": true
}
```
It indicates that the current capture can still be read, diagnosed, and stopped again, rather than being closed.
## Development
Run tests:
```bash
python -m pytest -q
```
Common local checks:
```bash
python charles-mcp-server.py
python -c "from charles_mcp.main import main; main()"
```
## Acknowledgements
If this project helps you, feel free to buy me a cup of coffee to support subsequent maintenance and iteration.
### WeChat Donate QR Code

### USDT-TRC20
`TCudxn9ByCxPZHXLtvqBjFmLWXywBoicRs`
## Changelog
### 2026-03-27 (v2.0.1)
- **Restricted Historical Package Path Reading**: Historical snapshot related entries now only allow access to `.chlsj` files in managed directories, preventing arbitrary local JSON file reads through tool interfaces.
- **Fixed Live Scan Window**: `query_live_capture_entries` and related live analysis paths now truly follow `scan_limit`, no longer silently scanning only a very small fixed window.
- **Fixed Body Filtering False Positives**: `request_body_contains` and `response_body_contains` no longer only match truncated preview text, correctly covering more complete request and response bodies.
- **Adjusted Runtime Data Directory**: The installation environment now defaults to using the user state directory to save snapshots and backups, avoiding writing runtime data into the package installation directory.
- **Released `2.0.1`**: Synchronized the above fixes, and updated versions on GitHub and PyPI.
### 2026-03-13 (v2)
- **Fixed `lower_name` Validation Crash**: `HeaderKV.lower_name` added default values and automatic validator calculation, solving the `get_traffic_entry_detail` output validation error `'lower_name' is a required property` that made the tool completely unusable.
- **Fixed Cursor Logic Pitfall**: `query_live_capture_entries` changed to read-only analysis (no longer advancing internal cursor), avoiding the issue of agents repeatedly calling and historical data being "consumed", always returning empty lists.
- **Prevented Context Explosion**: `read_live_capture` and `peek_live_capture` automatically compressed raw entries into routing summaries (host/method/path/status) before returning, no longer directly throwing complete HTTP request/response text to large models.
- **Broken Agent Deadlock**: The above three fixes collectively eliminated the agent's deadlock mode during real-time analysis due to detail crashes → fallback queries → cursor expired with no data → continuous retries.
- **Improved Tool Descriptions**: All live/history tools supplemented usage guidelines, side effects explanations, and recommended calling paths.
### 2026-03-13
- **Token Budget Optimization**: Output serialization slimmed down by about 50%, solving the issue of triggering "Context limit reached" after multiple `get_traffic_entry_detail` calls.
- `header_map`, `parsed_json`, `parsed_form`, `lower_name` marked as internal fields, no longer appearing in tool output.
- `TrafficQueryResult` and `TrafficDetailResult` automatically stripped `null` values.
- When `full_text` exists in detail views, automatically removed redundant `preview_text`.
- **Reduced Default Parameters**: `max_items` 20→10, `max_preview_chars` 256→128, `max_headers_per_side` 8→6, `max_body_chars` 4096→2048.
- **Large Response Warning**: When `get_traffic_entry_detail` output exceeds 12,000 characters, prompted in `warnings` for agents to narrow the request scope.
### 2026-03-11
- **Removed Deprecated Redaction System**: Deleted `include_sensitive` parameter and all `redactions_applied` / `redacted` / `sensitive_included` fields, reducing tool signature noise.
- **Fixed `errors_only` Preset Semantics**: This preset now automatically injects `has_error=True`, only returning traffic that truly errored.
- **HTTP Connection Reuse**: `LiveCaptureService` reused the same HTTP connection throughout the start → read → stop lifecycle, reducing TCP overhead during high-frequency polling.
- **Lightweight `entry_id` Calculation**: Changed from full JSON serialization to pipelined key field concatenation followed by SHA1, avoiding unnecessary serialization for large bodies.
## See Also
- [English README](README.en.md)
- [Tool Contract](docs/contracts/tools.md)
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.
Filesystem
Node.js MCP Server for filesystem operations with dynamic access control.