@datadive-tools/mcp 0.9.0 → 0.10.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.
package/README.md CHANGED
@@ -135,6 +135,11 @@ To skip the per-call confirmation (e.g. in an automated setup), set
135
135
  `DATADIVE_AUTO_CONFIRM_WRITES=true` in your client config — then these tools run
136
136
  without `confirm`.
137
137
 
138
+ None of the three is **safe to retry** — each call spends tokens again and
139
+ creates a separate dive / re-dive / Rank Radar, even with identical arguments. If
140
+ a call errors or times out, check `get_dive_status` / `list_niches` /
141
+ `list_rank_radars` for what already exists before calling again.
142
+
138
143
  Dives are **asynchronous**. `create_niche_dive` returns a `diveId` and an
139
144
  estimated completion time immediately; poll `get_dive_status` with that `diveId`
140
145
  until it reports `success`, which carries the new `nicheId` you then feed to
package/dist/index.js CHANGED
@@ -105,7 +105,7 @@ function extractMessage(body) {
105
105
  }
106
106
 
107
107
  // package.json
108
- var version = "0.9.0";
108
+ var version = "0.10.0";
109
109
 
110
110
  // src/http/client.ts
111
111
  var PKG_VERSION = version;
@@ -222,21 +222,37 @@ function isObject(v) {
222
222
  }
223
223
 
224
224
  // src/tools/list-niches.ts
225
+ var LIST_NICHES_MAX_PAGE_SIZE = 50;
225
226
  var inputSchema = {
226
- currentPage: z.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
227
- pageSize: z.number().int().min(1).max(100).optional().describe("Items per page (max 100). Defaults to 20.")
227
+ currentPage: z.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1. Keep requesting the next page while `hasNext` is true."),
228
+ pageSize: z.number().int().min(1).max(LIST_NICHES_MAX_PAGE_SIZE).optional().describe(`Items per page (max ${LIST_NICHES_MAX_PAGE_SIZE}). Defaults to 20.`),
229
+ searchText: z.string().trim().min(1).max(200).optional().describe(
230
+ "Case-insensitive partial match on the niche label or hero keyword. Prefer this over paging through everything when the user names a niche."
231
+ ),
232
+ searchAsin: z.string().trim().regex(/^[A-Za-z0-9]{10}$/, "must be a 10-character ASIN").optional().describe("Only niches whose competitor set contains this ASIN (10 characters, e.g. B08N5WRWNW)."),
233
+ orderBy: z.enum(["lastDived", "name"]).optional().describe(
234
+ "Sort field: `lastDived` (by latestResearchDate, newest first by default) or `name` (by nicheLabel, A\u2192Z by default). Defaults to `lastDived`."
235
+ ),
236
+ sortOrder: z.enum(["ASC", "DESC"]).optional().describe("Sort direction. Defaults to DESC for `lastDived` and ASC for `name`.")
228
237
  };
229
238
  var listNichesTool = {
230
239
  name: "list_niches",
231
240
  title: "List DataDive Niches",
232
- description: "Use this first when the user asks about their niches, or to find a `nicheId` for use with `get_niche_keywords`, `get_niche_competitors`, or `get_ranking_juice`. Retrieves a paginated list of Niches. Each Niche represents a market segment or product category being tracked. Returns nicheId, heroKeyword, nicheLabel, marketplace (com/uk/de/...), and latestResearchDate per niche, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
241
+ description: `Use this first when the user asks about their niches, or to find a \`nicheId\` for use with \`get_niche_keywords\`, \`get_niche_competitors\`, or \`get_ranking_juice\`. Retrieves a paginated list of Niches, newest dive first. Each Niche represents a market segment or product category being tracked. Returns nicheId, heroKeyword, nicheLabel, marketplace (com/uk/de/...), and latestResearchDate per niche, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev). Narrow with \`searchText\` (label/keyword) or \`searchAsin\` (a competitor ASIN) instead of scanning pages; a page holds at most ${LIST_NICHES_MAX_PAGE_SIZE} niches, so the full list of a large account needs several calls.`,
233
242
  inputSchema,
234
243
  annotations: { readOnlyHint: true },
235
244
  handler: async (args, ctx) => {
236
245
  return await httpGet(
237
246
  { config: ctx.config, toolName: "list_niches" },
238
247
  "/v1/niches",
239
- { currentPage: args.currentPage, pageSize: args.pageSize }
248
+ {
249
+ currentPage: args.currentPage,
250
+ pageSize: args.pageSize,
251
+ searchText: args.searchText,
252
+ searchAsin: args.searchAsin,
253
+ orderBy: args.orderBy,
254
+ sortOrder: args.sortOrder
255
+ }
240
256
  );
241
257
  }
242
258
  };
