<?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: Tuğkan</title>
    <description>The latest articles on DEV Community by Tuğkan (@decello).</description>
    <link>https://dev.to/decello</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%2F352653%2Fe6602100-541c-45a2-ba08-f662c24ec008.jpeg</url>
      <title>DEV Community: Tuğkan</title>
      <link>https://dev.to/decello</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/decello"/>
    <language>en</language>
    <item>
      <title>Track Multiple Claude Code Accounts from One Menu Bar with ClaudeHub</title>
      <dc:creator>Tuğkan</dc:creator>
      <pubDate>Mon, 21 Sep 2026 16:00:10 +0000</pubDate>
      <link>https://dev.to/decello/track-multiple-claude-code-accounts-from-one-menu-bar-with-claudehub-ggk</link>
      <guid>https://dev.to/decello/track-multiple-claude-code-accounts-from-one-menu-bar-with-claudehub-ggk</guid>
      <description>&lt;p&gt;If you use multiple Claude Code subscriptions, you have probably asked this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which account still has usage available?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I use multiple Claude Code accounts, each with its own five-hour and weekly usage limits. I wanted a simple way to check all of them without repeatedly switching accounts.&lt;/p&gt;

&lt;p&gt;That is why I built &lt;strong&gt;ClaudeHub&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;ClaudeHub is a free and open-source macOS menu bar app that displays usage information for multiple Claude Code subscription profiles in one place.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/tugkanboz/claude-hub" rel="noopener noreferrer"&gt;github.com/tugkanboz/claude-hub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Download: &lt;a href="https://github.com/tugkanboz/claude-hub/releases/latest" rel="noopener noreferrer"&gt;ClaudeHub Releases&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsosxd76bpf8wzaraj2c5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsosxd76bpf8wzaraj2c5.png" alt="ClaudeHub menu bar" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does ClaudeHub show?
&lt;/h2&gt;

&lt;p&gt;For each configured account, ClaudeHub can display:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Five-hour usage&lt;/li&gt;
&lt;li&gt;Seven-day usage&lt;/li&gt;
&lt;li&gt;Model-specific limits&lt;/li&gt;
&lt;li&gt;Time remaining until each limit resets&lt;/li&gt;
&lt;li&gt;Extra usage information when available&lt;/li&gt;
&lt;li&gt;Last update time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only the ClaudeHub icon appears in the menu bar. Clicking it opens a list of your accounts and their latest usage information.&lt;/p&gt;

&lt;p&gt;Usage is refreshed automatically every five minutes. You can also update it manually using &lt;strong&gt;Refresh Now&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping each account separate
&lt;/h2&gt;

&lt;p&gt;Claude Code supports custom profile directories through the &lt;code&gt;CLAUDE_CONFIG_DIR&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;This makes it possible to keep each account in a separate directory:&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="nv"&gt;CLAUDE_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.claude-accounts/account1"&lt;/span&gt; claude auth login
&lt;span class="nv"&gt;CLAUDE_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.claude-accounts/account2"&lt;/span&gt; claude auth login
&lt;span class="nv"&gt;CLAUDE_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.claude-accounts/account3"&lt;/span&gt; claude auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each command creates an isolated Claude Code profile and starts the browser login flow for that account.&lt;/p&gt;

&lt;p&gt;Your normal Claude Code configuration remains unchanged:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The additional accounts are stored separately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.claude-accounts/account1
~/.claude-accounts/account2
~/.claude-accounts/account3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Download the latest version from the releases page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tugkanboz/claude-hub/releases/latest" rel="noopener noreferrer"&gt;Download ClaudeHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the downloaded &lt;code&gt;.dmg&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;Drag &lt;strong&gt;ClaudeHub&lt;/strong&gt; into the &lt;strong&gt;Applications&lt;/strong&gt; folder.&lt;/li&gt;
&lt;li&gt;Create an isolated Claude Code profile for each account from Terminal.&lt;/li&gt;
&lt;li&gt;Open ClaudeHub.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Accounts &amp;gt; Add Existing Claude Profile...&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Choose one of the profile directories you created.&lt;/li&gt;
&lt;li&gt;Enter the label you want to see in the menu.&lt;/li&gt;
&lt;li&gt;Repeat the process for the remaining accounts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;ClaudeHub will then display all configured accounts in the same menu.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keychain access
&lt;/h2&gt;

