Skip to main content
Glama
rafafau
by rafafau

Fitaily MCP

CI License: Apache-2.0 Node

Remote Model Context Protocol server that exposes the Fitaily nutrition, training and progress API as 66 tools over Streamable HTTP.

Companion server. Fitaily MCP has no database and no business logic of its own: every tool calls the Fitaily REST API with the user's OAuth token. You need a running Fitaily backend, the same JWT_SECRET, and an OAuth client (ChatGPT, Claude, Cursor, OpenCode…). It is not a standalone fitness tracker.


What it is

  • Stateless. One McpServer + transport per request; no sessions, no cache, no storage.

  • OAuth 2.1 (PKCE). The Fitaily backend is the authorization server; this process only verifies HS256 access tokens (audience, scopes, expiry).

  • Scoped access. Every tool checks a *:read or *:write scope before calling the backend; writes are annotated as write/destructive so clients can ask first.

  • Thin. Backend failures are classified as unauthorized (401/403) or unreachable/error (502) and returned as short tool errors; response bodies are never forwarded to the model.

  • Structured results. Every tool declares an outputSchema and returns the same JSON in structuredContent and in content[0].text, so typed clients can parse results while text-only clients keep working.

Related MCP server: OpenGym Coach

What it is not

  • Not a medical device and not medical advice. It reports what the user logged.

  • Not a data store: nothing is persisted, cached or written to disk.

  • Not a replacement for the Fitaily REST API — the API remains the source of truth.

Requirements

Requirement

Notes

Node.js 26+

matches the Docker image and CI

pnpm 10+

corepack enable is enough

Fitaily backend

