<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[TestPulse]]></title><description><![CDATA[TestPulse]]></description><link>https://testpulse.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 29 Oct 2026 19:24:54 GMT</lastBuildDate><atom:link href="https://testpulse.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Building a test management tool where AI agents are first-class citizens, not an afterthought]]></title><description><![CDATA[TestPulse 1.0.0 just shipped. It's an open-source (AGPLv3) test management platform — projects, versioned test cases, suites, runs, reporting, a Jira…]]></description><link>https://testpulse.hashnode.dev/building-a-test-management-tool-where-ai-agents-are-first-class-citizens-not-an-afterthought</link><guid isPermaLink="true">https://testpulse.hashnode.dev/building-a-test-management-tool-where-ai-agents-are-first-class-citizens-not-an-afterthought</guid><category><![CDATA[ai agents]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Miguel Angel Alonso Pérez]]></dc:creator><pubDate>Wed, 28 Oct 2026 17:23:15 GMT</pubDate><content:encoded><![CDATA[<p>TestPulse 1.0.0 just shipped. It's an open-source (AGPLv3) test management platform — projects, versioned test cases, suites, runs, reporting, a Jira connector — but the part worth writing about is how it treats AI agents.</p>
<h2>The problem with "API access" as an AI story</h2>
<p>Most test management tools have a REST API. Some have a decent one. But "has an API" and "is designed for an AI agent to operate it safely" are different bars. An agent acting on your test suite needs:</p>
<ul>
<li>Its own identity, distinct from a shared service account, so actions are attributable</li>
<li>Permissions scoped the same way a human's are — not "all access because it has an API key"</li>
<li>A way to retry a failed call without double-creating data</li>
<li>Output it can actually parse and act on — keys, not opaque internal IDs</li>
</ul>
<p>None of that is exotic, but almost nothing ships with it by default. So I built it in from the start.</p>
<h2>What's actually in the MCP server</h2>
<p>TestPulse exposes an MCP server at <code>/mcp</code>, inside the same binary as the REST API — not a separate service, not a plugin. It's authenticated exactly the way REST is: a Bearer token, scoped to one project and one role, or unscoped and acting at its creator's current permissions, resolved fresh on every call.</p>
<p>Fourteen tools, deliberately coarse-grained — not a CRUD mirror of every REST endpoint, and not a handful of do-everything workflow mega-tools:</p>
<pre><code class="language-plaintext">upsert_test_case, upsert_step_group, upsert_test_suite, update_suite_membership,
bulk_add_cases, create_test_run, record_result, update_run_item_status,
search, get_details, get_run_progress, list_run_items,
list_unit_test_results, list_projects
</code></pre>
<p>Deletes are REST-only, deliberately — no delete tool exists.</p>
<p>Three of those tools (<code>upsert_test_case</code>'s create path, <code>bulk_add_cases</code>, <code>create_test_run</code>) accept an optional <code>idempotencyKey</code>. If a call times out and you retry with the same key, the server returns the original result instead of creating a duplicate — keys are scoped per caller and tool, expire after 24 hours, and only a <em>successful</em> call gets cached, so retrying a failed (validation/permission) call is always safe.</p>
<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/x13prwn6twwmrkts8ec4.png" alt="The MCP integration card in Settings — enabled, authenticated with the same scoped API tokens as REST" style="display:block;margin:0 auto" />

<h2>The two-call demo</h2>
<p>The scenario I kept coming back to while designing this: <em>"Read</em> <a href="http://requirements.md"><em>requirements.md</em></a> <em>and create a test suite in TestPulse."</em></p>
<p>An agent connected via MCP does this in two calls:</p>
<ol>
<li><code>upsert_test_suite</code> — create the suite</li>
<li><code>bulk_add_cases</code> — populate it, atomically. One malformed case fails the whole batch; nothing gets half-created.</li>
</ol>
<p>That atomicity matters more than it sounds like it should — the alternative (loop over a create-case call per case) leaves you debugging a partially-populated suite when call #14 of 20 fails on a schema issue.</p>
<h2>Everything else, briefly</h2>
<ul>
<li><strong>Core test management</strong>: projects, versioned test cases (every edit creates a new version, not an overwrite), suites, runs with an open/closed lifecycle, JUnit XML import with native plugins for pytest, Maven, Gradle, Jest, Go, .NET, GoogleTest, Jasmine, Mocha, and Vitest.</li>
<li><strong>Identity</strong>: local auth with sessions, scoped API tokens, SSO/OIDC, project-scoped RBAC.</li>
<li><strong>Reporting</strong>: pass-rate trends, flaky-case detection.</li>
<li><strong>Migration</strong>: structural importers from TestRail, Qase, Zephyr Scale, Kiwi TCMS, Xray, and Tuskr.</li>
<li><strong>Deployment</strong>: one Go binary, Postgres, multi-arch distroless images, <code>docker compose up</code> to evaluate, a Helm chart for production.</li>
</ul>
<img src="https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/uvshhzvpv8mubt65zfx9.png" alt="A run board for the Sample Project — 90% pass rate, one real failure called out among nine passing cases, not buried in a summary count" style="display:block;margin:0 auto" />

<h2>Try it</h2>
<pre><code class="language-plaintext">git clone https://github.com/Barayo/TestPulse
cd TestPulse
docker compose -f deploy/compose/docker-compose.yml up --build -d
</code></pre>
<p>That gets you a running instance with a seeded Sample Project — every MCP tool above has real data to operate on immediately, no setup required.</p>
<p>Repo: <a href="https://github.com/Barayo/TestPulse">https://github.com/Barayo/TestPulse</a> Docs: <a href="https://github.com/Barayo/TestPulse/blob/main/docs/mcp-server.md">https://github.com/Barayo/TestPulse/blob/main/docs/mcp-server.md</a></p>
<p>Feedback, issues, and PRs welcome — it's a 1.0, not a finished product.</p>
]]></content:encoded></item></channel></rss>