<?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: Ileana Mazilu</title>
    <description>The latest articles on DEV Community by Ileana Mazilu (@ileanamazilu1).</description>
    <link>https://dev.to/ileanamazilu1</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%2F4038102%2F904ad60b-bc35-496b-a302-9aa41a6d28a9.png</url>
      <title>DEV Community: Ileana Mazilu</title>
      <link>https://dev.to/ileanamazilu1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ileanamazilu1"/>
    <language>en</language>
    <item>
      <title>MDR Evidence Gap Agent: finding unsupported claims with Sanity Context</title>
      <dc:creator>Ileana Mazilu</dc:creator>
      <pubDate>Mon, 21 Sep 2026 06:45:55 +0000</pubDate>
      <link>https://dev.to/ileanamazilu1/mdr-evidence-gap-agent-finding-unsupported-claims-with-sanity-context-nio</link>
      <guid>https://dev.to/ileanamazilu1/mdr-evidence-gap-agent-finding-unsupported-claims-with-sanity-context-nio</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/sanity-2026-09-16"&gt;Sanity Challenge, Path One: Ship an Agent That Queries Real Content&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I'm a medical writer, and I built this project to explore how the clinical evidence requirements of the EU MDR can be modeled as structured content. In a clinical evaluation report, every claim needs supporting clinical evidence, and it is easy to lose track of which claims are still unsupported and which MDR requirements they touch. I have not worked on MDR submissions professionally: this is a learning project built from the public text of the regulation, and I built it with the help of an AI assistant.&lt;/p&gt;

&lt;p&gt;The result is &lt;strong&gt;MDR Evidence Gap Agent&lt;/strong&gt;: an agent that answers audit questions over a small dossier stored in Sanity, for example &lt;em&gt;"Which claims have no supporting evidence, and which MDR requirements do they affect?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything is synthetic.&lt;/strong&gt; The device (VascuSeal, a fictional Class III vascular closure device), its claims and its studies are invented. The requirement entries are my own short paraphrases of the regulation, not the official text. This is a demo, not regulatory advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why structure matters
&lt;/h2&gt;

&lt;p&gt;A keyword search finds pages that mention things. It cannot find what is &lt;em&gt;missing&lt;/em&gt;. In this project the gap is a claim with no linked evidence, which only exists as an empty reference list. The agent finds it by following references with GROQ, not by matching words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Live app:&lt;/strong&gt; &lt;a href="https://mdr-evidence-gap-agent.vercel.app" rel="noopener noreferrer"&gt;https://mdr-evidence-gap-agent.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ask the agent:&lt;/strong&gt; type a question or click an example. You can see every tool call and every GROQ query the agent wrote on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dossier:&lt;/strong&gt; all claims, evidence and requirements read from Sanity through the same Context endpoint. Claims with no evidence are flagged, and each has a button that asks the agent about that gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coverage:&lt;/strong&gt; a requirement by claim matrix that shows which requirements are at risk.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;About:&lt;/strong&gt; how the content is structured, and the limits of the demo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note: the live demo runs on free quotas and on a Sanity trial that ends around October 19. It may become unavailable after that, depending on what the free plan includes. The sample run below shows what the agent returns.&lt;/p&gt;

