Skip to main content
Glama
brian7989

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
```