MCP Server Proxy Configuration
The Model Context Protocol (MCP) enables AI assistants like Claude to interact with external tools and data sources through standardized server interfaces. MCP servers that provide web access — fetching pages, scraping data, calling APIs — need proxy infrastructure to ensure reliable access to external websites.
Why MCP Servers Need Proxies
MCP servers that access the web face unique challenges:
- Server-side execution: MCP servers run on the host machine or cloud infrastructure with IPs that may be blocked by target websites.
- AI-driven requests: The AI assistant decides which URLs to fetch, potentially targeting heavily protected sites without warning.
- Sustained access: MCP tools may be called repeatedly for similar URLs as the AI explores a topic, accumulating requests from a single IP.
- Diverse targets: A single conversation might trigger web fetches from documentation, search engines, APIs, and content sites.
Proxy Configuration for MCP Servers
TypeScript MCP Server with Proxy
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { HttpsProxyAgent } from 'https-proxy-agent';
import fetch from 'node-fetch';const proxyAgent = new HttpsProxyAgent( process.env.HEX_PROXY_URL!, // e.g. http://user:pass@gate.hexproxies.com:8080 );
// MCP tool that fetches web content through proxy. // The MCP TS SDK requires the schema constant, not a string literal. server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === 'fetch_webpage') { const url = request.params.arguments?.url as string;
const response = await fetch(url, { agent: proxyAgent, headers: { 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...', }, timeout: 30000, });
const html = await response.text();
return {
content: [{ type: 'text', text: html }],
};
}
throw new Error(Unknown tool: ${request.params.name});
});
```
Python MCP Server with Proxy
import os
import httpx
from mcp.server.fastmcp import FastMCPPROXY_URL = os.environ["HEX_PROXY_URL"] # http://user:pass@gate.hexproxies.com:8080
FastMCP is the high-level MCP server entrypoint that registers @app.tool() handlers. app = FastMCP("web-fetcher")
@app.tool() async def fetch_webpage(url: str) -> str: """Fetch a webpage through residential proxy.""" async with httpx.AsyncClient( proxy=PROXY_URL, timeout=30.0, headers={"User-Agent": "Mozilla/5.0 ..."}, ) as client: response = await client.get(url) return response.text ```
Geo-Targeted MCP Tools
Create MCP tools that accept geographic context for location-specific web access:
@app.tool()
async def fetch_webpage_from_location(url: str, country: str = "us") -> str:
"""Fetch a webpage as seen from a specific country."""
base = os.environ["HEX_PROXY_URL"] # http://user:pass@gate.hexproxies.com:8080
# Inject geo target into the proxy username; production code should parse safely.
proxy_url = base.replace("user:", f"user-country-{country}:")async with httpx.AsyncClient( proxy=proxy_url, timeout=30.0, ) as client: response = await client.get(url) return response.text ```
This enables the AI assistant to see web content from any geographic perspective, useful for tasks involving regional pricing, localized content, or geo-restricted access.
Browser-Based MCP Tools
For MCP tools that need to render JavaScript-heavy pages:
import { chromium } from 'playwright';async function fetchRenderedPage(url: string, country: string = 'us') {
const browser = await chromium.launch({
proxy: {
server: process.env.HEX_PROXY_SERVER!, // http://gate.hexproxies.com:8080
username: user-country-${country},
password: process.env.HEX_PROXY_PASS!,
},
});
const page = await browser.newPage(); await page.goto(url, { waitUntil: 'networkidle' }); const content = await page.content(); await browser.close(); return content; } ```
Production MCP Server Patterns
Session Management
Maintain proxy sessions per conversation to ensure consistent geographic identity:
import random
import stringclass ProxySessionManager: def __init__(self): self.sessions = {}
def get_proxy_for_conversation(self, conversation_id, country="us"): if conversation_id not in self.sessions: session_id = ''.join(random.choices(string.ascii_lowercase, k=8)) self.sessions[conversation_id] = session_id session = self.sessions[conversation_id] base = os.environ["HEX_PROXY_URL"] return base.replace( "user:", f"user-country-{country}-session-{session}:", ) ```
Rate Limiting
Implement per-URL rate limiting in your MCP server to be a good proxy consumer:
from collections import defaultdict
import timeclass RateLimiter: def __init__(self, min_interval=2.0): self.last_request = defaultdict(float) self.min_interval = min_interval
async def wait_if_needed(self, domain): elapsed = time.time() - self.last_request[domain] if elapsed < self.min_interval: await asyncio.sleep(self.min_interval - elapsed) self.last_request[domain] = time.time() ```
Error Handling
Return clear error messages to the AI assistant when proxy or access issues occur:
@app.tool()
async def fetch_webpage(url: str) -> str:
try:
async with httpx.AsyncClient(proxy=PROXY_URL, timeout=30.0) as client:
response = await client.get(url)
if response.status_code == 403:
return "Access blocked by the website. Try a different URL or approach."
response.raise_for_status()
return response.text
except httpx.TimeoutException:
return "Request timed out. The website may be slow or blocking this request."
except Exception as e:
return f"Failed to fetch page: {str(e)}"Cost Management
MCP tool calls are driven by AI conversation context. A single conversation might trigger 5-20 web fetches. At 100-500 KB per fetch, typical proxy bandwidth per conversation is 0.5-10 MB. Monthly costs depend on conversation volume but are typically modest for most MCP deployments.