@yoryoboy/bi-mcp 1.7.0 → 1.9.0

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 (31) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/index.js +27 -5
  3. package/dist/index.js.map +2 -2
  4. package/dist/mcp-use.json +6 -3
  5. package/dist/src/services/mercadolibre/mercadolibre-billing.js +77 -0
  6. package/dist/src/services/mercadolibre/mercadolibre-billing.js.map +7 -0
  7. package/dist/src/services/mercadolibre/mercadolibre-items.js +29 -2
  8. package/dist/src/services/mercadolibre/mercadolibre-items.js.map +2 -2
  9. package/dist/src/services/mercadolibre/mercadolibre-orders.js +18 -0
  10. package/dist/src/services/mercadolibre/mercadolibre-orders.js.map +2 -2
  11. package/dist/src/tools/mercadolibre/estimate-listing-fee.js +26 -34
  12. package/dist/src/tools/mercadolibre/estimate-listing-fee.js.map +2 -2
  13. package/dist/src/tools/mercadolibre/estimate-product-profitability.js +546 -0
  14. package/dist/src/tools/mercadolibre/estimate-product-profitability.js.map +7 -0
  15. package/dist/src/tools/mercadolibre/get-item-details.js +3 -25
  16. package/dist/src/tools/mercadolibre/get-item-details.js.map +2 -2
  17. package/dist/src/tools/mercadolibre/get-sales-by-item.js +129 -14
  18. package/dist/src/tools/mercadolibre/get-sales-by-item.js.map +2 -2
  19. package/dist/src/tools/mercadolibre/get-sales-trend.js +137 -14
  20. package/dist/src/tools/mercadolibre/get-sales-trend.js.map +2 -2
  21. package/dist/src/tools/mercadolibre/get-shipping-summary.js +450 -57
  22. package/dist/src/tools/mercadolibre/get-shipping-summary.js.map +2 -2
  23. package/dist/src/tools/mercadolibre/get-site-categories-and-listing-types.js +50 -0
  24. package/dist/src/tools/mercadolibre/get-site-categories-and-listing-types.js.map +7 -0
  25. package/dist/src/tools/mercadolibre/index.js +2 -0
  26. package/dist/src/tools/mercadolibre/index.js.map +2 -2
  27. package/dist/src/tools/mercadolibre/listing-fee-helpers.js +265 -0
  28. package/dist/src/tools/mercadolibre/listing-fee-helpers.js.map +7 -0
  29. package/dist/src/tools/mercadolibre/search-items.js +207 -52
  30. package/dist/src/tools/mercadolibre/search-items.js.map +2 -2
  31. package/package.json +1 -1
@@ -6,38 +6,16 @@ import {
6
6
  getMercadoLibreItemDetailsWithFallbackBatch
7
7
  } from "../../services/mercadolibre/mercadolibre-items.js";
8
8
  import { stripNulls } from "../../utils/strip-payload.js";
9
- import { asArray, asRecord, normalizeString, toNumber } from "./helpers.js";
10
9
  import { resolveMercadoLibreProfileOrSelection } from "./profile-resolution.js";
11
10
  import { mercadolibreProfileIdSchemaField } from "./write-helpers.js";
12
11
  const MAX_ITEM_IDS = 50;
13
12
  function formatItem(item) {
14
- return stripNulls({
15
- item_id: normalizeString(item.id),
16
- title: normalizeString(item.title),
17
- status: normalizeString(item.status),
18
- sub_status: Array.isArray(item.sub_status) ? item.sub_status : void 0,
19
- category_id: normalizeString(item.category_id),
20
- listing_type_id: normalizeString(item.listing_type_id),
21
- price: toNumber(item.price),
22
- currency_id: normalizeString(item.currency_id, "UNKNOWN"),
23
- available_quantity: toNumber(item.available_quantity),
24
- sold_quantity: toNumber(item.sold_quantity),
25
- condition: normalizeString(item.condition),
26
- permalink: normalizeString(item.permalink),
27
- pictures: asArray(item.pictures).map((picture) => ({
28
- id: normalizeString(picture.id),
29
- url: normalizeString(picture.url ?? picture.secure_url)
30
- })),
31
- shipping: stripNulls({
32
- mode: normalizeString(asRecord(item.shipping).mode),
33
- logistic_type: normalizeString(asRecord(item.shipping).logistic_type),
34
- free_shipping: asRecord(item.shipping).free_shipping
35
- })
36
- });
13
+ const { pictures: _pictures, ...rest } = item;
14
+ return stripNulls(rest);
37
15
  }
38
16
  const meliGetItemDetailsSchema = z.object({
39
17
  profileId: mercadolibreProfileIdSchemaField,
40
- itemIds: z.array(z.string().trim().min(1).describe("MercadoLibre item id.")).min(1).max(MAX_ITEM_IDS).describe("One or more MercadoLibre item ids (up to 50).")
18
+ itemIds: z.array(z.string().trim().min(1).describe("MercadoLibre item id.")).min(1).max(MAX_ITEM_IDS).describe("One or more MercadoLibre item ids (up to 50). Returns rich item details close to the MercadoLibre /items payload, excluding pictures.")
41
19
  });
