@datadive-tools/mcp 0.14.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/README.md +4 -0
- package/dist/index.js +405 -145
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +34 -1
- package/dist/lib.js +410 -146
- package/dist/lib.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -144,7 +144,7 @@ function extractMessage(body) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// package.json
|
|
147
|
-
var version = "0.
|
|
147
|
+
var version = "0.16.0";
|
|
148
148
|
|
|
149
149
|
// src/http/client.ts
|
|
150
150
|
var PKG_VERSION = version;
|
|
@@ -408,6 +408,33 @@ var SUPPORTED_MARKETPLACES = [
|
|
|
408
408
|
];
|
|
409
409
|
var RANK_RADAR_STATES = ["ACTIVE", "PAUSED", "ARCHIVED"];
|
|
410
410
|
var RANK_RADAR_STATUSES = [...RANK_RADAR_STATES, "ALL"];
|
|
411
|
+
var PPC_CAMPAIGN_BASE_SORT_BY = [
|
|
412
|
+
"name",
|
|
413
|
+
"state",
|
|
414
|
+
"bidStrategy",
|
|
415
|
+
"budget",
|
|
416
|
+
"impressions",
|
|
417
|
+
"clicks",
|
|
418
|
+
"ctr",
|
|
419
|
+
"cpc",
|
|
420
|
+
"spend",
|
|
421
|
+
"unitsSold",
|
|
422
|
+
"sales",
|
|
423
|
+
"acos",
|
|
424
|
+
"cvr",
|
|
425
|
+
"tosImpressionShare"
|
|
426
|
+
];
|
|
427
|
+
var PPC_CAMPAIGN_PLACEMENT_SORT_BY = [
|
|
428
|
+
...["tos", "ros", "pp", "off"].flatMap(
|
|
429
|
+
(p) => ["Impressions", "Clicks", "Spend", "Sales", "UnitsSold", "Ctr", "Cpc", "Acos"].map(
|
|
430
|
+
(m) => `${p}${m}`
|
|
431
|
+
)
|
|
432
|
+
),
|
|
433
|
+
"tosBidAdjustment",
|
|
434
|
+
"rosBidAdjustment",
|
|
435
|
+
"ppBidAdjustment"
|
|
436
|
+
];
|
|
437
|
+
var PPC_CAMPAIGN_STATES = ["ENABLED", "PAUSED"];
|
|
411
438
|
var ALERT_STATUSES = ["active", "resolved", "all"];
|
|
412
439
|
var BILLABLE_FEATURE_TYPES = [
|
|
413
440
|
"DIVED_ASINS",
|
|
@@ -459,42 +486,92 @@ var listRankRadarsTool = {
|
|
|
459
486
|
}
|
|
460
487
|
};
|
|
461
488
|
|
|
462
|
-
// src/tools/
|
|
489
|
+
// src/tools/rank-radar-keyword-query.ts
|
|
463
490
|
import { z as z7 } from "zod";
|
|
464
491
|
var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
465
492
|
var RANK_RADAR_KEYWORDS_MAX_PAGE_SIZE = 100;
|
|
466
493
|
var RANK_RADAR_MAX_DATE_RANGE_DAYS = 90;
|
|
467
|
-
var
|
|
494
|
+
var rankRadarKeywordQueryInputSchema = {
|
|
468
495
|
rankRadarId: z7.string().uuid().describe("The Rank Radar UUID (from `list_rank_radars`)."),
|
|
469
|
-
startDate: z7.string().regex(ISO_DATE, "startDate must be in yyyy-mm-dd format").describe("Start date
|
|
496
|
+
startDate: z7.string().regex(ISO_DATE, "startDate must be in yyyy-mm-dd format").describe("Start date of the range, yyyy-mm-dd (e.g. 2024-03-26)."),
|
|
470
497
|
endDate: z7.string().regex(ISO_DATE, "endDate must be in yyyy-mm-dd format").describe(
|
|
471
|
-
`End date
|
|
498
|
+
`End date of the range, yyyy-mm-dd (e.g. 2024-04-26). Must be on or after startDate, and at most ${RANK_RADAR_MAX_DATE_RANGE_DAYS} days after it.`
|
|
472
499
|
),
|
|
473
500
|
currentPage: z7.number().int().min(1).optional().describe("Page of keywords, 1-indexed. Defaults to 1."),
|
|
474
501
|
pageSize: z7.number().int().min(1).max(RANK_RADAR_KEYWORDS_MAX_PAGE_SIZE).optional().describe(`Keywords per page (max ${RANK_RADAR_KEYWORDS_MAX_PAGE_SIZE}). Defaults to 20.`)
|
|
475
502
|
};
|
|
503
|
+
var RANK_RADAR_KEYWORD_PAGING_NOTE = `Results are paged by keyword: \`data\` holds one page, and the response carries currentPage, pageSize, total, lastPage, hasNext and hasPrev. \`total\` is the Rank Radar's active keyword count; to read every keyword, call again with \`currentPage\` + 1 while \`hasNext\` is true, using \`pageSize\` ${RANK_RADAR_KEYWORDS_MAX_PAGE_SIZE} for large Rank Radars. Paused keywords are not included.`;
|
|
504
|
+
function rankRadarPath(rankRadarId, suffix = "") {
|
|
505
|
+
return `/v1/niches/rank-radars/${encodeURIComponent(rankRadarId)}${suffix ? `/${suffix}` : ""}`;
|
|
506
|
+
}
|
|
507
|
+
function rankRadarKeywordQuery(args) {
|
|
508
|
+
return {
|
|
509
|
+
startDate: args.startDate,
|
|
510
|
+
endDate: args.endDate,
|
|
511
|
+
currentPage: args.currentPage,
|
|
512
|
+
pageSize: args.pageSize
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/tools/get-rank-radar-data.ts
|
|
517
|
+
var inputSchema7 = rankRadarKeywordQueryInputSchema;
|
|
476
518
|
var getRankRadarDataTool = {
|
|
477
519
|
name: "get_rank_radar_data",
|
|
478
520
|
title: "Get Keyword Rankings for a Rank Radar",
|
|
479
|
-
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.
|
|
521
|
+
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. ${RANK_RADAR_KEYWORD_PAGING_NOTE} 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\`.`,
|
|
480
522
|
inputSchema: inputSchema7,
|
|
481
523
|
annotations: { readOnlyHint: true },
|
|
482
524
|
handler: async (args, ctx) => {
|
|
483
525
|
return await httpGet(
|
|
484
526
|
{ config: ctx.config, toolName: "get_rank_radar_data" },
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
startDate: args.startDate,
|
|
488
|
-
endDate: args.endDate,
|
|
489
|
-
currentPage: args.currentPage,
|
|
490
|
-
pageSize: args.pageSize
|
|
491
|
-
}
|
|
527
|
+
rankRadarPath(args.rankRadarId),
|
|
528
|
+
rankRadarKeywordQuery(args)
|
|
492
529
|
);
|
|
493
530
|
}
|
|
494
531
|
};
|
|
495
532
|
|
|
496
|
-
// src/tools/
|
|
533
|
+
// src/tools/get-rank-radar-sqp-data.ts
|
|
534
|
+
var inputSchema8 = rankRadarKeywordQueryInputSchema;
|
|
535
|
+
var getRankRadarSqpDataTool = {
|
|
536
|
+
name: "get_rank_radar_sqp_data",
|
|
537
|
+
title: "Get Search Query Performance for a Rank Radar",
|
|
538
|
+
description: `Use this when the user asks how shoppers search, click, add to cart and buy for the keywords of a Rank Radar \u2014 Amazon's Search Query Performance (SQP) data, from Brand Analytics. Requires startDate and endDate (yyyy-mm-dd), at most ${RANK_RADAR_MAX_DATE_RANGE_DAYS} days apart; 30 days or less keeps it fast. Each keyword's metrics are aggregated over the range: searchQueryVolume and searchQueryScore; impressions, clicks, cart adds and purchases, each as the total across all sellers (\`*TotalCount\`), this ASIN family's count (\`*AsinCount\`) and its share (\`*AsinShare\`); click, cart-add and purchase rates; and ctr/cvr for the market (\`*Total\`) and for this ASIN family (\`*Asin\`). \`numberOfDaysWithData\` says how many days of the range had SQP data; metrics are null when there is none, which is normal for low-volume keywords, recent dates (Amazon publishes SQP with a delay) and sellers without Brand Analytics. ${RANK_RADAR_KEYWORD_PAGING_NOTE} Each call counts toward API usage and is rate limited (about 60 requests/minute). Use after \`list_rank_radars\` to discover a \`rankRadarId\`.`,
|
|
539
|
+
inputSchema: inputSchema8,
|
|
540
|
+
annotations: { readOnlyHint: true },
|
|
541
|
+
handler: async (args, ctx) => {
|
|
542
|
+
return await httpGet(
|
|
543
|
+
{ config: ctx.config, toolName: "get_rank_radar_sqp_data" },
|
|
544
|
+
rankRadarPath(args.rankRadarId, "sqp"),
|
|
545
|
+
rankRadarKeywordQuery(args)
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// src/tools/get-rank-radar-ppc-data.ts
|
|
497
551
|
import { z as z8 } from "zod";
|
|
552
|
+
var inputSchema9 = {
|
|
553
|
+
...rankRadarKeywordQueryInputSchema,
|
|
554
|
+
includeCampaigns: z8.boolean().optional().describe(
|
|
555
|
+
"When true, each keyword also carries `campaigns`: the per-campaign/ad-group breakdown (campaign and ad group name, match type, targeting, spend, sales, clicks, impressions, orders, CPC, CTR, CVR, ACOS). Defaults to false; leave it off unless the user asks which campaigns drive a keyword."
|
|
556
|
+
)
|
|
557
|
+
};
|
|
558
|
+
var getRankRadarPpcDataTool = {
|
|
559
|
+
name: "get_rank_radar_ppc_data",
|
|
560
|
+
title: "Get PPC Data for a Rank Radar",
|
|
561
|
+
description: `Use this when the user asks about advertising (Sponsored Products) performance for the keywords of a Rank Radar. Requires startDate and endDate (yyyy-mm-dd), at most ${RANK_RADAR_MAX_DATE_RANGE_DAYS} days apart; 30 days or less keeps it fast. Each keyword's metrics are aggregated over the range: sponsoredRank (median; 101 means not in the top 100 sponsored results), impressionRank and impressionRankShare; how many exact, phrase, broad and auto campaigns target it; organicSales, ppcSales, ppcSpend, costPerClicks, clickThroughRate, conversionRate, totalClicks, totalImpressions, totalOrders and acos. Metrics are null or 0 when the seller runs no ads on a keyword. Set \`includeCampaigns\` for the per-campaign breakdown. ${RANK_RADAR_KEYWORD_PAGING_NOTE} Each call counts toward API usage and is rate limited (about 60 requests/minute). For the seller's campaigns as a whole, use \`list_ppc_campaigns\`. Use after \`list_rank_radars\` to discover a \`rankRadarId\`.`,
|
|
562
|
+
inputSchema: inputSchema9,
|
|
563
|
+
annotations: { readOnlyHint: true },
|
|
564
|
+
handler: async (args, ctx) => {
|
|
565
|
+
return await httpGet(
|
|
566
|
+
{ config: ctx.config, toolName: "get_rank_radar_ppc_data" },
|
|
567
|
+
rankRadarPath(args.rankRadarId, "ppc"),
|
|
568
|
+
{ ...rankRadarKeywordQuery(args), includeCampaigns: args.includeCampaigns }
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
// src/tools/create-rank-radar.ts
|
|
574
|
+
import { z as z9 } from "zod";
|
|
498
575
|
|
|
499
576
|
// src/tools/confirm.ts
|
|
500
577
|
function needsConfirmation(confirm, ctx) {
|
|
@@ -515,11 +592,11 @@ function requireConfirmation(confirm, ctx, costNote) {
|
|
|
515
592
|
|
|
516
593
|
// src/tools/create-rank-radar.ts
|
|
517
594
|
var PATH = "/v1/niches/rank-radars";
|
|
518
|
-
var
|
|
519
|
-
asin:
|
|
520
|
-
numberOfKeywords:
|
|
521
|
-
nicheId:
|
|
522
|
-
confirm:
|
|
595
|
+
var inputSchema10 = {
|
|
596
|
+
asin: z9.string().min(1).describe("The ASIN to track keyword rankings for."),
|
|
597
|
+
numberOfKeywords: z9.number().int().min(1).describe("How many keywords to track (minimum 1). More keywords = broader tracking but more Search Term tokens."),
|
|
598
|
+
nicheId: z9.string().min(1).describe("The niche this Rank Radar belongs to. Get one from `list_niches`."),
|
|
599
|
+
confirm: z9.boolean().optional().describe(
|
|
523
600
|
"Must be true to proceed \u2014 creating a Rank Radar spends Search Term tokens. Confirm the cost with the user first."
|
|
524
601
|
)
|
|
525
602
|
};
|
|
@@ -527,7 +604,7 @@ var createRankRadarTool = {
|
|
|
527
604
|
name: "create_rank_radar",
|
|
528
605
|
title: "Create a Rank Radar",
|
|
529
606
|
description: "Use this to start tracking organic and sponsored keyword rankings for an ASIN within a niche. \u26A0\uFE0F Spends Search Term tokens (cost scales with `numberOfKeywords`) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. Not safe to retry: each call spends tokens again and creates a separate Rank Radar \u2014 if a call errors or times out, check `list_rank_radars` instead of re-calling. The first call previews the creation and may return `warnings` alongside the cost note \u2014 for example that this product family is already tracked by another Rank Radar. Show every warning `message` to the user verbatim, before asking them to confirm; do not summarise it or decide on their behalf. A warning never blocks creation. Returns the new `rankRadarId`; read its data later with `get_rank_radar_data`. Requires `asin`, `numberOfKeywords` (min 1), and a `nicheId` from `list_niches`.",
|
|
530
|
-
inputSchema:
|
|
607
|
+
inputSchema: inputSchema10,
|
|
531
608
|
// destructiveHint is technically "additive" here (this creates a new Rank Radar, it
|
|
532
609
|
// doesn't destroy data), but we flag it destructive so clients prompt before the
|
|
533
610
|
// irreversible token spend. The confirm gate enforces this server-side regardless.
|
|
@@ -552,20 +629,20 @@ var createRankRadarTool = {
|
|
|
552
629
|
};
|
|
553
630
|
|
|
554
631
|
// src/tools/create-niche-dive.ts
|
|
555
|
-
import { z as
|
|
556
|
-
var
|
|
557
|
-
marketplace:
|
|
632
|
+
import { z as z10 } from "zod";
|
|
633
|
+
var inputSchema11 = {
|
|
634
|
+
marketplace: z10.enum(SUPPORTED_MARKETPLACES).describe(
|
|
558
635
|
"Amazon marketplace of the seed ASIN. Note these are full domain suffixes: com, ca, co.uk, com.mx, in, fr, de, es, it, co.jp (e.g. UK is `co.uk`, not `uk`)."
|
|
559
636
|
),
|
|
560
|
-
asin:
|
|
561
|
-
numberOfCompetitors:
|
|
562
|
-
confirm:
|
|
637
|
+
asin: z10.string().min(1).describe("The seed ASIN to start the dive from (e.g. B08N5WRWNW). Competitors are built around it."),
|
|
638
|
+
numberOfCompetitors: z10.number().int().min(2).describe("How many competitors to analyze (minimum 2). More competitors = deeper niche but more dive tokens."),
|
|
639
|
+
confirm: z10.boolean().optional().describe("Must be true to proceed \u2014 creating a dive spends dive tokens. Confirm the cost with the user first.")
|
|
563
640
|
};
|
|
564
641
|
var createNicheDiveTool = {
|
|
565
642
|
name: "create_niche_dive",
|
|
566
643
|
title: "Create a Niche Dive",
|
|
567
644
|
description: "Use this to start new niche research from a seed ASIN. \u26A0\uFE0F Spends dive tokens (cost scales with `numberOfCompetitors`) and cannot be undone \u2014 set `confirm: true` only after the user approves the cost. Not safe to retry: each call spends tokens again and starts a separate dive \u2014 if a call errors or times out, poll `get_dive_status` (or check `list_niches`) instead of re-calling. The dive runs asynchronously: this returns immediately with a `diveId` and an `estimatedCompletionDate`. Poll `get_dive_status` with that `diveId` until it reports `success` (which carries the new `nicheId` for use with `list_niches`, `get_niche_keywords`, etc.). Requires `marketplace`, `asin`, and `numberOfCompetitors` (min 2).",
|
|
568
|
-
inputSchema:
|
|
645
|
+
inputSchema: inputSchema11,
|
|
569
646
|
// destructiveHint is technically "additive" here (a dive creates a new niche, it
|
|
570
647
|
// doesn't destroy data), but we flag it destructive so clients prompt before the
|
|
571
648
|
// irreversible token spend. The confirm gate enforces this server-side regardless.
|
|
@@ -589,24 +666,65 @@ var createNicheDiveTool = {
|
|
|
589
666
|
}
|
|
590
667
|
};
|
|
591
668
|
|
|
669
|
+
// src/tools/create-niche-dive-from-competitors-list.ts
|
|
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
|
+
});
|
|
676
|
+
var inputSchema12 = {
|
|
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
|
+
|
|
592
710
|
// src/tools/redive-niche.ts
|
|
593
|
-
import { z as
|
|
594
|
-
var
|
|
595
|
-
nicheId:
|
|
596
|
-
mode:
|
|
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(
|
|
597
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."
|
|
598
716
|
),
|
|
599
|
-
numberOfCompetitors:
|
|
717
|
+
numberOfCompetitors: z12.number().int().min(2).optional().describe(
|
|
600
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."
|
|
601
719
|
),
|
|
602
|
-
heroAsin:
|
|
720
|
+
heroAsin: z12.string().min(1).optional().describe(
|
|
603
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."
|
|
604
722
|
),
|
|
605
|
-
lockedAsins:
|
|
723
|
+
lockedAsins: z12.array(z12.string().min(1)).optional().describe(
|
|
606
724
|
"`discover` mode only. ASINs that must stay in the competitor set regardless of what discovery finds. Cannot exceed `numberOfCompetitors`, and cannot overlap `excludedAsins`."
|
|
607
725
|
),
|
|
608
|
-
excludedAsins:
|
|
609
|
-
confirm:
|
|
726
|
+
excludedAsins: z12.array(z12.string().min(1)).optional().describe("`discover` mode only. ASINs discovery must never select."),
|
|
727
|
+
confirm: z12.boolean().optional().describe(
|
|
610
728
|
"Must be true to proceed \u2014 a re-dive spends dive tokens. Confirm the cost with the user first."
|
|
611
729
|
)
|
|
612
730
|
};
|
|
@@ -614,7 +732,7 @@ var rediveNicheTool = {
|
|
|
614
732
|
name: "redive_niche",
|
|
615
733
|
title: "Re-dive an Existing Niche",
|
|
616
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.",
|
|
617
|
-
inputSchema:
|
|
735
|
+
inputSchema: inputSchema13,
|
|
618
736
|
// Same reasoning as create_niche_dive: a re-dive is additive rather than destructive, but the
|
|
619
737
|
// irreversible token spend is what clients need to prompt on. The confirm gate enforces it regardless.
|
|
620
738
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
@@ -647,15 +765,17 @@ var rediveNicheTool = {
|
|
|
647
765
|
};
|
|
648
766
|
|
|
649
767
|
// src/tools/get-dive-status.ts
|
|
650
|
-
import { z as
|
|
651
|
-
var
|
|
652
|
-
diveId:
|
|
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
|
+
)
|
|
653
773
|
};
|
|
654
774
|
var getDiveStatusTool = {
|
|
655
775
|
name: "get_dive_status",
|
|
656
776
|
title: "Get Niche Dive Status",
|
|
657
|
-
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.",
|
|
658
|
-
inputSchema:
|
|
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,
|
|
659
779
|
annotations: { readOnlyHint: true },
|
|
660
780
|
handler: async (args, ctx) => {
|
|
661
781
|
return await httpGet(
|
|
@@ -666,10 +786,10 @@ var getDiveStatusTool = {
|
|
|
666
786
|
};
|
|
667
787
|
|
|
668
788
|
// src/tools/delete-niche.ts
|
|
669
|
-
import { z as
|
|
670
|
-
var
|
|
671
|
-
nicheId:
|
|
672
|
-
confirm:
|
|
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(
|
|
673
793
|
"Must be true to proceed \u2014 deleting a niche destroys its keywords, competitors and dive history permanently. Confirm with the user first."
|
|
674
794
|
)
|
|
675
795
|
};
|
|
@@ -677,7 +797,7 @@ var deleteNicheTool = {
|
|
|
677
797
|
name: "delete_niche",
|
|
678
798
|
title: "Delete a Niche",
|
|
679
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).",
|
|
680
|
-
inputSchema:
|
|
800
|
+
inputSchema: inputSchema15,
|
|
681
801
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
682
802
|
handler: async (args, ctx) => {
|
|
683
803
|
const pending = requireConfirmation(
|
|
@@ -695,15 +815,15 @@ var deleteNicheTool = {
|
|
|
695
815
|
};
|
|
696
816
|
|
|
697
817
|
// src/tools/pause-rank-radar.ts
|
|
698
|
-
import { z as
|
|
699
|
-
var
|
|
700
|
-
rankRadarId:
|
|
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`).")
|
|
701
821
|
};
|
|
702
822
|
var pauseRankRadarTool = {
|
|
703
823
|
name: "pause_rank_radar",
|
|
704
824
|
title: "Pause a Rank Radar",
|
|
705
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.",
|
|
706
|
-
inputSchema:
|
|
826
|
+
inputSchema: inputSchema16,
|
|
707
827
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
708
828
|
handler: async (args, ctx) => {
|
|
709
829
|
await httpPost(
|
|
@@ -716,15 +836,15 @@ var pauseRankRadarTool = {
|
|
|
716
836
|
};
|
|
717
837
|
|
|
718
838
|
// src/tools/resume-rank-radar.ts
|
|
719
|
-
import { z as
|
|
720
|
-
var
|
|
721
|
-
rankRadarId:
|
|
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`).")
|
|
722
842
|
};
|
|
723
843
|
var resumeRankRadarTool = {
|
|
724
844
|
name: "resume_rank_radar",
|
|
725
845
|
title: "Resume a Paused Rank Radar",
|
|
726
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.",
|
|
727
|
-
inputSchema:
|
|
847
|
+
inputSchema: inputSchema17,
|
|
728
848
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
729
849
|
handler: async (args, ctx) => {
|
|
730
850
|
await httpPost(
|
|
@@ -737,10 +857,10 @@ var resumeRankRadarTool = {
|
|
|
737
857
|
};
|
|
738
858
|
|
|
739
859
|
// src/tools/delete-rank-radar.ts
|
|
740
|
-
import { z as
|
|
741
|
-
var
|
|
742
|
-
rankRadarId:
|
|
743
|
-
confirm:
|
|
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(
|
|
744
864
|
"Must be true to proceed \u2014 deleting a Rank Radar destroys its ranking history permanently. Confirm with the user first."
|
|
745
865
|
)
|
|
746
866
|
};
|
|
@@ -748,7 +868,7 @@ var deleteRankRadarTool = {
|
|
|
748
868
|
name: "delete_rank_radar",
|
|
749
869
|
title: "Delete a Rank Radar",
|
|
750
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`.",
|
|
751
|
-
inputSchema:
|
|
871
|
+
inputSchema: inputSchema18,
|
|
752
872
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
753
873
|
handler: async (args, ctx) => {
|
|
754
874
|
const pending = requireConfirmation(
|
|
@@ -766,10 +886,10 @@ var deleteRankRadarTool = {
|
|
|
766
886
|
};
|
|
767
887
|
|
|
768
888
|
// src/tools/add-rank-radar-search-terms.ts
|
|
769
|
-
import { z as
|
|
770
|
-
var
|
|
771
|
-
rankRadarId:
|
|
772
|
-
searchTerms:
|
|
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(
|
|
773
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.`
|
|
774
894
|
)
|
|
775
895
|
};
|
|
@@ -777,7 +897,7 @@ var addRankRadarSearchTermsTool = {
|
|
|
777
897
|
name: "add_rank_radar_search_terms",
|
|
778
898
|
title: "Add Search Terms to a Rank Radar",
|
|
779
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.",
|
|
780
|
-
inputSchema:
|
|
900
|
+
inputSchema: inputSchema19,
|
|
781
901
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
782
902
|
handler: async (args, ctx) => {
|
|
783
903
|
return await httpPost(
|
|
@@ -789,10 +909,10 @@ var addRankRadarSearchTermsTool = {
|
|
|
789
909
|
};
|
|
790
910
|
|
|
791
911
|
// src/tools/pause-rank-radar-search-terms.ts
|
|
792
|
-
import { z as
|
|
793
|
-
var
|
|
794
|
-
rankRadarId:
|
|
795
|
-
rankRadarKeywordIds:
|
|
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(
|
|
796
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."
|
|
797
917
|
)
|
|
798
918
|
};
|
|
@@ -800,7 +920,7 @@ var pauseRankRadarSearchTermsTool = {
|
|
|
800
920
|
name: "pause_rank_radar_search_terms",
|
|
801
921
|
title: "Pause Search Terms of a Rank Radar",
|
|
802
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.",
|
|
803
|
-
inputSchema:
|
|
923
|
+
inputSchema: inputSchema20,
|
|
804
924
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
805
925
|
handler: async (args, ctx) => {
|
|
806
926
|
await httpPost(
|
|
@@ -817,10 +937,10 @@ var pauseRankRadarSearchTermsTool = {
|
|
|
817
937
|
};
|
|
818
938
|
|
|
819
939
|
// src/tools/resume-rank-radar-search-terms.ts
|
|
820
|
-
import { z as
|
|
821
|
-
var
|
|
822
|
-
rankRadarId:
|
|
823
|
-
rankRadarKeywordIds:
|
|
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(
|
|
824
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."
|
|
825
945
|
)
|
|
826
946
|
};
|
|
@@ -828,7 +948,7 @@ var resumeRankRadarSearchTermsTool = {
|
|
|
828
948
|
name: "resume_rank_radar_search_terms",
|
|
829
949
|
title: "Resume Paused Search Terms of a Rank Radar",
|
|
830
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.",
|
|
831
|
-
inputSchema:
|
|
951
|
+
inputSchema: inputSchema21,
|
|
832
952
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
833
953
|
handler: async (args, ctx) => {
|
|
834
954
|
await httpPost(
|
|
@@ -845,16 +965,16 @@ var resumeRankRadarSearchTermsTool = {
|
|
|
845
965
|
};
|
|
846
966
|
|
|
847
967
|
// src/tools/generate-listing-copy.ts
|
|
848
|
-
import { z as
|
|
849
|
-
var
|
|
850
|
-
nicheId:
|
|
851
|
-
strategy:
|
|
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(
|
|
852
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."
|
|
853
973
|
),
|
|
854
|
-
currentListing:
|
|
974
|
+
currentListing: z21.record(z21.union([z21.string(), z21.array(z21.string())])).describe(
|
|
855
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.'
|
|
856
976
|
),
|
|
857
|
-
confirm:
|
|
977
|
+
confirm: z21.boolean().optional().describe(
|
|
858
978
|
"Must be true to proceed \u2014 each generation spends one AI Copywriter prompt from the quota. Confirm the cost with the user first."
|
|
859
979
|
)
|
|
860
980
|
};
|
|
@@ -862,7 +982,7 @@ var generateListingCopyTool = {
|
|
|
862
982
|
name: "generate_listing_copy",
|
|
863
983
|
title: "Generate Listing Copy for a Niche",
|
|
864
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`.",
|
|
865
|
-
inputSchema:
|
|
985
|
+
inputSchema: inputSchema22,
|
|
866
986
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },
|
|
867
987
|
handler: async (args, ctx) => {
|
|
868
988
|
const pending = requireConfirmation(
|
|
@@ -880,16 +1000,16 @@ var generateListingCopyTool = {
|
|
|
880
1000
|
};
|
|
881
1001
|
|
|
882
1002
|
// src/tools/get-listing-copy-generation-status.ts
|
|
883
|
-
import { z as
|
|
884
|
-
var
|
|
885
|
-
nicheId:
|
|
886
|
-
generationId:
|
|
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`.")
|
|
887
1007
|
};
|
|
888
1008
|
var getListingCopyGenerationStatusTool = {
|
|
889
1009
|
name: "get_listing_copy_generation_status",
|
|
890
1010
|
title: "Get Listing Copy Generation Status",
|
|
891
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`.",
|
|
892
|
-
inputSchema:
|
|
1012
|
+
inputSchema: inputSchema23,
|
|
893
1013
|
annotations: { readOnlyHint: true },
|
|
894
1014
|
handler: async (args, ctx) => {
|
|
895
1015
|
return await httpGet(
|
|
@@ -900,16 +1020,16 @@ var getListingCopyGenerationStatusTool = {
|
|
|
900
1020
|
};
|
|
901
1021
|
|
|
902
1022
|
// src/tools/list-seller-profiles.ts
|
|
903
|
-
import { z as
|
|
904
|
-
var
|
|
905
|
-
currentPage:
|
|
906
|
-
pageSize:
|
|
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.")
|
|
907
1027
|
};
|
|
908
1028
|
var listSellerProfilesTool = {
|
|
909
1029
|
name: "list_seller_profiles",
|
|
910
1030
|
title: "List Connected Amazon Seller Profiles",
|
|
911
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).',
|
|
912
|
-
inputSchema:
|
|
1032
|
+
inputSchema: inputSchema24,
|
|
913
1033
|
annotations: { readOnlyHint: true },
|
|
914
1034
|
handler: async (args, ctx) => {
|
|
915
1035
|
return await httpGet(
|
|
@@ -924,15 +1044,15 @@ var listSellerProfilesTool = {
|
|
|
924
1044
|
};
|
|
925
1045
|
|
|
926
1046
|
// src/tools/get-seller-catalog.ts
|
|
927
|
-
import { z as
|
|
1047
|
+
import { z as z25 } from "zod";
|
|
928
1048
|
|
|
929
1049
|
// src/tools/seller-scoped.ts
|
|
930
|
-
import { z as
|
|
1050
|
+
import { z as z24 } from "zod";
|
|
931
1051
|
var sellerScopedInputSchema = {
|
|
932
|
-
sellerId:
|
|
1052
|
+
sellerId: z24.string().min(1).describe(
|
|
933
1053
|
"Amazon seller account ID. Get it from `list_seller_profiles`, or find it on the Connections page at https://2.datadive.tools."
|
|
934
1054
|
),
|
|
935
|
-
marketplace:
|
|
1055
|
+
marketplace: z24.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.')
|
|
936
1056
|
};
|
|
937
1057
|
function sellerMarketplacePath(sellerId, marketplace, suffix) {
|
|
938
1058
|
return `/v1/seller_profiles/${encodeURIComponent(sellerId)}/marketplaces/${encodeURIComponent(
|
|
@@ -941,19 +1061,19 @@ function sellerMarketplacePath(sellerId, marketplace, suffix) {
|
|
|
941
1061
|
}
|
|
942
1062
|
|
|
943
1063
|
// src/tools/get-seller-catalog.ts
|
|
944
|
-
var
|
|
1064
|
+
var inputSchema25 = {
|
|
945
1065
|
...sellerScopedInputSchema,
|
|
946
|
-
search:
|
|
947
|
-
brand:
|
|
948
|
-
status:
|
|
949
|
-
currentPage:
|
|
950
|
-
pageSize:
|
|
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.")
|
|
951
1071
|
};
|
|
952
1072
|
var getSellerCatalogTool = {
|
|
953
1073
|
name: "get_seller_catalog",
|
|
954
1074
|
title: "List Catalog ASINs for a Seller",
|
|
955
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).",
|
|
956
|
-
inputSchema:
|
|
1076
|
+
inputSchema: inputSchema25,
|
|
957
1077
|
annotations: { readOnlyHint: true },
|
|
958
1078
|
handler: async (args, ctx) => {
|
|
959
1079
|
return await httpGet(
|
|
@@ -971,28 +1091,28 @@ var getSellerCatalogTool = {
|
|
|
971
1091
|
};
|
|
972
1092
|
|
|
973
1093
|
// src/tools/get-seller-listing-changes.ts
|
|
974
|
-
import { z as
|
|
975
|
-
var
|
|
1094
|
+
import { z as z26 } from "zod";
|
|
1095
|
+
var inputSchema26 = {
|
|
976
1096
|
...sellerScopedInputSchema,
|
|
977
|
-
types:
|
|
978
|
-
asin:
|
|
979
|
-
brand:
|
|
980
|
-
search:
|
|
981
|
-
startDate:
|
|
982
|
-
endDate:
|
|
983
|
-
sortBy:
|
|
984
|
-
sortOrder:
|
|
985
|
-
includeCorrelations:
|
|
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(
|
|
986
1106
|
"When true, include the ranking/conversion `correlation` object per change (CVR and search-term movement before vs after). Defaults to false."
|
|
987
1107
|
),
|
|
988
|
-
currentPage:
|
|
989
|
-
pageSize:
|
|
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.")
|
|
990
1110
|
};
|
|
991
1111
|
var getSellerListingChangesTool = {
|
|
992
1112
|
name: "get_seller_listing_changes",
|
|
993
1113
|
title: "List Listing Changes for a Seller",
|
|
994
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).",
|
|
995
|
-
inputSchema:
|
|
1115
|
+
inputSchema: inputSchema26,
|
|
996
1116
|
annotations: { readOnlyHint: true },
|
|
997
1117
|
handler: async (args, ctx) => {
|
|
998
1118
|
return await httpGet(
|
|
@@ -1016,14 +1136,78 @@ var getSellerListingChangesTool = {
|
|
|
1016
1136
|
}
|
|
1017
1137
|
};
|
|
1018
1138
|
|
|
1139
|
+
// src/tools/list-ppc-campaigns.ts
|
|
1140
|
+
import { z as z27 } from "zod";
|
|
1141
|
+
var PPC_CAMPAIGNS_MAX_WINDOW_DAYS = 90;
|
|
1142
|
+
var PPC_CAMPAIGNS_MAX_PAGE_SIZE = 50;
|
|
1143
|
+
var inputSchema27 = {
|
|
1144
|
+
...sellerScopedInputSchema,
|
|
1145
|
+
startDate: z27.string().regex(
|
|
1146
|
+
ISO_8601,
|
|
1147
|
+
"startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)"
|
|
1148
|
+
).optional().describe("Start of the reporting window, ISO-8601. Defaults to 30 days before endDate."),
|
|
1149
|
+
endDate: z27.string().regex(
|
|
1150
|
+
ISO_8601,
|
|
1151
|
+
"endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)"
|
|
1152
|
+
).optional().describe(
|
|
1153
|
+
`End of the reporting window, ISO-8601. Defaults to now. At most ${PPC_CAMPAIGNS_MAX_WINDOW_DAYS} days after startDate.`
|
|
1154
|
+
),
|
|
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(
|
|
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."
|
|
1160
|
+
),
|
|
1161
|
+
sortBy: z27.enum([...PPC_CAMPAIGN_BASE_SORT_BY, ...PPC_CAMPAIGN_PLACEMENT_SORT_BY]).optional().describe(
|
|
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.`
|
|
1163
|
+
),
|
|
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.`)
|
|
1168
|
+
};
|
|
1169
|
+
var listPpcCampaignsTool = {
|
|
1170
|
+
name: "list_ppc_campaigns",
|
|
1171
|
+
title: "List PPC Campaigns for a Seller",
|
|
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\`.`,
|
|
1173
|
+
inputSchema: inputSchema27,
|
|
1174
|
+
annotations: { readOnlyHint: true },
|
|
1175
|
+
handler: async (args, ctx) => {
|
|
1176
|
+
if (args.asin && args.parentAsin) {
|
|
1177
|
+
throw new ApiError(
|
|
1178
|
+
"bad_request",
|
|
1179
|
+
400,
|
|
1180
|
+
"Pass either `asin` (one product) or `parentAsin` (its whole variation family), not both."
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
return await httpGet(
|
|
1184
|
+
{ config: ctx.config, toolName: "list_ppc_campaigns" },
|
|
1185
|
+
sellerMarketplacePath(args.sellerId, args.marketplace, "ppc/campaigns"),
|
|
1186
|
+
{
|
|
1187
|
+
startDate: args.startDate,
|
|
1188
|
+
endDate: args.endDate,
|
|
1189
|
+
asin: args.asin,
|
|
1190
|
+
parentAsin: args.parentAsin,
|
|
1191
|
+
search: args.search,
|
|
1192
|
+
includePlacements: args.includePlacements,
|
|
1193
|
+
sortBy: args.sortBy,
|
|
1194
|
+
sortOrder: args.sortOrder,
|
|
1195
|
+
state: args.state,
|
|
1196
|
+
currentPage: args.currentPage,
|
|
1197
|
+
pageSize: args.pageSize
|
|
1198
|
+
}
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1019
1203
|
// src/tools/get-asin-inventory-distribution.ts
|
|
1020
|
-
import { z as
|
|
1021
|
-
var
|
|
1022
|
-
sellerId:
|
|
1204
|
+
import { z as z28 } from "zod";
|
|
1205
|
+
var inputSchema28 = {
|
|
1206
|
+
sellerId: z28.string().min(1).describe(
|
|
1023
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."
|
|
1024
1208
|
),
|
|
1025
|
-
marketplace:
|
|
1026
|
-
asin:
|
|
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(
|
|
1027
1211
|
"Amazon Standard Identification Number (10 uppercase alphanumeric characters, e.g. B08XYZ1234)."
|
|
1028
1212
|
)
|
|
1029
1213
|
};
|
|
@@ -1031,7 +1215,7 @@ var getAsinInventoryDistributionTool = {
|
|
|
1031
1215
|
name: "get_asin_inventory_distribution",
|
|
1032
1216
|
title: "Get Per-Fulfillment-Center Inventory Distribution for an ASIN",
|
|
1033
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.",
|
|
1034
|
-
inputSchema:
|
|
1218
|
+
inputSchema: inputSchema28,
|
|
1035
1219
|
annotations: { readOnlyHint: true },
|
|
1036
1220
|
handler: async (args, ctx) => {
|
|
1037
1221
|
return await httpGet(
|
|
@@ -1044,18 +1228,18 @@ var getAsinInventoryDistributionTool = {
|
|
|
1044
1228
|
};
|
|
1045
1229
|
|
|
1046
1230
|
// src/tools/alert-query.ts
|
|
1047
|
-
import { z as
|
|
1231
|
+
import { z as z29 } from "zod";
|
|
1048
1232
|
var alertsQueryInputSchema = {
|
|
1049
|
-
sellerId:
|
|
1050
|
-
marketplace:
|
|
1051
|
-
status:
|
|
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(
|
|
1052
1236
|
"Lifecycle filter. `active` (default) returns unresolved alerts, `resolved` returns resolved alerts, `all` returns both. Dismissed alerts are never returned."
|
|
1053
1237
|
),
|
|
1054
|
-
updatedSince:
|
|
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(
|
|
1055
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."
|
|
1056
1240
|
),
|
|
1057
|
-
currentPage:
|
|
1058
|
-
pageSize:
|
|
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.")
|
|
1059
1243
|
};
|
|
1060
1244
|
|
|
1061
1245
|
// src/tools/list-indexing-issue-alerts.ts
|
|
@@ -1105,12 +1289,12 @@ var listBlindSpendAlertsTool = {
|
|
|
1105
1289
|
};
|
|
1106
1290
|
|
|
1107
1291
|
// src/tools/get-quota.ts
|
|
1108
|
-
var
|
|
1292
|
+
var inputSchema29 = {};
|
|
1109
1293
|
var getQuotaTool = {
|
|
1110
1294
|
name: "get_quota",
|
|
1111
1295
|
title: "Get Quota Usage",
|
|
1112
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.",
|
|
1113
|
-
inputSchema:
|
|
1297
|
+
inputSchema: inputSchema29,
|
|
1114
1298
|
annotations: { readOnlyHint: true },
|
|
1115
1299
|
handler: async (_args, ctx) => {
|
|
1116
1300
|
return await httpGet({ config: ctx.config, toolName: "get_quota" }, "/v1/quota");
|
|
@@ -1118,20 +1302,20 @@ var getQuotaTool = {
|
|
|
1118
1302
|
};
|
|
1119
1303
|
|
|
1120
1304
|
// src/tools/list-usage.ts
|
|
1121
|
-
import { z as
|
|
1122
|
-
var
|
|
1123
|
-
type:
|
|
1124
|
-
search:
|
|
1125
|
-
startDate:
|
|
1126
|
-
endDate:
|
|
1127
|
-
currentPage:
|
|
1128
|
-
pageSize:
|
|
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.")
|
|
1129
1313
|
};
|
|
1130
1314
|
var listUsageTool = {
|
|
1131
1315
|
name: "list_usage",
|
|
1132
1316
|
title: "List Billable Usage Logs",
|
|
1133
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).",
|
|
1134
|
-
inputSchema:
|
|
1318
|
+
inputSchema: inputSchema30,
|
|
1135
1319
|
annotations: { readOnlyHint: true },
|
|
1136
1320
|
handler: async (args, ctx) => {
|
|
1137
1321
|
return await httpGet(
|
|
@@ -1158,8 +1342,11 @@ var allTools = [
|
|
|
1158
1342
|
getRankingJuiceTool,
|
|
1159
1343
|
listRankRadarsTool,
|
|
1160
1344
|
getRankRadarDataTool,
|
|
1345
|
+
getRankRadarSqpDataTool,
|
|
1346
|
+
getRankRadarPpcDataTool,
|
|
1161
1347
|
createRankRadarTool,
|
|
1162
1348
|
createNicheDiveTool,
|
|
1349
|
+
createNicheDiveFromCompetitorsListTool,
|
|
1163
1350
|
rediveNicheTool,
|
|
1164
1351
|
getDiveStatusTool,
|
|
1165
1352
|
addRankRadarSearchTermsTool,
|
|
@@ -1174,6 +1361,7 @@ var allTools = [
|
|
|
1174
1361
|
listSellerProfilesTool,
|
|
1175
1362
|
getSellerCatalogTool,
|
|
1176
1363
|
getSellerListingChangesTool,
|
|
1364
|
+
listPpcCampaignsTool,
|
|
1177
1365
|
getAsinInventoryDistributionTool,
|
|
1178
1366
|
listIndexingIssueAlertsTool,
|
|
1179
1367
|
listBlindSpendAlertsTool,
|
|
@@ -1181,15 +1369,87 @@ var allTools = [
|
|
|
1181
1369
|
listUsageTool
|
|
1182
1370
|
];
|
|
1183
1371
|
|
|
1372
|
+
// src/whats-new.ts
|
|
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
|
+
},
|
|
1381
|
+
{
|
|
1382
|
+
version: "0.15.0",
|
|
1383
|
+
released: "2026-09-28",
|
|
1384
|
+
items: [
|
|
1385
|
+
"Search Query Performance for Rank Radar keywords (`get_rank_radar_sqp_data`): how shoppers search, click, add to cart and buy, for the whole market and for your product.",
|
|
1386
|
+
"PPC data for Rank Radar keywords (`get_rank_radar_ppc_data`): sponsored rank, spend, sales, ACOS and CPC per keyword, optionally broken down by campaign.",
|
|
1387
|
+
"Your Sponsored Products campaigns (`list_ppc_campaigns`): totals and placement performance per campaign, sortable by any metric, e.g. the top spenders first."
|
|
1388
|
+
]
|
|
1389
|
+
},
|
|
1390
|
+
{
|
|
1391
|
+
version: "0.14.0",
|
|
1392
|
+
released: "2026-09-25",
|
|
1393
|
+
items: [
|
|
1394
|
+
"Rank Radar keywords are now read page by page (`get_rank_radar_data`, up to 100 keywords per page), so large Rank Radars load reliably."
|
|
1395
|
+
]
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
version: "0.13.0",
|
|
1399
|
+
released: "2026-09-09",
|
|
1400
|
+
items: [
|
|
1401
|
+
"Before creating a Rank Radar, you are warned when the same product family is already tracked by another of your Rank Radars."
|
|
1402
|
+
]
|
|
1403
|
+
},
|
|
1404
|
+
{
|
|
1405
|
+
version: "0.12.0",
|
|
1406
|
+
released: "2026-09-07",
|
|
1407
|
+
items: ["Search and sort your niches by name, hero keyword or ASIN (`list_niches`)."]
|
|
1408
|
+
}
|
|
1409
|
+
];
|
|
1410
|
+
var INSTRUCTIONS_RELEASES = 2;
|
|
1411
|
+
function formatEntries(entries) {
|
|
1412
|
+
return entries.map((e) => `${e.released} (v${e.version}):
|
|
1413
|
+
${e.items.map((i) => `- ${i}`).join("\n")}`).join("\n\n");
|
|
1414
|
+
}
|
|
1415
|
+
var SERVER_INSTRUCTIONS = `DataDive tools for Amazon sellers: niche research (niches, keywords, roots, competitors, Ranking Juice), Rank Radar keyword rank tracking with SQP and PPC data, the seller's catalog, listing changes, inventory, PPC campaigns and alerts. Start with a discovery tool: \`list_niches\`, \`list_rank_radars\` or \`list_seller_profiles\`. Tools that spend tokens ask for confirmation first.
|
|
1416
|
+
|
|
1417
|
+
Recently added:
|
|
1418
|
+
${formatEntries(WHATS_NEW.slice(0, INSTRUCTIONS_RELEASES))}
|
|
1419
|
+
|
|
1420
|
+
When one of these additions fits what the user is doing, mention it once. The user can see the full list with the \`whats_new\` prompt.`;
|
|
1421
|
+
var WHATS_NEW_PROMPT = {
|
|
1422
|
+
name: "whats_new",
|
|
1423
|
+
title: "What's new in DataDive",
|
|
1424
|
+
description: "Recent additions to the DataDive tools, newest first."
|
|
1425
|
+
};
|
|
1426
|
+
function whatsNewPromptText() {
|
|
1427
|
+
return `Here is what is new in the DataDive tools, newest first:
|
|
1428
|
+
|
|
1429
|
+
${formatEntries(WHATS_NEW)}
|
|
1430
|
+
|
|
1431
|
+
Summarize this for me in a few short lines, and for each addition give one example question I could ask you.`;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1184
1434
|
// src/server.ts
|
|
1185
1435
|
function requiredScope(tool) {
|
|
1186
1436
|
return tool.annotations?.readOnlyHint === true ? SCOPE_READ : SCOPE_WRITE;
|
|
1187
1437
|
}
|
|
1188
1438
|
function buildServer(config) {
|
|
1189
|
-
const server = new McpServer(
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1439
|
+
const server = new McpServer(
|
|
1440
|
+
{
|
|
1441
|
+
name: "datadive",
|
|
1442
|
+
version: PKG_VERSION
|
|
1443
|
+
},
|
|
1444
|
+
{ instructions: SERVER_INSTRUCTIONS }
|
|
1445
|
+
);
|
|
1446
|
+
server.registerPrompt(
|
|
1447
|
+
WHATS_NEW_PROMPT.name,
|
|
1448
|
+
{ title: WHATS_NEW_PROMPT.title, description: WHATS_NEW_PROMPT.description },
|
|
1449
|
+
() => ({
|
|
1450
|
+
messages: [{ role: "user", content: { type: "text", text: whatsNewPromptText() } }]
|
|
1451
|
+
})
|
|
1452
|
+
);
|
|
1193
1453
|
for (const tool of allTools) {
|
|
1194
1454
|
server.registerTool(
|
|
1195
1455
|
tool.name,
|