@datadive-tools/mcp 0.15.0 → 0.16.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/dist/index.js CHANGED
@@ -144,7 +144,7 @@ function extractMessage(body) {
144
144
  }
145
145
 
146
146
  // package.json
147
- var version = "0.15.0";
147
+ var version = "0.16.0";
148
148
 
149
149
  // src/http/client.ts
150
150
  var PKG_VERSION = version;
@@ -666,24 +666,65 @@ var createNicheDiveTool = {
666
666
  }
667
667
  };
668
668
 
669
- // src/tools/redive-niche.ts
669
+ // src/tools/create-niche-dive-from-competitors-list.ts
670
670
  import { z as z11 } from "zod";
671
+ var DIVE_FROM_COMPETITORS_MIN_ASINS = 2;
672
+ var DIVE_FROM_COMPETITORS_MAX_ASINS = 200;
673
+ 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, {
674
+ message: "asins must not contain the same ASIN twice"
675
+ });
671
676
  var inputSchema12 = {
672
- nicheId: z11.string().min(1).describe("The niche to re-dive. Get one from `list_niches`."),
673
- mode: z11.enum(["same_competitors", "discover"]).describe(
677
+ marketplace: z11.enum(SUPPORTED_MARKETPLACES).describe(
678
+ "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`)."
679
+ ),
680
+ asins: normalizedAsins.describe(
681
+ `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.`
682
+ ),
683
+ confirm: z11.boolean().optional().describe(
684
+ "Must be true to proceed \u2014 this dive spends dive tokens for every ASIN. Confirm the cost with the user first."
685
+ )
686
+ };
687
+ var createNicheDiveFromCompetitorsListTool = {
688
+ name: "create_niche_dive_from_competitors_list",
689
+ title: "Create a Niche Dive from a Competitor List",
690
+ 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\`.`,
691
+ inputSchema: inputSchema12,
692
+ // Additive rather than destructive, but flagged destructive so clients prompt before the
693
+ // irreversible token spend — same reasoning as create_niche_dive. The confirm gate enforces it.
694
+ annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
695
+ handler: async (args, ctx) => {
696
+ const pending = requireConfirmation(
697
+ args.confirm,
698
+ ctx,
699
+ `Creating this dive consumes dive tokens \u2014 one batch per ASIN analyzed (${args.asins.length} ASINs given).`
700
+ );
701
+ if (pending) return pending;
702
+ return await httpPost(
703
+ { config: ctx.config, toolName: "create_niche_dive_from_competitors_list" },
704
+ "/v1/niches/dive_with_competitors",
705
+ { marketplace: args.marketplace, asins: args.asins }
706
+ );
707
+ }
708
+ };
709
+
710
+ // src/tools/redive-niche.ts
711
+ import { z as z12 } from "zod";
712
+ var inputSchema13 = {
713
+ nicheId: z12.string().min(1).describe("The niche to re-dive. Get one from `list_niches`."),
714
+ mode: z12.enum(["same_competitors", "discover"]).describe(
674
715
  "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."
675
716
  ),
676
- numberOfCompetitors: z11.number().int().min(2).optional().describe(
717
+ numberOfCompetitors: z12.number().int().min(2).optional().describe(
677
718
  "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."
678
719
  ),
679
- heroAsin: z11.string().min(1).optional().describe(
720
+ heroAsin: z12.string().min(1).optional().describe(
680
721
  "`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."
681
722
  ),
682
- lockedAsins: z11.array(z11.string().min(1)).optional().describe(
723
+ lockedAsins: z12.array(z12.string().min(1)).optional().describe(
683
724
  "`discover` mode only. ASINs that must stay in the competitor set regardless of what discovery finds. Cannot exceed `numberOfCompetitors`, and cannot overlap `excludedAsins`."
684
725
  ),
685
- excludedAsins: z11.array(z11.string().min(1)).optional().describe("`discover` mode only. ASINs discovery must never select."),
686
- confirm: z11.boolean().optional().describe(
726
+ excludedAsins: z12.array(z12.string().min(1)).optional().describe("`discover` mode only. ASINs discovery must never select."),
727
+ confirm: z12.boolean().optional().describe(
687
728
  "Must be true to proceed \u2014 a re-dive spends dive tokens. Confirm the cost with the user first."
688
729
  )
689
730
  };
@@ -691,7 +732,7 @@ var rediveNicheTool = {
691
732
  name: "redive_niche",
692
733
  title: "Re-dive an Existing Niche",
693
734
  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.",
694
- inputSchema: inputSchema12,
735
+ inputSchema: inputSchema13,
695
736
  // Same reasoning as create_niche_dive: a re-dive is additive rather than destructive, but the
696
737
  // irreversible token spend is what clients need to prompt on. The confirm gate enforces it regardless.
697
738
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
@@ -724,15 +765,17 @@ var rediveNicheTool = {
724
765
  };
725
766
 
726
767
  // src/tools/get-dive-status.ts
727
- import { z as z12 } from "zod";
728
- var inputSchema13 = {
729
- diveId: z12.string().min(1).describe("The dive identifier returned by `create_niche_dive` or `redive_niche`.")
768
+ import { z as z13 } from "zod";
769
+ var inputSchema14 = {
770
+ diveId: z13.string().min(1).describe(
771
+ "The dive identifier returned by `create_niche_dive`, `create_niche_dive_from_competitors_list` or `redive_niche`."
772
+ )
730
773
  };
731
774
  var getDiveStatusTool = {
732
775
  name: "get_dive_status",
733
776
  title: "Get Niche Dive Status",
734
- 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.",
735
- inputSchema: inputSchema13,
777
+ 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.",
778
+ inputSchema: inputSchema14,
736
779
  annotations: { readOnlyHint: true },
737
780
  handler: async (args, ctx) => {
738
781
  return await httpGet(
@@ -743,10 +786,10 @@ var getDiveStatusTool = {
743
786
  };
744
787
 
745
788
  // src/tools/delete-niche.ts
746
- import { z as z13 } from "zod";
747
- var inputSchema14 = {
748
- nicheId: z13.string().min(1).describe("The niche to delete. Get one from `list_niches`."),
749
- confirm: z13.boolean().optional().describe(
789
+ import { z as z14 } from "zod";
790
+ var inputSchema15 = {
791
+ nicheId: z14.string().min(1).describe("The niche to delete. Get one from `list_niches`."),
792
+ confirm: z14.boolean().optional().describe(
750
793
  "Must be true to proceed \u2014 deleting a niche destroys its keywords, competitors and dive history permanently. Confirm with the user first."
751
794
  )
752
795
  };
@@ -754,7 +797,7 @@ var deleteNicheTool = {
754
797
  name: "delete_niche",
755
798
  title: "Delete a Niche",
756
799
  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).",
757
- inputSchema: inputSchema14,
800
+ inputSchema: inputSchema15,
758
801
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
759
802
  handler: async (args, ctx) => {
760
803
  const pending = requireConfirmation(
@@ -772,15 +815,15 @@ var deleteNicheTool = {
772
815
  };
773
816
 
774
817
  // src/tools/pause-rank-radar.ts
775
- import { z as z14 } from "zod";
776
- var inputSchema15 = {
777
- rankRadarId: z14.string().uuid().describe("The Rank Radar UUID to pause (from `list_rank_radars`).")
818
+ import { z as z15 } from "zod";
819
+ var inputSchema16 = {
820
+ rankRadarId: z15.string().uuid().describe("The Rank Radar UUID to pause (from `list_rank_radars`).")
778
821
  };
779
822
  var pauseRankRadarTool = {
780
823
  name: "pause_rank_radar",
781
824
  title: "Pause a Rank Radar",
782
825
  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.",
783
- inputSchema: inputSchema15,
826
+ inputSchema: inputSchema16,
784
827
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
785
828
  handler: async (args, ctx) => {
786
829
  await httpPost(
@@ -793,15 +836,15 @@ var pauseRankRadarTool = {
793
836
  };
794
837
 
795
838
  // src/tools/resume-rank-radar.ts
796
- import { z as z15 } from "zod";
797
- var inputSchema16 = {
798
- rankRadarId: z15.string().uuid().describe("The Rank Radar UUID to resume (from `list_rank_radars`).")
839
+ import { z as z16 } from "zod";
840
+ var inputSchema17 = {
841
+ rankRadarId: z16.string().uuid().describe("The Rank Radar UUID to resume (from `list_rank_radars`).")
799
842
  };
800
843
  var resumeRankRadarTool = {
801
844
  name: "resume_rank_radar",
802
845
  title: "Resume a Paused Rank Radar",
803
846
  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.",
804
- inputSchema: inputSchema16,
847
+ inputSchema: inputSchema17,
805
848
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
806
849
  handler: async (args, ctx) => {
807
850
  await httpPost(
@@ -814,10 +857,10 @@ var resumeRankRadarTool = {
814
857
  };
815
858
 
816
859
  // src/tools/delete-rank-radar.ts
817
- import { z as z16 } from "zod";
818
- var inputSchema17 = {
819
- rankRadarId: z16.string().uuid().describe("The Rank Radar UUID to delete (from `list_rank_radars`)."),
820
- confirm: z16.boolean().optional().describe(
860
+ import { z as z17 } from "zod";
861
+ var inputSchema18 = {
862
+ rankRadarId: z17.string().uuid().describe("The Rank Radar UUID to delete (from `list_rank_radars`)."),
863
+ confirm: z17.boolean().optional().describe(
821
864
  "Must be true to proceed \u2014 deleting a Rank Radar destroys its ranking history permanently. Confirm with the user first."
822
865
  )
823
866
  };
@@ -825,7 +868,7 @@ var deleteRankRadarTool = {
825
868
  name: "delete_rank_radar",
826
869
  title: "Delete a Rank Radar",
827
870
  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`.",
828
- inputSchema: inputSchema17,
871
+ inputSchema: inputSchema18,
829
872
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
830
873
  handler: async (args, ctx) => {
831
874
  const pending = requireConfirmation(
@@ -843,10 +886,10 @@ var deleteRankRadarTool = {
843
886
  };
844
887
 
845
888
  // src/tools/add-rank-radar-search-terms.ts
846
- import { z as z17 } from "zod";
847
- var inputSchema18 = {
848
- rankRadarId: z17.string().uuid().describe("The Rank Radar UUID to add the search terms to (from `list_rank_radars`)."),
849
- searchTerms: z17.array(z17.string().min(1)).min(1).max(ADD_SEARCH_TERMS_MAX).describe(
889
+ import { z as z18 } from "zod";
890
+ var inputSchema19 = {
891
+ rankRadarId: z18.string().uuid().describe("The Rank Radar UUID to add the search terms to (from `list_rank_radars`)."),
892
+ searchTerms: z18.array(z18.string().min(1)).min(1).max(ADD_SEARCH_TERMS_MAX).describe(
850
893
  `The keywords to start tracking, 1\u2013${ADD_SEARCH_TERMS_MAX} per call. Must be unique. Amazon-ignored special characters (@, #, _, parentheses, \u2026) are rejected.`
851
894
  )
852
895
  };
@@ -854,7 +897,7 @@ var addRankRadarSearchTermsTool = {
854
897
  name: "add_rank_radar_search_terms",
855
898
  title: "Add Search Terms to a Rank Radar",
856
899
  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.",
857
- inputSchema: inputSchema18,
900
+ inputSchema: inputSchema19,
858
901
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
859
902
  handler: async (args, ctx) => {
860
903
  return await httpPost(
@@ -866,10 +909,10 @@ var addRankRadarSearchTermsTool = {
866
909
  };
867
910
 
868
911
  // src/tools/pause-rank-radar-search-terms.ts
869
- import { z as z18 } from "zod";
870
- var inputSchema19 = {
871
- rankRadarId: z18.string().uuid().describe("The Rank Radar UUID the search terms belong to."),
872
- rankRadarKeywordIds: z18.array(z18.string().uuid()).min(1).describe(
912
+ import { z as z19 } from "zod";
913
+ var inputSchema20 = {
914
+ rankRadarId: z19.string().uuid().describe("The Rank Radar UUID the search terms belong to."),
915
+ rankRadarKeywordIds: z19.array(z19.string().uuid()).min(1).describe(
873
916
  "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."
874
917
  )
875
918
  };
@@ -877,7 +920,7 @@ var pauseRankRadarSearchTermsTool = {
877
920
  name: "pause_rank_radar_search_terms",
878
921
  title: "Pause Search Terms of a Rank Radar",
879
922
  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.",
880
- inputSchema: inputSchema19,
923
+ inputSchema: inputSchema20,
881
924
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
882
925
  handler: async (args, ctx) => {
883
926
  await httpPost(
@@ -894,10 +937,10 @@ var pauseRankRadarSearchTermsTool = {
894
937
  };
895
938
 
896
939
  // src/tools/resume-rank-radar-search-terms.ts
897
- import { z as z19 } from "zod";
898
- var inputSchema20 = {
899
- rankRadarId: z19.string().uuid().describe("The Rank Radar UUID the search terms belong to."),
900
- rankRadarKeywordIds: z19.array(z19.string().uuid()).min(1).describe(
940
+ import { z as z20 } from "zod";
941
+ var inputSchema21 = {
942
+ rankRadarId: z20.string().uuid().describe("The Rank Radar UUID the search terms belong to."),
943
+ rankRadarKeywordIds: z20.array(z20.string().uuid()).min(1).describe(
901
944
  "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."
902
945
  )
903
946
  };
@@ -905,7 +948,7 @@ var resumeRankRadarSearchTermsTool = {
905
948
  name: "resume_rank_radar_search_terms",
906
949
  title: "Resume Paused Search Terms of a Rank Radar",
907
950
  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.",
908
- inputSchema: inputSchema20,
951
+ inputSchema: inputSchema21,
909
952
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
910
953
  handler: async (args, ctx) => {
911
954
  await httpPost(
@@ -922,16 +965,16 @@ var resumeRankRadarSearchTermsTool = {
922
965
  };
923
966
 
924
967
  // src/tools/generate-listing-copy.ts
925
- import { z as z20 } from "zod";
926
- var inputSchema21 = {
927
- nicheId: z20.string().min(1).describe("The niche whose keyword data the copy is written from. Get one from `list_niches`."),
928
- strategy: z20.enum(LISTING_COPY_STRATEGIES).describe(
968
+ import { z as z21 } from "zod";
969
+ var inputSchema22 = {
970
+ nicheId: z21.string().min(1).describe("The niche whose keyword data the copy is written from. Get one from `list_niches`."),
971
+ strategy: z21.enum(LISTING_COPY_STRATEGIES).describe(
929
972
  "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."
930
973
  ),
931
- currentListing: z20.record(z20.union([z20.string(), z20.array(z20.string())])).describe(
974
+ currentListing: z21.record(z21.union([z21.string(), z21.array(z21.string())])).describe(
932
975
  '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.'
933
976
  ),
934
- confirm: z20.boolean().optional().describe(
977
+ confirm: z21.boolean().optional().describe(
935
978
  "Must be true to proceed \u2014 each generation spends one AI Copywriter prompt from the quota. Confirm the cost with the user first."
936
979
  )
937
980
  };
@@ -939,7 +982,7 @@ var generateListingCopyTool = {
939
982
  name: "generate_listing_copy",
940
983
  title: "Generate Listing Copy for a Niche",
941
984
  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`.",
942
- inputSchema: inputSchema21,
985
+ inputSchema: inputSchema22,
943
986
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
944
987
  handler: async (args, ctx) => {
945
988
  const pending = requireConfirmation(
@@ -957,16 +1000,16 @@ var generateListingCopyTool = {
957
1000
  };
958
1001
 
959
1002
  // src/tools/get-listing-copy-generation-status.ts
960
- import { z as z21 } from "zod";
961
- var inputSchema22 = {
962
- nicheId: z21.string().min(1).describe("The niche the generation was started for."),
963
- generationId: z21.string().min(1).describe("The `generationId` returned by `generate_listing_copy`.")
1003
+ import { z as z22 } from "zod";
1004
+ var inputSchema23 = {
1005
+ nicheId: z22.string().min(1).describe("The niche the generation was started for."),
1006
+ generationId: z22.string().min(1).describe("The `generationId` returned by `generate_listing_copy`.")
964
1007
  };
965
1008
  var getListingCopyGenerationStatusTool = {
966
1009
  name: "get_listing_copy_generation_status",
967
1010
  title: "Get Listing Copy Generation Status",
968
1011
  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`.",
969
- inputSchema: inputSchema22,
1012
+ inputSchema: inputSchema23,
970
1013
  annotations: { readOnlyHint: true },
971
1014
  handler: async (args, ctx) => {
972
1015
  return await httpGet(
@@ -977,16 +1020,16 @@ var getListingCopyGenerationStatusTool = {
977
1020
  };
978
1021
 
979
1022
  // src/tools/list-seller-profiles.ts
980
- import { z as z22 } from "zod";
981
- var inputSchema23 = {
982
- currentPage: z22.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
983
- pageSize: z22.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
1023
+ import { z as z23 } from "zod";
1024
+ var inputSchema24 = {
1025
+ currentPage: z23.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1026
+ pageSize: z23.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
984
1027
  };
985
1028
  var listSellerProfilesTool = {
986
1029
  name: "list_seller_profiles",
987
1030
  title: "List Connected Amazon Seller Profiles",
988
1031
  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).',
989
- inputSchema: inputSchema23,
1032
+ inputSchema: inputSchema24,
990
1033
  annotations: { readOnlyHint: true },
991
1034
  handler: async (args, ctx) => {
992
1035
  return await httpGet(
@@ -1001,15 +1044,15 @@ var listSellerProfilesTool = {
1001
1044
  };
1002
1045
 
1003
1046
  // src/tools/get-seller-catalog.ts
1004
- import { z as z24 } from "zod";
1047
+ import { z as z25 } from "zod";
1005
1048
 
1006
1049
  // src/tools/seller-scoped.ts
1007
- import { z as z23 } from "zod";
1050
+ import { z as z24 } from "zod";
1008
1051
  var sellerScopedInputSchema = {
1009
- sellerId: z23.string().min(1).describe(
1052
+ sellerId: z24.string().min(1).describe(
1010
1053
  "Amazon seller account ID. Get it from `list_seller_profiles`, or find it on the Connections page at https://2.datadive.tools."
1011
1054
  ),
1012
- marketplace: z23.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.')
1055
+ marketplace: z24.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.')
1013
1056
  };
1014
1057
  function sellerMarketplacePath(sellerId, marketplace, suffix) {
1015
1058
  return `/v1/seller_profiles/${encodeURIComponent(sellerId)}/marketplaces/${encodeURIComponent(
@@ -1018,19 +1061,19 @@ function sellerMarketplacePath(sellerId, marketplace, suffix) {
1018
1061
  }
1019
1062
 
1020
1063
  // src/tools/get-seller-catalog.ts
1021
- var inputSchema24 = {
1064
+ var inputSchema25 = {
1022
1065
  ...sellerScopedInputSchema,
1023
- search: z24.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
1024
- brand: z24.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
1025
- status: z24.enum(CATALOG_STATUSES).optional().describe('Listing status filter. "Active" (default) or "all".'),
1026
- currentPage: z24.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1027
- pageSize: z24.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
1066
+ search: z25.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
1067
+ brand: z25.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
1068
+ status: z25.enum(CATALOG_STATUSES).optional().describe('Listing status filter. "Active" (default) or "all".'),
1069
+ currentPage: z25.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1070
+ pageSize: z25.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
1028
1071
  };
1029
1072
  var getSellerCatalogTool = {
1030
1073
  name: "get_seller_catalog",
1031
1074
  title: "List Catalog ASINs for a Seller",
1032
1075
  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).",
1033
- inputSchema: inputSchema24,
1076
+ inputSchema: inputSchema25,
1034
1077
  annotations: { readOnlyHint: true },
1035
1078
  handler: async (args, ctx) => {
1036
1079
  return await httpGet(
@@ -1048,28 +1091,28 @@ var getSellerCatalogTool = {
1048
1091
  };
1049
1092
 
1050
1093
  // src/tools/get-seller-listing-changes.ts
1051
- import { z as z25 } from "zod";
1052
- var inputSchema25 = {
1094
+ import { z as z26 } from "zod";
1095
+ var inputSchema26 = {
1053
1096
  ...sellerScopedInputSchema,
1054
- types: z25.array(z25.enum(LISTING_CHANGE_TYPES)).min(1).optional().describe('Filter to specific change types. Omit for all. Any of "Price", "Content", "Image".'),
1055
- asin: z25.string().min(1).optional().describe("Filter to an exact ASIN or parent ASIN."),
1056
- brand: z25.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
1057
- search: z25.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
1058
- startDate: z25.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."),
1059
- endDate: z25.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."),
1060
- sortBy: z25.enum(LISTING_CHANGE_SORT_BY).optional().describe('Sort field. "date" (default) or "type".'),
1061
- sortOrder: z25.enum(SORT_ORDER).optional().describe('Sort direction. "DESC" (default) or "ASC".'),
1062
- includeCorrelations: z25.boolean().optional().describe(
1097
+ 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".'),
1098
+ asin: z26.string().min(1).optional().describe("Filter to an exact ASIN or parent ASIN."),
1099
+ brand: z26.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
1100
+ search: z26.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
1101
+ 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."),
1102
+ 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."),
1103
+ sortBy: z26.enum(LISTING_CHANGE_SORT_BY).optional().describe('Sort field. "date" (default) or "type".'),
1104
+ sortOrder: z26.enum(SORT_ORDER).optional().describe('Sort direction. "DESC" (default) or "ASC".'),
1105
+ includeCorrelations: z26.boolean().optional().describe(
1063
1106
  "When true, include the ranking/conversion `correlation` object per change (CVR and search-term movement before vs after). Defaults to false."
1064
1107
  ),
1065
- currentPage: z25.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1066
- pageSize: z25.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
1108
+ currentPage: z26.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1109
+ pageSize: z26.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
1067
1110
  };
1068
1111
  var getSellerListingChangesTool = {
1069
1112
  name: "get_seller_listing_changes",
1070
1113
  title: "List Listing Changes for a Seller",
1071
1114
  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).",
1072
- inputSchema: inputSchema25,
1115
+ inputSchema: inputSchema26,
1073
1116
  annotations: { readOnlyHint: true },
1074
1117
  handler: async (args, ctx) => {
1075
1118
  return await httpGet(
@@ -1094,40 +1137,40 @@ var getSellerListingChangesTool = {
1094
1137
  };
1095
1138
 
1096
1139
  // src/tools/list-ppc-campaigns.ts
1097
- import { z as z26 } from "zod";
1140
+ import { z as z27 } from "zod";
1098
1141
  var PPC_CAMPAIGNS_MAX_WINDOW_DAYS = 90;
1099
1142
  var PPC_CAMPAIGNS_MAX_PAGE_SIZE = 50;
1100
- var inputSchema26 = {
1143
+ var inputSchema27 = {
1101
1144
  ...sellerScopedInputSchema,
1102
- startDate: z26.string().regex(
1145
+ startDate: z27.string().regex(
1103
1146
  ISO_8601,
1104
1147
  "startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)"
1105
1148
  ).optional().describe("Start of the reporting window, ISO-8601. Defaults to 30 days before endDate."),
1106
- endDate: z26.string().regex(
1149
+ endDate: z27.string().regex(
1107
1150
  ISO_8601,
1108
1151
  "endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)"
1109
1152
  ).optional().describe(
1110
1153
  `End of the reporting window, ISO-8601. Defaults to now. At most ${PPC_CAMPAIGNS_MAX_WINDOW_DAYS} days after startDate.`
1111
1154
  ),
1112
- asin: z26.string().min(1).optional().describe("Only campaigns advertising this ASIN. Cannot be combined with parentAsin."),
1113
- parentAsin: z26.string().min(1).optional().describe("Only campaigns advertising any ASIN of this variation family. Cannot be combined with asin."),
1114
- search: z26.string().min(3).max(200).optional().describe("Partial match on campaign name or advertised ASIN. At least 3 characters."),
1115
- includePlacements: z26.boolean().optional().describe(
1155
+ asin: z27.string().min(1).optional().describe("Only campaigns advertising this ASIN. Cannot be combined with parentAsin."),
1156
+ parentAsin: z27.string().min(1).optional().describe("Only campaigns advertising any ASIN of this variation family. Cannot be combined with asin."),
1157
+ search: z27.string().min(3).max(200).optional().describe("Partial match on campaign name or advertised ASIN. At least 3 characters."),
1158
+ includePlacements: z27.boolean().optional().describe(
1116
1159
  "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."
1117
1160
  ),
1118
- sortBy: z26.enum([...PPC_CAMPAIGN_BASE_SORT_BY, ...PPC_CAMPAIGN_PLACEMENT_SORT_BY]).optional().describe(
1161
+ sortBy: z27.enum([...PPC_CAMPAIGN_BASE_SORT_BY, ...PPC_CAMPAIGN_PLACEMENT_SORT_BY]).optional().describe(
1119
1162
  `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.`
1120
1163
  ),
1121
- sortOrder: z26.enum(SORT_ORDER).optional().describe('Sort direction. "ASC" (default) or "DESC".'),
1122
- state: z26.enum(PPC_CAMPAIGN_STATES).optional().describe("Only ENABLED or only PAUSED campaigns. Omit for both."),
1123
- currentPage: z26.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1124
- pageSize: z26.number().int().min(1).max(PPC_CAMPAIGNS_MAX_PAGE_SIZE).optional().describe(`Campaigns per page (max ${PPC_CAMPAIGNS_MAX_PAGE_SIZE}). Defaults to 20.`)
1164
+ sortOrder: z27.enum(SORT_ORDER).optional().describe('Sort direction. "ASC" (default) or "DESC".'),
1165
+ state: z27.enum(PPC_CAMPAIGN_STATES).optional().describe("Only ENABLED or only PAUSED campaigns. Omit for both."),
1166
+ currentPage: z27.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1167
+ 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.`)
1125
1168
  };
1126
1169
  var listPpcCampaignsTool = {
1127
1170
  name: "list_ppc_campaigns",
1128
1171
  title: "List PPC Campaigns for a Seller",
1129
1172
  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\`.`,
1130
- inputSchema: inputSchema26,
1173
+ inputSchema: inputSchema27,
1131
1174
  annotations: { readOnlyHint: true },
1132
1175
  handler: async (args, ctx) => {
1133
1176
  if (args.asin && args.parentAsin) {
@@ -1158,13 +1201,13 @@ var listPpcCampaignsTool = {
1158
1201
  };
1159
1202
 
1160
1203
  // src/tools/get-asin-inventory-distribution.ts
1161
- import { z as z27 } from "zod";
1162
- var inputSchema27 = {
1163
- sellerId: z27.string().min(1).describe(
1204
+ import { z as z28 } from "zod";
1205
+ var inputSchema28 = {
1206
+ sellerId: z28.string().min(1).describe(
1164
1207
  "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."
1165
1208
  ),
1166
- marketplace: z27.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.'),
1167
- asin: z27.string().regex(/^[A-Z0-9]{10}$/, "ASIN must be 10 uppercase alphanumeric characters (e.g. B08XYZ1234).").describe(
1209
+ marketplace: z28.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.'),
1210
+ asin: z28.string().regex(/^[A-Z0-9]{10}$/, "ASIN must be 10 uppercase alphanumeric characters (e.g. B08XYZ1234).").describe(
1168
1211
  "Amazon Standard Identification Number (10 uppercase alphanumeric characters, e.g. B08XYZ1234)."
1169
1212
  )
1170
1213
  };
@@ -1172,7 +1215,7 @@ var getAsinInventoryDistributionTool = {
1172
1215
  name: "get_asin_inventory_distribution",
1173
1216
  title: "Get Per-Fulfillment-Center Inventory Distribution for an ASIN",
1174
1217
  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.",
1175
- inputSchema: inputSchema27,
1218
+ inputSchema: inputSchema28,
1176
1219
  annotations: { readOnlyHint: true },
1177
1220
  handler: async (args, ctx) => {
1178
1221
  return await httpGet(
@@ -1185,18 +1228,18 @@ var getAsinInventoryDistributionTool = {
1185
1228
  };
1186
1229
 
1187
1230
  // src/tools/alert-query.ts
1188
- import { z as z28 } from "zod";
1231
+ import { z as z29 } from "zod";
1189
1232
  var alertsQueryInputSchema = {
1190
- sellerId: z28.string().min(1).max(50).optional().describe('Filter to a single connected Amazon seller account ID, e.g. "A1B2C3D4E5".'),
1191
- marketplace: z28.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.'),
1192
- status: z28.enum(ALERT_STATUSES).optional().describe(
1233
+ sellerId: z29.string().min(1).max(50).optional().describe('Filter to a single connected Amazon seller account ID, e.g. "A1B2C3D4E5".'),
1234
+ 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.'),
1235
+ status: z29.enum(ALERT_STATUSES).optional().describe(
1193
1236
  "Lifecycle filter. `active` (default) returns unresolved alerts, `resolved` returns resolved alerts, `all` returns both. Dismissed alerts are never returned."
1194
1237
  ),
1195
- updatedSince: z28.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(
1238
+ 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(
1196
1239
  "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."
1197
1240
  ),
1198
- currentPage: z28.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1199
- pageSize: z28.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
1241
+ currentPage: z29.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1242
+ pageSize: z29.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
1200
1243
  };
1201
1244
 
1202
1245
  // src/tools/list-indexing-issue-alerts.ts
@@ -1246,12 +1289,12 @@ var listBlindSpendAlertsTool = {
1246
1289
  };
1247
1290
 
1248
1291
  // src/tools/get-quota.ts
1249
- var inputSchema28 = {};
1292
+ var inputSchema29 = {};
1250
1293
  var getQuotaTool = {
1251
1294
  name: "get_quota",
1252
1295
  title: "Get Quota Usage",
1253
1296
  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.",
1254
- inputSchema: inputSchema28,
1297
+ inputSchema: inputSchema29,
1255
1298
  annotations: { readOnlyHint: true },
1256
1299
  handler: async (_args, ctx) => {
1257
1300
  return await httpGet({ config: ctx.config, toolName: "get_quota" }, "/v1/quota");
@@ -1259,20 +1302,20 @@ var getQuotaTool = {
1259
1302
  };
1260
1303
 
1261
1304
  // src/tools/list-usage.ts
1262
- import { z as z29 } from "zod";
1263
- var inputSchema29 = {
1264
- type: z29.enum(BILLABLE_FEATURE_TYPES).optional().describe("Filter to a single billable feature type. Omit to return all types."),
1265
- search: z29.string().min(1).optional().describe("Case-insensitive partial match on the user's name or email."),
1266
- startDate: z29.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."),
1267
- endDate: z29.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."),
1268
- currentPage: z29.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1269
- pageSize: z29.number().int().min(1).max(200).optional().describe("Items per page (max 200). Defaults to 50.")
1305
+ import { z as z30 } from "zod";
1306
+ var inputSchema30 = {
1307
+ type: z30.enum(BILLABLE_FEATURE_TYPES).optional().describe("Filter to a single billable feature type. Omit to return all types."),
1308
+ search: z30.string().min(1).optional().describe("Case-insensitive partial match on the user's name or email."),
1309
+ 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."),
1310
+ 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."),
1311
+ currentPage: z30.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
1312
+ pageSize: z30.number().int().min(1).max(200).optional().describe("Items per page (max 200). Defaults to 50.")
1270
1313
  };
1271
1314
  var listUsageTool = {
1272
1315
  name: "list_usage",
1273
1316
  title: "List Billable Usage Logs",
1274
1317
  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).",
1275
- inputSchema: inputSchema29,
1318
+ inputSchema: inputSchema30,
1276
1319
  annotations: { readOnlyHint: true },
1277
1320
  handler: async (args, ctx) => {
1278
1321
  return await httpGet(
@@ -1303,6 +1346,7 @@ var allTools = [
1303
1346
  getRankRadarPpcDataTool,
1304
1347
  createRankRadarTool,
1305
1348
  createNicheDiveTool,
1349
+ createNicheDiveFromCompetitorsListTool,
1306
1350
  rediveNicheTool,
1307
1351
  getDiveStatusTool,
1308
1352
  addRankRadarSearchTermsTool,
@@ -1327,6 +1371,13 @@ var allTools = [
1327
1371
 
1328
1372
  // src/whats-new.ts
1329
1373
  var WHATS_NEW = [
1374
+ {
1375
+ version: "0.16.0",
1376
+ released: "2026-09-28",
1377
+ items: [
1378
+ "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."
1379
+ ]
1380
+ },
1330
1381
  {
1331
1382
  version: "0.15.0",
1332
1383
  released: "2026-09-28",