@unchainedshop/api 4.0.0-rc.10 → 4.0.0-rc.11

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 (166) hide show
  1. package/lib/mcp/index.d.ts.map +1 -1
  2. package/lib/mcp/index.js +6 -0
  3. package/lib/mcp/index.js.map +1 -1
  4. package/lib/mcp/tools/delivery/createDeliveryProviderHandler.d.ts +44 -0
  5. package/lib/mcp/tools/delivery/createDeliveryProviderHandler.d.ts.map +1 -0
  6. package/lib/mcp/tools/delivery/createDeliveryProviderHandler.js +50 -0
  7. package/lib/mcp/tools/delivery/createDeliveryProviderHandler.js.map +1 -0
  8. package/lib/mcp/tools/delivery/deliveryInterfacesHandler.d.ts +20 -0
  9. package/lib/mcp/tools/delivery/deliveryInterfacesHandler.d.ts.map +1 -0
  10. package/lib/mcp/tools/delivery/deliveryInterfacesHandler.js +45 -0
  11. package/lib/mcp/tools/delivery/deliveryInterfacesHandler.js.map +1 -0
  12. package/lib/mcp/tools/delivery/deliveryProvider.d.ts +20 -0
  13. package/lib/mcp/tools/delivery/deliveryProvider.d.ts.map +1 -0
  14. package/lib/mcp/tools/delivery/deliveryProvider.js +43 -0
  15. package/lib/mcp/tools/delivery/deliveryProvider.js.map +1 -0
  16. package/lib/mcp/tools/delivery/deliveryProvidersCountHandler.d.ts +20 -0
  17. package/lib/mcp/tools/delivery/deliveryProvidersCountHandler.d.ts.map +1 -0
  18. package/lib/mcp/tools/delivery/deliveryProvidersCountHandler.js +36 -0
  19. package/lib/mcp/tools/delivery/deliveryProvidersCountHandler.js.map +1 -0
  20. package/lib/mcp/tools/delivery/deliveryProvidersListHandler.d.ts +24 -0
  21. package/lib/mcp/tools/delivery/deliveryProvidersListHandler.d.ts.map +1 -0
  22. package/lib/mcp/tools/delivery/deliveryProvidersListHandler.js +44 -0
  23. package/lib/mcp/tools/delivery/deliveryProvidersListHandler.js.map +1 -0
  24. package/lib/mcp/tools/delivery/index.d.ts +4 -0
  25. package/lib/mcp/tools/delivery/index.d.ts.map +1 -0
  26. package/lib/mcp/tools/delivery/index.js +17 -0
  27. package/lib/mcp/tools/delivery/index.js.map +1 -0
  28. package/lib/mcp/tools/delivery/removeDeliveryProviderHandler.d.ts +20 -0
  29. package/lib/mcp/tools/delivery/removeDeliveryProviderHandler.d.ts.map +1 -0
  30. package/lib/mcp/tools/delivery/removeDeliveryProviderHandler.js +37 -0
  31. package/lib/mcp/tools/delivery/removeDeliveryProviderHandler.js.map +1 -0
  32. package/lib/mcp/tools/delivery/updateDeliveryProviderHandler.d.ts +96 -0
  33. package/lib/mcp/tools/delivery/updateDeliveryProviderHandler.d.ts.map +1 -0
  34. package/lib/mcp/tools/delivery/updateDeliveryProviderHandler.js +54 -0
  35. package/lib/mcp/tools/delivery/updateDeliveryProviderHandler.js.map +1 -0
  36. package/lib/mcp/tools/order/getMonthlySalesBreakdownHandler.d.ts +36 -0
  37. package/lib/mcp/tools/order/getMonthlySalesBreakdownHandler.d.ts.map +1 -0
  38. package/lib/mcp/tools/order/getMonthlySalesBreakdownHandler.js +89 -0
  39. package/lib/mcp/tools/order/getMonthlySalesBreakdownHandler.js.map +1 -0
  40. package/lib/mcp/tools/order/getSalesSummaryHandler.d.ts +40 -0
  41. package/lib/mcp/tools/order/getSalesSummaryHandler.d.ts.map +1 -0
  42. package/lib/mcp/tools/order/getSalesSummaryHandler.js +90 -0
  43. package/lib/mcp/tools/order/getSalesSummaryHandler.js.map +1 -0
  44. package/lib/mcp/tools/order/getTopCustomersHandler.d.ts +52 -0
  45. package/lib/mcp/tools/order/getTopCustomersHandler.d.ts.map +1 -0
  46. package/lib/mcp/tools/order/getTopCustomersHandler.js +111 -0
  47. package/lib/mcp/tools/order/getTopCustomersHandler.js.map +1 -0
  48. package/lib/mcp/tools/order/getTopSellingProductsHandler.d.ts +28 -0
  49. package/lib/mcp/tools/order/getTopSellingProductsHandler.d.ts.map +1 -0
  50. package/lib/mcp/tools/order/getTopSellingProductsHandler.js +99 -0
  51. package/lib/mcp/tools/order/getTopSellingProductsHandler.js.map +1 -0
  52. package/lib/mcp/tools/order/index.d.ts +4 -0
  53. package/lib/mcp/tools/order/index.d.ts.map +1 -0
  54. package/lib/mcp/tools/order/index.js +13 -0
  55. package/lib/mcp/tools/order/index.js.map +1 -0
  56. package/lib/mcp/tools/order/ordersListHandler.d.ts +108 -0
  57. package/lib/mcp/tools/order/ordersListHandler.d.ts.map +1 -0
  58. package/lib/mcp/tools/order/ordersListHandler.js +91 -0
  59. package/lib/mcp/tools/order/ordersListHandler.js.map +1 -0
  60. package/lib/mcp/tools/payment/createPaymentProviderHandler.d.ts.map +1 -1
  61. package/lib/mcp/tools/payment/createPaymentProviderHandler.js +5 -2
  62. package/lib/mcp/tools/payment/createPaymentProviderHandler.js.map +1 -1
  63. package/lib/mcp/tools/payment/index.js +1 -1
  64. package/lib/mcp/tools/payment/index.js.map +1 -1
  65. package/lib/mcp/tools/payment/paymentInterfacesHandler.d.ts +2 -2
  66. package/lib/mcp/tools/payment/paymentInterfacesHandler.d.ts.map +1 -1
  67. package/lib/mcp/tools/payment/paymentInterfacesHandler.js +7 -5
  68. package/lib/mcp/tools/payment/paymentInterfacesHandler.js.map +1 -1
  69. package/lib/mcp/tools/payment/paymentProviderByIdHandler.js +1 -1
  70. package/lib/mcp/tools/payment/paymentProviderByIdHandler.js.map +1 -1
  71. package/lib/mcp/tools/payment/paymentProvidersListHandler.d.ts +4 -0
  72. package/lib/mcp/tools/payment/paymentProvidersListHandler.d.ts.map +1 -1
  73. package/lib/mcp/tools/payment/paymentProvidersListHandler.js +12 -3
  74. package/lib/mcp/tools/payment/paymentProvidersListHandler.js.map +1 -1
  75. package/lib/mcp/tools/payment/removePaymentProviderHandler.d.ts.map +1 -1
  76. package/lib/mcp/tools/payment/removePaymentProviderHandler.js +5 -4
  77. package/lib/mcp/tools/payment/removePaymentProviderHandler.js.map +1 -1
  78. package/lib/mcp/tools/payment/updatePaymentProviderHandler.d.ts +66 -10
  79. package/lib/mcp/tools/payment/updatePaymentProviderHandler.d.ts.map +1 -1
  80. package/lib/mcp/tools/payment/updatePaymentProviderHandler.js +18 -3
  81. package/lib/mcp/tools/payment/updatePaymentProviderHandler.js.map +1 -1
  82. package/lib/mcp/tools/warehousing/createWarehousingProviderHandler.d.ts +44 -0
  83. package/lib/mcp/tools/warehousing/createWarehousingProviderHandler.d.ts.map +1 -0
  84. package/lib/mcp/tools/warehousing/createWarehousingProviderHandler.js +48 -0
  85. package/lib/mcp/tools/warehousing/createWarehousingProviderHandler.js.map +1 -0
  86. package/lib/mcp/tools/warehousing/index.d.ts +4 -0
  87. package/lib/mcp/tools/warehousing/index.d.ts.map +1 -0
  88. package/lib/mcp/tools/warehousing/index.js +17 -0
  89. package/lib/mcp/tools/warehousing/index.js.map +1 -0
  90. package/lib/mcp/tools/warehousing/removeWarehousingProviderHandler.d.ts +20 -0
  91. package/lib/mcp/tools/warehousing/removeWarehousingProviderHandler.d.ts.map +1 -0
  92. package/lib/mcp/tools/warehousing/removeWarehousingProviderHandler.js +39 -0
  93. package/lib/mcp/tools/warehousing/removeWarehousingProviderHandler.js.map +1 -0
  94. package/lib/mcp/tools/warehousing/updateWarehousingProviderHandler.d.ts +96 -0
  95. package/lib/mcp/tools/warehousing/updateWarehousingProviderHandler.d.ts.map +1 -0
  96. package/lib/mcp/tools/warehousing/updateWarehousingProviderHandler.js +58 -0
  97. package/lib/mcp/tools/warehousing/updateWarehousingProviderHandler.js.map +1 -0
  98. package/lib/mcp/tools/warehousing/warehousingInterfacesHandler.d.ts +20 -0
  99. package/lib/mcp/tools/warehousing/warehousingInterfacesHandler.d.ts.map +1 -0
  100. package/lib/mcp/tools/warehousing/warehousingInterfacesHandler.js +45 -0
  101. package/lib/mcp/tools/warehousing/warehousingInterfacesHandler.js.map +1 -0
  102. package/lib/mcp/tools/warehousing/warehousingProviderHandler.d.ts +20 -0
  103. package/lib/mcp/tools/warehousing/warehousingProviderHandler.d.ts.map +1 -0
  104. package/lib/mcp/tools/warehousing/warehousingProviderHandler.js +45 -0
  105. package/lib/mcp/tools/warehousing/warehousingProviderHandler.js.map +1 -0
  106. package/lib/mcp/tools/warehousing/warehousingProvidersCountHandler.d.ts +20 -0
  107. package/lib/mcp/tools/warehousing/warehousingProvidersCountHandler.d.ts.map +1 -0
  108. package/lib/mcp/tools/warehousing/warehousingProvidersCountHandler.js +36 -0
  109. package/lib/mcp/tools/warehousing/warehousingProvidersCountHandler.js.map +1 -0
  110. package/lib/mcp/tools/warehousing/warehousingProvidersListHandler.d.ts +24 -0
  111. package/lib/mcp/tools/warehousing/warehousingProvidersListHandler.d.ts.map +1 -0
  112. package/lib/mcp/tools/warehousing/warehousingProvidersListHandler.js +44 -0
  113. package/lib/mcp/tools/warehousing/warehousingProvidersListHandler.js.map +1 -0
  114. package/lib/mcp/utils/orderFilters.d.ts +22 -0
  115. package/lib/mcp/utils/orderFilters.d.ts.map +1 -0
  116. package/lib/mcp/utils/orderFilters.js +34 -0
  117. package/lib/mcp/utils/orderFilters.js.map +1 -0
  118. package/lib/mcp/utils/sharedSchemas.d.ts +11 -0
  119. package/lib/mcp/utils/sharedSchemas.d.ts.map +1 -0
  120. package/lib/mcp/utils/sharedSchemas.js +11 -0
  121. package/lib/mcp/utils/sharedSchemas.js.map +1 -0
  122. package/lib/resolvers/queries/orders/orders.d.ts.map +1 -1
  123. package/lib/resolvers/queries/orders/orders.js +33 -4
  124. package/lib/resolvers/queries/orders/orders.js.map +1 -1
  125. package/lib/resolvers/queries/orders/ordersCount.d.ts.map +1 -1
  126. package/lib/resolvers/queries/orders/ordersCount.js +29 -1
  127. package/lib/resolvers/queries/orders/ordersCount.js.map +1 -1
  128. package/lib/schema/query.d.ts.map +1 -1
  129. package/lib/schema/query.js +11 -1
  130. package/lib/schema/query.js.map +1 -1
  131. package/package.json +1 -1
  132. package/src/mcp/index.ts +6 -1
  133. package/src/mcp/tools/delivery/createDeliveryProviderHandler.ts +62 -0
  134. package/src/mcp/tools/delivery/deliveryInterfacesHandler.ts +56 -0
  135. package/src/mcp/tools/delivery/deliveryProvider.ts +51 -0
  136. package/src/mcp/tools/delivery/deliveryProvidersCountHandler.ts +46 -0
  137. package/src/mcp/tools/delivery/deliveryProvidersListHandler.ts +54 -0
  138. package/src/mcp/tools/delivery/index.ts +74 -0
  139. package/src/mcp/tools/delivery/removeDeliveryProviderHandler.ts +45 -0
  140. package/src/mcp/tools/delivery/updateDeliveryProviderHandler.ts +68 -0
  141. package/src/mcp/tools/order/getMonthlySalesBreakdownHandler.ts +106 -0
  142. package/src/mcp/tools/order/getSalesSummaryHandler.ts +103 -0
  143. package/src/mcp/tools/order/getTopCustomersHandler.ts +123 -0
  144. package/src/mcp/tools/order/getTopSellingProductsHandler.ts +112 -0
  145. package/src/mcp/tools/order/index.ts +50 -0
  146. package/src/mcp/tools/order/ordersListHandler.ts +119 -0
  147. package/src/mcp/tools/payment/createPaymentProviderHandler.ts +7 -2
  148. package/src/mcp/tools/payment/index.ts +1 -1
  149. package/src/mcp/tools/payment/paymentInterfacesHandler.ts +13 -10
  150. package/src/mcp/tools/payment/paymentProviderByIdHandler.ts +1 -1
  151. package/src/mcp/tools/payment/paymentProvidersListHandler.ts +14 -4
  152. package/src/mcp/tools/payment/removePaymentProviderHandler.ts +6 -8
  153. package/src/mcp/tools/payment/updatePaymentProviderHandler.ts +23 -4
  154. package/src/mcp/tools/warehousing/createWarehousingProviderHandler.ts +61 -0
  155. package/src/mcp/tools/warehousing/index.ts +81 -0
  156. package/src/mcp/tools/warehousing/removeWarehousingProviderHandler.ts +46 -0
  157. package/src/mcp/tools/warehousing/updateWarehousingProviderHandler.ts +72 -0
  158. package/src/mcp/tools/warehousing/warehousingInterfacesHandler.ts +58 -0
  159. package/src/mcp/tools/warehousing/warehousingProviderHandler.ts +55 -0
  160. package/src/mcp/tools/warehousing/warehousingProvidersCountHandler.ts +46 -0
  161. package/src/mcp/tools/warehousing/warehousingProvidersListHandler.ts +55 -0
  162. package/src/mcp/utils/orderFilters.ts +52 -0
  163. package/src/mcp/utils/sharedSchemas.ts +12 -0
  164. package/src/resolvers/queries/orders/orders.ts +46 -5
  165. package/src/resolvers/queries/orders/ordersCount.ts +42 -1
  166. package/src/schema/query.ts +11 -1
