<?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: kero168</title>
    <description>The latest articles on DEV Community by kero168 (@kero168).</description>
    <link>https://dev.to/kero168</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%2F4028001%2F0e247a8a-3629-47b5-84ff-f1f3f3a6e2e2.png</url>
      <title>DEV Community: kero168</title>
      <link>https://dev.to/kero168</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kero168"/>
    <language>en</language>
    <item>
      <title>Your MCP server passes unit tests — and still breaks in Claude. Here's why.</title>
      <dc:creator>kero168</dc:creator>
      <pubDate>Tue, 14 Jul 2026 06:19:42 +0000</pubDate>
      <link>https://dev.to/kero168/your-mcp-server-passes-unit-tests-and-still-breaks-in-claude-heres-why-215i</link>
      <guid>https://dev.to/kero168/your-mcp-server-passes-unit-tests-and-still-breaks-in-claude-heres-why-215i</guid>
      <description>&lt;p&gt;If you've ever written an MCP (Model Context Protocol) server, you've probably lived this: every unit test is green, but the moment you connect the server to Claude, ChatGPT, or Cursor — tools don't show up, calls fail, the connection dies.&lt;/p&gt;

&lt;p&gt;The usual suspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;stdout pollution&lt;/strong&gt; — on stdio transport, stdout is protocol-only. One stray &lt;code&gt;console.log&lt;/code&gt; corrupts the JSON-RPC stream.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Malformed tool schemas&lt;/strong&gt; — &lt;code&gt;inputSchema&lt;/code&gt; isn't object-typed, &lt;code&gt;required&lt;/code&gt; is missing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Undeclared capabilities&lt;/strong&gt; — you implemented tools/resources/prompts but never declared them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ungraceful failures&lt;/strong&gt; — a missing required argument crashes the server instead of returning a proper error.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are catchable by language-level unit tests, because what's broken isn't your logic — it's the protocol boundary. There are 20,000+ public MCP servers, and until now no standard way to test that boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  mcp-testbench: test the protocol, not the language
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/kero168/mcp-testbench" rel="noopener noreferrer"&gt;mcp-testbench&lt;/a&gt; connects to your server &lt;strong&gt;as a real MCP client&lt;/strong&gt; and verifies the protocol itself. Your server can be written in TypeScript, Python, Go, Rust — anything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Zero install — point it at any stdio MCP server&lt;/span&gt;
npx mcp-testbench run &lt;span class="nt"&gt;--server&lt;/span&gt; &lt;span class="s2"&gt;"npx -y @modelcontextprotocol/server-everything"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single command runs 10 built-in conformance checks (C000–C040): handshake completion, name/version exposure, capability declarations, tools/list validity, per-tool descriptions and object-typed JSON Schemas, graceful rejection of missing required args, and resources/prompts listing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your own cases are declarative YAML
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dist/index.js"&lt;/span&gt;     &lt;span class="c1"&gt;# or url: "http://localhost:3000/mcp"&lt;/span&gt;

&lt;span class="na"&gt;suites&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;search tool&lt;/span&gt;
    &lt;span class="na"&gt;tool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;search&lt;/span&gt;
    &lt;span class="na"&gt;cases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;query&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;result.contains&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
        &lt;span class="na"&gt;expect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;true&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;     &lt;span class="c1"&gt;# missing required arg must fail cleanly&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supported expectations: &lt;code&gt;ok&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;result.contains&lt;/code&gt;, &lt;code&gt;result.matches&lt;/code&gt; (regex), &lt;code&gt;result.equals&lt;/code&gt;, &lt;code&gt;maxDurationMs&lt;/code&gt;. No test code — just YAML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four lines to put it in CI
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kero168/mcp-testbench@main&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dist/index.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failures show up as GitHub annotations on the PR, and a non-zero exit code fails the job. There's a &lt;code&gt;json&lt;/code&gt; reporter for other CI systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The roadmap includes &lt;code&gt;mcp-testbench audit&lt;/code&gt; (tool-description linting + declared-permissions vs. actual-behavior probes), snapshot testing, coverage reports, and a JUnit reporter. There are &lt;a href="https://github.com/kero168/mcp-testbench/issues" rel="noopener noreferrer"&gt;good first issues&lt;/a&gt; if you want in — the dev setup takes under 10 minutes.&lt;/p&gt;

&lt;p&gt;Point it at your server and tell me how it breaks: &lt;a href="https://github.com/kero168/mcp-testbench" rel="noopener noreferrer"&gt;https://github.com/kero168/mcp-testbench&lt;/a&gt; (MIT, &lt;a href="https://www.npmjs.com/package/mcp-testbench" rel="noopener noreferrer"&gt;npm&lt;/a&gt;)&lt;/p&gt;

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