@@ -426,7 +442,7 @@ var inputSchema8 = {
426
442
  var createRankRadarTool = {
427
443
  name: "create_rank_radar",
428
444
  title: "Create a Rank Radar",
429
- description: "Use this to start tracking organic and sponsored keyword rankings for an ASIN within a niche. \u26A0\uFE0F Spends Search Term tokens (cost scales with `numberOfKeywords`) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. Returns the new `rankRadarId`; read its data later with `get_rank_radar_data`. Requires `asin`, `numberOfKeywords` (min 1), and a `nicheId` from `list_niches`.",
445
+ description: "Use this to start tracking organic and sponsored keyword rankings for an ASIN within a niche. \u26A0\uFE0F Spends Search Term tokens (cost scales with `numberOfKeywords`) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. Not safe to retry: each call spends tokens again and creates a separate Rank Radar \u2014 if a call errors or times out, check `list_rank_radars` instead of re-calling. Returns the new `rankRadarId`; read its data later with `get_rank_radar_data`. Requires `asin`, `numberOfKeywords` (min 1), and a `nicheId` from `list_niches`.",
430
446
  inputSchema: inputSchema8,
431
447
  // destructiveHint is technically "additive" here (this creates a new Rank Radar, it
432
448
  // doesn't destroy data), but we flag it destructive so clients prompt before the
@@ -464,7 +480,7 @@ var inputSchema9 = {
464
480
  var createNicheDiveTool = {
465
481
  name: "create_niche_dive",
466
482
  title: "Create a Niche Dive",
467
- description: "Use this to start new niche research from a seed ASIN. \u26A0\uFE0F Spends dive tokens (cost scales with `numberOfCompetitors`) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. The dive runs asynchronously: this returns immediately with a `diveId` and an `estimatedCompletionDate`. Poll `get_dive_status` with that `diveId` until it reports `success` (which carries the new `nicheId` for use with `list_niches`, `get_niche_keywords`, etc.). Requires `marketplace`, `asin`, and `numberOfCompetitors` (min 2).",
483
+ description: "Use this to start new niche research from a seed ASIN. \u26A0\uFE0F Spends dive tokens (cost scales with `numberOfCompetitors`) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. Not safe to retry: each call spends tokens again and starts a separate dive \u2014 if a call errors or times out, poll `get_dive_status` (or check `list_niches`) instead of re-calling. The dive runs asynchronously: this returns immediately with a `diveId` and an `estimatedCompletionDate`. Poll `get_dive_status` with that `diveId` until it reports `success` (which carries the new `nicheId` for use with `list_niches`, `get_niche_keywords`, etc.). Requires `marketplace`, `asin`, and `numberOfCompetitors` (min 2).",
468
484
  inputSchema: inputSchema9,
469
485
  // destructiveHint is technically "additive" here (a dive creates a new niche, it
470
486
  // doesn't destroy data), but we flag it destructive so clients prompt before the
@@ -513,7 +529,7 @@ var inputSchema10 = {
513
529
  var rediveNicheTool = {
514
530
  name: "redive_niche",
515
531
  title: "Re-dive an Existing Niche",
516
- description: "Use this to refresh an existing niche's research with current Amazon data, instead of creating a new niche with `create_niche_dive`. \u26A0\uFE0F Spends dive tokens (one batch per ASIN dived) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. Two modes: `same_competitors` re-dives the niche's current competitor set (no other argument needed) and `discover` finds a fresh set, sized by `numberOfCompetitors` and steerable with `heroAsin` / `lockedAsins` / `excludedAsins`. Runs asynchronously: returns a `diveId` and an `estimatedCompletionDate` \u2014 poll `get_dive_status` with that `diveId` until it reports `success`. The niche keeps its `nicheId`, so existing rank radars and reports follow the refreshed data.",
532
+ description: "Use this to refresh an existing niche's research with current Amazon data, instead of creating a new niche with `create_niche_dive`. \u26A0\uFE0F Spends dive tokens (one batch per ASIN dived) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. Not safe to retry: each call spends tokens again and starts a separate re-dive \u2014 if a call errors or times out, poll `get_dive_status` instead of re-calling. Two modes: `same_competitors` re-dives the niche's current competitor set (no other argument needed) and `discover` finds a fresh set, sized by `numberOfCompetitors` and steerable with `heroAsin` / `lockedAsins` / `excludedAsins`. Runs asynchronously: returns a `diveId` and an `estimatedCompletionDate` \u2014 poll `get_dive_status` with that `diveId` until it reports `success`. The niche keeps its `nicheId`, so existing rank radars and reports follow the refreshed data.",
517
533
  inputSchema: inputSchema10,
518
534
  // Same reasoning as create_niche_dive: a re-dive is additive rather than destructive, but the
519
535
  // irreversible token spend is what clients need to prompt on. The confirm gate enforces it regardless.