Skip to content

Commit fb10c61

Browse files
committed
add tests
1 parent f525ddc commit fb10c61

File tree

7 files changed

+925
-61
lines changed

7 files changed

+925
-61
lines changed

src/garth_mcp_server/__init__.py

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -236,28 +236,14 @@ def get_activity_weather(activity_id: str) -> ConnectAPIResponse:
236236
return garth.connectapi(f"activity-service/activity/{activity_id}/weather")
237237

238238

239-
@server.tool()
240-
@requires_garth_session
241-
def get_body_composition(date: str | None = None) -> ConnectAPIResponse:
242-
"""
243-
Get body composition data from Garmin Connect.
244-
date: Date for body composition data (YYYY-MM-DD format), if not provided returns latest
245-
"""
246-
if date:
247-
endpoint = f"wellness-service/wellness/bodyComposition/{date}"
248-
else:
249-
endpoint = "wellness-service/wellness/bodyComposition"
250-
return garth.connectapi(endpoint)
251-
252-
253239
@server.tool()
254240
@requires_garth_session
255241
def get_respiration_data(date: str) -> ConnectAPIResponse:
256242
"""
257243
Get respiration data from Garmin Connect.
258244
date: Date for respiration data (YYYY-MM-DD format)
259245
"""
260-
return garth.connectapi(f"wellness-service/wellness/dailyRespiration/{date}")
246+
return garth.connectapi(f"wellness-service/wellness/daily/respiration/{date}")
261247

262248

