<?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: edu</title>
    <description>The latest articles on DEV Community by edu (@d_edu_eebba6de1155fe).</description>
    <link>https://dev.to/d_edu_eebba6de1155fe</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%2F4084226%2Febcf414e-5701-4fa2-8b5d-339acaa0e572.png</url>
      <title>DEV Community: edu</title>
      <link>https://dev.to/d_edu_eebba6de1155fe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/d_edu_eebba6de1155fe"/>
    <language>en</language>
    <item>
      <title>3 problems I hit connecting an AI agent to financial data (and how I fixed them)</title>
      <dc:creator>edu</dc:creator>
      <pubDate>Wed, 19 Aug 2026 04:31:06 +0000</pubDate>
      <link>https://dev.to/d_edu_eebba6de1155fe/3-problems-i-hit-connecting-an-ai-agent-to-financial-data-and-how-i-fixed-them-ed9</link>
      <guid>https://dev.to/d_edu_eebba6de1155fe/3-problems-i-hit-connecting-an-ai-agent-to-financial-data-and-how-i-fixed-them-ed9</guid>
      <description>&lt;p&gt;I've been wiring AI assistants into things that have to be &lt;em&gt;correct&lt;/em&gt;, and financial data is where the wheels come off fastest. "Roughly right" is fine for a chatbot; it is not fine for a number someone files a tax return on.&lt;/p&gt;

&lt;p&gt;Here are three problems I actually hit, in order, and what fixed each. None of the fixes are exotic — but the order matters, because each one exposes the next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 1: the model makes up numbers, confidently
&lt;/h2&gt;

&lt;p&gt;The first version just asked the model. "What's the GST on $4,180?" It answered instantly and looked right. It was subtly wrong about the threshold, and — worse — it had no idea it was wrong.&lt;/p&gt;

&lt;p&gt;This is the base failure of using a raw LLM for facts: it answers from training data with a cut-off date, and it cannot tell you how current that data is. For anything regulated, that disqualifies it on its own.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix: stop asking the model to &lt;em&gt;remember&lt;/em&gt;, make it &lt;em&gt;look up&lt;/em&gt;.&lt;/strong&gt; This is what MCP (Model Context Protocol) is for — an open standard for giving an assistant a tool it calls at question time. I pointed it at a public tax server instead of trusting recall:&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;"tax"&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;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://taxmcp.ai2fin.com"&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;Now the assistant fetches the figure from a defined source rather than inventing it. The number stopped drifting between runs, which was the first tell that it was real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 2: I could not tell a real answer from a lucky guess
&lt;/h2&gt;

&lt;p&gt;Grounding fixed the accuracy, but it created a subtler problem: the answers &lt;em&gt;looked&lt;/em&gt; the same whether they came from the tool or from the model filling a gap. If a call silently failed and the model improvised, I had no way to see it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix: demand provenance on every field, and refuse anything without it.&lt;/strong&gt; The server I used returns the source authority and a verification date on every response:&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="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;income_tax_estimate&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="err"&gt;country:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;income:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95000&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="err"&gt;&amp;lt;&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="err"&gt;incomeTax:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;takeHome:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;source:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ATO — ato.gov.au"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dataVerifiedOn:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-01"&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;So my rule became: if a number arrives without a &lt;code&gt;source&lt;/code&gt; and a &lt;code&gt;dataVerifiedOn&lt;/code&gt;, it does not get shown to the user. That one check turns "the model said so" into "the ATO said so, as of this date, and here is the link" — which is the difference between a demo and a tool you would put in front of a client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 3: two copies of the truth drifted apart
&lt;/h2&gt;

&lt;p&gt;By now I had the assistant answering from the tool, &lt;em&gt;and&lt;/em&gt; a web page showing the same calculators. Predictably, they disagreed. Someone updated one rate table and not the other, and now the chatbot and the website quoted different take-home numbers for the same salary. This is the oldest bug in the book and it is brutal in finance, because both answers look authoritative.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix: one engine, many surfaces — never two copies of the rate tables.&lt;/strong&gt; The setup that worked has the MCP server and the web calculators reading the &lt;em&gt;same&lt;/em&gt; underlying engine, so there is nothing to keep in sync by hand. When I saw the same &lt;code&gt;income_tax_estimate&lt;/code&gt; value in the chat and on the page, I knew there was only one source of truth behind both.&lt;/p&gt;

