#!/usr/bin/env python3 """Clean Proxys MCP server — exposes the proxy store as MCP tools for agents. Wraps the Clean Proxys REST API (same host). Run as streamable-http on :8090/mcp so any MCP client (Hermes, Claude, GPTs) can discover and buy proxies natively. """ import json import os import httpx from mcp.server import FastMCP API = os.environ.get("CLEAN_PROXYS_API", "http://127.0.0.1:5000") mcp = FastMCP( "clean-proxys", host="0.0.0.0", # disable DNS-rebinding guard so LAN/remote agents can connect instructions=( "Clean Proxys — buy clean residential SOCKS5/HTTP proxies programmatically. " "No KYC, Bitcoin billing, global geo-targeting. " "Flow: list_plans -> buy_proxy -> poll get_order_status until paid -> " "connect using the returned socks5/http strings." ), ) def _get(path, params=None): r = httpx.get(f"{API}{path}", params=params, timeout=20) r.raise_for_status() return r.json() def _post(path, body): r = httpx.post(f"{API}{path}", json=body, timeout=25) r.raise_for_status() return r.json() @mcp.tool() def get_info() -> str: """Service info, value proposition, proxy ports, and billing model.""" return json.dumps(_get("/api/v1/info"), indent=2) @mcp.tool() def list_plans() -> str: """List residential GB plans (id, name, gb, price_sats).""" return json.dumps(_get("/api/v1/plans"), indent=2) @mcp.tool() def buy_proxy( plan_id: int, country: str = "", city: str = "", username: str = "", password: str = "", ) -> str: """One-call purchase: creates an account + Bitcoin invoice and returns working proxy credentials plus a checkout URL. Args: plan_id: plan id from list_plans. country: ISO country code (US, GB, JP, ...) for geo-targeting; empty = any. city: optional city. username: optional account username (auto-generated if empty). password: optional account password (auto-generated if empty). Returns socks5/http connection strings, the proxy password, and an invoice checkout_url plus status_url to poll for payment. """ body = { "plan_id": plan_id, "country": country, "city": city, "username": username, "password": password, } return json.dumps(_post("/api/v1/proxy", body), indent=2) @mcp.tool() def get_order_status(order_id: str) -> str: """Poll the payment status of a proxy order (pending or paid).""" return json.dumps(_get(f"/api/v1/order/{order_id}"), indent=2) @mcp.tool() def list_dedicated_locations(product: int = 9) -> str: """List dedicated IP locations. product: 9=ISP, 3=datacenter.""" return json.dumps(_get("/dedicated/locations", params={"product": product}), indent=2) @mcp.tool() def mobile_account() -> str: """Mobile tier account info — balance, locations, and the 4G/static-mobile catalog.""" return json.dumps(_get("/api/v1/mobile/info"), indent=2) if __name__ == "__main__": mcp.settings.host = "0.0.0.0" mcp.settings.port = int(os.environ.get("MCP_PORT", "8090")) mcp.run(transport="streamable-http", mount_path="/mcp")