DT

dlwiest/ts-tonal-mcp

Developer tools
13 stars 8 forks Quality 83 Trend 40ISC

Talk to your Tonal account with an LLM

Overview

A Model Context Protocol (MCP) server that provides LLMs with access to Tonal fitness data. Built on top of @dlwiest/ts-tonal-client, this server enables AI assistants to answer questions about your workouts, fitness progress, and muscle readiness. - 🏋️ - Access recent workout data and performance metrics - 💪 - Get recovery status for workout planning - 📊 - View lifetime stats, streaks, and progress trends - 🎯 - Browse and filter available Tonal exercises - ✨ - Build and update custom workouts with per-set programming and supersets - 🔧 - Easy to add new tools via registry pattern The server requires your Tonal credentials as environment variables: Add to your Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json): Add the server to Claude Code using the CLI: Put TONAL_USERNAME and TONAL_PASSWORD in ~/.hermes/.env, then register the server under mcp_servers.tonal in ~/.hermes/config.yaml. Values in tools.

README

Tonal MCP Server

A Model Context Protocol (MCP) server that provides LLMs with access to Tonal fitness data. Built on top of @dlwiest/ts-tonal-client, this server enables AI assistants to answer questions about your workouts, fitness progress, and muscle readiness.

Features

  • 🏋️ Workout History - Access recent workout data and performance metrics
  • 💪 Muscle Readiness - Get recovery status for workout planning
  • 📊 Fitness Statistics - View lifetime stats, streaks, and progress trends
  • 🎯 Movement Database - Browse and filter available Tonal exercises
  • ✨ Workout Creation and Editing - Build and update custom workouts with per-set programming and supersets
  • 🔧 Extensible Architecture - Easy to add new tools via registry pattern

Installation

Clone and build from source:

git clone https://github.com/dlwiest/ts-tonal-mcp.git
cd ts-tonal-mcp
npm install
npm run build

Configuration

The server requires your Tonal credentials as environment variables:

export TONAL_USERNAME="[email protected]"
export TONAL_PASSWORD="your_password"

Or create a .env file:

[email protected]
TONAL_PASSWORD=your_password

Usage

Claude Desktop

Add to your Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "tonal": {
      "command": "node",
      "args": ["/path/to/ts-tonal-mcp/dist/index.js"],
      "env": {
        "TONAL_USERNAME": "[email protected]",
        "TONAL_PASSWORD": "your_password"
      }
    }
  }
}

Claude Code

Add the server to Claude Code using the CLI:

claude mcp add tonal-mcp node /path/to/ts-tonal-mcp/dist/index.js -e TONAL_USERNAME=your_email -e TONAL_PASSWORD=your_password

Hermes Agent

Put TONAL_USERNAME and TONAL_PASSWORD in ~/.hermes/.env, then register the server under mcp_servers.tonal in ~/.hermes/config.yaml. Values in tools.include are raw tool names such as get_muscle_readiness, never registry names such as mcp__tonal__get_muscle_readiness. Reload the Hermes gateway/MCP connection before expecting new tools in Telegram sessions.

See hermes-tonal for the complete read-only and full-access configurations and companion skill.

Remote (HTTP) Deployment

Set MCP_TRANSPORT=http to serve MCP over Streamable HTTP instead of stdio. The server listens on PORT (default 8080) at /mcp, with a /health endpoint for health checks. MCP_AUTH_TOKEN is required in this mode, and clients must send it as Authorization: Bearer .

MCP_TRANSPORT=http MCP_AUTH_TOKEN=$(openssl rand -hex 32) npm start

Connect from Claude Code:

claude mcp add --transport http tonal https://your-host/mcp --header "Authorization: Bearer "

Direct Usage

# Run the server directly (stdio mode)
npm start

# Or run the built JavaScript directly
node dist/index.js

Available Tools

The server provides 24 tools for LLM interactions:

