Skip to content

rushikeshmore/tor-mcp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

tor-mcp

License: MIT Python 3.11+ Tests

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")

{
  "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")

## Anonymity Check
- **Status**: Anonymous
- **Tor Exit IP**: 185.220.101.20
- **Direct IP**: 106.213.80.181
- **Tor Verified**: Yes

Quickstart

1. Install Tor

# 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

git clone https://github.com/rushikeshmore/tor-mcp.git
cd tor-mcp
uv sync
Claude Code
claude mcp add tor-mcp -- uv --directory $(pwd) run tor-mcp
Claude Desktop

In claude_desktop_config.json:

{
  "mcpServers": {
    "tor-mcp": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/tor-mcp", "run", "tor-mcp"]
    }
  }
}
Cursor / Windsurf / generic MCP clients
{
  "mcpServers": {
    "tor-mcp": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/tor-mcp", "run", "tor-mcp"],
      "env": {
        "TOR_SOCKS_PORT": "9050",
        "TOR_DEFAULT_ROUTE": "tor"
      }
    }
  }
}

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

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.

About

Every web request your LLM tool makes leaks your IP. tor-mcp routes them through Tor with selective per-URL routing. MCP server for Claude Code, Cursor, Windsurf.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages