<?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: Rhein</title>
    <description>The latest articles on DEV Community by Rhein (@rhein).</description>
    <link>https://dev.to/rhein</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%2F4056679%2F3af808c3-fd29-43c1-9c1d-20bf58113a1c.jpg</url>
      <title>DEV Community: Rhein</title>
      <link>https://dev.to/rhein</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rhein"/>
    <language>en</language>
    <item>
      <title>I turned my calculator site into an MCP server so AI assistants stop guessing at math</title>
      <dc:creator>Rhein</dc:creator>
      <pubDate>Sat, 22 Aug 2026 08:29:07 +0000</pubDate>
      <link>https://dev.to/rhein/i-turned-my-calculator-site-into-an-mcp-server-so-ai-assistants-stop-guessing-at-math-25l</link>
      <guid>https://dev.to/rhein/i-turned-my-calculator-site-into-an-mcp-server-so-ai-assistants-stop-guessing-at-math-25l</guid>
      <description>&lt;p&gt;Ask a language model to compute &lt;code&gt;2 + 3 * sqrt(16)&lt;/code&gt; and it will usually get it right. Ask it for &lt;code&gt;1,000,000&lt;/code&gt; seconds in days, or a compound-interest figure over 30 years, and it will answer with total confidence — and sometimes be quietly wrong. It's predicting the next token, not running a calculator.&lt;/p&gt;

&lt;p&gt;I already had a calculator. A whole site of them — 70-odd converters and calculators I'd built over the past year. So instead of writing another chatbot wrapper, I did the obvious thing: I let the AI call the real tools directly, over MCP.&lt;/p&gt;

&lt;p&gt;Here's how that went, including the parts that didn't work.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP in one paragraph
&lt;/h2&gt;

&lt;p&gt;The Model Context Protocol is a standard way for an AI assistant to discover and call external tools. The assistant asks your server "what can you do?", gets back a list of typed tools, and calls them with arguments — you run the actual code and hand back a real result. Claude speaks it, and a growing list of other clients do too. Think of it as giving the model hands instead of asking it to remember everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint that shaped everything
&lt;/h2&gt;

&lt;p&gt;My site is static, served by a single Cloudflare Worker. There's no backend to spin up, no container, no database. So the MCP server couldn't be a separate service — it had to live &lt;em&gt;inside&lt;/em&gt; the Worker I already deploy.&lt;/p&gt;

&lt;p&gt;That pushed me toward the simplest possible design: JSON-RPC 2.0 over plain HTTP, completely stateless. No sessions, no auth, no storage. A POST is a tool call, a GET returns server info. The whole thing is a few hundred lines with zero dependencies, sharing the same calculation code the website's buttons already run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;POST /mcp
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "scientific_calculator",
              "arguments": { "expression": "2 + 3 * sqrt(16)" } } }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;"jsonrpc"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&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;"expression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2 + 3 * sqrt(16)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&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;h2&gt;
  
  
  Decision 1: every result links back to a real page
&lt;/h2&gt;

&lt;p&gt;The single design choice I care about most: every tool response includes a &lt;code&gt;source_url&lt;/code&gt; pointing at the human page for that tool.&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;"expression"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2 + 3 * sqrt(16)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"result"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SmartTools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_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://smart-tools.xyz/en/scientific-calculator"&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;Two reasons. The honest one: if an assistant tells a user "your monthly payment is X," the user should be able to click through and see the formula, not take a black box on faith. The selfish one: it's a citation trail back to the site. (The link even localises — pass &lt;code&gt;locale: "de"&lt;/code&gt; and you get the German page.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision 2: no &lt;code&gt;eval&lt;/code&gt;, ever
&lt;/h2&gt;

&lt;p&gt;The scientific calculator evaluates arbitrary expressions — &lt;code&gt;sin(pi/6)&lt;/code&gt;, &lt;code&gt;5!&lt;/code&gt;, &lt;code&gt;log(1000)&lt;/code&gt;, nested parentheses, operator precedence. The lazy way is &lt;code&gt;eval()&lt;/code&gt;. In code that runs inside the same Worker that serves the whole site, that's a door I'm not opening.&lt;/p&gt;

&lt;p&gt;So it's a hand-rolled recursive-descent parser. Boring, safe, and it forced me to make the grammar decisions explicit. One that trips people up:&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="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;   &lt;span class="c1"&gt;// -4, not 4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unary minus binds &lt;em&gt;looser&lt;/em&gt; than exponentiation, so this reads as &lt;code&gt;-(2^2)&lt;/code&gt; — the mathematical convention, and the same answer a TI calculator gives. Factorial binds tighter than &lt;code&gt;^&lt;/code&gt;; &lt;code&gt;%&lt;/code&gt; is floored modulo, not "percent". None of that is guessed at runtime; it's all in the parser.&lt;/p&gt;

&lt;p&gt;The parser is byte-for-byte identical between the website widget and the MCP tool, and a test asserts they never drift apart.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I couldn't expose (the honest ceiling)
&lt;/h2&gt;

&lt;p&gt;Not everything made it. The site has image converters, a PDF-to-image tool, an Excel viewer — and none of them can be MCP tools. They rely on a browser canvas and WASM running on the user's own machine; there's nothing to run in a stateless Worker. About ten tools stay browser-only. Anything that's pure calculation or text made the cut — 73 tools so far.&lt;/p&gt;

