thryve-mcp-server 0.0.1

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.
Files changed (197) hide show
  1. package/Overview.csv +351 -0
  2. package/README.md +229 -0
  3. package/dist/category-mappings.d.ts +34 -0
  4. package/dist/category-mappings.d.ts.map +1 -0
  5. package/dist/category-mappings.js +146 -0
  6. package/dist/category-mappings.js.map +1 -0
  7. package/dist/data-enrichment.d.ts +19 -0
  8. package/dist/data-enrichment.d.ts.map +1 -0
  9. package/dist/data-enrichment.js +105 -0
  10. package/dist/data-enrichment.js.map +1 -0
  11. package/dist/data-types-generated.d.ts +70 -0
  12. package/dist/data-types-generated.d.ts.map +1 -0
  13. package/dist/data-types-generated.js +2597 -0
  14. package/dist/data-types-generated.js.map +1 -0
  15. package/dist/data-types.d.ts +53 -0
  16. package/dist/data-types.d.ts.map +1 -0
  17. package/dist/data-types.js +138 -0
  18. package/dist/data-types.js.map +1 -0
  19. package/dist/debug-logger.d.ts +17 -0
  20. package/dist/debug-logger.d.ts.map +1 -0
  21. package/dist/debug-logger.js +35 -0
  22. package/dist/debug-logger.js.map +1 -0
  23. package/dist/debug-middleware.d.ts +12 -0
  24. package/dist/debug-middleware.d.ts.map +1 -0
  25. package/dist/debug-middleware.js +36 -0
  26. package/dist/debug-middleware.js.map +1 -0
  27. package/dist/helpers.d.ts +19 -0
  28. package/dist/helpers.d.ts.map +1 -0
  29. package/dist/helpers.js +166 -0
  30. package/dist/helpers.js.map +1 -0
  31. package/dist/index.d.ts +3 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +754 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/schemas.d.ts +557 -0
  36. package/dist/schemas.d.ts.map +1 -0
  37. package/dist/schemas.js +338 -0
  38. package/dist/schemas.js.map +1 -0
  39. package/dist/tool-loader.d.ts +34 -0
  40. package/dist/tool-loader.d.ts.map +1 -0
  41. package/dist/tool-loader.js +108 -0
  42. package/dist/tool-loader.js.map +1 -0
  43. package/dist/tool-registry.d.ts +44 -0
  44. package/dist/tool-registry.d.ts.map +1 -0
  45. package/dist/tool-registry.js +54 -0
  46. package/dist/tool-registry.js.map +1 -0
  47. package/dist/tools/baseCategoryHandler.d.ts +28 -0
  48. package/dist/tools/baseCategoryHandler.d.ts.map +1 -0
  49. package/dist/tools/baseCategoryHandler.js +184 -0
  50. package/dist/tools/baseCategoryHandler.js.map +1 -0
  51. package/dist/tools/categories/getActivity.d.ts +10 -0
  52. package/dist/tools/categories/getActivity.d.ts.map +1 -0
  53. package/dist/tools/categories/getActivity.js +5 -0
  54. package/dist/tools/categories/getActivity.js.map +1 -0
  55. package/dist/tools/categories/getAudioAndHearing.d.ts +10 -0
  56. package/dist/tools/categories/getAudioAndHearing.d.ts.map +1 -0
  57. package/dist/tools/categories/getAudioAndHearing.js +5 -0
  58. package/dist/tools/categories/getAudioAndHearing.js.map +1 -0
  59. package/dist/tools/categories/getBloodGlucose.d.ts +10 -0
  60. package/dist/tools/categories/getBloodGlucose.d.ts.map +1 -0
  61. package/dist/tools/categories/getBloodGlucose.js +5 -0
  62. package/dist/tools/categories/getBloodGlucose.js.map +1 -0
  63. package/dist/tools/categories/getBodyComposition.d.ts +10 -0
  64. package/dist/tools/categories/getBodyComposition.d.ts.map +1 -0
  65. package/dist/tools/categories/getBodyComposition.js +5 -0
  66. package/dist/tools/categories/getBodyComposition.js.map +1 -0
  67. package/dist/tools/categories/getCardiovascular.d.ts +10 -0
  68. package/dist/tools/categories/getCardiovascular.d.ts.map +1 -0
  69. package/dist/tools/categories/getCardiovascular.js +5 -0
  70. package/dist/tools/categories/getCardiovascular.js.map +1 -0
  71. package/dist/tools/categories/getHeartRate.d.ts +10 -0
  72. package/dist/tools/categories/getHeartRate.d.ts.map +1 -0
  73. package/dist/tools/categories/getHeartRate.js +5 -0
  74. package/dist/tools/categories/getHeartRate.js.map +1 -0
  75. package/dist/tools/categories/getLocation.d.ts +10 -0
  76. package/dist/tools/categories/getLocation.d.ts.map +1 -0
  77. package/dist/tools/categories/getLocation.js +5 -0
  78. package/dist/tools/categories/getLocation.js.map +1 -0
  79. package/dist/tools/categories/getMicrobiome.d.ts +10 -0
  80. package/dist/tools/categories/getMicrobiome.d.ts.map +1 -0
  81. package/dist/tools/categories/getMicrobiome.js +5 -0
  82. package/dist/tools/categories/getMicrobiome.js.map +1 -0
  83. package/dist/tools/categories/getMovementAnalysis.d.ts +10 -0
  84. package/dist/tools/categories/getMovementAnalysis.d.ts.map +1 -0
  85. package/dist/tools/categories/getMovementAnalysis.js +5 -0
  86. package/dist/tools/categories/getMovementAnalysis.js.map +1 -0
  87. package/dist/tools/categories/getNutrition.d.ts +10 -0
  88. package/dist/tools/categories/getNutrition.d.ts.map +1 -0
  89. package/dist/tools/categories/getNutrition.js +5 -0
  90. package/dist/tools/categories/getNutrition.js.map +1 -0
  91. package/dist/tools/categories/getRespiratory.d.ts +10 -0
  92. package/dist/tools/categories/getRespiratory.d.ts.map +1 -0
  93. package/dist/tools/categories/getRespiratory.js +5 -0
  94. package/dist/tools/categories/getRespiratory.js.map +1 -0
  95. package/dist/tools/categories/getSleep.d.ts +10 -0
  96. package/dist/tools/categories/getSleep.d.ts.map +1 -0
  97. package/dist/tools/categories/getSleep.js +5 -0
  98. package/dist/tools/categories/getSleep.js.map +1 -0
  99. package/dist/tools/categories/getStressAndHRV.d.ts +10 -0
  100. package/dist/tools/categories/getStressAndHRV.d.ts.map +1 -0
  101. package/dist/tools/categories/getStressAndHRV.js +5 -0
  102. package/dist/tools/categories/getStressAndHRV.js.map +1 -0
  103. package/dist/tools/categories/getWellness.d.ts +10 -0
  104. package/dist/tools/categories/getWellness.d.ts.map +1 -0
  105. package/dist/tools/categories/getWellness.js +5 -0
  106. package/dist/tools/categories/getWellness.js.map +1 -0
  107. package/dist/tools/categories/getWomensHealth.d.ts +10 -0
  108. package/dist/tools/categories/getWomensHealth.d.ts.map +1 -0
  109. package/dist/tools/categories/getWomensHealth.js +5 -0
  110. package/dist/tools/categories/getWomensHealth.js.map +1 -0
  111. package/dist/tools/categories/getWorkouts.d.ts +10 -0
  112. package/dist/tools/categories/getWorkouts.d.ts.map +1 -0
  113. package/dist/tools/categories/getWorkouts.js +5 -0
  114. package/dist/tools/categories/getWorkouts.js.map +1 -0
  115. package/dist/tools/categoryTools.d.ts +100 -0
  116. package/dist/tools/categoryTools.d.ts.map +1 -0
  117. package/dist/tools/categoryTools.js +66 -0
  118. package/dist/tools/categoryTools.js.map +1 -0
  119. package/dist/tools/get.d.ts +39 -0
  120. package/dist/tools/get.d.ts.map +1 -0
  121. package/dist/tools/get.js +118 -0
  122. package/dist/tools/get.js.map +1 -0
  123. package/dist/tools/getAvailableDataTypes.d.ts +101 -0
  124. package/dist/tools/getAvailableDataTypes.d.ts.map +1 -0
  125. package/dist/tools/getAvailableDataTypes.js +120 -0
  126. package/dist/tools/getAvailableDataTypes.js.map +1 -0
  127. package/dist/tools/getConnectionWidgetUrl.d.ts +12 -0
  128. package/dist/tools/getConnectionWidgetUrl.d.ts.map +1 -0
  129. package/dist/tools/getConnectionWidgetUrl.js +26 -0
  130. package/dist/tools/getConnectionWidgetUrl.js.map +1 -0
  131. package/dist/tools/getDailyData.d.ts +30 -0
  132. package/dist/tools/getDailyData.d.ts.map +1 -0
  133. package/dist/tools/getDailyData.js +114 -0
  134. package/dist/tools/getDailyData.js.map +1 -0
  135. package/dist/tools/getEpochData.d.ts +30 -0
  136. package/dist/tools/getEpochData.d.ts.map +1 -0
  137. package/dist/tools/getEpochData.js +111 -0
  138. package/dist/tools/getEpochData.js.map +1 -0
  139. package/dist/tools/getHealthDataByCategory.d.ts +38 -0
  140. package/dist/tools/getHealthDataByCategory.d.ts.map +1 -0
  141. package/dist/tools/getHealthDataByCategory.js +231 -0
  142. package/dist/tools/getHealthDataByCategory.js.map +1 -0
  143. package/dist/tools/getUserInformation.d.ts +8 -0
  144. package/dist/tools/getUserInformation.d.ts.map +1 -0
  145. package/dist/tools/getUserInformation.js +24 -0
  146. package/dist/tools/getUserInformation.js.map +1 -0
  147. package/dist/tools/index.d.ts +7 -0
  148. package/dist/tools/index.d.ts.map +1 -0
  149. package/dist/tools/index.js +12 -0
  150. package/dist/tools/index.js.map +1 -0
  151. package/dist/tools/legacy.d.ts +37 -0
  152. package/dist/tools/legacy.d.ts.map +1 -0
  153. package/dist/tools/legacy.js +105 -0
  154. package/dist/tools/legacy.js.map +1 -0
  155. package/dist/tools/listConnectedSources.d.ts +11 -0
  156. package/dist/tools/listConnectedSources.d.ts.map +1 -0
  157. package/dist/tools/listConnectedSources.js +29 -0
  158. package/dist/tools/listConnectedSources.js.map +1 -0
  159. package/dist/tools/listDevices.d.ts +11 -0
  160. package/dist/tools/listDevices.d.ts.map +1 -0
  161. package/dist/tools/listDevices.js +29 -0
  162. package/dist/tools/listDevices.js.map +1 -0
  163. package/dist/tools/search-filters.d.ts +42 -0
  164. package/dist/tools/search-filters.d.ts.map +1 -0
  165. package/dist/tools/search-filters.js +210 -0
  166. package/dist/tools/search-filters.js.map +1 -0
  167. package/dist/tools/search-keyword.d.ts +32 -0
  168. package/dist/tools/search-keyword.d.ts.map +1 -0
  169. package/dist/tools/search-keyword.js +122 -0
  170. package/dist/tools/search-keyword.js.map +1 -0
  171. package/dist/tools/search-tools.d.ts +8 -0
  172. package/dist/tools/search-tools.d.ts.map +1 -0
  173. package/dist/tools/search-tools.js +273 -0
  174. package/dist/tools/search-tools.js.map +1 -0
  175. package/dist/types.d.ts +37 -0
  176. package/dist/types.d.ts.map +1 -0
  177. package/dist/types.js +2 -0
  178. package/dist/types.js.map +1 -0
  179. package/package.json +30 -0
  180. package/parse-csv.py +152 -0
  181. package/src/category-mappings.ts +181 -0
  182. package/src/data-enrichment.ts +125 -0
  183. package/src/data-types-generated.ts +2652 -0
  184. package/src/helpers.ts +198 -0
  185. package/src/index.ts +859 -0
  186. package/src/schemas.ts +372 -0
  187. package/src/tools/baseCategoryHandler.ts +243 -0
  188. package/src/tools/categoryTools.ts +101 -0
  189. package/src/tools/get.ts +147 -0
  190. package/src/tools/getAvailableDataTypes.ts +148 -0
  191. package/src/tools/index.ts +32 -0
  192. package/src/tools/listConnectedSources.ts +45 -0
  193. package/src/tools/listDevices.ts +45 -0
  194. package/src/tools/search-filters.ts +253 -0
  195. package/src/tools/search-keyword.ts +162 -0
  196. package/src/types.ts +44 -0
  197. package/tsconfig.json +20 -0