263249
@server.tool()
@@ -277,7 +263,7 @@ def get_blood_pressure(date: str) -> ConnectAPIResponse:
277263
Get blood pressure readings from Garmin Connect.
278264
date: Date for blood pressure data (YYYY-MM-DD format)
279265
"""
280-
return garth.connectapi(f"wellness-service/wellness/dailyBloodPressure/{date}")
266+
return garth.connectapi(f"bloodpressure-service/bloodpressure/dayview/{date}")
281267

282268

283269
@server.tool()
@@ -307,27 +293,10 @@ def get_gear() -> ConnectAPIResponse:
307293
"""
308294
Get gear information from Garmin Connect.
309295
"""
310-
return garth.connectapi("gear-service/gear")
311-
312-
313-
@server.tool()
314-
@requires_garth_session
315-
def get_gear_stats(gear_uuid: str) -> ConnectAPIResponse:
316-
"""
317-
Get usage statistics for specific gear.
318-
gear_uuid: UUID of the gear item
319-
"""
320-
return garth.connectapi(f"gear-service/gear/stats/{gear_uuid}")
321-
322-
323-
@server.tool()
324-
@requires_garth_session
325-
def get_connectapi_endpoint(endpoint: str) -> ConnectAPIResponse:
326-
"""
327-
Get the data from a given Garmin Connect API endpoint.
328-
This is a generic tool that can be used to get data from any Garmin Connect API endpoint.
329-
"""
330-
return garth.connectapi(endpoint)
296+
profile = garth.UserProfile.get()
297+
return garth.connectapi(
298+
f"gear-service/gear/filterGear?userProfilePk={profile.profile_id}"
299+
)
331300

332301

333302
@server.tool()
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
interactions:
2+
- request:
3+
body: null
4+
headers:
5+
Accept:
6+
- '*/*'
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
User-Agent:
12+
- python-requests/2.32.3
13+
method: GET
14+
uri: https://thegarth.s3.amazonaws.com/oauth_consumer.json
15+
response:
16+
body:
17+
string: '{"consumer_key": "SANITIZED", "consumer_secret": "SANITIZED"}'
18+
headers:
19+
Accept-Ranges:
20+
- bytes
21+
Content-Length:
22+
- '124'
23+
Content-Type:
24+
- application/json
25+
Date:
26+
- Thu, 30 Oct 2025 03:27:02 GMT
27+
ETag:
28+
- '"20240b1013cb35419bb5b2cff1407a4e"'
29+
Last-Modified:
30+
- Thu, 03 Aug 2023 00:16:11 GMT
31+
Server:
32+
- AmazonS3
33+
x-amz-id-2:
34+
- +FGh7Li4ANpZP7r0gbq2YC1Fp6w1gYIdpsCIiq9tCj6raoR7zwUmyDZ2HQDra6Mn3bw6HJBPqmo=
35+
x-amz-request-id:
36+
- PP2FJVFR6CGSAE0B
37+
x-amz-server-side-encryption:
38+
- AES256
39+
status:
40+
code: 200
41+
message: OK
42+
- request:
43+
body: mfa_token=SANITIZED
44+
headers:
45+
Accept:
46+
- !!binary |
47+
Ki8q
48+
Accept-Encoding:
49+
- !!binary |
50+
Z3ppcCwgZGVmbGF0ZQ==
51+
Authorization:
52+
- Bearer SANITIZED
53+
Connection:
54+
- !!binary |
55+
a2VlcC1hbGl2ZQ==
56+
Content-Length:
57+
- '74'
58+
Content-Type:
59+
- !!binary |
60+
YXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVk
61+
User-Agent:
62+
- !!binary |
63+
Y29tLmdhcm1pbi5hbmRyb2lkLmFwcHMuY29ubmVjdG1vYmlsZQ==
64+
method: POST
65+
uri: https://connectapi.garmin.com/oauth-service/oauth/exchange/user/2.0
66+
response:
67+
body:
68+
string: '{"scope": "GARMINPAY_WRITE ATP_READ GHS_SAMD INSIGHTS_READ CIQ_APPSTORE_SERVICES_CREATE
69+
COMMUNITY_COURSE_WRITE GCOFFER_WRITE DT_CLIENT_ANALYTICS_WRITE CIQ_APPSTORE_SERVICES_DELETE
70+
OMT_SUBSCRIPTION_READ CONNECT_READ COMMUNITY_COURSE_READ GOLF_API_READ GHS_UPLOAD
71+
DIVE_API_READ CIQ_APPSTORE_SERVICES_READ CIQ_APPSTORE_SERVICES_UPDATE CONNECT_WRITE
72+
CONNECT_MCT_DAILY_LOG_READ DI_OAUTH_2_AUTHORIZATION_CODE_CREATE GARMINPAY_READ
73+
GOLF_API_WRITE INSIGHTS_WRITE PRODUCT_SEARCH_READ OMT_CAMPAIGN_READ GCOFFER_READ
74+
ATP_WRITE", "jti": "SANITIZED", "access_token": "SANITIZED", "token_type":
75+
"bearer", "refresh_token": "SANITIZED", "expires_in": 70754, "refresh_token_expires_in":
76+
2591999}'
77+
headers:
78+
CF-RAY:
79+
- 9967d07ffbb1576f-QRO
80+
Cache-Control:
81+
- no-cache, no-store, private
82+
Connection:
83+
- keep-alive
84+
Content-Type:
85+
- application/json
86+
Date:
87+
- Thu, 30 Oct 2025 03:27:01 GMT
88+
NEL:
89+
- '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}'
90+
Report-To:
91+
- '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=Ta%2BB7dL8Pcc4CkcY2TZYJfG62irGaM5jxtVj35ac%2BX7jy0HsypHEkgwW3ugqcNLP20G86%2B3ktJlv%2FonrOGiPX1rQbHsSjVBEwcH%2FRLojSWnf5XaHaYgi4QAp0td5SPWgyH3%2BMJBqCZXAbUVZyUyVqYJYcw%3D%3D"}],"group":"cf-nel","max_age":604800}'
92+
Server:
93+
- cloudflare
94+
Set-Cookie:
95+
- _cfuvid=SANITIZED; path=SANITIZED; domain=SANITIZED; HttpOnly; Secure; SameSite=SANITIZED
96+
Transfer-Encoding:
97+
- chunked
98+
alt-svc:
99+
- h3=":443"; ma=86400
100+
cf-cache-status:
101+
- DYNAMIC
102+
pragma:
103+
- no-cache
104+
status:
105+
code: 200
106+
message: OK
107+
- request:
108+
body: null
109+
headers:
110+
Accept:
111+
- '*/*'
112+
Accept-Encoding:
113+
- gzip, deflate
114+
Authorization:
115+
- Bearer SANITIZED
116+
Connection:
117+
- keep-alive
118+
User-Agent:
119+
- GCM-iOS-5.7.2.1
120+
method: GET
121+
uri: https://connectapi.garmin.com/bloodpressure-service/bloodpressure/dayview/2025-10-29
122+
response:
123+
body:
124+
string: '{"startDate": "2025-10-29", "endDate": "2025-10-29", "bloodPressureMeasurements":
125+
[{"version": 1761793376298, "systolic": 120, "diastolic": 80, "pulse": 55,
126+
"multiMeasurement": false, "notes": "", "sourceType": "MANUAL", "measurementTimestampLocal":
127+
"2025-10-29T21:02:00.0", "measurementTimestampGMT": "2025-10-30T03:02:00.0",
128+
"category": "STAGE_1_HIGH", "categoryName": "NORMAL"}], "totalMeasurementCount":
129+
null, "elevatedMeasurementCount": null}'
130+
headers:
131+
CF-RAY:
132+
- 9967d082ca35ca13-QRO
133+
Cache-Control:
134+
- no-cache, no-store, private
135+
Connection:
136+
- keep-alive
137+
Content-Type:
138+
- application/json
139+
Date:
140+
- Thu, 30 Oct 2025 03:27:01 GMT
141+
NEL:
142+
- '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}'
143+
Report-To:
144+
- '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=xsEYGt%2BRKQMeruQOiKpQrd7gfCJ7UxR9oK9iYBXXk%2FXHfb79FM7hUHA3zjFlGSqpWh2RBBu1LEfNfGZMGzWmYvCyQAwPmvuO%2FDL8W8f4anNb8ml4JrTmifbL%2FUT65XCj0%2BIdDrN89wrZZSKz19qqroJwew%3D%3D"}],"group":"cf-nel","max_age":604800}'
145+
Server:
146+
- cloudflare
147+
Set-Cookie:
148+
- _cfuvid=SANITIZED; path=SANITIZED; domain=SANITIZED; HttpOnly; Secure; SameSite=SANITIZED
149+
Transfer-Encoding:
150+
- chunked
151+
alt-svc:
152+
- h3=":443"; ma=86400
153+
cf-cache-status:
154+
- DYNAMIC
155+
pragma:
156+
- no-cache
157+
status:
158+
code: 200
159+
message: OK
160+
version: 1
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
interactions:
2+
- request:
3+
body: mfa_token=SANITIZED
4+
headers:
5+
Accept:
6+
- !!binary |
7+
Ki8q
8+
Accept-Encoding:
9+
- !!binary |
10+
Z3ppcCwgZGVmbGF0ZQ==
11+
Authorization:
12+
- Bearer SANITIZED
13+
Connection:
14+
- !!binary |
15+
a2VlcC1hbGl2ZQ==
16+
Content-Length:
17+
- '74'
18+
Content-Type:
19+
- !!binary |
20+
YXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVk
21+
User-Agent:
22+
- !!binary |
23+
Y29tLmdhcm1pbi5hbmRyb2lkLmFwcHMuY29ubmVjdG1vYmlsZQ==
24+
method: POST
25+
uri: https://connectapi.garmin.com/oauth-service/oauth/exchange/user/2.0
26+
response:
27+
body:
28+
string: "<!doctype html><html lang=\"en\"><head><title>HTTP Status 401 \u2013
29+
Unauthorized</title><style type=\"text/css\">body {font-family:Tahoma,Arial,sans-serif;}
30+
h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;}
31+
h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;}
32+
.line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP
33+
Status 401 \u2013 Unauthorized</h1><hr class=\"line\" /><p><b>Type</b> Status
34+
Report</p><p><b>Message</b> ConsumerKey is invalid</p><p><b>Description</b>
35+
The request has not been applied to the target resource because it lacks valid
36+
authentication credentials for that resource.</p><hr class=\"line\" /><h3>Garmin
37+
Connect API Server</h3></body></html>"
38+
headers:
39+
CF-RAY:
40+
- 9967d3fa8bd4676d-DFW
41+
Connection:
42+
- keep-alive
43+
Content-Type:
44+
- text/html;charset=utf-8
45+
Date:
46+
- Thu, 30 Oct 2025 03:29:23 GMT
47+
NEL:
48+
- '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}'
49+
Report-To:
50+
- '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=V4Oj3VTP01H%2FvM3NS6X8JlKDbcYJ0i5E6liIzzQ%2BaHv9RvQv7n%2B7Cqh0e0UZWI9I3VN0fHfLe2zZIuoimfKhCvhTlvj6pLWT%2BYKx3gNi%2FM3U8%2FhslhuJxeLZ7bvXIRLVkx9qtqbWdlpVG3PpDzKiJhzsaA%3D%3D"}],"group":"cf-nel","max_age":604800}'
51+
Server:
52+
- cloudflare
53+
Set-Cookie:
54+
- _cfuvid=SANITIZED; path=SANITIZED; domain=SANITIZED; HttpOnly; Secure; SameSite=SANITIZED
55+
Transfer-Encoding:
56+
- chunked
57+
alt-svc:
58+
- h3=":443"; ma=86400
59+
cf-cache-status:
60+
- DYNAMIC
61+
content-language:
62+
- en
63+
www-authenticate:
64+
- OAuth realm="connect.garmin.com"
65+
status:
66+
code: 401
67+
message: Unauthorized
68+
version: 1
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
interactions:
2+
- request:
3+
body: mfa_token=SANITIZED
4+
headers:
5+
Accept:
6+
- !!binary |
7+
Ki8q
8+
Accept-Encoding:
9+
- !!binary |
10+
Z3ppcCwgZGVmbGF0ZQ==
11+
Authorization:
12+
- Bearer SANITIZED
13+
Connection:
14+
- !!binary |
15+
a2VlcC1hbGl2ZQ==
16+
Content-Length:
17+
- '74'
18+
Content-Type:
19+
- !!binary |
20+
YXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVk
21+
User-Agent:
22+
- !!binary |
23+
Y29tLmdhcm1pbi5hbmRyb2lkLmFwcHMuY29ubmVjdG1vYmlsZQ==
24+
method: POST
25+
uri: https://connectapi.garmin.com/oauth-service/oauth/exchange/user/2.0
26+
response:
27+
body:
28+
string: "<!doctype html><html lang=\"en\"><head><title>HTTP Status 401 \u2013
29+
Unauthorized</title><style type=\"text/css\">body {font-family:Tahoma,Arial,sans-serif;}
30+
h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;}
31+
h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;}
32+
.line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP
33+
Status 401 \u2013 Unauthorized</h1><hr class=\"line\" /><p><b>Type</b> Status
34+
Report</p><p><b>Message</b> ConsumerKey is invalid</p><p><b>Description</b>
35+
The request has not been applied to the target resource because it lacks valid
36+
authentication credentials for that resource.</p><hr class=\"line\" /><h3>Garmin
37+
Connect API Server</h3></body></html>"
38+
headers:
39+
CF-RAY:
40+
- 9967d4016bccf0a0-DFW
41+
Connection:
42+
- keep-alive
43+
Content-Type:
44+
- text/html;charset=utf-8
45+
Date:
46+
- Thu, 30 Oct 2025 03:29:24 GMT
47+
NEL:
48+
- '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}'
49+
Report-To:
50+
- '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=bhta17%2BVdXc61oh0ovrt916DhWNscC2tvv9p9dv7czjv09FYJKcGwgB%2BF4hqalEt4rubpnIpbF%2FaR4CC0eGCIYLtwr%2FgrGA6QOHHMEfFadaGzJlW5Gh2PbZ5bNQpe%2FA8RT%2BIAYlSCfz8bEwmo1NUhI5dKw%3D%3D"}],"group":"cf-nel","max_age":604800}'
51+
Server:
52+
- cloudflare
53+
Set-Cookie:
54+
- _cfuvid=SANITIZED; path=SANITIZED; domain=SANITIZED; HttpOnly; Secure; SameSite=SANITIZED
55+
Transfer-Encoding:
56+
- chunked
57+
alt-svc:
58+
- h3=":443"; ma=86400
59+
cf-cache-status:
60+
- DYNAMIC
61+
content-language:
62+
- en
63+
www-authenticate:
64+
- OAuth realm="connect.garmin.com"
65+
status:
66+
code: 401
67+
message: Unauthorized
68+
version: 1

0 commit comments

Comments
 (0)