&lt;p&gt;Claude Code stores authentication credentials in macOS Keychain.&lt;/p&gt;

&lt;p&gt;When ClaudeHub accesses a profile for the first time, macOS may ask for your Mac password. Choose &lt;strong&gt;Always Allow&lt;/strong&gt; if you do not want to approve the same access every time ClaudeHub starts.&lt;/p&gt;

&lt;p&gt;Choosing &lt;strong&gt;Allow&lt;/strong&gt; may grant access only for the current run. In that case, the password prompt can return when you reopen the app.&lt;/p&gt;

&lt;p&gt;ClaudeHub does not write tokens to its configuration files or logs. Credentials are held in memory only while the app is running.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when a token expires?
&lt;/h2&gt;

&lt;p&gt;When a normal access token is about to expire, ClaudeHub uses the installed Claude Code CLI to renew the session.&lt;/p&gt;

&lt;p&gt;This happens automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It does not open a browser.&lt;/li&gt;
&lt;li&gt;It does not invoke a Claude model.&lt;/li&gt;
&lt;li&gt;It does not consume subscription usage.&lt;/li&gt;
&lt;li&gt;It normally does not require another login.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A manual login is only required if the account was logged out, the refresh token was revoked or the session became invalid.&lt;/p&gt;

&lt;p&gt;Run the login command again using the same profile directory:&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="nv"&gt;CLAUDE_CONFIG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.claude-accounts/account2"&lt;/span&gt; claude auth login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After completing the login, quit and reopen ClaudeHub. You do not need to remove the profile from ClaudeHub or add it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens after restarting the Mac?
&lt;/h2&gt;

&lt;p&gt;Your profiles and authentication sessions are not removed when the computer restarts.&lt;/p&gt;

&lt;p&gt;ClaudeHub stores the account labels and profile directory paths locally. Claude Code keeps the authentication credentials in macOS Keychain.&lt;/p&gt;

&lt;p&gt;After restarting the Mac, you only need to open ClaudeHub again. A new browser login is not normally required.&lt;/p&gt;

&lt;p&gt;If you want ClaudeHub to launch automatically, add it under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;System Settings &amp;gt; General &amp;gt; Login Items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Technical overview
&lt;/h2&gt;

&lt;p&gt;ClaudeHub is a native macOS application built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Swift&lt;/li&gt;
&lt;li&gt;AppKit&lt;/li&gt;
&lt;li&gt;Swift Package Manager&lt;/li&gt;
&lt;li&gt;macOS Security Framework&lt;/li&gt;
&lt;li&gt;URLSession&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The app currently supports Apple Silicon Macs running macOS 13 or newer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Language support
&lt;/h2&gt;

&lt;p&gt;ClaudeHub follows the macOS system language.&lt;/p&gt;

&lt;p&gt;It currently includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;English&lt;/li&gt;
&lt;li&gt;Turkish&lt;/li&gt;
&lt;li&gt;French&lt;/li&gt;
&lt;li&gt;Spanish&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the current system language is not supported, ClaudeHub falls back to English.&lt;/p&gt;

&lt;p&gt;The product name remains &lt;strong&gt;ClaudeHub&lt;/strong&gt; in every language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy
&lt;/h2&gt;

&lt;p&gt;ClaudeHub is designed to store as little information as possible.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It never modifies your normal &lt;code&gt;~/.claude&lt;/code&gt; configuration.&lt;/li&gt;
&lt;li&gt;It does not write tokens to configuration files.&lt;/li&gt;
&lt;li&gt;It does not include tokens in logs.&lt;/li&gt;
&lt;li&gt;It does not collect telemetry.&lt;/li&gt;
&lt;li&gt;It does not contain advertising or analytics SDKs.&lt;/li&gt;
&lt;li&gt;It stores only the account label and profile directory path locally.&lt;/li&gt;
&lt;li&gt;Usage requests are sent directly to Anthropic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project is open source, so its behavior can be reviewed directly in the source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open source
&lt;/h2&gt;

