DEV Community

Cover image for I turned my calculator site into an MCP server so AI assistants stop guessing at math
Rhein
Rhein

Posted on

I turned my calculator site into an MCP server so AI assistants stop guessing at math

Ask a language model to compute 2 + 3 * sqrt(16) and it will usually get it right. Ask it for 1,000,000 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.

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.

Here's how that went, including the parts that didn't work.

MCP in one paragraph

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.

The constraint that shaped everything

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 inside the Worker I already deploy.

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.

POST /mcp
{ "jsonrpc": "2.0", "id": 1, "method": "tools/call",
  "params": { "name": "scientific_calculator",
              "arguments": { "expression": "2 + 3 * sqrt(16)" } } }
Enter fullscreen mode Exit fullscreen mode
{ "jsonrpc": "2.0", "id": 1, "result": {
    "expression": "2 + 3 * sqrt(16)", "result": 14 } }
Enter fullscreen mode Exit fullscreen mode

Decision 1: every result links back to a real page

The single design choice I care about most: every tool response includes a source_url pointing at the human page for that tool.

{ "expression": "2 + 3 * sqrt(16)", "result": 14,
  "provider": "SmartTools",
  "source_url": "https://smart-tools.xyz/en/scientific-calculator" }
Enter fullscreen mode Exit fullscreen mode

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 locale: "de" and you get the German page.)

Decision 2: no eval, ever

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

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:

-2^2   // -4, not 4
Enter fullscreen mode Exit fullscreen mode

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

The parser is byte-for-byte identical between the website widget and the MCP tool, and a test asserts they never drift apart.

What I couldn't expose (the honest ceiling)

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.

I'd rather say that plainly than pretend the server does more than it does.

Try it

No key, no sign-up, nothing stored — it reads your input, computes, replies.

claude mcp add --transport http smart-tools https://smart-tools.xyz/mcp
Enter fullscreen mode Exit fullscreen mode

Or point any MCP client at the endpoint directly:

{ "mcpServers": {
    "smart-tools": { "type": "http", "url": "https://smart-tools.xyz/mcp" } } }
Enter fullscreen mode Exit fullscreen mode

There's a fuller write-up (and the tool list) at smart-tools.xyz/en/mcp.

What I'd still like to figure out

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 exactly 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.

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.

Top comments (0)