Skip to main content
Glama

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.

Related MCP server: Tor MCP Server

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 mcp add tor-mcp -- uv --directory $(pwd) run tor-mcp

In claude_desktop_config.json:

{
  "mcpServers": {
    "tor-mcp": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/tor-mcp", "run", "tor-mcp"]
    }
  }
}
{
  "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.

Available Tools

4 tools
tor_check_anonymityA
Read-onlyIdempotent

Check if Tor is active and verify anonymity.

Contacts check.torproject.org to verify traffic routes through Tor, then compares the Tor exit IP with the direct (real) IP to confirm anonymity. Use this before making sensitive requests.

Args: params (TorStatusInput): Validated input containing: - response_format (ResponseFormat): 'json' or 'markdown'

Returns: str: Anonymity verification result.

JSON schema:
{
    "tor_exit_ip": str | null,
    "is_tor_verified": bool,
    "direct_ip": str,
    "ips_differ": bool | null,
    "anonymous": bool
}

Examples: - Use when: "Am I anonymous right now?" -> default params - Use when: "Verify Tor is working before OSINT" -> default params - Don't use when: You want to fetch a URL (use tor_private_fetch) - Don't use when: You want full connection details (use tor_privacy_status)

Error Handling: - Tor not running: Returns error with install instructions - Network issues: Returns partial result with error field

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, and destructiveHint=false, so the agent knows the tool is safe and idempotent. The description adds valuable behavioral context: it contacts check.torproject.org, compares Tor exit IP with direct IP, and describes error handling (e.g., Tor not running returns install instructions). This goes beyond what annotations provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (purpose, args, returns, examples, error handling). It is front-loaded with the main purpose. While slightly verbose due to examples and error handling, every section is relevant and adds value.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity (checking anonymity involves multiple steps), the description is comprehensive. It explains the process, output schema, error cases, and provides usage examples. The presence of an output schema further enriches completeness. For a tool with one parameter, this is fully adequate.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Although schema description coverage is 0%, the description details the single parameter (params: TorStatusInput) and its subfield response_format, including the enum values ('json' or 'markdown') and default. It also explains the output schema. This adds substantial meaning beyond the input schema alone.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Check if Tor is active and verify anonymity.' It uses specific verbs (check, verify) and a clear resource (Tor anonymity). The description also distinguishes from sibling tools by explicitly stating when not to use it, such as for fetching URLs (use tor_private_fetch) or full connection details (use tor_privacy_status).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use the tool (e.g., 'before making sensitive requests', questions like 'Am I anonymous right now?') and when not to use it (e.g., 'Don't use when: You want to fetch a URL'). It also names alternative tools (tor_private_fetch, tor_privacy_status), making differentiation easy.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

tor_new_identityA

Request a new Tor circuit to get a different exit IP.

Changes your apparent location and IP address by requesting a new circuit from the Tor daemon. Requires the Tor control port (9051). Rate-limited to once per 10 seconds by Tor itself.

Args: params (TorCircuitInput): Validated input containing: - verify (bool): Check if IP actually changed (default: True) - response_format (ResponseFormat): 'json' or 'markdown'

Returns: str: Circuit rotation result.

JSON schema:
{
    "success": bool,
    "old_ip": str | null,
    "new_ip": str | null,
    "ip_changed": bool | null,
    "waited_seconds": float  (only if rate-limited)
}

Examples: - Use when: "Get a new exit IP" -> default params - Use when: "A site rate-limited me" -> default params - Use when: "I need to appear from a different location" -> default params - Don't use when: You just need to check current IP (use tor_check_anonymity)

Error Handling: - stem not installed: Returns pip install instruction - Control port not enabled: Returns torrc configuration hint - Rate-limited: Waits automatically (max 10s), reports wait time

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Discloses that it changes IP, requires Tor control port 9051, is rate-limited to once per 10 seconds, and details error handling (stem not installed, control port not enabled, rate limiting). This goes beyond the annotations (readOnlyHint=false, etc.).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear sections and front-loaded purpose. Every sentence adds value, though there is slight repetition in the examples (e.g., 'default params' repeated).

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers prerequisites, rate limiting, error handling, return schema, and example usage. No gaps given the tool's complexity and the presence of an output schema.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description lists the fields of the params object with defaults and meanings, but the input schema already provides descriptions for each parameter. The description adds value through usage examples and error handling context, but the parameter details are largely redundant with the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool requests a new Tor circuit to get a different exit IP, and distinguishes it from sibling tools by explicitly noting not to use for checking IP (use tor_check_anonymity instead).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit examples of when to use (e.g., 'Get a new exit IP', 'A site rate-limited me') and when not to use ('You just need to check current IP'), including a sibling tool alternative.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

tor_privacy_statusA
Read-onlyIdempotent

Show current Tor connection status and routing configuration.

Provides a full health check: Tor connectivity, exit IP, control port availability, routing rules, and configuration. Use this for diagnostics.

