fitness-mcp
Provides read-only access to Hevy workout data, including recent workouts, detailed workout logs, and body measurements.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fitness-mcpWhat's my weight trend this month?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
fitness-mcp
A personal remote MCP (Model Context Protocol) server that lets Claude read and manage your Hevy workout data directly in conversation. Deployed on Vercel's free Hobby tier.
License
Related MCP server: garmin-connect-mcp-server
Status
Hevy: implemented — read: recent workouts, workout detail, body measurements, exercise template search, routine folder listing, routine listing, routine detail. Write: create/update routines (workout plan templates) and routine folders, so a training menu designed in conversation can be pushed directly into the Hevy app. Uses the official Hevy REST API directly.
Tools exposed
Tool | Type | Description |
| read | List recent Hevy workouts (title, time, exercises) |
| read | Full sets/reps/weight detail for one workout |
| read | Recent Hevy body measurement entries (weight, body fat %) |
| read | Search Hevy's exercise library by name to resolve the |
| read | List existing Hevy routines (id, title, folder, exercise count, updated time), optionally filtered by folder, to find a |
| read | Full exercise/set/rep(-range)/weight/rest-time detail for one routine (template) — read the current contents before |
| write | Create a new Hevy routine (workout plan template) |
| write | Replace an existing Hevy routine's title/notes/exercises entirely (folder assignment cannot be changed via update — see below) |
| read | List existing routine folders (id, title, index) to resolve a |
| write | Create a folder to organize routines |
The write tools make real changes to the user's Hevy account (creating/replacing routines and folders). Their tool descriptions instruct the calling LLM to show the user the full planned content and get explicit confirmation before calling, and they require a confirm: true argument as a structural nudge in the same direction — but since that argument is set by the same LLM deciding whether to call the tool at all, it is not a guarantee of human confirmation, only a deliberate extra step. There is no scope separation between read and write tools at the authentication layer (see Authentication below) — any authenticated caller can invoke any tool.
Authentication
Claude's "Request headers" option for custom connectors (a static Authorization: Bearer <token> header) is still in beta and not available on every account. So this server also implements a minimal OAuth 2.1 authorization server (Authorization Code + PKCE) at /api/oauth/authorize and /api/oauth/token, purely so Claude's standard OAuth Client ID/Secret fields work as an always-available fallback.
There's no login screen and no client database — /authorize auto-approves. That's safe because the real credential check happens at /token: a code can only be exchanged for an access token by presenting the correct OAUTH_CLIENT_SECRET, which never appears in a browser-visible URL (only client_id does, at /authorize). The access token it returns is just MCP_BEARER_TOKEN itself, so the resource-server check (lib/auth.ts) doesn't change depending on which path a client used to get it. See lib/oauth.ts for the full reasoning, including the accepted tradeoffs (no server-side session/code storage — codes are self-contained, HMAC-signed, and expire in 60s).
Generating the three secrets from one memorable passphrase
MCP_BEARER_TOKEN, OAUTH_CLIENT_ID, and OAUTH_CLIENT_SECRET can all be derived deterministically from a single master passphrase, so losing the stored values isn't a disaster — just re-derive them. Paste this into a fresh terminal (it isn't installed anywhere permanent on purpose — see below) and it'll prompt for the passphrase once per session instead of making you type it into every command:
derive() {
if [ -z "$MASTER_PASSPHRASE" ]; then
printf "Master passphrase: "
read -rs MASTER_PASSPHRASE
echo
fi
echo -n "$1" | openssl dgst -sha256 -hmac "$MASTER_PASSPHRASE" -hex | awk '{print $2}'
}
derive "fitness-mcp:bearer-token" # → MCP_BEARER_TOKEN
derive "fitness-mcp:oauth-client-id" # → OAUTH_CLIENT_ID
derive "fitness-mcp:oauth-client-secret" # → OAUTH_CLIENT_SECRETThe label strings aren't secret (they're safe to keep in this README) — only the passphrase is. Running derive again with the same passphrase always reproduces the same values.
This is deliberately left as a copy-paste snippet rather than something installed into ~/.bashrc: putting the function alone in a shell rc file is fine, but putting export MASTER_PASSPHRASE=... there too means the passphrase sits in plaintext on disk indefinitely — a tradeoff we're choosing not to make by default. If you don't mind that tradeoff on your own machine, adding both to ~/.bashrc works and skips the per-session prompt entirely.
Local development
npm install
cp .env.example .env.local # fill in real values
vercel devSmoke test (replace $MCP_BEARER_TOKEN):
curl -X POST http://localhost:3000/api/mcp \
-H "Authorization: Bearer $MCP_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Should return the 10 tools above. A request with a missing/wrong token should get 401.
Testing
Three layers, all run in CI (.github/workflows/ci.yml) on every push/PR — none require real Hevy secrets, so they work the same in a public repo:
npm run test # unit + integration (vitest) — pure logic, plus the real Next.js
# route handler exercised with fetch mocked
npm run build
npm run test:e2e # starts a real `next start` server and hits it over real HTTP
# (node's built-in test runner, no extra dependency)Unit (
lib/*.test.ts): bearer-token verification, OAuth code signing/PKCE/redirect-URI allowlisting (including the RFC 7636 PKCE test vector), Hevy routine request-body construction and validation (@rejection in notes, set-type enum, read-only field stripping, exercise-template search caching/pagination, routine-folder listing/pagination-walk, routine listing/folder-filtering/pagination-walk, routine detail read-shape unwrapping).Integration (
test/integration/*.test.ts): the realapp/api/mcp/route.tshandler wired to reallib/auth.ts/lib/hevy.tswith onlyfetchmocked, including a fullsearch_exercise_templates→create_routine_folder→create_routine× 3 walkthrough of a real 3-day/week training program; the real/api/oauth/authorizeand/api/oauth/tokenroute handlers; and the.well-knownOAuth metadata routes.E2E (
test/e2e/*.e2e.test.mjs): boots the production build and asserts over real HTTP — health check, 401 on bad/missing auth,tools/listreturns all 10 tools, OAuth discovery metadata, and a full authorization-code + PKCE round trip that ends with a working access token against/api/mcp. Doesn't exercise real Hevy data (CI has no real credentials by design).
Manually verifying Hevy write operations
CI never touches real Hevy data, so the routine-write tools (create_routine, update_routine, create_routine_folder) — and list_routine_folders, list_routines, get_routine_detail, which read the same resource — need a one-off manual check against a real Hevy Pro account after any change to them:
Set a real
HEVY_API_KEYin.env.local, then runvercel dev.Call
search_exercise_templateswith a real query (e.g. via the smoke-testcurlpattern above, usingtools/callinstead oftools/list) and confirm real candidates come back.Call
create_routinewith an obviously-throwaway title (e.g."fitness-mcp manual test — delete me") andconfirm: true, and note the returnedid.Open the Hevy app or web app and visually confirm the routine was created with the expected exercises, sets, reps, and weights.
Check whether the returned
webUrl(https://hevy.com/routines/{id}) actually opens the routine — it's an unverified best-effort guess at Hevy's URL pattern, not a documented API field. If it doesn't resolve, that's worth a follow-up to remove or fix the field.Call
get_routine_detailwith thatidand confirm the returned exercises/sets/reps/weights match what you just created — this also confirms thetitleandsuperset_idfields actually come back on a real GET response (see the "unverified" comments on those fields inlib/hevy.ts). Calllist_routines(with nofolderId, then with the folder's id, then withfolderId: null) and confirm the new routine shows up in the right buckets.Optionally call
update_routineagainst the sameidto verify the overwrite path (note it has nofolderIdparameter — Hevy's update endpoint has nofolder_idfield at all, and sending one, evennull, 400s, so a routine's folder can only be set at creation), andcreate_routine_folderfollowed bycreate_routinewith its returnedfolderIdto verify folder filing. Calllist_routine_foldersafterward and confirm the newly created folder shows up with a matchingid/title.Delete the test routine manually in the Hevy app. Hevy's public API has no documented
DELETE /v1/routinesendpoint, so this server cannot clean up after itself — there is intentionally nodelete_routinetool.Never commit a real
HEVY_API_KEY, and never run this check in CI.
Environment variables
Variable | Purpose |
| Hevy Pro API key from https://hevy.com/settings?developer (read + write — workouts, routines, routine folders) |
| Shared secret this server requires on every request, and the access_token our OAuth flow issues — see Authentication above |
| Credentials for this server's own minimal OAuth authorization server — see Authentication above |
| Optional. Comma-separated allowlist for |
Set these in the Vercel project's Environment Variables (Production + Preview). Never commit real values — .env.example only documents the names.
Deploy
vercel linkvercel env add HEVY_API_KEY/vercel env add MCP_BEARER_TOKEN/vercel env add OAUTH_CLIENT_ID/vercel env add OAUTH_CLIENT_SECRET(repeat for each environment you use)Connect this GitHub repo in the Vercel dashboard for auto-deploy on push to
main, or runvercel --prodmanually.Note the deployed URL.
fitness-mcp.vercel.appis often already taken by an unrelated project on Vercel's shared.vercel.appnamespace — check the actual assigned domain under Project → Settings → Domains (orvercel inspect <deployment-url>). This project's production URL ishttps://fitness-mcp-eight.vercel.app/api/mcp.
Connect to Claude
Custom connectors can only be added from claude.ai (web) or the desktop app — not from the mobile app. Once added there, they're usable from mobile automatically.
On claude.ai: Settings → Connectors → Add custom connector.
Name:
Fitness Data. URL:https://fitness-mcp-eight.vercel.app/api/mcp.If your account has the "Request headers" beta: add
Authorization: Bearer <MCP_BEARER_TOKEN>there and skip to step 5.Otherwise, open Advanced settings and fill in OAuth Client ID / OAuth Client Secret with the
OAUTH_CLIENT_ID/OAUTH_CLIENT_SECRETvalues set in Vercel. Claude will discover the/authorizeand/tokenendpoints automatically via this server's.well-knownmetadata.Save. Claude should list the 10 tools above.
Try asking: "直近のワークアウトを教えて" (tell me about my recent workouts), or "3日/週の筋トレメニューを考えてHevyに登録して" (design a 3-day/week training menu and register it in Hevy).
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenancePython MCP server for the Hevy fitness app. Gives Claude full access to your Hevy data. Log workouts, manage routines, track body measurements, browse exercises, and more. Covers all 25 endpoints of the official Hevy API.MIT
- AlicenseAqualityBmaintenanceA read-only MCP server that gives Claude Desktop access to your Garmin Connect data — daily health metrics, sleep, activities, training status, and body composition.6MIT
- AlicenseNot gradedqualityCmaintenanceMCP server that connects Claude to the Hevy fitness app, enabling AI-driven personalized strength coaching by reading lifting history and programming workouts.MIT
- AlicenseNot gradedqualityCmaintenanceHosted MCP server that syncs health data from Apple Health, Fitbit, Oura, and Google Health Connect, enabling Claude and ChatGPT to query workouts, sleep, nutrition, and recovery in plain English with interactive charts.MIT
Related MCP Connectors
WHOOP recovery, strain, sleep and workouts in Claude via official WHOOP OAuth. Free, open source.
Create Hevy routines and analyze your training from chat. Unofficial; BYO Hevy PRO API key.
Garmin data in Claude: 135 tools — activities, sleep, HRV, training, workouts. Free, open source.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ikeike443/fitness-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server