@hasna/microservices 0.0.10 → 0.0.12

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 (93) hide show
  1. package/bin/index.js +101 -2
  2. package/bin/mcp.js +107 -4
  3. package/dist/index.js +86 -1
  4. package/microservices/microservice-analytics/package.json +27 -0
  5. package/microservices/microservice-analytics/src/cli/index.ts +373 -0
  6. package/microservices/microservice-analytics/src/db/analytics.ts +564 -0
  7. package/microservices/microservice-analytics/src/db/database.ts +93 -0
  8. package/microservices/microservice-analytics/src/db/migrations.ts +50 -0
  9. package/microservices/microservice-analytics/src/index.ts +37 -0
  10. package/microservices/microservice-analytics/src/mcp/index.ts +334 -0
  11. package/microservices/microservice-assets/package.json +27 -0
  12. package/microservices/microservice-assets/src/cli/index.ts +375 -0
  13. package/microservices/microservice-assets/src/db/assets.ts +370 -0
  14. package/microservices/microservice-assets/src/db/database.ts +93 -0
  15. package/microservices/microservice-assets/src/db/migrations.ts +51 -0
  16. package/microservices/microservice-assets/src/index.ts +32 -0
  17. package/microservices/microservice-assets/src/mcp/index.ts +346 -0
  18. package/microservices/microservice-compliance/package.json +27 -0
  19. package/microservices/microservice-compliance/src/cli/index.ts +467 -0
  20. package/microservices/microservice-compliance/src/db/compliance.ts +633 -0
  21. package/microservices/microservice-compliance/src/db/database.ts +93 -0
  22. package/microservices/microservice-compliance/src/db/migrations.ts +63 -0
  23. package/microservices/microservice-compliance/src/index.ts +46 -0
  24. package/microservices/microservice-compliance/src/mcp/index.ts +438 -0
  25. package/microservices/microservice-habits/package.json +27 -0
  26. package/microservices/microservice-habits/src/cli/index.ts +315 -0
  27. package/microservices/microservice-habits/src/db/database.ts +93 -0
  28. package/microservices/microservice-habits/src/db/habits.ts +451 -0
  29. package/microservices/microservice-habits/src/db/migrations.ts +46 -0
  30. package/microservices/microservice-habits/src/index.ts +31 -0
  31. package/microservices/microservice-habits/src/mcp/index.ts +313 -0
  32. package/microservices/microservice-health/package.json +27 -0
  33. package/microservices/microservice-health/src/cli/index.ts +484 -0
  34. package/microservices/microservice-health/src/db/database.ts +93 -0
  35. package/microservices/microservice-health/src/db/health.ts +708 -0
  36. package/microservices/microservice-health/src/db/migrations.ts +70 -0
  37. package/microservices/microservice-health/src/index.ts +63 -0
  38. package/microservices/microservice-health/src/mcp/index.ts +437 -0
  39. package/microservices/microservice-invoices/dashboard/dist/assets/index-Bngq7FNM.css +1 -0
  40. package/microservices/microservice-invoices/dashboard/dist/assets/index-aHW4ARZR.js +124 -0
  41. package/microservices/microservice-invoices/dashboard/dist/index.html +13 -0
  42. package/microservices/microservice-notifications/package.json +27 -0
  43. package/microservices/microservice-notifications/src/cli/index.ts +349 -0
  44. package/microservices/microservice-notifications/src/db/database.ts +93 -0
  45. package/microservices/microservice-notifications/src/db/migrations.ts +62 -0
  46. package/microservices/microservice-notifications/src/db/notifications.ts +509 -0
  47. package/microservices/microservice-notifications/src/index.ts +41 -0
  48. package/microservices/microservice-notifications/src/mcp/index.ts +422 -0
  49. package/microservices/microservice-products/package.json +27 -0
  50. package/microservices/microservice-products/src/cli/index.ts +416 -0
  51. package/microservices/microservice-products/src/db/categories.ts +154 -0
  52. package/microservices/microservice-products/src/db/database.ts +93 -0
  53. package/microservices/microservice-products/src/db/migrations.ts +58 -0
  54. package/microservices/microservice-products/src/db/pricing-tiers.ts +66 -0
  55. package/microservices/microservice-products/src/db/products.ts +452 -0
  56. package/microservices/microservice-products/src/index.ts +53 -0
  57. package/microservices/microservice-products/src/mcp/index.ts +453 -0
  58. package/microservices/microservice-projects/package.json +27 -0
  59. package/microservices/microservice-projects/src/cli/index.ts +480 -0
  60. package/microservices/microservice-projects/src/db/database.ts +93 -0
  61. package/microservices/microservice-projects/src/db/migrations.ts +65 -0
  62. package/microservices/microservice-projects/src/db/projects.ts +715 -0
  63. package/microservices/microservice-projects/src/index.ts +57 -0
  64. package/microservices/microservice-projects/src/mcp/index.ts +501 -0
  65. package/microservices/microservice-proposals/package.json +27 -0
  66. package/microservices/microservice-proposals/src/cli/index.ts +400 -0
  67. package/microservices/microservice-proposals/src/db/database.ts +93 -0
  68. package/microservices/microservice-proposals/src/db/migrations.ts +52 -0
  69. package/microservices/microservice-proposals/src/db/proposals.ts +532 -0
  70. package/microservices/microservice-proposals/src/index.ts +37 -0
  71. package/microservices/microservice-proposals/src/mcp/index.ts +375 -0
  72. package/microservices/microservice-reading/package.json +27 -0
  73. package/microservices/microservice-reading/src/cli/index.ts +464 -0
  74. package/microservices/microservice-reading/src/db/database.ts +93 -0
  75. package/microservices/microservice-reading/src/db/migrations.ts +59 -0
  76. package/microservices/microservice-reading/src/db/reading.ts +524 -0
  77. package/microservices/microservice-reading/src/index.ts +51 -0
  78. package/microservices/microservice-reading/src/mcp/index.ts +368 -0
  79. package/microservices/microservice-travel/package.json +27 -0
  80. package/microservices/microservice-travel/src/cli/index.ts +505 -0
  81. package/microservices/microservice-travel/src/db/database.ts +93 -0
  82. package/microservices/microservice-travel/src/db/migrations.ts +77 -0
  83. package/microservices/microservice-travel/src/db/travel.ts +802 -0
  84. package/microservices/microservice-travel/src/index.ts +60 -0
  85. package/microservices/microservice-travel/src/mcp/index.ts +495 -0
  86. package/microservices/microservice-wiki/package.json +27 -0
  87. package/microservices/microservice-wiki/src/cli/index.ts +345 -0
  88. package/microservices/microservice-wiki/src/db/database.ts +93 -0
  89. package/microservices/microservice-wiki/src/db/migrations.ts +55 -0
  90. package/microservices/microservice-wiki/src/db/wiki.ts +395 -0
  91. package/microservices/microservice-wiki/src/index.ts +32 -0
  92. package/microservices/microservice-wiki/src/mcp/index.ts +344 -0
  93. package/package.json +1 -1
