Health Coach MCP
by brian7989
README.md
# Health Coach MCP
A personal health and fitness MCP server backed by Supabase Postgres. It exposes tools for profile data, goals, body measurements, nutrition, workouts, sleep, check-ins, contextual life events, analytics, and coach memories over MCP Streamable HTTP.
The server is built for private, user-scoped use: every tool call resolves the authenticated user from the request token, not from tool input.
## Architecture
```mermaid
flowchart LR
claude["Claude / MCP client"] -->|Streamable HTTP| hono["Hono app"]
subgraph app["Health Coach MCP server"]
hono --> auth["Auth middleware"]
hono --> mcp["MCP transport"]
hono --> oauth["OAuth consent UI"]
mcp --> tools["MCP tools"]
tools --> services["Domain services"]
services --> repos["Repositories"]
end
auth -->|JWKS validation| supabaseAuth["Supabase Auth"]
oauth -->|OAuth 2.1 / PKCE| supabaseAuth
repos -->|Drizzle ORM| postgres["Supabase Postgres"]
services --> domains["Profiles, goals, measurements, nutrition, workouts, sleep, check-ins, events, analytics, memories"]
```
## Stack
- Node.js 24
- TypeScript
- Hono
- MCP TypeScript SDK
- Drizzle ORM
- Supabase Postgres
- Vitest
## Configuration
Copy the sample environment and fill in the values for your Supabase project:
```bash
cp .env.example .env
```
Required for production:
- `DATABASE_URL`: Supabase pooled Postgres URL. Use the transaction-mode pooler on Railway.
- `PUBLIC_BASE_URL`: public HTTPS origin for the deployed server, for example `https://health-coach-mcp-production.up.railway.app`.
- `SUPABASE_URL`: your Supabase project URL.
- `SUPABASE_PUBLISHABLE_KEY`: public browser key used by the OAuth consent page.
Optional:
- `SUPABASE_OAUTH_ISSUER`: explicit Supabase OAuth issuer. Defaults to `${SUPABASE_URL}/auth/v1`.
- `SUPABASE_JWKS_URL`: explicit JWKS URL. Defaults to `${SUPABASE_URL}/auth/v1/.well-known/jwks.json`.
- `PERSONAL_CONNECTOR_TOKEN` and `PERSONAL_CONNECTOR_USER_ID`: private single-user connector URL fallback for clients that cannot send bearer headers or complete OAuth.
- `DEV_AUTH_TOKEN` and `DEV_AUTH_USER_ID`: local development only.
Do not commit `.env`.
## Commands
```bash
pnpm install
pnpm format
pnpm lint
pnpm typecheck
pnpm test
pnpm test:integration
pnpm test:e2e
pnpm build
```
## Local Development
```bash
pnpm install
pnpm dev
```
The service exposes:
- `GET /health`
- `GET /ready`
- `POST /mcp`
`/mcp` expects `Authorization: Bearer <Supabase access token>`. For local development only, `DEV_AUTH_TOKEN` can be used instead.
## Database
Migrations live in `supabase/migrations`.
For local Supabase:
```bash
pnpm db:start
pnpm db:reset
```
For hosted Supabase, apply migrations with the Supabase CLI:
```bash
supabase db push --db-url "$DATABASE_URL"
```
## Claude Connector
For the real multi-user flow, enable Supabase Auth's OAuth 2.1 Server and Dynamic Client Registration in the Supabase dashboard. Then add the deployed MCP endpoint in Claude:
Use the deployed MCP endpoint:
```text
https://YOUR_PUBLIC_HOSTNAME/mcp
```
Leave OAuth client ID and secret blank when Dynamic Client Registration is enabled.
For private development only, if Claude cannot complete OAuth for your custom connector, use the connector-token fallback:
```text
https://YOUR_PUBLIC_HOSTNAME/mcp?connector_token=YOUR_RANDOM_CONNECTOR_TOKEN
```
Treat that URL as a secret. It maps every request to the configured `PERSONAL_CONNECTOR_USER_ID`.
## Deployment
Railway is the recommended first deployment target. Set production variables in Railway, deploy the Node service, then confirm:
```bash
curl https://YOUR_PUBLIC_HOSTNAME/health
curl https://YOUR_PUBLIC_HOSTNAME/ready
```
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues