@trainheroic-unofficial/core
v3.1.0
Published
Shared MCP tool layer for TrainHeroic, used by the local and Cloudflare servers.
Readme
@trainheroic-unofficial/core
The shared MCP (Model Context Protocol) tool layer for
TrainHeroic. Each tool (a function an AI assistant can call) is defined once here and reused
by both servers: the local stdio server (@trainheroic-unofficial/coach-mcp) and the hosted
Cloudflare worker (@trainheroic-unofficial/cloudflare). You only need this package if you
are building your own MCP server; to use the tools, run one of those servers.
Part of the trainheroic-unofficial workspace.
Contents
How a server uses it
A server builds a ToolContext and passes it, along with its MCP server instance, to the
registerXxxTools(server, ctx) functions (the register* names below are the real exports;
registerXxxTools is shorthand for all of them). ToolContext is { client, index }: an
authenticated TrainHeroicClient (from @trainheroic-unofficial/js) and anything implementing
the ExerciseIndex interface (the in-memory ExerciseLibrary here, or the hosted worker's
D1-backed store).
Install it alongside the MCP SDK and the js client (both peers you construct from):
npm install @trainheroic-unofficial/core @trainheroic-unofficial/js @modelcontextprotocol/[email protected]import { McpServer } from "@modelcontextprotocol/server";
import { serveStdio } from "@modelcontextprotocol/server/stdio";
import { TrainHeroicClient, ExerciseLibrary } from "@trainheroic-unofficial/js";
import {
registerReadTools,
registerExerciseTools,
registerWorkoutTools,
// registerAthleteTools, registerTeamTools, registerAnalyticsTools, registerMessagingTools
type ToolContext,
} from "@trainheroic-unofficial/core";
const client = new TrainHeroicClient(
process.env.TRAINHEROIC_EMAIL!,
process.env.TRAINHEROIC_PASSWORD!,
);
const index = new ExerciseLibrary(client);
serveStdio(() => {
const server = new McpServer({ name: "trainheroic", version: "1.0.0" });
const ctx: ToolContext = { client, index };
registerReadTools(server, ctx);
registerExerciseTools(server, ctx);
registerWorkoutTools(server, ctx);
return server;
});Because the context depends on the ExerciseIndex interface rather than a concrete store, the
same tools run against the local in-memory library and the hosted D1
(Cloudflare's SQLite database) mirror without change.
What the tools cover
Each register* function registers a group of tools, and the individual tool names the model
sees are snake_case (e.g. analytics_query, exercise_resolve). The groups:
- coach reads (profile, athletes, teams, programs, notifications, analytics catalog)
- athlete management (
registerAthleteTools, the coach's roster view: invite, archive, restore) - team management (create, rename, delete, join codes)
- analytics report pulls (
analytics_query: readiness, 1RM (one-rep max) and working-max history, training summary, compliance, lift progress) - exercise library operations (resolve, search, get, sync, create, forget, stats)
- the workout/session lifecycle (build a draft, read it back, publish, unpublish, copy, save as template, remove)
- messaging (list, read, draft, send, delete)
Every endpoint reaches the model through a typed tool; the surface has no raw-request escape hatch.
(registerAthleteTools here is the coach's roster view, distinct from the athlete's own
training tools that the athlete server registers.)
Tools return their result in-band. A failure comes back as an error result the model can read
and self-correct from. Reads are annotated read-only. Athlete-facing or
destructive actions (publish, unpublish, remove, send, delete, archive, team/code delete) pass
through a confirmation gate. The gate prefers MCP multi-round-trip elicitation
(input_required); when the client already confirmed, it accepts an explicit
confirm: true argument. It fails closed if neither condition is met.
The D1-backed warehouse sync tools live in the cloudflare package because they depend on its storage.
Develop
Run pnpm install once at the repo root (Node >= 24, pnpm 11), then from this package:
pnpm build # tsdown
pnpm typecheck
pnpm test
pnpm exec vitest run test/confirm.test.ts # one file