<?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: Miguel Angel Alonso Pérez</title>
    <description>The latest articles on DEV Community by Miguel Angel Alonso Pérez (@miguel_angelalonsoprez).</description>
    <link>https://dev.to/miguel_angelalonsoprez</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%2F4211750%2F04a21888-aa7a-4929-bca4-4bd4f468e9d9.jpg</url>
      <title>DEV Community: Miguel Angel Alonso Pérez</title>
      <link>https://dev.to/miguel_angelalonsoprez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/miguel_angelalonsoprez"/>
    <language>en</language>
    <item>
      <title>Building a test management tool where AI agents are first-class citizens, not an afterthought</title>
      <dc:creator>Miguel Angel Alonso Pérez</dc:creator>
      <pubDate>Wed, 28 Oct 2026 14:27:20 +0000</pubDate>
      <link>https://dev.to/miguel_angelalonsoprez/building-a-test-management-tool-where-ai-agents-are-first-class-citizens-not-an-afterthought-1ek2</link>
      <guid>https://dev.to/miguel_angelalonsoprez/building-a-test-management-tool-where-ai-agents-are-first-class-citizens-not-an-afterthought-1ek2</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with "API access" as an AI story
&lt;/h2&gt;

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

&lt;ul&gt;
&lt;li&gt;Its own identity, distinct from a shared service account, so actions are attributable&lt;/li&gt;
&lt;li&gt;Permissions scoped the same way a human's are — not "all access because it has an API key"&lt;/li&gt;
&lt;li&gt;A way to retry a failed call without double-creating data&lt;/li&gt;
&lt;li&gt;Output it can actually parse and act on — keys, not opaque internal IDs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that is exotic, but almost nothing ships with it by default. So I built it in from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually in the MCP server
&lt;/h2&gt;

&lt;p&gt;TestPulse exposes an MCP server at &lt;code&gt;/mcp&lt;/code&gt;, 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.&lt;/p&gt;

&lt;p&gt;Fourteen tools, deliberately coarse-grained — not a CRUD mirror of every REST endpoint, and not a handful of do-everything workflow mega-tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deletes are REST-only, deliberately — no delete tool exists.&lt;/p&gt;

&lt;p&gt;Three of those tools (&lt;code&gt;upsert_test_case&lt;/code&gt;'s create path, &lt;code&gt;bulk_add_cases&lt;/code&gt;, &lt;code&gt;create_test_run&lt;/code&gt;) accept an optional &lt;code&gt;idempotencyKey&lt;/code&gt;. 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 &lt;em&gt;successful&lt;/em&gt; call gets cached, so retrying a failed (validation/permission) call is always safe.&lt;/p&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%2F5qrdschp9nlouwmepmsp.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%2F5qrdschp9nlouwmepmsp.png" alt="The MCP integration card in Settings — enabled, authenticated with the same scoped API tokens as REST" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The two-call demo
&lt;/h2&gt;

&lt;p&gt;The scenario I kept coming back to while designing this: &lt;em&gt;"Read requirements.md and create a test suite in TestPulse."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;An agent connected via MCP does this in two calls:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;upsert_test_suite&lt;/code&gt; — create the suite&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bulk_add_cases&lt;/code&gt; — populate it, atomically. One malformed case fails the whole batch; nothing gets half-created.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Everything else, briefly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core test management&lt;/strong&gt;: 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity&lt;/strong&gt;: local auth with sessions, scoped API tokens, SSO/OIDC, project-scoped RBAC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reporting&lt;/strong&gt;: pass-rate trends, flaky-case detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration&lt;/strong&gt;: structural importers from TestRail, Qase, Zephyr Scale, Kiwi TCMS, Xray, and Tuskr.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deployment&lt;/strong&gt;: one Go binary, Postgres, multi-arch distroless images, &lt;code&gt;docker compose up&lt;/code&gt; to evaluate, a Helm chart for production.&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%2F9us5j8at5xciot9jt7ae.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%2F9us5j8at5xciot9jt7ae.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" width="800" height="485"&gt;&lt;/a&gt;&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;git clone https://github.com/Barayo/TestPulse
&lt;span class="nb"&gt;cd &lt;/span&gt;TestPulse
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; deploy/compose/docker-compose.yml up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Repo: &lt;a href="https://github.com/Barayo/TestPulse" rel="noopener noreferrer"&gt;https://github.com/Barayo/TestPulse&lt;/a&gt;&lt;br&gt;
Docs: &lt;a href="https://github.com/Barayo/TestPulse/blob/main/docs/mcp-server.md" rel="noopener noreferrer"&gt;https://github.com/Barayo/TestPulse/blob/main/docs/mcp-server.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feedback, issues, and PRs welcome — it's a 1.0, not a finished product.&lt;/p&gt;

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