Skip to main content
Glama

running-coach-mcp

A small, read-only Model Context Protocol (MCP) server that connects an AI client to a personal Intervals.icu account.

Milestone 1 exposes a single tool, get_recent_runs, which returns the athlete's most recent running activities as clean, normalized JSON.

Architecture

Garmin  →  Intervals.icu  →  Next.js backend (this app)  →  MCP  →  AI client
  • Garmin — the athlete's watch/activity source, synced into Intervals.icu.

  • Intervals.icu — source of truth for activity data. Accessed read-only via HTTP Basic Auth.

  • Next.js backend (src/lib/intervals/*) — a small server-only client and domain layer that fetches raw activities and converts them into our own normalized model (src/types/activity.ts). MCP code never touches the raw Intervals.icu response shape directly.

  • MCP (src/app/api/mcp/route.ts) — exposes the domain layer as MCP tools over Streamable HTTP, via mcp-handler.

  • AI client — any MCP-compatible client (Claude Desktop, Cursor, MCP Inspector, etc.) that calls get_recent_runs.

src/
  app/
    api/
      health/route.ts   # GET /api/health — liveness + config check (no secrets, no Intervals calls)
      mcp/route.ts       # MCP endpoint (Streamable HTTP) — registers get_recent_runs
    page.tsx             # minimal info page (no UI framework needed)

  lib/
    intervals/
      auth.ts            # reads INTERVALS_API_KEY / INTERVALS_ATHLETE_ID, builds Basic Auth header
      client.ts           # server-only fetch wrapper (GET only, timeout, no-store, error mapping)
      activities.ts       # domain layer: getRecentRuns() — fetch, filter, sort, limit
      normalizers.ts       # IntervalsActivity -> RunningActivity conversion
    running/
      pace.ts             # pure pace calculation/formatting helpers

  types/
    activity.ts           # IntervalsActivity (raw) and RunningActivity (our model)

This milestone is intentionally read-only: no database, no OAuth, no write endpoints. The IntervalsClient layer is structured so that future methods (getActivity, getActivityStreams, getWellness, getCalendar, and eventually write operations) can be added without reshaping what's already here — see the comments in src/lib/intervals/client.ts.

Related MCP server: Intervals.icu MCP Server

Setup

  1. Install dependencies:

    npm install
  2. Create .env.local (see .env.example):

    INTERVALS_API_KEY=<your personal Intervals API key>
    INTERVALS_ATHLETE_ID=0

    Get your API key from Intervals.icu under Settings → Developer Settings. INTERVALS_ATHLETE_ID defaults to 0 (your own athlete) if omitted.

  3. Run the dev server:

    npm run dev
  • MCP endpoint: http://localhost:3000/api/mcp

  • Health check: http://localhost:3000/api/health

Testing the MCP endpoint with MCP Inspector

With the dev server running, in another terminal:

npx @modelcontextprotocol/inspector

This opens the Inspector UI in your browser. Connect with:

  • Transport: Streamable HTTP

  • URL: http://localhost:3000/api/mcp

Once connected, open the Tools tab, select get_recent_runs, and call it (optionally with limit and days) to see your recent runs as JSON.

The get_recent_runs tool

  • limit (optional integer, 1–20, default 5) — max number of runs to return.

  • days (optional integer, 7–365, default 90) — how far back to search.

It fetches up to 100 recent activities from Intervals.icu within the date window, filters to running types (Run, TrailRun, VirtualRun), sorts newest first, and returns up to limit normalized runs. If fewer runs exist in the window, it returns whatever is available.

Quality checks

npm run lint     # ESLint
npx tsc --noEmit # TypeScript type checking
npm run test     # unit tests (vitest)
npm run build    # production build

Milestone status

Milestone 1 (this milestone) is strictly read-only: no database, no Supabase, no OAuth, no write operations (no POST/PUT/PATCH/DELETE calls to Intervals.icu).

Future milestones will build on this foundation to add:

  • Activity streams (getActivityStreams)

  • Wellness data (getWellness)

  • Calendar / planned workouts (getCalendar)

  • Deeper training analytics

  • Eventually, workout creation/editing (createWorkout, updateWorkout, deleteWorkout) — write access, once OAuth and stronger safeguards are in place

Notes / assumptions

  • Never commit .env.local or any file containing a real API key (.env* is git-ignored; .env.example is explicitly un-ignored so the template can be committed).

Related MCP Connectors

Related MCP Servers