@hasna/microservices 0.0.3 → 0.0.5

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 (68) hide show
  1. package/bin/index.js +63 -0
  2. package/bin/mcp.js +63 -0
  3. package/dist/index.js +63 -0
  4. package/microservices/microservice-ads/package.json +27 -0
  5. package/microservices/microservice-ads/src/cli/index.ts +605 -0
  6. package/microservices/microservice-ads/src/db/campaigns.ts +797 -0
  7. package/microservices/microservice-ads/src/db/database.ts +93 -0
  8. package/microservices/microservice-ads/src/db/migrations.ts +60 -0
  9. package/microservices/microservice-ads/src/index.ts +39 -0
  10. package/microservices/microservice-ads/src/mcp/index.ts +480 -0
  11. package/microservices/microservice-contracts/package.json +27 -0
  12. package/microservices/microservice-contracts/src/cli/index.ts +770 -0
  13. package/microservices/microservice-contracts/src/db/contracts.ts +925 -0
  14. package/microservices/microservice-contracts/src/db/database.ts +93 -0
  15. package/microservices/microservice-contracts/src/db/migrations.ts +141 -0
  16. package/microservices/microservice-contracts/src/index.ts +43 -0
  17. package/microservices/microservice-contracts/src/mcp/index.ts +617 -0
  18. package/microservices/microservice-domains/package.json +27 -0
  19. package/microservices/microservice-domains/src/cli/index.ts +691 -0
  20. package/microservices/microservice-domains/src/db/database.ts +93 -0
  21. package/microservices/microservice-domains/src/db/domains.ts +1164 -0
  22. package/microservices/microservice-domains/src/db/migrations.ts +60 -0
  23. package/microservices/microservice-domains/src/index.ts +65 -0
  24. package/microservices/microservice-domains/src/mcp/index.ts +536 -0
  25. package/microservices/microservice-hiring/package.json +27 -0
  26. package/microservices/microservice-hiring/src/cli/index.ts +741 -0
  27. package/microservices/microservice-hiring/src/db/database.ts +93 -0
  28. package/microservices/microservice-hiring/src/db/hiring.ts +1085 -0
  29. package/microservices/microservice-hiring/src/db/migrations.ts +89 -0
  30. package/microservices/microservice-hiring/src/index.ts +80 -0
  31. package/microservices/microservice-hiring/src/lib/scoring.ts +206 -0
  32. package/microservices/microservice-hiring/src/mcp/index.ts +709 -0
  33. package/microservices/microservice-payments/package.json +27 -0
  34. package/microservices/microservice-payments/src/cli/index.ts +609 -0
  35. package/microservices/microservice-payments/src/db/database.ts +93 -0
  36. package/microservices/microservice-payments/src/db/migrations.ts +81 -0
  37. package/microservices/microservice-payments/src/db/payments.ts +1204 -0
  38. package/microservices/microservice-payments/src/index.ts +51 -0
  39. package/microservices/microservice-payments/src/mcp/index.ts +683 -0
  40. package/microservices/microservice-payroll/package.json +27 -0
  41. package/microservices/microservice-payroll/src/cli/index.ts +643 -0
  42. package/microservices/microservice-payroll/src/db/database.ts +93 -0
  43. package/microservices/microservice-payroll/src/db/migrations.ts +95 -0
  44. package/microservices/microservice-payroll/src/db/payroll.ts +1377 -0
  45. package/microservices/microservice-payroll/src/index.ts +48 -0
  46. package/microservices/microservice-payroll/src/mcp/index.ts +666 -0
  47. package/microservices/microservice-shipping/package.json +27 -0
  48. package/microservices/microservice-shipping/src/cli/index.ts +606 -0
  49. package/microservices/microservice-shipping/src/db/database.ts +93 -0
  50. package/microservices/microservice-shipping/src/db/migrations.ts +69 -0
  51. package/microservices/microservice-shipping/src/db/shipping.ts +1093 -0
  52. package/microservices/microservice-shipping/src/index.ts +53 -0
  53. package/microservices/microservice-shipping/src/mcp/index.ts +533 -0
  54. package/microservices/microservice-social/package.json +27 -0
  55. package/microservices/microservice-social/src/cli/index.ts +689 -0
  56. package/microservices/microservice-social/src/db/database.ts +93 -0
  57. package/microservices/microservice-social/src/db/migrations.ts +88 -0
  58. package/microservices/microservice-social/src/db/social.ts +1046 -0
  59. package/microservices/microservice-social/src/index.ts +46 -0
  60. package/microservices/microservice-social/src/mcp/index.ts +655 -0
  61. package/microservices/microservice-subscriptions/package.json +27 -0
  62. package/microservices/microservice-subscriptions/src/cli/index.ts +715 -0
  63. package/microservices/microservice-subscriptions/src/db/database.ts +93 -0
  64. package/microservices/microservice-subscriptions/src/db/migrations.ts +125 -0
  65. package/microservices/microservice-subscriptions/src/db/subscriptions.ts +1256 -0
  66. package/microservices/microservice-subscriptions/src/index.ts +41 -0
  67. package/microservices/microservice-subscriptions/src/mcp/index.ts +631 -0
  68. package/package.json +1 -1
