11import os
22from datetime import date
33from functools import wraps
4+ from urllib .parse import urlencode
45
56import garth
67from mcp .server .fastmcp import FastMCP
78
89
9- __version__ = "0.0.6 "
10+ __version__ = "0.0.7 "
1011
1112server = FastMCP ("Garth - Garmin Connect" , dependencies = ["garth" ], version = __version__ )
1213
@@ -23,6 +24,278 @@ def wrapper(*args, **kwargs):
2324 return wrapper
2425
2526
27+ # Tools using Garth data classes
28+
29+
30+ @server .tool ()
31+ @requires_garth_session
32+ def user_profile () -> str | garth .UserProfile :
33+ """
34+ Get user profile information using Garth's UserProfile data class.
35+ """
36+ return garth .UserProfile .get ()
37+
38+
39+ @server .tool ()
40+ @requires_garth_session
41+ def user_settings () -> str | garth .UserSettings :
42+ """
43+ Get user settings using Garth's UserSettings data class.
44+ """
45+ return garth .UserSettings .get ()
46+
47+
48+ @server .tool ()
49+ @requires_garth_session
50+ def weekly_intensity_minutes (
51+ end_date : date | None = None , weeks : int = 1
52+ ) -> str | list [garth .WeeklyIntensityMinutes ]:
53+ """
54+ Get weekly intensity minutes data for a given date and number of weeks.
55+ If no date is provided, the current date will be used.
56+ If no weeks are provided, 1 week will be used.
57+ """
58+ return garth .WeeklyIntensityMinutes .list (end_date , weeks )
59+
60+
61+ @server .tool ()
62+ @requires_garth_session
63+ def daily_body_battery (
64+ end_date : date | None = None , days : int = 1
65+ ) -> str | list [garth .DailyBodyBatteryStress ]:
66+ """
67+ Get daily body battery data for a given date and number of days.
68+ If no date is provided, the current date will be used.
69+ If no days are provided, 1 day will be used.
70+ """
71+ return garth .DailyBodyBatteryStress .list (end_date , days )
72+
73+
74+ @server .tool ()
75+ @requires_garth_session
76+ def daily_hydration (
77+ end_date : date | None = None , days : int = 1
78+ ) -> str | list [garth .DailyHydration ]:
79+ """
80+ Get daily hydration data for a given date and number of days.
81+ If no date is provided, the current date will be used.
82+ If no days are provided, 1 day will be used.
83+ """
84+ return garth .DailyHydration .list (end_date , days )
85+
86+
87+ @server .tool ()
88+ @requires_garth_session
89+ def daily_steps (
90+ end_date : date | None = None , days : int = 1
91+ ) -> str | list [garth .DailySteps ]:
92+ """
93+ Get daily steps data for a given date and number of days.
94+ If no date is provided, the current date will be used.
95+ If no days are provided, 1 day will be used.
96+ """
97+ return garth .DailySteps .list (end_date , days )
98+
99+
100+ @server .tool ()
101+ @requires_garth_session
102+ def weekly_steps (
103+ end_date : date | None = None , weeks : int = 1
104+ ) -> str | list [garth .WeeklySteps ]:
105+ """
106+ Get weekly steps data for a given date and number of weeks.
107+ If no date is provided, the current date will be used.
108+ If no weeks are provided, 1 week will be used.
109+ """
110+ return garth .WeeklySteps .list (end_date , weeks )
111+
112+
113+ @server .tool ()
114+ @requires_garth_session
115+ def daily_hrv (
116+ end_date : date | None = None , days : int = 1
117+ ) -> str | list [garth .DailyHRV ]:
118+ """
119+ Get daily heart rate variability data for a given date and number of days.
120+ If no date is provided, the current date will be used.
121+ If no days are provided, 1 day will be used.
122+ """
123+ return garth .DailyHRV .list (end_date , days )
124+
125+
126+ @server .tool ()
127+ @requires_garth_session
128+ def hrv_data (end_date : date | None = None , days : int = 1 ) -> str | list [garth .HRVData ]:
129+ """
130+ Get detailed HRV data for a given date and number of days.
131+ If no date is provided, the current date will be used.
132+ If no days are provided, 1 day will be used.
133+ """
134+ return garth .HRVData .list (end_date , days )
135+
136+
137+ @server .tool ()
138+ @requires_garth_session
139+ def daily_sleep (
140+ end_date : date | None = None , days : int = 1
141+ ) -> str | list [garth .DailySleep ]:
142+ """
143+ Get daily sleep summary data for a given date and number of days.
144+ If no date is provided, the current date will be used.
145+ If no days are provided, 1 day will be used.
146+ """
147+ return garth .DailySleep .list (end_date , days )
148+
149+
150+ # Tools using direct API calls
151+
152+
153+ @server .tool ()
154+ @requires_garth_session
155+ def get_activities (
156+ start_date : str | None = None , limit : int | None = None
157+ ) -> str | dict | None :
158+ """
159+ Get list of activities from Garmin Connect.
160+ start_date: Start date for activities (YYYY-MM-DD format)
161+ limit: Maximum number of activities to return
162+ """
163+ params = {}
164+ if start_date :
165+ params ["startDate" ] = start_date
166+ if limit :
167+ params ["limit" ] = str (limit )
168+
169+ endpoint = "activitylist-service/activities/search/activities"
170+ if params :
171+ endpoint += "?" + urlencode (params )
172+ return garth .connectapi (endpoint )
173+
174+
175+ @server .tool ()
176+ @requires_garth_session
177+ def get_activities_by_date (date : str ) -> str | dict | None :
178+ """
179+ Get activities for a specific date from Garmin Connect.
180+ date: Date for activities (YYYY-MM-DD format)
181+ """
182+ return garth .connectapi (f"wellness-service/wellness/dailySummaryChart/{ date } " )
183+
184+
185+ @server .tool ()
186+ @requires_garth_session
187+ def get_activity_details (activity_id : str ) -> str | dict | None :
188+ """
189+ Get detailed information for a specific activity.
190+ activity_id: Garmin Connect activity ID
191+ """
192+ return garth .connectapi (f"activity-service/activity/{ activity_id } " )
193+
194+
195+ @server .tool ()
196+ @requires_garth_session
197+ def get_activity_splits (activity_id : str ) -> str | dict | None :
198+ """
199+ Get lap/split data for a specific activity.
200+ activity_id: Garmin Connect activity ID
201+ """
202+ return garth .connectapi (f"activity-service/activity/{ activity_id } /splits" )
203+
204+
205+ @server .tool ()
206+ @requires_garth_session
207+ def get_activity_weather (activity_id : str ) -> str | dict | None :
208+ """
209+ Get weather data for a specific activity.
210+ activity_id: Garmin Connect activity ID
211+ """
212+ return garth .connectapi (f"activity-service/activity/{ activity_id } /weather" )
213+
214+
215+ @server .tool ()
216+ @requires_garth_session
217+ def get_body_composition (date : str | None = None ) -> str | dict | None :
218+ """
219+ Get body composition data from Garmin Connect.
220+ date: Date for body composition data (YYYY-MM-DD format), if not provided returns latest
221+ """
222+ if date :
223+ endpoint = f"wellness-service/wellness/bodyComposition/{ date } "
224+ else :
225+ endpoint = "wellness-service/wellness/bodyComposition"
226+ return garth .connectapi (endpoint )
227+
228+
229+ @server .tool ()
230+ @requires_garth_session
231+ def get_respiration_data (date : str ) -> str | dict | None :
232+ """
233+ Get respiration data from Garmin Connect.
234+ date: Date for respiration data (YYYY-MM-DD format)
235+ """
236+ return garth .connectapi (f"wellness-service/wellness/dailyRespiration/{ date } " )
237+
238+
239+ @server .tool ()
240+ @requires_garth_session
241+ def get_spo2_data (date : str ) -> str | dict | None :
242+ """
243+ Get SpO2 (blood oxygen) data from Garmin Connect.
244+ date: Date for SpO2 data (YYYY-MM-DD format)
245+ """
246+ return garth .connectapi (f"wellness-service/wellness/dailyPulseOx/{ date } " )
247+
248+
249+ @server .tool ()
250+ @requires_garth_session
251+ def get_blood_pressure (date : str ) -> str | dict | None :
252+ """
253+ Get blood pressure readings from Garmin Connect.
254+ date: Date for blood pressure data (YYYY-MM-DD format)
255+ """
256+ return garth .connectapi (f"wellness-service/wellness/dailyBloodPressure/{ date } " )
257+
258+
259+ @server .tool ()
260+ @requires_garth_session
261+ def get_devices () -> str | dict | None :
262+ """
263+ Get connected devices from Garmin Connect.
264+ """
265+ return garth .connectapi ("device-service/deviceregistration/devices" )
266+
267+
268+ @server .tool ()
269+ @requires_garth_session
270+ def get_device_settings (device_id : str ) -> str | dict | None :
271+ """
272+ Get settings for a specific device.
273+ device_id: Device ID from Garmin Connect
274+ """
275+ return garth .connectapi (
276+ f"device-service/deviceservice/device-info/settings/{ device_id } "
277+ )
278+
279+
280+ @server .tool ()
281+ @requires_garth_session
282+ def get_gear () -> str | dict | None :
283+ """
284+ Get gear information from Garmin Connect.
285+ """
286+ return garth .connectapi ("gear-service/gear" )
287+
288+
289+ @server .tool ()
290+ @requires_garth_session
291+ def get_gear_stats (gear_uuid : str ) -> str | dict | None :
292+ """
293+ Get usage statistics for specific gear.
294+ gear_uuid: UUID of the gear item
295+ """
296+ return garth .connectapi (f"gear-service/gear/stats/{ gear_uuid } " )
297+
298+
26299@server .tool ()
27300@requires_garth_session
28301def get_connectapi_endpoint (endpoint : str ) -> str | dict | None :
@@ -36,16 +309,20 @@ def get_connectapi_endpoint(endpoint: str) -> str | dict | None:
36309@server .tool ()
37310@requires_garth_session
38311def nightly_sleep (
39- end_date : date | None = None , nights : int = 1
312+ end_date : date | None = None , nights : int = 1 , sleep_movement : bool = False
40313) -> str | list [garth .SleepData ]:
41314 """
42315 Get sleep stats for a given date and number of nights.
43316 If no date is provided, the current date will be used.
44317 If no nights are provided, 1 night will be used.
318+ sleep_movement provides detailed sleep movement data. If looking at
319+ multiple nights, it'll be a lot of data.
45320 """
46321 sleep_data = garth .SleepData .list (end_date , nights )
47- for night in sleep_data :
48- del night .sleep_movement
322+ if not sleep_movement :
323+ for night in sleep_data :
324+ if hasattr (night , "sleep_movement" ):
325+ del night .sleep_movement
49326 return sleep_data
50327
51328
0 commit comments