--- title: MCP Server Guide --- # MCP Server Guide ProxyWhirl provides a Model Context Protocol (MCP) server that enables AI assistants to manage proxies programmatically. This guide covers installation, configuration, the unified tool interface, resources, prompts, and integration patterns. ## Overview The MCP server exposes ProxyWhirl's proxy management capabilities through a standardized protocol that AI assistants can use. It provides: - **Unified Tool**: A single `proxywhirl` tool with action-based operations - **Resources**: Real-time proxy pool health and configuration data - **Prompts**: Guided workflows for proxy selection and troubleshooting ```{contents} :local: :depth: 2 ``` ```{note} The MCP server requires Python 3.10 or higher due to FastMCP dependencies. ``` ## Quick Start ### Installation ```bash # Install ProxyWhirl with MCP support pip install "proxywhirl[mcp]" # Or with uv uv pip install "proxywhirl[mcp]" ``` ### Running the Server ```bash # Run the MCP server with stdio transport (default) proxywhirl-mcp # With uvx (no install required) uvx "proxywhirl[mcp]" proxywhirl-mcp # Specify transport type proxywhirl-mcp --transport http proxywhirl-mcp --transport sse proxywhirl-mcp --transport streamable-http # Or use the Python module directly python -m proxywhirl.mcp.server ``` ### Auto-Loading Proxies The MCP server automatically loads proxies from `proxywhirl.db` if it exists in the current directory (or the path set by `PROXYWHIRL_MCP_DB`). Use the {doc}`cli-reference` to populate the database first: ```bash # Fetch proxies and save to database proxywhirl fetch # Then run MCP server (proxies load automatically) proxywhirl-mcp ``` ```{note} **Auto-fetch fallback:** If the database does not exist or is empty after loading, the MCP server will automatically fetch proxies from public sources (up to 100 proxies) to ensure the pool is not empty on startup. This cold-start behavior means the server is usable even without pre-populating the database. ``` ### CLI Options ```bash proxywhirl-mcp [OPTIONS] Options: --transport {stdio,http,sse,streamable-http} Transport type (default: stdio) --api-key API_KEY API key for authentication --db DB Path to proxy database file (default: proxywhirl.db) --log-level {debug,info,warning,error} Log level (default: info) Environment Variables: PROXYWHIRL_MCP_API_KEY API key for authentication PROXYWHIRL_MCP_DB Path to proxy database file PROXYWHIRL_MCP_LOG_LEVEL Log level ``` ## The `proxywhirl` Tool The MCP server exposes a single unified tool called `proxywhirl` that handles all proxy management operations through an `action` parameter. ### Available Actions | Action | Description | Required Parameters | Optional Parameters | |--------|-------------|---------------------|---------------------| | `list` | List all proxies in the pool | - | `api_key` | | `rotate` | Get next proxy using rotation strategy | - | `api_key` | | `status` | Get detailed status for a specific proxy | `proxy_id` | `api_key` | | `recommend` | Get best proxy based on criteria | - | `criteria`, `api_key` | | `health` | Get pool health overview | - | `api_key` | | `reset_cb` | Reset circuit breaker for a proxy | `proxy_id` | `api_key` | | `add` | Add a new proxy to the pool | `proxy_url` | `api_key` | | `remove` | Remove a proxy from the pool | `proxy_id` | `api_key` | | `fetch` | Fetch proxies from public sources | - | `criteria.max_proxies`, `api_key` | | `validate` | Validate proxy connectivity | - | `proxy_id`, `criteria.timeout`, `api_key` | | `set_strategy` | Change rotation strategy | `strategy` | `api_key` | ### Action Examples #### List All Proxies ```json { "action": "list" } ``` **Response:** ```json { "proxies": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "url": "http://proxy1.example.com:8080", "status": "healthy", "success_rate": 0.95, "avg_latency_ms": 120.5, "region": "US", "total_requests": 150, "total_successes": 142, "total_failures": 8 } ], "total": 1, "healthy": 1, "degraded": 0, "unhealthy": 0, "dead": 0, "unknown": 0 } ``` #### Rotate to Next Proxy ```json { "action": "rotate" } ``` **Response:** ```json { "proxy": { "id": "550e8400-e29b-41d4-a716-446655440000", "url": "http://proxy1.example.com:8080", "status": "healthy", "success_rate": 0.95, "avg_latency_ms": 120.5, "region": "US" } } ``` #### Get Proxy Status ```json { "action": "status", "proxy_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **Response:** ```json { "proxy_id": "550e8400-e29b-41d4-a716-446655440000", "url": "http://proxy1.example.com:8080", "status": "healthy", "metrics": { "success_rate": 0.95, "avg_latency_ms": 120.5, "total_requests": 150, "successful_requests": 142, "failed_requests": 8, "ema_response_time_ms": 115.3 }, "health": { "last_check": "2024-01-15T10:30:00Z", "last_success": "2024-01-15T10:29:55Z", "last_failure": "2024-01-15T09:15:00Z", "circuit_breaker": "closed", "consecutive_failures": 0, "consecutive_successes": 25 }, "region": "US", "protocol": "http" } ``` #### Get Proxy Recommendation ```json { "action": "recommend", "criteria": { "region": "US", "performance": "high" } } ``` **Response:** ```json { "recommendation": { "id": "550e8400-e29b-41d4-a716-446655440000", "url": "http://proxy1.example.com:8080", "score": 0.92, "reason": "Best high performance proxy in US region with high reliability", "metrics": { "success_rate": 0.95, "avg_latency_ms": 85.2, "region": "US", "performance_tier": "high", "total_requests": 500 }, "alternatives": [ { "id": "660e8400-e29b-41d4-a716-446655440001", "url": "http://proxy2.example.com:8080", "score": 0.88, "reason": "Alternative with 92.0% success rate" } ] } } ``` The `criteria` parameter supports: - `region`: Country code filter (e.g., "US", "DE", "JP") - `performance`: Performance tier (`"high"`, `"medium"`, `"low"`) #### Check Pool Health ```json { "action": "health" } ``` **Response:** ```json { "pool_status": "healthy", "total_proxies": 10, "healthy_proxies": 8, "degraded_proxies": 1, "failed_proxies": 1, "average_success_rate": 0.89, "average_latency_ms": 145.3, "last_update": "2024-01-15T10:30:00Z", "total_requests": 5000, "total_successes": 4450, "total_failures": 550, "rate_limit": { "enabled": false, "message": "Rate limiting not configured" } } ``` :::{note} Pool status values match those in the {doc}`/reference/rest-api` `/api/v1/health` endpoint: ::: - `empty`: No proxies in pool - `healthy`: 70%+ proxies are healthy - `degraded`: 30-70% proxies are healthy - `critical`: Less than 30% proxies are healthy #### Reset Circuit Breaker ```json { "action": "reset_cb", "proxy_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **Response:** ```json { "success": true, "proxy_id": "550e8400-e29b-41d4-a716-446655440000", "previous_state": "open", "new_state": "closed", "message": "Circuit breaker reset successfully for proxy 550e8400-e29b-41d4-a716-446655440000" } ``` #### Add Proxy ```json { "action": "add", "proxy_url": "http://newproxy.example.com:8080" } ``` **Response:** ```json { "success": true, "proxy": { "id": "660e8400-e29b-41d4-a716-446655440001", "url": "http://newproxy.example.com:8080", "protocol": "http", "status": "unknown" }, "pool_size": 11 } ``` #### Remove Proxy ```json { "action": "remove", "proxy_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **Response:** ```json { "success": true, "proxy_id": "550e8400-e29b-41d4-a716-446655440000", "message": "Proxy 550e8400-e29b-41d4-a716-446655440000 removed successfully", "pool_size": 9 } ``` #### Fetch Proxies ```json { "action": "fetch", "criteria": {"max_proxies": 50} } ``` **Response:** ```json { "success": true, "fetched": 50, "pool_size": 60, "message": "Fetched 50 proxies from public sources" } ``` #### Validate Proxies ```json { "action": "validate", "proxy_id": "550e8400-e29b-41d4-a716-446655440000" } ``` **Response:** ```json { "validated": 1, "valid": 1, "invalid": 0, "results": [ { "proxy_id": "550e8400-e29b-41d4-a716-446655440000", "url": "http://proxy1.example.com:8080", "valid": true, "latency_ms": 120.5, "status_code": 200 } ], "message": "Validated 1 proxies: 1 valid, 0 invalid" } ``` Omit `proxy_id` to validate all proxies in the pool. ```{note} When validating all proxies, the `results` array in the response is truncated to the first 10 entries to limit response size. The `validated`, `valid`, and `invalid` counts still reflect all proxies tested. ``` #### Set Strategy ```json { "action": "set_strategy", "strategy": "performance-based" } ``` **Response:** ```json { "success": true, "previous_strategy": "RoundRobinStrategy", "new_strategy": "PerformanceBasedStrategy", "message": "Strategy changed from RoundRobinStrategy to PerformanceBasedStrategy" } ``` Valid strategies: `round-robin`, `random`, `weighted`, `least-used`, `performance-based`, `session`, `geo-targeted`. For detailed strategy configuration and tuning, see {doc}`advanced-strategies`. ## Resources The MCP server exposes two resources that AI assistants can read for real-time data. ### resource://proxywhirl/health Returns real-time pool health data as JSON. This resource provides the same data as the `health` action but is accessible as a resource that can be subscribed to. ```json { "pool_status": "healthy", "total_proxies": 10, "healthy_proxies": 8, "degraded_proxies": 1, "failed_proxies": 1, "average_success_rate": 0.89, "average_latency_ms": 145.3, "last_update": "2024-01-15T10:30:00Z", "total_requests": 5000, "total_successes": 4450, "total_failures": 550, "rate_limit": { "enabled": false, "message": "Rate limiting not configured" } } ``` ### resource://proxywhirl/config Returns current configuration settings as JSON. ```json { "rotation_strategy": "RoundRobinStrategy", "timeout": 30.0, "verify_ssl": true, "follow_redirects": true, "pool_connections": 10, "pool_max_keepalive": 20, "circuit_breaker": { "failure_threshold": 5, "timeout_duration": 60, "window_duration": 120 }, "retry_policy": { "max_attempts": 3, "backoff_strategy": "exponential", "base_delay": 1.0, "max_backoff_delay": 60.0, "multiplier": 2.0 }, "logging": { "level": "INFO", "format": "json", "redact_credentials": true } } ``` ## Prompts The MCP server provides two guided workflow prompts for AI assistants. ### proxy_selection_workflow A step-by-step workflow for selecting the optimal proxy for a request: 1. **Assess Requirements**: Determine target geography and performance needs 2. **Check Pool Health**: Verify pool status before selection 3. **Get Recommendation**: Use criteria-based recommendation 4. **Verify Selected Proxy**: Check circuit breaker and metrics 5. **Use or Rotate**: Execute request or switch proxies if issues arise ### troubleshooting_workflow A diagnostic workflow for debugging proxy issues: 1. **Diagnose Pool Health**: Check overall pool status 2. **Check Specific Proxy**: Examine individual proxy metrics 3. **Circuit Breaker Issues**: Reset breakers if needed 4. **Find Alternative**: Get fresh recommendations 5. **Rotate Away**: Switch to different proxy ## Authentication Authentication is optional. When not configured, all requests are allowed. The MCP server uses FastMCP v2 middleware for authentication. ### Enabling Authentication ```python from proxywhirl.mcp.server import set_auth from proxywhirl.mcp.auth import MCPAuth # Enable authentication with API key auth = MCPAuth(api_key="your-secret-api-key") set_auth(auth) ``` ### Authenticated Tool Calls When authentication is enabled, include `api_key` in tool calls: ```json { "action": "list", "api_key": "your-secret-api-key" } ``` ### Auth Middleware The server uses FastMCP v2's middleware system to validate API keys on every tool call: ```python # Middleware is automatically registered with the MCP server # It validates api_key from tool arguments or context ``` ### Disabling Authentication ```python from proxywhirl.mcp.server import set_auth # Disable authentication set_auth(None) ``` ```{warning} Store API keys securely using environment variables or a secrets manager. Never hardcode keys in source code. See {doc}`deployment-security` for production security best practices and {doc}`/reference/configuration` for all environment variables. ``` ## Progress Reporting Long-running operations (fetch, validate) report progress via FastMCP v2's context: ```python # Progress is automatically reported for: # - fetch: Reports progress as proxies are fetched from sources # - validate: Reports progress as each proxy is validated # AI assistants can display this progress to users ``` ## Transport Options The MCP server supports multiple transport protocols: ```python from proxywhirl.mcp import ProxyWhirlMCPServer server = ProxyWhirlMCPServer() # stdio transport (default) - for CLI integration server.run(transport="stdio") # HTTP transport - for REST-like access server.run(transport="http") # SSE transport - for server-sent events server.run(transport="sse") # Streamable HTTP - for streaming responses server.run(transport="streamable-http") ``` ## Integration with Claude Desktop Add ProxyWhirl to your Claude Desktop configuration: ### macOS Edit `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "proxywhirl": { "command": "proxywhirl-mcp" } } } ``` ### Windows Edit `%APPDATA%\Claude\claude_desktop_config.json`: ```json { "mcpServers": { "proxywhirl": { "command": "proxywhirl-mcp" } } } ``` ### With uvx (Recommended) Use uvx to run without a global install: ```json { "mcpServers": { "proxywhirl": { "command": "uvx", "args": ["proxywhirl[mcp]", "proxywhirl-mcp"] } } } ``` ### With Transport Options To use a specific transport: ```json { "mcpServers": { "proxywhirl": { "command": "proxywhirl-mcp", "args": ["--transport", "http"] } } } ``` ## Programmatic Usage Use the MCP server functions directly in Python code: ```python import asyncio from proxywhirl.mcp.server import ( proxywhirl, get_rotator, set_rotator, cleanup_rotator, ) from proxywhirl import AsyncProxyWhirl async def main(): # List all proxies result = await proxywhirl(action="list") print(f"Total proxies: {result.get('total', 0)}") # Check pool health result = await proxywhirl(action="health") print(f"Pool status: {result.get('pool_status', 'unknown')}") # Get recommendation with criteria result = await proxywhirl( action="recommend", criteria={"region": "US", "performance": "high"} ) if "recommendation" in result: print(f"Recommended: {result['recommendation']['url']}") # Rotate to next proxy result = await proxywhirl(action="rotate") if "proxy" in result: print(f"Selected: {result['proxy']['url']}") # Clean up await cleanup_rotator() asyncio.run(main()) ``` ### Custom Rotator Configuration ```python import asyncio from proxywhirl.mcp.server import set_rotator, proxywhirl, cleanup_rotator from proxywhirl import AsyncProxyWhirl, ProxyConfiguration async def main(): # Create custom rotator config = ProxyConfiguration( timeout=60, pool_connections=50, ) rotator = AsyncProxyWhirl( strategy="round-robin", config=config ) # Set as global MCP rotator await set_rotator(rotator) # Add proxies await rotator.add_proxy("http://proxy1.example.com:8080") await rotator.add_proxy("http://proxy2.example.com:8080") # Now MCP tool calls use this rotator result = await proxywhirl(action="list") print(f"Proxies: {result['total']}") # Cleanup await cleanup_rotator() asyncio.run(main()) ``` ## Lifecycle Management The MCP server uses FastMCP v2's lifespan management for proper resource initialization and cleanup: ```python from proxywhirl.mcp.server import mcp_lifespan async def run_server(): async with mcp_lifespan(): # Server is running # Rotator is initialized from database # Resources are available pass # Cleanup is automatic on exit ``` ### How Lifespan Works 1. **Startup**: Initializes the `AsyncProxyWhirl`, loads proxies from database if available 2. **Running**: Rotator is available for all tool calls 3. **Shutdown**: Cleans up rotator resources automatically ```python # The lifespan is automatically passed to FastMCP: mcp = FastMCP("ProxyWhirl", lifespan=_mcp_lifespan) ``` ### Server Class Usage ```python from proxywhirl.mcp import ProxyWhirlMCPServer from proxywhirl import AsyncProxyWhirl async def main(): # With custom rotator rotator = AsyncProxyWhirl(strategy="weighted") server = ProxyWhirlMCPServer(proxy_manager=rotator) await server.initialize() # Required for async setup server.run() # Starts the server # Without custom rotator (uses defaults) server = ProxyWhirlMCPServer() server.run() # Auto-initializes rotator on first use ``` ## Error Handling The MCP server returns errors in a consistent format: ```json { "error": "Error message description", "code": 400 } ``` ### Common Error Codes | Code | Meaning | Example | |------|---------|---------| | 400 | Bad Request | Missing required parameter | | 401 | Unauthorized | Invalid API key | | 404 | Not Found | Proxy ID not found | | 500 | Internal Error | Server-side failure | ### Example Error Responses ```json // Missing proxy_id { "error": "proxy_id required for status action", "code": 400 } // Authentication failed { "error": "Authentication failed: Invalid API key", "code": 401 } // Proxy not found { "error": "Proxy not found: invalid-uuid", "code": 404 } ``` ## Best Practices ### 1. Always Check Pool Health First Before making proxy selections, verify the pool is healthy: ```json {"action": "health"} ``` If `pool_status` is `"critical"` or `"empty"`, fetch new proxies before proceeding. ### 2. Use Recommendations for Critical Requests For important requests, use the `recommend` action instead of `rotate`: ```json { "action": "recommend", "criteria": {"performance": "high"} } ``` ### 3. Monitor Circuit Breaker States Check proxy status before use to avoid open circuit breakers. For circuit breaker internals (state transitions, rolling windows, persistence), see {doc}`retry-failover`. ```json { "action": "status", "proxy_id": "your-proxy-id" } ``` If `circuit_breaker` is `"open"`, either wait or reset it. ### 4. Handle Errors Gracefully Always check for error responses: ```python result = await proxywhirl(action="status", proxy_id="invalid") if "error" in result: print(f"Error {result['code']}: {result['error']}") else: print(f"Status: {result['status']}") ``` ### 5. Clean Up Resources Always clean up when done: ```python from proxywhirl.mcp.server import cleanup_rotator await cleanup_rotator() ``` ## Troubleshooting ### Server Won't Start **Python version too low:** ``` RuntimeError: MCP server requires Python 3.10 or higher ``` Solution: Upgrade to Python 3.10+. **FastMCP not installed:** ``` FastMCP is not installed. MCP server cannot run. ``` Solution: `pip install fastmcp` or `pip install "proxywhirl[mcp]"` ### No Proxies Available **Database not found:** ``` MCP: No database at proxywhirl.db, starting with empty pool ``` Solution: Run `proxywhirl fetch --output proxywhirl.db` first. ### Authentication Failures **Invalid API key:** ```json {"error": "Authentication failed: Invalid API key", "code": 401} ``` Solution: Verify the API key matches what was configured with `set_auth()`. ## See Also ::::{grid} 2 :gutter: 3 :::{grid-item-card} Async Client Guide :link: /guides/async-client :link-type: doc `AsyncProxyWhirl` patterns used by the MCP server internally for proxy rotation. ::: :::{grid-item-card} Advanced Strategies :link: /guides/advanced-strategies :link-type: doc All rotation strategies available through the `set_strategy` MCP action. ::: :::{grid-item-card} Retry & Failover :link: /guides/retry-failover :link-type: doc Circuit breaker states and retry policies exposed via MCP `status` and `reset_cb` actions. ::: :::{grid-item-card} CLI Reference :link: /guides/cli-reference :link-type: doc CLI commands for proxy management, an alternative interface to MCP. ::: :::{grid-item-card} REST API Reference :link: /reference/rest-api :link-type: doc The REST API provides similar functionality to MCP via HTTP endpoints. ::: :::{grid-item-card} Python API Reference :link: /reference/python-api :link-type: doc Complete API docs for `AsyncProxyWhirl` and all models used by the MCP server. ::: ::::