Tool Description
get_muscle_readiness Get current muscle readiness percentages for recovery planning
get_movements Browse Tonal movements/exercises, optionally filtered by muscle groups
search_movements Advanced search with 11+ filters (muscle groups, equipment, arm angle, skill level, etc.)
get_recent_workouts View recent workout history with summary statistics
get_user_stats Get comprehensive fitness statistics and current streak
get_recent_progress Analyze recent progress including workout frequency and trends
get_goal_metrics Get weekly goal metrics (Volume, Work, Movement Quality Score, Strength Sets, Power Reps, Endurance Sets, Functional Strength Score) with the current week’s actual, target, and range plus a recent trend; optional name filter
get_strength_scores Get Tonal’s headline current Strength Score by body region and a compact per-activity trend; optional days is a calendar-day lookback, not a row count
get_achievements Get the total achievements earned, the next milestones, and earned achievements newest first; optional limit (default 15)
list_workout_activities List one page of workout activities using the oldest-first API offset (default 0) and limit from 1 to 100 (default 20)
get_workout_activity_details Get one completed activity with performed sets, movement names, weights, reps, one-rep max, volume, range of motion, prescriptions, set modifiers, reps in reserve, personal records, and set timing
get_workout_summary Get one formatted workout summary with session metadata and a per-movement breakdown
list_custom_workouts List all your custom workouts created on Tonal
create_workout Create a new custom workout with exercises, sets, reps/duration, and block grouping
delete_custom_workout Delete a custom workout by name; requires confirm: true
get_custom_workout_details Get detailed information about a custom workout including all sets
get_workout_for_editing Get the complete editable structure of an existing workout
update_workout Update an existing workout by replacing its full set list
estimate_workout_duration Estimate how long a prescribed workout would take, without creating or modifying anything
get_suggested_weights Get Tonal’s last recorded suggested weight in pounds per on-machine movement (an estimate of what 100% loads), from your most recent performed set
search_workout_library Search Tonal’s published guided workouts by text, level, target area, training type, device, length, and equipment (required, or limited to what you own), newest first
search_program_library Search Tonal’s published programs by text, level, weeks, workouts per week, device, and equipment, newest first
get_library_workout Get a guided workout’s exercises in the shape create_workout accepts, flagging sets with no weight percentage
get_library_program Get a program’s week-by-week schedule of workouts

The library tools read Tonal’s full catalog (over 3,000 workouts and 300 programs) through the client’s on-disk cache, so only the first search in a 24-hour window downloads it.

Per-set programming

create_workout, update_workout, and estimate_workout_duration accept setDetails when sets differ. Each entry may contain reps, duration, weight, warmUp, dropSet, burnout, spotter, eccentric, chains, flex, and description. When present, setDetails is authoritative and its length is the set count. Without it, the existing sets, reps, duration, and weight fields still create uniform sets. An exercise-level weight acts as the fallback for any set that omits its own.

Mode flags default to false when omitted. get_workout_for_editing preserves them in setDetails, including for otherwise uniform sets, so passing those exercises back to update_workout retains modes enabled on the Tonal touchscreen. Requests enabling spotter, eccentric, chains, or burnout are rejected only when the movement explicitly reports the matching onMachineInfo.*Disabled flag as true; missing support metadata does not block them. Tonal reports no equivalent disabled flag for flex.

Weights in pounds

Tonal stores a set’s weight as a percentage of its suggested weight for the movement, which it records on every performed set. In measured history, the loaded weight matched percentage × suggested weight within 1 lb on 97% of sets without eccentric, chains, or Smart Flex. The suggestion depends on the prescribed reps: in every session that mixed rep counts for a movement, the higher-rep sets carried a lower suggestion. weight (a percentage) works exactly as before. weightLb is an optional alternative (exercise level or per set, never alongside weight at the same level) that converts each set’s pounds using the suggestion from your most recent performance of that movement at the same reps (or duration, for timed movements) in the last 20 Tonal workouts. That recorded suggestion may already have changed, so converted pounds are estimates: in measured history it carried into the next session within 5% for 92% of sets. Results outside 1–100% are rejected: Tonal’s own programming never exceeds 100%, and higher values are unverified on a trainer. Off-machine movements, or movements you haven’t performed recently at that rep count, need weight. The response lists each conversion, and get_suggested_weights shows the suggestions available per rep count. Because the stored value is a percentage, the trainer’s load rises with Tonal’s suggestion as you progress.

{
  "title": "Bench Progression",
  "exercises": [
    {
      "movementName": "Bench Press",
      "setDetails": [
        { "reps": 10, "weight": 40, "warmUp": true },
        { "reps": 8, "weight": 55 },
        { "reps": 6, "weight": 65, "dropSet": true }
      ]
    }
  ]
}

