<?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: Swayam Verma</title>
    <description>The latest articles on DEV Community by Swayam Verma (@iswayamverma).</description>
    <link>https://dev.to/iswayamverma</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%2F4158441%2F0b7c4ad3-8b96-4a4d-b3f9-30c9fa7a19fd.jpg</url>
      <title>DEV Community: Swayam Verma</title>
      <link>https://dev.to/iswayamverma</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iswayamverma"/>
    <language>en</language>
    <item>
      <title>How I Rebuilt My AI Doctor App with Spring AI and MCP (and Stopped Stuffing Prompts)</title>
      <dc:creator>Swayam Verma</dc:creator>
      <pubDate>Fri, 02 Oct 2026 20:37:40 +0000</pubDate>
      <link>https://dev.to/iswayamverma/how-i-rebuilt-my-ai-doctor-app-with-spring-ai-and-mcp-and-stopped-stuffing-prompts-40b1</link>
      <guid>https://dev.to/iswayamverma/how-i-rebuilt-my-ai-doctor-app-with-spring-ai-and-mcp-and-stopped-stuffing-prompts-40b1</guid>
      <description>&lt;p&gt;Hi, I'm &lt;strong&gt;Swayam Verma&lt;/strong&gt;, a backend developer from Bhopal, India, working with Java and Spring Boot. This post is about &lt;strong&gt;Virtual AI Doctor&lt;/strong&gt;, an AI health assistant I built, and the biggest change I made to it: moving from prompt concatenation to &lt;strong&gt;agentic tool calling with the Model Context Protocol (MCP)&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Virtual AI Doctor is a learning and portfolio project. It does not replace a real doctor.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What the app does
&lt;/h2&gt;

&lt;p&gt;Users describe their symptoms in &lt;strong&gt;Hindi or English&lt;/strong&gt; and get medical guidance back in the same language. The stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java 17, Spring Boot 3.5, Spring Security with JWT&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spring AI&lt;/strong&gt; for LLM orchestration (&lt;code&gt;ChatClient&lt;/code&gt;, &lt;code&gt;ChatMemory&lt;/code&gt;, tool calling)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Groq (LLaMA 3.1)&lt;/strong&gt;, accessed through Spring AI's OpenAI-compatible client&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MySQL&lt;/strong&gt; with Spring Data JPA and Hibernate&lt;/li&gt;
&lt;li&gt;Deployed on &lt;strong&gt;Render&lt;/strong&gt; (backend), &lt;strong&gt;Railway&lt;/strong&gt; (database) and &lt;strong&gt;Vercel&lt;/strong&gt; (frontend)&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%2Fqy83qqc3unvzypfx2jnt.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%2Fqy83qqc3unvzypfx2jnt.png" alt="Virtual AI Doctor pharmacy finder" width="800" height="381"&gt;&lt;/a&gt;&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%2F3samzis75yoyjlryiy4n.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%2F3samzis75yoyjlryiy4n.png" alt="Virtual AI Doctor chat screen" width="800" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with my first design
&lt;/h2&gt;

&lt;p&gt;My first version was linear. For every message, the backend built one big prompt: instructions, the user's health profile (age, blood group, allergies, medical history), the conversation so far, and the new symptoms. Then it asked the model to answer and parsed the result.&lt;/p&gt;

&lt;p&gt;That worked, but it had three problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every request carried the full patient profile, even when the question didn't need it.&lt;/li&gt;
&lt;li&gt;Prompts grew with every turn.&lt;/li&gt;
&lt;li&gt;Detecting emergencies meant parsing a severity tag out of plain text, which was fragile.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The fix: let the model call tools
&lt;/h2&gt;

&lt;p&gt;Spring AI supports an MCP server/client architecture, built on the official MCP Java SDK. I turned two parts of the app into tools the model can call when it decides it needs them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getPatientProfile&lt;/code&gt;&lt;/strong&gt;: fetches the patient's health profile on demand, instead of injecting it into every request&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;flagHighSeverity&lt;/code&gt;&lt;/strong&gt;: called by the model itself when it judges a consultation to be an emergency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model now decides when it needs the profile, and it decides when something is serious enough to flag. The backend no longer scrapes severity out of text. A tool call is a structured action, not a guess at a string.&lt;/p&gt;

&lt;p&gt;When a high-severity flag comes in, the backend sends an &lt;strong&gt;email alert&lt;/strong&gt; through the Brevo API and the user can download a &lt;strong&gt;PDF report&lt;/strong&gt; of the consultation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making tools work per user
&lt;/h2&gt;

&lt;p&gt;Tool calls need to know &lt;em&gt;whose&lt;/em&gt; session they belong to. I used a request-scoped context (&lt;code&gt;ConsultationContext&lt;/code&gt;) so the tools can see the current session without exposing it to the model. Getting this right matters, because a tool that returns the wrong patient's profile would be a serious bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory across turns
&lt;/h2&gt;

&lt;p&gt;Multi-turn conversations use Spring AI's &lt;strong&gt;&lt;code&gt;ChatMemory&lt;/code&gt;&lt;/strong&gt;, backed by Spring AI's JDBC chat-memory repository on MySQL. The assistant keeps the context of the conversation, and past sessions are stored in the consultation history with diagnosis and severity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;JWT authentication&lt;/strong&gt; for signup and login, with secured APIs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nearby pharmacy finder&lt;/strong&gt; using OpenStreetMap and the Overpass API (no API key needed)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health profile&lt;/strong&gt; with age, blood group, allergies and medical history&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dark and light mode&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tool calling beats prompt stuffing.&lt;/strong&gt; The model asks for context when it needs it, which keeps prompts smaller and data access explicit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured actions beat text parsing.&lt;/strong&gt; Replacing "find the severity tag in the reply" with a &lt;code&gt;flagHighSeverity&lt;/code&gt; tool made emergency handling cleaner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security is part of the design.&lt;/strong&gt; In a health app, authentication and per-session context come before features.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;MEDIUM-severity tool&lt;/strong&gt; to go with the existing HIGH-severity flag&lt;/li&gt;
&lt;li&gt;A React frontend&lt;/li&gt;
&lt;li&gt;WebSocket-based real-time chat&lt;/li&gt;
&lt;li&gt;Voice input for symptoms&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it and see the code
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live demo: &lt;a href="https://virtual-ai-doctor-frontend.vercel.app" rel="noopener noreferrer"&gt;https://virtual-ai-doctor-frontend.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source code: &lt;a href="https://github.com/Swayamverma8/Virtual-Ai-Doctor" rel="noopener noreferrer"&gt;https://github.com/Swayamverma8/Virtual-Ai-Doctor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;My portfolio: &lt;a href="https://swayam-portfolio-eta.vercel.app" rel="noopener noreferrer"&gt;https://swayam-portfolio-eta.vercel.app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm a final-year CSE student at TIT Bhopal, a Google Student Ambassador (Gemini AI), and open to backend and AI-focused roles. Connect with me on &lt;a href="https://www.linkedin.com/in/iswayamverma/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; or &lt;a href="https://github.com/iswayamverma" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you've built something similar with Spring AI or MCP, I'd love to hear what you ran into.&lt;/p&gt;

</description>
      <category>java</category>
      <category>springboot</category>
      <category>ai</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