&lt;p&gt;If you have ever maintained two copies of anything that changes yearly, you know why this is the fix that actually lets you sleep.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern, stripped down
&lt;/h2&gt;

&lt;p&gt;Three rules, and they generalise well beyond tax:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Don't let the model remember facts — give it a tool that fetches them.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Require provenance (source + date) on every fetched value, and reject the ones without it.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep one engine behind every surface, so your answers cannot disagree with each other.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tax domain just makes the stakes legible — a wrong GST figure is obvious and embarrassing. But the same three problems show up any time an assistant touches data it has no business memorising: pricing, inventory, medical dosages, anything that changes.&lt;/p&gt;

&lt;p&gt;The server I used for all of this is free and needs no account, if you want something real to test the pattern against: &lt;a href="https://taxmcp.ai2fin.com" rel="noopener noreferrer"&gt;taxmcp.ai2fin.com&lt;/a&gt;. Point your client at it and try to catch it giving you a number without a source. You won't — and that constraint is the whole point.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Grounding an LLM in real tax data with a public MCP server</title>
      <dc:creator>edu</dc:creator>
      <pubDate>Wed, 19 Aug 2026 04:26:39 +0000</pubDate>
      <link>https://dev.to/d_edu_eebba6de1155fe/grounding-an-llm-in-real-tax-data-with-a-public-mcp-server-5h58</link>
      <guid>https://dev.to/d_edu_eebba6de1155fe/grounding-an-llm-in-real-tax-data-with-a-public-mcp-server-5h58</guid>
      <description>&lt;p&gt;We all know the failure mode: ask an LLM for a tax rate and it'll hand you a confident number with zero provenance. Fine for a demo, useless for anything you'd ship. The interesting question is how you &lt;em&gt;ground&lt;/em&gt; the model in a source of truth — and MCP has quietly made that a one-liner.&lt;/p&gt;

&lt;p&gt;Quick recap for anyone who missed it: &lt;strong&gt;Model Context Protocol&lt;/strong&gt; is an open standard (Anthropic, late 2024) for connecting an assistant to external tools and data. Client support is now broad — Claude Desktop, Claude Code, ChatGPT, Cursor, VS Code, Windsurf.&lt;/p&gt;

&lt;h2&gt;
  
  
  A free public server to try it against
&lt;/h2&gt;

&lt;p&gt;2Fin runs a free public MCP server for tax at &lt;code&gt;taxmcp.ai2fin.com&lt;/code&gt;. No key, no auth. Point any MCP client at it:&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;"tax"&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;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://taxmcp.ai2fin.com"&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;You get eight tools: &lt;code&gt;tax_rate_lookup&lt;/code&gt;, &lt;code&gt;compute_gst_vat&lt;/code&gt;, &lt;code&gt;income_tax_estimate&lt;/code&gt;, &lt;code&gt;company_tax_estimate&lt;/code&gt;, &lt;code&gt;cgt_estimate&lt;/code&gt;, &lt;code&gt;superannuation_estimate&lt;/code&gt;, &lt;code&gt;student_loan_repayment&lt;/code&gt;, &lt;code&gt;compare_countries&lt;/code&gt; — across 88 countries.&lt;/p&gt;

&lt;h2&gt;
  
  
  The design choice worth stealing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Every response includes its source authority and a &lt;code&gt;dataVerifiedOn&lt;/code&gt; date.&lt;/strong&gt; That's the part most "AI + data" integrations skip, and it's exactly what makes the output auditable instead of vibes. A wrong or stale number is &lt;em&gt;visible&lt;/em&gt; rather than hidden inside a confident sentence.&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="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;income_tax_estimate&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="err"&gt;country:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;income:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;95000&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="err"&gt;&amp;lt;&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="err"&gt;incomeTax:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;medicareLevy:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;takeHome:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;source:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ATO — ato.gov.au"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dataVerifiedOn:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-06-01"&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;The same engine backs their web calculators, so the MCP answers and the site can't drift apart — which, if you've ever maintained two copies of the same rate table, you'll appreciate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Good worked example of the pattern: don't make the model &lt;em&gt;remember&lt;/em&gt; facts, give it a tool that &lt;em&gt;fetches&lt;/em&gt; them with a citation attached. The tax domain just makes the stakes obvious — but the same design applies to any data your assistant has no business memorising.&lt;/p&gt;

&lt;p&gt;If you want to poke at it, it's free and needs no account: &lt;a href="https://taxmcp.ai2fin.com" rel="noopener noreferrer"&gt;taxmcp.ai2fin.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
