RunCoach

RunCoach

An MCP server that exposes a runner's own training data — computed training load, race-time predictions, and generated plans — as tools an LLM can call for grounded coaching advice.

Category
访问服务器

README

RunCoach

Live: runcoach-ruby.vercel.app · MCP endpoint https://runcoach-ruby.vercel.app/mcp

An MCP server that exposes a runner's own training data — computed training load, race-time predictions, generated plans — as tools an LLM can call.

The point is grounding. Ask any assistant "should I back off this week" and it will produce plausible coaching advice from nothing. RunCoach makes the same question resolve against the athlete's actual recorded activities, with the answer carrying the specific runs it came from:

You: How's my training going, and what could I run a half in?

Claude (calls analyze_training_status, then predict_race_time): Training is consistent and load is steady — 46.8 km/week across 17 runs in the last 28 days, with an acute-to-chronic ratio of 1.00 and no gap longer than three days. Fitness is being maintained rather than built, which is the right place to be between goals and worth changing if you have a race coming.

For the half: 1:38:48, most likely between 1:36:49 and 1:40:47. That's fitted to your own distance/time curve across your 5K (21:30, 8 July), 10K (44:50, 3 June) and half (1:38:48, 29 April) — your fatigue exponent came out at 1.059 against a population average of 1.06, so you hold pace about as well as typical as distance grows. One activity was excluded: a 3 km on 28 July recorded at a pace faster than the world record for that distance, almost certainly a GPS error.

Every figure above is real output from npm test's fixture athlete, not an illustration — the exact strings appear in test/readme.test.ts, which fails if the science moves and the README is not updated with it. Every tool returns a confidence and an explanation of how its number was derived, and the server's instructions tell the model to carry both through. A prediction stated without its basis is indistinguishable from a guess.

Tools

Tool Answers
analyze_training_status "How is my training going?" — composite readiness read from load trend, consistency and volume, with explicit caveats
get_training_load Fitness (CTL), fatigue (ATL), form (TSB) and acute:chronic ratio over a window
predict_race_time "What could I run for X?" — with the reference efforts used and the implausible ones excluded
get_recent_activities Recent runs with pace, HR and per-activity computed load
generate_training_plan A periodised plan from measured volume, or a refusal explaining why one would be unsafe

Where the numbers come from

RunCoach reimplements nothing. Every calculation is @runman/core, the fixture-tested science package that also backs the Runman web app. Asking Claude a question and loading the corresponding page run the same code against the same database.

That package is vendored here as a git submodule, and RunCoach's tests import the same fixture file Runman's tests use — so "what should this data produce" has one definition across both repos rather than two that drift.

git clone --recursive https://github.com/varun-gangadharan/runcoach
npm install
npm test          # 31 tests, no database or credentials required

Design decisions worth explaining

Tools do not take an athlete id. The API key resolves to exactly one athlete, and that id becomes the tool context. If a model could pass an athlete id, a prompt-injected instruction in an activity title could ask it to — and the payload here is somebody's personal health data. There is simply no argument by which one athlete's key can read another's data. A test asserts this stays true.

A refusal is a valid answer. When there is no data to support a prediction, the tool says so and instructs the model not to substitute an estimate. Same for plan generation: if the athlete has no recent training, or the race is three weeks away, it refuses with a reason rather than producing a plan built on a guessed baseline. Models fill gaps by default; the tool output has to actively push back.

Output is prose, not JSON. Models paraphrase sentences more faithfully than they read nested objects, and are far less likely to invent a field that was not there. Precise figures still appear — inside sentences.

Each HTTP request builds its own server instance. Sharing one across invocations in a serverless environment risks interleaving two athletes' sessions inside a warm container. That is not an acceptable failure mode here.

Deployment

The HTTP transport is a single Vercel function. Authentication is a bearer token — an API key the athlete issues from the Runman profile page, stored only as a SHA-256 hash.

vercel env add SUPABASE_URL
vercel env add SUPABASE_SERVICE_ROLE_KEY
vercel deploy --prod

Note that the Vercel entry point (api/mcp.js) is plain JavaScript re-exporting the compiled handler from dist/, rather than TypeScript importing src/. Vercel compiles a .ts function in place but leaves relative import specifiers untouched, so a .ts specifier survives into the deployed bundle and fails at runtime looking for a file that was never shipped.

After deploying, check it end to end with a real MCP client:

node --experimental-strip-types scripts/verify-deployment.ts \
  https://runcoach-ruby.vercel.app/mcp rc_live_...

That connects over the network exactly as Claude would — handshake, tool discovery, then a call to each of the five tools — and asserts the answers come back grounded rather than merely well-formed.

Then add it in any MCP client:

{
  "mcpServers": {
    "runcoach": {
      "type": "http",
      "url": "https://runcoach-ruby.vercel.app/mcp",
      "headers": { "Authorization": "Bearer rc_live_..." }
    }
  }
}

Running locally over stdio

For use against your own data on your own machine:

{
  "mcpServers": {
    "runcoach": {
      "command": "node",
      "args": ["/absolute/path/to/runcoach/dist/stdio.js"],
      "env": {
        "SUPABASE_URL": "...",
        "SUPABASE_SERVICE_ROLE_KEY": "...",
        "RUNCOACH_API_KEY": "rc_live_..."
      }
    }
  }
}

A stdio server has no requests to authenticate — it runs as a child process of the client — so the athlete is fixed at startup. That is only appropriate on the athlete's own machine, which is why the deployed transport does it per request instead.

Testing

npm test

31 tests across three layers: the tool handlers against Runman's shared fixture set (consistent runner, no-heart-rate runner, single activity, empty history, GPS-glitch history, sporadic runner, returning runner, volume spike), and an end-to-end protocol test that connects a real MCP client to a real server and drives it through tool discovery, schema validation and invocation. The third layer pins the example figures quoted in this README.

Not in v1

Garmin-sourced tools (recovery, sleep, HRV) by proxying into an upstream Garmin MCP server, gated behind the athlete having linked an account. Deliberately separable, so an athlete without Garmin sees no degradation.

Licence

MIT.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选