Deletion also requires an explicit opt-in. Pass the exact workout name and "confirm": true to delete_custom_workout; requests without confirmation do not delete anything.

Example Conversations

With the MCP server connected, you can ask Claude:

Fitness Insights:

  • “Should I work out today?” → Gets muscle readiness data
  • “What did I do this week?” → Shows recent workout history
  • “How’s my progress lately?” → Analyzes recent metrics and trends
  • “Am I staying consistent with my workouts?” → Reviews frequency and streaks

Exercise Discovery:

  • “What chest exercises are available?” → Filters movements by muscle group
  • “Show me beginner-friendly leg exercises” → Searches with skill level filter
  • “Find exercises that use the bench” → Filters by equipment

Workout Management:

  • “Show me my custom workouts” → Lists all your created workouts
  • “Create a push/pull workout with warmup and cooldown” → Builds a structured workout
  • “Show me details for ‘Upper Body Blast’” → View full workout structure with all sets
  • “Edit ‘Upper Body Blast’ and make its last set a drop set” → Fetches the editable structure, then replaces the workout’s full set list
  • “Delete my workout called ‘Old Routine’” → Removes a specific custom workout

Development

# Install dependencies
npm install

# Build TypeScript
npm run build

# Type checking
npm run typecheck

# Development mode (watch)
npm run dev

Adding New Tools

Thanks to the registry pattern, adding new tools is straightforward:

  1. Create your tool function in the appropriate file (e.g., src/tools/analytics.ts)
  2. Add the tool definition to src/tools/registry.ts
  3. The server automatically discovers and registers new tools

Protocol

This server uses the Model Context Protocol (MCP) over stdio for communication. It’s compatible with:

  • ✅ Claude Desktop
  • ✅ Claude Code
  • ✅ Other MCP-compatible LLM tools

Security

  • Credentials are handled securely via environment variables
  • Movement metadata is cached locally for up to 24 hours; credentials are not stored or logged
  • All communication with Tonal’s API uses the official client library

Dependencies

License

ISC

Contributing

Contributions welcome! This server is designed to be easily extensible. Please feel free to:

  • Add new tools for additional Tonal data
  • Improve error handling and validation
  • Enhance documentation and examples

Adding a tool

A tool name lives in six places and nothing in the build validates any of them. src/tools/registry.ts is the only source of truth; the rest are hand-maintained and drift silently. They split into two layers that fail differently:

Can the tool be called at all?

  1. src/tools/registry.ts — the definition.
  2. hermes-tonal/config/mcp_servers.tonal.yaml — read-only profile allowlist (raw names, no mcp__tonal__ prefix). Read-only tools only.
  3. hermes-tonal/config/mcp_servers.tonal.full.yaml — full profile allowlist, every tool.

Miss these and Hermes never exposes the tool, however good the server is.

Does the agent know when and how to use it?

  1. This README’s tool table, plus any stated tool count.
  2. hermes-tonal/skills/health/tonal/SKILL.md — the mcp__tonal__* inventory, the read-only count, and the relevant ## Read workflows entry. Inventory membership alone is not enough.
  3. hermes-tonal/skills/health/tonal/references/*.md — the runbook that actually drives behavior. A “when to use” line pointing at a workflow no runbook describes is worse than silence, because it implies a procedure that does not exist.

Verify mechanically rather than by eye: load the built dist/tools/registry.js and check each inventory in both directions — nothing missing, and no name that is not registered. When checking SKILL.md for guidance coverage, strip the bare inventory list first so roster membership does not count as coverage.

Note also that npm run typecheck covers tests/ via tsconfig.test.json, while npm test does not typecheck at all — tsx strips types without checking them. A test fixture that omits an optional field is only a type-honesty guard if npm run typecheck runs.

Contact

For questions or support, please contact Derrick Wiest.

View the full README on GitHub

Install

This server does not publish a one-line install command.

Open the repository installation guide New to MCP? Start with the mcpskill.org guide →

Configuration

{ "mcpServers": { "tonal": { "command": "node", "args": ["/path/to/ts-tonal-mcp/dist/index.js"], "env": { "TONAL_USERNAME": "[email protected]", "TONAL_PASSWORD": "your_password" } } } }