<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lucas</title>
    <description>The latest articles on DEV Community by Lucas (@ipvolt).</description>
    <link>https://dev.to/ipvolt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4141024%2F8e3c92f5-a73e-405c-be65-550c8785ad87.png</url>
      <title>DEV Community: Lucas</title>
      <link>https://dev.to/ipvolt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ipvolt"/>
    <language>en</language>
    <item>
      <title>My coding agent kept "fixing" proxy errors wrong, so I built an MCP server</title>
      <dc:creator>Lucas</dc:creator>
      <pubDate>Fri, 02 Oct 2026 13:08:43 +0000</pubDate>
      <link>https://dev.to/ipvolt/my-coding-agent-kept-fixing-proxy-errors-wrong-so-i-built-an-mcp-server-3jg3</link>
      <guid>https://dev.to/ipvolt/my-coding-agent-kept-fixing-proxy-errors-wrong-so-i-built-an-mcp-server-3jg3</guid>
      <description>&lt;p&gt;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.  &lt;/p&gt;

&lt;p&gt;Give one a 407 and it would happily "fix" it by putting the proxy credentials into the target site's &lt;code&gt;Authorization&lt;/code&gt; header. Show it a timeout on a POST and it would just retry, without asking whether the first request had already reached the server.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Don't let the agent paste raw logs
&lt;/h2&gt;

&lt;p&gt;The diagnose tool doesn't accept logs, URLs or credentials. It takes structured observations instead. This is a real input for the 407 case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"client"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"requests"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.34.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"phase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"connect_tunnel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;407&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"responseSource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"proxy"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The schema is strict on purpose. Unknown fields are rejected, and &lt;code&gt;responseSource&lt;/code&gt; should only say &lt;code&gt;proxy&lt;/code&gt; or &lt;code&gt;target&lt;/code&gt; when you actually know which layer answered, not because of the status code alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Never recommend an automatic retry
&lt;/h2&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;407 reports an intermediary authentication requirement. It does not identify a wrong password as the sole cause.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Retries got the strictest treatment. In the output schema, &lt;code&gt;automaticRetryRecommended&lt;/code&gt; isn't a boolean. It's the literal &lt;code&gt;false&lt;/code&gt;. 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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"retry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"automaticRetryRecommended"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reconcile_before_repeating"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"advice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Do not automatically repeat POST/PATCH or an operation of unknown semantics. ..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Credentials never go through the agent
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. The one tool that touches the network is boring on purpose
&lt;/h2&gt;

&lt;p&gt;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 &lt;code&gt;IPVOLT_ENABLE_ROUTE_CHECK=1&lt;/code&gt;. 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.&lt;/p&gt;

&lt;p&gt;The hosted version keeps basic metrics: tool name, success or error, duration and toolkit version. Nothing about your requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Return structured data and text
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trade-offs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client versions are pinned.&lt;/strong&gt; 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The local package requires Node 24+.&lt;/strong&gt; That rules out everyone still on Node 22.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The first npm release went out without build provenance.&lt;/strong&gt; The next one should come from CI with provenance attached.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Use the hosted endpoint (&lt;code&gt;https://mcp.ipvolt.com/mcp&lt;/code&gt;, Streamable HTTP, no signup or API key), or run it locally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx &lt;span class="nt"&gt;--yes&lt;/span&gt; @ipvolt/proxy-toolkit-mcp@0.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup for different MCP clients is at &lt;a href="https://ipvolt.com/mcp" rel="noopener noreferrer"&gt;ipvolt.com/mcp&lt;/a&gt;, and the code is on &lt;a href="https://github.com/ipvolt/proxy-toolkit-mcp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Full disclosure: I'm building ipvolt, a proxy service. The toolkit is provider-neutral and works fine without it.&lt;/p&gt;

&lt;p&gt;If you've built MCP tools yourself: do you return structured data, text, or both? Curious what's worked for you.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
