Skip to main content
Glama
README.md
# FitnessMCP

FitnessMCP is a production-ready remote [Model Context Protocol](https://modelcontextprotocol.io/) server that lets AI assistants work with fitness data from multiple services through one secure endpoint.

One server. One `/mcp` URL. Multiple fitness integrations.

## What It Connects

FitnessMCP currently includes tool groups for:

- **Hevy**: workouts, routines, exercise templates, routine folders, and workout events
- **Strava**: athlete profile, recent activities, and segment starring
- **Cronometer**: diary entries, daily nutrition, food search, food details, custom foods, macro targets, fasting history, and day completion
- **Intervals.icu**: athlete profile, activities, wellness, events, gear, reminders, and sport settings

It also includes high-level utility tools:

- `fitness_get_connected_services`
- `fitness_get_integration_plan`

## Why Use FitnessMCP

- **One MCP connection** instead of separate servers for each fitness app
- **Cloudflare Worker deployment** for a low-maintenance hosted endpoint
- **OAuth-ready MCP transport** for remote MCP clients
- **Encrypted credential storage** for user-supplied keys
- **No checked-in secrets**: all real credentials are supplied through Worker secrets or local-only files
- **Typed TypeScript codebase** with validation, tests, and CI

## Important Security Note

This repository intentionally does **not** include real API keys, tokens, passwords, KV namespace IDs, or personal credentials.

You will see placeholders such as:

```txt
replace_with_hevy_api_key
REPLACE_WITH_YOUR_PRODUCTION_KV_NAMESPACE_ID
```

That is expected. Replace those only in your own local `.dev.vars` file or in Cloudflare Worker secrets. Never commit real credentials.

## How The Pieces Fit Together

```mermaid
flowchart LR
	A["AI assistant"] --> B["FitnessMCP /mcp endpoint"]
	B --> C["Hevy client"]
	B --> D["Strava client"]
	B --> E["Cronometer client"]
	B --> F["Intervals.icu client"]
	B --> G["Cloudflare KV encrypted storage"]
```

## Beginner-Friendly Setup

This section assumes you are comfortable copying and pasting commands, but you do not need to be a professional developer.

### Step 1: Install The Required Apps

Install these first:

1. **Node.js** from [nodejs.org](https://nodejs.org/)
2. **Git** from [git-scm.com](https://git-scm.com/)
3. **A Cloudflare account** from [cloudflare.com](https://www.cloudflare.com/)
4. **A GitHub account** from [github.com](https://github.com/)

To check that Node.js and Git are installed, open Terminal and run:

```bash
node --version
git --version
```

If both commands print version numbers, you are ready.

### Step 2: Download The Project

Clone the repository:

```bash
git clone https://github.com/senojjones/FitnessMCP.git
cd FitnessMCP
```

Install the project dependencies:

```bash
npm install
```

### Step 3: Log In To Cloudflare From Terminal

Run:

```bash
npx wrangler login
```

Your browser will open. Sign in to Cloudflare and approve Wrangler.

### Step 4: Create Cloudflare KV Storage

FitnessMCP uses Cloudflare KV to store OAuth sessions and encrypted user credentials.

Run:

```bash
npx wrangler kv namespace create OAUTH_KV
```

Cloudflare will print something like:

```txt
{ binding = "OAUTH_KV", id = "abc123..." }
```

Copy the `id` value.

Open `wrangler.jsonc` and replace:

```txt
REPLACE_WITH_YOUR_PRODUCTION_KV_NAMESPACE_ID
```

with the KV namespace ID Cloudflare gave you.

For local development, you can use the same ID for:

```txt
REPLACE_WITH_YOUR_DEV_KV_NAMESPACE_ID
```

or create a second namespace:

```bash
npx wrangler kv namespace create OAUTH_KV --env dev
```

### Step 5: Create A GitHub OAuth App

FitnessMCP uses GitHub sign-in to identify users.

1. Go to [GitHub Developer Settings](https://github.com/settings/developers)
2. Click **New OAuth App**
3. Use this for local development:

```txt
Application name: FitnessMCP Local
Homepage URL: http://localhost:8787
Authorization callback URL: http://localhost:8787/callback
```

4. Click **Register application**
5. Copy the **Client ID**
6. Click **Generate a new client secret**
7. Copy the **Client Secret**

Keep these private.

### Step 6: Create Your Local Secret File

Copy the example file:

```bash
cp .dev.vars.example .dev.vars
```

Open `.dev.vars` in a text editor.

Replace:

```txt
GITHUB_CLIENT_ID=replace_with_your_github_oauth_client_id
GITHUB_CLIENT_SECRET=replace_with_your_github_oauth_client_secret
```

with your GitHub OAuth values.

Generate an encryption key:

```bash
openssl rand -hex 32
```

Copy the output and put it into:

```txt
COOKIE_ENCRYPTION_KEY=replace_with_64_character_hex_string
```

### Step 7: Add Fitness Service Credentials

You do not need every service. Configure only the apps you use.

#### Hevy

Get your API key from Hevy developer settings, then set:

```txt
HEVY_API_KEY=replace_with_hevy_api_key
```

#### Strava

Create a Strava app at [strava.com/settings/api](https://www.strava.com/settings/api), then set:

```txt
STRAVA_CLIENT_ID=replace_with_strava_client_id
STRAVA_CLIENT_SECRET=replace_with_strava_client_secret
STRAVA_ACCESS_TOKEN=replace_with_strava_access_token
STRAVA_REFRESH_TOKEN=replace_with_strava_refresh_token
```

#### Cronometer

Set:

```txt
CRONOMETER_USERNAME=replace_with_cronometer_email
CRONOMETER_PASSWORD=replace_with_cronometer_password
```

Cronometer access can be sensitive because it may involve account credentials. Use a strong unique password and keep `.dev.vars` private.

#### Intervals.icu

Create an API key in Intervals.icu, then set:

```txt
INTERVALS_ICU_API_KEY=replace_with_intervals_icu_api_key
INTERVALS_ICU_ATHLETE_ID=replace_with_intervals_icu_athlete_id
```

### Step 8: Run FitnessMCP Locally

Start the server:

```bash
npm run dev
```

Open:

```txt
http://localhost:8787
```

Check health:

```txt
http://localhost:8787/health
```

The MCP endpoint is:

```txt
http://localhost:8787/mcp
```

### Step 9: Deploy To Cloudflare

Before deploying, add secrets to Cloudflare. Run these one at a time:

```bash
npx wrangler secret put GITHUB_CLIENT_ID
npx wrangler secret put GITHUB_CLIENT_SECRET
npx wrangler secret put COOKIE_ENCRYPTION_KEY
```

Then add whichever fitness services you use:

```bash
npx wrangler secret put HEVY_API_KEY
npx wrangler secret put STRAVA_CLIENT_ID
npx wrangler secret put STRAVA_CLIENT_SECRET
npx wrangler secret put STRAVA_ACCESS_TOKEN
npx wrangler secret put STRAVA_REFRESH_TOKEN
npx wrangler secret put CRONOMETER_USERNAME
npx wrangler secret put CRONOMETER_PASSWORD
npx wrangler secret put INTERVALS_ICU_API_KEY
npx wrangler secret put INTERVALS_ICU_ATHLETE_ID
```

Deploy:

```bash
npm run deploy
```

Cloudflare will print your Worker URL. It will look similar to:

```txt
https://fitnessmcp.YOUR_SUBDOMAIN.workers.dev
```

Your production MCP endpoint is:

```txt
https://fitnessmcp.YOUR_SUBDOMAIN.workers.dev/mcp
```

### Step 10: Connect An MCP Client

For an MCP client that supports remote MCP through `mcp-remote`, use:

```json
{
  "mcpServers": {
    "fitnessmcp": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://fitnessmcp.YOUR_SUBDOMAIN.workers.dev/mcp"
      ]
    }
  }
}
```

Restart your MCP client after saving the config.

## Tool Naming

Tools are namespaced by service:

```txt
fitness_get_connected_services
strava_get_athlete
strava_get_recent_activities
cronometer_get_daily_nutrition
intervals_get_wellness
get_workouts
get_routines
```

The Hevy tools keep their original concise names for compatibility.

## Local Development Commands

```bash
npm run dev
npm run type-check
npm run test:run
npm run lint
npm run format
npm run check
```

## Project Structure

```txt
FitnessMCP/
  src/
    app.ts                    # Hono app and route mounting
    mcp-agent.ts              # MCP server and tool registration
    routes/                   # MCP and utility routes
    middleware/               # bearer auth middleware
    lib/
      client.ts               # Hevy API client
      strava-client.ts        # Strava API client
      cronometer-client.ts    # Cronometer API client
      intervals-client.ts     # Intervals.icu API client
      key-storage.ts          # encrypted KV credential helpers
      service-registry.ts     # connected service status
      schemas.ts              # Zod schemas
      transforms.ts           # validation and API transforms
  test/                       # unit and integration tests
  wrangler.jsonc              # Cloudflare Worker config
  .dev.vars.example           # local secret template
```

## Production Checklist

Before sharing your Worker URL:

- `wrangler.jsonc` has your own KV namespace IDs
- `.dev.vars` is not committed
- All Cloudflare secrets are set with `wrangler secret put`
- `npm run check` passes
- `/health` returns `status: healthy`
- Your MCP client can call `fitness_get_connected_services`

## Security

Read [SECURITY.md](SECURITY.md) before deploying.

## Contributing

Issues and pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTING.md) first.

## License

MIT