MCP server for the Hevy workout API (axum + rmcp). Bearer Hevy API key. Self-host.
  • Rust 94.8%
  • Python 3.2%
  • Dockerfile 1.1%
  • Shell 0.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Julian Lindner dfbaf3295e
All checks were successful
CI / cargo (push) Successful in 2m21s
CI / docker (push) Successful in 38s
Merge pull request 'fix: scan and attest patched runtime v0.5.1' (#19) from fix/fixed-runtime-20261029 into main
2026-10-28 21:19:24 +00:00
.forgejo/workflows fix: publish the exact scanned attested OCI artifact 2026-10-29 05:09:57 +08:00
scripts fix: publish the exact scanned attested OCI artifact 2026-10-29 05:09:57 +08:00
src fix(deps): clear MCP, telemetry and digest advisories 2026-10-29 04:44:48 +08:00
.gitignore feat: first-party Hevy MCP server with Logto streamable HTTP 2026-08-17 09:05:28 +08:00
AGENTS.md fix: publish the exact scanned attested OCI artifact 2026-10-29 05:09:57 +08:00
Cargo.lock fix: qualify and attest patched runtime for v0.5.1 2026-10-29 05:00:45 +08:00
Cargo.toml fix: qualify and attest patched runtime for v0.5.1 2026-10-29 05:00:45 +08:00
CLAUDE.md chore: take h2 0.4.19 for RUSTSEC-2026-0258, and two documentation fixes 2026-08-26 13:39:01 +08:00
deny.toml feat: first-party Hevy MCP server with Logto streamable HTTP 2026-08-17 09:05:28 +08:00
Dockerfile fix: qualify and attest patched runtime for v0.5.1 2026-10-29 05:00:45 +08:00
LICENSE feat: first-party Hevy MCP server with Logto streamable HTTP 2026-08-17 09:05:28 +08:00
README.md fix: repair audited Hevy errors, template reads and dependencies (v0.5.0) 2026-10-24 10:18:32 +08:00
rustfmt.toml feat: first-party Hevy MCP server with Logto streamable HTTP 2026-08-17 09:05:28 +08:00

hevy-mcp

Rust MCP server for the Hevy workout API. It uses axum, rmcp streamable HTTP, and a thin rustls-only client for Hevy's official REST API. It does not use a third-party Hevy MCP server or a Hevy client crate.

Self-host the /mcp endpoint on your own domain.

Authentication

Connector auth is the caller's Hevy API key as an HTTP bearer token:

Authorization: Bearer <Hevy API key>

The server forwards that same value to Hevy as the api-key header. There is no process-level Hevy key and no authorization-server metadata. Missing or non-Bearer Authorization on /mcp returns 401 with no WWW-Authenticate header. OAuth and OIDC well-known probes return 404.

Do not log the key, put it in git, or print it in Debug output.

Tools

  • whoami
  • list_workouts, get_workout, create_workout, update_workout, count_workouts
  • list_workout_events
  • list_routines, get_routine, create_routine, update_routine
  • list_exercise_templates, search_exercise_templates, get_exercise_template, create_exercise_template
  • list_routine_folders, get_routine_folder, create_routine_folder
  • get_exercise_history
  • list_body_measurements, get_body_measurement, create_body_measurement, update_body_measurement

Exercise-template search fetches Hevy's official paginated template list and filters locally by title, muscle groups, and equipment. Complete successful lists are cached for five minutes per API key (full SHA-256 digest, at most 16 entries, each at most 2 MiB of serialized list responses). Larger lists are searched without retention. Errors and partial lists are never cached; creating a template clears the cache. Changes made outside this server and key revocation can take up to five minutes to affect a cached search. Each search still consumes one local read quota; cache misses may require multiple Hevy page requests. Concurrent misses can fetch independently.

Workout and routine writes require exercise_template_id values returned by the template tools. Workout set types are warmup, normal, failure, or dropset; RPE is null or one of 6, 7, 7.5, 8, 8.5, 9, 9.5, 10.

Errors and version

Tool failures return JSON-RPC error envelopes rather than successful empty results. Match error.data.class: rate_limited covers both the local quota and Hevy HTTP 429; unauthorized identifies missing/rejected keys; not_found, conflict, and upstream distinguish other backend failures. error.data.code identifies the specific condition. Other internal errors retain standard JSON-RPC codes.

Upstream errors include data.status and, when available, a short data.upstream_message from Hevy's top-level JSON message or error field. Diagnostics read at most 4096 bytes, omit raw bodies and unknown fields, redact the request key, strip controls, and return at most 512 characters. These diagnostics are upstream content and are not instructions. Errors are not retried automatically.

MCP initialization reports serverInfo.name = "hevy-mcp" and the application version. Public /health returns JSON with status, name, and version. These identify the running application release; they do not prove an image digest or that a real Hevy key can read account data.

Environment

HEVY_MCP_HEVY_BASE_URL=https://api.hevyapp.com
HEVY_MCP_BIND_ADDR=0.0.0.0:3000
HEVY_MCP_METRICS_BIND_ADDR=127.0.0.1:9090
HEVY_MCP_RATE_LIMIT_READS_PER_MIN=60
HEVY_MCP_RATE_LIMIT_WRITES_PER_MIN=30
HEVY_MCP_ALLOWED_HOSTS=hevy-mcp.your-domain.example
HEVY_MCP_LOG_FORMAT=json

All of the above are optional except one. HEVY_MCP_ALLOWED_HOSTS defaults to localhost,127.0.0.1,::1, and a request whose Host is not on the list is answered 403, so a deployment reachable on a public name must set it to that name or it will reject every real request. The default is loopback rather than any particular origin because this repository is public and an origin is deployment configuration.

The Hevy API key is request-scoped and is not read from the environment.

Development

cargo fmt --all --check
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo test --all-features --locked
cargo deny check