Chakudya MCP Server

Chakudya MCP Server

Exposes the Chakudya Nutrition Registry API as MCP tools, enabling clients to search Malawian food data, perform clinical nutrition lookups, and query a RAG knowledge base.

Category
Visit Server

README

Chakudya MCP Server

An MCP (Model Context Protocol) server that exposes the Chakudya Nutrition Registry (CNR) API — https://chakudya-api.edisontaimu9.workers.dev — as a set of MCP tools, so any MCP-compatible client (Claude, Claude Code, other LLM agents) can search Malawian food data, run clinical nutrition lookups, and query the RAG knowledge base directly.

This is a new, separate layer. It does not replace or modify the Chakudya Worker. It's a small Node/TypeScript HTTP service that sits in front of your existing API and translates MCP tool calls into plain HTTP requests against the routes your Worker already serves.

MCP Client (Claude, etc.)
        │  Streamable HTTP (JSON-RPC over HTTP + SSE)
        ▼
Chakudya MCP Server  (this project)
        │  plain HTTPS fetch()
        ▼
Chakudya Worker API  (unchanged) → Supabase / Cohere / Groq / USDA / OFF / FatSecret

Why a separate server, not a Worker

The official MCP TypeScript SDK's StreamableHTTPServerTransport is built for Node's http.IncomingMessage/ServerResponse. Cloudflare Workers use the Fetch API instead, and the SDK's web-standard variant (WebStandardStreamableHTTPServerTransport) is newer and less battle-tested for production session management. Running this as a plain Node service (Docker, Render, Fly.io, a VPS, etc.) is the more standard, better-documented path today, and it keeps this concern fully decoupled from your Worker's deploy cycle. Nothing stops you from porting it to the web-standard transport on Workers later if you want a single-platform deploy — the tool logic in src/tools/* doesn't care which transport wraps it.

Tools

All 15 tools call your existing Chakudya Worker over HTTPS — none of them touch Supabase, Cohere, or Groq directly, and none of them need ADMIN_API_KEY (every route they use is public).

Tool Chakudya route(s) used
search_food GET /foods → falls back to GET /foods/lookup
get_food_details GET /foods/:id
calculate_nutrients GET /foods or /foods/:id, then scales per-100g values in-process
analyze_meal same as above, looped and summed across multiple items
barcode_lookup GET /packaged?barcode= → falls back to GET /foods/lookup?barcode=
packaged_food_search GET /packaged and/or GET /products
diabetes_exchange_lookup GET /exchange
renal_exchange_lookup GET /renal
enteral_formula_lookup GET /formulas
nutrition_calculator none — pure BMI/BMR (Mifflin-St Jeor)/TDEE math
rag_retrieve POST /rag/retrieve
search_guidelines POST /rag/ask (context: "clinical")
retrieve_evidence POST /rag/ask (context: "both", higher top_k)
disease_information POST /rag/ask, query framed for educational disease overview
medicine_information POST /rag/ask, query explicitly instructed to exclude dosing/prescribing

disease_information and medicine_information always return an educational disclaimer alongside the answer and are prompted to avoid diagnosis/prescribing language — but they're still LLM-generated text grounded on whatever's in your RAG knowledge base, not a verified medical reference. Treat them as a starting point for a learner, same as the rest of the RAG-backed tools.

Project layout

src/
├── index.ts                 Express app, Streamable HTTP session wiring, graceful shutdown
├── config/env.ts            Zod-validated environment config, loaded once at startup
├── clients/chakudyaClient.ts  Fetch wrapper for the Chakudya Worker (GET/POST, error normalization)
├── server/
│   ├── createServer.ts      Builds one McpServer instance and registers all tool modules
│   └── security.ts          Bearer auth + per-IP rate limiting for this server's /mcp endpoint
├── tools/
│   ├── foodTools.ts
│   ├── clinicalTools.ts
│   ├── ragTools.ts
│   └── educationTools.ts
└── utils/
    ├── logger.ts             Structured JSON logging
    └── toolResult.ts         Consistent success/error shaping for every tool handler

Environment variables

Copy .env.example to .env and fill in:

Variable Required Notes
CHAKUDYA_API_BASE_URL no (defaults to your Worker) Already set to https://chakudya-api.edisontaimu9.workers.dev
CHAKUDYA_ADMIN_API_KEY no Not used by any current tool; only needed if you add an admin-gated tool later
PORT no (default 8787)
MCP_AUTH_TOKEN yes in production Bearer token MCP clients must send. Server refuses to start in production without it
MCP_ALLOWED_ORIGINS no Comma-separated CORS origins; leave blank to disable browser access
MCP_RATE_LIMIT_PER_MIN no (default 60) Per-IP cap on this server's own /mcp endpoint
NODE_ENV no (default development) Set to production for deploys

Security considerations

  • Auth is mandatory in production. env.ts exits the process at startup if NODE_ENV=production and MCP_AUTH_TOKEN is unset — this is a deliberate fail-closed check, not just a warning.
  • This server sits in front of your rate-limited RAG routes. /rag/ask on your Worker is capped at 15 req/min per IP — but that's per client IP as seen by the Worker, which would be this server's IP once deployed, shared across everyone using it. The MCP-level rate limiter (MCP_RATE_LIMIT_PER_MIN) exists so one misbehaving MCP client can't silently exhaust that budget for everyone else. Tune it down if you expect multiple concurrent MCP clients.
  • No admin key is embedded or required. Every tool calls a public CNR route. If you add an admin-gated tool later, keep CHAKUDYA_ADMIN_API_KEY server-side only — never expose it to the MCP client.
  • Session state is in-memory, per-process. Fine for a single instance. If you ever scale to multiple instances behind a load balancer, either enable sticky sessions (route by Mcp-Session-Id) or swap the transports map in src/index.ts for a shared store.
  • CORS is off by default. Only enable MCP_ALLOWED_ORIGINS if you have a specific browser-based MCP client; server-to-server MCP clients (Claude Desktop, Claude Code, etc.) don't need it.

Running locally (Termux)

cd ~
git clone https://github.com/edisontaimu9-ui/chakudya-mcp-server.git
cd chakudya-mcp-server
cp .env.example .env
# edit .env: set MCP_AUTH_TOKEN to a long random string
npm install
npm run build
npm start

Or for iterative dev with auto-reload:

npm run dev

Health check: curl http://localhost:8787/health

Connecting an MCP client

Point any Streamable-HTTP-capable MCP client at:

POST/GET/DELETE  https://<your-deployed-host>/mcp
Header: Authorization: Bearer <MCP_AUTH_TOKEN>

For Claude Desktop / Claude Code, add it as a remote MCP server pointing at that URL with the same bearer token. Consult Anthropic's current docs for the exact config file syntax, since that's changed over time — check https://docs.claude.com for the latest mcpServers remote-server format.

Deployment: Render (recommended — free, no credit card)

This repo includes render.yaml, so Render's Blueprint feature deploys it without any manual dashboard configuration.

  1. Push this repo to GitHub (commands below).
  2. In the Render dashboard: New → Blueprint, connect your GitHub account, pick the chakudya-mcp-server repo. Render reads render.yaml automatically.
  3. Render provisions the service on the Free plan and auto-generates a random MCP_AUTH_TOKEN (via generateValue: true). After the first deploy, go to the service's Environment tab to copy that generated token — you'll need it in your MCP client config.
  4. Deploy. Your MCP endpoint will be https://chakudya-mcp-server.onrender.com/mcp (Render may append a random suffix if that name's taken — check the dashboard for your actual URL).

The free-tier sleep problem, and the fix

Render's free web services spin down after 15 minutes with no traffic, then take 30-60 seconds to wake on the next request. That's fine for a health check, but it can drop an in-progress MCP session (session state lives in memory — see src/index.ts) if the client goes quiet mid-conversation for too long.

Fix: keep it warm with a free uptime monitor pinging /health every 5-10 minutes.

  1. Sign up at uptimerobot.com (free plan, no card).
  2. Add a new HTTP(s) monitor:
    • URL: https://<your-service>.onrender.com/health
    • Interval: 5 minutes
  3. Save. /health is unauthenticated by design, specifically so this monitor doesn't need your MCP_AUTH_TOKEN.

This keeps the service warm 24/7 within the free plan's 750 hrs/month (well under the cap for one service pinged this way).

Updating after a code change

Render auto-redeploys on every push to your connected branch — no extra step needed:

git add .
git commit -m "Update MCP server"
git push

Watch the deploy in the Render dashboard's Events tab; it typically finishes in 1-2 minutes for a project this size.

Other deployment options

Docker anywhere

docker build -t chakudya-mcp-server .
docker run -d -p 8787:8787 \
  -e NODE_ENV=production \
  -e MCP_AUTH_TOKEN=<long-random-string> \
  -e CHAKUDYA_API_BASE_URL=https://chakudya-api.edisontaimu9.workers.dev \
  --name chakudya-mcp chakudya-mcp-server

Option C — Plain VPS with a process manager

npm install --omit=dev
npm run build
npx pm2 start dist/index.js --name chakudya-mcp

Put it behind Nginx/Caddy for TLS termination if you're not already fronting it with something that handles HTTPS.

Termux deployment (git workflow)

cd ~
# first time only:
git clone https://github.com/edisontaimu9-ui/chakudya-mcp-server.git
cd chakudya-mcp-server

# after any file update:
cp /storage/emulated/0/Download/<updated-file>.ts src/<path>/<updated-file>.ts
git add .
git commit -m "Update MCP server"
git push

Then redeploy on whichever platform you chose (Render/Railway/Fly auto-redeploy on push if you connected the GitHub repo; otherwise trigger a manual redeploy or re-run the Docker/pm2 commands above on your host).

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured