@einlogic/mcp-fitbit 1.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -209
- package/build/account.js +32 -0
- package/build/activities.js +12 -14
- package/build/activity-timeseries.js +14 -20
- package/build/auth-tools.js +31 -0
- package/build/auth.js +197 -105
- package/build/azm-timeseries.js +4 -8
- package/build/compat/api.js +136 -0
- package/build/compat/convert.js +434 -0
- package/build/compat/tools.js +432 -0
- package/build/config.js +102 -32
- package/build/daily-activity.js +35 -7
- package/build/data-points.js +44 -0
- package/build/heart-rate.js +28 -98
- package/build/hrv.js +15 -15
- package/build/index.js +36 -20
- package/build/nutrition.js +41 -152
- package/build/profile.js +6 -9
- package/build/sleep.js +12 -16
- package/build/utils.js +193 -50
- package/build/weight.js +12 -13
- package/package.json +74 -74
package/README.md
CHANGED
|
@@ -1,209 +1,185 @@
|
|
|
1
|
-
# Fitbit MCP Connector for AI
|
|
2
|
-
|
|
3
|
-
[ for improvement opportunities and technical details.
|
|
209
|
-
|
|
1
|
+
# Google Health (Fitbit) MCP Connector for AI
|
|
2
|
+
|
|
3
|
+
[](https://github.com/TheDigitalNinja/mcp-fitbit/actions/workflows/ci.yml)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
> Connect AI assistants to your Fitbit / Pixel Watch health data through the **Google Health API**
|
|
7
|
+
|
|
8
|
+
The legacy Fitbit Web API was shut down on **2 November 2026**. Google replaced it with the
|
|
9
|
+
[Google Health API](https://developers.google.com/health), which uses Google OAuth 2.0. This server now talks to
|
|
10
|
+
the Google Health API and returns its raw JSON.
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
🏃 **Exercise & Activities** - Workout sessions and daily activity roll-ups
|
|
15
|
+
😴 **Sleep Analysis** - Sleep sessions
|
|
16
|
+
⚖️ **Weight Tracking** - Weight data points
|
|
17
|
+
❤️ **Heart Rate Data** - Samples, daily roll-ups and HRV
|
|
18
|
+
🍎 **Nutrition Logs** - Food log, calories, macros and water
|
|
19
|
+
👤 **Profile & Devices** - Profile, settings, identity and paired trackers
|
|
20
|
+
|
|
21
|
+
*Ask your AI things like: "Show me my sleep patterns this week" or "What's my step count trend this month?"*
|
|
22
|
+
|
|
23
|
+
## ⚠️ Breaking changes from the Fitbit version
|
|
24
|
+
|
|
25
|
+
- You must create a **Google Cloud** OAuth client (see below) and re-authorize. Old Fitbit tokens cannot be reused.
|
|
26
|
+
- Credentials are now `GOOGLE_HEALTH_CLIENT_ID` / `GOOGLE_HEALTH_CLIENT_SECRET` (the old `FITBIT_*` names still work as a fallback).
|
|
27
|
+
- The token file is now `.google-health-token.json` (the old `.fitbit-token.json` is ignored and can be deleted).
|
|
28
|
+
- Responses are the Google Health API's JSON, not Fitbit's. Fitbit-style `period` (`1d`, `7d`, ...) parameters are gone: tools take explicit start/end times, as the API does.
|
|
29
|
+
- `get_activity_goals` was removed: the Google Health API has no goals endpoint.
|
|
30
|
+
- New tools: `get_identity`, `get_settings`, `list_paired_devices`, `list_data_points`, `reconcile_data_points`.
|
|
31
|
+
|
|
32
|
+
## Setup
|
|
33
|
+
|
|
34
|
+
### 1. Link your account to Google Health
|
|
35
|
+
|
|
36
|
+
In the **Google Health mobile app**, tap **Sign in with Google** and choose the Google Account to use (this creates a
|
|
37
|
+
Google Health profile or migrates your existing Fitbit account). Without this, the API returns `ACCOUNT_NOT_LINKED`.
|
|
38
|
+
|
|
39
|
+
### 2. Create Google OAuth credentials
|
|
40
|
+
|
|
41
|
+
1. In the [Google Cloud Console](https://console.cloud.google.com/), create (or pick) a project.
|
|
42
|
+
2. [Enable the Google Health API](https://console.developers.google.com/apis/library/health.googleapis.com).
|
|
43
|
+
3. Create an **OAuth client ID** of type **Web application** and add the authorized redirect URI
|
|
44
|
+
`http://localhost:3000/callback`. Note the **Client ID** and **Client secret**.
|
|
45
|
+
4. On the [OAuth consent screen / Audience](https://console.developers.google.com/auth/audience) page, keep the user
|
|
46
|
+
type **External** and add your Google Account under **Test users**.
|
|
47
|
+
5. On the [Data Access](https://console.developers.google.com/auth/scopes) page, add these scopes (all read-only):
|
|
48
|
+
- `https://www.googleapis.com/auth/googlehealth.activity_and_fitness.readonly`
|
|
49
|
+
- `https://www.googleapis.com/auth/googlehealth.health_metrics_and_measurements.readonly`
|
|
50
|
+
- `https://www.googleapis.com/auth/googlehealth.sleep.readonly`
|
|
51
|
+
- `https://www.googleapis.com/auth/googlehealth.nutrition.readonly`
|
|
52
|
+
- `https://www.googleapis.com/auth/googlehealth.profile.readonly`
|
|
53
|
+
- `https://www.googleapis.com/auth/googlehealth.settings.readonly`
|
|
54
|
+
|
|
55
|
+
> **Refresh tokens in Testing mode expire after 7 days.** For a personal tool, set the app's publishing status to
|
|
56
|
+
> **In production** (an unverified app is limited to 100 users and shows a warning screen at consent, which is fine
|
|
57
|
+
> for your own account). Verification is only required beyond that.
|
|
58
|
+
|
|
59
|
+
### 3. Install and configure
|
|
60
|
+
|
|
61
|
+
**From npm:**
|
|
62
|
+
```bash
|
|
63
|
+
npm install -g mcp-fitbit
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**From source:**
|
|
67
|
+
```bash
|
|
68
|
+
git clone https://github.com/TheDigitalNinja/mcp-fitbit
|
|
69
|
+
cd mcp-fitbit
|
|
70
|
+
npm install
|
|
71
|
+
npm run build
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Create a `.env` file in the package directory (the server prints the expected path if it is missing):
|
|
75
|
+
```bash
|
|
76
|
+
GOOGLE_HEALTH_CLIENT_ID=your_client_id_here
|
|
77
|
+
GOOGLE_HEALTH_CLIENT_SECRET=your_client_secret_here
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4. Claude Desktop
|
|
81
|
+
|
|
82
|
+
Add to `claude_desktop_config.json`:
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"mcpServers": {
|
|
86
|
+
"google-health": {
|
|
87
|
+
"command": "mcp-fitbit",
|
|
88
|
+
"args": [],
|
|
89
|
+
"env": {
|
|
90
|
+
"GOOGLE_HEALTH_CLIENT_ID": "your_client_id_here",
|
|
91
|
+
"GOOGLE_HEALTH_CLIENT_SECRET": "your_client_secret_here"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
For a local build use `"command": "node", "args": ["C:\\path\\to\\mcp-fitbit\\build\\index.js"]`.
|
|
98
|
+
|
|
99
|
+
Config file locations:
|
|
100
|
+
- Windows: `%AppData%\Claude\claude_desktop_config.json`
|
|
101
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
102
|
+
- Linux: `~/.config/Claude/claude_desktop_config.json`
|
|
103
|
+
|
|
104
|
+
### First Run Authorization
|
|
105
|
+
|
|
106
|
+
1. The server opens your browser to `http://localhost:3000/auth`
|
|
107
|
+
2. Sign in to Google and grant the requested Google Health permissions
|
|
108
|
+
3. The server verifies your Google Health link (`/v4/users/me/identity`) and saves the token
|
|
109
|
+
4. Your AI can now access your data. Access tokens refresh automatically.
|
|
110
|
+
|
|
111
|
+
## Available Tools
|
|
112
|
+
|
|
113
|
+
Time parameters follow the API: ranges are **start inclusive, end exclusive**. Samples (`get_weight`, `get_heart_rate`)
|
|
114
|
+
use RFC 3339 timestamps (e.g. `2026-03-04T00:00:00Z`); sessions use civil time (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SS`).
|
|
115
|
+
List tools accept `pageSize` and `pageToken` (use `nextPageToken` from the response).
|
|
116
|
+
|
|
117
|
+
| Tool | Google Health API | Parameters |
|
|
118
|
+
|------|-------------------|------------|
|
|
119
|
+
| `get_weight` | `weight` data points | `startTime`, optional `endTime` (RFC 3339) |
|
|
120
|
+
| `get_sleep_by_date_range` | `sleep` data points (max 25/page) | `startTime`, optional `endTime` (civil) |
|
|
121
|
+
| `get_exercises` | `exercise` data points (max 25/page) | `startTime`, optional `endTime` (civil) |
|
|
122
|
+
| `get_daily_activity_summary` | daily roll-ups of steps, distance, total calories, active zone minutes, floors | `date` |
|
|
123
|
+
| `get_activity_timeseries` | `dailyRollUp` (max 90 days; 14 for calories/active minutes) | `dataType`, `startDate`, `endDate` |
|
|
124
|
+
| `get_azm_timeseries` | `dailyRollUp` of `active-zone-minutes` | `startDate`, `endDate` |
|
|
125
|
+
| `get_heart_rate` | `heart-rate` samples | `startTime`, optional `endTime` (RFC 3339) |
|
|
126
|
+
| `get_heart_rate_by_date_range` | `dailyRollUp` of `heart-rate` (max 14 days) | `startDate`, `endDate` |
|
|
127
|
+
| `get_hrv` / `get_hrv_by_date_range` | `daily-heart-rate-variability` | `date` / `startDate`, `endDate` |
|
|
128
|
+
| `get_food_log` | `nutrition-log` data points | `startTime`, optional `endTime` (civil) |
|
|
129
|
+
| `get_nutrition` / `get_nutrition_by_date_range` | `dailyRollUp` of `nutrition-log` / `hydration-log` | `dataType`, `date` / `startDate`, `endDate` |
|
|
130
|
+
| `get_profile` | `users/me/profile` | none |
|
|
131
|
+
| `get_identity` | `users/me/identity` | none |
|
|
132
|
+
| `get_settings` | `users/me/settings` | none |
|
|
133
|
+
| `list_paired_devices` | `users/me/pairedDevices` | none |
|
|
134
|
+
| `reauthenticate` | not an API call: deletes the local tokens (memory + `.google-health-token.json`) and opens a new Google sign-in; new tokens are saved on approval, no restart needed. Cancels a pending sign-in. Confirm with `get_identity` | none |
|
|
135
|
+
| `list_data_points` | any of the 38 data types | `dataType`, optional raw `filter` |
|
|
136
|
+
| `reconcile_data_points` | `dataPoints:reconcile` for any data type | `dataType`, optional `filter`, `dataSourceFamily` |
|
|
137
|
+
|
|
138
|
+
**Activity data types:** `steps`, `distance`, `total-calories`, `active-energy-burned`, `floors`, `active-minutes`
|
|
139
|
+
|
|
140
|
+
See the [data types reference](https://developers.google.com/health/data-types) for everything `list_data_points` can query.
|
|
141
|
+
|
|
142
|
+
## Fitbit-compatible output mode
|
|
143
|
+
|
|
144
|
+
Set `GOOGLE_HEALTH_OUTPUT_MODE=fitbit` (in the Claude Desktop `env` block or `.env`) to make the overlapping tools behave
|
|
145
|
+
like the old Fitbit Web API server, so prompts and projects written against it keep working. The default (`google`) mode
|
|
146
|
+
is unchanged. In `fitbit` mode these tools are replaced by Fitbit-shaped versions (same names, `period` parameters and
|
|
147
|
+
**inclusive** end dates); everything else (`get_profile`, `get_exercises`, `get_identity`, `get_settings`,
|
|
148
|
+
`list_paired_devices`, `list_data_points`, `reconcile_data_points`) stays a Google proxy.
|
|
149
|
+
|
|
150
|
+
| Tool | Fitbit-style response | Notes |
|
|
151
|
+
|------|----------------------|-------|
|
|
152
|
+
| `get_weight` (`period`, `date?`) | `{ weight: [{ date, time, logId, weight }] }` | Pounds (follows the account unit). Duplicate weigh-ins from several sources are collapsed to one. No `bmi`/`fat`. |
|
|
153
|
+
| `get_sleep_by_date_range` | `{ sleep: [{ dateOfSleep, minutesAsleep, timeInBed, levels.summary.{deep,light,rem,wake} }] }` | Max 100 days. No sleep score or efficiency (the Fitbit API had neither). |
|
|
154
|
+
| `get_daily_activity_summary` | `{ activities: [], goals: {}, summary: { caloriesOut, steps, distances, floors, *ActiveMinutes, restingHeartRate, heartRateZones } }` | Miles. Exercise sessions and goals are not included. |
|
|
155
|
+
| `get_heart_rate` (`period`) / `get_heart_rate_by_date_range` | `{ 'activities-heart': [{ dateTime, value: { restingHeartRate, heartRateZones } }] }` | Zones: Out of Range / Fat Burn / Cardio / Peak with `min`, `max`, `minutes`, `caloriesOut`. |
|
|
156
|
+
| `get_hrv` / `get_hrv_by_date_range` | `{ hrv: [{ dateTime, value: { dailyRmssd, deepRmssd } }] }` | Milliseconds; max 30 days. |
|
|
157
|
+
| `get_activity_timeseries` | `{ 'activities-<resource>': [{ dateTime, value: '<string>' }] }` | `steps`, `distance`, `calories`, `activityCalories` (+ `tracker/` variants). `caloriesBMR` is not available. |
|
|
158
|
+
| `get_azm_timeseries` | `{ 'activities-active-zone-minutes': [...] }` | Total + fat burn / cardio / peak. |
|
|
159
|
+
| `get_food_log` / `get_nutrition` / `get_nutrition_by_date_range` | Fitbit food-log summary / `foods-log-caloriesIn`, `foods-log-water` | No individual food entries or goals. |
|
|
160
|
+
|
|
161
|
+
How the numbers are mapped (verified against a real day of Fitbit-era data):
|
|
162
|
+
|
|
163
|
+
- **Heart-rate zones:** Fitbit's Out of Range / Fat Burn / Cardio / Peak are Google's Light / Moderate / Vigorous / Peak.
|
|
164
|
+
Fat Burn minutes equal Active Zone Minutes fat burn (1x); Cardio and Peak minutes are Active Zone Minutes divided by 2
|
|
165
|
+
(they count double). Out of Range comes from time-in-zone. Calories per zone come from `calories-in-heart-rate-zone`
|
|
166
|
+
and the bpm bounds from the account's own `daily-heart-rate-zones`.
|
|
167
|
+
- **Sleep:** stage minutes come straight from Google's stage summary, which matched Fitbit's deep/REM numbers exactly.
|
|
168
|
+
The wake date (`dateOfSleep`) is derived from the sleep end time and its UTC offset.
|
|
169
|
+
- **Units and time zone:** weight, distance and water follow the account's Google Health unit settings; days are
|
|
170
|
+
interpreted in the account time zone (override with `COMPAT_TIME_ZONE`).
|
|
171
|
+
- **Known small differences:** Google re-derives some values, so a historical day can differ slightly from what Fitbit
|
|
172
|
+
returned at the time (e.g. a few steps or 1 bpm of resting heart rate). Fields that could not be provided are listed in a
|
|
173
|
+
top-level `_compat.notes` array when relevant.
|
|
174
|
+
|
|
175
|
+
## Development
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
npm run build # Compile TypeScript
|
|
179
|
+
npm test # Run tests
|
|
180
|
+
npm run lint # Check code quality
|
|
181
|
+
npm run format # Fix formatting
|
|
182
|
+
npm run dev # Run with MCP inspector
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Architecture:** See [CLAUDE.md](CLAUDE.md) and [MIGRATION_PLAN.md](MIGRATION_PLAN.md).
|
package/build/account.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { registerTool, handleGoogleHealthCall } from './utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Registers account-level tools: identity, settings and paired devices.
|
|
4
|
+
* @param server The McpServer instance.
|
|
5
|
+
* @param getAccessTokenFn Function to retrieve the current access token.
|
|
6
|
+
*/
|
|
7
|
+
export function registerAccountTools(server, getAccessTokenFn) {
|
|
8
|
+
registerTool(server, {
|
|
9
|
+
name: 'get_identity',
|
|
10
|
+
description: "Get the raw JSON response for the user's Google Health identity (legacyUserId and healthUserId). Returns an error if the Google Account is not linked to Google Health.",
|
|
11
|
+
parametersSchema: {},
|
|
12
|
+
handler: async () => handleGoogleHealthCall('users/me/identity', getAccessTokenFn, {
|
|
13
|
+
errorContext: 'identity',
|
|
14
|
+
}),
|
|
15
|
+
});
|
|
16
|
+
registerTool(server, {
|
|
17
|
+
name: 'get_settings',
|
|
18
|
+
description: "Get the raw JSON response for the user's Google Health settings (units, locale, time zone, stride settings).",
|
|
19
|
+
parametersSchema: {},
|
|
20
|
+
handler: async () => handleGoogleHealthCall('users/me/settings', getAccessTokenFn, {
|
|
21
|
+
errorContext: 'settings',
|
|
22
|
+
}),
|
|
23
|
+
});
|
|
24
|
+
registerTool(server, {
|
|
25
|
+
name: 'list_paired_devices',
|
|
26
|
+
description: 'Get the raw JSON response listing the trackers and smartwatches paired to the user (device version and type, battery level, last sync time, features).',
|
|
27
|
+
parametersSchema: {},
|
|
28
|
+
handler: async () => handleGoogleHealthCall('users/me/pairedDevices', getAccessTokenFn, {
|
|
29
|
+
errorContext: 'paired devices',
|
|
30
|
+
}),
|
|
31
|
+
});
|
|
32
|
+
}
|
package/build/activities.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
-
import { registerTool, CommonSchemas,
|
|
2
|
-
// --- Tool Registration ---
|
|
1
|
+
import { registerTool, CommonSchemas, listDataPoints, buildRangeFilter, } from './utils.js';
|
|
3
2
|
/**
|
|
4
|
-
* Registers
|
|
3
|
+
* Registers the exercise tool with the MCP server (Google Health `exercise` data type).
|
|
5
4
|
* @param server The McpServer instance.
|
|
6
5
|
* @param getAccessTokenFn Function to retrieve the current access token.
|
|
7
6
|
*/
|
|
8
7
|
export function registerActivitiesTool(server, getAccessTokenFn) {
|
|
9
8
|
registerTool(server, {
|
|
10
9
|
name: 'get_exercises',
|
|
11
|
-
description:
|
|
10
|
+
description: 'Get the raw JSON response for exercise sessions from the Google Health API (data type "exercise"), filtered by the civil start time. Requires a start civil time; the end is optional and exclusive. Note: the API returns at most 25 exercises per page; use pageToken to continue.',
|
|
12
11
|
parametersSchema: {
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
startTime: CommonSchemas.civilStartTime,
|
|
13
|
+
endTime: CommonSchemas.civilEndTime,
|
|
14
|
+
pageSize: CommonSchemas.pageSize,
|
|
15
|
+
pageToken: CommonSchemas.pageToken,
|
|
15
16
|
},
|
|
16
|
-
handler: async ({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
errorContext: `after date '${afterDate}'`
|
|
22
|
-
});
|
|
23
|
-
}
|
|
17
|
+
handler: async ({ startTime, endTime, pageSize, pageToken, }) => listDataPoints('exercise', getAccessTokenFn, {
|
|
18
|
+
filter: buildRangeFilter('exercise.interval.civil_start_time', startTime, endTime),
|
|
19
|
+
pageSize,
|
|
20
|
+
pageToken,
|
|
21
|
+
}, `exercise from '${startTime}'${endTime ? ` to '${endTime}'` : ''}`),
|
|
24
22
|
});
|
|
25
23
|
}
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { registerTool, CommonSchemas,
|
|
2
|
+
import { registerTool, CommonSchemas, dailyRollUp } from './utils.js';
|
|
3
|
+
export const ACTIVITY_DATA_TYPES = [
|
|
4
|
+
'steps',
|
|
5
|
+
'distance',
|
|
6
|
+
'total-calories',
|
|
7
|
+
'active-energy-burned',
|
|
8
|
+
'floors',
|
|
9
|
+
'active-minutes',
|
|
10
|
+
];
|
|
3
11
|
/**
|
|
4
12
|
* Registers an activity time series tool with the MCP server.
|
|
5
13
|
* @param server The McpServer instance.
|
|
@@ -8,28 +16,14 @@ import { registerTool, CommonSchemas, handleFitbitApiCall } from './utils.js';
|
|
|
8
16
|
export function registerActivityTimeSeriesTool(server, getAccessTokenFn) {
|
|
9
17
|
registerTool(server, {
|
|
10
18
|
name: 'get_activity_timeseries',
|
|
11
|
-
description: "Get the raw JSON response for activity
|
|
19
|
+
description: "Get the raw JSON response for daily activity roll-ups from the Google Health API (dailyRollUp) over a civil date range. Supports data types 'steps', 'distance', 'total-calories', 'active-energy-burned', 'floors', 'active-minutes'. startDate is inclusive, endDate is exclusive. Note: The API allows at most 90 days per request (14 days for 'total-calories' and 'active-minutes').",
|
|
12
20
|
parametersSchema: {
|
|
13
|
-
|
|
14
|
-
.enum(
|
|
15
|
-
'steps',
|
|
16
|
-
'distance',
|
|
17
|
-
'calories',
|
|
18
|
-
'activityCalories',
|
|
19
|
-
'caloriesBMR',
|
|
20
|
-
'tracker/activityCalories',
|
|
21
|
-
'tracker/calories',
|
|
22
|
-
'tracker/distance'
|
|
23
|
-
])
|
|
24
|
-
.describe("Activity resource to retrieve (e.g., 'steps', 'distance', 'calories')"),
|
|
21
|
+
dataType: z
|
|
22
|
+
.enum(ACTIVITY_DATA_TYPES)
|
|
23
|
+
.describe("Activity data type to roll up (e.g., 'steps', 'distance')"),
|
|
25
24
|
startDate: CommonSchemas.startDate,
|
|
26
25
|
endDate: CommonSchemas.endDate,
|
|
27
26
|
},
|
|
28
|
-
handler: async ({
|
|
29
|
-
const endpoint = `activities/${resourcePath}/date/${startDate}/${endDate}.json`;
|
|
30
|
-
return handleFitbitApiCall(endpoint, { resourcePath, startDate, endDate }, getAccessTokenFn, {
|
|
31
|
-
errorContext: `resource '${resourcePath}' from ${startDate} to ${endDate}`
|
|
32
|
-
});
|
|
33
|
-
}
|
|
27
|
+
handler: async ({ dataType, startDate, endDate, }) => dailyRollUp(dataType, getAccessTokenFn, { startDate, endDate }, `data type '${dataType}' from ${startDate} to ${endDate}`),
|
|
34
28
|
});
|
|
35
29
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { registerTool, createSuccessResponse, createErrorResponse, } from './utils.js';
|
|
2
|
+
/**
|
|
3
|
+
* Registers the re-authorization tool.
|
|
4
|
+
* @param server The McpServer instance.
|
|
5
|
+
* @param reauthenticate Clears the local tokens and starts a new OAuth flow.
|
|
6
|
+
*/
|
|
7
|
+
export function registerAuthTools(server, reauthenticate) {
|
|
8
|
+
registerTool(server, {
|
|
9
|
+
name: 'reauthenticate',
|
|
10
|
+
description: "Clear this MCP server's locally stored Google Health tokens (memory and .google-health-token.json) and start a new Google sign-in. A browser window opens for consent; once approved, new tokens are saved and the other tools work again without restarting the server. Use when the token has expired (testing-mode refresh tokens last 7 days), was revoked, or the wrong Google account is connected. Cancels any sign-in that is still pending. Verify afterwards with get_identity.",
|
|
11
|
+
parametersSchema: {},
|
|
12
|
+
handler: async () => {
|
|
13
|
+
try {
|
|
14
|
+
const { tokenFileCleared, authUrl } = await reauthenticate();
|
|
15
|
+
if (!authUrl) {
|
|
16
|
+
return createErrorResponse('Local tokens were cleared, but the authorization flow could not start. Check GOOGLE_HEALTH_CLIENT_ID / GOOGLE_HEALTH_CLIENT_SECRET and that the OAuth callback port is free, then call reauthenticate again.');
|
|
17
|
+
}
|
|
18
|
+
return createSuccessResponse({
|
|
19
|
+
status: 'authorization_started',
|
|
20
|
+
tokenFileCleared,
|
|
21
|
+
authUrl,
|
|
22
|
+
next: `Approve access in the browser window that opened (or open ${authUrl} manually). Tools will fail until you finish; then call get_identity to confirm.`,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.error('Re-authentication failed:', error);
|
|
27
|
+
return createErrorResponse(`Re-authentication failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|