I work on proxy infrastructure, which means I read a lot of proxy errors. And I kept noticing the same thing: the coding agents I used were great at almost everything except this.
Give one a 407 and it would happily "fix" it by putting the proxy credentials into the target site's Authorization header. Show it a timeout on a POST and it would just retry, without asking whether the first request had already reached the server.
That's not the model being dumb. Proxy errors are a narrow area with a lot of confidently wrong answers floating around the internet, and the model has read plenty of them.
So I built a small MCP server that gives the agent grounded answers for this one area. It's free, open source and works with any proxy provider. Here's what I learned designing it.
1. Don't let the agent paste raw logs
The diagnose tool doesn't accept logs, URLs or credentials. It takes structured observations instead. This is a real input for the 407 case:
{
"client": "requests",
"version": "2.34.2",
"phase": "connect_tunnel",
"status": 407,
"responseSource": "proxy"
}
Two reasons. Logs are full of things that shouldn't end up in a model's context, like credentials and session cookies. And the phase matters more than people expect. A 407 during the CONNECT tunnel is your proxy talking, not the website, so the fix is in your proxy config and never in the target's headers.
The schema is strict on purpose. Unknown fields are rejected, and responseSource should only say proxy or target when you actually know which layer answered, not because of the status code alone.
2. Never recommend an automatic retry
Every diagnosis comes back with likely causes, the next thing to check, and what it can't know. For the 407 above, the summary starts with this:
407 reports an intermediary authentication requirement. It does not identify a wrong password as the sole cause.
Retries got the strictest treatment. In the output schema, automaticRetryRecommended isn't a boolean. It's the literal false. The tool can only tell the agent what kind of retry thinking applies. Here's what comes back for a POST that timed out waiting for response headers:
"retry": {
"automaticRetryRecommended": false,
"category": "reconcile_before_repeating",
"advice": "Do not automatically repeat POST/PATCH or an operation of unknown semantics. ..."
}
If a write timed out after it left your machine, the order might already exist. A missing response isn't proof that nothing happened, and the tool says so instead of letting the agent assume.
3. Credentials never go through the agent
Config templates use environment variable placeholders. No tool asks for a password as an argument, and no response ever contains one. If the agent never sees the secret, it can't paste it into a transcript, a log or a commit.
4. The one tool that touches the network is boring on purpose
Run locally, the server sends no telemetry and makes no network requests. There's one optional route check, and it stays off until you set IPVOLT_ENABLE_ROUTE_CHECK=1. Then it sends a single request through your proxy to one fixed endpoint, with a 10-second deadline, and that's all it can do. It can't be pointed at an arbitrary URL, so it can't be turned into a port scanner.
The hosted version keeps basic metrics: tool name, success or error, duration and toolkit version. Nothing about your requests.
5. Return structured data and text
Every tool has an output schema, and responses include both the structured result and the same result as text. Clients that read structured content get clean JSON they can pipe into the next step. Clients that don't still get a usable answer.
The trade-offs
- Client versions are pinned. The diagnose tool only accepts the exact versions the rules were tested against: curl 8.22.0, Requests 2.34.2, HTTPX 0.28.1 and Playwright 1.63.0. That keeps the answers honest, but anyone on another version gets an error instead of a diagnosis.
- The local package requires Node 24+. That rules out everyone still on Node 22.
- The first npm release went out without build provenance. The next one should come from CI with provenance attached.
Try it
Use the hosted endpoint (https://mcp.ipvolt.com/mcp, Streamable HTTP, no signup or API key), or run it locally:
npx --yes @ipvolt/proxy-toolkit-mcp@0.1.0
Setup for different MCP clients is at ipvolt.com/mcp, and the code is on GitHub.
Full disclosure: I'm building ipvolt, a proxy service. The toolkit is provider-neutral and works fine without it.
If you've built MCP tools yourself: do you return structured data, text, or both? Curious what's worked for you.
Top comments (0)