Isaac Limb ← Portfolio atonom.ai ↗
Project · Professional

Atonom

Web Developer at a 30-person AI-agent startup. I own the infrastructure and deployment for three products, I build the internal tools our marketing team runs on, and I built the MCP server that puts our CRM inside Claude.

Visit atonom.ai ↗
Role
Web Developer
Stack
TypeScript · Next.js · Postgres · Supabase · Netlify · Render · Cloudflare
Status
Current, since May 2026
Links
3products owned end to end
9internal tools built
MCPClaude connected to our CRM
Prodeverything live

Claude, connected to our CRM

The thing I am proudest of here is a remote MCP server that lets a sales rep ask Claude about our CRM in plain language and get a real answer from live data. Reps add it to their own Claude account, which means inference runs on their subscription and costs the company nothing.

The interesting part is not the tools. It is that a connector like this is a bearer of someone's CRM access, so it had to be built like one.

Claude OAuth 2.1 · PKCE · consent authorization re-checked every request CRM, read-only
Self-hosted authorization server. Revoke a rep in the CRM and the next tool call fails.
Why metadata-only is enforced in code, not in the prompt. A model that can write SQL against the CRM could read an email body if the schema allowed it. So the schema does not allow it: the comms tables are blocked outright, and a separate rule rejects wildcard selects. Telling the model not to look is not a control.

Infrastructure and deployment

I own the code, the pull requests, and the deployment for three products, across Netlify, Render, Supabase, and Cloudflare. Nobody reviews my work, which is the reason a lot of what follows exists.

Internal tools

Most of what I build is internal tooling for our marketing team. I sit on that team as its engineer rather than as a marketer, and over time these have grown into one internal workspace behind login, with around nine tools in it.

A retried job must never double-execute. Queue jobs get idempotency keys, bursts for the same contact collapse into one, and anything that deletes remote data snapshots it locally first and refuses to delete if the archive is unconfirmed. The slow nightly sync is split from the fast drain for one reason: a three-hour job would be retried mid-run by the queue, and then it would run twice.

What I would do differently

In May I moved a hardcoded analytics ID into an environment variable. Good change, correct code, and it silently stopped all data collection for thirty-one days, because an empty string is falsy and the gate returned quietly. Nothing threw. The site looked perfect.

I did not fix it by setting the variable. I moved the conversions that matter to the server, where no client-side toggle can kill them, and added a capture-gap metric that goes red when the client and server counts diverge. You cannot alert on an absence, but you can alert on a ratio.

Atonom's code is private, so this page describes architecture and decisions rather than showing source.
← All projects Get in touch →