qualmcp 0.1.1 → 0.1.3
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.
- package/package.json +1 -1
- package/src/tools.js +24 -9
package/package.json
CHANGED
package/src/tools.js
CHANGED
|
@@ -31,7 +31,7 @@ function safeLimit(v, max = 200) {
|
|
|
31
31
|
|
|
32
32
|
export const tools = {
|
|
33
33
|
analytics_salesRank: {
|
|
34
|
-
name: '
|
|
34
|
+
name: 'analytics_salesRank',
|
|
35
35
|
description: 'Ranking of products by sales quantity/revenue across orders with flexible filters and grouping. Always returns product names when possible.',
|
|
36
36
|
schema: z.object({
|
|
37
37
|
dateFrom: z.string().datetime().optional(),
|
|
@@ -169,7 +169,7 @@ export const tools = {
|
|
|
169
169
|
},
|
|
170
170
|
|
|
171
171
|
analytics_stockHealth: {
|
|
172
|
-
name: '
|
|
172
|
+
name: 'analytics_stockHealth',
|
|
173
173
|
description: 'Stock + sales velocity and days-cover metrics per product (optionally per store). Returns product names and signals; interpretation is left to the agent.',
|
|
174
174
|
schema: z.object({
|
|
175
175
|
storeId: z.number().optional(),
|
|
@@ -179,8 +179,8 @@ export const tools = {
|
|
|
179
179
|
orderScope: OrderScopeSchema,
|
|
180
180
|
includeZeroSales: z.boolean().default(true),
|
|
181
181
|
minSalesQty: z.number().default(0),
|
|
182
|
-
limit: z.number().
|
|
183
|
-
offset: z.number().
|
|
182
|
+
limit: z.number().default(200),
|
|
183
|
+
offset: z.number().default(0)
|
|
184
184
|
}),
|
|
185
185
|
handler: async (input) => {
|
|
186
186
|
const {
|
|
@@ -213,8 +213,8 @@ export const tools = {
|
|
|
213
213
|
clauses.push(`o.${field} <= $${params.length}`);
|
|
214
214
|
|
|
215
215
|
const where = clauses.length > 0 ? clauses.join(' AND ') : '1=1';
|
|
216
|
-
const lim = safeLimit(limit,
|
|
217
|
-
const off = Math.max(0, Math.floor(Number(offset
|
|
216
|
+
const lim = safeLimit(limit ?? 200, 2000);
|
|
217
|
+
const off = Math.max(0, Math.floor(Number(offset ?? 0)));
|
|
218
218
|
|
|
219
219
|
const sql = `
|
|
220
220
|
WITH stock AS (
|
|
@@ -288,7 +288,7 @@ export const tools = {
|
|
|
288
288
|
LIMIT 1
|
|
289
289
|
) pr ON TRUE
|
|
290
290
|
)
|
|
291
|
-
SELECT *
|
|
291
|
+
SELECT *, COUNT(*) OVER()::bigint AS total_count
|
|
292
292
|
FROM with_product
|
|
293
293
|
WHERE (sales_qty_window >= $${params.length + 1})
|
|
294
294
|
${includeZeroSales ? '' : 'AND sales_qty_window > 0'}
|
|
@@ -301,6 +301,15 @@ export const tools = {
|
|
|
301
301
|
|
|
302
302
|
const res = await query(sql, [...params, Number(minSalesQty || 0)]);
|
|
303
303
|
|
|
304
|
+
const rows = (res.rows || []).map((r) => {
|
|
305
|
+
if (!r || typeof r !== 'object') return r;
|
|
306
|
+
const { total_count, ...rest } = r;
|
|
307
|
+
return rest;
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
const total = res.rows && res.rows.length > 0 ? Number(res.rows[0].total_count || 0) : 0;
|
|
311
|
+
const hasMore = off + rows.length < total;
|
|
312
|
+
|
|
304
313
|
return {
|
|
305
314
|
window: {
|
|
306
315
|
from: from.toISOString(),
|
|
@@ -308,13 +317,19 @@ export const tools = {
|
|
|
308
317
|
days: windowDays,
|
|
309
318
|
dateField: field
|
|
310
319
|
},
|
|
311
|
-
|
|
320
|
+
meta: {
|
|
321
|
+
limit: lim,
|
|
322
|
+
offset: off,
|
|
323
|
+
total,
|
|
324
|
+
hasMore
|
|
325
|
+
},
|
|
326
|
+
rows
|
|
312
327
|
};
|
|
313
328
|
}
|
|
314
329
|
},
|
|
315
330
|
|
|
316
331
|
analytics_salesTimeSeries: {
|
|
317
|
-
name: '
|
|
332
|
+
name: 'analytics_salesTimeSeries',
|
|
318
333
|
description: 'Time series aggregation of orders and items sold over time buckets (day/week/month) with flexible orderScope.',
|
|
319
334
|
schema: z.object({
|
|
320
335
|
dateFrom: z.string().datetime().optional(),
|