grlll commited on
Commit
adcb689
·
1 Parent(s): 7ccc1fe

create a local mcp_server gradio app that will become the private space.

Browse files
Files changed (5) hide show
  1. .gitignore +1 -3
  2. .mcp.json +18 -0
  3. create_test_db.py +24 -0
  4. data/export.xml +346 -0
  5. mcp_server.py +182 -0
.gitignore CHANGED
@@ -8,6 +8,4 @@ wheels/
8
 
9
  # Virtual environments
10
  .venv
11
- .DS_Store
12
-
13
- data
 
8
 
9
  # Virtual environments
10
  .venv
11
+ .DS_Store
 
 
.mcp.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "mcpServers": {
3
+ "playwright": {
4
+ "command": "npx",
5
+ "args": [
6
+ "@playwright/mcp@latest"
7
+ ]
8
+ },
9
+ "sqlite": {
10
+ "command": "uvx",
11
+ "args": [
12
+ "mcp-server-sqlite",
13
+ "--db-path",
14
+ "data/health_data.db"
15
+ ]
16
+ }
17
+ }
18
+ }
create_test_db.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Create a test SQLite database from the export.xml file."""
3
+
4
+ from src.parser.parser import AppleHealthParser
5
+ import os
6
+
7
+ def main():
8
+ xml_file = "data/export.xml"
9
+ db_file = "data/health_data.db"
10
+
11
+ # Remove existing database if it exists
12
+ if os.path.exists(db_file):
13
+ os.remove(db_file)
14
+ print(f"Removed existing {db_file}")
15
+
16
+ # Create the parser with no date cutoff to include all records
17
+ from datetime import timedelta
18
+ parser = AppleHealthParser(db_path=db_file, data_cutoff=timedelta(days=36500)) # 100 years
19
+ print(f"Parsing {xml_file}...")
20
+ parser.parse_file(xml_file)
21
+ print(f"Successfully created {db_file}")
22
+
23
+ if __name__ == "__main__":
24
+ main()
data/export.xml ADDED
@@ -0,0 +1,346 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE HealthData [
3
+ <!-- HealthKit Export Version: 14 -->
4
+ <!ELEMENT HealthData (ExportDate,Me,(Record|Correlation|Workout|ActivitySummary|ClinicalRecord|Audiogram|VisionPrescription)*)>
5
+ <!ATTLIST HealthData
6
+ locale CDATA #REQUIRED
7
+ >
8
+ <!ELEMENT ExportDate EMPTY>
9
+ <!ATTLIST ExportDate
10
+ value CDATA #REQUIRED
11
+ >
12
+ <!ELEMENT Me EMPTY>
13
+ <!ATTLIST Me
14
+ HKCharacteristicTypeIdentifierDateOfBirth CDATA #REQUIRED
15
+ HKCharacteristicTypeIdentifierBiologicalSex CDATA #REQUIRED
16
+ HKCharacteristicTypeIdentifierBloodType CDATA #REQUIRED
17
+ HKCharacteristicTypeIdentifierFitzpatrickSkinType CDATA #REQUIRED
18
+ HKCharacteristicTypeIdentifierCardioFitnessMedicationsUse CDATA #REQUIRED
19
+ >
20
+ <!ELEMENT Record ((MetadataEntry|HeartRateVariabilityMetadataList)*)>
21
+ <!ATTLIST Record
22
+ type CDATA #REQUIRED
23
+ unit CDATA #IMPLIED
24
+ value CDATA #IMPLIED
25
+ sourceName CDATA #REQUIRED
26
+ sourceVersion CDATA #IMPLIED
27
+ device CDATA #IMPLIED
28
+ creationDate CDATA #IMPLIED
29
+ startDate CDATA #REQUIRED
30
+ endDate CDATA #REQUIRED
31
+ >
32
+ <!-- Note: Any Records that appear as children of a correlation also appear as top-level records in this document. -->
33
+ <!ELEMENT Correlation ((MetadataEntry|Record)*)>
34
+ <!ATTLIST Correlation
35
+ type CDATA #REQUIRED
36
+ sourceName CDATA #REQUIRED
37
+ sourceVersion CDATA #IMPLIED
38
+ device CDATA #IMPLIED
39
+ creationDate CDATA #IMPLIED
40
+ startDate CDATA #REQUIRED
41
+ endDate CDATA #REQUIRED
42
+ >
43
+ <!ELEMENT Workout ((MetadataEntry|WorkoutEvent|WorkoutRoute|WorkoutStatistics)*)>
44
+ <!ATTLIST Workout
45
+ workoutActivityType CDATA #REQUIRED
46
+ duration CDATA #IMPLIED
47
+ durationUnit CDATA #IMPLIED
48
+ totalDistance CDATA #IMPLIED
49
+ totalDistanceUnit CDATA #IMPLIED
50
+ totalEnergyBurned CDATA #IMPLIED
51
+ totalEnergyBurnedUnit CDATA #IMPLIED
52
+ sourceName CDATA #REQUIRED
53
+ sourceVersion CDATA #IMPLIED
54
+ device CDATA #IMPLIED
55
+ creationDate CDATA #IMPLIED
56
+ startDate CDATA #REQUIRED
57
+ endDate CDATA #REQUIRED
58
+ >
59
+ <!ELEMENT WorkoutActivity ((MetadataEntry)*)>
60
+ <!ATTLIST WorkoutActivity
61
+ uuid CDATA #REQUIRED
62
+ startDate CDATA #REQUIRED
63
+ endDate CDATA #IMPLIED
64
+ duration CDATA #IMPLIED
65
+ durationUnit CDATA #IMPLIED
66
+ >
67
+ <!ELEMENT WorkoutEvent ((MetadataEntry)*)>
68
+ <!ATTLIST WorkoutEvent
69
+ type CDATA #REQUIRED
70
+ date CDATA #REQUIRED
71
+ duration CDATA #IMPLIED
72
+ durationUnit CDATA #IMPLIED
73
+ >
74
+ <!ELEMENT WorkoutStatistics EMPTY>
75
+ <!ATTLIST WorkoutStatistics
76
+ type CDATA #REQUIRED
77
+ startDate CDATA #REQUIRED
78
+ endDate CDATA #REQUIRED
79
+ average CDATA #IMPLIED
80
+ minimum CDATA #IMPLIED
81
+ maximum CDATA #IMPLIED
82
+ sum CDATA #IMPLIED
83
+ unit CDATA #IMPLIED
84
+ >
85
+ <!ELEMENT WorkoutRoute ((MetadataEntry|FileReference)*)>
86
+ <!ATTLIST WorkoutRoute
87
+ sourceName CDATA #REQUIRED
88
+ sourceVersion CDATA #IMPLIED
89
+ device CDATA #IMPLIED
90
+ creationDate CDATA #IMPLIED
91
+ startDate CDATA #REQUIRED
92
+ endDate CDATA #REQUIRED
93
+ >
94
+ <!ELEMENT FileReference EMPTY>
95
+ <!ATTLIST FileReference
96
+ path CDATA #REQUIRED
97
+ >
98
+ <!ELEMENT ActivitySummary EMPTY>
99
+ <!ATTLIST ActivitySummary
100
+ dateComponents CDATA #IMPLIED
101
+ activeEnergyBurned CDATA #IMPLIED
102
+ activeEnergyBurnedGoal CDATA #IMPLIED
103
+ activeEnergyBurnedUnit CDATA #IMPLIED
104
+ appleMoveTime CDATA #IMPLIED
105
+ appleMoveTimeGoal CDATA #IMPLIED
106
+ appleExerciseTime CDATA #IMPLIED
107
+ appleExerciseTimeGoal CDATA #IMPLIED
108
+ appleStandHours CDATA #IMPLIED
109
+ appleStandHoursGoal CDATA #IMPLIED
110
+ >
111
+ <!ELEMENT MetadataEntry EMPTY>
112
+ <!ATTLIST MetadataEntry
113
+ key CDATA #REQUIRED
114
+ value CDATA #REQUIRED
115
+ >
116
+ <!-- Note: Heart Rate Variability records captured by Apple Watch may include an associated list of instantaneous beats-per-minute readings. -->
117
+ <!ELEMENT HeartRateVariabilityMetadataList (InstantaneousBeatsPerMinute*)>
118
+ <!ELEMENT InstantaneousBeatsPerMinute EMPTY>
119
+ <!ATTLIST InstantaneousBeatsPerMinute
120
+ bpm CDATA #REQUIRED
121
+ time CDATA #REQUIRED
122
+ >
123
+ <!ELEMENT ClinicalRecord EMPTY>
124
+ <!ATTLIST ClinicalRecord
125
+ type CDATA #REQUIRED
126
+ identifier CDATA #REQUIRED
127
+ sourceName CDATA #REQUIRED
128
+ sourceURL CDATA #REQUIRED
129
+ fhirVersion CDATA #REQUIRED
130
+ receivedDate CDATA #REQUIRED
131
+ resourceFilePath CDATA #REQUIRED
132
+ >
133
+ <!ELEMENT Audiogram ((MetadataEntry|SensitivityPoint)*)>
134
+ <!ATTLIST Audiogram
135
+ type CDATA #REQUIRED
136
+ sourceName CDATA #REQUIRED
137
+ sourceVersion CDATA #IMPLIED
138
+ device CDATA #IMPLIED
139
+ creationDate CDATA #IMPLIED
140
+ startDate CDATA #REQUIRED
141
+ endDate CDATA #REQUIRED
142
+ >
143
+ <!ELEMENT SensitivityPoint EMPTY>
144
+ <!ATTLIST SensitivityPoint
145
+ frequencyValue CDATA #REQUIRED
146
+ frequencyUnit CDATA #REQUIRED
147
+ leftEarValue CDATA #IMPLIED
148
+ leftEarUnit CDATA #IMPLIED
149
+ leftEarMasked CDATA #IMPLIED
150
+ leftEarClampingRangeLowerBound CDATA #IMPLIED
151
+ leftEarClampingRangeUpperBound CDATA #IMPLIED
152
+ rightEarValue CDATA #IMPLIED
153
+ rightEarUnit CDATA #IMPLIED
154
+ rightEarMasked CDATA #IMPLIED
155
+ rightEarClampingRangeLowerBound CDATA #IMPLIED
156
+ rightEarClampingRangeUpperBound CDATA #IMPLIED
157
+ >
158
+ <!ELEMENT VisionPrescription ((RightEye|LeftEye|Attachment|MetadataEntry)*)>
159
+ <!ATTLIST VisionPrescription
160
+ type CDATA #REQUIRED
161
+ dateIssued CDATA #REQUIRED
162
+ expirationDate CDATA #IMPLIED
163
+ brand CDATA #IMPLIED
164
+ >
165
+ <!ELEMENT RightEye EMPTY>
166
+ <!ATTLIST RightEye
167
+ sphere CDATA #IMPLIED
168
+ sphereUnit CDATA #IMPLIED
169
+ cylinder CDATA #IMPLIED
170
+ cylinderUnit CDATA #IMPLIED
171
+ axis CDATA #IMPLIED
172
+ axisUnit CDATA #IMPLIED
173
+ add CDATA #IMPLIED
174
+ addUnit CDATA #IMPLIED
175
+ vertex CDATA #IMPLIED
176
+ vertexUnit CDATA #IMPLIED
177
+ prismAmount CDATA #IMPLIED
178
+ prismAmountUnit CDATA #IMPLIED
179
+ prismAngle CDATA #IMPLIED
180
+ prismAngleUnit CDATA #IMPLIED
181
+ farPD CDATA #IMPLIED
182
+ farPDUnit CDATA #IMPLIED
183
+ nearPD CDATA #IMPLIED
184
+ nearPDUnit CDATA #IMPLIED
185
+ baseCurve CDATA #IMPLIED
186
+ baseCurveUnit CDATA #IMPLIED
187
+ diameter CDATA #IMPLIED
188
+ diameterUnit CDATA #IMPLIED
189
+ >
190
+ <!ELEMENT LeftEye EMPTY>
191
+ <!ATTLIST LeftEye
192
+ sphere CDATA #IMPLIED
193
+ sphereUnit CDATA #IMPLIED
194
+ cylinder CDATA #IMPLIED
195
+ cylinderUnit CDATA #IMPLIED
196
+ axis CDATA #IMPLIED
197
+ axisUnit CDATA #IMPLIED
198
+ add CDATA #IMPLIED
199
+ addUnit CDATA #IMPLIED
200
+ vertex CDATA #IMPLIED
201
+ vertexUnit CDATA #IMPLIED
202
+ prismAmount CDATA #IMPLIED
203
+ prismAmountUnit CDATA #IMPLIED
204
+ prismAngle CDATA #IMPLIED
205
+ prismAngleUnit CDATA #IMPLIED
206
+ farPD CDATA #IMPLIED
207
+ farPDUnit CDATA #IMPLIED
208
+ nearPD CDATA #IMPLIED
209
+ nearPDUnit CDATA #IMPLIED
210
+ baseCurve CDATA #IMPLIED
211
+ baseCurveUnit CDATA #IMPLIED
212
+ diameter CDATA #IMPLIED
213
+ diameterUnit CDATA #IMPLIED
214
+ >
215
+ <!ELEMENT Attachment EMPTY>
216
+ <!ATTLIST Attachment
217
+ identifier CDATA #IMPLIED
218
+ >
219
+ ]>
220
+ <HealthData locale="en_US">
221
+ <ExportDate value="2025-01-14 12:00:00 +0100"/>
222
+ <Me HKCharacteristicTypeIdentifierDateOfBirth="1995-01-15" HKCharacteristicTypeIdentifierBiologicalSex="HKBiologicalSexMale" HKCharacteristicTypeIdentifierBloodType="HKBloodTypeNotSet" HKCharacteristicTypeIdentifierFitzpatrickSkinType="HKFitzpatrickSkinTypeNotSet" HKCharacteristicTypeIdentifierCardioFitnessMedicationsUse="None"/>
223
+
224
+ <!-- Blood Glucose Records -->
225
+ <Record type="HKQuantityTypeIdentifierBloodGlucose" sourceName="Health" sourceVersion="17.3" unit="mmol&lt;180.1558800000541&gt;/L" creationDate="2024-01-09 22:32:54 +0100" startDate="2024-01-09 12:30:00 +0100" endDate="2024-01-09 12:30:00 +0100" value="4.59">
226
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
227
+ <MetadataEntry key="HKBloodGlucoseMealTime" value="1"/>
228
+ </Record>
229
+ <Record type="HKQuantityTypeIdentifierBloodGlucose" sourceName="Health" sourceVersion="17.3" unit="mmol&lt;180.1558800000541&gt;/L" creationDate="2024-01-09 22:45:00 +0100" startDate="2024-01-09 18:30:00 +0100" endDate="2024-01-09 18:30:00 +0100" value="5.23">
230
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
231
+ <MetadataEntry key="HKBloodGlucoseMealTime" value="2"/>
232
+ </Record>
233
+
234
+ <!-- Body Mass Index Records -->
235
+ <Record type="HKQuantityTypeIdentifierBodyMassIndex" sourceName="Zepp Life" sourceVersion="202311211629" unit="count" creationDate="2023-12-03 22:41:05 +0100" startDate="2023-12-03 22:38:45 +0100" endDate="2023-12-03 22:38:45 +0100" value="21.3294"/>
236
+ <Record type="HKQuantityTypeIdentifierBodyMassIndex" sourceName="Zepp Life" sourceVersion="202311211629" unit="count" creationDate="2023-12-04 08:50:30 +0100" startDate="2023-12-04 01:07:25 +0100" endDate="2023-12-04 01:07:25 +0100" value="22.4251"/>
237
+
238
+ <!-- Body Mass Records -->
239
+ <Record type="HKQuantityTypeIdentifierBodyMass" sourceName="Zepp Life" sourceVersion="202311211629" unit="kg" creationDate="2023-12-03 22:41:05 +0100" startDate="2023-12-03 22:38:45 +0100" endDate="2023-12-03 22:38:45 +0100" value="68.5"/>
240
+ <Record type="HKQuantityTypeIdentifierBodyMass" sourceName="Zepp Life" sourceVersion="202311211629" unit="kg" creationDate="2023-12-04 08:50:30 +0100" startDate="2023-12-04 01:07:25 +0100" endDate="2023-12-04 01:07:25 +0100" value="72"/>
241
+
242
+ <!-- Heart Rate Records -->
243
+ <Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="count/min" creationDate="2023-11-20 19:26:08 +0100" startDate="2023-11-20 19:20:56 +0100" endDate="2023-11-20 19:20:56 +0100" value="56">
244
+ <MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="0"/>
245
+ </Record>
246
+ <Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="count/min" creationDate="2023-11-20 19:42:40 +0100" startDate="2023-11-20 19:42:39 +0100" endDate="2023-11-20 19:42:39 +0100" value="96">
247
+ <MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="1"/>
248
+ </Record>
249
+ <Record type="HKQuantityTypeIdentifierHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="count/min" creationDate="2023-11-20 19:59:33 +0100" startDate="2023-11-20 19:59:19 +0100" endDate="2023-11-20 19:59:19 +0100" value="150">
250
+ <MetadataEntry key="HKMetadataKeyHeartRateMotionContext" value="2"/>
251
+ </Record>
252
+
253
+ <!-- Step Count Records -->
254
+ <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="count" creationDate="2023-11-20 14:15:00 +0100" startDate="2023-11-20 14:00:00 +0100" endDate="2023-11-20 14:15:00 +0100" value="234"/>
255
+ <Record type="HKQuantityTypeIdentifierStepCount" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="count" creationDate="2023-11-20 14:30:00 +0100" startDate="2023-11-20 14:15:00 +0100" endDate="2023-11-20 14:30:00 +0100" value="456"/>
256
+
257
+ <!-- Blood Pressure Correlation -->
258
+ <Correlation type="HKCorrelationTypeIdentifierBloodPressure" sourceName="Health" sourceVersion="17.3" creationDate="2024-01-10 12:52:42 +0100" startDate="2024-01-10 11:30:00 +0100" endDate="2024-01-10 11:30:00 +0100">
259
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
260
+ <Record type="HKQuantityTypeIdentifierBloodPressureSystolic" sourceName="Health" sourceVersion="17.3" unit="mmHg" creationDate="2024-01-10 12:52:42 +0100" startDate="2024-01-10 11:30:00 +0100" endDate="2024-01-10 11:30:00 +0100" value="136">
261
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
262
+ </Record>
263
+ <Record type="HKQuantityTypeIdentifierBloodPressureDiastolic" sourceName="Health" sourceVersion="17.3" unit="mmHg" creationDate="2024-01-10 12:52:42 +0100" startDate="2024-01-10 11:30:00 +0100" endDate="2024-01-10 11:30:00 +0100" value="69">
264
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
265
+ </Record>
266
+ </Correlation>
267
+
268
+ <!-- Sleep Analysis Records -->
269
+ <Record type="HKCategoryTypeIdentifierSleepAnalysis" sourceName="AutoSleep" sourceVersion="6.4.0" creationDate="2020-07-08 08:37:42 +0100" startDate="2020-07-08 01:05:06 +0100" endDate="2020-07-08 08:30:00 +0100" value="HKCategoryValueSleepAnalysisInBed">
270
+ <MetadataEntry key="HKTimeZone" value="Europe/Zurich"/>
271
+ </Record>
272
+ <Record type="HKCategoryTypeIdentifierSleepAnalysis" sourceName="AutoSleep" sourceVersion="6.4.0" creationDate="2020-07-08 08:37:42 +0100" startDate="2020-07-08 01:15:00 +0100" endDate="2020-07-08 08:30:00 +0100" value="HKCategoryValueSleepAnalysisAsleepUnspecified"/>
273
+
274
+ <!-- Workout Records -->
275
+ <Workout workoutActivityType="HKWorkoutActivityTypeWalking" duration="88.23378186623255" durationUnit="min" totalDistance="5.234" totalDistanceUnit="km" totalEnergyBurned="342.5" totalEnergyBurnedUnit="Cal" sourceName="Guillaume's Apple Watch" sourceVersion="6.2.5" device="&lt;&lt;HKDevice: 0x533b03430&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:6.2.5&gt;" creationDate="2020-05-27 18:57:19 +0100" startDate="2020-05-27 17:26:15 +0100" endDate="2020-05-27 18:57:18 +0100">
276
+ <MetadataEntry key="HKIndoorWorkout" value="0"/>
277
+ <MetadataEntry key="HKAverageMETs" value="5.43210 kcal/hr·kg"/>
278
+ <WorkoutEvent type="HKWorkoutEventTypePause" date="2020-05-27 17:45:00 +0100"/>
279
+ <WorkoutEvent type="HKWorkoutEventTypeResume" date="2020-05-27 17:47:30 +0100"/>
280
+ <WorkoutStatistics type="HKQuantityTypeIdentifierHeartRate" startDate="2020-05-27 17:26:15 +0100" endDate="2020-05-27 18:57:18 +0100" average="92.5" minimum="65" maximum="125" unit="count/min"/>
281
+ <WorkoutStatistics type="HKQuantityTypeIdentifierActiveEnergyBurned" startDate="2020-05-27 17:26:15 +0100" endDate="2020-05-27 18:57:18 +0100" sum="342.5" unit="Cal"/>
282
+ <WorkoutRoute sourceName="Guillaume's Apple Watch" sourceVersion="11.2" creationDate="2024-12-13 13:17:22 +0200" startDate="2024-12-13 12:58:33 +0200" endDate="2024-12-13 12:59:02 +0200">
283
+ <MetadataEntry key="HKMetadataKeySyncVersion" value="2"/>
284
+ <MetadataEntry key="HKMetadataKeySyncIdentifier" value="48F229FA-1BC6-47E6-91BE-BBA6796D5E8C"/>
285
+ <FileReference path="/workout-routes/route_2024-12-13_11.59am.gpx"/>
286
+ </WorkoutRoute>
287
+ <WorkoutRoute sourceName="Guillaume's Apple Watch" sourceVersion="11.2" creationDate="2024-12-13 13:17:22 +0200" startDate="2024-12-13 12:58:33 +0200" endDate="2024-12-13 12:59:02 +0200">
288
+ <MetadataEntry key="HKMetadataKeySyncVersion" value="2"/>
289
+ <MetadataEntry key="HKMetadataKeySyncIdentifier" value="48F229FA-1BC6-47E6-91BE-BBA6796D5E8C"/>
290
+ <FileReference path="/workout-routes/route_2024-12-13_11.59am.gpx"/>
291
+ </WorkoutRoute>
292
+ </Workout>
293
+
294
+ <Workout workoutActivityType="HKWorkoutActivityTypeCycling" duration="28.81272718310356" durationUnit="min" totalDistance="12.5" totalDistanceUnit="km" totalEnergyBurned="289.3" totalEnergyBurnedUnit="Cal" sourceName="Guillaume's Apple Watch" sourceVersion="6.2.5" device="&lt;&lt;HKDevice: 0x533b03430&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:6.2.5&gt;" creationDate="2020-05-28 15:24:12 +0100" startDate="2020-05-28 14:55:22 +0100" endDate="2020-05-28 15:24:11 +0100">
295
+ <MetadataEntry key="HKIndoorWorkout" value="0"/>
296
+ <WorkoutStatistics type="HKQuantityTypeIdentifierHeartRate" startDate="2020-05-28 14:55:22 +0100" endDate="2020-05-28 15:24:11 +0100" average="115.2" minimum="88" maximum="145" unit="count/min"/>
297
+ </Workout>
298
+
299
+ <!-- Activity Summary Records -->
300
+ <ActivitySummary dateComponents="2020-05-27" activeEnergyBurned="547.163" activeEnergyBurnedGoal="680" activeEnergyBurnedUnit="Cal" appleMoveTime="0" appleMoveTimeGoal="0" appleExerciseTime="44" appleExerciseTimeGoal="30" appleStandHours="10" appleStandHoursGoal="12"/>
301
+ <ActivitySummary dateComponents="2020-05-28" activeEnergyBurned="732.037" activeEnergyBurnedGoal="680" activeEnergyBurnedUnit="Cal" appleMoveTime="0" appleMoveTimeGoal="0" appleExerciseTime="55" appleExerciseTimeGoal="30" appleStandHours="14" appleStandHoursGoal="12"/>
302
+ <ActivitySummary dateComponents="2020-05-29" activeEnergyBurned="824.261" activeEnergyBurnedGoal="680" activeEnergyBurnedUnit="Cal" appleMoveTime="0" appleMoveTimeGoal="0" appleExerciseTime="53" appleExerciseTimeGoal="30" appleStandHours="15" appleStandHoursGoal="12"/>
303
+
304
+ <!-- Additional diverse records -->
305
+ <Record type="HKQuantityTypeIdentifierActiveEnergyBurned" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="Cal" creationDate="2023-11-20 14:30:00 +0100" startDate="2023-11-20 14:00:00 +0100" endDate="2023-11-20 14:30:00 +0100" value="45.3"/>
306
+
307
+ <Record type="HKQuantityTypeIdentifierBasalEnergyBurned" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="Cal" creationDate="2023-11-20 15:00:00 +0100" startDate="2023-11-20 14:00:00 +0100" endDate="2023-11-20 15:00:00 +0100" value="85.7"/>
308
+
309
+ <Record type="HKQuantityTypeIdentifierDistanceWalkingRunning" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="km" creationDate="2023-11-20 14:30:00 +0100" startDate="2023-11-20 14:00:00 +0100" endDate="2023-11-20 14:30:00 +0100" value="0.523"/>
310
+
311
+ <Record type="HKCategoryTypeIdentifierAppleStandHour" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" creationDate="2023-11-20 15:00:00 +0100" startDate="2023-11-20 14:00:00 +0100" endDate="2023-11-20 15:00:00 +0100" value="HKCategoryValueAppleStandHourStood"/>
312
+
313
+ <Record type="HKCategoryTypeIdentifierMindfulSession" sourceName="Breathe" sourceVersion="1.0" creationDate="2023-11-20 16:00:00 +0100" startDate="2023-11-20 15:55:00 +0100" endDate="2023-11-20 16:00:00 +0100" value="HKCategoryValueNotApplicable"/>
314
+
315
+ <Record type="HKQuantityTypeIdentifierBodyFatPercentage" sourceName="Health" sourceVersion="17.3" unit="%" creationDate="2024-01-10 09:00:00 +0100" startDate="2024-01-10 09:00:00 +0100" endDate="2024-01-10 09:00:00 +0100" value="18.5">
316
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
317
+ </Record>
318
+
319
+ <Record type="HKQuantityTypeIdentifierHeight" sourceName="Health" sourceVersion="17.3" unit="cm" creationDate="2023-12-01 10:00:00 +0100" startDate="2023-12-01 10:00:00 +0100" endDate="2023-12-01 10:00:00 +0100" value="180">
320
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
321
+ </Record>
322
+
323
+ <Record type="HKQuantityTypeIdentifierRestingHeartRate" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="count/min" creationDate="2023-11-21 07:00:00 +0100" startDate="2023-11-20 00:00:00 +0100" endDate="2023-11-21 00:00:00 +0100" value="52"/>
324
+
325
+ <Record type="HKQuantityTypeIdentifierVO2Max" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="mL/kg·min" creationDate="2023-11-20 18:00:00 +0100" startDate="2023-11-20 17:26:15 +0100" endDate="2023-11-20 18:00:00 +0100" value="45.2">
326
+ <MetadataEntry key="HKVO2MaxTestType" value="2"/>
327
+ </Record>
328
+
329
+ <Record type="HKQuantityTypeIdentifierAppleSleepingWristTemperature" sourceName="Guillaume's Apple Watch" sourceVersion="10.0" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch6,2, software:10.0&gt;" unit="degC" creationDate="2023-11-21 07:30:00 +0100" startDate="2023-11-21 01:00:00 +0100" endDate="2023-11-21 07:00:00 +0100" value="0.23">
330
+ <MetadataEntry key="HKSleepingWristTemperatureSourceType" value="0"/>
331
+ </Record>
332
+
333
+ <Record type="HKQuantityTypeIdentifierHeartRateVariabilitySDNN" sourceName="Guillaume's Apple Watch" sourceVersion="9.6.3" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch4,4, software:9.6.3&gt;" unit="ms" creationDate="2023-11-20 08:00:00 +0100" startDate="2023-11-20 07:55:00 +0100" endDate="2023-11-20 08:00:00 +0100" value="45.3">
334
+ <HeartRateVariabilityMetadataList>
335
+ <InstantaneousBeatsPerMinute bpm="72" time="7:47:41.86 PM"/>
336
+ <InstantaneousBeatsPerMinute bpm="68" time="7:47:53.78 PM"/>
337
+ </HeartRateVariabilityMetadataList>
338
+ </Record>
339
+
340
+ <Record type="HKQuantityTypeIdentifierOxygenSaturation" sourceName="Health" sourceVersion="17.3" unit="%" creationDate="2024-01-10 10:00:00 +0100" startDate="2024-01-10 10:00:00 +0100" endDate="2024-01-10 10:00:00 +0100" value="98">
341
+ <MetadataEntry key="HKWasUserEntered" value="1"/>
342
+ </Record>
343
+
344
+ <Record type="HKQuantityTypeIdentifierRespiratoryRate" sourceName="Guillaume's Apple Watch" sourceVersion="10.0" device="&lt;&lt;HKDevice: 0x533ad2350&gt;, name:Apple Watch, manufacturer:Apple Inc., model:Watch, hardware:Watch6,2, software:10.0&gt;" unit="count/min" creationDate="2023-11-21 07:00:00 +0100" startDate="2023-11-21 01:00:00 +0100" endDate="2023-11-21 07:00:00 +0100" value="14.5"/>
345
+
346
+ </HealthData>
mcp_server.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import sqlite3
3
+ import pandas as pd
4
+ import json
5
+ import os
6
+ from huggingface_hub import hf_hub_download
7
+
8
+ # Configuration
9
+ LOCAL_DB_PATH = "data/health_data.db"
10
+ DATA_REPO = os.getenv("DATA_REPO", None) # e.g., "username/project-data"
11
+ IS_HF_SPACE = os.getenv("SPACE_ID") is not None
12
+
13
+ def get_db_connection(token=None):
14
+ """Get a connection to the SQLite database."""
15
+ if DATA_REPO and IS_HF_SPACE:
16
+ # Running in HF Spaces - download from private dataset
17
+ try:
18
+ db_path = hf_hub_download(
19
+ repo_id=DATA_REPO,
20
+ filename="health_data.db",
21
+ repo_type="dataset",
22
+ token=token
23
+ )
24
+ return sqlite3.connect(db_path)
25
+ except Exception as e:
26
+ raise Exception(f"Failed to download database from {DATA_REPO}: {str(e)}")
27
+ else:
28
+ # Local development mode
29
+ if not os.path.exists(LOCAL_DB_PATH):
30
+ raise FileNotFoundError(f"Database file not found: {LOCAL_DB_PATH}. Run create_test_db.py first.")
31
+ return sqlite3.connect(LOCAL_DB_PATH)
32
+
33
+ def execute_sql_query(sql_query, hf_token=None):
34
+ """Execute any SQL query on the Apple Health SQLite database.
35
+
36
+ Args:
37
+ sql_query (str): The SQL query to execute
38
+ hf_token (str): Hugging Face token for accessing private dataset
39
+
40
+ Returns:
41
+ str: JSON formatted results or error message
42
+ """
43
+ if not sql_query or not sql_query.strip():
44
+ return "Error: Empty SQL query provided"
45
+
46
+ try:
47
+ conn = get_db_connection(token=hf_token)
48
+
49
+ # Execute the query
50
+ result = pd.read_sql_query(sql_query, conn)
51
+ conn.close()
52
+
53
+ # Convert to JSON
54
+ return json.dumps(result.to_dict('records'), indent=2)
55
+
56
+ except Exception as e:
57
+ return f"Error executing SQL query: {str(e)}"
58
+
59
+ # MCP Server Interface
60
+ with gr.Blocks(title="Apple Health MCP Server") as demo:
61
+ if IS_HF_SPACE and DATA_REPO:
62
+ gr.Markdown("# Apple Health MCP Server")
63
+ gr.Markdown(f"This is an MCP server for querying Apple Health data from dataset: `{DATA_REPO}`")
64
+ else:
65
+ gr.Markdown("# Apple Health MCP Server (Local Development)")
66
+ gr.Markdown(f"Database: `{LOCAL_DB_PATH}`")
67
+
68
+ with gr.Tab("SQL Query Interface"):
69
+ gr.Markdown("### Execute SQL Queries")
70
+ gr.Markdown("Enter any SQL query to execute against your Apple Health SQLite database.")
71
+
72
+ # Only show token input if running in HF Spaces
73
+ if IS_HF_SPACE and DATA_REPO:
74
+ hf_token_input = gr.Textbox(
75
+ label="Hugging Face Token",
76
+ placeholder="hf_...",
77
+ type="password",
78
+ info="Your HF token to access the private dataset. Get it from https://huggingface.co/settings/tokens"
79
+ )
80
+ else:
81
+ hf_token_input = gr.Textbox(visible=False, value=None)
82
+
83
+ sql_input = gr.Textbox(
84
+ label="SQL Query",
85
+ placeholder="SELECT * FROM activity_summaries LIMIT 10;",
86
+ lines=5,
87
+ value="SELECT name FROM sqlite_master WHERE type='table';",
88
+ info="Available tables depend on your export data. Run the first sample query to see all tables."
89
+ )
90
+
91
+ query_btn = gr.Button("Execute Query", variant="primary")
92
+ output = gr.Code(language="json", label="Query Results")
93
+
94
+ # Sample queries for easy testing
95
+ gr.Markdown("### Sample Queries")
96
+ gr.Examples(
97
+ examples=[
98
+ ["SELECT name FROM sqlite_master WHERE type='table';"],
99
+ ["SELECT * FROM workout LIMIT 5;"],
100
+ ["SELECT * FROM instantaneousbeatsperminute;"],
101
+ ],
102
+ inputs=sql_input
103
+ )
104
+
105
+ query_btn.click(
106
+ fn=execute_sql_query,
107
+ inputs=[sql_input, hf_token_input],
108
+ outputs=output
109
+ )
110
+
111
+ with gr.Tab("MCP Endpoint"):
112
+ if IS_HF_SPACE:
113
+ space_id = os.getenv("SPACE_ID", "your-space-id")
114
+ gr.Markdown(f"""
115
+ ## MCP Server Endpoint
116
+
117
+ This space can be used as an MCP server with the following configuration:
118
+
119
+ ```json
120
+ {{
121
+ "mcpServers": {{
122
+ "apple-health": {{
123
+ "command": "npx",
124
+ "args": [
125
+ "mcp-remote",
126
+ "https://huggingface.co/spaces/{space_id}/gradio_api/mcp/sse",
127
+ "--header",
128
+ "Authorization:${{AUTH_HEADER}}"
129
+ ],
130
+ "env": {{
131
+ "AUTH_HEADER": "Bearer YOUR_HF_TOKEN_HERE"
132
+ }}
133
+ }}
134
+ }}
135
+ }}
136
+ ```
137
+
138
+ **Setup Instructions:**
139
+ 1. Replace `YOUR_HF_TOKEN_HERE` with your actual Hugging Face token
140
+ 2. Add this configuration to your Claude Desktop config file
141
+ 3. Claude will be able to query your Apple Health data using SQL
142
+ """)
143
+ else:
144
+ gr.Markdown("""
145
+ ## MCP Server Endpoint
146
+
147
+ This local server can be used as an MCP server with the following configuration:
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "apple-health-local": {
153
+ "command": "npx",
154
+ "args": [
155
+ "mcp-remote",
156
+ "http://localhost:7860/gradio_api/mcp/sse"
157
+ ]
158
+ }
159
+ }
160
+ }
161
+ ```
162
+
163
+ **Setup Instructions:**
164
+ 1. Run this server: `python mcp_server.py`
165
+ 2. Add the above configuration to your Claude Desktop config file
166
+ 3. Claude will be able to query your Apple Health data using SQL
167
+
168
+ **Note:** This is a local development server. No authentication is required.
169
+ """)
170
+
171
+ if __name__ == "__main__":
172
+ if IS_HF_SPACE:
173
+ print(f"Starting Apple Health MCP Server in HF Spaces mode")
174
+ if DATA_REPO:
175
+ print(f"Data repository: {DATA_REPO}")
176
+ else:
177
+ print("Warning: DATA_REPO environment variable not set")
178
+ else:
179
+ print(f"Starting Apple Health MCP Server (Local Development)")
180
+ print(f"Database path: {LOCAL_DB_PATH}")
181
+
182
+ demo.launch(mcp_server=True, server_name="0.0.0.0" if not IS_HF_SPACE else None)