must expose /api/oauth/* and accept the shared JWT_SECRET

OAuth client

any MCP client that supports remote HTTP + OAuth discovery

Quickstart

git clone https://github.com/rafafau/fitaily-mcp.git
cd fitaily-mcp
corepack enable
pnpm install
cp .env.example .env      # set JWT_SECRET, FITAILY_API_URL and MCP_RESOURCE_URL
pnpm dev
curl -s http://localhost:3091/health
# {"success":true,"service":"fitaily","version":"0.2.0","timestamp":"…"}

To exercise /mcp locally you need a token minted for this resource. Load the same .env in your shell and create a short-lived development token:

set -a; . ./.env; set +a
TOKEN=$(node -e "console.log(require('jsonwebtoken').sign(
  { userId: 'dev', scope: 'nutrition:read training:read progress:read', aud: process.env.MCP_RESOURCE_URL },
  process.env.JWT_SECRET,
  { expiresIn: '15m', algorithm: 'HS256' }
))")

curl -s http://localhost:3091/mcp \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{
        "protocolVersion":"2025-06-18","capabilities":{},
        "clientInfo":{"name":"curl","version":"0"}}}'

Connect a client

The public endpoint is https://mcp.fitaily.com/mcp; locally it is http://localhost:3091/mcp. Authentication is discovered automatically through /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server.

Claude Code

claude mcp add --transport http fitaily https://mcp.fitaily.com/mcp

Cursor / VS Code / generic JSON config

{
  "mcpServers": {
    "fitaily": { "type": "http", "url": "https://mcp.fitaily.com/mcp" }
  }
}

OpenCode

{
  "mcp": {
    "fitaily": { "type": "remote", "url": "https://mcp.fitaily.com/mcp", "enabled": true }
  }
}

Claude Desktop (no native remote transport — bridge it):

{
  "mcpServers": {
    "fitaily": { "command": "npx", "args": ["-y", "mcp-remote", "https://mcp.fitaily.com/mcp"] }
  }
}

ChatGPT — Settings → Connectors → add a custom connector with the endpoint URL, then complete the OAuth prompt.

During OAuth the client registers itself dynamically, opens the Fitaily consent screen and receives an access token whose aud must equal MCP_RESOURCE_URL. A mismatch is rejected with 403 Token audience mismatch.

Configuration

Variable

Required

Default

Purpose

JWT_SECRET

✅

—

Verifies HS256 access tokens. Must equal the backend value. /mcp returns 500 without it.

MCP_RESOURCE_URL

✅ in production

https://mcp.fitaily.com/mcp

Public endpoint URL and expected token audience. No trailing slash.

FITAILY_API_URL

✅

http://localhost:3001/api

Base URL of the REST API, including /api.

OAUTH_ISSUER

✅

https://fitaily.com

Authorization server issuer, used in discovery documents and the optional iss check.

PORT

3091

HTTP port.

CORS_ORIGIN

*

* or a comma-separated origin allowlist.

TRUST_PROXY

1

Reverse-proxy hops for client IP + req.protocol (one proxy/tunnel hop by default). Set false when directly exposed.

MCP_BODY_LIMIT

1mb

Max JSON body for POST /mcp.

RATE_LIMIT_WINDOW_MS

60000

Rate-limit window per IP.

RATE_LIMIT_MAX

300

Requests per window per IP on /mcp.

ACCESS_LOG

true

false disables the method/path/status access log.

Authentication and scopes

Tokens are HS256 JWTs with userId, client_id, scope, aud, jti and a 15-minute TTL. The server pins HS256, checks aud, and verifies iss when the claim is present.

Scope

Unlocks

nutrition:read

meals, foods, water, recipes, diet plans, pantry, profile

nutrition:write

log/edit/delete meals and water, change goals, recipe CRUD, diet-plan generate/activate/extend, fridge and shopping list

training:read

activities, plans, workout templates, exercise catalog, strength stats

training:write

log_activity, workout template create/update/delete

progress:read

measurements, goals, aggregates, trends, forecasts

progress:write

add/update measurements and goals

A 401 response carries WWW-Authenticate: Bearer resource_metadata="…" so clients can find the authorization server. Missing scopes return 403 Missing scope: <scope>.

Write confirmation is a client responsibility. The server marks mutating tools with readOnlyHint: false and deletions with destructiveHint: true, but it cannot block a call the model already decided to make. The bundled skills encode the “show a summary → wait for yes → call the tool” workflow.

Tools

Full reference with scopes and read/write/destructive labels: docs/tools.md.

Group

Tools

Highlights

Nutrition

8

today's totals, meal history, goals, meal suggestions, meal CRUD

Foods

2

catalog search, barcode lookup

Water

3

history, log, delete

Recipes

13

public catalog, tags, favorites, ratings, CRUD, “fit remaining macros”

Diet plans

5

list, detail, generate, activate, extend

Training

8

history, plans, cardio logging, workout template CRUD

Exercise catalog

2

search, detail

Strength stats

3

personal records, exercise history, muscle activation

Progress

6

summary, measurements, goals

Stats

7

nutrition/activity/measurement aggregates, history, trend, forecast

Profile

3

profile, coach plan, dashboard summary

Fridge & shopping

6

fridge items, shopping list

Architecture

client ──OAuth token──▶ POST /mcp
                         ├─ rate limit (per IP)
                         ├─ verify JWT (HS256, aud, scopes)
                         ├─ parse JSON (max 1 MB)
                         ├─ new McpServer + tools bound to the token
                         └─ fetch FITAILY_API_URL/... (10–15 s timeout)
  • One server/transport per request: safe to run multiple replicas, no sticky sessions.

  • No database, no queue, no disk writes; rate-limit counters live in process memory.

  • The backend body is never returned to the model — only a stable status and a generic message, so an internal error cannot leak data or HTML.

  • Backend quirks worth knowing before changing tools: docs/backend-contract.md.

Development

pnpm dev             # tsx dev server on :3091 (loads .env when present)
pnpm test            # vitest (unit + HTTP integration)
pnpm typecheck       # src + tests
pnpm lint            # eslint 10 + typescript-eslint
pnpm format          # prettier
pnpm build           # tsc → dist/
pnpm validate:skills # SKILL.md frontmatter + tool names
src/
  api/         fetch wrapper for the Fitaily API
  auth/        JWT verification + scope checks
  schemas/     zod input schemas shared by all tools
  tools/       one file per domain (66 tools)
  config.ts    env parsing
  server.ts    express app, OAuth discovery, /mcp
tests/         vitest + supertest
docs/          tool reference and backend contract
skills/        agent skills that teach clients how to use the tools

Adding or renaming a tool

  1. Add the zod schema in src/schemas/common.ts.

  2. Register it in src/tools/<domain>.ts with a description that says when to call it, annotations, and the matching requireScope.

  3. Add the row to docs/tools.md (the docs test fails otherwise).

  4. Add or update a test in tests/tools.test.ts and, if it is a new workflow, a skill.

  5. Run pnpm lint && pnpm typecheck && pnpm test.

Self-hosting

docker build -t fitaily-mcp .
docker run --rm -p 3091:3091 --env-file .env fitaily-mcp

docker-compose.yml next to the backend:

services:
  mcp:
    build: .
    env_file: .env
    restart: unless-stopped
    ports:
      - "3091:3091"
    # Same private network as the backend; FITAILY_API_URL can then use
    # http://backend:3001/api instead of a public hostname.
    networks:
      - fitaily

networks:
  fitaily:
    external: true

Put a TLS-terminating reverse proxy or tunnel in front of it and keep TRUST_PROXY=1; the container runs as the unprivileged node user and exposes a HEALTHCHECK against /health.

Privacy and data handling

  • The server stores nothing: no database, no cache, no files, no analytics.

  • Access logs contain method, path, status and duration only — never tokens or bodies.

  • Health data (meals, weight, workouts) passes through in memory for the duration of a request. Fitaily's own privacy policy governs the backend data.

  • Rate-limit counters are in memory and disappear on restart.

Security

Please report vulnerabilities privately — see SECURITY.md. Do not open a public issue for a suspected token leak or auth bypass.

Operational notes:

  • JWT_SECRET is shared with the backend. Rotating it invalidates every outstanding token.

  • Tokens are bearer credentials: never log them, never put them in a URL.

  • Keep MCP_RESOURCE_URL exact; a trailing slash or wrong scheme breaks the audience check.

  • The default rate limit is intentionally generous (300 req/min/IP). Tighten it for public deployments if your clients make many parallel tool calls.

Design decisions

  • Generic outputSchema. Every tool advertises outputSchema: { type: "object" } rather than 66 per-tool field schemas. Tools return trimmed, purpose-built projections, and the real consumer benefit is structuredContent mirroring content[0].text. A per-tool schema would duplicate projections and break when the backend adds fields.

  • No streaming progress notifications. Every call is a single backend round-trip (typically 100–800 ms). If a future tool (e.g. AI diet plan generation) needs minutes, progress notifications can be added then without changing today's clients.

  • ESM modules. The package is "type": "module" and compiled with module: NodeNext. This matches the modern Node/MCP ecosystem; the Docker image builds the TypeScript inside the container and ships only dist/.

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md for the local loop and the tool checklist.

License

Apache License 2.0 © 2026 Rafal Sipiatowski. See NOTICE for the attribution notice. The license includes an express patent grant; modified files must carry prominent change notices when redistributed.

Fitaily is a trademark of its owner. This repository is an independent integration; the Fitaily backend and mobile apps are not part of it.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Self-hosted fitness tracking MCP server that gives AI assistants access to your nutrition, training, weight, sleep, and accomplishment data via 77 tools and 5 resources. Enables natural-language logging and querying of personal health metrics through Claude or ChatGPT.
    72 npm
    1
    BSD 2-Clause "Simplified"
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables ChatGPT to manage fitness routines, guided sessions, workout history, body weight tracking, and statistics through a remote authenticated MCP server with read and write tools.
    AGPL 3.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables logging workouts, sets, routines, food, water, and macros through natural-language chat in any MCP client, with OAuth-based secure access and timezone-aware daily tracking.
    ISC
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP-compatible clients to search foods and manage a personal nutrition diary, saved meals, exercise, weight, favorites, recipes, and monthly summaries via the FatSecret Platform API. It provides 39 tools with confirmation for mutating actions, OAuth 1.0 3-legged authentication, and MCP Streamable HTTP transport.
    MIT