&lt;p&gt;Sample run. The agent called &lt;code&gt;initial_context&lt;/code&gt;, then wrote its own queries. One of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"claim"&lt;/span&gt;&lt;span class="ss"&gt;]{&lt;/span&gt;
  &lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"requirements"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="n"&gt;requirements&lt;/span&gt;&lt;span class="ss"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;annex&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ss"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"evidence"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="ss"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;{&lt;/span&gt; &lt;span class="n"&gt;_id&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;studyType&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="ss"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"evidenceCount"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;evidence&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;
&lt;span class="ss"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its answer: two claims have no linked evidence.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Five-year seal durability&lt;/strong&gt; affects Article 61(1) (clinical evidence for conformity) and Annex XIV Part B (PMCF).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-world PMCF confirmation&lt;/strong&gt; affects Annex XIV Part B (PMCF) and Article 86 (PSUR).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the two gaps I planted in the dataset. The agent found both without being told where to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;This is the core agent loop. It connects to the Sanity Context MCP endpoint, hands the endpoint's tools to Gemini and loops until the model answers. The deployed app wraps the same loop in a streaming API route with a rate limit and retries when the model is busy, and keeps all keys on the server.&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;Client&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;@modelcontextprotocol/sdk/client/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StreamableHTTPClientTransport&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;@modelcontextprotocol/sdk/client/streamableHttp.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;GoogleGenAI&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;@google/genai&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CONTEXT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CONTEXT_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GEMINI_API_KEY&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&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;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Which claims in the VascuSeal dossier have no supporting evidence, and which MDR requirements do they affect?&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;SYSTEM&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are the MDR Evidence Gap Agent.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You audit a SYNTHETIC Class III device dossier (VascuSeal) stored in Sanity as three linked document types: requirement, claim and evidence.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Call initial_context first, then use groq_query to follow references between claims, requirements and evidence.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Only report what the linked documents show. Never declare a claim compliant. Requirement texts are paraphrased summaries, not the official MDR text.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Answer concisely in English and list each gap with the affected requirements.&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;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &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;mcp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&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;mdr-evidence-gap-agent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&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="nx"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StreamableHTTPClientTransport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONTEXT_URL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;requestInit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;CONTEXT_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt; &lt;span class="p"&gt;}&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;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listTools&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;functionDeclarations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;schema&lt;/span&gt; &lt;span class="o"&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inputSchema&lt;/span&gt; &lt;span class="o"&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="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$schema&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&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="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parametersJsonSchema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;schema&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;ai&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleGenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GEMINI_API_KEY&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;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;question&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;let&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;step&lt;/span&gt;&lt;span class="o"&gt;++&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;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;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gemini-3.1-flash-lite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;systemInstruction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;functionDeclarations&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;calls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;functionCalls&lt;/span&gt; &lt;span class="o"&gt;||&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;calls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;content&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;responses&lt;/span&gt; &lt;span class="o"&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;call&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;calls&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;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callTool&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="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="o"&gt;||&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;out&lt;/span&gt; &lt;span class="o"&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;content&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;functionResponse&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="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;out&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="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responses&lt;/span&gt; &lt;span class="p"&gt;});&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;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Used Sanity
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema:&lt;/strong&gt; three document types defined in TypeScript in Sanity Studio: &lt;code&gt;requirement&lt;/code&gt; (title, annex, text), &lt;code&gt;evidence&lt;/code&gt; (title, study type, summary) and &lt;code&gt;claim&lt;/code&gt; (title, text, and two arrays of references: &lt;code&gt;requirements&lt;/code&gt; and &lt;code&gt;evidence&lt;/code&gt;). The links between them are what make the agent's questions answerable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content:&lt;/strong&gt; 6 requirement summaries, 4 pieces of synthetic evidence and 5 synthetic claims in the &lt;code&gt;production&lt;/code&gt; dataset. Two claims deliberately have an empty &lt;code&gt;evidence&lt;/code&gt; array.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Studio:&lt;/strong&gt; deployed at &lt;a href="https://mdr-evidence-gap.sanity.studio/" rel="noopener noreferrer"&gt;https://mdr-evidence-gap.sanity.studio/&lt;/a&gt; (requires a Sanity login), so the Context endpoint can read the deployed schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sanity Context:&lt;/strong&gt; an MCP endpoint created in the Context app, with a dataset source, a GROQ filter (&lt;code&gt;_type in ["requirement", "claim", "evidence"]&lt;/code&gt;) that limits what the agent can read, and custom instructions. The app connects with a read-only Context Viewer token kept on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What the agent does with the content:&lt;/strong&gt; it calls &lt;code&gt;initial_context&lt;/code&gt; to learn the schema, then runs &lt;code&gt;groq_query&lt;/code&gt; to resolve references (&lt;code&gt;-&amp;gt;&lt;/code&gt;, &lt;code&gt;count()&lt;/code&gt;, &lt;code&gt;references()&lt;/code&gt;), and reports each claim with no evidence together with the requirements it is tied to. The Dossier and Coverage pages read the same content through the same endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sanity Project Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Project ID:&lt;/strong&gt; &lt;code&gt;0hmb0qqg&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dataset:&lt;/strong&gt; &lt;code&gt;production&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Studio:&lt;/strong&gt; &lt;a href="https://mdr-evidence-gap.sanity.studio/" rel="noopener noreferrer"&gt;https://mdr-evidence-gap.sanity.studio/&lt;/a&gt; (requires a Sanity login)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP endpoint name:&lt;/strong&gt; &lt;code&gt;mdr-evidence-gap-agent&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I built a small Knowledge Base from the text of four MDR provisions (Article 61, Annex XIV, Article 86, Annex I), but the agent does not read it yet: the endpoint behind this demo serves the dataset only.&lt;/li&gt;
&lt;li&gt;The dataset is small (15 documents) and fully synthetic.&lt;/li&gt;
&lt;li&gt;Requirement texts are short paraphrases, simplified, and were checked against public copies of the regulation with the help of an AI assistant. They are not legal text.&lt;/li&gt;
&lt;li&gt;The agent reports what the linked documents show. It does not judge compliance.&lt;/li&gt;
&lt;li&gt;The demo uses a free model quota, so questions are limited to 10 per visitor per hour, and the model may occasionally be busy. If you see an error, try again.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>sanitychallenge</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
    <item>
      <title>I built two AI agents at recent hackathons — now selling the codebases</title>
      <dc:creator>Ileana Mazilu</dc:creator>
      <pubDate>Sat, 29 Aug 2026 21:37:48 +0000</pubDate>
      <link>https://dev.to/ileanamazilu1/i-built-two-ai-agents-at-recent-hackathons-now-selling-the-codebases-346g</link>
      <guid>https://dev.to/ileanamazilu1/i-built-two-ai-agents-at-recent-hackathons-now-selling-the-codebases-346g</guid>
      <description>&lt;p&gt;Over the past month I've been deep in hackathon mode, building AI agents for healthcare/pharma use cases. Two of them turned out solid enough that I think someone else could build on them:&lt;/p&gt;

&lt;p&gt;RxTrack Agent (ADK version) — a prescription-tracking agent built with Google's Agent Development Kit + Gemini + Firestore. GCP deployment verified, README included, built for the "All Things Agentic" hackathon.&lt;/p&gt;

&lt;p&gt;RxTrack Agent (AWS version) — the same concept, rebuilt with AWS Strands SDK + Bedrock + Firestore, for "Agents for Humans."&lt;/p&gt;

&lt;p&gt;Both are functional, tested, complete codebases. I don't have the bandwidth to develop them further right now, so I'm selling the source code outright — $149 each, no exclusivity, full ownership transfers to the buyer.&lt;/p&gt;

&lt;p&gt;Links: &lt;a href="https://dev.tourl"&gt;RxTrack ADK&lt;/a&gt;: &lt;a href="https://iancuileana.gumroad.com/l/agtwi" rel="noopener noreferrer"&gt;https://iancuileana.gumroad.com/l/agtwi&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.tourl"&gt; RxTrack AWS&lt;/a&gt;:&lt;a href="https://iancuileana.gumroad.com/l/sqwkl" rel="noopener noreferrer"&gt;https://iancuileana.gumroad.com/l/sqwkl&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Happy to answer questions about the build, the frameworks, or the hackathon experience in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>showdev</category>
      <category>python</category>
      <category>javascript</category>
    </item>
    <item>
      <title>title: The Three-Headed Bug That Almost Sank My Compliance App published: true tags: devchallenge, bugsmash</title>
      <dc:creator>Ileana Mazilu</dc:creator>
      <pubDate>Mon, 27 Jul 2026 13:23:14 +0000</pubDate>
      <link>https://dev.to/ileanamazilu1/title-the-three-headed-bug-that-almost-sank-my-compliance-app-published-true-tags-devchallenge-2n1g</link>
      <guid>https://dev.to/ileanamazilu1/title-the-three-headed-bug-that-almost-sank-my-compliance-app-published-true-tags-devchallenge-2n1g</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;I'm not a traditional developer. I spent 20 years as a pharmacy technician and medical writer before I taught myself to build software using AI-assisted, no-code tools. Earlier this year, I built MedAd Compliance AI—a tool powered by Google's Gemini API that reviews medical device documentation against EU MDR and ICH-GCP regulatory standards. I didn't write the code by hand; I built it through natural-language prompts in Lovable, describing what I needed in plain English and iterating until the app behaved. That doesn't mean the debugging disappeared—it just looked different than a typical engineer's day. Three bugs, back to back, almost took the whole project down before it ever reached a user.&lt;/p&gt;

&lt;p&gt;You can see the live tool here: &lt;a href="https://ileana-mazilu.lovable.app/medad-ai" rel="noopener noreferrer"&gt;https://ileana-mazilu.lovable.app/medad-ai&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #1: The AI That Lied With Confidence
&lt;/h2&gt;

&lt;p&gt;The review engine would sometimes generate compliance feedback that sounded completely authoritative—citing specific clauses or requirements—but the details didn't actually match real EU MDR or ICH-GCP language. It wasn't obviously wrong. It was confidently, fluently wrong, which is far more dangerous in a regulatory tool than an answer that's obviously broken.&lt;/p&gt;

&lt;p&gt;As someone with 20 years of hands-on regulatory experience, I could catch this because I knew the real standards well enough to spot the mismatch. A user without that background wouldn't have known to question it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I did:&lt;/strong&gt; I rewrote the underlying instructions given to the AI, adding explicit constraints forcing it to only reference verifiable regulatory language and to flag uncertainty instead of inventing specifics when it wasn't sure. I tested it repeatedly against documents where I already knew the correct compliance answer, comparing the AI's output against my own expert review until the confident-but-wrong pattern stopped showing up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #2: The PDF That Refused to Change
&lt;/h2&gt;

&lt;p&gt;Every time I generated a new compliance report for a different document, the exported PDF looked suspiciously similar to the last one—even when the underlying analysis should have been completely different. I only caught it because I ran two clearly different test documents back to back and compared the outputs side by side; they were nearly identical, which should have been impossible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I did:&lt;/strong&gt; I traced it back to the report template pulling from a cached or default data set instead of the fresh analysis each time. I had to rebuild the export step so it explicitly pulled the current session's results instead of falling back to old data, then re-tested with several different documents in a row to confirm each report was actually unique.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug #3: The Rewrite Feature That Broke Itself
&lt;/h2&gt;

&lt;p&gt;The tool's rewriting function—meant to suggest corrected, compliant language for flawed document sections—would sometimes cut off mid-sentence or return incomplete text, especially on longer passages. It wasn't a crash; it was worse, because it looked finished when it wasn't, and a user could easily miss that the suggested rewrite was truncated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I did:&lt;/strong&gt; I identified that longer inputs were hitting an output limit that silently cut the response short instead of erroring out. I adjusted how the tool handled longer passages, breaking them into smaller chunks the AI could process fully instead of trying to force one long response, then verified the fix by testing with the longest, most complex document sections I could find.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Building with AI tools doesn't mean the debugging disappears—it just changes shape. Instead of stepping through stack traces, I was stepping through prompts, outputs, and edge cases, trying to figure out not just &lt;em&gt;what&lt;/em&gt; broke, but &lt;em&gt;why&lt;/em&gt; an AI model would confidently produce something wrong. That's a different kind of debugging discipline, and it took my regulatory background—not a computer science background—to actually catch two of these three bugs in the first place.&lt;/p&gt;

&lt;p&gt;What I'm proud of: none of these bugs were hypothetical. They stood between real users and a tool meant to help people navigate genuinely high-stakes regulatory work. Fixing them wasn't just a technical win—it was the difference between a tool I could stand behind and one I couldn't.&lt;/p&gt;

&lt;p&gt;🔗 Try the tool: &lt;a href="https://ileana-mazilu.lovable.app/medad-ai" rel="noopener noreferrer"&gt;https://ileana-mazilu.lovable.app/medad-ai&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
    </item>
    <item>
      <title>How I Built an AI Tool for Medical Device Regulatory Compliance</title>
      <dc:creator>Ileana Mazilu</dc:creator>
      <pubDate>Mon, 20 Jul 2026 12:03:55 +0000</pubDate>
      <link>https://dev.to/ileanamazilu1/how-i-built-an-ai-tool-for-medical-device-regulatory-compliance-716</link>
      <guid>https://dev.to/ileanamazilu1/how-i-built-an-ai-tool-for-medical-device-regulatory-compliance-716</guid>
      <description>&lt;p&gt;After 20+ years in pharmacy and 3 years as a Senior Clinical Evaluation Medical Writer, I kept seeing the same problem: small and mid-sized medical device companies often lack large in-house regulatory teams, yet face the same compliance scrutiny as major manufacturers when preparing Clinical Evaluation Reports (CER), Post-Market Clinical Follow-up (PMCF), and Periodic Safety Update Reports (PSUR).&lt;/p&gt;

&lt;p&gt;Manual review is slow, expensive, and error-prone — even small gaps can delay approval or trigger regulatory findings.&lt;/p&gt;

&lt;p&gt;So I built MedAd Compliance AI, combining my real-world EU MDR and ICH-GCP expertise with AI-powered document review. Unlike generic AI writing tools, it's grounded in actual clinical evaluation practice — flagging compliance gaps before submission, not after rejection.&lt;/p&gt;

&lt;p&gt;You can try it here: &lt;a href="https://ileana-mazilu.lovable.app" rel="noopener noreferrer"&gt;https://ileana-mazilu.lovable.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love feedback from anyone in medtech, regulatory affairs, or healthtech!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>healthtech</category>
      <category>startup</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