@@ -0,0 +1,70 @@
1
+ export interface MigrationEntry {
2
+ id: number;
3
+ name: string;
4
+ sql: string;
5
+ }
6
+
7
+ export const MIGRATIONS: MigrationEntry[] = [
8
+ {
9
+ id: 1,
10
+ name: "initial_schema",
11
+ sql: `
12
+ CREATE TABLE IF NOT EXISTS metrics (
13
+ id TEXT PRIMARY KEY,
14
+ type TEXT NOT NULL,
15
+ value REAL NOT NULL,
16
+ unit TEXT,
17
+ notes TEXT,
18
+ recorded_at TEXT NOT NULL,
19
+ metadata TEXT NOT NULL DEFAULT '{}',
20
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS medications (
24
+ id TEXT PRIMARY KEY,
25
+ name TEXT NOT NULL,
26
+ dosage TEXT,
27
+ frequency TEXT,
28
+ start_date TEXT,
29
+ end_date TEXT,
30
+ refill_date TEXT,
31
+ notes TEXT,
32
+ active INTEGER NOT NULL DEFAULT 1,
33
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
34
+ );
35
+
36
+ CREATE TABLE IF NOT EXISTS appointments (
37
+ id TEXT PRIMARY KEY,
38
+ provider TEXT NOT NULL,
39
+ specialty TEXT,
40
+ location TEXT,
41
+ scheduled_at TEXT NOT NULL,
42
+ status TEXT NOT NULL DEFAULT 'scheduled' CHECK(status IN ('scheduled','completed','cancelled','rescheduled')),
43
+ notes TEXT,
44
+ follow_up_date TEXT,
45
+ metadata TEXT NOT NULL DEFAULT '{}',
46
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
47
+ );
48
+
49
+ CREATE TABLE IF NOT EXISTS fitness_logs (
50
+ id TEXT PRIMARY KEY,
51
+ activity TEXT NOT NULL,
52
+ duration_min INTEGER,
53
+ calories_burned INTEGER,
54
+ distance REAL,
55
+ notes TEXT,
56
+ logged_at TEXT NOT NULL,
57
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
58
+ );
59
+
60
+ CREATE INDEX IF NOT EXISTS idx_metrics_type ON metrics(type);
61
+ CREATE INDEX IF NOT EXISTS idx_metrics_recorded_at ON metrics(recorded_at);
62
+ CREATE INDEX IF NOT EXISTS idx_medications_active ON medications(active);
63
+ CREATE INDEX IF NOT EXISTS idx_medications_name ON medications(name);
64
+ CREATE INDEX IF NOT EXISTS idx_appointments_scheduled_at ON appointments(scheduled_at);
65
+ CREATE INDEX IF NOT EXISTS idx_appointments_status ON appointments(status);
66
+ CREATE INDEX IF NOT EXISTS idx_fitness_logs_activity ON fitness_logs(activity);
67
+ CREATE INDEX IF NOT EXISTS idx_fitness_logs_logged_at ON fitness_logs(logged_at);
68
+ `,
69
+ },
70
+ ];
@@ -0,0 +1,63 @@
1
+ /**
2
+ * microservice-health — Health tracking microservice
3
+ */
4
+
5
+ export {
6
+ createMetric,
7
+ getMetric,
8
+ listMetrics,
9
+ deleteMetric,
10
+ getMetricTrend,
11
+ type Metric,
12
+ type CreateMetricInput,
13
+ type ListMetricsOptions,
14
+ type MetricTrendPoint,
15
+ } from "./db/health.js";
16
+
17
+ export {
18
+ createMedication,
19
+ getMedication,
20
+ listMedications,
21
+ updateMedication,
22
+ deactivateMedication,
23
+ deleteMedication,
24
+ getMedicationSchedule,
25
+ type Medication,
26
+ type CreateMedicationInput,
27
+ type UpdateMedicationInput,
28
+ type ListMedicationsOptions,
29
+ } from "./db/health.js";
30
+
31
+ export {
32
+ createAppointment,
33
+ getAppointment,
34
+ listAppointments,
35
+ updateAppointment,
36
+ completeAppointment,
37
+ cancelAppointment,
38
+ deleteAppointment,
39
+ getUpcomingAppointments,
40
+ type Appointment,
41
+ type CreateAppointmentInput,
42
+ type UpdateAppointmentInput,
43
+ type ListAppointmentsOptions,
44
+ } from "./db/health.js";
45
+
46
+ export {
47
+ createFitnessLog,
48
+ getFitnessLog,
49
+ listFitnessLogs,
50
+ deleteFitnessLog,
51
+ getFitnessStats,
52
+ type FitnessLog,
53
+ type CreateFitnessLogInput,
54
+ type ListFitnessLogsOptions,
55
+ type FitnessStats,
56
+ } from "./db/health.js";
57
+
58
+ export {
59
+ getHealthSummary,
60
+ type HealthSummary,
61
+ } from "./db/health.js";
62
+
63
+ export { getDatabase, closeDatabase } from "./db/database.js";
@@ -0,0 +1,437 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { z } from "zod";
6
+ import {
7
+ createMetric,
8
+ getMetric,
9
+ listMetrics,
10
+ deleteMetric,
11
+ getMetricTrend,
12
+ createMedication,
13
+ getMedication,
14
+ listMedications,
15
+ updateMedication,
16
+ deactivateMedication,
17
+ getMedicationSchedule,
18
+ createAppointment,
19
+ getAppointment,
20
+ listAppointments,
21
+ updateAppointment,
22
+ completeAppointment,
23
+ cancelAppointment,
24
+ getUpcomingAppointments,
25
+ createFitnessLog,
26
+ getFitnessLog,
27
+ listFitnessLogs,
28
+ deleteFitnessLog,
29
+ getFitnessStats,
30
+ getHealthSummary,
31
+ } from "../db/health.js";
32
+
33
+ const server = new McpServer({
34
+ name: "microservice-health",
35
+ version: "0.0.1",
36
+ });
37
+
38
+ // --- Metrics ---
39
+
40
+ server.registerTool(
41
+ "record_metric",
42
+ {
43
+ title: "Record Metric",
44
+ description: "Record a health metric (e.g. weight, blood pressure, heart rate).",
45
+ inputSchema: {
46
+ type: z.string(),
47
+ value: z.number(),
48
+ unit: z.string().optional(),
49
+ notes: z.string().optional(),
50
+ recorded_at: z.string().optional(),
51
+ },
52
+ },
53
+ async (params) => {
54
+ const metric = createMetric(params);
55
+ return { content: [{ type: "text", text: JSON.stringify(metric, null, 2) }] };
56
+ }
57
+ );
58
+
59
+ server.registerTool(
60
+ "get_metric",
61
+ {
62
+ title: "Get Metric",
63
+ description: "Get a metric by ID.",
64
+ inputSchema: { id: z.string() },
65
+ },
66
+ async ({ id }) => {
67
+ const metric = getMetric(id);
68
+ if (!metric) {
69
+ return { content: [{ type: "text", text: `Metric '${id}' not found.` }], isError: true };
70
+ }
71
+ return { content: [{ type: "text", text: JSON.stringify(metric, null, 2) }] };
72
+ }
73
+ );
74
+
75
+ server.registerTool(
76
+ "list_metrics",
77
+ {
78
+ title: "List Metrics",
79
+ description: "List health metrics with optional filters.",
80
+ inputSchema: {
81
+ type: z.string().optional(),
82
+ limit: z.number().optional(),
83
+ },
84
+ },
85
+ async (params) => {
86
+ const metrics = listMetrics(params);
87
+ return {
88
+ content: [
89
+ { type: "text", text: JSON.stringify({ metrics, count: metrics.length }, null, 2) },
90
+ ],
91
+ };
92
+ }
93
+ );
94
+
95
+ server.registerTool(
96
+ "delete_metric",
97
+ {
98
+ title: "Delete Metric",
99
+ description: "Delete a metric by ID.",
100
+ inputSchema: { id: z.string() },
101
+ },
102
+ async ({ id }) => {
103
+ const deleted = deleteMetric(id);
104
+ return { content: [{ type: "text", text: JSON.stringify({ id, deleted }) }] };
105
+ }
106
+ );
107
+
108
+ server.registerTool(
109
+ "get_metric_trend",
110
+ {
111
+ title: "Get Metric Trend",
112
+ description: "Get daily average trend for a metric type over N days.",
113
+ inputSchema: {
114
+ type: z.string(),
115
+ days: z.number().optional().default(30),
116
+ },
117
+ },
118
+ async ({ type, days }) => {
119
+ const trend = getMetricTrend(type, days);
120
+ return {
121
+ content: [{ type: "text", text: JSON.stringify({ type, days, trend }, null, 2) }],
122
+ };
123
+ }
124
+ );
125
+
126
+ // --- Medications ---
127
+
128
+ server.registerTool(
129
+ "add_medication",
130
+ {
131
+ title: "Add Medication",
132
+ description: "Add a new medication.",
133
+ inputSchema: {
134
+ name: z.string(),
135
+ dosage: z.string().optional(),
136
+ frequency: z.string().optional(),
137
+ start_date: z.string().optional(),
138
+ end_date: z.string().optional(),
139
+ refill_date: z.string().optional(),
140
+ notes: z.string().optional(),
141
+ },
142
+ },
143
+ async (params) => {
144
+ const med = createMedication(params);
145
+ return { content: [{ type: "text", text: JSON.stringify(med, null, 2) }] };
146
+ }
147
+ );
148
+
149
+ server.registerTool(
150
+ "get_medication",
151
+ {
152
+ title: "Get Medication",
153
+ description: "Get a medication by ID.",
154
+ inputSchema: { id: z.string() },
155
+ },
156
+ async ({ id }) => {
157
+ const med = getMedication(id);
158
+ if (!med) {
159
+ return { content: [{ type: "text", text: `Medication '${id}' not found.` }], isError: true };
160
+ }
161
+ return { content: [{ type: "text", text: JSON.stringify(med, null, 2) }] };
162
+ }
163
+ );
164
+
165
+ server.registerTool(
166
+ "list_medications",
167
+ {
168
+ title: "List Medications",
169
+ description: "List medications with optional filters.",
170
+ inputSchema: {
171
+ active: z.boolean().optional(),
172
+ search: z.string().optional(),
173
+ limit: z.number().optional(),
174
+ },
175
+ },
176
+ async (params) => {
177
+ const meds = listMedications(params);
178
+ return {
179
+ content: [
180
+ { type: "text", text: JSON.stringify({ medications: meds, count: meds.length }, null, 2) },
181
+ ],
182
+ };
183
+ }
184
+ );
185
+
186
+ server.registerTool(
187
+ "update_medication",
188
+ {
189
+ title: "Update Medication",
190
+ description: "Update an existing medication.",
191
+ inputSchema: {
192
+ id: z.string(),
193
+ name: z.string().optional(),
194
+ dosage: z.string().optional(),
195
+ frequency: z.string().optional(),
196
+ start_date: z.string().optional(),
197
+ end_date: z.string().optional(),
198
+ refill_date: z.string().optional(),
199
+ notes: z.string().optional(),
200
+ active: z.boolean().optional(),
201
+ },
202
+ },
203
+ async ({ id, ...input }) => {
204
+ const med = updateMedication(id, input);
205
+ if (!med) {
206
+ return { content: [{ type: "text", text: `Medication '${id}' not found.` }], isError: true };
207
+ }
208
+ return { content: [{ type: "text", text: JSON.stringify(med, null, 2) }] };
209
+ }
210
+ );
211
+
212
+ server.registerTool(
213
+ "deactivate_medication",
214
+ {
215
+ title: "Deactivate Medication",
216
+ description: "Deactivate a medication (mark as no longer active).",
217
+ inputSchema: { id: z.string() },
218
+ },
219
+ async ({ id }) => {
220
+ const med = deactivateMedication(id);
221
+ if (!med) {
222
+ return { content: [{ type: "text", text: `Medication '${id}' not found.` }], isError: true };
223
+ }
224
+ return { content: [{ type: "text", text: JSON.stringify(med, null, 2) }] };
225
+ }
226
+ );
227
+
228
+ server.registerTool(
229
+ "get_medication_schedule",
230
+ {
231
+ title: "Get Medication Schedule",
232
+ description: "Get all active medications (current medication schedule).",
233
+ inputSchema: {},
234
+ },
235
+ async () => {
236
+ const meds = getMedicationSchedule();
237
+ return {
238
+ content: [
239
+ { type: "text", text: JSON.stringify({ medications: meds, count: meds.length }, null, 2) },
240
+ ],
241
+ };
242
+ }
243
+ );
244
+
245
+ // --- Appointments ---
246
+
247
+ server.registerTool(
248
+ "schedule_appointment",
249
+ {
250
+ title: "Schedule Appointment",
251
+ description: "Schedule a new appointment.",
252
+ inputSchema: {
253
+ provider: z.string(),
254
+ scheduled_at: z.string(),
255
+ specialty: z.string().optional(),
256
+ location: z.string().optional(),
257
+ notes: z.string().optional(),
258
+ follow_up_date: z.string().optional(),
259
+ },
260
+ },
261
+ async (params) => {
262
+ const appt = createAppointment(params);
263
+ return { content: [{ type: "text", text: JSON.stringify(appt, null, 2) }] };
264
+ }
265
+ );
266
+
267
+ server.registerTool(
268
+ "get_appointment",
269
+ {
270
+ title: "Get Appointment",
271
+ description: "Get an appointment by ID.",
272
+ inputSchema: { id: z.string() },
273
+ },
274
+ async ({ id }) => {
275
+ const appt = getAppointment(id);
276
+ if (!appt) {
277
+ return { content: [{ type: "text", text: `Appointment '${id}' not found.` }], isError: true };
278
+ }
279
+ return { content: [{ type: "text", text: JSON.stringify(appt, null, 2) }] };
280
+ }
281
+ );
282
+
283
+ server.registerTool(
284
+ "list_appointments",
285
+ {
286
+ title: "List Appointments",
287
+ description: "List appointments with optional filters.",
288
+ inputSchema: {
289
+ status: z.string().optional(),
290
+ provider: z.string().optional(),
291
+ limit: z.number().optional(),
292
+ },
293
+ },
294
+ async (params) => {
295
+ const appts = listAppointments(params);
296
+ return {
297
+ content: [
298
+ { type: "text", text: JSON.stringify({ appointments: appts, count: appts.length }, null, 2) },
299
+ ],
300
+ };
301
+ }
302
+ );
303
+
304
+ server.registerTool(
305
+ "complete_appointment",
306
+ {
307
+ title: "Complete Appointment",
308
+ description: "Mark an appointment as completed.",
309
+ inputSchema: { id: z.string() },
310
+ },
311
+ async ({ id }) => {
312
+ const appt = completeAppointment(id);
313
+ if (!appt) {
314
+ return { content: [{ type: "text", text: `Appointment '${id}' not found.` }], isError: true };
315
+ }
316
+ return { content: [{ type: "text", text: JSON.stringify(appt, null, 2) }] };
317
+ }
318
+ );
319
+
320
+ server.registerTool(
321
+ "cancel_appointment",
322
+ {
323
+ title: "Cancel Appointment",
324
+ description: "Cancel an appointment.",
325
+ inputSchema: { id: z.string() },
326
+ },
327
+ async ({ id }) => {
328
+ const appt = cancelAppointment(id);
329
+ if (!appt) {
330
+ return { content: [{ type: "text", text: `Appointment '${id}' not found.` }], isError: true };
331
+ }
332
+ return { content: [{ type: "text", text: JSON.stringify(appt, null, 2) }] };
333
+ }
334
+ );
335
+
336
+ server.registerTool(
337
+ "get_upcoming_appointments",
338
+ {
339
+ title: "Get Upcoming Appointments",
340
+ description: "Get scheduled appointments within the next N days.",
341
+ inputSchema: {
342
+ days: z.number().optional().default(30),
343
+ },
344
+ },
345
+ async ({ days }) => {
346
+ const appts = getUpcomingAppointments(days);
347
+ return {
348
+ content: [
349
+ { type: "text", text: JSON.stringify({ appointments: appts, count: appts.length, days }, null, 2) },
350
+ ],
351
+ };
352
+ }
353
+ );
354
+
355
+ // --- Fitness ---
356
+
357
+ server.registerTool(
358
+ "log_fitness",
359
+ {
360
+ title: "Log Fitness Activity",
361
+ description: "Log a fitness activity (e.g. running, swimming, yoga).",
362
+ inputSchema: {
363
+ activity: z.string(),
364
+ duration_min: z.number().optional(),
365
+ calories_burned: z.number().optional(),
366
+ distance: z.number().optional(),
367
+ notes: z.string().optional(),
368
+ logged_at: z.string().optional(),
369
+ },
370
+ },
371
+ async (params) => {
372
+ const log = createFitnessLog(params);
373
+ return { content: [{ type: "text", text: JSON.stringify(log, null, 2) }] };
374
+ }
375
+ );
376
+
377
+ server.registerTool(
378
+ "list_fitness_logs",
379
+ {
380
+ title: "List Fitness Logs",
381
+ description: "List fitness logs with optional filters.",
382
+ inputSchema: {
383
+ activity: z.string().optional(),
384
+ limit: z.number().optional(),
385
+ },
386
+ },
387
+ async (params) => {
388
+ const logs = listFitnessLogs(params);
389
+ return {
390
+ content: [
391
+ { type: "text", text: JSON.stringify({ logs, count: logs.length }, null, 2) },
392
+ ],
393
+ };
394
+ }
395
+ );
396
+
397
+ server.registerTool(
398
+ "get_fitness_stats",
399
+ {
400
+ title: "Get Fitness Stats",
401
+ description: "Get fitness statistics for the last N days.",
402
+ inputSchema: {
403
+ days: z.number().optional().default(30),
404
+ },
405
+ },
406
+ async ({ days }) => {
407
+ const stats = getFitnessStats(days);
408
+ return { content: [{ type: "text", text: JSON.stringify({ days, ...stats }, null, 2) }] };
409
+ }
410
+ );
411
+
412
+ // --- Summary ---
413
+
414
+ server.registerTool(
415
+ "get_health_summary",
416
+ {
417
+ title: "Get Health Summary",
418
+ description: "Get an overview of all health data: metrics, medications, appointments, and fitness.",
419
+ inputSchema: {},
420
+ },
421
+ async () => {
422
+ const summary = getHealthSummary();
423
+ return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] };
424
+ }
425
+ );
426
+
427
+ // --- Start ---
428
+ async function main() {
429
+ const transport = new StdioServerTransport();
430
+ await server.connect(transport);
431
+ console.error("microservice-health MCP server running on stdio");
432
+ }
433
+
434
+ main().catch((error) => {
435
+ console.error("Fatal error:", error);
436
+ process.exit(1);
437
+ });
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-black:#000;--spacing:.25rem;--container-md:28rem;--container-lg:32rem;--container-7xl:80rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--tracking-tight:-.025em;--tracking-wider:.05em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-background:oklch(100% 0 0);--color-foreground:oklch(14.5% 0 0);--color-card:oklch(100% 0 0);--color-primary:oklch(20.5% 0 0);--color-primary-foreground:oklch(98.5% 0 0);--color-muted:oklch(96.5% 0 0);--color-muted-foreground:oklch(55.6% 0 0);--color-accent:oklch(96.5% 0 0);--color-destructive:oklch(57.7% .245 27.325);--color-border:oklch(92.2% 0 0);--color-success:oklch(59.6% .145 163.225);--color-warning:oklch(68.1% .162 75.834);--color-info:oklch(62.3% .214 259.815)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.fixed{position:fixed}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.inset-y-0{inset-block:calc(var(--spacing) * 0)}.top-0{top:calc(var(--spacing) * 0)}.right-0{right:calc(var(--spacing) * 0)}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-inline:auto}.mt-1{margin-top:calc(var(--spacing) * 1)}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.ml-1{margin-left:calc(var(--spacing) * 1)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.min-h-screen{min-height:100vh}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-4{width:calc(var(--spacing) * 4)}.w-5{width:calc(var(--spacing) * 5)}.w-8{width:calc(var(--spacing) * 8)}.w-9{width:calc(var(--spacing) * 9)}.w-full{width:100%}.w-px{width:1px}.max-w-7xl{max-width:var(--container-7xl)}.max-w-lg{max-width:var(--container-lg)}.max-w-md{max-width:var(--container-md)}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-1{gap:calc(var(--spacing) * 1)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-primary\/30{border-color:#1717174d}@supports (color:color-mix(in lab,red,red)){.border-primary\/30{border-color:color-mix(in oklab,var(--color-primary) 30%,transparent)}}.bg-accent{background-color:var(--color-accent)}.bg-background{background-color:var(--color-background)}.bg-black\/20{background-color:#0003}@supports (color:color-mix(in lab,red,red)){.bg-black\/20{background-color:color-mix(in oklab,var(--color-black) 20%,transparent)}}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab,red,red)){.bg-black\/30{background-color:color-mix(in oklab,var(--color-black) 30%,transparent)}}.bg-border{background-color:var(--color-border)}.bg-card{background-color:var(--color-card)}.bg-destructive\/10{background-color:#e400141a}@supports (color:color-mix(in lab,red,red)){.bg-destructive\/10{background-color:color-mix(in oklab,var(--color-destructive) 10%,transparent)}}.bg-info\/10{background-color:#3080ff1a}@supports (color:color-mix(in lab,red,red)){.bg-info\/10{background-color:color-mix(in oklab,var(--color-info) 10%,transparent)}}.bg-muted{background-color:var(--color-muted)}.bg-muted\/30{background-color:#f3f3f34d}@supports (color:color-mix(in lab,red,red)){.bg-muted\/30{background-color:color-mix(in oklab,var(--color-muted) 30%,transparent)}}.bg-primary{background-color:var(--color-primary)}.bg-primary\/5{background-color:#1717170d}@supports (color:color-mix(in lab,red,red)){.bg-primary\/5{background-color:color-mix(in oklab,var(--color-primary) 5%,transparent)}}.bg-success\/10{background-color:#0097671a}@supports (color:color-mix(in lab,red,red)){.bg-success\/10{background-color:color-mix(in oklab,var(--color-success) 10%,transparent)}}.bg-warning\/10{background-color:#cd89001a}@supports (color:color-mix(in lab,red,red)){.bg-warning\/10{background-color:color-mix(in oklab,var(--color-warning) 10%,transparent)}}.p-1{padding:calc(var(--spacing) * 1)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-20{padding-block:calc(var(--spacing) * 20)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-4{padding-top:calc(var(--spacing) * 4)}.text-left{text-align:left}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-destructive{color:var(--color-destructive)}.text-foreground{color:var(--color-foreground)}.text-info{color:var(--color-info)}.text-muted-foreground{color:var(--color-muted-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-success{color:var(--color-success)}.text-warning{color:var(--color-warning)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.line-through{text-decoration-line:line-through}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-primary\/20{--tw-ring-color:#17171733}@supports (color:color-mix(in lab,red,red)){.ring-primary\/20{--tw-ring-color:color-mix(in oklab, var(--color-primary) 20%, transparent)}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media(hover:hover){.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-accent\/50:hover{background-color:#f3f3f380}@supports (color:color-mix(in lab,red,red)){.hover\:bg-accent\/50:hover{background-color:color-mix(in oklab,var(--color-accent) 50%,transparent)}}.hover\:bg-primary\/90:hover{background-color:#171717e6}@supports (color:color-mix(in lab,red,red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab,var(--color-primary) 90%,transparent)}}.hover\:shadow-sm:hover{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.disabled\:opacity-50:disabled{opacity:.5}@media(min-width:40rem){.sm\:col-span-2{grid-column:span 2/span 2}.sm\:table-cell{display:table-cell}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(min-width:48rem){.md\:table-cell{display:table-cell}}@media(min-width:64rem){.lg\:col-span-5{grid-column:span 5/span 5}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}}}*{border-color:var(--color-border)}body{background-color:var(--color-background);color:var(--color-foreground);font-family:Inter,system-ui,-apple-system,sans-serif}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}