&lt;p&gt;I'd rather say that plainly than pretend the server does more than it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;No key, no sign-up, nothing stored — it reads your input, computes, replies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http smart-tools https://smart-tools.xyz/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or point any MCP client at the endpoint directly:&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;"smart-tools"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&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://smart-tools.xyz/mcp"&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;There's a fuller write-up (and the tool list) at &lt;a href="https://smart-tools.xyz/en/mcp" rel="noopener noreferrer"&gt;smart-tools.xyz/en/mcp&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd still like to figure out
&lt;/h2&gt;

&lt;p&gt;The thing I keep going back and forth on: how much a general-purpose calculator tool actually helps a modern model, versus just letting it reason. For arithmetic it clearly reasons fine now. For long unit conversions, financial formulas, and anything where being &lt;em&gt;exactly&lt;/em&gt; right matters, handing it a deterministic tool still feels safer than trusting a token predictor. If you've measured this properly, I'd genuinely like to hear it.&lt;/p&gt;

&lt;p&gt;It's a side project, it's free, and I built it because the alternative was trusting an LLM's mental math. If you try it and something's wrong, tell me — that's the whole point of a tool that shows its work.&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>I built 79 browser tools that never upload your data — here's why client-side matters</title>
      <dc:creator>Rhein</dc:creator>
      <pubDate>Fri, 31 Jul 2026 13:27:31 +0000</pubDate>
      <link>https://dev.to/rhein/i-built-79-browser-tools-that-never-upload-your-data-heres-why-client-side-matters-2o29</link>
      <guid>https://dev.to/rhein/i-built-79-browser-tools-that-never-upload-your-data-heres-why-client-side-matters-2o29</guid>
      <description>&lt;p&gt;Every developer has hit this: you need to resize an image, decode a JWT, or format a messy JSON blob. So you search "online [thing]", land on some tool, and… it wants to upload your file to a server first.&lt;/p&gt;

&lt;p&gt;For a random stock photo, fine. For a customer CSV, a production JWT, or a screenshot of an internal dashboard? You just handed a stranger's server your data.&lt;/p&gt;

&lt;p&gt;I got tired of that, so I built &lt;strong&gt;&lt;a href="https://smart-tools.xyz" rel="noopener noreferrer"&gt;SmartTools&lt;/a&gt;&lt;/strong&gt; — 79 small, single-purpose tools that do all the work &lt;strong&gt;in your browser&lt;/strong&gt;. Nothing is uploaded. Ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule: the data never leaves the tab
&lt;/h2&gt;

&lt;p&gt;Open any tool, open your browser's Network tab, and use it. You'll see no upload request. That's the whole design constraint:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Image tools&lt;/strong&gt; (resize, crop, compress, convert JPG/PNG/WebP, favicon, colour-palette extraction) run on a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;. The file is read with &lt;code&gt;FileReader&lt;/code&gt;, drawn, re-encoded, and handed back — the bytes never touch a network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text &amp;amp; dev tools&lt;/strong&gt; (JSON formatter, Base64, JWT decode, hash, regex tester, URL encode, cron parser, UUID, number-base) are plain JavaScript on the string you paste.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculators&lt;/strong&gt; (finance, health, math) are just arithmetic on your device.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because there's no server round-trip, the tools also work offline after first load, and there are no accounts, no rate limits, and no "upgrade to process files over 5 MB".&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is more than a privacy nicety
&lt;/h2&gt;

&lt;p&gt;A few things fall out of the client-side approach that genuinely surprised me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The JSON formatter uses your browser's own &lt;code&gt;JSON.parse&lt;/code&gt;.&lt;/strong&gt; If it says your document is valid, every runtime built on the same spec parses it too — no lenient validator quietly accepting a trailing comma that breaks in prod.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The UUID generator uses &lt;code&gt;crypto.getRandomValues&lt;/code&gt;, not &lt;code&gt;Math.random&lt;/code&gt;.&lt;/strong&gt; That distinction matters the moment a UUID becomes a token instead of a row id — predictable randomness is a real breach class.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image conversion strips EXIF/GPS as a side effect&lt;/strong&gt; of redrawing on a canvas. Handy when you're about to post a phone photo somewhere public.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of that needs a backend. It's all sitting in the browser already; most "online tools" just don't use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest trade-offs
&lt;/h2&gt;

&lt;p&gt;Client-side isn't free of limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Big images hit browser memory/canvas caps (a 50 MP photo is ~200 MB decoded before you even encode).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The honest trade-offs
&lt;/h2&gt;

&lt;p&gt;Client-side isn't free of limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Big images hit browser memory/canvas caps (a 50 MP photo is ~200 MB decoded before you even encode).&lt;/li&gt;
&lt;li&gt;There's no server to do heavy lifting like OCR or real video transcoding.&lt;/li&gt;
&lt;li&gt;You're trusting the page's JavaScript — but that's inspectable, and "no network request" is verifiable in a way "we delete your file after an hour" never is.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Free and multilingual
&lt;/h2&gt;

&lt;p&gt;79 tools, 7 languages (EN/DE/ES/FR/IT/NL/UK), no sign-up. Every calculator's maths is cross-checked against an independent implementation before it ships, because a calculator that's confidently wrong is worse than none.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://smart-tools.xyz" rel="noopener noreferrer"&gt;smart-tools.xyz&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'd genuinely like feedback — especially: &lt;strong&gt;which tool would you want next?&lt;/strong&gt; I pick what to build from real demand, so tell me what you keep googling.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>privacy</category>
      <category>website</category>
    </item>
  </channel>
</rss>