&lt;p&gt;You can inspect the source code, open an issue or contribute to the project:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tugkanboz/claude-hub" rel="noopener noreferrer"&gt;github.com/tugkanboz/claude-hub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>claude</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Let your AI agent test your API: two-go's AI layer and MCP server</title>
      <dc:creator>Tuğkan</dc:creator>
      <pubDate>Tue, 02 Jun 2026 18:51:45 +0000</pubDate>
      <link>https://dev.to/decello/let-your-ai-agent-test-your-api-two-gos-ai-layer-and-mcp-server-3ddp</link>
      <guid>https://dev.to/decello/let-your-ai-agent-test-your-api-two-gos-ai-layer-and-mcp-server-3ddp</guid>
      <description>&lt;p&gt;There's a moment in every project where you have a working endpoint, you &lt;em&gt;know&lt;/em&gt;&lt;br&gt;
you should write tests for it, and you also know you're about to spend the next&lt;br&gt;
hour wiring up an HTTP client, an assertion library, and a dozen little helpers&lt;br&gt;
before you write a single meaningful check.&lt;/p&gt;

&lt;p&gt;I got tired of that moment. So I built &lt;a href="https://www.npmjs.com/package/two-go" rel="noopener noreferrer"&gt;&lt;strong&gt;two-go&lt;/strong&gt;&lt;/a&gt;:&lt;br&gt;
a small, zero-dependency library for testing HTTP APIs from Node. You build a&lt;br&gt;
request with a chainable API, attach the checks you care about, and &lt;code&gt;await&lt;/code&gt; it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;go&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;two-go&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bearer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sr"&gt;/json/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expectJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data[0].id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one chain sends the request and runs all three checks. If any of them&lt;br&gt;
fails, it throws, so there's no special runner to configure. It works on its&lt;br&gt;
own, and it drops straight into &lt;code&gt;node:test&lt;/code&gt;, Jest, Vitest, or Mocha with no&lt;br&gt;
plugin.&lt;/p&gt;

&lt;p&gt;That's the core. But the part I actually want to talk about in this post is the&lt;br&gt;
part that didn't exist in API testing libraries when I started: &lt;strong&gt;letting an AI&lt;br&gt;
agent do the testing work for you.&lt;/strong&gt; two-go ships two things for that: an&lt;br&gt;
optional AI layer, and an MCP server.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why bother with an AI layer at all?
&lt;/h2&gt;

&lt;p&gt;Most of the friction in API testing isn't writing assertions. It's the &lt;em&gt;staring&lt;br&gt;
at a response and deciding what to assert&lt;/em&gt;. What status should this return?&lt;br&gt;
What's the shape of &lt;code&gt;data[0]&lt;/code&gt;? Which fields could leak? Which weird payloads&lt;br&gt;
break it?&lt;/p&gt;

&lt;p&gt;Those are exactly the questions a language model is good at taking a first pass&lt;br&gt;
at. So two-go has an optional &lt;code&gt;two-go/ai&lt;/code&gt; entry point that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;draft a test suite from a live endpoint or a sample response,&lt;/li&gt;
&lt;li&gt;explain why a test failed,&lt;/li&gt;
&lt;li&gt;review a response for likely bugs,&lt;/li&gt;
&lt;li&gt;and generate adversarial payloads to fuzz an endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important design choice: &lt;strong&gt;the AI layer never touches the core.&lt;/strong&gt; two-go still has zero runtime dependencies. The AI module talks to the provider over &lt;code&gt;fetch&lt;/code&gt; with your own key, and it works with OpenAI, Anthropic, or any compatible endpoint, including a local model.&lt;/p&gt;
&lt;h3&gt;
  
  
  Drafting a suite from a real endpoint
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk-...
two-go ai gen https://api.example.com/users &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/users.twogo.mjs

