内容
# strava-mcp-kit
[](https://www.npmjs.com/package/@guutong/strava-mcp-kit)
[](https://www.npmjs.com/package/@guutong/strava-mcp-kit)
[](https://www.typescriptlang.org/)
[](https://github.com/Guutong/strava-mcp-kit/actions/workflows/ci.yml)
[](LICENSE)
[](https://nodejs.org)
一个适用于 [Strava v3 API](https://developers.strava.com/docs/reference/) 的 [Model Context Protocol](https://modelcontextprotocol.io) 服务器。涵盖了 [公共swagger规范](https://developers.strava.com/swagger/swagger.json) 中的所有端点 — 34个端点工具,涵盖运动员、活动、路段、路段努力、俱乐部、装备、路线、上传和流(读+写),以及 **4个元工具**,用于OAuth启动、约定和离线端点查找。每个端点工具都接受 `dry_run: true`。
服务器是手动编写的,基于规范(而不是 `swagger-codegen` 的庞大生成的客户端)。它直接使用 `fetch`,并带有 Zod 验证的工具模式,这使得运行时体积小,审计表面容易。
### 元工具概览
| 工具 | 重要原因 |
|---|---|
| `strava_api_conventions` | 一次调用返回Strava约定备忘录 — 分页迭代规则、对象表示、折线、日期、状态代码、速率限制、作用域。跳过文档站点。 |
| `strava_describe_endpoint` | 查找任意端点的参数、响应、作用域、速率限制和内联注释(分页提示、日期格式、折线提醒),无需调用。 |
| `strava_oauth_authorize_url` | 构建用户在浏览器中打开的OAuth授权URL。无需现有凭证。 |
| `strava_oauth_exchange_code` | 将授权代码兑换为访问 + 刷新令牌。启动一个全新的集成,调用两个工具。 |
## 两种模式 — 选择一种(或两种)
### 模式 1 — 最终用户(健身分析)
设置 `STRAVA_*` 环境变量,每个工具都会调用Strava。 有用的提示:
- 每周 **时间分区** 分解 → 极化 / 金字塔 / 阈值混合
- **TSS / 训练负荷** 按强度 × 时长
- **HR漂移** 在单个活动中 → 有氧脱耦
- **FTP / 阈值速度趋势** 通过重复的路段努力
- **过度训练** 从恢复HR上升的信号
- 周对周比较距离、强度和海拔
### 模式 2 — 开发人员(Strava SDK助手)
**无需凭证。** 服务器在没有任何环境变量的情况下启动,并为LLM提供所有必要的信息,以便在不切换到文档的情况下与Strava集成:
- `strava_api_conventions` — 完整的约定备忘录(分页迭代规则、对象表示、折线、日期、状态代码、作用域)。
- `strava_describe_endpoint` — 任意端点的完整OpenAPI规范切片(参数、响应、作用域、速率限制、上下文注释)。
- `dry_run: true` — 每个端点工具都接受此标志。 当为真时,服务器返回请求描述符和可复制的 `curl`、TypeScript `fetch` 和Python `requests` 代码段。 适用于读写工具 — 查看 `POST /activities` 在编写任何代码之前如何发送其 multipart 正文。
- `strava_oauth_authorize_url` + `strava_oauth_exchange_code` — 在两个工具调用中启动全新的Strava集成,无需 `curl`。
问克劳德:
> “显示我如何使用 `getActivityStreams` 调用活动 12345,带有心率和瓦特。 使用 `dry_run`。”
## 安装
发布到npm为 `@guutong/strava-mcp-kit`。 需要Node 18+。 CI在Node 20和22上运行。
直接使用 `npx`(零安装):
```sh
npx -y @guutong/strava-mcp-kit
```
或全局安装:
```sh
npm install -g @guutong/strava-mcp-kit
strava-mcp-kit # 二进制名称保持无范围
```
或从源代码开发:
```sh
git clone https://github.com/Guutong/strava-mcp-kit.git
cd strava-mcp-kit
npm install
npm run build
```
或在没有构建步骤的情况下直接运行:
```sh
npm run dev
```
打开 [官方MCP检查器](https://github.com/modelcontextprotocol/inspector) 指向此服务器:
```sh
npm run inspect
```
## OAuth设置(一次性)
### 选项A — 从MCP主机内部启动(推荐)
一旦服务器连接到您的LLM客户端,请询问它:
```
1. 使用strava_oauth_authorize_url,client_id="12345",scopes=["read","activity:read_all","profile:read_all"]。
打开返回的URL,在浏览器中点击授权,然后粘贴 `code` 查询参数回这里。
2. 然后使用strava_oauth_exchange_code,client_id,client_secret和代码。
保存它返回的refresh_token。
```
LLM会引导您完成重定向(重定向URL本身无法加载 — 这是预期的)。您最终会获得 `access_token`、`refresh_token`、`expires_at` 和运动员摘要在一个工具结果中。 在MCP主机配置文件中设置刷新令牌,您就完成了。
### 选项B — 手动 `curl`
1. 在 <https://www.strava.com/settings/api> 上创建一个Strava API应用程序。 将 `Authorization Callback Domain` 设置为 `localhost` 进行开发。
2. 打开此URL,在浏览器中替换 `YOUR_CLIENT_ID`:
```
https://www.strava.com/oauth/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=http://localhost/exchange_token&approval_prompt=force&scope=read,activity:read_all,profile:read_all,activity:write,profile:write
```
3. 从重定向URL栏复制 `code`。
4. 兑换:
```sh
curl -X POST https://www.strava.com/api/v3/oauth/token \
-d client_id=YOUR_CLIENT_ID -d client_secret=YOUR_CLIENT_SECRET \
-d code=AUTH_CODE -d grant_type=authorization_code
```
### 作用域
| 作用域 | 所需功能 |
|---|---|
| `read` | 公共个人资料,公共俱乐部,公共路段 |
| `activity:read_all` | 读取所有活动,包括私人活动 |
| `profile:read_all` | 运动员体重,FTP,心率最大值 |
| `activity:write` | 标记为 `(w)` 的工具 — 创建/更新活动,标记路段,上传 |
| `profile:write` | `strava_update_logged_in_athlete` |
如果您只需要读取权限,请删除写入作用域。
### 配置环境变量
```sh
export STRAVA_CLIENT_ID=12345
export STRAVA_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
export STRAVA_REFRESH_TOKEN=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
```
快捷方式用于短期实验:直接设置 `STRAVA_ACCESS_TOKEN`。令牌在 ~6 小时后过期。
## 与Claude Desktop一起使用
编辑 `~/Library/Application Support/Claude/claude_desktop_config.json`(macOS)或 `%APPDATA%\Claude\claude_desktop_config.json`(Windows):
```json
{
"mcpServers": {
"strava": {
"command": "npx",
"args": ["-y", "@guutong/strava-mcp-kit"],
"env": {
"STRAVA_CLIENT_ID": "12345",
"STRAVA_CLIENT_SECRET": "your-client-secret",
"STRAVA_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}
```
如果您从源代码克隆,请指向本地构建:
```json
{
"command": "node",
"args": ["/absolute/path/to/strava-mcp-kit/dist/index.js"]
}
```
重启Claude Desktop,并验证 `strava_*` 工具是否出现在工具面板中。
## 与Claude Code一起使用
```sh
claude mcp add strava \
--env STRAVA_CLIENT_ID=... \
--env STRAVA_CLIENT_SECRET=... \
--env STRAVA_REFRESH_TOKEN=... \
-- node /absolute/path/to/strava-mcp-kit/dist/index.js
```
## 工具(共38个)
`(w)` 标记需要 `activity:write` 或 `profile:write` 作用域的工具。
| 分组 | 工具 | 用途 |
|---|---|---|
| **元** | `strava_api_conventions` | 返回Strava约定备忘录(分页、日期、折线、状态代码、作用域)。 无需凭证。 |
| | `strava_describe_endpoint` | 完整的OpenAPI规范切片(参数、响应、作用域、速率限制、上下文注释) — 无需凭证 |
| | `strava_oauth_authorize_url` | 为任何 `client_id` 和作用域集构建OAuth授权URL |
| | `strava_oauth_exchange_code` | 将授权代码兑换为访问 + 刷新令牌 |
| **运动员** | `strava_get_logged_in_athlete` | 个人资料、体重、FTP、心率最大值 |
| | `strava_update_logged_in_athlete` (w) | 更新运动员体重 |
| | `strava_get_logged_in_athlete_zones` | 心率/功率区 |
| | `strava_get_athlete_stats` | 年度和所有时间总计 |
| **活动** | `strava_get_logged_in_athlete_activities` | 列出活动(使用 `after` / `before` epoch进行过滤) |
| | `strava_get_activity_by_id` | 详细活动 |
| | `strava_create_activity` (w) | 创建手动活动 |
| | `strava_update_activity_by_id` (w) | 更新名称、运动类型、装备等 |
| | `strava_get_activity_laps` | 圈数据 |
| | `strava_get_activity_zones` | 时间分区拆分(仅限Strava Summit) |
| | `strava_get_activity_streams` | 原始时间序列(心率、瓦特、踏频、纬度经度、海拔...) |
| | `strava_get_activity_comments` | 评论 |
| | `strava_get_activity_kudoers` | 给出赞的运动员 |
| **路段** | `strava_get_segment_by_id` | 路段详情 |
| | `strava_get_starred_segments` | 星标路段 |
| | `strava_star_segment` (w) | 星标或取消星标路段 |
| | `strava_explore_segments` | 在边界框内查找路段 |
| | `strava_get_segment_streams` | 流 |
| **路段努力** | `strava_get_segment_efforts` | 您在路段上的所有努力随时间变化 |
| | `strava_get_segment_effort_by_id` | 单次努力 |
| | `strava_get_segment_effort_streams` | 流 |
| **俱乐部** | `strava_get_logged_in_athlete_clubs`, `strava_get_club_by_id`, `strava_get_club_members`, `strava_get_club_admins`, `strava_get_club_activities` | 俱乐部信息 |
| **装备** | `strava_get_gear_by_id` | 自行车或鞋子详情 |
| **路线** | `strava_get_routes_by_athlete`, `strava_get_route_by_id`, `strava_get_route_streams` | 路线 |
| | `strava_export_route_gpx`, `strava_export_route_tcx` | 导出路线到GPS文件 |
| **上传** | `strava_create_upload` (w) | 上传GPX/TCX/FIT活动(`dry_run`不读取文件) |
| | `strava_get_upload_by_id` | 轮询上传状态 |
每个端点工具都接受一个特殊的 `dry_run: boolean` 参数。当为真时,工具不会调用Strava API;它会返回请求描述符和可复制的 `curl`、TypeScript `fetch` 和Python `requests` 代码段。
### 支持的流键
`time`, `distance`, `latlng`, `altitude`, `velocity_smooth`, `heartrate`, `cadence`, `watts`, `temp`, `moving`, `grade_smooth`
## Strava API 约定
LLM应该知道的最重要规则:**分页通过迭代直到收到空数组** — Strava可以返回少于 `per_page` 个项目,即使还有更多页面剩余。 完整的约定集(对象表示方式 via `resource_state`,折线编码,ISO-8601日期,速率限制,作用域)位于 `strava_api_conventions` 工具后面。 调用它一次,并在集成会话开始时,LLM就会拥有它需要的规则。
`strava_describe_endpoint` 还在 `notes` 中嵌入了每个端点相关的约定子集 — 适用时分页警告,日期参数的ISO-8601提醒,上传正文的多部分提示,返回 `map.polyline` 的折线提醒。
## 示例提示 — 开发模式(无需凭证)```
调用 `strava_api_conventions` 并显示分页规则和速率限制。
```
```
使用 `strava_describe_endpoint` 获取 `getActivityStreams`。 显示参数、响应形状和所需的OAuth作用域。
```
```
使用 `strava_get_activity_streams`,带有 `dry_run=true`,`id=12345678`,`keys=["heartrate","watts"]`. 提供Python `requests` 代码段。
```
```
我想上传FIT文件。 使用 `strava_create_upload`,带有 `dry_run`,`file_path="/tmp/ride.fit"`,`data_type="fit"`。 显示多部分正文形状,以便我可以复制它。
```
示例dry-run输出:
```json
{
"request": {
"method": "GET",
"url": "https://www.strava.com/api/v3/activities/12345678/streams?keys=heartrate%2Cwatts&key_by_type=true",
"headers": {
"Authorization": "Bearer <STRAVA_ACCESS_TOKEN>",
"Accept": "application/json"
}
},
"snippets": {
"curl": "curl -X GET '...' -H 'Authorization: Bearer <STRAVA_ACCESS_TOKEN>' ...",
"typescript": "const res = await fetch(...);\nconst data = await res.json();",
"python": "import requests\n\nurl = '...'\n..."
},
"notes": [
"Authorization header uses a placeholder. Replace <STRAVA_ACCESS_TOKEN> with a real bearer token."
]
}
```
## 示例提示 — 最终用户模式(健身分析)```
拉取我最近30天的活动。 计算总距离、时间和每周负荷。 标记任何周对周跳跃超过10%。 估计我在第2区与4-5区的活动时间百分比。
```
```
找到我这个月骑过的路段。 比较我的最后5次努力。 监控配速、平均心率和心率漂移。 我是否变得更强还是更疲劳?
```
```
对于活动 12345678,获取心率 + 瓦特 + 速度平滑流。 构建时间分区直方图。 将 TSS 与我的FTP相比,从 `strava_get_logged_in_athlete` 获取。
```## 环境变量
| 变量 | 必需 | 描述 |
|---|---|---|
| `STRAVA_ACCESS_TOKEN` | 二者之一或以下三项 | 短效令牌(~6小时) |
| `STRAVA_CLIENT_ID` | 刷新令牌模式 | 从您的Strava应用设置。 |
| `STRAVA_CLIENT_SECRET` | 刷新令牌模式 | 从您的Strava应用设置。 |
| `STRAVA_REFRESH_TOKEN` | 刷新令牌模式 | 长效刷新令牌 |
| `STRAVA_API_BASE_URL` | 否 | 覆盖API基址。 默认 `https://www.strava.com/api/v3` |
如果您仅使用开发模式(`dry_run` + `describe_endpoint`),您可以留空所有这些设置。实时工具返回一个明确的 `Strava credentials missing` 错误,如果您忘记传递 `dry_run`。
## 项目布局
```
src/
├── index.ts 入口 — 启动 McpServer,带有stdio传输
├── client.ts REST 客户端,OAuth 刷新,buildRequest()(由dry_run使用)
├── snippets.ts 将构建的请求格式化为curl / TypeScript / Python
├── spec.ts 加载strava-spec.json,公开describeOperation()并带有注释
├── conventions.ts 约定备忘录文本 + OAuth 帮助程序(URL生成器,代码交换)
├── tools.ts Zod类型工具注册表:34个端点工具 + 4个元工具
└── strava-spec.json Strava swagger 2.0 规范(在构建时复制到dist/)
tests/ Vitest单元测试(无网络,无fetch模拟)
.github/workflows/ CI(类型检查,lint,测试,构建)+ 标签门控npm发布
```
## 贡献
欢迎提交拉取请求。请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 了解开发设置、每个工具使用的描述符模式,以及添加新Strava端点的逐步配方。贡献者需遵守 [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)。对于帮助处理存储库的AI助手,请参阅 [CLAUDE.md](CLAUDE.md)。
贡献者快速入门:
```sh
npm install
npm run dev # 使用tsx运行服务器
npm run inspect # MCP检查器UI
npm run lint:fix # Biome格式 + lint并自动修复
npm test # vitest
```
CI(`.github/workflows/ci.yml`)在每次PR提交时运行类型检查、lint、测试和构建,针对Node 20和22。标签推送(`v*`)还会发布到npm — 在存储库秘密中设置`NPM_TOKEN`以启用此功能。
## 故障排除
**`Strava credentials missing`**
未设置环境变量,且未传递`dry_run`。配置`STRAVA_ACCESS_TOKEN`或`CLIENT_ID + CLIENT_SECRET + REFRESH_TOKEN`三项,或传递`dry_run: true`。
**`StravaApiError 401`**
访问令牌已过期且没有配置刷新令牌,或者刷新令牌已被撤销。重新运行OAuth流程。
**`StravaApiError 403`**
缺少作用域。读取私人活动需要`activity:read_all`。写入需要`activity:write`或`profile:write`。
**`StravaApiError 429`**
达到速率限制。Strava 允许每个访问令牌每15分钟100个请求和每天1000个请求,在所有端点之间共享。 等待下一个15分钟窗口或00:00 UTC。
## 限制
- `strava_get_activity_zones` 需要运动员账户上的Strava Summit。
- 流端点默认为 `key_by_type=true`(符合Strava自己的建议)。
- `strava_get_athlete_stats` 仅返回经过身份验证的运动员ID的数据。
- 捆绑的 `strava-spec.json` 反映了发布时的Strava swagger。如果API获得新的端点,请刷新规范并添加一个工具 — 参见 `CONTRIBUTING.md`。
## 许可证
MIT — 参见 [LICENSE](LICENSE)。
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器