tor-mcp
# tor-mcp
[](https://opensource.org/licenses/MIT)
[](https://www.python.org/downloads/)
[](https://github.com/rushikeshmore/tor-mcp/actions/workflows/tests.yml)
> Every web request your LLM tool makes leaks your IP. tor-mcp routes them through Tor, with selective per-URL routing so things that block Tor still work.
An MCP server for Claude Code, Claude Desktop, Cursor, Windsurf, and any other MCP-speaking client. Drop in, get anonymous web fetches as a tool.
## Why this exists
When an LLM agent uses a tool like `fetch` or `web_search`, the request goes straight from your machine. Your IP, your ISP, your geo, your fingerprint. For OSINT lookups, breach checks, threat-intel APIs, or just "I don't want this corner of the web to know who's asking," that's a leak by design.
tor-mcp slots in as an MCP server so the agent's web requests go through your local Tor SOCKS5 proxy with DNS-safe routing (`socks5h://`, no DNS leaks). It's not trying to anonymize your browser — it's trying to anonymize the web traffic an AI tool makes on your behalf.
## What you get
Four MCP tools, all with both JSON and Markdown output:
| Tool | What it does |
|---|---|
| `tor_private_fetch` | Fetch a URL. Routes via Tor or direct based on URL pattern matching, or force one with `force_tor` / `force_direct` (mutually exclusive). |
| `tor_check_anonymity` | Verify Tor is active. Compares the Tor exit IP with your real IP via a multi-provider fallback chain. |
| `tor_new_identity` | Rotate Tor circuit. Get a new exit IP via the Tor control port. Rate-limited to once per 10s by Tor itself. |
| `tor_privacy_status` | Full health check: connection, exit IP, control port, routing rules, config. |
### Sample output
`tor_private_fetch(url="https://api.ipify.org?format=json")` →
```json
{
"status_code": 200,
"url": "https://api.ipify.org?format=json",
"content_type": "application/json",
"content": "{\"ip\":\"185.220.101.20\"}",
"routed_through": "tor",
"routing": {"route": "tor", "reason": "default route: tor"}
}
```
`tor_check_anonymity(response_format="markdown")` →
```markdown
## Anonymity Check
- **Status**: Anonymous
- **Tor Exit IP**: 185.220.101.20
- **Direct IP**: 106.213.80.181
- **Tor Verified**: Yes
```
## Quickstart
### 1. Install Tor
```bash
# macOS
brew install tor && brew services start tor
# Ubuntu/Debian
sudo apt install tor && sudo systemctl start tor
# Verify
curl --socks5-hostname 127.0.0.1:9050 https://check.torproject.org/api/ip
```
### 2. Clone and add to your MCP client
```bash
git clone https://github.com/rushikeshmore/tor-mcp.git
cd tor-mcp
uv sync
```
<details>
<summary>Claude Code</summary>
```bash
claude mcp add tor-mcp -- uv --directory $(pwd) run tor-mcp
```
</details>
<details>
<summary>Claude Desktop</summary>
In `claude_desktop_config.json`:
```json
{
"mcpServers": {
"tor-mcp": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/tor-mcp", "run", "tor-mcp"]
}
}
}
```
</details>
<details>
<summary>Cursor / Windsurf / generic MCP clients</summary>
```json
{
"mcpServers": {
"tor-mcp": {
"command": "uv",
"args": ["--directory", "/absolute/path/to/tor-mcp", "run", "tor-mcp"],
"env": {
"TOR_SOCKS_PORT": "9050",
"TOR_DEFAULT_ROUTE": "tor"
}
}
}
}
```
</details>
You can also launch the server with `python -m tor_mcp` if it's installed in your environment.
### 3. Optional: enable circuit rotation
To use `tor_new_identity`, edit your `torrc` (`/usr/local/etc/tor/torrc` on macOS, `/etc/tor/torrc` on Linux):
```
ControlPort 9051
CookieAuthentication 1
```
Restart Tor afterwards.
## Routing
URL → route is decided in this order:
| Priority | Rule | Route |
|---|---|---|
| 1 | `.onion` domains | Tor (always) |
| 2 | `localhost`, `127.0.0.1`, `*.local` | Direct (always) |
| 3 | User `TOR_PATTERNS` | Tor |
| 4 | User `DIRECT_PATTERNS` | Direct |
| 5 | Default OSINT/security domains (Shodan, HIBP, Censys, urlscan, VirusTotal, OTX, crt.sh) | Tor |
| 6 | Default Tor-blocking domains (GitHub, OpenAI, Anthropic, Google) | Direct |
| 7 | Everything else | `TOR_DEFAULT_ROUTE` (default: `tor`) |
URL inputs without a scheme get one added automatically — `https://` for remote hosts, `http://` for `localhost` / `127.0.0.1` / `*.local` so TLS doesn't blow up against a plain dev server.
## Configuration
All via environment variables:
| Variable | Default | Description |
|---|---|---|
| `TOR_SOCKS_PORT` | `9050` | Tor SOCKS5 proxy port |
| `TOR_CONTROL_PORT` | `9051` | Tor control port (for circuit rotation) |
| `TOR_CONTROL_PASSWORD` | | Control port password (if set in torrc) |
| `TOR_DEFAULT_ROUTE` | `tor` | Fallback route: `tor` or `direct` |
| `TOR_TIMEOUT` | `30` | HTTP request timeout in seconds |
| `TOR_PATTERNS` | | Comma-separated hostnames to route through Tor (supports `*.example.com`) |
| `DIRECT_PATTERNS` | | Comma-separated hostnames to route direct |
## Threat model
What tor-mcp **does** protect against:
- Origin-IP exposure on outbound HTTP(S) requests made by your LLM tool. Traffic exits from a Tor relay, not your home IP.
- DNS leaks. SOCKS5h forces remote DNS resolution at the exit; your resolver never sees the target hostname.
- Trivial geo-fencing and IP-rate-limiting on a per-source basis (use `tor_new_identity` to rotate).
What it **does not** protect against:
- TLS / JA3 / HTTP-header fingerprinting. The traffic is over Tor, but it's still recognizable as Python `httpx`. If a target compares fingerprints, they can tell.
- Traffic correlation by a global adversary. Standard Tor caveat.
- Account-level deanonymization. If you log in, the target knows who you are regardless of route.
- Browser fingerprinting. Not applicable — there's no browser here, just an HTTP client.
- OS / process / clipboard / filesystem metadata. tor-mcp only handles HTTP traffic.
- Malicious or surveilling exits. Use HTTPS-only targets, treat exit-served content as untrusted.
The Tor control port without authentication assumes a single-user machine. On shared or multi-user hosts, set `TOR_CONTROL_PASSWORD` and a hashed password in `torrc`.
This is a privacy tool, not an evasion tool. Don't use it for fraud, abuse, or anything you wouldn't be comfortable explaining. Tor exit operators get a lot of flak from people doing exactly that, and bad actors hurt the network for everyone.
## Tools (parameters)
### `tor_private_fetch`
- `url` (string, required): Target URL.
- `method` (string, optional): `GET`, `POST`, `HEAD`, `PUT`, `DELETE`, `PATCH`, `OPTIONS`. Default `GET`.
- `force_tor` (bool, optional): Force Tor regardless of routing rules.
- `force_direct` (bool, optional): Force direct regardless of routing rules.
Setting both `force_tor=true` and `force_direct=true` is rejected as invalid input.
- `response_format` (string, optional): `json` or `markdown`. Default `json`.
### `tor_check_anonymity`
- `response_format` (string, optional): `json` or `markdown`. Default `json`.
Compares the Tor exit IP against the direct IP via a multi-provider chain (ipify → httpbin → ifconfig.co) so a single provider outage doesn't break the comparison.
### `tor_new_identity`
- `verify` (bool, optional): Check whether the IP actually changed. Default `true`.
- `response_format` (string, optional): `json` or `markdown`. Default `json`.
### `tor_privacy_status`
- `response_format` (string, optional): `json` or `markdown`. Default `json`.
## Development
```bash
git clone https://github.com/rushikeshmore/tor-mcp.git
cd tor-mcp
uv sync --dev
uv run python -m pytest # run tests (no Tor needed, all mocked)
uv run python -m ruff check src/ tests/
uv run tor-mcp # start MCP server on stdio
```
Tests are fully mocked — you don't need Tor running to develop. Live verification (against a running Tor daemon) is documented in `CLAUDE.md`.
## License
[MIT](LICENSE).
TDQS
Scored across 4 tools
Each of the four tools has a clearly distinct purpose: anonymity check, circuit renewal, status diagnostics, and privacy-aware URL fetching. The descriptions include explicit 'Use when' and 'Don't use when' cues that eliminate ambiguity.
All tool names follow a consistent `tor_verb_noun` pattern (e.g., `tor_check_anonymity`, `tor_new_identity`), making the set predictable and easy to navigate.
With exactly 4 tools, the server is well-scoped for its purpose. Each tool covers an essential operation (verification, identity change, status, fetching) without unnecessary bloat or fragmentation.
The tool set covers the full lifecycle of Tor usage: verifying anonymity, requesting a new identity, diagnosing the overall privacy status, and fetching URLs with intelligent routing. No obvious gaps exist for the stated domain.