@datadive-tools/mcp 0.15.0 → 0.16.1
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 +2 -1
- package/dist/index.js +215 -129
- package/dist/index.js.map +1 -1
- package/dist/lib.js +215 -129
- package/dist/lib.js.map +1 -1
- package/package.json +1 -1
package/dist/lib.js
CHANGED
|
@@ -139,7 +139,7 @@ function extractMessage(body) {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
// package.json
|
|
142
|
-
var version = "0.
|
|
142
|
+
var version = "0.16.1";
|
|
143
143
|
|
|
144
144
|
// src/http/client.ts
|
|
145
145
|
var PKG_VERSION = version;
|
|
@@ -510,13 +510,41 @@ function rankRadarKeywordQuery(args) {
|
|
|
510
510
|
|
|
511
511
|
// src/tools/get-rank-radar-data.ts
|
|
512
512
|
var inputSchema7 = rankRadarKeywordQueryInputSchema;
|
|
513
|
+
async function readAllKeywords(args, ctx) {
|
|
514
|
+
const request = { config: ctx.config, toolName: "get_rank_radar_data" };
|
|
515
|
+
const path = rankRadarPath(args.rankRadarId);
|
|
516
|
+
const keywords = [];
|
|
517
|
+
let page;
|
|
518
|
+
let currentPage = 1;
|
|
519
|
+
do {
|
|
520
|
+
page = await httpGet(
|
|
521
|
+
request,
|
|
522
|
+
path,
|
|
523
|
+
rankRadarKeywordQuery({ ...args, currentPage, pageSize: RANK_RADAR_KEYWORDS_MAX_PAGE_SIZE })
|
|
524
|
+
);
|
|
525
|
+
keywords.push(...page.data ?? []);
|
|
526
|
+
currentPage++;
|
|
527
|
+
} while (page.hasNext && currentPage <= page.lastPage && (page.data?.length ?? 0) > 0);
|
|
528
|
+
return {
|
|
529
|
+
data: keywords,
|
|
530
|
+
currentPage: 1,
|
|
531
|
+
pageSize: keywords.length,
|
|
532
|
+
total: page.total,
|
|
533
|
+
lastPage: 1,
|
|
534
|
+
hasNext: false,
|
|
535
|
+
hasPrev: false
|
|
536
|
+
};
|
|
537
|
+
}
|
|
513
538
|
var getRankRadarDataTool = {
|
|
514
539
|
name: "get_rank_radar_data",
|
|
515
540
|
title: "Get Keyword Rankings for a Rank Radar",
|
|
516
|
-
description: `Use this to analyze keyword ranking trends over time. Requires startDate and endDate (yyyy-mm-dd), at most ${RANK_RADAR_MAX_DATE_RANGE_DAYS} days apart. Retrieves historical keyword ranking data for the specified Rank Radar within the date range. ${
|
|
541
|
+
description: `Use this to analyze keyword ranking trends over time. Requires startDate and endDate (yyyy-mm-dd), at most ${RANK_RADAR_MAX_DATE_RANGE_DAYS} days apart. Retrieves historical keyword ranking data for the specified Rank Radar within the date range. Without \`currentPage\` and \`pageSize\` it returns every active keyword in one result. To read one page instead \u2014 faster for large Rank Radars \u2014 pass \`currentPage\` and/or \`pageSize\` (max ${RANK_RADAR_KEYWORDS_MAX_PAGE_SIZE}) and continue with \`currentPage\` + 1 while \`hasNext\` is true. The response carries currentPage, pageSize, total, lastPage, hasNext and hasPrev; \`total\` is the Rank Radar's active keyword count. Paused keywords are not included. Each keyword has id, keyword, searchVolume, relevancy, ranks (per-day { date, organicRank, sponsoredRank, impressionRank }), and any highlight annotations. A rank of 101 means the product was not in the top 100 results that day. For PPC metrics use \`get_rank_radar_ppc_data\`; for Search Query Performance use \`get_rank_radar_sqp_data\`. Use after \`list_rank_radars\` to discover a \`rankRadarId\`.`,
|
|
517
542
|
inputSchema: inputSchema7,
|
|
518
543
|
annotations: { readOnlyHint: true },
|
|
519
544
|
handler: async (args, ctx) => {
|
|
545
|
+
if (args.currentPage === void 0 && args.pageSize === void 0) {
|
|
546
|
+
return await readAllKeywords(args, ctx);
|
|
547
|
+
}
|
|
520
548
|
return await httpGet(
|
|
521
549
|
{ config: ctx.config, toolName: "get_rank_radar_data" },
|
|
522
550
|
rankRadarPath(args.rankRadarId),
|
|
@@ -661,24 +689,65 @@ var createNicheDiveTool = {
|
|
|
661
689
|
}
|
|
662
690
|
};
|
|
663
691
|
|
|
664
|
-
// src/tools/
|
|
692
|
+
// src/tools/create-niche-dive-from-competitors-list.ts
|
|
665
693
|
import { z as z11 } from "zod";
|
|
694
|
+
var DIVE_FROM_COMPETITORS_MIN_ASINS = 2;
|
|
695
|
+
var DIVE_FROM_COMPETITORS_MAX_ASINS = 200;
|
|
696
|
+
var normalizedAsins = z11.array(z11.string().trim().min(1).toUpperCase()).min(DIVE_FROM_COMPETITORS_MIN_ASINS).max(DIVE_FROM_COMPETITORS_MAX_ASINS).refine((asins) => new Set(asins).size === asins.length, {
|
|
697
|
+
message: "asins must not contain the same ASIN twice"
|
|
698
|
+
});
|
|
666
699
|
var inputSchema12 = {
|
|
667
|
-
|
|
668
|
-
|
|
700
|
+
marketplace: z11.enum(SUPPORTED_MARKETPLACES).describe(
|
|
701
|
+
"Amazon marketplace where the ASINs are listed. Full domain suffixes: com, ca, co.uk, com.mx, in, fr, de, es, it, co.jp (e.g. UK is `co.uk`, not `uk`)."
|
|
702
|
+
),
|
|
703
|
+
asins: normalizedAsins.describe(
|
|
704
|
+
`The exact competitor ASINs to build the niche from, ${DIVE_FROM_COMPETITORS_MIN_ASINS} to ${DIVE_FROM_COMPETITORS_MAX_ASINS}, each once. No other competitors are added.`
|
|
705
|
+
),
|
|
706
|
+
confirm: z11.boolean().optional().describe(
|
|
707
|
+
"Must be true to proceed \u2014 this dive spends dive tokens for every ASIN. Confirm the cost with the user first."
|
|
708
|
+
)
|
|
709
|
+
};
|
|
710
|
+
var createNicheDiveFromCompetitorsListTool = {
|
|
711
|
+
name: "create_niche_dive_from_competitors_list",
|
|
712
|
+
title: "Create a Niche Dive from a Competitor List",
|
|
713
|
+
description: `Use this when the user already knows which products belong in the niche \u2014 e.g. they list competitor ASINs, or picked them from search results \u2014 and wants niche research on exactly those. Unlike \`create_niche_dive\`, which discovers competitors around one seed ASIN, this builds the niche from the given ASINs only (${DIVE_FROM_COMPETITORS_MIN_ASINS}-${DIVE_FROM_COMPETITORS_MAX_ASINS}). \u26A0\uFE0F Spends dive tokens for every ASIN 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. Fails without spending tokens when the ASIN count is over the plan's limit, when the account is at its niche limit, or when Amazon product data cannot be found for enough of the ASINs. The dive runs asynchronously: this returns a \`diveId\` and an \`estimatedCompletionDate\`; poll \`get_dive_status\` with the \`diveId\` until it reports \`success\`, which carries the new \`nicheId\`.`,
|
|
714
|
+
inputSchema: inputSchema12,
|
|
715
|
+
// Additive rather than destructive, but flagged destructive so clients prompt before the
|
|
716
|
+
// irreversible token spend — same reasoning as create_niche_dive. The confirm gate enforces it.
|
|
717
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
718
|
+
handler: async (args, ctx) => {
|
|
719
|
+
const pending = requireConfirmation(
|
|
720
|
+
args.confirm,
|
|
721
|
+
ctx,
|
|
722
|
+
`Creating this dive consumes dive tokens \u2014 one batch per ASIN analyzed (${args.asins.length} ASINs given).`
|
|
723
|
+
);
|
|
724
|
+
if (pending) return pending;
|
|
725
|
+
return await httpPost(
|
|
726
|
+
{ config: ctx.config, toolName: "create_niche_dive_from_competitors_list" },
|
|
727
|
+
"/v1/niches/dive_with_competitors",
|
|
728
|
+
{ marketplace: args.marketplace, asins: args.asins }
|
|
729
|
+
);
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
// src/tools/redive-niche.ts
|
|
734
|
+
import { z as z12 } from "zod";
|
|
735
|
+
var inputSchema13 = {
|
|
736
|
+
nicheId: z12.string().min(1).describe("The niche to re-dive. Get one from `list_niches`."),
|
|
737
|
+
mode: z12.enum(["same_competitors", "discover"]).describe(
|
|
669
738
|
"How to pick the competitors for the refreshed dive. `same_competitors` re-runs the niche's current competitor set and takes no other argument \u2014 use it to refresh stale data. `discover` searches for a fresh competitor set, which is what you want when the niche has changed."
|
|
670
739
|
),
|
|
671
|
-
numberOfCompetitors:
|
|
740
|
+
numberOfCompetitors: z12.number().int().min(2).optional().describe(
|
|
672
741
|
"Total ASINs the refreshed dive should contain, including `lockedAsins`. Required in `discover` mode and rejected in `same_competitors` mode. Dive tokens are spent per ASIN."
|
|
673
742
|
),
|
|
674
|
-
heroAsin:
|
|
743
|
+
heroAsin: z12.string().min(1).optional().describe(
|
|
675
744
|
"`discover` mode only. Seed product the competitor discovery starts from. Defaults to the niche's highest-selling competitor, preferring one the user's own connected seller account owns."
|
|
676
745
|
),
|
|
677
|
-
lockedAsins:
|
|
746
|
+
lockedAsins: z12.array(z12.string().min(1)).optional().describe(
|
|
678
747
|
"`discover` mode only. ASINs that must stay in the competitor set regardless of what discovery finds. Cannot exceed `numberOfCompetitors`, and cannot overlap `excludedAsins`."
|
|
679
748
|
),
|
|
680
|
-
excludedAsins:
|
|
681
|
-
confirm:
|
|
749
|
+
excludedAsins: z12.array(z12.string().min(1)).optional().describe("`discover` mode only. ASINs discovery must never select."),
|
|
750
|
+
confirm: z12.boolean().optional().describe(
|
|
682
751
|
"Must be true to proceed \u2014 a re-dive spends dive tokens. Confirm the cost with the user first."
|
|
683
752
|
)
|
|
684
753
|
};
|
|
@@ -686,7 +755,7 @@ var rediveNicheTool = {
|
|
|
686
755
|
name: "redive_niche",
|
|
687
756
|
title: "Re-dive an Existing Niche",
|
|
688
757
|
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.",
|
|
689
|
-
inputSchema:
|
|
758
|
+
inputSchema: inputSchema13,
|
|
690
759
|
// Same reasoning as create_niche_dive: a re-dive is additive rather than destructive, but the
|
|
691
760
|
// irreversible token spend is what clients need to prompt on. The confirm gate enforces it regardless.
|
|
692
761
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
@@ -719,15 +788,17 @@ var rediveNicheTool = {
|
|
|
719
788
|
};
|
|
720
789
|
|
|
721
790
|
// src/tools/get-dive-status.ts
|
|
722
|
-
import { z as
|
|
723
|
-
var
|
|
724
|
-
diveId:
|
|
791
|
+
import { z as z13 } from "zod";
|
|
792
|
+
var inputSchema14 = {
|
|
793
|
+
diveId: z13.string().min(1).describe(
|
|
794
|
+
"The dive identifier returned by `create_niche_dive`, `create_niche_dive_from_competitors_list` or `redive_niche`."
|
|
795
|
+
)
|
|
725
796
|
};
|
|
726
797
|
var getDiveStatusTool = {
|
|
727
798
|
name: "get_dive_status",
|
|
728
799
|
title: "Get Niche Dive Status",
|
|
729
|
-
description: "Use this to poll a niche dive started with `create_niche_dive` or `redive_niche` until it finishes. Returns one of three shapes keyed by `status`: `in_progress` (with `estimatedCompletionDate`), `success` (with the `nicheId` plus `tokensUsed`/`tokensLeft`), or `error` (with an `error` message). On `success`, use the `nicheId` with `list_niches`, `get_niche_keywords`, `get_niche_competitors`, etc. (for a re-dive it is the same niche you passed in, now carrying refreshed data). Re-poll periodically \u2014 dives can take minutes; the `estimatedCompletionDate` hints at when to check.",
|
|
730
|
-
inputSchema:
|
|
800
|
+
description: "Use this to poll a niche dive started with `create_niche_dive`, `create_niche_dive_from_competitors_list` or `redive_niche` until it finishes. Returns one of three shapes keyed by `status`: `in_progress` (with `estimatedCompletionDate`), `success` (with the `nicheId` plus `tokensUsed`/`tokensLeft`), or `error` (with an `error` message). On `success`, use the `nicheId` with `list_niches`, `get_niche_keywords`, `get_niche_competitors`, etc. (for a re-dive it is the same niche you passed in, now carrying refreshed data). Re-poll periodically \u2014 dives can take minutes; the `estimatedCompletionDate` hints at when to check.",
|
|
801
|
+
inputSchema: inputSchema14,
|
|
731
802
|
annotations: { readOnlyHint: true },
|
|
732
803
|
handler: async (args, ctx) => {
|
|
733
804
|
return await httpGet(
|
|
@@ -738,10 +809,10 @@ var getDiveStatusTool = {
|
|
|
738
809
|
};
|
|
739
810
|
|
|
740
811
|
// src/tools/delete-niche.ts
|
|
741
|
-
import { z as
|
|
742
|
-
var
|
|
743
|
-
nicheId:
|
|
744
|
-
confirm:
|
|
812
|
+
import { z as z14 } from "zod";
|
|
813
|
+
var inputSchema15 = {
|
|
814
|
+
nicheId: z14.string().min(1).describe("The niche to delete. Get one from `list_niches`."),
|
|
815
|
+
confirm: z14.boolean().optional().describe(
|
|
745
816
|
"Must be true to proceed \u2014 deleting a niche destroys its keywords, competitors and dive history permanently. Confirm with the user first."
|
|
746
817
|
)
|
|
747
818
|
};
|
|
@@ -749,7 +820,7 @@ var deleteNicheTool = {
|
|
|
749
820
|
name: "delete_niche",
|
|
750
821
|
title: "Delete a Niche",
|
|
751
822
|
description: "Use this to permanently remove a niche the user no longer needs. \u26A0\uFE0F Deletes the niche and everything attached to it \u2014 keywords, competitors, all dives and diveboxes \u2014 and cannot be undone; the dive tokens already spent on it are NOT refunded. Set `confirm: true` only after the user approves. A niche used by any Rank Radar cannot be deleted: the call fails with a conflict, so delete or archive those rank radars first (`list_rank_radars` with the `nicheId` filter shows them).",
|
|
752
|
-
inputSchema:
|
|
823
|
+
inputSchema: inputSchema15,
|
|
753
824
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
754
825
|
handler: async (args, ctx) => {
|
|
755
826
|
const pending = requireConfirmation(
|
|
@@ -767,15 +838,15 @@ var deleteNicheTool = {
|
|
|
767
838
|
};
|
|
768
839
|
|
|
769
840
|
// src/tools/pause-rank-radar.ts
|
|
770
|
-
import { z as
|
|
771
|
-
var
|
|
772
|
-
rankRadarId:
|
|
841
|
+
import { z as z15 } from "zod";
|
|
842
|
+
var inputSchema16 = {
|
|
843
|
+
rankRadarId: z15.string().uuid().describe("The Rank Radar UUID to pause (from `list_rank_radars`).")
|
|
773
844
|
};
|
|
774
845
|
var pauseRankRadarTool = {
|
|
775
846
|
name: "pause_rank_radar",
|
|
776
847
|
title: "Pause a Rank Radar",
|
|
777
848
|
description: "Use this to pause tracking on a Rank Radar without losing anything \u2014 the action the DataDive API calls 'archiving', so when a user asks to archive a Rank Radar, this is the tool they mean. Tracking stops for all of its keywords and the Daily Tracked Keywords quota they held is freed for other Rank Radars, while the Rank Radar and its ranking history are kept. Fully reversible with `resume_rank_radar`, so it needs no `confirm` \u2014 prefer it over `delete_rank_radar` whenever the user may want the data back. Paused Rank Radars show up in `list_rank_radars` under `status: PAUSED` \u2014 not `ARCHIVED`, which covers Rank Radars removed with `delete_rank_radar`. Safe to repeat: pausing an already paused Rank Radar succeeds and changes nothing.",
|
|
778
|
-
inputSchema:
|
|
849
|
+
inputSchema: inputSchema16,
|
|
779
850
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
780
851
|
handler: async (args, ctx) => {
|
|
781
852
|
await httpPost(
|
|
@@ -788,15 +859,15 @@ var pauseRankRadarTool = {
|
|
|
788
859
|
};
|
|
789
860
|
|
|
790
861
|
// src/tools/resume-rank-radar.ts
|
|
791
|
-
import { z as
|
|
792
|
-
var
|
|
793
|
-
rankRadarId:
|
|
862
|
+
import { z as z16 } from "zod";
|
|
863
|
+
var inputSchema17 = {
|
|
864
|
+
rankRadarId: z16.string().uuid().describe("The Rank Radar UUID to resume (from `list_rank_radars`).")
|
|
794
865
|
};
|
|
795
866
|
var resumeRankRadarTool = {
|
|
796
867
|
name: "resume_rank_radar",
|
|
797
868
|
title: "Resume a Paused Rank Radar",
|
|
798
869
|
description: "Use this to restart tracking on a Rank Radar that was paused with `pause_rank_radar` (shown by `list_rank_radars` under `status: PAUSED`; a deleted, `ARCHIVED` one cannot be resumed). Keywords are resumed as far as the available Daily Tracked Keywords quota allows, most relevant first \u2014 so with a tight quota only part of the original keyword set comes back; check `get_quota` first if that matters. Reversible with `pause_rank_radar`, so it needs no `confirm`. Fails with a bad-request error when there is no quota left at all. Safe to repeat: resuming an already active Rank Radar changes nothing.",
|
|
799
|
-
inputSchema:
|
|
870
|
+
inputSchema: inputSchema17,
|
|
800
871
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
801
872
|
handler: async (args, ctx) => {
|
|
802
873
|
await httpPost(
|
|
@@ -809,10 +880,10 @@ var resumeRankRadarTool = {
|
|
|
809
880
|
};
|
|
810
881
|
|
|
811
882
|
// src/tools/delete-rank-radar.ts
|
|
812
|
-
import { z as
|
|
813
|
-
var
|
|
814
|
-
rankRadarId:
|
|
815
|
-
confirm:
|
|
883
|
+
import { z as z17 } from "zod";
|
|
884
|
+
var inputSchema18 = {
|
|
885
|
+
rankRadarId: z17.string().uuid().describe("The Rank Radar UUID to delete (from `list_rank_radars`)."),
|
|
886
|
+
confirm: z17.boolean().optional().describe(
|
|
816
887
|
"Must be true to proceed \u2014 deleting a Rank Radar destroys its ranking history permanently. Confirm with the user first."
|
|
817
888
|
)
|
|
818
889
|
};
|
|
@@ -820,7 +891,7 @@ var deleteRankRadarTool = {
|
|
|
820
891
|
name: "delete_rank_radar",
|
|
821
892
|
title: "Delete a Rank Radar",
|
|
822
893
|
description: "Use this to permanently remove a Rank Radar and all of its keyword ranking history. \u26A0\uFE0F Cannot be undone \u2014 there is no restore endpoint; set `confirm: true` only after the user approves. It does free the Daily Tracked Keywords quota those keywords held. Deleted Rank Radars are what `list_rank_radars` returns for `status: ARCHIVED`. If the user only wants to stop tracking for a while and keep the history \u2014 or asks to 'archive' the Rank Radar, which is what the DataDive API calls pausing \u2014 use `pause_rank_radar` instead: it frees the same quota and is reversible with `resume_rank_radar`.",
|
|
823
|
-
inputSchema:
|
|
894
|
+
inputSchema: inputSchema18,
|
|
824
895
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
825
896
|
handler: async (args, ctx) => {
|
|
826
897
|
const pending = requireConfirmation(
|
|
@@ -838,10 +909,10 @@ var deleteRankRadarTool = {
|
|
|
838
909
|
};
|
|
839
910
|
|
|
840
911
|
// src/tools/add-rank-radar-search-terms.ts
|
|
841
|
-
import { z as
|
|
842
|
-
var
|
|
843
|
-
rankRadarId:
|
|
844
|
-
searchTerms:
|
|
912
|
+
import { z as z18 } from "zod";
|
|
913
|
+
var inputSchema19 = {
|
|
914
|
+
rankRadarId: z18.string().uuid().describe("The Rank Radar UUID to add the search terms to (from `list_rank_radars`)."),
|
|
915
|
+
searchTerms: z18.array(z18.string().min(1)).min(1).max(ADD_SEARCH_TERMS_MAX).describe(
|
|
845
916
|
`The keywords to start tracking, 1\u2013${ADD_SEARCH_TERMS_MAX} per call. Must be unique. Amazon-ignored special characters (@, #, _, parentheses, \u2026) are rejected.`
|
|
846
917
|
)
|
|
847
918
|
};
|
|
@@ -849,7 +920,7 @@ var addRankRadarSearchTermsTool = {
|
|
|
849
920
|
name: "add_rank_radar_search_terms",
|
|
850
921
|
title: "Add Search Terms to a Rank Radar",
|
|
851
922
|
description: "Use this to start tracking extra keywords on an existing Rank Radar, instead of creating a new one with `create_rank_radar`. New keywords are added to the niche and tracked from now on; any matching keyword that was previously paused is resumed. Each newly tracked keyword takes one Daily Tracked Keywords slot, and the call fails if the quota is exhausted \u2014 check `get_quota` first. Reversible with `pause_rank_radar_search_terms`, which frees the slots again, so it needs no `confirm`. Returns `originBreakdown` (where each submitted term ended up) and `keywordToRankRadarKeywordIdMap`, whose ids are what the pause/resume search-term tools take.",
|
|
852
|
-
inputSchema:
|
|
923
|
+
inputSchema: inputSchema19,
|
|
853
924
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
854
925
|
handler: async (args, ctx) => {
|
|
855
926
|
return await httpPost(
|
|
@@ -861,10 +932,10 @@ var addRankRadarSearchTermsTool = {
|
|
|
861
932
|
};
|
|
862
933
|
|
|
863
934
|
// src/tools/pause-rank-radar-search-terms.ts
|
|
864
|
-
import { z as
|
|
865
|
-
var
|
|
866
|
-
rankRadarId:
|
|
867
|
-
rankRadarKeywordIds:
|
|
935
|
+
import { z as z19 } from "zod";
|
|
936
|
+
var inputSchema20 = {
|
|
937
|
+
rankRadarId: z19.string().uuid().describe("The Rank Radar UUID the search terms belong to."),
|
|
938
|
+
rankRadarKeywordIds: z19.array(z19.string().uuid()).min(1).describe(
|
|
868
939
|
"The Rank Radar keyword ids to pause. These are the `id` values from `get_rank_radar_data`, or the values of `keywordToRankRadarKeywordIdMap` returned by `add_rank_radar_search_terms` \u2014 not the keyword text."
|
|
869
940
|
)
|
|
870
941
|
};
|
|
@@ -872,7 +943,7 @@ var pauseRankRadarSearchTermsTool = {
|
|
|
872
943
|
name: "pause_rank_radar_search_terms",
|
|
873
944
|
title: "Pause Search Terms of a Rank Radar",
|
|
874
945
|
description: "Use this to stop tracking individual keywords on a Rank Radar while keeping the Rank Radar itself active \u2014 the way to trim a keyword set the user finds too broad. The DataDive API calls this 'archiving' the search terms; nothing is lost. The paused keywords keep their history and their Daily Tracked Keywords slots are freed for other keywords. Reversible with `resume_rank_radar_search_terms`, so it needs no `confirm`. Takes keyword ids, not keyword text: get them from `get_rank_radar_data`. Safe to repeat: already-paused terms are left unchanged.",
|
|
875
|
-
inputSchema:
|
|
946
|
+
inputSchema: inputSchema20,
|
|
876
947
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
877
948
|
handler: async (args, ctx) => {
|
|
878
949
|
await httpPost(
|
|
@@ -889,10 +960,10 @@ var pauseRankRadarSearchTermsTool = {
|
|
|
889
960
|
};
|
|
890
961
|
|
|
891
962
|
// src/tools/resume-rank-radar-search-terms.ts
|
|
892
|
-
import { z as
|
|
893
|
-
var
|
|
894
|
-
rankRadarId:
|
|
895
|
-
rankRadarKeywordIds:
|
|
963
|
+
import { z as z20 } from "zod";
|
|
964
|
+
var inputSchema21 = {
|
|
965
|
+
rankRadarId: z20.string().uuid().describe("The Rank Radar UUID the search terms belong to."),
|
|
966
|
+
rankRadarKeywordIds: z20.array(z20.string().uuid()).min(1).describe(
|
|
896
967
|
"The Rank Radar keyword ids to resume. These are the `id` values from `get_rank_radar_data`, or the values of `keywordToRankRadarKeywordIdMap` returned by `add_rank_radar_search_terms` \u2014 not the keyword text."
|
|
897
968
|
)
|
|
898
969
|
};
|
|
@@ -900,7 +971,7 @@ var resumeRankRadarSearchTermsTool = {
|
|
|
900
971
|
name: "resume_rank_radar_search_terms",
|
|
901
972
|
title: "Resume Paused Search Terms of a Rank Radar",
|
|
902
973
|
description: "Use this to start tracking keywords again that were paused with `pause_rank_radar_search_terms`. Each resumed keyword takes back one Daily Tracked Keywords slot, and the call fails if the quota is exhausted \u2014 check `get_quota` first. Reversible with `pause_rank_radar_search_terms`, so it needs no `confirm`. Takes keyword ids, not keyword text: get them from `get_rank_radar_data`. Safe to repeat: already-active terms are left unchanged.",
|
|
903
|
-
inputSchema:
|
|
974
|
+
inputSchema: inputSchema21,
|
|
904
975
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
905
976
|
handler: async (args, ctx) => {
|
|
906
977
|
await httpPost(
|
|
@@ -917,16 +988,16 @@ var resumeRankRadarSearchTermsTool = {
|
|
|
917
988
|
};
|
|
918
989
|
|
|
919
990
|
// src/tools/generate-listing-copy.ts
|
|
920
|
-
import { z as
|
|
921
|
-
var
|
|
922
|
-
nicheId:
|
|
923
|
-
strategy:
|
|
991
|
+
import { z as z21 } from "zod";
|
|
992
|
+
var inputSchema22 = {
|
|
993
|
+
nicheId: z21.string().min(1).describe("The niche whose keyword data the copy is written from. Get one from `list_niches`."),
|
|
994
|
+
strategy: z21.enum(LISTING_COPY_STRATEGIES).describe(
|
|
924
995
|
"Which generation strategy to use. `cosmo` targets Amazon's COSMO relevance model, `ranking-juice` optimises for DataDive's ranking-juice score, `nlp` writes for keyword coverage, and `cosmo-rufus` targets COSMO plus the Rufus shopping assistant. Ask the user if they have no preference."
|
|
925
996
|
),
|
|
926
|
-
currentListing:
|
|
997
|
+
currentListing: z21.record(z21.union([z21.string(), z21.array(z21.string())])).describe(
|
|
927
998
|
'The seller\'s existing listing to rewrite, as an object \u2014 e.g. `{ "title": "...", "description": "...", "bullets": ["...", "..."] }`. Pass what the user has; it is the starting point the generated copy builds on.'
|
|
928
999
|
),
|
|
929
|
-
confirm:
|
|
1000
|
+
confirm: z21.boolean().optional().describe(
|
|
930
1001
|
"Must be true to proceed \u2014 each generation spends one AI Copywriter prompt from the quota. Confirm the cost with the user first."
|
|
931
1002
|
)
|
|
932
1003
|
};
|
|
@@ -934,7 +1005,7 @@ var generateListingCopyTool = {
|
|
|
934
1005
|
name: "generate_listing_copy",
|
|
935
1006
|
title: "Generate Listing Copy for a Niche",
|
|
936
1007
|
description: "Use this to draft an optimised Amazon listing \u2014 title, bullets and description \u2014 from a niche's keyword research and the seller's current listing. It writes text only: nothing is published to Amazon, and the user still has to paste the result into Seller Central. \u26A0\uFE0F Spends one AI Copywriter prompt from the quota per call and cannot be undone \u2014 set `confirm: true` only after the user approves. Not safe to retry: each call spends another prompt, so if a call errors or times out, poll `get_listing_copy_generation_status` instead of re-calling. Runs asynchronously: returns a `generationId` \u2014 poll `get_listing_copy_generation_status` with it until the status is `complete`.",
|
|
937
|
-
inputSchema:
|
|
1008
|
+
inputSchema: inputSchema22,
|
|
938
1009
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
939
1010
|
handler: async (args, ctx) => {
|
|
940
1011
|
const pending = requireConfirmation(
|
|
@@ -952,16 +1023,16 @@ var generateListingCopyTool = {
|
|
|
952
1023
|
};
|
|
953
1024
|
|
|
954
1025
|
// src/tools/get-listing-copy-generation-status.ts
|
|
955
|
-
import { z as
|
|
956
|
-
var
|
|
957
|
-
nicheId:
|
|
958
|
-
generationId:
|
|
1026
|
+
import { z as z22 } from "zod";
|
|
1027
|
+
var inputSchema23 = {
|
|
1028
|
+
nicheId: z22.string().min(1).describe("The niche the generation was started for."),
|
|
1029
|
+
generationId: z22.string().min(1).describe("The `generationId` returned by `generate_listing_copy`.")
|
|
959
1030
|
};
|
|
960
1031
|
var getListingCopyGenerationStatusTool = {
|
|
961
1032
|
name: "get_listing_copy_generation_status",
|
|
962
1033
|
title: "Get Listing Copy Generation Status",
|
|
963
1034
|
description: "Use this to poll a listing-copy draft started with `generate_listing_copy` until it finishes. Returns one of three shapes keyed by `status`: `generating` (still running \u2014 poll again in a few seconds), `complete` (with `result`, carrying the generated `title`, `bullets`, `description`, `itemHighlights` and its `rankingJuice` score), or `failed` (with an `error` message). Polling is free \u2014 it does not spend another AI Copywriter prompt, so always poll rather than re-calling `generate_listing_copy`.",
|
|
964
|
-
inputSchema:
|
|
1035
|
+
inputSchema: inputSchema23,
|
|
965
1036
|
annotations: { readOnlyHint: true },
|
|
966
1037
|
handler: async (args, ctx) => {
|
|
967
1038
|
return await httpGet(
|
|
@@ -972,16 +1043,16 @@ var getListingCopyGenerationStatusTool = {
|
|
|
972
1043
|
};
|
|
973
1044
|
|
|
974
1045
|
// src/tools/list-seller-profiles.ts
|
|
975
|
-
import { z as
|
|
976
|
-
var
|
|
977
|
-
currentPage:
|
|
978
|
-
pageSize:
|
|
1046
|
+
import { z as z23 } from "zod";
|
|
1047
|
+
var inputSchema24 = {
|
|
1048
|
+
currentPage: z23.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
1049
|
+
pageSize: z23.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
979
1050
|
};
|
|
980
1051
|
var listSellerProfilesTool = {
|
|
981
1052
|
name: "list_seller_profiles",
|
|
982
1053
|
title: "List Connected Amazon Seller Profiles",
|
|
983
1054
|
description: 'Use this when the user asks which Amazon seller accounts are connected, or as the discovery step to find the `sellerId` and `marketplace` required by `get_asin_inventory_distribution`, `get_seller_catalog`, `get_seller_listing_changes`, and the alert tools. Returns a paginated list of the organization\'s connected seller profiles \u2014 each item has sellerId, sellerName, marketplace (e.g. "com", "co.uk"), hasAdApi (whether Advertising API credentials are connected), and createdAt \u2014 plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).',
|
|
984
|
-
inputSchema:
|
|
1055
|
+
inputSchema: inputSchema24,
|
|
985
1056
|
annotations: { readOnlyHint: true },
|
|
986
1057
|
handler: async (args, ctx) => {
|
|
987
1058
|
return await httpGet(
|
|
@@ -996,15 +1067,15 @@ var listSellerProfilesTool = {
|
|
|
996
1067
|
};
|
|
997
1068
|
|
|
998
1069
|
// src/tools/get-seller-catalog.ts
|
|
999
|
-
import { z as
|
|
1070
|
+
import { z as z25 } from "zod";
|
|
1000
1071
|
|
|
1001
1072
|
// src/tools/seller-scoped.ts
|
|
1002
|
-
import { z as
|
|
1073
|
+
import { z as z24 } from "zod";
|
|
1003
1074
|
var sellerScopedInputSchema = {
|
|
1004
|
-
sellerId:
|
|
1075
|
+
sellerId: z24.string().min(1).describe(
|
|
1005
1076
|
"Amazon seller account ID. Get it from `list_seller_profiles`, or find it on the Connections page at https://2.datadive.tools."
|
|
1006
1077
|
),
|
|
1007
|
-
marketplace:
|
|
1078
|
+
marketplace: z24.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.')
|
|
1008
1079
|
};
|
|
1009
1080
|
function sellerMarketplacePath(sellerId, marketplace, suffix) {
|
|
1010
1081
|
return `/v1/seller_profiles/${encodeURIComponent(sellerId)}/marketplaces/${encodeURIComponent(
|
|
@@ -1013,19 +1084,19 @@ function sellerMarketplacePath(sellerId, marketplace, suffix) {
|
|
|
1013
1084
|
}
|
|
1014
1085
|
|
|
1015
1086
|
// src/tools/get-seller-catalog.ts
|
|
1016
|
-
var
|
|
1087
|
+
var inputSchema25 = {
|
|
1017
1088
|
...sellerScopedInputSchema,
|
|
1018
|
-
search:
|
|
1019
|
-
brand:
|
|
1020
|
-
status:
|
|
1021
|
-
currentPage:
|
|
1022
|
-
pageSize:
|
|
1089
|
+
search: z25.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
|
|
1090
|
+
brand: z25.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
|
|
1091
|
+
status: z25.enum(CATALOG_STATUSES).optional().describe('Listing status filter. "Active" (default) or "all".'),
|
|
1092
|
+
currentPage: z25.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
1093
|
+
pageSize: z25.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
1023
1094
|
};
|
|
1024
1095
|
var getSellerCatalogTool = {
|
|
1025
1096
|
name: "get_seller_catalog",
|
|
1026
1097
|
title: "List Catalog ASINs for a Seller",
|
|
1027
1098
|
description: "Use this when the user wants to browse or search a seller's own Amazon catalog \u2014 the products they sell on a given marketplace. Requires a `sellerId` + `marketplace` (use `list_seller_profiles` to discover them). Filter by `search` (title/brand), `brand`, and `status` (Active by default, or all). Returns a paginated list where each item has asin, title, parentAsin, brand, status, imageUrl, and hasVariations, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
|
|
1028
|
-
inputSchema:
|
|
1099
|
+
inputSchema: inputSchema25,
|
|
1029
1100
|
annotations: { readOnlyHint: true },
|
|
1030
1101
|
handler: async (args, ctx) => {
|
|
1031
1102
|
return await httpGet(
|
|
@@ -1043,28 +1114,28 @@ var getSellerCatalogTool = {
|
|
|
1043
1114
|
};
|
|
1044
1115
|
|
|
1045
1116
|
// src/tools/get-seller-listing-changes.ts
|
|
1046
|
-
import { z as
|
|
1047
|
-
var
|
|
1117
|
+
import { z as z26 } from "zod";
|
|
1118
|
+
var inputSchema26 = {
|
|
1048
1119
|
...sellerScopedInputSchema,
|
|
1049
|
-
types:
|
|
1050
|
-
asin:
|
|
1051
|
-
brand:
|
|
1052
|
-
search:
|
|
1053
|
-
startDate:
|
|
1054
|
-
endDate:
|
|
1055
|
-
sortBy:
|
|
1056
|
-
sortOrder:
|
|
1057
|
-
includeCorrelations:
|
|
1120
|
+
types: z26.array(z26.enum(LISTING_CHANGE_TYPES)).min(1).optional().describe('Filter to specific change types. Omit for all. Any of "Price", "Content", "Image".'),
|
|
1121
|
+
asin: z26.string().min(1).optional().describe("Filter to an exact ASIN or parent ASIN."),
|
|
1122
|
+
brand: z26.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
|
|
1123
|
+
search: z26.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
|
|
1124
|
+
startDate: z26.string().regex(ISO_8601, "startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)").optional().describe("ISO-8601 date/timestamp; return only changes detected on or after this time."),
|
|
1125
|
+
endDate: z26.string().regex(ISO_8601, "endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)").optional().describe("ISO-8601 date/timestamp; return only changes detected on or before this time."),
|
|
1126
|
+
sortBy: z26.enum(LISTING_CHANGE_SORT_BY).optional().describe('Sort field. "date" (default) or "type".'),
|
|
1127
|
+
sortOrder: z26.enum(SORT_ORDER).optional().describe('Sort direction. "DESC" (default) or "ASC".'),
|
|
1128
|
+
includeCorrelations: z26.boolean().optional().describe(
|
|
1058
1129
|
"When true, include the ranking/conversion `correlation` object per change (CVR and search-term movement before vs after). Defaults to false."
|
|
1059
1130
|
),
|
|
1060
|
-
currentPage:
|
|
1061
|
-
pageSize:
|
|
1131
|
+
currentPage: z26.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
1132
|
+
pageSize: z26.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
1062
1133
|
};
|
|
1063
1134
|
var getSellerListingChangesTool = {
|
|
1064
1135
|
name: "get_seller_listing_changes",
|
|
1065
1136
|
title: "List Listing Changes for a Seller",
|
|
1066
1137
|
description: "Use this when the user asks what changed on their Amazon listings \u2014 price, content (title/bullets/description), or image edits \u2014 for a seller account. Requires a `sellerId` + `marketplace` (use `list_seller_profiles` to discover them). Filter by `types`, `asin`/parent ASIN, `brand`, `search`, and a `startDate`/`endDate` range; sort with `sortBy`/`sortOrder`. Set `includeCorrelations: true` to attach the ranking/conversion impact per change. Returns a paginated list where each item has asin, title, imageUrl, date, type, contentType, description, previousValue, newValue, and (optionally) correlation, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
|
|
1067
|
-
inputSchema:
|
|
1138
|
+
inputSchema: inputSchema26,
|
|
1068
1139
|
annotations: { readOnlyHint: true },
|
|
1069
1140
|
handler: async (args, ctx) => {
|
|
1070
1141
|
return await httpGet(
|
|
@@ -1089,40 +1160,40 @@ var getSellerListingChangesTool = {
|
|
|
1089
1160
|
};
|
|
1090
1161
|
|
|
1091
1162
|
// src/tools/list-ppc-campaigns.ts
|
|
1092
|
-
import { z as
|
|
1163
|
+
import { z as z27 } from "zod";
|
|
1093
1164
|
var PPC_CAMPAIGNS_MAX_WINDOW_DAYS = 90;
|
|
1094
1165
|
var PPC_CAMPAIGNS_MAX_PAGE_SIZE = 50;
|
|
1095
|
-
var
|
|
1166
|
+
var inputSchema27 = {
|
|
1096
1167
|
...sellerScopedInputSchema,
|
|
1097
|
-
startDate:
|
|
1168
|
+
startDate: z27.string().regex(
|
|
1098
1169
|
ISO_8601,
|
|
1099
1170
|
"startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)"
|
|
1100
1171
|
).optional().describe("Start of the reporting window, ISO-8601. Defaults to 30 days before endDate."),
|
|
1101
|
-
endDate:
|
|
1172
|
+
endDate: z27.string().regex(
|
|
1102
1173
|
ISO_8601,
|
|
1103
1174
|
"endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)"
|
|
1104
1175
|
).optional().describe(
|
|
1105
1176
|
`End of the reporting window, ISO-8601. Defaults to now. At most ${PPC_CAMPAIGNS_MAX_WINDOW_DAYS} days after startDate.`
|
|
1106
1177
|
),
|
|
1107
|
-
asin:
|
|
1108
|
-
parentAsin:
|
|
1109
|
-
search:
|
|
1110
|
-
includePlacements:
|
|
1178
|
+
asin: z27.string().min(1).optional().describe("Only campaigns advertising this ASIN. Cannot be combined with parentAsin."),
|
|
1179
|
+
parentAsin: z27.string().min(1).optional().describe("Only campaigns advertising any ASIN of this variation family. Cannot be combined with asin."),
|
|
1180
|
+
search: z27.string().min(3).max(200).optional().describe("Partial match on campaign name or advertised ASIN. At least 3 characters."),
|
|
1181
|
+
includePlacements: z27.boolean().optional().describe(
|
|
1111
1182
|
"Include the per-placement breakdown (topOfSearch, restOfSearch, productPage, offAmazon, each with its bid adjustment). Defaults to true; set false for a smaller response when placements are not needed."
|
|
1112
1183
|
),
|
|
1113
|
-
sortBy:
|
|
1184
|
+
sortBy: z27.enum([...PPC_CAMPAIGN_BASE_SORT_BY, ...PPC_CAMPAIGN_PLACEMENT_SORT_BY]).optional().describe(
|
|
1114
1185
|
`Sort field. Defaults to "name". Any of ${PPC_CAMPAIGN_BASE_SORT_BY.join(", ")}. The placement fields (e.g. "tosSpend", "tosAcos", "ppBidAdjustment") are only accepted when includePlacements is not false.`
|
|
1115
1186
|
),
|
|
1116
|
-
sortOrder:
|
|
1117
|
-
state:
|
|
1118
|
-
currentPage:
|
|
1119
|
-
pageSize:
|
|
1187
|
+
sortOrder: z27.enum(SORT_ORDER).optional().describe('Sort direction. "ASC" (default) or "DESC".'),
|
|
1188
|
+
state: z27.enum(PPC_CAMPAIGN_STATES).optional().describe("Only ENABLED or only PAUSED campaigns. Omit for both."),
|
|
1189
|
+
currentPage: z27.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
1190
|
+
pageSize: z27.number().int().min(1).max(PPC_CAMPAIGNS_MAX_PAGE_SIZE).optional().describe(`Campaigns per page (max ${PPC_CAMPAIGNS_MAX_PAGE_SIZE}). Defaults to 20.`)
|
|
1120
1191
|
};
|
|
1121
1192
|
var listPpcCampaignsTool = {
|
|
1122
1193
|
name: "list_ppc_campaigns",
|
|
1123
1194
|
title: "List PPC Campaigns for a Seller",
|
|
1124
1195
|
description: `Use this when the user asks about their Sponsored Products campaigns \u2014 which campaigns spend the most, which have a high ACOS, how placements (top of search, rest of search, product pages) perform, or which campaigns advertise a given product. Requires a \`sellerId\` + \`marketplace\` (use \`list_seller_profiles\` to discover them). Narrow to one product with \`asin\`, or to a variation family with \`parentAsin\` (not both). The reporting window defaults to the last 30 days and cannot exceed ${PPC_CAMPAIGNS_MAX_WINDOW_DAYS} days. Returns a paginated list where each campaign has campaignId, name, type, state, targetingType, bidStrategy, budget { type, amount, currencyCode }, window totals (impressions, clicks, ctr, cpc, cvr, spend, unitsSold, orders, sales, tosImpressionShare, acos, roas, tacos), asinCount and, unless includePlacements is false, \`placements\`; plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev). To find the biggest spenders, sort with \`sortBy: "spend", sortOrder: "DESC"\` rather than paging through every campaign. Each call counts toward API usage and is rate limited (about 60 requests/minute). For per-keyword ad data of a Rank Radar, use \`get_rank_radar_ppc_data\`.`,
|
|
1125
|
-
inputSchema:
|
|
1196
|
+
inputSchema: inputSchema27,
|
|
1126
1197
|
annotations: { readOnlyHint: true },
|
|
1127
1198
|
handler: async (args, ctx) => {
|
|
1128
1199
|
if (args.asin && args.parentAsin) {
|
|
@@ -1153,13 +1224,13 @@ var listPpcCampaignsTool = {
|
|
|
1153
1224
|
};
|
|
1154
1225
|
|
|
1155
1226
|
// src/tools/get-asin-inventory-distribution.ts
|
|
1156
|
-
import { z as
|
|
1157
|
-
var
|
|
1158
|
-
sellerId:
|
|
1227
|
+
import { z as z28 } from "zod";
|
|
1228
|
+
var inputSchema28 = {
|
|
1229
|
+
sellerId: z28.string().min(1).describe(
|
|
1159
1230
|
"Amazon seller account ID as connected to DataDive. Get it from `list_seller_profiles`, or find it on the Connections page at https://2.datadive.tools."
|
|
1160
1231
|
),
|
|
1161
|
-
marketplace:
|
|
1162
|
-
asin:
|
|
1232
|
+
marketplace: z28.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.'),
|
|
1233
|
+
asin: z28.string().regex(/^[A-Z0-9]{10}$/, "ASIN must be 10 uppercase alphanumeric characters (e.g. B08XYZ1234).").describe(
|
|
1163
1234
|
"Amazon Standard Identification Number (10 uppercase alphanumeric characters, e.g. B08XYZ1234)."
|
|
1164
1235
|
)
|
|
1165
1236
|
};
|
|
@@ -1167,7 +1238,7 @@ var getAsinInventoryDistributionTool = {
|
|
|
1167
1238
|
name: "get_asin_inventory_distribution",
|
|
1168
1239
|
title: "Get Per-Fulfillment-Center Inventory Distribution for an ASIN",
|
|
1169
1240
|
description: "Use this when the user asks about inventory levels, stock by fulfillment center, or where their units are sitting across Amazon's fulfillment network for a specific ASIN. Requires the Amazon sellerId (from `list_seller_profiles` or the user's DataDive Connections page at https://2.datadive.tools), the marketplace code, and the ASIN. Returns totalSellableUnits and a per-FC `distribution` array (fc, state, availableStock, availableStockPercentage). `lastUpdatedAt` may be null when no successful inventory ingestion has occurred in the last 30 days.",
|
|
1170
|
-
inputSchema:
|
|
1241
|
+
inputSchema: inputSchema28,
|
|
1171
1242
|
annotations: { readOnlyHint: true },
|
|
1172
1243
|
handler: async (args, ctx) => {
|
|
1173
1244
|
return await httpGet(
|
|
@@ -1180,18 +1251,18 @@ var getAsinInventoryDistributionTool = {
|
|
|
1180
1251
|
};
|
|
1181
1252
|
|
|
1182
1253
|
// src/tools/alert-query.ts
|
|
1183
|
-
import { z as
|
|
1254
|
+
import { z as z29 } from "zod";
|
|
1184
1255
|
var alertsQueryInputSchema = {
|
|
1185
|
-
sellerId:
|
|
1186
|
-
marketplace:
|
|
1187
|
-
status:
|
|
1256
|
+
sellerId: z29.string().min(1).max(50).optional().describe('Filter to a single connected Amazon seller account ID, e.g. "A1B2C3D4E5".'),
|
|
1257
|
+
marketplace: z29.enum(SUPPORTED_MARKETPLACES).optional().describe('Filter to a single Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.'),
|
|
1258
|
+
status: z29.enum(ALERT_STATUSES).optional().describe(
|
|
1188
1259
|
"Lifecycle filter. `active` (default) returns unresolved alerts, `resolved` returns resolved alerts, `all` returns both. Dismissed alerts are never returned."
|
|
1189
1260
|
),
|
|
1190
|
-
updatedSince:
|
|
1261
|
+
updatedSince: z29.string().regex(ISO_8601, "updatedSince must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)").optional().describe(
|
|
1191
1262
|
"ISO-8601 timestamp; return only alerts surfaced at or after this time (filters on `lastAlertedAt`). Use it to incrementally sync since your last poll. Defaults to the last 30 days when omitted."
|
|
1192
1263
|
),
|
|
1193
|
-
currentPage:
|
|
1194
|
-
pageSize:
|
|
1264
|
+
currentPage: z29.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
1265
|
+
pageSize: z29.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
1195
1266
|
};
|
|
1196
1267
|
|
|
1197
1268
|
// src/tools/list-indexing-issue-alerts.ts
|
|
@@ -1241,12 +1312,12 @@ var listBlindSpendAlertsTool = {
|
|
|
1241
1312
|
};
|
|
1242
1313
|
|
|
1243
1314
|
// src/tools/get-quota.ts
|
|
1244
|
-
var
|
|
1315
|
+
var inputSchema29 = {};
|
|
1245
1316
|
var getQuotaTool = {
|
|
1246
1317
|
name: "get_quota",
|
|
1247
1318
|
title: "Get Quota Usage",
|
|
1248
1319
|
description: "Use this when the user asks how much quota or how many tokens they have left, or before creating a Niche Dive or Rank Radar to check remaining headroom. Returns current quota usage and capacity for each billable feature \u2014 DIVED_ASINS (Dive tokens), RANK_RADAR_KEYWORDS (tracked keywords), PRODUCT_BRIEF_ASINS, and AI_COPYWRITER_PROMPTS \u2014 each with `used` and `capacity` (null when unlimited / not applicable), plus `nextRefreshDate` (ISO-8601 timestamp of the next quota reset, or null). Takes no arguments.",
|
|
1249
|
-
inputSchema:
|
|
1320
|
+
inputSchema: inputSchema29,
|
|
1250
1321
|
annotations: { readOnlyHint: true },
|
|
1251
1322
|
handler: async (_args, ctx) => {
|
|
1252
1323
|
return await httpGet({ config: ctx.config, toolName: "get_quota" }, "/v1/quota");
|
|
@@ -1254,20 +1325,20 @@ var getQuotaTool = {
|
|
|
1254
1325
|
};
|
|
1255
1326
|
|
|
1256
1327
|
// src/tools/list-usage.ts
|
|
1257
|
-
import { z as
|
|
1258
|
-
var
|
|
1259
|
-
type:
|
|
1260
|
-
search:
|
|
1261
|
-
startDate:
|
|
1262
|
-
endDate:
|
|
1263
|
-
currentPage:
|
|
1264
|
-
pageSize:
|
|
1328
|
+
import { z as z30 } from "zod";
|
|
1329
|
+
var inputSchema30 = {
|
|
1330
|
+
type: z30.enum(BILLABLE_FEATURE_TYPES).optional().describe("Filter to a single billable feature type. Omit to return all types."),
|
|
1331
|
+
search: z30.string().min(1).optional().describe("Case-insensitive partial match on the user's name or email."),
|
|
1332
|
+
startDate: z30.string().regex(ISO_8601, "startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)").optional().describe("ISO-8601 date/timestamp; return only usage logs recorded on or after this time."),
|
|
1333
|
+
endDate: z30.string().regex(ISO_8601, "endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)").optional().describe("ISO-8601 date/timestamp; return only usage logs recorded on or before this time."),
|
|
1334
|
+
currentPage: z30.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
1335
|
+
pageSize: z30.number().int().min(1).max(200).optional().describe("Items per page (max 200). Defaults to 50.")
|
|
1265
1336
|
};
|
|
1266
1337
|
var listUsageTool = {
|
|
1267
1338
|
name: "list_usage",
|
|
1268
1339
|
title: "List Billable Usage Logs",
|
|
1269
1340
|
description: "Use this when the user asks who consumed tokens, how their quota was spent, or wants an audit of billable activity over a date range. Retrieves a paginated list of billable feature usage logs for the organization \u2014 each entry is a token-consumption event (a dive, rank-radar creation, AI copywriter prompt, etc.). Filter by `type` (billable feature), `search` (user name/email), and `startDate`/`endDate`. Each item includes name, email, qty (tokens consumed), type, action (specific operation), nicheId/nicheName, rankRadarId, and date, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
|
|
1270
|
-
inputSchema:
|
|
1341
|
+
inputSchema: inputSchema30,
|
|
1271
1342
|
annotations: { readOnlyHint: true },
|
|
1272
1343
|
handler: async (args, ctx) => {
|
|
1273
1344
|
return await httpGet(
|
|
@@ -1298,6 +1369,7 @@ var allTools = [
|
|
|
1298
1369
|
getRankRadarPpcDataTool,
|
|
1299
1370
|
createRankRadarTool,
|
|
1300
1371
|
createNicheDiveTool,
|
|
1372
|
+
createNicheDiveFromCompetitorsListTool,
|
|
1301
1373
|
rediveNicheTool,
|
|
1302
1374
|
getDiveStatusTool,
|
|
1303
1375
|
addRankRadarSearchTermsTool,
|
|
@@ -1322,6 +1394,20 @@ var allTools = [
|
|
|
1322
1394
|
|
|
1323
1395
|
// src/whats-new.ts
|
|
1324
1396
|
var WHATS_NEW = [
|
|
1397
|
+
{
|
|
1398
|
+
version: "0.16.1",
|
|
1399
|
+
released: "2026-09-29",
|
|
1400
|
+
items: [
|
|
1401
|
+
"`get_rank_radar_data` returns every tracked keyword of a Rank Radar in one answer again, also in AI apps that still show its older inputs. Pass `currentPage` / `pageSize` to read one page at a time."
|
|
1402
|
+
]
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
version: "0.16.0",
|
|
1406
|
+
released: "2026-09-28",
|
|
1407
|
+
items: [
|
|
1408
|
+
"Niche research from your own competitor list (`create_niche_dive_from_competitors_list`): give 2 to 200 ASINs and the niche is built from exactly those products, with no automatic competitor search."
|
|
1409
|
+
]
|
|
1410
|
+
},
|
|
1325
1411
|
{
|
|
1326
1412
|
version: "0.15.0",
|
|
1327
1413
|
released: "2026-09-28",
|