&lt;span class="c"&gt;# or from a saved response, with a different provider two-go ai gen ./sample.json --provider anthropic -o test/users.twogo.mjs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or from code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aiGenerateTests&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;two-go/ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;aiGenerateTests&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ada&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// or "anthropic", or a custom { baseURL } for a local model&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is a normal &lt;code&gt;*.twogo.mjs&lt;/code&gt; file. It goes into git and runs in CI like anything else. Treat it as a &lt;em&gt;first draft&lt;/em&gt;. It gets the boilerplate and the obvious checks out of the way, and you tighten the assertions from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explaining a failure (after the fact, never changing the result)
&lt;/h3&gt;

&lt;p&gt;When a test fails, you can ask the model what probably went wrong. This is advisory: it runs &lt;em&gt;after&lt;/em&gt; the failure and never changes pass or fail.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;explainFailure&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;two-go/ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;expectStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;why&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;explainFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;why&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// likely cause plus a suggested fix&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reviewing and fuzzing
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;aiReview&lt;/code&gt; looks at a response and hands back a list of likely problems: a leaked token, a wrong type, a field that shouldn't be there. &lt;code&gt;aiFuzz&lt;/code&gt; generates adversarial payloads you then send with the normal client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;aiReview&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aiFuzz&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;two-go/ai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;aiReview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// [{ severity, field, message }]&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payloads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;aiFuzz&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;payloads&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;possible bug on payload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both are advisory. &lt;code&gt;aiReview&lt;/code&gt; gives you findings, &lt;code&gt;aiFuzz&lt;/code&gt; gives you inputs, and &lt;em&gt;you&lt;/em&gt; decide what to do with them. The model proposes; your assertions dispose.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MCP server: hand the tools to the agent
&lt;/h2&gt;

&lt;p&gt;The AI layer above is two-go calling out to a model. The &lt;strong&gt;MCP server&lt;/strong&gt; flips the direction: it lets an agent like Claude drive two-go directly, as a set of tools.&lt;/p&gt;

&lt;p&gt;If you haven't run into it yet, &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP (Model Context Protocol)&lt;/a&gt; is an open standard for exposing tools to AI agents. two-go ships an MCP server&lt;br&gt;
that runs over stdio with no dependencies, no URL, no account, and no API key. It's all local.&lt;/p&gt;

&lt;p&gt;Install once so the &lt;code&gt;two-go-mcp&lt;/code&gt; command is on your PATH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; two-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then wire it into your client. For &lt;strong&gt;Claude Code&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add two-go two-go-mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For &lt;strong&gt;Claude Desktop&lt;/strong&gt;, &lt;strong&gt;Cursor&lt;/strong&gt;, &lt;strong&gt;Windsurf&lt;/strong&gt;, &lt;strong&gt;Copilot CLI&lt;/strong&gt;, or &lt;strong&gt;Kiro&lt;/strong&gt;,&lt;br&gt;
drop this into the client's MCP config:&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;"mcpServers"&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;"two-go"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"two-go-mcp"&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(VS Code uses a top-level &lt;code&gt;servers&lt;/code&gt; key and a &lt;code&gt;"type": "stdio"&lt;/code&gt; field; Codex and Gemini have their own &lt;code&gt;mcp add&lt;/code&gt; commands. The README has a copy-paste block for each.)&lt;/p&gt;

&lt;p&gt;The tools it exposes to the agent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http_request&lt;/code&gt;: send a request, get back status, headers, timing, and body.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gen_openapi&lt;/code&gt; / &lt;code&gt;gen_postman&lt;/code&gt;: generate a suite from a spec or collection.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;infer_schema&lt;/code&gt;: infer a JSON schema from a value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;validate_schema&lt;/code&gt;: validate a value against a schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once it's connected, you can just &lt;em&gt;talk&lt;/em&gt; to your agent: "call the staging users endpoint and tell me if the shape changed," or "generate a test suite from this OpenAPI file." The agent makes the calls through two-go and reasons over the real responses.&lt;/p&gt;

&lt;p&gt;And because the server logic is importable, you can host it yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;two-go/mcp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;jsonrpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tools/list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The rest of the box (so the AI output has somewhere to land)
&lt;/h2&gt;

&lt;p&gt;The AI features are only useful because the generated tests run against a real library. The non-AI core is the bulk of two-go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP client + inline checks&lt;/strong&gt;: &lt;code&gt;expectStatus&lt;/code&gt;, &lt;code&gt;expectJson&lt;/code&gt;, &lt;code&gt;expectHeader&lt;/code&gt;,
&lt;code&gt;expectJsonSchema&lt;/code&gt;, &lt;code&gt;expectSorted&lt;/code&gt;, and a long list of others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;expect()&lt;/code&gt; for any value&lt;/strong&gt;: Jest-style matchers, with &lt;code&gt;.not&lt;/code&gt;, &lt;code&gt;.resolves&lt;/code&gt;,
and &lt;code&gt;.rejects&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Soft assertions&lt;/strong&gt;: collect every failure in a run, throw once at the end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polling&lt;/strong&gt;: &lt;code&gt;eventually&lt;/code&gt; / &lt;code&gt;pollUntil&lt;/code&gt; for slow or eventually-consistent
endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshots&lt;/strong&gt;, &lt;strong&gt;sessions with &lt;code&gt;{{token}}&lt;/code&gt; chaining&lt;/strong&gt;, a &lt;strong&gt;fake-data
generator&lt;/strong&gt;, &lt;strong&gt;async helpers&lt;/strong&gt; (&lt;code&gt;parallelLimit&lt;/code&gt;, &lt;code&gt;mapLimit&lt;/code&gt;, &lt;code&gt;waterfall&lt;/code&gt;), a
~170-function &lt;strong&gt;utility belt&lt;/strong&gt;, and a &lt;strong&gt;JSON schema validator + inference&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Importers&lt;/strong&gt; that turn an OpenAPI doc or Postman collection into a starting
suite, with a CLI: &lt;code&gt;two-go gen openapi ./openapi.json -o test/api.twogo.mjs&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of it ships with hand-written TypeScript types and zero runtime dependencies. Node 18+ (it uses the built-in &lt;code&gt;fetch&lt;/code&gt;), ESM only.&lt;/p&gt;

&lt;h2&gt;
  
  
  A realistic flow, end to end
&lt;/h2&gt;

&lt;p&gt;Here's how the pieces fit together in practice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You have an endpoint and an OpenAPI doc. Run &lt;code&gt;two-go gen openapi&lt;/code&gt; to get a
skeleton suite.&lt;/li&gt;
&lt;li&gt;Ask your agent (via the MCP server) to call the live endpoint and flag any
drift from the doc.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;aiReview&lt;/code&gt; on a sample response to surface leaks and type mismatches.&lt;/li&gt;
&lt;li&gt;Tighten the generated checks into real assertions by hand.&lt;/li&gt;
&lt;li&gt;Commit the &lt;code&gt;*.twogo.mjs&lt;/code&gt; files. They run in CI like any other test.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The AI does the tedious first pass: boilerplate, obvious checks, "here's what looks off." You keep the judgment calls. Nothing about the AI layer is load bearing. Pull the key and the core still works exactly the same.&lt;/p&gt;

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



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;two-go &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/two-go" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/two-go&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/tugkanboz/two-go" rel="noopener noreferrer"&gt;https://github.com/tugkanboz/two-go&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The roadmap (JUnit/JSON reporters, a GraphQL helper, a cookie jar, request-level retry) is open in the issues. If you build something with it, or break it, I'd love to hear about it.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>ai</category>
      <category>mcp</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I started awesome-ai-testing because I couldn't find a clean map of AI testing tools</title>
      <dc:creator>Tuğkan</dc:creator>
      <pubDate>Tue, 05 May 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/decello/i-started-awesome-ai-testing-because-i-couldnt-find-a-clean-map-of-ai-testing-tools-5l8</link>
      <guid>https://dev.to/decello/i-started-awesome-ai-testing-because-i-couldnt-find-a-clean-map-of-ai-testing-tools-5l8</guid>
      <description>&lt;p&gt;The AI testing space moves fast. Test generators, self-healing frameworks, MCP servers, LLM evaluators, visual AI platforms, new tools every month. Most of them claim to "use AI" without saying what that actually means.&lt;/p&gt;