@@ -0,0 +1,74 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { Context } from '../../../context.js';
3
+ import {
4
+ deliveryProvidersListHandler,
5
+ DeliveryProvidersListSchema,
6
+ } from './deliveryProvidersListHandler.js';
7
+ import {
8
+ deliveryProvidersCountHandler,
9
+ DeliveryProvidersCountSchema,
10
+ } from './deliveryProvidersCountHandler.js';
11
+ import { DeliveryProviderByIdSchema, deliveryProviderHandler } from './deliveryProvider.js';
12
+ import { deliveryInterfacesHandler, DeliveryInterfacesSchema } from './deliveryInterfacesHandler.js';
13
+ import {
14
+ createDeliveryProviderHandler,
15
+ CreateDeliveryProviderSchema,
16
+ } from './createDeliveryProviderHandler.js';
17
+ import {
18
+ updateDeliveryProviderHandler,
19
+ UpdateDeliveryProviderSchema,
20
+ } from './updateDeliveryProviderHandler.js';
21
+ import {
22
+ removeDeliveryProviderHandler,
23
+ RemoveDeliveryProviderSchema,
24
+ } from './removeDeliveryProviderHandler.js';
25
+
26
+ export const registerDeliveryProviderTools = (server: McpServer, context: Context) => {
27
+ server.tool(
28
+ 'deliveryProviders_list',
29
+ 'Get all delivery providers, optionally filtered by type.',
30
+ DeliveryProvidersListSchema,
31
+ async (params) => deliveryProvidersListHandler(context, params),
32
+ );
33
+
34
+ server.tool(
35
+ 'deliveryProviders_count',
36
+ 'Returns total number of delivery providers, optionally filtered by type.',
37
+ DeliveryProvidersCountSchema,
38
+ async (params) => deliveryProvidersCountHandler(context, params),
39
+ );
40
+
41
+ server.tool(
42
+ 'deliveryProvider_get',
43
+ 'Get a specific delivery provider by ID.',
44
+ DeliveryProviderByIdSchema,
45
+ async (params) => deliveryProviderHandler(context, params),
46
+ );
47
+ server.tool(
48
+ 'deliveryInterfaces_list',
49
+ 'Get all delivery interfaces filtered by type.',
50
+ DeliveryInterfacesSchema,
51
+ async (params) => deliveryInterfacesHandler(context, params),
52
+ );
53
+
54
+ server.tool(
55
+ 'deliveryProvider_add',
56
+ 'Adds new delivery provider.',
57
+ CreateDeliveryProviderSchema,
58
+ async (params) => createDeliveryProviderHandler(context, params),
59
+ );
60
+
61
+ server.tool(
62
+ 'deliveryProvider_update',
63
+ 'Updates the delivery provider specified.',
64
+ UpdateDeliveryProviderSchema,
65
+ async (params) => updateDeliveryProviderHandler(context, params),
66
+ );
67
+
68
+ server.tool(
69
+ 'deliveryProvider_remove',
70
+ 'Soft deletes a delivery provider by setting its deleted timestamp.',
71
+ RemoveDeliveryProviderSchema,
72
+ async (params) => removeDeliveryProviderHandler(context, params),
73
+ );
74
+ };
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { log } from '@unchainedshop/logger';
4
+ import { DeliverProviderNotFoundError } from '../../../errors.js';
5
+
6
+ export const RemoveDeliveryProviderSchema = {
7
+ deliveryProviderId: z.string().min(1).describe('ID of the delivery provider to soft delete'),
8
+ };
9
+
10
+ export const RemoveDeliveryProviderZodSchema = z.object(RemoveDeliveryProviderSchema);
11
+ export type RemoveDeliveryProviderParams = z.infer<typeof RemoveDeliveryProviderZodSchema>;
12
+
13
+ export async function removeDeliveryProviderHandler(
14
+ context: Context,
15
+ params: RemoveDeliveryProviderParams,
16
+ ) {
17
+ const { modules, userId } = context;
18
+ const { deliveryProviderId } = params;
19
+ try {
20
+ log('handler removeDeliveryProviderHandler', { userId, deliveryProviderId });
21
+
22
+ const provider = await modules.delivery.findProvider({ deliveryProviderId });
23
+ if (!provider) throw new DeliverProviderNotFoundError({ deliveryProviderId });
24
+
25
+ const removed = await modules.delivery.delete(deliveryProviderId);
26
+
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text' as const,
31
+ text: JSON.stringify({ provider: removed }),
32
+ },
33
+ ],
34
+ };
35
+ } catch (error) {
36
+ return {
37
+ content: [
38
+ {
39
+ type: 'text' as const,
40
+ text: `Error removing delivery provider: ${(error as Error).message}`,
41
+ },
42
+ ],
43
+ };
44
+ }
45
+ }
@@ -0,0 +1,68 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { log } from '@unchainedshop/logger';
4
+ import { DeliverProviderNotFoundError } from '../../../errors.js';
5
+
6
+ const ConfigurationEntry = z.object({
7
+ key: z.string().min(1).describe('Configuration key'),
8
+ value: z.any().describe('Configuration value'),
9
+ });
10
+
11
+ export const UpdateDeliveryProviderSchema = {
12
+ deliveryProviderId: z.string().min(1).describe('ID of the delivery provider to update'),
13
+ deliveryProvider: z
14
+ .object({
15
+ configuration: z
16
+ .array(ConfigurationEntry)
17
+ .optional()
18
+ .describe('List of key-value configuration entries'),
19
+ })
20
+ .refine(
21
+ (data) => {
22
+ const keys = data.configuration?.map((entry) => entry.key) || [];
23
+ return new Set(keys).size === keys.length;
24
+ },
25
+ {
26
+ message: 'Duplicate keys are not allowed in configuration.',
27
+ path: ['configuration'],
28
+ },
29
+ ),
30
+ };
31
+
32
+ export const UpdateDeliveryProviderZodSchema = z.object(UpdateDeliveryProviderSchema);
33
+ export type UpdateDeliveryProviderParams = z.infer<typeof UpdateDeliveryProviderZodSchema>;
34
+
35
+ export async function updateDeliveryProviderHandler(
36
+ context: Context,
37
+ params: UpdateDeliveryProviderParams,
38
+ ) {
39
+ const { modules, userId } = context;
40
+ const { deliveryProviderId, deliveryProvider } = params;
41
+
42
+ try {
43
+ log('handler updateDeliveryProviderHandler', { userId, deliveryProviderId, deliveryProvider });
44
+
45
+ if (!(await modules.delivery.providerExists({ deliveryProviderId })))
46
+ throw new DeliverProviderNotFoundError({ deliveryProviderId });
47
+
48
+ const updated = await modules.delivery.update(deliveryProviderId, deliveryProvider as any);
49
+
50
+ return {
51
+ content: [
52
+ {
53
+ type: 'text' as const,
54
+ text: JSON.stringify({ provider: updated }),
55
+ },
56
+ ],
57
+ };
58
+ } catch (error) {
59
+ return {
60
+ content: [
61
+ {
62
+ type: 'text' as const,
63
+ text: `Error updating delivery provider: ${(error as Error).message}`,
64
+ },
65
+ ],
66
+ };
67
+ }
68
+ }
@@ -0,0 +1,106 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { log } from '@unchainedshop/logger';
4
+ import { DateRangeSchema, OrderFilterSchema } from '../../utils/sharedSchemas.js';
5
+ import { formatSummaryMap, resolveDateRange, resolveOrderFilters } from '../../utils/orderFilters.js';
6
+
7
+ export const MonthlySalesBreakdownSchema = {
8
+ ...DateRangeSchema,
9
+ ...OrderFilterSchema,
10
+ };
11
+
12
+ export const MonthlySalesBreakdownZodSchema = z.object(MonthlySalesBreakdownSchema);
13
+ export type MonthlySalesBreakdownParams = z.infer<typeof MonthlySalesBreakdownZodSchema>;
14
+
15
+ export async function getMonthlySalesBreakdownHandler(
16
+ context: Context,
17
+ params: MonthlySalesBreakdownParams,
18
+ ) {
19
+ const { modules, userId } = context;
20
+
21
+ try {
22
+ log('handler getMonthlySalesBreakdownHandler', { userId, params });
23
+
24
+ const { from, to, paymentProviderIds, deliveryProviderIds, status } = params;
25
+
26
+ const { startDate, endDate } = resolveDateRange(from, to);
27
+ const filters = await resolveOrderFilters(modules, { paymentProviderIds, deliveryProviderIds });
28
+ if (!filters) {
29
+ return {
30
+ content: [
31
+ {
32
+ type: 'text' as const,
33
+ text: JSON.stringify({
34
+ totalSalesAmount: 0,
35
+ orderCount: 0,
36
+ averageOrderValue: 0,
37
+ currencyCode: null,
38
+ summary: [],
39
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
40
+ }),
41
+ },
42
+ ],
43
+ };
44
+ }
45
+
46
+ const orders = await modules.orders.findOrders({
47
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
48
+ status,
49
+ ...filters,
50
+ } as any);
51
+
52
+ let totalSalesAmount = 0;
53
+ let orderCount = 0;
54
+
55
+ const monthlyMap = new Map<string, { sales: number; orders: number }>();
56
+
57
+ for (let i = 0; i < 12; i++) {
58
+ const date = new Date(endDate.getFullYear(), endDate.getMonth() - i, 1);
59
+ const label = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
60
+ monthlyMap.set(label, { sales: 0, orders: 0 });
61
+ }
62
+
63
+ for (const order of orders) {
64
+ const orderDate = new Date(order.created);
65
+ if (orderDate < startDate || orderDate > endDate) continue;
66
+
67
+ orderCount++;
68
+ const itemsAmount = order.calculation?.find((c) => c.category === 'ITEMS')?.amount || 0;
69
+ totalSalesAmount += itemsAmount;
70
+
71
+ const label = `${orderDate.getFullYear()}-${String(orderDate.getMonth() + 1).padStart(2, '0')}`;
72
+ const entry = monthlyMap.get(label);
73
+ if (entry) {
74
+ entry.sales += itemsAmount;
75
+ entry.orders += 1;
76
+ }
77
+ }
78
+
79
+ const averageOrderValue = orderCount > 0 ? totalSalesAmount / orderCount : 0;
80
+
81
+ return {
82
+ content: [
83
+ {
84
+ type: 'text' as const,
85
+ text: JSON.stringify({
86
+ totalSalesAmount,
87
+ orderCount,
88
+ averageOrderValue,
89
+ currencyCode: orders[0]?.currencyCode ?? null,
90
+ summary: formatSummaryMap(monthlyMap),
91
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
92
+ }),
93
+ },
94
+ ],
95
+ };
96
+ } catch (error) {
97
+ return {
98
+ content: [
99
+ {
100
+ type: 'text' as const,
101
+ text: `Error fetching monthly sales breakdown: ${(error as Error).message}`,
102
+ },
103
+ ],
104
+ };
105
+ }
106
+ }
@@ -0,0 +1,103 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { log } from '@unchainedshop/logger';
4
+ import { DateRangeSchema, OrderFilterSchema } from '../../utils/sharedSchemas.js';
5
+ import { formatSummaryMap, resolveDateRange, resolveOrderFilters } from '../../utils/orderFilters.js';
6
+
7
+ export const SalesSummarySchema = {
8
+ ...DateRangeSchema,
9
+ ...OrderFilterSchema,
10
+ days: z.number().int().min(1).max(365).optional(),
11
+ };
12
+
13
+ export const SalesSummaryZodSchema = z.object(SalesSummarySchema);
14
+ export type SalesSummaryParams = z.infer<typeof SalesSummaryZodSchema>;
15
+
16
+ export async function getSalesSummaryHandler(context: Context, params: SalesSummaryParams) {
17
+ const { modules, userId } = context;
18
+
19
+ try {
20
+ log('handler getSalesSummaryHandler', { userId, params });
21
+
22
+ const { from, to, days = 30, paymentProviderIds, deliveryProviderIds, status } = params;
23
+ const { startDate, endDate } = resolveDateRange(from, to, days);
24
+
25
+ const filters = await resolveOrderFilters(modules, { paymentProviderIds, deliveryProviderIds });
26
+ if (!filters) {
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text' as const,
31
+ text: JSON.stringify({
32
+ totalSalesAmount: 0,
33
+ orderCount: 0,
34
+ averageOrderValue: 0,
35
+ currencyCode: null,
36
+ summary: [],
37
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
38
+ }),
39
+ },
40
+ ],
41
+ };
42
+ }
43
+
44
+ const orders = await modules.orders.findOrders({
45
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
46
+ ...filters,
47
+ status,
48
+ } as any);
49
+
50
+ let totalSalesAmount = 0;
51
+ let orderCount = 0;
52
+
53
+ const dateMap = new Map<string, { sales: number; orders: number }>();
54
+ for (let i = 0; i < days; i++) {
55
+ const date = new Date(endDate.getTime() - i * 86400000);
56
+ const label = date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
57
+ dateMap.set(label, { sales: 0, orders: 0 });
58
+ }
59
+
60
+ for (const order of orders) {
61
+ const orderDate = new Date(order.created);
62
+ if (orderDate < startDate || orderDate > endDate) continue;
63
+
64
+ orderCount++;
65
+ const itemsAmount = order.calculation?.find((c) => c.category === 'ITEMS')?.amount || 0;
66
+ totalSalesAmount += itemsAmount;
67
+
68
+ const label = orderDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
69
+ const entry = dateMap.get(label);
70
+ if (entry) {
71
+ entry.sales += itemsAmount;
72
+ entry.orders += 1;
73
+ }
74
+ }
75
+
76
+ const averageOrderValue = orderCount > 0 ? totalSalesAmount / orderCount : 0;
77
+
78
+ return {
79
+ content: [
80
+ {
81
+ type: 'text' as const,
82
+ text: JSON.stringify({
83
+ totalSalesAmount,
84
+ orderCount,
85
+ averageOrderValue,
86
+ currencyCode: orders[0]?.currencyCode ?? null,
87
+ summary: formatSummaryMap(dateMap),
88
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
89
+ }),
90
+ },
91
+ ],
92
+ };
93
+ } catch (error) {
94
+ return {
95
+ content: [
96
+ {
97
+ type: 'text' as const,
98
+ text: `Error fetching sales summary: ${(error as Error).message}`,
99
+ },
100
+ ],
101
+ };
102
+ }
103
+ }
@@ -0,0 +1,123 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { log } from '@unchainedshop/logger';
4
+ import { resolveDateRange } from '../../utils/orderFilters.js';
5
+ import { OrderStatus } from '@unchainedshop/core-orders';
6
+
7
+ export const GetTopCustomersSchema = {
8
+ limit: z.number().int().min(1).max(100).optional().describe('Max number of customers to return'),
9
+ status: z.string().optional().describe('Order status to filter by (e.g., CONFIRMED, DELIVERED)'),
10
+ dateRange: z
11
+ .object({
12
+ start: z.string().datetime().optional(),
13
+ end: z.string().datetime().optional(),
14
+ })
15
+ .optional()
16
+ .describe('Optional date range to filter orders'),
17
+ };
18
+
19
+ export const GetTopCustomersZodSchema = z.object(GetTopCustomersSchema);
20
+ export type GetTopCustomersParams = z.infer<typeof GetTopCustomersZodSchema>;
21
+
22
+ export async function getTopCustomersHandler(context: Context, params: GetTopCustomersParams) {
23
+ const { modules, userId, loaders } = context;
24
+ const { status, dateRange, limit = 10 } = params;
25
+
26
+ try {
27
+ log('handler getTopCustomersHandler', { userId, params });
28
+
29
+ const match: any = { status };
30
+ const { startDate, endDate } = resolveDateRange(dateRange.start, dateRange.end);
31
+
32
+ const orders = await modules.orders.findOrders(
33
+ {
34
+ status: [OrderStatus.CONFIRMED, OrderStatus.FULLFILLED],
35
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
36
+ },
37
+ {
38
+ projection: {
39
+ _id: 1,
40
+ },
41
+ },
42
+ );
43
+ const orderIds = orders.map(({ _id }) => _id);
44
+
45
+ if (startDate) match.created = { ...(match.created || {}), $gte: startDate.toISOString() };
46
+ if (endDate) match.created = { ...(match.created || {}), $lte: endDate.toISOString() };
47
+ if (orderIds?.length) match._id = { $in: orderIds };
48
+
49
+ const topCustomers = await modules.orders.aggregateOrders({
50
+ match,
51
+ project: {
52
+ userId: 1,
53
+ created: 1,
54
+ itemAmount: {
55
+ $let: {
56
+ vars: {
57
+ item: {
58
+ $first: {
59
+ $filter: {
60
+ input: '$calculation',
61
+ as: 'c',
62
+ cond: { $eq: ['$$c.category', 'ITEMS'] },
63
+ },
64
+ },
65
+ },
66
+ },
67
+ in: '$$item.amount',
68
+ },
69
+ },
70
+ },
71
+ group: {
72
+ _id: '$userId',
73
+ totalSpent: { $sum: '$itemAmount' },
74
+ orderCount: { $sum: 1 },
75
+ lastOrderDate: { $max: '$created' },
76
+ },
77
+ addFields: {
78
+ averageOrderValue: {
79
+ $cond: [{ $eq: ['$orderCount', 0] }, 0, { $divide: ['$totalSpent', '$orderCount'] }],
80
+ },
81
+ },
82
+ sort: { totalSpent: -1 },
83
+ limit,
84
+ });
85
+
86
+ const normalizedCustomers = await Promise.all(
87
+ topCustomers.map(async (c) => {
88
+ const user = await modules.users.findUserById(c._id);
89
+ const avatar = await loaders.fileLoader.load({
90
+ fileId: user?.avatarId,
91
+ });
92
+ return {
93
+ userId: c._id?.toString?.() ?? null,
94
+ user: {
95
+ avatar,
96
+ },
97
+ totalSpent: c.totalSpent,
98
+ orderCount: c.orderCount,
99
+ lastOrderDate: c.lastOrderDate,
100
+ averageOrderValue: Math.round(c.averageOrderValue),
101
+ };
102
+ }),
103
+ );
104
+
105
+ return {
106
+ content: [
107
+ {
108
+ type: 'text' as const,
109
+ text: JSON.stringify({ customers: normalizedCustomers }),
110
+ },
111
+ ],
112
+ };
113
+ } catch (error) {
114
+ return {
115
+ content: [
116
+ {
117
+ type: 'text' as const,
118
+ text: `Error fetching top customers: ${(error as Error).message}`,
119
+ },
120
+ ],
121
+ };
122
+ }
123
+ }
@@ -0,0 +1,112 @@
1
+ import { z } from 'zod';
2
+ import { Context } from '../../../context.js';
3
+ import { log } from '@unchainedshop/logger';
4
+ import { getNormalizedProductDetails } from '../../utils/getNormalizedProductDetails.js';
5
+ import { OrderStatus } from '@unchainedshop/core-orders';
6
+ import { resolveDateRange } from '../../utils/orderFilters.js';
7
+
8
+ export const TopSellingProductsSchema = {
9
+ from: z.string().datetime().optional().describe('Start date (ISO format)'),
10
+ to: z.string().datetime().optional().describe('End date (ISO format)'),
11
+ limit: z.number().int().min(1).max(100).optional().default(10),
12
+ };
13
+
14
+ export const TopSellingProductsZodSchema = z.object(TopSellingProductsSchema);
15
+ export type TopSellingProductsParams = z.infer<typeof TopSellingProductsZodSchema>;
16
+
17
+ export async function getTopSellingProductsHandler(context: Context, params: TopSellingProductsParams) {
18
+ const { modules, userId } = context;
19
+ const { from, to, limit } = params;
20
+
21
+ try {
22
+ log('handler getTopSellingProductsHandler', { userId, params });
23
+
24
+ const match: any = {};
25
+
26
+ const { startDate, endDate } = resolveDateRange(from, to);
27
+
28
+ const orders = await modules.orders.findOrders(
29
+ {
30
+ status: [OrderStatus.CONFIRMED, OrderStatus.FULLFILLED],
31
+ dateRange: { start: startDate.toISOString(), end: endDate.toISOString() },
32
+ },
33
+ {
34
+ projection: {
35
+ _id: 1,
36
+ },
37
+ },
38
+ );
39
+ const orderIds = orders.map(({ _id }) => _id);
40
+
41
+ if (startDate) match.created = { ...(match.created || {}), $gte: startDate.toISOString() };
42
+ if (endDate) match.created = { ...(match.created || {}), $lte: endDate.toISOString() };
43
+ if (orderIds?.length) match.orderId = { $in: orderIds };
44
+
45
+ const topProducts = await modules.orders.positions.aggregatePositions({
46
+ match,
47
+ project: {
48
+ productId: 1,
49
+ quantity: 1,
50
+ itemAmount: {
51
+ $let: {
52
+ vars: {
53
+ item: {
54
+ $first: {
55
+ $filter: {
56
+ input: '$calculation',
57
+ as: 'c',
58
+ cond: { $eq: ['$$c.category', 'ITEM'] },
59
+ },
60
+ },
61
+ },
62
+ },
63
+ in: '$$item.amount',
64
+ },
65
+ },
66
+ },
67
+ group: {
68
+ _id: '$productId',
69
+ totalSold: { $sum: '$quantity' },
70
+ totalRevenue: { $sum: '$itemAmount' },
71
+ },
72
+ sort: { totalSold: -1 },
73
+ limit,
74
+ });
75
+
76
+ const normalizedTopSellingProducts = await Promise.all(
77
+ topProducts.map(async (p) => {
78
+ const product = await getNormalizedProductDetails(p._id, context);
79
+ return {
80
+ productId: p._id?.toString?.() ?? null,
81
+ product,
82
+ totalSold: p.totalSold,
83
+ totalRevenue: p.totalRevenue,
84
+ };
85
+ }),
86
+ );
87
+
88
+ return {
89
+ content: [
90
+ {
91
+ type: 'text' as const,
92
+ text: JSON.stringify({
93
+ products: normalizedTopSellingProducts,
94
+ dateRange: {
95
+ start: from ? new Date(from).toISOString() : null,
96
+ end: to ? new Date(to).toISOString() : null,
97
+ },
98
+ }),
99
+ },
100
+ ],
101
+ };
102
+ } catch (error) {
103
+ return {
104
+ content: [
105
+ {
106
+ type: 'text' as const,
107
+ text: `Error fetching top-selling products: ${(error as Error).message}`,
108
+ },
109
+ ],
110
+ };
111
+ }
112
+ }
@@ -0,0 +1,50 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { Context } from '../../../context.js';
3
+ import { ordersListHandler, OrdersListSchema } from './ordersListHandler.js';
4
+ import { getSalesSummaryHandler, SalesSummarySchema } from './getSalesSummaryHandler.js';
5
+ import {
6
+ getMonthlySalesBreakdownHandler,
7
+ MonthlySalesBreakdownSchema,
8
+ } from './getMonthlySalesBreakdownHandler.js';
9
+ import { getTopCustomersHandler, GetTopCustomersSchema } from './getTopCustomersHandler.js';
10
+ import {
11
+ getTopSellingProductsHandler,
12
+ TopSellingProductsSchema,
13
+ } from './getTopSellingProductsHandler.js';
14
+
15
+ export const registerOrderTools = (server: McpServer, context: Context) => {
16
+ server.tool(
17
+ 'orders_list',
18
+ 'Get all orders, filtered and sorted. Supports pagination, status filters, provider types, and date range.',
19
+ OrdersListSchema,
20
+ async (params) => ordersListHandler(context, params),
21
+ );
22
+
23
+ server.tool(
24
+ 'ordersSales_summary',
25
+ 'Get aggregate sales analytics including total revenue, order count, average order value, and daily breakdown over a time range.',
26
+ SalesSummarySchema,
27
+ async (params) => getSalesSummaryHandler(context, params),
28
+ );
29
+
30
+ server.tool(
31
+ 'ordersSales_monthlyBreakdown',
32
+ 'Break down total sales by month. Supports filtering by status and provider IDs, with optional custom date range.',
33
+ MonthlySalesBreakdownSchema,
34
+ async (params) => getMonthlySalesBreakdownHandler(context, params),
35
+ );
36
+
37
+ server.tool(
38
+ 'orders_topCustomers',
39
+ 'Get top customers by total amount spent, with optional date and status filters.',
40
+ GetTopCustomersSchema,
41
+ async (params) => getTopCustomersHandler(context, params),
42
+ );
43
+
44
+ server.tool(
45
+ 'topSelling_products',
46
+ 'Get top-selling products by quantity and revenue within an optional date range.',
47
+ TopSellingProductsSchema,
48
+ (params) => getTopSellingProductsHandler(context, params),
49
+ );
50
+ };