npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

matrix-workouts-mcp

v0.3.0

Published

An MCP server for Matrix / Johnson Fitness workout history — power, cadence, resistance and heart rate the dashboard never shows.

Readme

matrix-workouts-mcp

npm CI license

An MCP server for your Matrix / Johnson Fitness ride history, so an AI agent can answer questions about it.

The dashboard at matrixworkouts.jfit.co shows six numbers per ride. The record behind it carries a sample every 10 seconds with power (watts), console resistance level and cadence (rpm) — none of which appears anywhere in the site's UI, and none of which its own list page can reach for anything older than about a week. This server hands all of it to an agent, with the cross-ride views the platform has never had: power curve, training volume by week or month, one ride against another.

> what was my best 5-minute power this month, and which ride was it?
> compare my last two Sprint 8 sessions
> has my average power at resistance 8 moved since August?

Install

claude mcp add matrix-workouts -- npx -y matrix-workouts-mcp

Or, in a client that takes JSON:

{
  "mcpServers": {
    "matrix-workouts": {
      "command": "npx",
      "args": ["-y", "matrix-workouts-mcp"],
      "env": {
        "MATRIX_XID": "your-member-number",
        "MATRIX_PIN": "your-passcode"
      }
    }
  }
}

Node 20 or newer.

You can also run it without credentials. Download your history once with npx matrix-workouts-history, point MATRIX_CACHE_DIR at the result, and every tool except refresh_history works against it. No passcode in a config file, no sign-in from the server at all.

Configuration

| Variable | Default | What it does | |---|---|---| | MATRIX_XID | — | Member number from the gym tag. Without it the server is read-only over the cache. | | MATRIX_PIN | — | Numeric passcode. Used for one sign-in request, never stored or logged. | | MATRIX_CACHE_DIR | $XDG_CACHE_HOME/matrix-workouts-mcp, else ~/.cache/matrix-workouts-mcp | Where the downloaded history is kept, written 0600. | | MATRIX_CACHE_TTL_MINUTES | 60 | How long a cached history is served before a refetch. | | MCP_TRANSPORT | stdio | Set to http to serve over HTTP instead. | | MATRIX_HTTP_PORT | 3000 | HTTP transport only. Falls back to PORT when unset. | | MATRIX_HTTP_HOST | 127.0.0.1 | HTTP transport only. Loopback on purpose — see Privacy. | | MATRIX_MCP_TOKEN | — | Bearer token. Required for the HTTP transport; the server refuses to start without it. |

Tools

| Tool | Returns | |---|---| | list_workouts | Rides newest first, one compact row each. Filter by date, machine or program mode. No sample series. | | get_workout | One ride in full: work, power, cadence and resistance (average and peak), heart rate's average, minimum and maximum both as reported and as filtered, and what the console was holding constant. | | get_samples | The 10-second interval series as CSV, over a window and a stride you choose. Downsampled to fit the row limit, never cut short. | | summarize_history | Volume by week or month, plus the power curve over the matched rides. Same date, machine and mode filters as list_workouts. | | compare_workouts | Two to eight rides side by side on every derived number. | | export_workout | Writes a lossless JSON export document and returns the path. | | refresh_history | Re-downloads from the API. Needs credentials. |

Tools answer from one cached history rather than fetching per call — the API returns every ride on the account in a single response, so anything else would re-download your entire history a dozen times a conversation.

Two heart-rate averages, always. The platform's own figure is not derived from the sample series and does not always agree with it: the console averages in real time, ahead of the dropouts the series preserves. On a glitching strap the reported figure is the better one — on one ride with independent Apple Watch ground truth, the console's 153 bpm was exactly right and the filtered series gave 151. Reporting only ours would be quietly misleading, so both go out, labelled.

Privacy

This is your heart rate at ten-second resolution, and handing it to a language model is a real decision rather than a formality. What the server does about that:

  • The HTTP transport refuses to start without MATRIX_MCP_TOKEN, and binds 127.0.0.1 unless you deliberately change it. Over stdio nothing is listening at all, which is the mode to prefer.
  • Credentials are used for one sign-in request. The passcode is never stored, logged or returned; the bearer token is never persisted. Errors from the API client are redacted before they can carry a token into a message.
  • Cached and exported files are written 0600, outside any repository.
  • export_workout returns a path, never the contents — partly for your context budget, mostly because a document is a thing you keep rather than a thing you paste.
  • Nothing is sent anywhere but apollo.jfit.co, the API the data came from.

Running with no credentials at all, over a cache you downloaded yourself, is the tightest configuration and everything except refresh_history still works.

Development

npm test         # vitest, against real ride fixtures — no network
npm run typecheck
npm run build
npm run dev      # tsx src/cli.ts

Tests use three real captured rides and a stubbed downloader, so the suite needs neither credentials nor a network. src/docs.test.ts fails the build if a tool or an environment variable is missing from the tables above — see AGENTS.md.

Cutting a release:

npm run release -- patch     # bumps, tags, packs; refuses without a CHANGELOG entry
git push origin main:main --follow-tags
npm publish --access public

Part of matrix-workouts

| | | |---|---| | matrix-workouts-core | the parser, API client and export format this is built on | | matrix-workouts-chrome | a Chrome extension that puts the same data on screen | | matrix-workouts-mcp | this server |

MIT licensed. Unaffiliated with Matrix Fitness or Johnson Health Tech.