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

CLIENTSPhone / SMS+1 401 598 8402ChatGPTMCP connectorBrowseringredientchecker.appPROVIDERSTelnyx10DLC · webhooksCOMPUTERailway — Express APIapi.ingredientchecker.appPOST /webhooks/telnyxPOST /mcpPOST /askGET /healthlib/agent · lib/toolsVercel — Next.js667 static pages · ISR 24hdeveloper.* rewriteDATA / MODELSupabasePostgres · RLSOpenAIgpt-5.6-lunaNOTES• Both API surfaces answer from one shared tool layer (lib/tools.js) — SMS and ChatGPT cannot diverge.• Supabase is reached with the anon key only; RLS grants public read on 2 tables, everything else is service-role.• The web tier never calls the API — it reads Supabase directly at build time and via ISR.• Telnyx webhooks are Ed25519-signed; the API verifies before doing any model work.• Conversation memory is in-process and expires after 30 min — it does not survive a redeploy.

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

ComponentRuns onResponsibility
backend/RailwayExpress API: Telnyx webhook, MCP endpoint, the LLM tool loop
src/VercelNext.js site: 667 static pages, landing page, developer docs
PROTEIN_BARS_DBSupabasePostgres. Shared with Protein Bar Nerd — this project reads, it does not own
TelnyxSaaSPhone number, 10DLC registration, inbound/outbound SMS
OpenAISaaSgpt-5.6-luna, function calling for the agent loop

Repository layout

PathWhat lives there
backend/server.jsExpress app, routes, webhook handling
backend/lib/tools.jsTool definitions shared by both surfaces
backend/lib/ingredients.jsAll Supabase queries
backend/lib/agent.jsSystem prompt + bounded tool-calling loop
backend/lib/telnyx.jsSend SMS, verify webhook signatures
backend/lib/mcp.jsMCP server for ChatGPT
backend/lib/conversations.jsIn-memory SMS conversation history
src/app/Next routes incl. these docs under /developer
src/middleware.tsRewrites 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