Args: params (TorStatusInput): Validated input containing: - response_format (ResponseFormat): 'json' or 'markdown'

Returns: str: Complete privacy status.

JSON schema:
{
    "connection": {
        "connected": bool, "is_tor": bool, "exit_ip": str | null,
        "control_available": bool, "error": str | null
    },
    "routing": {
        "default_route": str, "user_tor_patterns": list,
        "user_direct_patterns": list, "use_defaults": bool
    },
    "config": {"socks_port": int, "control_port": int, "timeout": float},
    "health": str
}

Examples: - Use when: "Is Tor working?" -> default params - Use when: "Show me the routing config" -> default params - Use when: "Debug why requests are failing" -> default params - Don't use when: You just need to verify anonymity (use tor_check_anonymity)

Error Handling: - Returns status regardless of Tor state (offline status is valid)

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, idempotentHint=true, destructiveHint=false, so the safety profile is clear. Description adds value by stating 'Returns status regardless of Tor state (offline status is valid)', which is important behavioral context not in annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is well-structured with sections (main, args, returns, json schema, examples, error handling). However, it is somewhat lengthy and could be more concise while retaining clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the presence of a detailed output schema and comprehensive annotations, the description covers all essential aspects: purpose, usage, parameters, return structure, error handling, and behavioral traits. No gaps identified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% but the description has an 'Args' section explaining the response_format parameter and its enum options (json/markdown). This adds meaning beyond the schema references. Could be more detailed on usage of each format.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'Show current Tor connection status and routing configuration' with specific verb and resource. Distinguished from siblings by naming tor_check_anonymity as the alternative for verifying anonymity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit usage examples for common queries (e.g., 'Is Tor working?', 'Show me the routing config', 'Debug why requests are failing') and a clear exclusion: 'Don't use when: You just need to verify anonymity (use tor_check_anonymity)'. Error handling note adds context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

tor_private_fetchA
Read-onlyIdempotent

Fetch a URL with privacy-aware routing through Tor.

Automatically decides whether to route through Tor or direct based on URL pattern matching. Built-in rules route .onion through Tor, localhost direct, OSINT domains (Shodan, HIBP) through Tor, and Tor-blocking domains (GitHub, OpenAI) direct.

Args: params (TorFetchInput): Validated input parameters containing: - url (str): Target URL (e.g., 'https://example.com') - method (str): HTTP method, default GET - force_tor (bool): Override routing rules, force Tor - force_direct (bool): Override routing rules, force direct - response_format (ResponseFormat): 'json' or 'markdown'

Returns: str: Response with status_code, content, routing decision, and content_type.

JSON schema:
{
    "status_code": int,
    "url": str,
    "content_type": str,
    "content": str,
    "routed_through": "tor" | "direct",
    "routing": {"route": str, "reason": str}
}

Examples: - Use when: "Fetch this webpage anonymously" -> url="https://example.com" - Use when: "Check Shodan for host info" -> url="https://api.shodan.io/..." - Use when: "Access onion site" -> url="http://xyz.onion/page" - Don't use when: You need to verify Tor is working (use tor_check_anonymity) - Don't use when: You need a new exit IP first (use tor_new_identity)

Error Handling: - Connection refused: Tor not running, returns install instructions - 403 Forbidden: Target blocks Tor, suggests force_direct or new_identity - 429 Rate Limited: Suggests tor_new_identity for fresh exit IP - Timeout: Suggests increasing TOR_TIMEOUT

ParametersJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint false. Description adds substantial behavioral context: automatic routing rules, error handling for various HTTP responses, and response format details. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is well-structured with sections (Args, Returns, Examples, Error Handling). It is front-loaded with the core purpose. While somewhat lengthy, all content is relevant and earns its place. A minor trim could be possible, but overall efficient.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (automatic routing, error handling, multiple use cases), the description is remarkably complete. It covers the routing decision logic, error scenarios with suggested actions, and provides a detailed output schema. No gaps identified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema descriptions already cover parameter meanings, but description adds valuable context beyond schema: explains automatic routing logic, when to use force_tor/force_direct, and error handling strategies that relate to parameters. Since schema coverage is high, baseline is 3, and description adds enough to warrant a 4.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states it fetches a URL with privacy-aware routing through Tor. Uses specific verb 'fetch' and resource 'URL', and distinguishes from sibling tools like tor_check_anonymity and tor_new_identity in usage guidelines.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit 'Use when' and 'Don't use when' examples with clear alternatives, e.g., 'Don't use when: You need to verify Tor is working (use tor_check_anonymity)'. Provides excellent context for when to use this tool versus siblings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 4 tool updatesv0.1.0
    • First observedtor_check_anonymity
    • First observedtor_new_identity
    • First observedtor_privacy_status
    • First observedtor_private_fetch

TDQS

A4.8/5.0
Disambiguation5/5

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.

Naming Consistency5/5

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.

Tool Count5/5

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.

Completeness5/5

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.

Maintenance

ActivityInactive
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rushikeshmore/tor-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server