package/src/index.ts ADDED
@@ -0,0 +1,859 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
+ } from "@modelcontextprotocol/sdk/types.js";
9
+ import express, { type Request } from "express";
10
+ import { z } from "zod";
11
+
12
+ // Import schemas
13
+ import {
14
+ GetAvailableDataTypesSchema,
15
+ ListConnectedSourcesSchema,
16
+ ListDevicesSchema,
17
+ GetActivitySchema,
18
+ GetHeartRateSchema,
19
+ GetSleepSchema,
20
+ GetWorkoutsSchema,
21
+ GetStressAndHRVSchema,
22
+ GetRespiratorySchema,
23
+ GetBloodGlucoseSchema,
24
+ GetBodyCompositionSchema,
25
+ GetWomensHealthSchema,
26
+ GetNutritionSchema,
27
+ GetCardiovascularSchema,
28
+ GetWellnessSchema,
29
+ GetAudioAndHearingSchema,
30
+ GetMicrobiomeSchema,
31
+ GetLocationSchema,
32
+ GetMovementAnalysisSchema,
33
+ SearchKeywordSchema,
34
+ SearchFiltersSchema,
35
+ } from "./schemas.js";
36
+
37
+ // Import tool handlers
38
+ import {
39
+ getAvailableDataTypes,
40
+ listConnectedSources,
41
+ listDevices,
42
+ getActivity,
43
+ getHeartRate,
44
+ getSleep,
45
+ getWorkouts,
46
+ getStressAndHRV,
47
+ getRespiratory,
48
+ getBloodGlucose,
49
+ getBodyComposition,
50
+ getWomensHealth,
51
+ getNutrition,
52
+ getCardiovascular,
53
+ getWellness,
54
+ getAudioAndHearing,
55
+ getMicrobiome,
56
+ getLocation,
57
+ getMovementAnalysis,
58
+ searchKeyword,
59
+ searchFilters,
60
+ } from "./tools/index.js";
61
+
62
+ // Create MCP server
63
+ const server = new Server(
64
+ {
65
+ name: "thryve-mcp-server",
66
+ version: "1.0.0",
67
+ },
68
+ {
69
+ capabilities: {
70
+ tools: {},
71
+ },
72
+ }
73
+ );
74
+
75
+ // Register tool handlers
76
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
77
+ return {
78
+ tools: [
79
+ // PRIMARY CATEGORY-SPECIFIC TOOLS
80
+ {
81
+ name: "GetActivityData",
82
+ description:
83
+ "Get Activity data including steps, distance, floors climbed, elevation, calories burned, and general activity metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Activity.",
84
+ inputSchema: {
85
+ type: "object",
86
+ properties: {
87
+ resolution: {
88
+ type: "string",
89
+ enum: ["daily", "epoch"],
90
+ description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')",
91
+ },
92
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format (e.g., '2025-10-28T00:00:00Z'). Defaults to 7 days ago" },
93
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format (e.g., '2025-10-28T23:59:59Z'). Defaults to now" },
94
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs (e.g., [1, 2] for Fitbit and Garmin)" },
95
+ limit: { type: "number", description: "Maximum records to return" },
96
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Activity'])" },
97
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
98
+ },
99
+ },
100
+ },
101
+ {
102
+ name: "GetHeartRateData",
103
+ description:
104
+ "Get Heart Rate data including resting heart rate, average heart rate, heart rate zones, and heart rate variability. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Heart Rate.",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
109
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
110
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
111
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
112
+ limit: { type: "number", description: "Maximum records to return" },
113
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Heart Rate'])" },
114
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
115
+ },
116
+ },
117
+ },
118
+ {
119
+ name: "GetSleepData",
120
+ description:
121
+ "Get Sleep data including sleep duration, sleep stages (deep, light, REM), sleep efficiency, sleep quality, and sleep-related metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Sleep.",
122
+ inputSchema: {
123
+ type: "object",
124
+ properties: {
125
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
126
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
127
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
128
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
129
+ limit: { type: "number", description: "Maximum records to return" },
130
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Sleep'])" },
131
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
132
+ },
133
+ },
134
+ },
135
+ {
136
+ name: "GetWorkoutsData",
137
+ description:
138
+ "Get Workouts data including exercise sessions, workout types, duration, intensity, performance metrics (cadence, speed, pace, power), and biomechanics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Workouts.",
139
+ inputSchema: {
140
+ type: "object",
141
+ properties: {
142
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
143
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
144
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
145
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
146
+ limit: { type: "number", description: "Maximum records to return" },
147
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Workouts'])" },
148
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
149
+ },
150
+ },
151
+ },
152
+ {
153
+ name: "GetStressAndHRVData",
154
+ description:
155
+ "Get Stress & HRV data including stress levels, heart rate variability (HRV) metrics like RMSSD and SDNN, frequency power analysis, and recovery metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Stress & HRV.",
156
+ inputSchema: {
157
+ type: "object",
158
+ properties: {
159
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
160
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
161
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
162
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
163
+ limit: { type: "number", description: "Maximum records to return" },
164
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Stress & HRV'])" },
165
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
166
+ },
167
+ },
168
+ },
169
+ {
170
+ name: "GetRespiratoryData",
171
+ description:
172
+ "Get Respiratory data including breathing rate, SpO2 (blood oxygen), respiratory rate variability, and lung function metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Respiratory & Breathing.",
173
+ inputSchema: {
174
+ type: "object",
175
+ properties: {
176
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
177
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
178
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
179
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
180
+ limit: { type: "number", description: "Maximum records to return" },
181
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Respiratory & Breathing'])" },
182
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
183
+ },
184
+ },
185
+ },
186
+ {
187
+ name: "GetBloodGlucoseData",
188
+ description:
189
+ "Get Blood Glucose data including glucose levels, CGM (continuous glucose monitoring) data, and HbA1c measurements. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Blood Glucose.",
190
+ inputSchema: {
191
+ type: "object",
192
+ properties: {
193
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
194
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
195
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
196
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
197
+ limit: { type: "number", description: "Maximum records to return" },
198
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Blood Glucose'])" },
199
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
200
+ },
201
+ },
202
+ },
203
+ {
204
+ name: "GetBodyCompositionData",
205
+ description:
206
+ "Get Body Composition data including weight, height, BMI, body fat percentage, muscle mass, bone mass, and body measurements. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Body Composition & Measurements.",
207
+ inputSchema: {
208
+ type: "object",
209
+ properties: {
210
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
211
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
212
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
213
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
214
+ limit: { type: "number", description: "Maximum records to return" },
215
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Body Composition & Measurements'])" },
216
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
217
+ },
218
+ },
219
+ },
220
+ {
221
+ name: "GetWomensHealthData",
222
+ description:
223
+ "Get Women's Health data including menstrual cycle tracking, ovulation, fertility prediction, symptoms, and reproductive health metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Women's Health.",
224
+ inputSchema: {
225
+ type: "object",
226
+ properties: {
227
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
228
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
229
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
230
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
231
+ limit: { type: "number", description: "Maximum records to return" },
232
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Women\\'s Health'])" },
233
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
234
+ },
235
+ },
236
+ },
237
+ {
238
+ name: "GetNutritionData",
239
+ description:
240
+ "Get Nutrition data including calories, macros (protein, carbs, fats), micronutrients (vitamins, minerals), hydration, and meal consumption data. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Detailed Nutrition.",
241
+ inputSchema: {
242
+ type: "object",
243
+ properties: {
244
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
245
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
246
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
247
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
248
+ limit: { type: "number", description: "Maximum records to return" },
249
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Detailed Nutrition'])" },
250
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
251
+ },
252
+ },
253
+ },
254
+ {
255
+ name: "GetCardiovascularData",
256
+ description:
257
+ "Get Cardiovascular data including blood pressure, VO2 max, cardiac analysis, arrhythmia detection, and ECG data. Aggregates both cardiovascular health metrics and advanced cardiac analysis. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Cardiovascular Health, Advanced Cardiac Analysis.",
258
+ inputSchema: {
259
+ type: "object",
260
+ properties: {
261
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
262
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
263
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
264
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
265
+ limit: { type: "number", description: "Maximum records to return" },
266
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Cardiovascular Health', 'Advanced Cardiac Analysis'])" },
267
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude (e.g., ['Advanced Cardiac Analysis'])" },
268
+ },
269
+ },
270
+ },
271
+ {
272
+ name: "GetWellnessData",
273
+ description:
274
+ "Get Wellness data including mood, energy levels, self-reported health data, lifestyle events, and general wellness metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Self-Reported Wellness.",
275
+ inputSchema: {
276
+ type: "object",
277
+ properties: {
278
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
279
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
280
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
281
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
282
+ limit: { type: "number", description: "Maximum records to return" },
283
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Self-Reported Wellness'])" },
284
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
285
+ },
286
+ },
287
+ },
288
+ {
289
+ name: "GetAudioAndHearingData",
290
+ description:
291
+ "Get Audio & Hearing data including hearing health, audio exposure levels (headphone and ambient), and environmental sound measurements. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Audio & Hearing Health.",
292
+ inputSchema: {
293
+ type: "object",
294
+ properties: {
295
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
296
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
297
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
298
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
299
+ limit: { type: "number", description: "Maximum records to return" },
300
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Audio & Hearing Health'])" },
301
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
302
+ },
303
+ },
304
+ },
305
+ {
306
+ name: "GetMicrobiomeData",
307
+ description:
308
+ "Get Microbiome data including gut health metrics, microbiome composition across different bacterial species, and digestive metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Microbiome & Gut Health.",
309
+ inputSchema: {
310
+ type: "object",
311
+ properties: {
312
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
313
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
314
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
315
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
316
+ limit: { type: "number", description: "Maximum records to return" },
317
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Microbiome & Gut Health'])" },
318
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
319
+ },
320
+ },
321
+ },
322
+ {
323
+ name: "GetLocationData",
324
+ description:
325
+ "Get Location data including GPS coordinates (latitude, longitude), routes, and location tracking data. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Location & GPS.",
326
+ inputSchema: {
327
+ type: "object",
328
+ properties: {
329
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
330
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
331
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
332
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
333
+ limit: { type: "number", description: "Maximum records to return" },
334
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Location & GPS'])" },
335
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
336
+ },
337
+ },
338
+ },
339
+ {
340
+ name: "GetMovementAnalysisData",
341
+ description:
342
+ "Get Movement Analysis data including movement patterns, gait analysis, stride length, ground contact time, vertical oscillation, and biomechanics metrics. Supports both daily aggregated data and intraday time-series data via the 'resolution' parameter. Sub-categories available for filtering with includeFields/excludeFields: Movement Analysis.",
343
+ inputSchema: {
344
+ type: "object",
345
+ properties: {
346
+ resolution: { type: "string", enum: ["daily", "epoch"], description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data (default: 'daily')" },
347
+ startTimestamp: { type: "string", description: "Start time in ISO 8601 format. Defaults to 7 days ago" },
348
+ endTimestamp: { type: "string", description: "End time in ISO 8601 format. Defaults to now" },
349
+ dataSources: { type: "array", items: { type: "number" }, description: "Filter by data source IDs" },
350
+ limit: { type: "number", description: "Maximum records to return" },
351
+ includeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to include (e.g., ['Movement Analysis'])" },
352
+ excludeFields: { type: "array", items: { type: "string" }, description: "Sub-categories to exclude" },
353
+ },
354
+ },
355
+ },
356
+
357
+ // REFERENCE TOOLS
358
+ {
359
+ name: "getAvailableDataTypes",
360
+ description:
361
+ "Get comprehensive catalog of 250+ available health data types organized by the 16 high-level health categories (Activity, Heart Rate, Sleep, etc.). Returns category descriptions, data type details, units, and which wearables support each type. Use this to understand what data is available in each category before making data requests.",
362
+ inputSchema: {
363
+ type: "object",
364
+ properties: {
365
+ category: {
366
+ type: "string",
367
+ enum: [
368
+ "Activity",
369
+ "Heart Rate",
370
+ "Sleep",
371
+ "Workouts",
372
+ "Stress & HRV",
373
+ "Respiratory",
374
+ "Blood Glucose",
375
+ "Body Composition",
376
+ "Womens Health",
377
+ "Nutrition",
378
+ "Cardiovascular",
379
+ "Wellness",
380
+ "Audio & Hearing",
381
+ "Microbiome",
382
+ "Location",
383
+ "Movement Analysis",
384
+ ],
385
+ description: "Filter by high-level health category to see available data types in that category",
386
+ },
387
+ search: {
388
+ type: "string",
389
+ description: "Search for data types by keyword (e.g., 'calories', 'steps', 'heart'). Results will be grouped by category.",
390
+ },
391
+ dataSource: {
392
+ type: "string",
393
+ description: "Filter by wearable brand (e.g., 'Fitbit', 'Garmin', 'Whoop', 'Oura'). Results will be grouped by category.",
394
+ },
395
+ },
396
+ },
397
+ },
398
+
399
+ // SEARCH AND RETRIEVAL TOOLS
400
+ {
401
+ name: "searchKeyword",
402
+ description:
403
+ "Search for health data by property name. Searches for data types matching the property name (e.g., 'SleepBinary', 'Steps', 'HeartRate') and returns matching records within the specified time range.",
404
+ inputSchema: {
405
+ type: "object",
406
+ properties: {
407
+ query: {
408
+ type: "string",
409
+ description: "Property name to search for (e.g., 'SleepBinary', 'Steps', 'HeartRate'). Searches for exact or partial matches in data type property names"
410
+ },
411
+ startDate: { type: "string", description: "Start date in YYYY-MM-DD format" },
412
+ endDate: { type: "string", description: "End date in YYYY-MM-DD format" },
413
+ dataType: { type: "string", description: "Filter by data type category (e.g., 'Activity', 'Sleep')" },
414
+ dataSource: { type: "string", description: "Filter by data source ID" },
415
+ page: { type: "number", description: "Page number for pagination (default: 1)" },
416
+ pageSize: { type: "number", description: "Results per page (default: 50, max: 200)" },
417
+ sortBy: {
418
+ type: "string",
419
+ enum: ["timestamp", "day", "value", "dataType", "dataSource"],
420
+ description: "Field to sort by (default: 'timestamp')"
421
+ },
422
+ sortOrder: {
423
+ type: "string",
424
+ enum: ["asc", "desc"],
425
+ description: "Sort order (default: 'desc')"
426
+ },
427
+ },
428
+ required: ["query"],
429
+ },
430
+ },
431
+ {
432
+ name: "searchFilters",
433
+ description:
434
+ "Advanced search with comprehensive filtering options. Supports filtering by data types, categories, data sources, value ranges, and time ranges. Can group results by category or return flat paginated results. Replaces the legacy getDailyData and getEpochData tools with unified functionality.",
435
+ inputSchema: {
436
+ type: "object",
437
+ properties: {
438
+ resolution: {
439
+ type: "string",
440
+ enum: ["daily", "epoch", "both"],
441
+ description: "Data resolution: 'daily' for daily aggregated data, 'epoch' for intraday time-series data, 'both' for combined (default: 'both')"
442
+ },
443
+ dataTypes: {
444
+ type: "array",
445
+ items: { type: "number" },
446
+ description: "Filter by specific data type IDs (e.g., [1000, 3000] for Steps and HeartRate)"
447
+ },
448
+ dataSources: {
449
+ type: "array",
450
+ items: { type: "number" },
451
+ description: "Filter by data source IDs (e.g., [1, 2] for Fitbit and Garmin)"
452
+ },
453
+ categories: {
454
+ type: "array",
455
+ items: {
456
+ type: "string",
457
+ enum: [
458
+ "Activity", "Heart Rate", "Sleep", "Workouts", "Stress & HRV",
459
+ "Respiratory", "Blood Glucose", "Body Composition", "Womens Health",
460
+ "Nutrition", "Cardiovascular", "Wellness", "Audio & Hearing",
461
+ "Microbiome", "Location", "Movement Analysis"
462
+ ]
463
+ },
464
+ description: "Filter by high-level health categories"
465
+ },
466
+ groupByCategory: {
467
+ type: "boolean",
468
+ description: "Group results by category (default: false). Returns { results: { Category: [...] }, metadata: {...} } format"
469
+ },
470
+ limit: { type: "number", description: "Maximum number of records to return" },
471
+ startDate: { type: "string", description: "Start date in YYYY-MM-DD format" },
472
+ endDate: { type: "string", description: "End date in YYYY-MM-DD format" },
473
+ startTimestamp: { type: "string", description: "Start timestamp in ISO 8601 format" },
474
+ endTimestamp: { type: "string", description: "End timestamp in ISO 8601 format" },
475
+ valueMin: { type: "number", description: "Minimum value threshold" },
476
+ valueMax: { type: "number", description: "Maximum value threshold" },
477
+ page: { type: "number", description: "Page number for pagination (default: 1)" },
478
+ pageSize: { type: "number", description: "Results per page (default: 50, max: 200)" },
479
+ sortBy: {
480
+ type: "string",
481
+ enum: ["timestamp", "day", "value", "dataType", "dataSource"],
482
+ description: "Field to sort by (default: 'timestamp')"
483
+ },
484
+ sortOrder: {
485
+ type: "string",
486
+ enum: ["asc", "desc"],
487
+ description: "Sort order (default: 'desc')"
488
+ },
489
+ },
490
+ },
491
+ },
492
+ // END USER INFORMATION TOOLS
493
+ {
494
+ name: "listConnectedSources",
495
+ description:
496
+ "List all data sources (wearables, apps) that the user has connected to their Thryve account. Returns information about each connected source including the data source ID and when it was connected.",
497
+ inputSchema: {
498
+ type: "object",
499
+ properties: {},
500
+ },
501
+ },
502
+ {
503
+ name: "listDevices",
504
+ description:
505
+ "List all devices that the user has connected through their data sources. Returns device names, associated data sources, connection times, and device configurations.",
506
+ inputSchema: {
507
+ type: "object",
508
+ properties: {},
509
+ },
510
+ },
511
+ ],
512
+ };
513
+ });
514
+
515
+ // Store current request in context (set during HTTP request handling)
516
+ let currentRequest: Request | null = null;
517
+
518
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
519
+ if (!currentRequest) {
520
+ throw new Error("No request context available. This server must be called via HTTP transport.");
521
+ }
522
+
523
+ try {
524
+ switch (request.params.name) {
525
+ // CATEGORY-SPECIFIC TOOLS
526
+ case "GetActivityData": {
527
+ const args = GetActivitySchema.parse(request.params.arguments);
528
+ const data = await getActivity(currentRequest, args);
529
+ return {
530
+ content: [
531
+ {
532
+ type: "text",
533
+ text: JSON.stringify(data, null, 2),
534
+ },
535
+ ],
536
+ };
537
+ }
538
+
539
+ case "GetHeartRateData": {
540
+ const args = GetHeartRateSchema.parse(request.params.arguments);
541
+ const data = await getHeartRate(currentRequest, args);
542
+ return {
543
+ content: [
544
+ {
545
+ type: "text",
546
+ text: JSON.stringify(data, null, 2),
547
+ },
548
+ ],
549
+ };
550
+ }
551
+
552
+ case "GetSleepData": {
553
+ const args = GetSleepSchema.parse(request.params.arguments);
554
+ const data = await getSleep(currentRequest, args);
555
+ return {
556
+ content: [
557
+ {
558
+ type: "text",
559
+ text: JSON.stringify(data, null, 2),
560
+ },
561
+ ],
562
+ };
563
+ }
564
+
565
+ case "GetWorkoutsData": {
566
+ const args = GetWorkoutsSchema.parse(request.params.arguments);
567
+ const data = await getWorkouts(currentRequest, args);
568
+ return {
569
+ content: [
570
+ {
571
+ type: "text",
572
+ text: JSON.stringify(data, null, 2),
573
+ },
574
+ ],
575
+ };
576
+ }
577
+
578
+ case "GetStressAndHRVData": {
579
+ const args = GetStressAndHRVSchema.parse(request.params.arguments);
580
+ const data = await getStressAndHRV(currentRequest, args);
581
+ return {
582
+ content: [
583
+ {
584
+ type: "text",
585
+ text: JSON.stringify(data, null, 2),
586
+ },
587
+ ],
588
+ };
589
+ }
590
+
591
+ case "GetRespiratoryData": {
592
+ const args = GetRespiratorySchema.parse(request.params.arguments);
593
+ const data = await getRespiratory(currentRequest, args);
594
+ return {
595
+ content: [
596
+ {
597
+ type: "text",
598
+ text: JSON.stringify(data, null, 2),
599
+ },
600
+ ],
601
+ };
602
+ }
603
+
604
+ case "GetBloodGlucoseData": {
605
+ const args = GetBloodGlucoseSchema.parse(request.params.arguments);
606
+ const data = await getBloodGlucose(currentRequest, args);
607
+ return {
608
+ content: [
609
+ {
610
+ type: "text",
611
+ text: JSON.stringify(data, null, 2),
612
+ },
613
+ ],
614
+ };
615
+ }
616
+
617
+ case "GetBodyCompositionData": {
618
+ const args = GetBodyCompositionSchema.parse(request.params.arguments);
619
+ const data = await getBodyComposition(currentRequest, args);
620
+ return {
621
+ content: [
622
+ {
623
+ type: "text",
624
+ text: JSON.stringify(data, null, 2),
625
+ },
626
+ ],
627
+ };
628
+ }
629
+
630
+ case "GetWomensHealthData": {
631
+ const args = GetWomensHealthSchema.parse(request.params.arguments);
632
+ const data = await getWomensHealth(currentRequest, args);
633
+ return {
634
+ content: [
635
+ {
636
+ type: "text",
637
+ text: JSON.stringify(data, null, 2),
638
+ },
639
+ ],
640
+ };
641
+ }
642
+
643
+ case "GetNutritionData": {
644
+ const args = GetNutritionSchema.parse(request.params.arguments);
645
+ const data = await getNutrition(currentRequest, args);
646
+ return {
647
+ content: [
648
+ {
649
+ type: "text",
650
+ text: JSON.stringify(data, null, 2),
651
+ },
652
+ ],
653
+ };
654
+ }
655
+
656
+ case "GetCardiovascularData": {
657
+ const args = GetCardiovascularSchema.parse(request.params.arguments);
658
+ const data = await getCardiovascular(currentRequest, args);
659
+ return {
660
+ content: [
661
+ {
662
+ type: "text",
663
+ text: JSON.stringify(data, null, 2),
664
+ },
665
+ ],
666
+ };
667
+ }
668
+
669
+ case "GetWellnessData": {
670
+ const args = GetWellnessSchema.parse(request.params.arguments);
671
+ const data = await getWellness(currentRequest, args);
672
+ return {
673
+ content: [
674
+ {
675
+ type: "text",
676
+ text: JSON.stringify(data, null, 2),
677
+ },
678
+ ],
679
+ };
680
+ }
681
+
682
+ case "GetAudioAndHearingData": {
683
+ const args = GetAudioAndHearingSchema.parse(request.params.arguments);
684
+ const data = await getAudioAndHearing(currentRequest, args);
685
+ return {
686
+ content: [
687
+ {
688
+ type: "text",
689
+ text: JSON.stringify(data, null, 2),
690
+ },
691
+ ],
692
+ };
693
+ }
694
+
695
+ case "GetMicrobiomeData": {
696
+ const args = GetMicrobiomeSchema.parse(request.params.arguments);
697
+ const data = await getMicrobiome(currentRequest, args);
698
+ return {
699
+ content: [
700
+ {
701
+ type: "text",
702
+ text: JSON.stringify(data, null, 2),
703
+ },
704
+ ],
705
+ };
706
+ }
707
+
708
+ case "GetLocationData": {
709
+ const args = GetLocationSchema.parse(request.params.arguments);
710
+ const data = await getLocation(currentRequest, args);
711
+ return {
712
+ content: [
713
+ {
714
+ type: "text",
715
+ text: JSON.stringify(data, null, 2),
716
+ },
717
+ ],
718
+ };
719
+ }
720
+
721
+ case "GetMovementAnalysisData": {
722
+ const args = GetMovementAnalysisSchema.parse(request.params.arguments);
723
+ const data = await getMovementAnalysis(currentRequest, args);
724
+ return {
725
+ content: [
726
+ {
727
+ type: "text",
728
+ text: JSON.stringify(data, null, 2),
729
+ },
730
+ ],
731
+ };
732
+ }
733
+
734
+ // SEARCH AND RETRIEVAL TOOLS
735
+ case "searchKeyword": {
736
+ const args = SearchKeywordSchema.parse(request.params.arguments);
737
+ const data = await searchKeyword(currentRequest, args);
738
+ return {
739
+ content: [
740
+ {
741
+ type: "text",
742
+ text: JSON.stringify(data, null, 2),
743
+ },
744
+ ],
745
+ };
746
+ }
747
+
748
+ case "searchFilters": {
749
+ const args = SearchFiltersSchema.parse(request.params.arguments);
750
+ const data = await searchFilters(currentRequest, args);
751
+ return {
752
+ content: [
753
+ {
754
+ type: "text",
755
+ text: JSON.stringify(data, null, 2),
756
+ },
757
+ ],
758
+ };
759
+ }
760
+
761
+ // REFERENCE TOOLS
762
+ case "getAvailableDataTypes": {
763
+ const args = GetAvailableDataTypesSchema.parse(request.params.arguments);
764
+ const data = await getAvailableDataTypes(currentRequest, args);
765
+ return {
766
+ content: [
767
+ {
768
+ type: "text",
769
+ text: JSON.stringify(data, null, 2),
770
+ },
771
+ ],
772
+ };
773
+ }
774
+
775
+ case "listConnectedSources": {
776
+ const args = ListConnectedSourcesSchema.parse(request.params.arguments);
777
+ const data = await listConnectedSources(currentRequest, args);
778
+ return {
779
+ content: [
780
+ {
781
+ type: "text",
782
+ text: JSON.stringify(data, null, 2),
783
+ },
784
+ ],
785
+ };
786
+ }
787
+
788
+ case "listDevices": {
789
+ const args = ListDevicesSchema.parse(request.params.arguments);
790
+ const data = await listDevices(currentRequest, args);
791
+ return {
792
+ content: [
793
+ {
794
+ type: "text",
795
+ text: JSON.stringify(data, null, 2),
796
+ },
797
+ ],
798
+ };
799
+ }
800
+
801
+ default:
802
+ throw new Error(`Unknown tool: ${request.params.name}`);
803
+ }
804
+ } catch (error) {
805
+ if (error instanceof z.ZodError) {
806
+ throw new Error(`Invalid arguments: ${error.message}`);
807
+ }
808
+ throw error;
809
+ }
810
+ });
811
+
812
+ // Create Express app with stateless HTTP transport
813
+ const app = express();
814
+ app.use(express.json());
815
+
816
+ // Create stateless transport (no session management)
817
+ const transport = new StreamableHTTPServerTransport({
818
+ sessionIdGenerator: undefined, // Stateless mode
819
+ enableJsonResponse: true,
820
+ });
821
+
822
+ // Connect server to transport
823
+ await server.connect(transport);
824
+
825
+ // Handle MCP requests
826
+ app.post("/mcp", async (req, res) => {
827
+ try {
828
+ // Store request in context for tool handlers to access headers
829
+ currentRequest = req;
830
+ await transport.handleRequest(req, res, req.body);
831
+ currentRequest = null; // Clear context after request
832
+ } catch (error) {
833
+ currentRequest = null; // Clear context on error
834
+ console.error("Error handling MCP request:", error);
835
+ res.status(500).json({
836
+ error: "Internal server error",
837
+ message: error instanceof Error ? error.message : "Unknown error",
838
+ });
839
+ }
840
+ });
841
+
842
+ // Health check endpoint
843
+ app.get("/health", (_req, res) => {
844
+ res.json({
845
+ status: "healthy",
846
+ server: "thryve-mcp-server",
847
+ version: "1.0.0",
848
+ stateless: true,
849
+ });
850
+ });
851
+
852
+ // Start server
853
+ const PORT = process.env.PORT || 30000;
854
+ app.listen(PORT, () => {
855
+ console.log(`Thryve MCP Server running on http://localhost:${PORT}`);
856
+ console.log(`MCP endpoint: http://localhost:${PORT}/mcp`);
857
+ console.log(`Health check: http://localhost:${PORT}/health`);
858
+ console.log("Mode: Stateless (no session management)");
859
+ });