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.
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.tsexits the process at startup ifNODE_ENV=productionandMCP_AUTH_TOKENis unset — this is a deliberate fail-closed check, not just a warning. - This server sits in front of your rate-limited RAG routes.
/rag/askon 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_KEYserver-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 thetransportsmap insrc/index.tsfor a shared store. - CORS is off by default. Only enable
MCP_ALLOWED_ORIGINSif 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.
- Push this repo to GitHub (commands below).
- In the Render dashboard: New → Blueprint, connect your GitHub account, pick the
chakudya-mcp-serverrepo. Render readsrender.yamlautomatically. - Render provisions the service on the Free plan and auto-generates a random
MCP_AUTH_TOKEN(viagenerateValue: 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. - 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.
- Sign up at uptimerobot.com (free plan, no card).
- Add a new HTTP(s) monitor:
- URL:
https://<your-service>.onrender.com/health - Interval: 5 minutes
- URL:
- Save.
/healthis unauthenticated by design, specifically so this monitor doesn't need yourMCP_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).
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。