MCP server (streamable-http) wrapping proxy store: 5 tools, nginx /mcp route, Hermes registration, llms.txt/meta discovery

This commit is contained in:
drjones
2026-09-19 21:43:45 -07:00
parent 2ae2c44bbc
commit 4095cf06fe
5 changed files with 129 additions and 1 deletions

4
app.py
View File

@@ -536,6 +536,10 @@ a working proxy — no KYC, Bitcoin billing, programmatic end-to-end.
- Billing: pay-per-GB, Bitcoin (BTCPay) - Billing: pay-per-GB, Bitcoin (BTCPay)
- Signup: username + password only (no email, no KYC) - Signup: username + password only (no email, no KYC)
## MCP (for agent clients)
- Streamable HTTP MCP server: {SITE_HOST}/mcp
- Tools: get_info, list_plans, buy_proxy, get_order_status, list_dedicated_locations
## API ## API
- OpenAPI spec: {SITE_HOST}/openapi.json - OpenAPI spec: {SITE_HOST}/openapi.json
- GET /api/v1/info -> service info (JSON) - GET /api/v1/info -> service info (JSON)

14
clean-proxys-mcp.service Normal file
View File

@@ -0,0 +1,14 @@
[Unit]
Description=Clean Proxys MCP server (streamable-http)
After=network.target app.service
[Service]
WorkingDirectory=/opt/pleiades
ExecStart=/opt/pleiades/mcp-venv/bin/python /opt/pleiades/mcp_server.py
Environment=CLEAN_PROXYS_API=http://127.0.0.1:5000
Environment=MCP_PORT=8090
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target

97
mcp_server.py Normal file
View File

@@ -0,0 +1,97 @@
#!/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)
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")

View File

@@ -9,6 +9,19 @@ server {
expires 7d; expires 7d;
} }
# MCP server (streamable-http) for agent clients
location /mcp {
proxy_pass http://127.0.0.1:8090;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
}
location / { location / {
proxy_pass http://127.0.0.1:5000; proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host; proxy_set_header Host $host;

View File

@@ -6,7 +6,7 @@
<title>{% block title %}Clean Proxys{% endblock %}</title> <title>{% block title %}Clean Proxys{% endblock %}</title>
<meta name="description" content="Clean residential SOCKS5 & HTTP proxies. Global geo-targeting. No KYC. Bitcoin checkout. Encrypted backend. Agent-ready."> <meta name="description" content="Clean residential SOCKS5 & HTTP proxies. Global geo-targeting. No KYC. Bitcoin checkout. Encrypted backend. Agent-ready.">
<meta name="robots" content="index,follow"> <meta name="robots" content="index,follow">
<meta name="ai-agent" content="api: /api/v1/info, /openapi.json; purchase: POST /api/v1/proxy; auth: username+password; billing: bitcoin"> <meta name="ai-agent" content="mcp: /mcp (streamable-http); api: /api/v1/info, /openapi.json; purchase: POST /api/v1/proxy; auth: username+password; billing: bitcoin">
<meta property="og:title" content="Clean Proxys — residential SOCKS5 proxies"> <meta property="og:title" content="Clean Proxys — residential SOCKS5 proxies">
<meta property="og:description" content="Clean, fast, geo-targeted residential proxies. No KYC. Pay with Bitcoin. Built for agents and humans alike."> <meta property="og:description" content="Clean, fast, geo-targeted residential proxies. No KYC. Pay with Bitcoin. Built for agents and humans alike.">
<meta property="og:type" content="website"> <meta property="og:type" content="website">