@@ -0,0 +1,41 @@
1
+ /**
2
+ * microservice-subscriptions — Subscription and recurring billing management microservice
3
+ */
4
+
5
+ export {
6
+ createPlan,
7
+ getPlan,
8
+ listPlans,
9
+ updatePlan,
10
+ deletePlan,
11
+ countPlans,
12
+ createSubscriber,
13
+ getSubscriber,
14
+ listSubscribers,
15
+ updateSubscriber,
16
+ deleteSubscriber,
17
+ countSubscribers,
18
+ cancelSubscriber,
19
+ upgradeSubscriber,
20
+ downgradeSubscriber,
21
+ recordEvent,
22
+ getEvent,
23
+ listEvents,
24
+ getMrr,
25
+ getArr,
26
+ getChurnRate,
27
+ listExpiring,
28
+ getSubscriberStats,
29
+ type Plan,
30
+ type CreatePlanInput,
31
+ type UpdatePlanInput,
32
+ type ListPlansOptions,
33
+ type Subscriber,
34
+ type CreateSubscriberInput,
35
+ type UpdateSubscriberInput,
36
+ type ListSubscribersOptions,
37
+ type SubscriptionEvent,
38
+ type ListEventsOptions,
39
+ } from "./db/subscriptions.js";
40
+
41
+ export { getDatabase, closeDatabase } from "./db/database.js";
@@ -0,0 +1,631 @@
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
+ createPlan,
8
+ getPlan,
9
+ listPlans,
10
+ updatePlan,
11
+ deletePlan,
12
+ createSubscriber,
13
+ getSubscriber,
14
+ listSubscribers,
15
+ cancelSubscriber,
16
+ upgradeSubscriber,
17
+ downgradeSubscriber,
18
+ listEvents,
19
+ getMrr,
20
+ getArr,
21
+ getChurnRate,
22
+ listExpiring,
23
+ getSubscriberStats,
24
+ pauseSubscriber,
25
+ resumeSubscriber,
26
+ extendTrial,
27
+ createDunning,
28
+ listDunning,
29
+ updateDunning,
30
+ bulkImportSubscribers,
31
+ exportSubscribers,
32
+ getLtv,
33
+ getNrr,
34
+ getCohortReport,
35
+ comparePlans,
36
+ getExpiringRenewals,
37
+ } from "../db/subscriptions.js";
38
+
39
+ const server = new McpServer({
40
+ name: "microservice-subscriptions",
41
+ version: "0.0.1",
42
+ });
43
+
44
+ // --- Plans ---
45
+
46
+ server.registerTool(
47
+ "create_plan",
48
+ {
49
+ title: "Create Plan",
50
+ description: "Create a new subscription plan.",
51
+ inputSchema: {
52
+ name: z.string(),
53
+ price: z.number(),
54
+ interval: z.enum(["monthly", "yearly", "lifetime"]).default("monthly"),
55
+ features: z.array(z.string()).optional(),
56
+ active: z.boolean().optional(),
57
+ },
58
+ },
59
+ async (params) => {
60
+ const plan = createPlan(params);
61
+ return { content: [{ type: "text", text: JSON.stringify(plan, null, 2) }] };
62
+ }
63
+ );
64
+
65
+ server.registerTool(
66
+ "get_plan",
67
+ {
68
+ title: "Get Plan",
69
+ description: "Get a subscription plan by ID.",
70
+ inputSchema: { id: z.string() },
71
+ },
72
+ async ({ id }) => {
73
+ const plan = getPlan(id);
74
+ if (!plan) {
75
+ return { content: [{ type: "text", text: `Plan '${id}' not found.` }], isError: true };
76
+ }
77
+ return { content: [{ type: "text", text: JSON.stringify(plan, null, 2) }] };
78
+ }
79
+ );
80
+
81
+ server.registerTool(
82
+ "list_plans",
83
+ {
84
+ title: "List Plans",
85
+ description: "List subscription plans with optional filters.",
86
+ inputSchema: {
87
+ active_only: z.boolean().optional(),
88
+ interval: z.string().optional(),
89
+ limit: z.number().optional(),
90
+ },
91
+ },
92
+ async (params) => {
93
+ const plans = listPlans(params);
94
+ return {
95
+ content: [
96
+ { type: "text", text: JSON.stringify({ plans, count: plans.length }, null, 2) },
97
+ ],
98
+ };
99
+ }
100
+ );
101
+
102
+ server.registerTool(
103
+ "update_plan",
104
+ {
105
+ title: "Update Plan",
106
+ description: "Update a subscription plan.",
107
+ inputSchema: {
108
+ id: z.string(),
109
+ name: z.string().optional(),
110
+ price: z.number().optional(),
111
+ interval: z.enum(["monthly", "yearly", "lifetime"]).optional(),
112
+ features: z.array(z.string()).optional(),
113
+ active: z.boolean().optional(),
114
+ },
115
+ },
116
+ async ({ id, ...input }) => {
117
+ const plan = updatePlan(id, input);
118
+ if (!plan) {
119
+ return { content: [{ type: "text", text: `Plan '${id}' not found.` }], isError: true };
120
+ }
121
+ return { content: [{ type: "text", text: JSON.stringify(plan, null, 2) }] };
122
+ }
123
+ );
124
+
125
+ server.registerTool(
126
+ "delete_plan",
127
+ {
128
+ title: "Delete Plan",
129
+ description: "Delete a subscription plan by ID.",
130
+ inputSchema: { id: z.string() },
131
+ },
132
+ async ({ id }) => {
133
+ const deleted = deletePlan(id);
134
+ return { content: [{ type: "text", text: JSON.stringify({ id, deleted }) }] };
135
+ }
136
+ );
137
+
138
+ // --- Subscribers ---
139
+
140
+ server.registerTool(
141
+ "create_subscriber",
142
+ {
143
+ title: "Create Subscriber",
144
+ description: "Create a new subscriber for a plan.",
145
+ inputSchema: {
146
+ plan_id: z.string(),
147
+ customer_name: z.string(),
148
+ customer_email: z.string(),
149
+ status: z.enum(["trialing", "active", "past_due", "canceled", "expired", "paused"]).optional(),
150
+ trial_ends_at: z.string().optional(),
151
+ metadata: z.record(z.unknown()).optional(),
152
+ },
153
+ },
154
+ async (params) => {
155
+ const subscriber = createSubscriber(params);
156
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
157
+ }
158
+ );
159
+
160
+ server.registerTool(
161
+ "get_subscriber",
162
+ {
163
+ title: "Get Subscriber",
164
+ description: "Get a subscriber by ID.",
165
+ inputSchema: { id: z.string() },
166
+ },
167
+ async ({ id }) => {
168
+ const subscriber = getSubscriber(id);
169
+ if (!subscriber) {
170
+ return { content: [{ type: "text", text: `Subscriber '${id}' not found.` }], isError: true };
171
+ }
172
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
173
+ }
174
+ );
175
+
176
+ server.registerTool(
177
+ "list_subscribers",
178
+ {
179
+ title: "List Subscribers",
180
+ description: "List subscribers with optional filters.",
181
+ inputSchema: {
182
+ plan_id: z.string().optional(),
183
+ status: z.string().optional(),
184
+ search: z.string().optional(),
185
+ limit: z.number().optional(),
186
+ },
187
+ },
188
+ async (params) => {
189
+ const subscribers = listSubscribers(params);
190
+ return {
191
+ content: [
192
+ {
193
+ type: "text",
194
+ text: JSON.stringify({ subscribers, count: subscribers.length }, null, 2),
195
+ },
196
+ ],
197
+ };
198
+ }
199
+ );
200
+
201
+ server.registerTool(
202
+ "cancel_subscriber",
203
+ {
204
+ title: "Cancel Subscriber",
205
+ description: "Cancel a subscription.",
206
+ inputSchema: { id: z.string() },
207
+ },
208
+ async ({ id }) => {
209
+ const subscriber = cancelSubscriber(id);
210
+ if (!subscriber) {
211
+ return { content: [{ type: "text", text: `Subscriber '${id}' not found.` }], isError: true };
212
+ }
213
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
214
+ }
215
+ );
216
+
217
+ server.registerTool(
218
+ "upgrade_subscriber",
219
+ {
220
+ title: "Upgrade Subscriber",
221
+ description: "Upgrade a subscriber to a new plan.",
222
+ inputSchema: {
223
+ subscriber_id: z.string(),
224
+ new_plan_id: z.string(),
225
+ },
226
+ },
227
+ async ({ subscriber_id, new_plan_id }) => {
228
+ const subscriber = upgradeSubscriber(subscriber_id, new_plan_id);
229
+ if (!subscriber) {
230
+ return {
231
+ content: [{ type: "text", text: `Subscriber or plan not found.` }],
232
+ isError: true,
233
+ };
234
+ }
235
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
236
+ }
237
+ );
238
+
239
+ server.registerTool(
240
+ "downgrade_subscriber",
241
+ {
242
+ title: "Downgrade Subscriber",
243
+ description: "Downgrade a subscriber to a new plan.",
244
+ inputSchema: {
245
+ subscriber_id: z.string(),
246
+ new_plan_id: z.string(),
247
+ },
248
+ },
249
+ async ({ subscriber_id, new_plan_id }) => {
250
+ const subscriber = downgradeSubscriber(subscriber_id, new_plan_id);
251
+ if (!subscriber) {
252
+ return {
253
+ content: [{ type: "text", text: `Subscriber or plan not found.` }],
254
+ isError: true,
255
+ };
256
+ }
257
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
258
+ }
259
+ );
260
+
261
+ // --- Events ---
262
+
263
+ server.registerTool(
264
+ "list_events",
265
+ {
266
+ title: "List Events",
267
+ description: "List subscription events.",
268
+ inputSchema: {
269
+ subscriber_id: z.string().optional(),
270
+ type: z.string().optional(),
271
+ limit: z.number().optional(),
272
+ },
273
+ },
274
+ async (params) => {
275
+ const events = listEvents(params);
276
+ return {
277
+ content: [
278
+ { type: "text", text: JSON.stringify({ events, count: events.length }, null, 2) },
279
+ ],
280
+ };
281
+ }
282
+ );
283
+
284
+ // --- Analytics ---
285
+
286
+ server.registerTool(
287
+ "get_mrr",
288
+ {
289
+ title: "Get MRR",
290
+ description: "Get monthly recurring revenue.",
291
+ inputSchema: {},
292
+ },
293
+ async () => {
294
+ const mrr = getMrr();
295
+ return { content: [{ type: "text", text: JSON.stringify({ mrr }) }] };
296
+ }
297
+ );
298
+
299
+ server.registerTool(
300
+ "get_arr",
301
+ {
302
+ title: "Get ARR",
303
+ description: "Get annual recurring revenue.",
304
+ inputSchema: {},
305
+ },
306
+ async () => {
307
+ const arr = getArr();
308
+ return { content: [{ type: "text", text: JSON.stringify({ arr }) }] };
309
+ }
310
+ );
311
+
312
+ server.registerTool(
313
+ "get_churn_rate",
314
+ {
315
+ title: "Get Churn Rate",
316
+ description: "Get churn rate for a period.",
317
+ inputSchema: {
318
+ period_days: z.number().default(30),
319
+ },
320
+ },
321
+ async ({ period_days }) => {
322
+ const rate = getChurnRate(period_days);
323
+ return { content: [{ type: "text", text: JSON.stringify({ churn_rate: rate, period_days }) }] };
324
+ }
325
+ );
326
+
327
+ server.registerTool(
328
+ "list_expiring",
329
+ {
330
+ title: "List Expiring Subscriptions",
331
+ description: "List subscriptions expiring within a number of days.",
332
+ inputSchema: {
333
+ days: z.number().default(7),
334
+ },
335
+ },
336
+ async ({ days }) => {
337
+ const expiring = listExpiring(days);
338
+ return {
339
+ content: [
340
+ {
341
+ type: "text",
342
+ text: JSON.stringify({ expiring, count: expiring.length }, null, 2),
343
+ },
344
+ ],
345
+ };
346
+ }
347
+ );
348
+
349
+ server.registerTool(
350
+ "get_subscriber_stats",
351
+ {
352
+ title: "Get Subscriber Stats",
353
+ description: "Get subscriber statistics including counts by status, MRR, and ARR.",
354
+ inputSchema: {},
355
+ },
356
+ async () => {
357
+ const stats = getSubscriberStats();
358
+ const mrr = getMrr();
359
+ const arr = getArr();
360
+ return {
361
+ content: [
362
+ { type: "text", text: JSON.stringify({ ...stats, mrr, arr }, null, 2) },
363
+ ],
364
+ };
365
+ }
366
+ );
367
+
368
+ // --- Pause/Resume ---
369
+
370
+ server.registerTool(
371
+ "pause_subscriber",
372
+ {
373
+ title: "Pause Subscriber",
374
+ description: "Pause a subscription. Paused subscribers are excluded from MRR.",
375
+ inputSchema: {
376
+ id: z.string(),
377
+ resume_date: z.string().optional().describe("Optional scheduled resume date (YYYY-MM-DD HH:MM:SS)"),
378
+ },
379
+ },
380
+ async ({ id, resume_date }) => {
381
+ const subscriber = pauseSubscriber(id, resume_date);
382
+ if (!subscriber) {
383
+ return { content: [{ type: "text", text: `Subscriber '${id}' not found or cannot be paused.` }], isError: true };
384
+ }
385
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
386
+ }
387
+ );
388
+
389
+ server.registerTool(
390
+ "resume_subscriber",
391
+ {
392
+ title: "Resume Subscriber",
393
+ description: "Resume a paused subscription.",
394
+ inputSchema: { id: z.string() },
395
+ },
396
+ async ({ id }) => {
397
+ const subscriber = resumeSubscriber(id);
398
+ if (!subscriber) {
399
+ return { content: [{ type: "text", text: `Subscriber '${id}' not found or not paused.` }], isError: true };
400
+ }
401
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
402
+ }
403
+ );
404
+
405
+ // --- Trial Extension ---
406
+
407
+ server.registerTool(
408
+ "extend_trial",
409
+ {
410
+ title: "Extend Trial",
411
+ description: "Extend a subscriber's trial period by a number of days.",
412
+ inputSchema: {
413
+ id: z.string(),
414
+ days: z.number().describe("Number of days to extend the trial"),
415
+ },
416
+ },
417
+ async ({ id, days }) => {
418
+ const subscriber = extendTrial(id, days);
419
+ if (!subscriber) {
420
+ return { content: [{ type: "text", text: `Subscriber '${id}' not found.` }], isError: true };
421
+ }
422
+ return { content: [{ type: "text", text: JSON.stringify(subscriber, null, 2) }] };
423
+ }
424
+ );
425
+
426
+ // --- Bulk Import/Export ---
427
+
428
+ server.registerTool(
429
+ "bulk_import_subscribers",
430
+ {
431
+ title: "Bulk Import Subscribers",
432
+ description: "Bulk import multiple subscribers at once.",
433
+ inputSchema: {
434
+ subscribers: z.array(z.object({
435
+ plan_id: z.string(),
436
+ customer_name: z.string(),
437
+ customer_email: z.string(),
438
+ status: z.enum(["trialing", "active", "past_due", "canceled", "expired", "paused"]).optional(),
439
+ trial_ends_at: z.string().optional(),
440
+ current_period_end: z.string().optional(),
441
+ })),
442
+ },
443
+ },
444
+ async ({ subscribers }) => {
445
+ const imported = bulkImportSubscribers(subscribers);
446
+ return {
447
+ content: [
448
+ { type: "text", text: JSON.stringify({ imported: imported.length, subscribers: imported }, null, 2) },
449
+ ],
450
+ };
451
+ }
452
+ );
453
+
454
+ server.registerTool(
455
+ "export_subscribers",
456
+ {
457
+ title: "Export Subscribers",
458
+ description: "Export all subscribers in CSV or JSON format.",
459
+ inputSchema: {
460
+ format: z.enum(["csv", "json"]).default("json"),
461
+ },
462
+ },
463
+ async ({ format }) => {
464
+ const output = exportSubscribers(format);
465
+ return { content: [{ type: "text", text: output }] };
466
+ }
467
+ );
468
+
469
+ // --- Dunning ---
470
+
471
+ server.registerTool(
472
+ "create_dunning",
473
+ {
474
+ title: "Create Dunning Attempt",
475
+ description: "Create a new dunning attempt for a subscriber.",
476
+ inputSchema: {
477
+ subscriber_id: z.string(),
478
+ attempt_number: z.number().optional(),
479
+ status: z.enum(["pending", "retrying", "failed", "recovered"]).optional(),
480
+ next_retry_at: z.string().optional(),
481
+ },
482
+ },
483
+ async (params) => {
484
+ const attempt = createDunning(params);
485
+ return { content: [{ type: "text", text: JSON.stringify(attempt, null, 2) }] };
486
+ }
487
+ );
488
+
489
+ server.registerTool(
490
+ "list_dunning",
491
+ {
492
+ title: "List Dunning Attempts",
493
+ description: "List dunning attempts with optional filters.",
494
+ inputSchema: {
495
+ subscriber_id: z.string().optional(),
496
+ status: z.string().optional(),
497
+ limit: z.number().optional(),
498
+ },
499
+ },
500
+ async (params) => {
501
+ const attempts = listDunning(params);
502
+ return {
503
+ content: [
504
+ { type: "text", text: JSON.stringify({ attempts, count: attempts.length }, null, 2) },
505
+ ],
506
+ };
507
+ }
508
+ );
509
+
510
+ server.registerTool(
511
+ "update_dunning",
512
+ {
513
+ title: "Update Dunning Attempt",
514
+ description: "Update a dunning attempt status or next retry date.",
515
+ inputSchema: {
516
+ id: z.string(),
517
+ status: z.enum(["pending", "retrying", "failed", "recovered"]).optional(),
518
+ next_retry_at: z.string().optional(),
519
+ },
520
+ },
521
+ async ({ id, ...input }) => {
522
+ const attempt = updateDunning(id, input);
523
+ if (!attempt) {
524
+ return { content: [{ type: "text", text: `Dunning attempt '${id}' not found.` }], isError: true };
525
+ }
526
+ return { content: [{ type: "text", text: JSON.stringify(attempt, null, 2) }] };
527
+ }
528
+ );
529
+
530
+ // --- LTV ---
531
+
532
+ server.registerTool(
533
+ "get_ltv",
534
+ {
535
+ title: "Get LTV",
536
+ description: "Get lifetime value per subscriber and average LTV.",
537
+ inputSchema: {},
538
+ },
539
+ async () => {
540
+ const result = getLtv();
541
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
542
+ }
543
+ );
544
+
545
+ // --- NRR ---
546
+
547
+ server.registerTool(
548
+ "get_nrr",
549
+ {
550
+ title: "Get NRR",
551
+ description: "Calculate net revenue retention for a given month.",
552
+ inputSchema: {
553
+ month: z.string().describe("Month in YYYY-MM format"),
554
+ },
555
+ },
556
+ async ({ month }) => {
557
+ const result = getNrr(month);
558
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
559
+ }
560
+ );
561
+
562
+ // --- Cohort Analysis ---
563
+
564
+ server.registerTool(
565
+ "cohort_report",
566
+ {
567
+ title: "Cohort Report",
568
+ description: "Generate a cohort retention analysis for the last N months.",
569
+ inputSchema: {
570
+ months: z.number().default(6),
571
+ },
572
+ },
573
+ async ({ months }) => {
574
+ const report = getCohortReport(months);
575
+ return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
576
+ }
577
+ );
578
+
579
+ // --- Plan Comparison ---
580
+
581
+ server.registerTool(
582
+ "compare_plans",
583
+ {
584
+ title: "Compare Plans",
585
+ description: "Compare two plans side by side showing price and feature differences.",
586
+ inputSchema: {
587
+ id1: z.string().describe("First plan ID"),
588
+ id2: z.string().describe("Second plan ID"),
589
+ },
590
+ },
591
+ async ({ id1, id2 }) => {
592
+ const result = comparePlans(id1, id2);
593
+ if (!result) {
594
+ return { content: [{ type: "text", text: "One or both plans not found." }], isError: true };
595
+ }
596
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
597
+ }
598
+ );
599
+
600
+ // --- Expiring Renewals ---
601
+
602
+ server.registerTool(
603
+ "expiring_renewals",
604
+ {
605
+ title: "Expiring Renewals",
606
+ description: "List subscribers whose current period ends within N days.",
607
+ inputSchema: {
608
+ days: z.number().default(7),
609
+ },
610
+ },
611
+ async ({ days }) => {
612
+ const expiring = getExpiringRenewals(days);
613
+ return {
614
+ content: [
615
+ { type: "text", text: JSON.stringify({ expiring, count: expiring.length }, null, 2) },
616
+ ],
617
+ };
618
+ }
619
+ );
620
+
621
+ // --- Start ---
622
+ async function main() {
623
+ const transport = new StdioServerTransport();
624
+ await server.connect(transport);
625
+ console.error("microservice-subscriptions MCP server running on stdio");
626
+ }
627
+
628
+ main().catch((error) => {
629
+ console.error("Fatal error:", error);
630
+ process.exit(1);
631
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/microservices",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Mini business apps for AI agents - invoices, contacts, bookkeeping and more, each with its own SQLite database",
5
5
  "type": "module",
6
6
  "bin": {