&lt;p&gt;Existing awesome lists either don't cover AI testing or mix it into broader test automation directories. The result: hard to compare options, hard to find open source alternatives, hard to see what's mature versus experimental.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tugkanboz/awesome-ai-testing" rel="noopener noreferrer"&gt;awesome-ai-testing&lt;/a&gt; is a new curated list focused on this gap. It currently has around 100 tools across 20 categories, with each entry tagged as open source, commercial, or open core. I'm sharing it early, not polished, to get feedback from people who actually run tests for a living.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in it
&lt;/h2&gt;

&lt;p&gt;Categories are organized by what people actually search for, not academic taxonomies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Test Generation&lt;/strong&gt;: Qodo-Cover, EvoMaster, EvoSuite, Diffblue, plus AI editors like Cursor and Claude Code that can write Playwright, Cypress, and Selenium tests with the right prompting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP-Based Testing&lt;/strong&gt;: Playwright MCP, Chrome DevTools MCP, and community variants. This is the area moving fastest right now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-Healing Test Frameworks&lt;/strong&gt;: Healenium for open source, Testim and Functionize on the commercial side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Natural Language Test Authoring&lt;/strong&gt;: Magnitude, Passmark, Shortest, Auto Playwright, Midscene.js, all open source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM-as-Judge Evaluation&lt;/strong&gt;: Promptfoo, DeepEval, Ragas, Inspect AI, TruLens, Arize Phoenix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mock and Service Virtualization&lt;/strong&gt;: Mountebank, WireMock, Mockoon, MSW, Pact, Hoverfly. Useful when mocking LLM APIs in tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual AI, Mobile AI, Performance, Accessibility, API testing&lt;/strong&gt;: Each with its own section.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each entry has a badge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🆓 Open source&lt;/li&gt;
&lt;li&gt;💰 Commercial
&lt;/li&gt;
&lt;li&gt;🆓💰 Open core (free tier or open source with paid features)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Open source options are listed first in each category. The commercial space is well-promoted already; open source needs visibility.&lt;/p&gt;

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

&lt;p&gt;To keep the list useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tools abandoned for 2+ years&lt;/li&gt;
&lt;li&gt;Marketing pages without a real product&lt;/li&gt;
&lt;li&gt;Generic test runners with weak AI claims (no Selenium with bolted-on plugins)&lt;/li&gt;
&lt;li&gt;Closed source tools without a free trial or transparent pricing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Honest disclaimers
&lt;/h2&gt;

&lt;p&gt;The list is new. A few caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some categories are denser than others. AI Code Review didn't make the cut yet because it doesn't clearly belong in a testing-focused list.&lt;/li&gt;
&lt;li&gt;This is a curated list, not a benchmark. Tools are included based on quality signals (active maintenance, real users, transparent pricing), not personal testing of every entry.&lt;/li&gt;
&lt;li&gt;Categorization will shift as the list grows. Promptfoo sits in two categories because it genuinely does both.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Three workflows the list supports
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"I need to test my AI app"&lt;/strong&gt; → LLM and AI System Testing + Mock and Service Virtualization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"I want AI to write my tests"&lt;/strong&gt; → Test Generation + MCP-Based Testing + Natural Language Test Authoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"My existing test suite is brittle"&lt;/strong&gt; → Self-Healing Test Frameworks + Test Analytics and Triage&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What would help right now
&lt;/h2&gt;

&lt;p&gt;If you maintain or use an AI testing tool that's missing, open a PR. The contribution guide is two screens long.&lt;/p&gt;

&lt;p&gt;If you spot something miscategorized or inaccurate, open an issue.&lt;/p&gt;

&lt;p&gt;If you tried a tool from the list and it was great or terrible, drop it in the comments here. That kind of signal is exactly what turns a list into a useful list.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/tugkanboz/awesome-ai-testing" rel="noopener noreferrer"&gt;https://github.com/tugkanboz/awesome-ai-testing&lt;/a&gt;&lt;/p&gt;

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