ALWAYS follow these instructions first and only fallback to search or additional context if the information here is incomplete or found to be in error.
- hevy-mcp is a Model Context Protocol (MCP) server for the Hevy Fitness API, enabling AI agents to manage workouts, routines, exercise templates, and folders via the Hevy API.
- The codebase is TypeScript (Node.js v20+), with a clear separation between tool implementations (
src/tools/), generated API clients (src/generated/), and utility logic (src/utils/). - API client code is generated from the OpenAPI spec using Kubb. Do not manually edit generated files.
- Type Safety: The project uses Zod schema inference for type-safe tool parameters, eliminating manual type assertions and ensuring compile-time type safety.
Run these commands in order to set up a working development environment (Corepack is bundled with Node.js v20+, so run corepack use pnpm@10.22.0 once per machine if pnpm isn't available):
-
Install dependencies:
pnpm install
- Takes approximately 30 seconds. NEVER CANCEL - set timeout to 60+ seconds.
-
Build the project:
pnpm run build
- Takes approximately 3-5 seconds. TypeScript compilation via tsdown.
- Always build before running the server or testing changes.
-
Run linting/formatting:
pnpm run check
- Takes less than 1 second.
- EXPECTED WARNING: Biome schema version mismatch warning is normal and can be ignored.
-
Run unit tests only:
pnpm vitest run --exclude tests/integration/**- Takes approximately 1-2 seconds. NEVER CANCEL.
- This is the primary testing command for development.
-
Run integration tests (requires API key):
pnpm vitest run tests/integration
- WILL FAIL without valid
HEVY_API_KEYin.envfile (by design). - Integration tests require real API access and cannot run in sandboxed environments.
- WILL FAIL without valid
-
Run all tests:
pnpm test- Takes approximately 1-2 seconds for unit tests only (without API key).
- WILL FAIL if
HEVY_API_KEYis missing due to integration test failure (by design).
-
Regenerate API client from OpenAPI spec:
pnpm run build:client
- Takes approximately 4-5 seconds. NEVER CANCEL.
- EXPECTED WARNINGS: OpenAPI validation warnings about missing schemas are normal.
- Always run this after updating
openapi-spec.json.
-
Validate OpenAPI spec:
pnpm run validate:openapi
- Takes less than 1 second.
- Uses IBM OpenAPI Validator with Spectral ruleset (
.spectral.yaml). - Validates
openapi-spec.jsonagainst OpenAPI 3.0 specification. - EXPECTED WARNINGS: Since this is an external API spec from Hevy, some warnings are expected and acceptable.
-
Development server (with hot reload):
pnpm run dev
- REQUIRES: Valid
HEVY_API_KEYin.envfile or will exit immediately. - Server runs indefinitely until stopped.
- REQUIRES: Valid
-
Production server:
pnpm start- REQUIRES: Valid
HEVY_API_KEYin.envfile or will exit immediately. - Must run
pnpm run buildfirst.
pnpm run export-specs: Fails with network error (ENOTFOUND api.hevyapp.com) in sandboxed environments.pnpm run inspect: MCP inspector tool - may timeout in environments without proper MCP client setup.
Only list commands here that are known to be flaky or unsupported in some
environments. Other documented commands (including pnpm run check:types) are
expected to succeed locally; treat failures as issues to fix rather than
environmental flakiness. See README.md for the canonical list of commands.
pnpm run check:types is expected to pass locally before opening a PR; see the
"Type checking validation" section below.
Create a .env file in the project root with:
HEVY_API_KEY=your_hevy_api_key_hereCRITICAL: Without this API key:
- Servers will not start
- Integration tests will fail (by design)
- API client functionality cannot be tested
- Supported: Node.js >= 20
- Recommended: Use the exact version pinned in
.nvmrc(CI uses this exact version) - If you use
nvm, runnvm usein the repo root to match.nvmrc - Use
node --versionto verify current version
Always perform these validation steps after making changes:
-
Build validation:
pnpm run build
- Must complete successfully without errors.
-
Unit test validation:
pnpm vitest run --exclude tests/integration/**- All unit tests must pass.
-
Code style validation:
pnpm run check
- Must complete without errors (warnings about Biome schema are acceptable).
- EXPECTED: Warnings about
anyusage inwebhooks.tsare acceptable (API methods not yet available).
-
Type checking validation:
pnpm run check:types
- Must complete without errors.
- Runs the TypeScript compiler in check-only mode (no emitted files), as
configured in the
check:typesscript inpackage.json. - Note:
pnpm run build(tsup) may still succeed when this fails. - Treat failures here as issues to fix (even if the build passes).
- Run this locally before opening a PR (CI does not currently run this check).
- Verifies all type inference is working correctly.
-
MCP tool functionality validation (if API key available):
- Start development server:
pnpm run dev - Test MCP tool endpoints with a client
- Verify tool responses are correctly formatted
- Start development server:
- ALWAYS run unit tests after any source code changes
- ALWAYS run build validation before committing changes
- ALWAYS use type inference (
InferToolParams) instead of manual type assertions - DO NOT attempt to fix TypeScript errors in
src/generated/- these are auto-generated files - DO NOT commit
.envfiles containing real API keys - DO NOT use
as anyoras unknowntype assertions in tool handlers
src/
├── index.ts # Main entry point - register tools here
├── tools/ # MCP tool implementations
│ ├── workouts.ts # Workout management tools
│ ├── routines.ts # Routine management tools
│ ├── templates.ts # Exercise template tools
│ ├── folders.ts # Routine folder tools
│ └── webhooks.ts # Webhook subscription tools
├── generated/ # Auto-generated API client (DO NOT EDIT)
│ ├── client/ # Kubb-generated client code
│ └── schemas/ # Zod validation schemas
└── utils/ # Shared helper functions
├── tool-helpers.ts # Type inference utilities (InferToolParams)
├── error-handler.ts # Centralized error handling (withErrorHandling)
├── response-formatter.ts # MCP response utilities
├── formatters.ts # Data formatting helpers
├── hevyClient.ts # API client factory
├── hevyClientKubb.ts # Kubb client wrapper
├── config.ts # Configuration parsing
└── httpServer.ts # HTTP server utilities (deprecated)
tests/
├── integration/ # Integration tests (require API key)
└── unit tests are co-located with source files (*.test.ts)
The project uses Zod schema inference for type-safe tool parameters. This eliminates manual type assertions and ensures types match schemas automatically.
Always extract Zod schemas and use InferToolParams for type safety:
import type { InferToolParams } from "../utils/tool-helpers.js";
import { withErrorHandling } from "../utils/error-handler.js";
// 1. Define schema as const
const getRoutinesSchema = {
page: z.coerce.number().int().gte(1).default(1),
pageSize: z.coerce.number().int().gte(1).lte(10).default(5),
} as const;
// 2. Infer types from schema
type GetRoutinesParams = InferToolParams<typeof getRoutinesSchema>;
// 3. Use inferred type in handler
server.tool(
"get-routines",
"Description...",
getRoutinesSchema, // Use the schema constant
withErrorHandling(async (args: GetRoutinesParams) => {
// args is fully typed - no manual assertions needed!
const { page, pageSize } = args;
// ...
}, "get-routines"),
);Key Benefits:
- ✅ Single source of truth (Zod schema defines both validation and types)
- ✅ No manual type assertions (
args as {...}) - ✅ Automatic type updates when schemas change
- ✅ Full IDE autocomplete and type checking
DO NOT:
- ❌ Use
args as { ... }type assertions - ❌ Define parameter types separately from Zod schemas
- ❌ Use
Record<string, unknown>in handler signatures (use inferred types)
- Create new tool file in
src/tools/ - Define Zod schema with
as constassertion - Infer parameter types using
InferToolParams<typeof schema> - Implement handler with typed parameters (no manual assertions)
- Wrap with error handling using
withErrorHandlingfromsrc/utils/error-handler.ts - Format outputs using helpers in
src/utils/formatters.ts - Register tools in
src/index.ts - Add unit tests co-located with implementation
- NEVER edit files in
src/generated/directly - Regenerate API client:
pnpm run build:client - If OpenAPI spec changes, update
openapi-spec.jsonfirst - Generated types are available in
src/generated/client/types/index.ts
- Use centralized error handling from
src/utils/error-handler.ts - Wrap handlers with
withErrorHandling(fn, "context-name") - Follow existing error response patterns in tool implementations
- Error responses automatically include
isError: trueflag
- Server won't start: Check for
HEVY_API_KEYin.envfile - Integration tests failing: Expected without valid API key
- TypeScript errors in generated code: Expected - ignore these
- Build failures: Run
pnpm run checkto identify formatting/linting issues - Network errors in export-specs: Expected in sandboxed environments
- Type errors in tool handlers: Use
InferToolParams<typeof schema>instead of manual type assertions - Linter warnings about
any: Expected inwebhooks.tswhere API methods don't exist yet (see TODOs)
- Build time: 3-5 seconds
- Unit test time: 1-2 seconds
- Dependency installation: 30 seconds
- API client generation: 4-5 seconds
- Type checking: < 1 second
InferToolParams<T>: Infers TypeScript types from Zod schema objectscreateTypedToolHandler: Optional wrapper for automatic validation (MCP SDK already validates)
withErrorHandling<TParams>(fn, context): Wraps handlers with error handling while preserving parameter typescreateErrorResponse(error, context?): Creates standardized error responses
createJsonResponse(data, options?): Creates JSON-formatted MCP responsescreateTextResponse(text): Creates text-formatted MCP responsescreateEmptyResponse(message): Creates empty responses with messages
Remember: Always reference these instructions first before searching for additional information or running exploratory commands.