Architecture
Ingredient Checker answers questions about what is in packaged food, from a database of 514 ingredient safety reports and 767 products. It is exposed through three surfaces — SMS, a ChatGPT app, and the web — backed by one Express API and one Postgres database.
System diagram
The one design decision that matters
SMS and ChatGPT both answer from the same three tools defined once in backend/lib/tools.js. The SMS agent converts them to OpenAI function-calling schemas; the MCP server registers them for ChatGPT. Same names, same arguments, same results.
The alternative — implementing lookups per surface — means a query fix lands in one place and silently not the other, and the two products drift apart. Anything that changes what the assistant can do belongs in that file.
Components
| Component | Runs on | Responsibility |
|---|---|---|
backend/ | Railway | Express API: Telnyx webhook, MCP endpoint, the LLM tool loop |
src/ | Vercel | Next.js site: 667 static pages, landing page, developer docs |
| PROTEIN_BARS_DB | Supabase | Postgres. Shared with Protein Bar Nerd — this project reads, it does not own |
| Telnyx | SaaS | Phone number, 10DLC registration, inbound/outbound SMS |
| OpenAI | SaaS | gpt-5.6-luna, function calling for the agent loop |
Repository layout
| Path | What lives there |
|---|---|
backend/server.js | Express app, routes, webhook handling |
backend/lib/tools.js | Tool definitions shared by both surfaces |
backend/lib/ingredients.js | All Supabase queries |
backend/lib/agent.js | System prompt + bounded tool-calling loop |
backend/lib/telnyx.js | Send SMS, verify webhook signatures |
backend/lib/mcp.js | MCP server for ChatGPT |
backend/lib/conversations.js | In-memory SMS conversation history |
src/app/ | Next routes incl. these docs under /developer |
src/middleware.ts | Rewrites developer.* onto /developer |
A caveat about the database
Shared, not owned
Supabase project PROTEIN_BARS_DB also backs proteinbarnerd.com. Schema changes affect both sites, and the ingredient pages were copied into this repo rather than shared as a package — so the same fix may need applying twice.
Separating the data is the stated intent behind this project, but it has not happened yet.
Start here
- SMS flow — end-to-end walkthrough of an inbound text
- ChatGPT app flow — MCP handshake and a tool call
- Requirements & dependencies — everything needed to run this
- Operations — deploys, env vars, and how to debug a failure