42
20
  async function meliGetItemDetailsHandler(params) {
43
21
  try {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/mercadolibre/get-item-details.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { formatMercadoLibreError } from \"../../services/mercadolibre/mercadolibre-api.js\";\nimport {\n getMercadoLibreItemDetailsBatch,\n getMercadoLibreItemDetailsWithFallbackBatch,\n} from \"../../services/mercadolibre/mercadolibre-items.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\nimport { asArray, asRecord, normalizeString, toNumber } from \"./helpers.js\";\nimport { resolveMercadoLibreProfileOrSelection } from \"./profile-resolution.js\";\nimport { mercadolibreProfileIdSchemaField } from \"./write-helpers.js\";\n\nconst MAX_ITEM_IDS = 50;\n\nfunction formatItem(item: Record<string, unknown>) {\n return stripNulls({\n item_id: normalizeString(item.id),\n title: normalizeString(item.title),\n status: normalizeString(item.status),\n sub_status: Array.isArray(item.sub_status) ? item.sub_status : undefined,\n category_id: normalizeString(item.category_id),\n listing_type_id: normalizeString(item.listing_type_id),\n price: toNumber(item.price),\n currency_id: normalizeString(item.currency_id, \"UNKNOWN\"),\n available_quantity: toNumber(item.available_quantity),\n sold_quantity: toNumber(item.sold_quantity),\n condition: normalizeString(item.condition),\n permalink: normalizeString(item.permalink),\n pictures: asArray<Record<string, unknown>>(item.pictures).map((picture) => ({\n id: normalizeString(picture.id),\n url: normalizeString(picture.url ?? picture.secure_url),\n })),\n shipping: stripNulls({\n mode: normalizeString(asRecord(item.shipping).mode),\n logistic_type: normalizeString(asRecord(item.shipping).logistic_type),\n free_shipping: asRecord(item.shipping).free_shipping,\n }),\n });\n}\n\nexport const meliGetItemDetailsSchema = z.object({\n profileId: mercadolibreProfileIdSchemaField,\n itemIds: z\n .array(z.string().trim().min(1).describe(\"MercadoLibre item id.\"))\n .min(1)\n .max(MAX_ITEM_IDS)\n .describe(\"One or more MercadoLibre item ids (up to 50).\"),\n});\n\nexport async function meliGetItemDetailsHandler(params: z.infer<typeof meliGetItemDetailsSchema>) {\n try {\n const profileResolution = await resolveMercadoLibreProfileOrSelection(params.profileId);\n if (!profileResolution.ok) {\n return profileResolution.response;\n }\n\n const profileId = profileResolution.value.profileId;\n const uniqueItemIds = Array.from(new Set(params.itemIds));\n\n const multigetItems = await getMercadoLibreItemDetailsBatch(profileId, uniqueItemIds);\n if (multigetItems.length === uniqueItemIds.length) {\n return object(\n stripNulls({\n metadata: {\n profile_id: profileId,\n requested: uniqueItemIds.length,\n successful: multigetItems.length,\n failed: 0,\n source: \"items_multiget\",\n },\n items: multigetItems.map(formatItem),\n })\n );\n }\n\n const batch = await getMercadoLibreItemDetailsWithFallbackBatch(profileId, uniqueItemIds, {\n maxConcurrency: 10,\n maxRetries: 2,\n });\n\n return object(\n stripNulls({\n metadata: {\n profile_id: profileId,\n requested: uniqueItemIds.length,\n successful: batch.successful.length,\n failed: batch.failed.length,\n has_failures: batch.failed.length > 0,\n source: \"item_by_item_fallback\",\n },\n items: batch.successful.map((entry) => formatItem(entry.document)),\n failures: batch.failed.map((failure) => ({\n item_id: failure.id,\n message: failure.message,\n status_code: failure.statusCode,\n attempts: failure.attempts,\n retryable: failure.retryable,\n })),\n })\n );\n } catch (err) {\n return error(formatMercadoLibreError(err, \"Failed to fetch MercadoLibre item details\"));\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { formatMercadoLibreError } from \"../../services/mercadolibre/mercadolibre-api.js\";\nimport {\n getMercadoLibreItemDetailsBatch,\n getMercadoLibreItemDetailsWithFallbackBatch,\n} from \"../../services/mercadolibre/mercadolibre-items.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\nimport { resolveMercadoLibreProfileOrSelection } from \"./profile-resolution.js\";\nimport { mercadolibreProfileIdSchemaField } from \"./write-helpers.js\";\n\nconst MAX_ITEM_IDS = 50;\n\nfunction formatItem(item: Record<string, unknown>) {\n const { pictures: _pictures, ...rest } = item;\n return stripNulls(rest);\n}\n\nexport const meliGetItemDetailsSchema = z.object({\n profileId: mercadolibreProfileIdSchemaField,\n itemIds: z\n .array(z.string().trim().min(1).describe(\"MercadoLibre item id.\"))\n .min(1)\n .max(MAX_ITEM_IDS)\n .describe(\"One or more MercadoLibre item ids (up to 50). Returns rich item details close to the MercadoLibre /items payload, excluding pictures.\"),\n});\n\nexport async function meliGetItemDetailsHandler(params: z.infer<typeof meliGetItemDetailsSchema>) {\n try {\n const profileResolution = await resolveMercadoLibreProfileOrSelection(params.profileId);\n if (!profileResolution.ok) {\n return profileResolution.response;\n }\n\n const profileId = profileResolution.value.profileId;\n const uniqueItemIds = Array.from(new Set(params.itemIds));\n\n const multigetItems = await getMercadoLibreItemDetailsBatch(profileId, uniqueItemIds);\n if (multigetItems.length === uniqueItemIds.length) {\n return object(\n stripNulls({\n metadata: {\n profile_id: profileId,\n requested: uniqueItemIds.length,\n successful: multigetItems.length,\n failed: 0,\n source: \"items_multiget\",\n },\n items: multigetItems.map(formatItem),\n })\n );\n }\n\n const batch = await getMercadoLibreItemDetailsWithFallbackBatch(profileId, uniqueItemIds, {\n maxConcurrency: 10,\n maxRetries: 2,\n });\n\n return object(\n stripNulls({\n metadata: {\n profile_id: profileId,\n requested: uniqueItemIds.length,\n successful: batch.successful.length,\n failed: batch.failed.length,\n has_failures: batch.failed.length > 0,\n source: \"item_by_item_fallback\",\n },\n items: batch.successful.map((entry) => formatItem(entry.document)),\n failures: batch.failed.map((failure) => ({\n item_id: failure.id,\n message: failure.message,\n status_code: failure.statusCode,\n attempts: failure.attempts,\n retryable: failure.retryable,\n })),\n })\n );\n } catch (err) {\n return error(formatMercadoLibreError(err, \"Failed to fetch MercadoLibre item details\"));\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB,SAAS,+BAA+B;AACxC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B,SAAS,6CAA6C;AACtD,SAAS,wCAAwC;AAEjD,MAAM,eAAe;AAErB,SAAS,WAAW,MAA+B;AACjD,QAAM,EAAE,UAAU,WAAW,GAAG,KAAK,IAAI;AACzC,SAAO,WAAW,IAAI;AACxB;AAEO,MAAM,2BAA2B,EAAE,OAAO;AAAA,EAC/C,WAAW;AAAA,EACX,SAAS,EACN,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,SAAS,uBAAuB,CAAC,EAChE,IAAI,CAAC,EACL,IAAI,YAAY,EAChB,SAAS,uIAAuI;AACrJ,CAAC;AAED,eAAsB,0BAA0B,QAAkD;AAChG,MAAI;AACF,UAAM,oBAAoB,MAAM,sCAAsC,OAAO,SAAS;AACtF,QAAI,CAAC,kBAAkB,IAAI;AACzB,aAAO,kBAAkB;AAAA,IAC3B;AAEA,UAAM,YAAY,kBAAkB,MAAM;AAC1C,UAAM,gBAAgB,MAAM,KAAK,IAAI,IAAI,OAAO,OAAO,CAAC;AAExD,UAAM,gBAAgB,MAAM,gCAAgC,WAAW,aAAa;AACpF,QAAI,cAAc,WAAW,cAAc,QAAQ;AACjD,aAAO;AAAA,QACL,WAAW;AAAA,UACT,UAAU;AAAA,YACR,YAAY;AAAA,YACZ,WAAW,cAAc;AAAA,YACzB,YAAY,cAAc;AAAA,YAC1B,QAAQ;AAAA,YACR,QAAQ;AAAA,UACV;AAAA,UACA,OAAO,cAAc,IAAI,UAAU;AAAA,QACrC,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,QAAQ,MAAM,4CAA4C,WAAW,eAAe;AAAA,MACxF,gBAAgB;AAAA,MAChB,YAAY;AAAA,IACd,CAAC;AAED,WAAO;AAAA,MACL,WAAW;AAAA,QACT,UAAU;AAAA,UACR,YAAY;AAAA,UACZ,WAAW,cAAc;AAAA,UACzB,YAAY,MAAM,WAAW;AAAA,UAC7B,QAAQ,MAAM,OAAO;AAAA,UACrB,cAAc,MAAM,OAAO,SAAS;AAAA,UACpC,QAAQ;AAAA,QACV;AAAA,QACA,OAAO,MAAM,WAAW,IAAI,CAAC,UAAU,WAAW,MAAM,QAAQ,CAAC;AAAA,QACjE,UAAU,MAAM,OAAO,IAAI,CAAC,aAAa;AAAA,UACvC,SAAS,QAAQ;AAAA,UACjB,SAAS,QAAQ;AAAA,UACjB,aAAa,QAAQ;AAAA,UACrB,UAAU,QAAQ;AAAA,UAClB,WAAW,QAAQ;AAAA,QACrB,EAAE;AAAA,MACJ,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,wBAAwB,KAAK,2CAA2C,CAAC;AAAA,EACxF;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,13 @@
1
1
  import { error, object } from "mcp-use/server";
2
2
  import { z } from "zod";
3
- import { formatMercadoLibreError } from "../../services/mercadolibre/mercadolibre-api.js";
4
- import { searchMercadoLibreOrders } from "../../services/mercadolibre/mercadolibre-orders.js";
3
+ import {
4
+ formatMercadoLibreError,
5
+ normalizeMercadoLibrePaging
6
+ } from "../../services/mercadolibre/mercadolibre-api.js";
7
+ import {
8
+ searchMercadoLibreOrders,
9
+ searchMercadoLibreOrdersBatch
10
+ } from "../../services/mercadolibre/mercadolibre-orders.js";
5
11
  import { stripNulls } from "../../utils/strip-payload.js";
6
12
  import {
7
13
  asArray,
@@ -15,6 +21,17 @@ import {
15
21
  } from "./helpers.js";
16
22
  import { resolveMercadoLibreProfileOrSelection } from "./profile-resolution.js";
17
23
  import { mercadolibreProfileIdSchemaField } from "./write-helpers.js";
24
+ const PAGE_FETCH_CONCURRENCY = 15;
25
+ const PAGE_FETCH_MAX_RETRIES = 2;
26
+ const salesByItemSchema = [
27
+ "item_id",
28
+ "title",
29
+ "variation_id",
30
+ "orders",
31
+ "units",
32
+ "revenue",
33
+ "last_order_date"
34
+ ];
18
35
  const meliGetSalesByItemSchema = z.object({
19
36
  profileId: mercadolibreProfileIdSchemaField,
20
37
  startDate: z.string().regex(mercadoLibreDateRegex).describe("Start date in YYYY-MM-DD format."),
@@ -23,6 +40,33 @@ const meliGetSalesByItemSchema = z.object({
23
40
  limit: z.number().int().min(1).max(50).optional().describe("Orders page size to aggregate."),
24
41
  offset: z.number().int().min(0).max(5e3).optional().describe("Orders offset to aggregate.")
25
42
  });
43
+ function buildSalesByItemMetadata(params) {
44
+ const hasFailures = params.pagesFailed > 0;
45
+ const universeFullyFetched = !hasFailures && params.ordersAggregated >= params.total;
46
+ const nextOffset = params.effectiveOffset + params.ordersAggregated;
47
+ const hasMore = hasFailures || nextOffset < params.total;
48
+ let continuation = "Agregado completo. No more pages for this query.";
49
+ if (hasFailures) {
50
+ const offsets = params.failedOffsets.slice(0, 5).join(", ");
51
+ const moreOffsets = params.failedOffsets.length > 5 ? ` y ${params.failedOffsets.length - 5} offsets adicionales` : "";
52
+ continuation = `Agregado parcial por paginas fallidas. Reintentar la misma tool con los mismos filtros para recomputar el total. Offsets fallidos: ${offsets}${moreOffsets}.`;
53
+ }
54
+ return {
55
+ total: params.total,
56
+ limit: params.effectiveLimit,
57
+ offset: params.effectiveOffset,
58
+ returned: params.ordersAggregated,
59
+ orders_aggregated: params.ordersAggregated,
60
+ has_more: hasMore,
61
+ next_offset: !hasFailures && nextOffset < params.total ? nextOffset : void 0,
62
+ continuation,
63
+ fetch_mode: "full_aggregate",
64
+ pages_requested: params.pagesRequested,
65
+ pages_succeeded: params.pagesSucceeded,
66
+ pages_failed: params.pagesFailed,
67
+ universe_fully_fetched: universeFullyFetched
68
+ };
69
+ }
26
70
  async function meliGetSalesByItemHandler(params) {
27
71
  try {
28
72
  const profileResolution = await resolveMercadoLibreProfileOrSelection(params.profileId);
@@ -30,17 +74,66 @@ async function meliGetSalesByItemHandler(params) {
30
74
  return profileResolution.response;
31
75
  }
32
76
  const profileId = profileResolution.value.profileId;
77
+ const requestedLimit = params.limit ?? 50;
78
+ const requestedOffset = params.offset ?? 0;
33
79
  const response = await searchMercadoLibreOrders(profileId, {
34
80
  seller: "",
35
81
  from: params.startDate,
36
82
  to: params.endDate,
37
83
  status: params.status,
38
- limit: params.limit ?? 50,
39
- offset: params.offset ?? 0
84
+ limit: requestedLimit,
85
+ offset: requestedOffset
40
86
  });
41
- const orders = asArray(response.results);
87
+ const paging = normalizeMercadoLibrePaging(asRecord(response.paging));
88
+ const baseOrders = asArray(response.results);
89
+ const effectiveLimit = paging.limit || requestedLimit || baseOrders.length;
90
+ const effectiveOffset = paging.offset || requestedOffset || 0;
91
+ const allOrders = [...baseOrders];
92
+ const failedPages = [];
93
+ let pagesRequested = 1;
94
+ let pagesSucceeded = 1;
95
+ if (effectiveLimit > 0 && paging.total > effectiveOffset + baseOrders.length) {
96
+ const nextOffsets = [];
97
+ for (let nextOffset = effectiveOffset + effectiveLimit; nextOffset < paging.total; nextOffset += effectiveLimit) {
98
+ nextOffsets.push(nextOffset);
99
+ }
100
+ pagesRequested += nextOffsets.length;
101
+ if (nextOffsets.length > 0) {
102
+ const batch = await searchMercadoLibreOrdersBatch(
103
+ profileId,
104
+ {
105
+ seller: "",
106
+ from: params.startDate,
107
+ to: params.endDate,
108
+ status: params.status,
109
+ limit: effectiveLimit
110
+ },
111
+ nextOffsets,
112
+ {
113
+ maxConcurrency: PAGE_FETCH_CONCURRENCY,
114
+ maxRetries: PAGE_FETCH_MAX_RETRIES
115
+ }
116
+ );
117
+ pagesSucceeded += batch.successful.length;
118
+ batch.successful.forEach((entry) => {
119
+ allOrders.push(...asArray(entry.document.results));
120
+ });
121
+ batch.failed.forEach((failure) => {
122
+ const failedOffset = Number(failure.id);
123
+ failedPages.push({
124
+ offset: failedOffset,
125
+ limit: effectiveLimit,
126
+ page_number: Math.floor(failedOffset / effectiveLimit) + 1,
127
+ message: failure.message,
128
+ status_code: failure.statusCode,
129
+ attempts: failure.attempts,
130
+ retryable: failure.retryable
131
+ });
132
+ });
133
+ }
134
+ }
42
135
  const salesByCurrency = {};
43
- for (const order of orders) {
136
+ for (const order of allOrders) {
44
137
  const currencyId = normalizeString(order.currency_id, "UNKNOWN");
45
138
  const orderDate = compactDateTime(order.date_created);
46
139
  for (const orderItem of asArray(order.order_items)) {
@@ -71,19 +164,41 @@ async function meliGetSalesByItemHandler(params) {
71
164
  const result = Object.fromEntries(
72
165
  Object.entries(salesByCurrency).map(([currencyId, items]) => [
73
166
  currencyId,
74
- Object.values(items).map((item) => ({ ...item, revenue: roundMoney(item.revenue) })).sort((left, right) => right.revenue - left.revenue)
167
+ Object.values(items).map((item) => ({
168
+ ...item,
169
+ revenue: roundMoney(item.revenue)
170
+ })).sort((left, right) => right.revenue - left.revenue)
171
+ ])
172
+ );
173
+ const compactResult = Object.fromEntries(
174
+ Object.entries(result).map(([currencyId, items]) => [
175
+ currencyId,
176
+ items.map((item) => [
177
+ item.item_id,
178
+ item.title,
179
+ item.variation_id,
180
+ item.orders,
181
+ item.units,
182
+ item.revenue,
183
+ item.last_order_date ?? ""
184
+ ])
75
185
  ])
76
186
  );
77
187
  return object(
78
188
  stripNulls({
79
189
  profile_id: profileId,
80
- metadata: {
81
- orders_aggregated: orders.length,
82
- offset: params.offset ?? 0,
83
- limit: params.limit ?? 50,
84
- note: "If metadata.total is higher than orders_aggregated, reenviar la misma tool con un offset mayor para completar el agregado."
85
- },
86
- sales_by_currency: result
190
+ metadata: buildSalesByItemMetadata({
191
+ total: paging.total,
192
+ effectiveLimit,
193
+ effectiveOffset,
194
+ ordersAggregated: allOrders.length,
195
+ pagesRequested,
196
+ pagesSucceeded,
197
+ pagesFailed: failedPages.length,
198
+ failedOffsets: failedPages.map((failure) => failure.offset)
199
+ }),
200
+ sales_by_item_schema: salesByItemSchema,
201
+ sales_by_currency: compactResult
87
202
  })
88
203
  );
89
204
  } catch (err) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/mercadolibre/get-sales-by-item.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { formatMercadoLibreError } from \"../../services/mercadolibre/mercadolibre-api.js\";\nimport { searchMercadoLibreOrders } from \"../../services/mercadolibre/mercadolibre-orders.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\nimport {\n asArray,\n asRecord,\n compactDateTime,\n currencyBucket,\n mercadoLibreDateRegex,\n normalizeString,\n roundMoney,\n toNumber,\n} from \"./helpers.js\";\nimport { resolveMercadoLibreProfileOrSelection } from \"./profile-resolution.js\";\nimport { mercadolibreProfileIdSchemaField } from \"./write-helpers.js\";\n\nexport const meliGetSalesByItemSchema = z.object({\n profileId: mercadolibreProfileIdSchemaField,\n startDate: z.string().regex(mercadoLibreDateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(mercadoLibreDateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n status: z.string().trim().min(1).optional().describe(\"Optional order status filter.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Orders page size to aggregate.\"),\n offset: z.number().int().min(0).max(5000).optional().describe(\"Orders offset to aggregate.\"),\n});\n\nexport async function meliGetSalesByItemHandler(\n params: z.infer<typeof meliGetSalesByItemSchema>\n) {\n try {\n const profileResolution = await resolveMercadoLibreProfileOrSelection(params.profileId);\n if (!profileResolution.ok) {\n return profileResolution.response;\n }\n\n const profileId = profileResolution.value.profileId;\n const response = await searchMercadoLibreOrders(profileId, {\n seller: \"\",\n from: params.startDate,\n to: params.endDate,\n status: params.status,\n limit: params.limit ?? 50,\n offset: params.offset ?? 0,\n });\n\n const orders = asArray<Record<string, unknown>>(response.results);\n type SalesByItemRow = {\n item_id: string;\n title: string;\n variation_id: string;\n orders: number;\n units: number;\n revenue: number;\n last_order_date?: string | null;\n };\n const salesByCurrency: Record<string, Record<string, SalesByItemRow>> = {};\n\n for (const order of orders) {\n const currencyId = normalizeString(order.currency_id, \"UNKNOWN\");\n const orderDate = compactDateTime(order.date_created);\n for (const orderItem of asArray<Record<string, unknown>>(order.order_items)) {\n const item = asRecord(orderItem.item);\n const itemId = normalizeString(item.id);\n const variationId = normalizeString(item.variation_id);\n const itemKey = `${itemId}::${variationId}`;\n const bucket = currencyBucket<Record<string, SalesByItemRow>>(\n salesByCurrency,\n currencyId,\n () => ({})\n );\n bucket[itemKey] = bucket[itemKey] ?? {\n item_id: itemId,\n title: normalizeString(item.title),\n variation_id: variationId,\n orders: 0,\n units: 0,\n revenue: 0,\n last_order_date: orderDate,\n };\n bucket[itemKey].orders += 1;\n bucket[itemKey].units += toNumber(orderItem.quantity);\n bucket[itemKey].revenue += toNumber(orderItem.unit_price) * toNumber(orderItem.quantity);\n bucket[itemKey].last_order_date = orderDate ?? bucket[itemKey].last_order_date;\n }\n }\n\n const result = Object.fromEntries(\n Object.entries(salesByCurrency).map(([currencyId, items]) => [\n currencyId,\n Object.values(items)\n .map((item) => ({ ...item, revenue: roundMoney(item.revenue) }))\n .sort((left, right) => right.revenue - left.revenue),\n ])\n );\n\n return object(\n stripNulls({\n profile_id: profileId,\n metadata: {\n orders_aggregated: orders.length,\n offset: params.offset ?? 0,\n limit: params.limit ?? 50,\n note: \"If metadata.total is higher than orders_aggregated, reenviar la misma tool con un offset mayor para completar el agregado.\",\n },\n sales_by_currency: result,\n })\n );\n } catch (err) {\n return error(formatMercadoLibreError(err, \"Failed to aggregate MercadoLibre sales by item\"));\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n formatMercadoLibreError,\n normalizeMercadoLibrePaging,\n} from \"../../services/mercadolibre/mercadolibre-api.js\";\nimport {\n searchMercadoLibreOrders,\n searchMercadoLibreOrdersBatch,\n} from \"../../services/mercadolibre/mercadolibre-orders.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\nimport {\n asArray,\n asRecord,\n compactDateTime,\n currencyBucket,\n mercadoLibreDateRegex,\n normalizeString,\n roundMoney,\n toNumber,\n} from \"./helpers.js\";\nimport { resolveMercadoLibreProfileOrSelection } from \"./profile-resolution.js\";\nimport { mercadolibreProfileIdSchemaField } from \"./write-helpers.js\";\n\nconst PAGE_FETCH_CONCURRENCY = 15;\nconst PAGE_FETCH_MAX_RETRIES = 2;\nconst salesByItemSchema = [\n \"item_id\",\n \"title\",\n \"variation_id\",\n \"orders\",\n \"units\",\n \"revenue\",\n \"last_order_date\",\n] as const;\n\nexport const meliGetSalesByItemSchema = z.object({\n profileId: mercadolibreProfileIdSchemaField,\n startDate: z.string().regex(mercadoLibreDateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(mercadoLibreDateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n status: z.string().trim().min(1).optional().describe(\"Optional order status filter.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Orders page size to aggregate.\"),\n offset: z.number().int().min(0).max(5000).optional().describe(\"Orders offset to aggregate.\"),\n});\n\ntype FailedPage = {\n offset: number;\n limit: number;\n page_number: number;\n message: string;\n status_code?: number;\n attempts: number;\n retryable: boolean;\n};\n\nfunction buildSalesByItemMetadata(params: {\n total: number;\n effectiveLimit: number;\n effectiveOffset: number;\n ordersAggregated: number;\n pagesRequested: number;\n pagesSucceeded: number;\n pagesFailed: number;\n failedOffsets: number[];\n}) {\n const hasFailures = params.pagesFailed > 0;\n const universeFullyFetched = !hasFailures && params.ordersAggregated >= params.total;\n const nextOffset = params.effectiveOffset + params.ordersAggregated;\n const hasMore = hasFailures || nextOffset < params.total;\n\n let continuation = \"Agregado completo. No more pages for this query.\";\n if (hasFailures) {\n const offsets = params.failedOffsets.slice(0, 5).join(\", \");\n const moreOffsets =\n params.failedOffsets.length > 5\n ? ` y ${params.failedOffsets.length - 5} offsets adicionales`\n : \"\";\n continuation =\n `Agregado parcial por paginas fallidas. Reintentar la misma tool con los mismos filtros para recomputar el total. ` +\n `Offsets fallidos: ${offsets}${moreOffsets}.`;\n }\n\n return {\n total: params.total,\n limit: params.effectiveLimit,\n offset: params.effectiveOffset,\n returned: params.ordersAggregated,\n orders_aggregated: params.ordersAggregated,\n has_more: hasMore,\n next_offset: !hasFailures && nextOffset < params.total ? nextOffset : undefined,\n continuation,\n fetch_mode: \"full_aggregate\",\n pages_requested: params.pagesRequested,\n pages_succeeded: params.pagesSucceeded,\n pages_failed: params.pagesFailed,\n universe_fully_fetched: universeFullyFetched,\n };\n}\n\nexport async function meliGetSalesByItemHandler(\n params: z.infer<typeof meliGetSalesByItemSchema>\n) {\n try {\n const profileResolution = await resolveMercadoLibreProfileOrSelection(params.profileId);\n if (!profileResolution.ok) {\n return profileResolution.response;\n }\n\n const profileId = profileResolution.value.profileId;\n const requestedLimit = params.limit ?? 50;\n const requestedOffset = params.offset ?? 0;\n const response = await searchMercadoLibreOrders(profileId, {\n seller: \"\",\n from: params.startDate,\n to: params.endDate,\n status: params.status,\n limit: requestedLimit,\n offset: requestedOffset,\n });\n\n const paging = normalizeMercadoLibrePaging(asRecord(response.paging));\n const baseOrders = asArray<Record<string, unknown>>(response.results);\n const effectiveLimit = paging.limit || requestedLimit || baseOrders.length;\n const effectiveOffset = paging.offset || requestedOffset || 0;\n const allOrders = [...baseOrders];\n const failedPages: FailedPage[] = [];\n let pagesRequested = 1;\n let pagesSucceeded = 1;\n\n if (effectiveLimit > 0 && paging.total > effectiveOffset + baseOrders.length) {\n const nextOffsets: number[] = [];\n for (\n let nextOffset = effectiveOffset + effectiveLimit;\n nextOffset < paging.total;\n nextOffset += effectiveLimit\n ) {\n nextOffsets.push(nextOffset);\n }\n\n pagesRequested += nextOffsets.length;\n\n if (nextOffsets.length > 0) {\n const batch = await searchMercadoLibreOrdersBatch(\n profileId,\n {\n seller: \"\",\n from: params.startDate,\n to: params.endDate,\n status: params.status,\n limit: effectiveLimit,\n },\n nextOffsets,\n {\n maxConcurrency: PAGE_FETCH_CONCURRENCY,\n maxRetries: PAGE_FETCH_MAX_RETRIES,\n }\n );\n\n pagesSucceeded += batch.successful.length;\n batch.successful.forEach((entry) => {\n allOrders.push(...asArray<Record<string, unknown>>(entry.document.results));\n });\n batch.failed.forEach((failure) => {\n const failedOffset = Number(failure.id);\n failedPages.push({\n offset: failedOffset,\n limit: effectiveLimit,\n page_number: Math.floor(failedOffset / effectiveLimit) + 1,\n message: failure.message,\n status_code: failure.statusCode,\n attempts: failure.attempts,\n retryable: failure.retryable,\n });\n });\n }\n }\n\n type SalesByItemRow = {\n item_id: string;\n title: string;\n variation_id: string;\n orders: number;\n units: number;\n revenue: number;\n last_order_date?: string | null;\n };\n const salesByCurrency: Record<string, Record<string, SalesByItemRow>> = {};\n\n for (const order of allOrders) {\n const currencyId = normalizeString(order.currency_id, \"UNKNOWN\");\n const orderDate = compactDateTime(order.date_created);\n for (const orderItem of asArray<Record<string, unknown>>(order.order_items)) {\n const item = asRecord(orderItem.item);\n const itemId = normalizeString(item.id);\n const variationId = normalizeString(item.variation_id);\n const itemKey = `${itemId}::${variationId}`;\n const bucket = currencyBucket<Record<string, SalesByItemRow>>(\n salesByCurrency,\n currencyId,\n () => ({})\n );\n bucket[itemKey] = bucket[itemKey] ?? {\n item_id: itemId,\n title: normalizeString(item.title),\n variation_id: variationId,\n orders: 0,\n units: 0,\n revenue: 0,\n last_order_date: orderDate,\n };\n bucket[itemKey].orders += 1;\n bucket[itemKey].units += toNumber(orderItem.quantity);\n bucket[itemKey].revenue += toNumber(orderItem.unit_price) * toNumber(orderItem.quantity);\n bucket[itemKey].last_order_date = orderDate ?? bucket[itemKey].last_order_date;\n }\n }\n\n const result = Object.fromEntries(\n Object.entries(salesByCurrency).map(([currencyId, items]) => [\n currencyId,\n Object.values(items)\n .map((item) => ({\n ...item,\n revenue: roundMoney(item.revenue),\n }))\n .sort((left, right) => right.revenue - left.revenue),\n ])\n );\n\n const compactResult = Object.fromEntries(\n Object.entries(result).map(([currencyId, items]) => [\n currencyId,\n items.map((item) => [\n item.item_id,\n item.title,\n item.variation_id,\n item.orders,\n item.units,\n item.revenue,\n item.last_order_date ?? \"\",\n ]),\n ])\n );\n\n return object(\n stripNulls({\n profile_id: profileId,\n metadata: buildSalesByItemMetadata({\n total: paging.total,\n effectiveLimit,\n effectiveOffset,\n ordersAggregated: allOrders.length,\n pagesRequested,\n pagesSucceeded,\n pagesFailed: failedPages.length,\n failedOffsets: failedPages.map((failure) => failure.offset),\n }),\n sales_by_item_schema: salesByItemSchema,\n sales_by_currency: compactResult,\n })\n );\n } catch (err) {\n return error(formatMercadoLibreError(err, \"Failed to aggregate MercadoLibre sales by item\"));\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,25 +1,91 @@
1
1
  import { error, object } from "mcp-use/server";
2
2
  import { z } from "zod";
3
- import { formatMercadoLibreError } from "../../services/mercadolibre/mercadolibre-api.js";
4
- import { searchMercadoLibreOrders } from "../../services/mercadolibre/mercadolibre-orders.js";
3
+ import {
4
+ formatMercadoLibreError,
5
+ normalizeMercadoLibrePaging
6
+ } from "../../services/mercadolibre/mercadolibre-api.js";
7
+ import {
8
+ searchMercadoLibreOrders,
9
+ searchMercadoLibreOrdersBatch
10
+ } from "../../services/mercadolibre/mercadolibre-orders.js";
5
11
  import { stripNulls } from "../../utils/strip-payload.js";
6
- import { asArray, mercadoLibreDateRegex, normalizeString, roundMoney, toNumber } from "./helpers.js";
12
+ import {
13
+ asArray,
14
+ asRecord,
15
+ mercadoLibreDateRegex,
16
+ normalizeString,
17
+ roundMoney,
18
+ toNumber
19
+ } from "./helpers.js";
7
20
  import { resolveMercadoLibreProfileOrSelection } from "./profile-resolution.js";
8
21
  import { mercadolibreProfileIdSchemaField } from "./write-helpers.js";
22
+ const PAGE_FETCH_CONCURRENCY = 15;
23
+ const PAGE_FETCH_MAX_RETRIES = 2;
24
+ const DEFAULT_PAGE_LIMIT = 50;
25
+ const BUSINESS_TIME_ZONE = "America/Argentina/Buenos_Aires";
26
+ function formatDateInTimeZone(dateValue, timeZone) {
27
+ const formatter = new Intl.DateTimeFormat("en-CA", {
28
+ timeZone,
29
+ year: "numeric",
30
+ month: "2-digit",
31
+ day: "2-digit"
32
+ });
33
+ const parts = formatter.formatToParts(dateValue);
34
+ const year = parts.find((part) => part.type === "year")?.value;
35
+ const month = parts.find((part) => part.type === "month")?.value;
36
+ const day = parts.find((part) => part.type === "day")?.value;
37
+ if (!year || !month || !day) {
38
+ return "";
39
+ }
40
+ return `${year}-${month}-${day}`;
41
+ }
42
+ function getLocalDateBucket(createdAt, timeZone) {
43
+ if (!createdAt) {
44
+ return "";
45
+ }
46
+ const date = new Date(createdAt);
47
+ if (Number.isNaN(date.getTime())) {
48
+ return "";
49
+ }
50
+ return formatDateInTimeZone(date, timeZone);
51
+ }
9
52
  function getWeekBucket(dateValue) {
10
53
  const date = /* @__PURE__ */ new Date(`${dateValue}T00:00:00.000Z`);
11
54
  const day = date.getUTCDay() || 7;
12
55
  date.setUTCDate(date.getUTCDate() - day + 1);
13
56
  return date.toISOString().slice(0, 10);
14
57
  }
58
+ function buildSalesTrendMetadata(params) {
59
+ const hasFailures = params.pagesFailed > 0;
60
+ const universeFullyFetched = !hasFailures && params.ordersAggregated >= params.total;
61
+ let continuation = "Agregado completo. No more pages for this query.";
62
+ if (hasFailures) {
63
+ const offsets = params.failedOffsets.slice(0, 5).join(", ");
64
+ const moreOffsets = params.failedOffsets.length > 5 ? ` y ${params.failedOffsets.length - 5} offsets adicionales` : "";
65
+ continuation = `Agregado parcial por paginas fallidas. Reintentar la misma tool con los mismos filtros para recomputar el trend completo. Offsets fallidos: ${offsets}${moreOffsets}.`;
66
+ }
67
+ return {
68
+ total_matching_orders: params.total,
69
+ limit: params.effectiveLimit,
70
+ offset: params.effectiveOffset,
71
+ returned: params.ordersAggregated,
72
+ orders_aggregated: params.ordersAggregated,
73
+ continuation,
74
+ fetch_mode: "full_aggregate",
75
+ pages_requested: params.pagesRequested,
76
+ pages_succeeded: params.pagesSucceeded,
77
+ pages_failed: params.pagesFailed,
78
+ universe_fully_fetched: universeFullyFetched
79
+ };
80
+ }
15
81
  const meliGetSalesTrendSchema = z.object({
16
82
  profileId: mercadolibreProfileIdSchemaField,
17
83
  startDate: z.string().regex(mercadoLibreDateRegex).describe("Start date in YYYY-MM-DD format."),
18
84
  endDate: z.string().regex(mercadoLibreDateRegex).describe("End date in YYYY-MM-DD format."),
19
85
  granularity: z.enum(["day", "week"]).optional().describe("Trend granularity. Defaults to day."),
20
86
  status: z.string().trim().min(1).optional().describe("Optional order status filter."),
21
- limit: z.number().int().min(1).max(50).optional().describe("Orders page size to aggregate."),
22
- offset: z.number().int().min(0).max(5e3).optional().describe("Orders offset to aggregate.")
87
+ limit: z.number().int().min(1).max(50).optional().describe("Optional MercadoLibre page size used internally while aggregating the full trend. Defaults to 50."),
88
+ offset: z.number().int().min(0).max(5e3).optional().describe("Optional starting offset used internally before fetching the remaining pages needed to aggregate the full trend. Defaults to 0.")
23
89
  });
24
90
  async function meliGetSalesTrendHandler(params) {
25
91
  try {
@@ -29,18 +95,68 @@ async function meliGetSalesTrendHandler(params) {
29
95
  }
30
96
  const profileId = profileResolution.value.profileId;
31
97
  const granularity = params.granularity ?? "day";
98
+ const requestedLimit = params.limit ?? DEFAULT_PAGE_LIMIT;
99
+ const requestedOffset = params.offset ?? 0;
32
100
  const response = await searchMercadoLibreOrders(profileId, {
33
101
  seller: "",
34
102
  from: params.startDate,
35
103
  to: params.endDate,
36
104
  status: params.status,
37
- limit: params.limit ?? 50,
38
- offset: params.offset ?? 0
105
+ limit: requestedLimit,
106
+ offset: requestedOffset
39
107
  });
108
+ const paging = normalizeMercadoLibrePaging(asRecord(response.paging));
109
+ const baseOrders = asArray(response.results);
110
+ const effectiveLimit = paging.limit || requestedLimit || baseOrders.length;
111
+ const effectiveOffset = paging.offset || requestedOffset || 0;
112
+ const allOrders = [...baseOrders];
113
+ const failedPages = [];
114
+ let pagesRequested = 1;
115
+ let pagesSucceeded = 1;
116
+ if (effectiveLimit > 0 && paging.total > effectiveOffset + baseOrders.length) {
117
+ const nextOffsets = [];
118
+ for (let nextOffset = effectiveOffset + effectiveLimit; nextOffset < paging.total; nextOffset += effectiveLimit) {
119
+ nextOffsets.push(nextOffset);
120
+ }
121
+ pagesRequested += nextOffsets.length;
122
+ if (nextOffsets.length > 0) {
123
+ const batch = await searchMercadoLibreOrdersBatch(
124
+ profileId,
125
+ {
126
+ seller: "",
127
+ from: params.startDate,
128
+ to: params.endDate,
129
+ status: params.status,
130
+ limit: effectiveLimit
131
+ },
132
+ nextOffsets,
133
+ {
134
+ maxConcurrency: PAGE_FETCH_CONCURRENCY,
135
+ maxRetries: PAGE_FETCH_MAX_RETRIES
136
+ }
137
+ );
138
+ pagesSucceeded += batch.successful.length;
139
+ batch.successful.forEach((entry) => {
140
+ allOrders.push(...asArray(entry.document.results));
141
+ });
142
+ batch.failed.forEach((failure) => {
143
+ const failedOffset = Number(failure.id);
144
+ failedPages.push({
145
+ offset: failedOffset,
146
+ limit: effectiveLimit,
147
+ page_number: Math.floor(failedOffset / effectiveLimit) + 1,
148
+ message: failure.message,
149
+ status_code: failure.statusCode,
150
+ attempts: failure.attempts,
151
+ retryable: failure.retryable
152
+ });
153
+ });
154
+ }
155
+ }
40
156
  const buckets = {};
41
- for (const order of asArray(response.results)) {
157
+ for (const order of allOrders) {
42
158
  const createdAt = normalizeString(order.date_created);
43
- const dateBucket = createdAt.slice(0, 10);
159
+ const dateBucket = getLocalDateBucket(createdAt, BUSINESS_TIME_ZONE);
44
160
  if (!dateBucket) {
45
161
  continue;
46
162
  }
@@ -55,6 +171,16 @@ async function meliGetSalesTrendHandler(params) {
55
171
  buckets[currencyId][bucketKey].orders += 1;
56
172
  buckets[currencyId][bucketKey].revenue += toNumber(order.total_amount);
57
173
  }
174
+ const metadata = buildSalesTrendMetadata({
175
+ total: paging.total,
176
+ effectiveLimit,
177
+ effectiveOffset,
178
+ ordersAggregated: allOrders.length,
179
+ pagesRequested,
180
+ pagesSucceeded,
181
+ pagesFailed: failedPages.length,
182
+ failedOffsets: failedPages.map((failure) => failure.offset)
183
+ });
58
184
  return object(
59
185
  stripNulls({
60
186
  profile_id: profileId,
@@ -65,11 +191,8 @@ async function meliGetSalesTrendHandler(params) {
65
191
  Object.values(items).map((item) => ({ ...item, revenue: roundMoney(item.revenue) })).sort((left, right) => left.bucket.localeCompare(right.bucket))
66
192
  ])
67
193
  ),
68
- metadata: {
69
- orders_aggregated: asArray(response.results).length,
70
- limit: params.limit ?? 50,
71
- offset: params.offset ?? 0
72
- }
194
+ metadata,
195
+ failed_pages: failedPages.length > 0 ? failedPages : void 0
73
196
  })
74
197
  );
75
198
  } catch (err) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/tools/mercadolibre/get-sales-trend.ts"],
4
- "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport { formatMercadoLibreError } from \"../../services/mercadolibre/mercadolibre-api.js\";\nimport { searchMercadoLibreOrders } from \"../../services/mercadolibre/mercadolibre-orders.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\nimport { asArray, mercadoLibreDateRegex, normalizeString, roundMoney, toNumber } from \"./helpers.js\";\nimport { resolveMercadoLibreProfileOrSelection } from \"./profile-resolution.js\";\nimport { mercadolibreProfileIdSchemaField } from \"./write-helpers.js\";\n\nfunction getWeekBucket(dateValue: string): string {\n const date = new Date(`${dateValue}T00:00:00.000Z`);\n const day = date.getUTCDay() || 7;\n date.setUTCDate(date.getUTCDate() - day + 1);\n return date.toISOString().slice(0, 10);\n}\n\nexport const meliGetSalesTrendSchema = z.object({\n profileId: mercadolibreProfileIdSchemaField,\n startDate: z.string().regex(mercadoLibreDateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(mercadoLibreDateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n granularity: z.enum([\"day\", \"week\"]).optional().describe(\"Trend granularity. Defaults to day.\"),\n status: z.string().trim().min(1).optional().describe(\"Optional order status filter.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Orders page size to aggregate.\"),\n offset: z.number().int().min(0).max(5000).optional().describe(\"Orders offset to aggregate.\"),\n});\n\nexport async function meliGetSalesTrendHandler(params: z.infer<typeof meliGetSalesTrendSchema>) {\n try {\n const profileResolution = await resolveMercadoLibreProfileOrSelection(params.profileId);\n if (!profileResolution.ok) {\n return profileResolution.response;\n }\n\n const profileId = profileResolution.value.profileId;\n const granularity = params.granularity ?? \"day\";\n const response = await searchMercadoLibreOrders(profileId, {\n seller: \"\",\n from: params.startDate,\n to: params.endDate,\n status: params.status,\n limit: params.limit ?? 50,\n offset: params.offset ?? 0,\n });\n\n const buckets: Record<string, Record<string, { bucket: string; orders: number; revenue: number }>> = {};\n\n for (const order of asArray<Record<string, unknown>>(response.results)) {\n const createdAt = normalizeString(order.date_created);\n const dateBucket = createdAt.slice(0, 10);\n if (!dateBucket) {\n continue;\n }\n\n const bucketKey = granularity === \"week\" ? getWeekBucket(dateBucket) : dateBucket;\n const currencyId = normalizeString(order.currency_id, \"UNKNOWN\");\n buckets[currencyId] = buckets[currencyId] ?? {};\n buckets[currencyId][bucketKey] = buckets[currencyId][bucketKey] ?? {\n bucket: bucketKey,\n orders: 0,\n revenue: 0,\n };\n buckets[currencyId][bucketKey].orders += 1;\n buckets[currencyId][bucketKey].revenue += toNumber(order.total_amount);\n }\n\n return object(\n stripNulls({\n profile_id: profileId,\n granularity,\n trend_by_currency: Object.fromEntries(\n Object.entries(buckets).map(([currencyId, items]) => [\n currencyId,\n Object.values(items)\n .map((item) => ({ ...item, revenue: roundMoney(item.revenue) }))\n .sort((left, right) => left.bucket.localeCompare(right.bucket)),\n ])\n ),\n metadata: {\n orders_aggregated: asArray(response.results).length,\n limit: params.limit ?? 50,\n offset: params.offset ?? 0,\n },\n })\n );\n } catch (err) {\n return error(formatMercadoLibreError(err, \"Failed to build MercadoLibre sales trend\"));\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { error, object } from \"mcp-use/server\";\nimport { z } from \"zod\";\n\nimport {\n formatMercadoLibreError,\n normalizeMercadoLibrePaging,\n} from \"../../services/mercadolibre/mercadolibre-api.js\";\nimport {\n searchMercadoLibreOrders,\n searchMercadoLibreOrdersBatch,\n} from \"../../services/mercadolibre/mercadolibre-orders.js\";\nimport { stripNulls } from \"../../utils/strip-payload.js\";\nimport {\n asArray,\n asRecord,\n mercadoLibreDateRegex,\n normalizeString,\n roundMoney,\n toNumber,\n} from \"./helpers.js\";\nimport { resolveMercadoLibreProfileOrSelection } from \"./profile-resolution.js\";\nimport { mercadolibreProfileIdSchemaField } from \"./write-helpers.js\";\n\nconst PAGE_FETCH_CONCURRENCY = 15;\nconst PAGE_FETCH_MAX_RETRIES = 2;\nconst DEFAULT_PAGE_LIMIT = 50;\nconst BUSINESS_TIME_ZONE = \"America/Argentina/Buenos_Aires\";\n\ntype FailedPage = {\n offset: number;\n limit: number;\n page_number: number;\n message: string;\n status_code?: number;\n attempts: number;\n retryable: boolean;\n};\n\nfunction formatDateInTimeZone(dateValue: Date, timeZone: string): string {\n const formatter = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n });\n\n const parts = formatter.formatToParts(dateValue);\n const year = parts.find((part) => part.type === \"year\")?.value;\n const month = parts.find((part) => part.type === \"month\")?.value;\n const day = parts.find((part) => part.type === \"day\")?.value;\n\n if (!year || !month || !day) {\n return \"\";\n }\n\n return `${year}-${month}-${day}`;\n}\n\nfunction getLocalDateBucket(createdAt: string, timeZone: string): string {\n if (!createdAt) {\n return \"\";\n }\n\n const date = new Date(createdAt);\n if (Number.isNaN(date.getTime())) {\n return \"\";\n }\n\n return formatDateInTimeZone(date, timeZone);\n}\n\nfunction getWeekBucket(dateValue: string): string {\n const date = new Date(`${dateValue}T00:00:00.000Z`);\n const day = date.getUTCDay() || 7;\n date.setUTCDate(date.getUTCDate() - day + 1);\n return date.toISOString().slice(0, 10);\n}\n\nfunction buildSalesTrendMetadata(params: {\n total: number;\n effectiveLimit: number;\n effectiveOffset: number;\n ordersAggregated: number;\n pagesRequested: number;\n pagesSucceeded: number;\n pagesFailed: number;\n failedOffsets: number[];\n}) {\n const hasFailures = params.pagesFailed > 0;\n const universeFullyFetched = !hasFailures && params.ordersAggregated >= params.total;\n\n let continuation = \"Agregado completo. No more pages for this query.\";\n if (hasFailures) {\n const offsets = params.failedOffsets.slice(0, 5).join(\", \");\n const moreOffsets =\n params.failedOffsets.length > 5\n ? ` y ${params.failedOffsets.length - 5} offsets adicionales`\n : \"\";\n continuation =\n `Agregado parcial por paginas fallidas. Reintentar la misma tool con los mismos filtros para recomputar el trend completo. ` +\n `Offsets fallidos: ${offsets}${moreOffsets}.`;\n }\n\n return {\n total_matching_orders: params.total,\n limit: params.effectiveLimit,\n offset: params.effectiveOffset,\n returned: params.ordersAggregated,\n orders_aggregated: params.ordersAggregated,\n continuation,\n fetch_mode: \"full_aggregate\",\n pages_requested: params.pagesRequested,\n pages_succeeded: params.pagesSucceeded,\n pages_failed: params.pagesFailed,\n universe_fully_fetched: universeFullyFetched,\n };\n}\n\nexport const meliGetSalesTrendSchema = z.object({\n profileId: mercadolibreProfileIdSchemaField,\n startDate: z.string().regex(mercadoLibreDateRegex).describe(\"Start date in YYYY-MM-DD format.\"),\n endDate: z.string().regex(mercadoLibreDateRegex).describe(\"End date in YYYY-MM-DD format.\"),\n granularity: z.enum([\"day\", \"week\"]).optional().describe(\"Trend granularity. Defaults to day.\"),\n status: z.string().trim().min(1).optional().describe(\"Optional order status filter.\"),\n limit: z.number().int().min(1).max(50).optional().describe(\"Optional MercadoLibre page size used internally while aggregating the full trend. Defaults to 50.\"),\n offset: z.number().int().min(0).max(5000).optional().describe(\"Optional starting offset used internally before fetching the remaining pages needed to aggregate the full trend. Defaults to 0.\"),\n});\n\nexport async function meliGetSalesTrendHandler(params: z.infer<typeof meliGetSalesTrendSchema>) {\n try {\n const profileResolution = await resolveMercadoLibreProfileOrSelection(params.profileId);\n if (!profileResolution.ok) {\n return profileResolution.response;\n }\n\n const profileId = profileResolution.value.profileId;\n const granularity = params.granularity ?? \"day\";\n const requestedLimit = params.limit ?? DEFAULT_PAGE_LIMIT;\n const requestedOffset = params.offset ?? 0;\n const response = await searchMercadoLibreOrders(profileId, {\n seller: \"\",\n from: params.startDate,\n to: params.endDate,\n status: params.status,\n limit: requestedLimit,\n offset: requestedOffset,\n });\n\n const paging = normalizeMercadoLibrePaging(asRecord(response.paging));\n const baseOrders = asArray<Record<string, unknown>>(response.results);\n const effectiveLimit = paging.limit || requestedLimit || baseOrders.length;\n const effectiveOffset = paging.offset || requestedOffset || 0;\n const allOrders = [...baseOrders];\n const failedPages: FailedPage[] = [];\n let pagesRequested = 1;\n let pagesSucceeded = 1;\n\n if (effectiveLimit > 0 && paging.total > effectiveOffset + baseOrders.length) {\n const nextOffsets: number[] = [];\n for (\n let nextOffset = effectiveOffset + effectiveLimit;\n nextOffset < paging.total;\n nextOffset += effectiveLimit\n ) {\n nextOffsets.push(nextOffset);\n }\n\n pagesRequested += nextOffsets.length;\n\n if (nextOffsets.length > 0) {\n const batch = await searchMercadoLibreOrdersBatch(\n profileId,\n {\n seller: \"\",\n from: params.startDate,\n to: params.endDate,\n status: params.status,\n limit: effectiveLimit,\n },\n nextOffsets,\n {\n maxConcurrency: PAGE_FETCH_CONCURRENCY,\n maxRetries: PAGE_FETCH_MAX_RETRIES,\n }\n );\n\n pagesSucceeded += batch.successful.length;\n batch.successful.forEach((entry) => {\n allOrders.push(...asArray<Record<string, unknown>>(entry.document.results));\n });\n batch.failed.forEach((failure) => {\n const failedOffset = Number(failure.id);\n failedPages.push({\n offset: failedOffset,\n limit: effectiveLimit,\n page_number: Math.floor(failedOffset / effectiveLimit) + 1,\n message: failure.message,\n status_code: failure.statusCode,\n attempts: failure.attempts,\n retryable: failure.retryable,\n });\n });\n }\n }\n\n const buckets: Record<string, Record<string, { bucket: string; orders: number; revenue: number }>> = {};\n\n for (const order of allOrders) {\n const createdAt = normalizeString(order.date_created);\n const dateBucket = getLocalDateBucket(createdAt, BUSINESS_TIME_ZONE);\n if (!dateBucket) {\n continue;\n }\n\n const bucketKey = granularity === \"week\" ? getWeekBucket(dateBucket) : dateBucket;\n const currencyId = normalizeString(order.currency_id, \"UNKNOWN\");\n buckets[currencyId] = buckets[currencyId] ?? {};\n buckets[currencyId][bucketKey] = buckets[currencyId][bucketKey] ?? {\n bucket: bucketKey,\n orders: 0,\n revenue: 0,\n };\n buckets[currencyId][bucketKey].orders += 1;\n buckets[currencyId][bucketKey].revenue += toNumber(order.total_amount);\n }\n\n const metadata = buildSalesTrendMetadata({\n total: paging.total,\n effectiveLimit,\n effectiveOffset,\n ordersAggregated: allOrders.length,\n pagesRequested,\n pagesSucceeded,\n pagesFailed: failedPages.length,\n failedOffsets: failedPages.map((failure) => failure.offset),\n });\n\n return object(\n stripNulls({\n profile_id: profileId,\n granularity,\n trend_by_currency: Object.fromEntries(\n Object.entries(buckets).map(([currencyId, items]) => [\n currencyId,\n Object.values(items)\n .map((item) => ({ ...item, revenue: roundMoney(item.revenue) }))\n .sort((left, right) => left.bucket.localeCompare(right.bucket)),\n ])\n ),\n metadata,\n failed_pages: failedPages.length > 0 ? failedPages : undefined,\n })\n );\n } catch (err) {\n return error(formatMercadoLibreError(err, \"Failed to build MercadoLibre sales trend\"));\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,OAAO,cAAc;AAC9B,SAAS,SAAS;AAElB;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,6CAA6C;AACtD,SAAS,wCAAwC;AAEjD,MAAM,yBAAyB;AAC/B,MAAM,yBAAyB;AAC/B,MAAM,qBAAqB;AAC3B,MAAM,qBAAqB;AAY3B,SAAS,qBAAqB,WAAiB,UAA0B;AACvE,QAAM,YAAY,IAAI,KAAK,eAAe,SAAS;AAAA,IACjD;AAAA,IACA,MAAM;AAAA,IACN,OAAO;AAAA,IACP,KAAK;AAAA,EACP,CAAC;AAED,QAAM,QAAQ,UAAU,cAAc,SAAS;AAC/C,QAAM,OAAO,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,MAAM,GAAG;AACzD,QAAM,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,OAAO,GAAG;AAC3D,QAAM,MAAM,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,KAAK,GAAG;AAEvD,MAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK;AAC3B,WAAO;AAAA,EACT;AAEA,SAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG;AAChC;AAEA,SAAS,mBAAmB,WAAmB,UAA0B;AACvE,MAAI,CAAC,WAAW;AACd,WAAO;AAAA,EACT;AAEA,QAAM,OAAO,IAAI,KAAK,SAAS;AAC/B,MAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAAG;AAChC,WAAO;AAAA,EACT;AAEA,SAAO,qBAAqB,MAAM,QAAQ;AAC5C;AAEA,SAAS,cAAc,WAA2B;AAChD,QAAM,OAAO,oBAAI,KAAK,GAAG,SAAS,gBAAgB;AAClD,QAAM,MAAM,KAAK,UAAU,KAAK;AAChC,OAAK,WAAW,KAAK,WAAW,IAAI,MAAM,CAAC;AAC3C,SAAO,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE;AACvC;AAEA,SAAS,wBAAwB,QAS9B;AACD,QAAM,cAAc,OAAO,cAAc;AACzC,QAAM,uBAAuB,CAAC,eAAe,OAAO,oBAAoB,OAAO;AAE/E,MAAI,eAAe;AACnB,MAAI,aAAa;AACf,UAAM,UAAU,OAAO,cAAc,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI;AAC1D,UAAM,cACJ,OAAO,cAAc,SAAS,IAC1B,MAAM,OAAO,cAAc,SAAS,CAAC,yBACrC;AACN,mBACE,+IACqB,OAAO,GAAG,WAAW;AAAA,EAC9C;AAEA,SAAO;AAAA,IACL,uBAAuB,OAAO;AAAA,IAC9B,OAAO,OAAO;AAAA,IACd,QAAQ,OAAO;AAAA,IACf,UAAU,OAAO;AAAA,IACjB,mBAAmB,OAAO;AAAA,IAC1B;AAAA,IACA,YAAY;AAAA,IACZ,iBAAiB,OAAO;AAAA,IACxB,iBAAiB,OAAO;AAAA,IACxB,cAAc,OAAO;AAAA,IACrB,wBAAwB;AAAA,EAC1B;AACF;AAEO,MAAM,0BAA0B,EAAE,OAAO;AAAA,EAC9C,WAAW;AAAA,EACX,WAAW,EAAE,OAAO,EAAE,MAAM,qBAAqB,EAAE,SAAS,kCAAkC;AAAA,EAC9F,SAAS,EAAE,OAAO,EAAE,MAAM,qBAAqB,EAAE,SAAS,gCAAgC;AAAA,EAC1F,aAAa,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,qCAAqC;AAAA,EAC9F,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,EAAE,SAAS,EAAE,SAAS,+BAA+B;AAAA,EACpF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,SAAS,mGAAmG;AAAA,EAC9J,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,IAAI,GAAI,EAAE,SAAS,EAAE,SAAS,iIAAiI;AACjM,CAAC;AAED,eAAsB,yBAAyB,QAAiD;AAC9F,MAAI;AACF,UAAM,oBAAoB,MAAM,sCAAsC,OAAO,SAAS;AACtF,QAAI,CAAC,kBAAkB,IAAI;AACzB,aAAO,kBAAkB;AAAA,IAC3B;AAEA,UAAM,YAAY,kBAAkB,MAAM;AAC1C,UAAM,cAAc,OAAO,eAAe;AAC1C,UAAM,iBAAiB,OAAO,SAAS;AACvC,UAAM,kBAAkB,OAAO,UAAU;AACzC,UAAM,WAAW,MAAM,yBAAyB,WAAW;AAAA,MACzD,QAAQ;AAAA,MACR,MAAM,OAAO;AAAA,MACb,IAAI,OAAO;AAAA,MACX,QAAQ,OAAO;AAAA,MACf,OAAO;AAAA,MACP,QAAQ;AAAA,IACV,CAAC;AAED,UAAM,SAAS,4BAA4B,SAAS,SAAS,MAAM,CAAC;AACpE,UAAM,aAAa,QAAiC,SAAS,OAAO;AACpE,UAAM,iBAAiB,OAAO,SAAS,kBAAkB,WAAW;AACpE,UAAM,kBAAkB,OAAO,UAAU,mBAAmB;AAC5D,UAAM,YAAY,CAAC,GAAG,UAAU;AAChC,UAAM,cAA4B,CAAC;AACnC,QAAI,iBAAiB;AACrB,QAAI,iBAAiB;AAErB,QAAI,iBAAiB,KAAK,OAAO,QAAQ,kBAAkB,WAAW,QAAQ;AAC5E,YAAM,cAAwB,CAAC;AAC/B,eACM,aAAa,kBAAkB,gBACnC,aAAa,OAAO,OACpB,cAAc,gBACd;AACA,oBAAY,KAAK,UAAU;AAAA,MAC7B;AAEA,wBAAkB,YAAY;AAE9B,UAAI,YAAY,SAAS,GAAG;AAC1B,cAAM,QAAQ,MAAM;AAAA,UAClB;AAAA,UACA;AAAA,YACE,QAAQ;AAAA,YACR,MAAM,OAAO;AAAA,YACb,IAAI,OAAO;AAAA,YACX,QAAQ,OAAO;AAAA,YACf,OAAO;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,YACE,gBAAgB;AAAA,YAChB,YAAY;AAAA,UACd;AAAA,QACF;AAEA,0BAAkB,MAAM,WAAW;AACnC,cAAM,WAAW,QAAQ,CAAC,UAAU;AAClC,oBAAU,KAAK,GAAG,QAAiC,MAAM,SAAS,OAAO,CAAC;AAAA,QAC5E,CAAC;AACD,cAAM,OAAO,QAAQ,CAAC,YAAY;AAChC,gBAAM,eAAe,OAAO,QAAQ,EAAE;AACtC,sBAAY,KAAK;AAAA,YACf,QAAQ;AAAA,YACR,OAAO;AAAA,YACP,aAAa,KAAK,MAAM,eAAe,cAAc,IAAI;AAAA,YACzD,SAAS,QAAQ;AAAA,YACjB,aAAa,QAAQ;AAAA,YACrB,UAAU,QAAQ;AAAA,YAClB,WAAW,QAAQ;AAAA,UACrB,CAAC;AAAA,QACH,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,UAA+F,CAAC;AAEtG,eAAW,SAAS,WAAW;AAC7B,YAAM,YAAY,gBAAgB,MAAM,YAAY;AACpD,YAAM,aAAa,mBAAmB,WAAW,kBAAkB;AACnE,UAAI,CAAC,YAAY;AACf;AAAA,MACF;AAEA,YAAM,YAAY,gBAAgB,SAAS,cAAc,UAAU,IAAI;AACvE,YAAM,aAAa,gBAAgB,MAAM,aAAa,SAAS;AAC/D,cAAQ,UAAU,IAAI,QAAQ,UAAU,KAAK,CAAC;AAC9C,cAAQ,UAAU,EAAE,SAAS,IAAI,QAAQ,UAAU,EAAE,SAAS,KAAK;AAAA,QACjE,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,SAAS;AAAA,MACX;AACA,cAAQ,UAAU,EAAE,SAAS,EAAE,UAAU;AACzC,cAAQ,UAAU,EAAE,SAAS,EAAE,WAAW,SAAS,MAAM,YAAY;AAAA,IACvE;AAEA,UAAM,WAAW,wBAAwB;AAAA,MACvC,OAAO,OAAO;AAAA,MACd;AAAA,MACA;AAAA,MACA,kBAAkB,UAAU;AAAA,MAC5B;AAAA,MACA;AAAA,MACA,aAAa,YAAY;AAAA,MACzB,eAAe,YAAY,IAAI,CAAC,YAAY,QAAQ,MAAM;AAAA,IAC5D,CAAC;AAED,WAAO;AAAA,MACL,WAAW;AAAA,QACT,YAAY;AAAA,QACZ;AAAA,QACA,mBAAmB,OAAO;AAAA,UACxB,OAAO,QAAQ,OAAO,EAAE,IAAI,CAAC,CAAC,YAAY,KAAK,MAAM;AAAA,YACnD;AAAA,YACA,OAAO,OAAO,KAAK,EAChB,IAAI,CAAC,UAAU,EAAE,GAAG,MAAM,SAAS,WAAW,KAAK,OAAO,EAAE,EAAE,EAC9D,KAAK,CAAC,MAAM,UAAU,KAAK,OAAO,cAAc,MAAM,MAAM,CAAC;AAAA,UAClE,CAAC;AAAA,QACH;AAAA,QACA;AAAA,QACA,cAAc,YAAY,SAAS,IAAI,cAAc;AAAA,MACvD,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,wBAAwB,KAAK,0CAA0C,CAAC;AAAA,EACvF;AACF;",
6
6
  "names": []
7
7
  }