@datadive-tools/mcp 0.6.0 → 0.8.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 +7 -4
- package/dist/index.js +186 -29
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +184 -0
- package/dist/lib.js +851 -0
- package/dist/lib.js.map +1 -0
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -18,6 +18,9 @@ except as the `x-api-key` header on requests to `api.datadive.tools`.
|
|
|
18
18
|
- "Run a niche dive on ASIN B08N5WRWNW in the US marketplace with 5 competitors."
|
|
19
19
|
- "Is my dive done yet?"
|
|
20
20
|
- "Start a rank radar tracking 10 keywords for ASIN B08N5WRWNW in niche X."
|
|
21
|
+
- "Which Amazon seller accounts are connected?"
|
|
22
|
+
- "Search my catalog for active 'widget' products in the US."
|
|
23
|
+
- "What price or content changes happened on my listings last week?"
|
|
21
24
|
|
|
22
25
|
## 1. Get a DataDive API key
|
|
23
26
|
|
|
@@ -102,7 +105,10 @@ niches, plus pagination metadata. If you don't, see Troubleshooting below.
|
|
|
102
105
|
| `create_niche_dive` | **Spends dive tokens.** Starts new niche research from a seed ASIN. Async — returns a `diveId` to poll with `get_dive_status`. Requires `confirm: true`. |
|
|
103
106
|
| `get_dive_status` | Poll a dive started by `create_niche_dive`: `in_progress`, `success` (carries the new `nicheId`), or `error`. |
|
|
104
107
|
| `create_rank_radar` | **Spends Search Term tokens.** Starts tracking keyword rankings for an ASIN in a niche. Returns a `rankRadarId`. Requires `confirm: true`. |
|
|
105
|
-
| `
|
|
108
|
+
| `list_seller_profiles` | Paginated list of connected Amazon seller accounts. Discovery step — returns the `sellerId` + `marketplace` the seller-scoped tools below (and the alert tools) need. |
|
|
109
|
+
| `get_seller_catalog` | Paginated catalog of a seller's own ASINs. Filter by `search`, `brand`, and `status` (Active by default). |
|
|
110
|
+
| `get_seller_listing_changes` | Paginated price/content/image changes on a seller's listings. Filter by `types`, `asin`, `brand`, `search`, and a date range; optionally include ranking/conversion `correlation`. |
|
|
111
|
+
| `get_asin_inventory_distribution` | Per-fulfillment-center sellable inventory for an ASIN. Requires `sellerId` from `list_seller_profiles` or your Connections page. |
|
|
106
112
|
| `list_indexing_issue_alerts` | Paginated list of indexing-issue alerts — ASINs no longer indexed for their tracked keywords. Filter by `sellerId`, `marketplace`, `status`, or `updatedSince`. |
|
|
107
113
|
| `list_blind_spend_alerts` | Paginated list of blind-spend alerts — ad spend on search terms with little or no sales, with per-term spend/clicks/CVR. Same filters as above. |
|
|
108
114
|
| `get_quota` | Current quota usage and capacity per billable feature, plus the next refresh date. No arguments. |
|
|
@@ -113,9 +119,6 @@ read-only; the two `create_*` tools below spend tokens (`get_dive_status` only
|
|
|
113
119
|
polls a dive and is read-only) — see
|
|
114
120
|
[Creating dives & rank radars](#creating-dives--rank-radars).
|
|
115
121
|
|
|
116
|
-
> **Coming next:** AI copywriter. We'll prioritize based on usage signal — if
|
|
117
|
-
> you have a request, open an issue.
|
|
118
|
-
|
|
119
122
|
### Creating dives & rank radars
|
|
120
123
|
|
|
121
124
|
`create_niche_dive` and `create_rank_radar` **consume billable tokens and cannot
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
5
|
|
|
6
6
|
// src/config.ts
|
|
7
|
+
var SCOPE_READ = "datadive.read";
|
|
8
|
+
var SCOPE_WRITE = "datadive.write";
|
|
7
9
|
var DEFAULT_BASE_URL = "https://api.datadive.tools";
|
|
8
10
|
var KEY_HELP_URL = "https://2.datadive.tools/api-key";
|
|
9
11
|
function loadConfig(env = process.env) {
|
|
@@ -18,7 +20,7 @@ function loadConfig(env = process.env) {
|
|
|
18
20
|
const autoConfirmWrites = ["1", "true", "yes"].includes(
|
|
19
21
|
(env.DATADIVE_AUTO_CONFIRM_WRITES ?? "").trim().toLowerCase()
|
|
20
22
|
);
|
|
21
|
-
return { apiKey, baseUrl, autoConfirmWrites };
|
|
23
|
+
return { credentials: { kind: "api-key", apiKey }, baseUrl, autoConfirmWrites };
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
// src/server.ts
|
|
@@ -103,7 +105,7 @@ function extractMessage(body) {
|
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
// package.json
|
|
106
|
-
var version = "0.
|
|
108
|
+
var version = "0.8.0";
|
|
107
109
|
|
|
108
110
|
// src/http/client.ts
|
|
109
111
|
var PKG_VERSION = version;
|
|
@@ -142,7 +144,7 @@ async function httpGet(ctx, path, query) {
|
|
|
142
144
|
res = await fetch(url, {
|
|
143
145
|
method: "GET",
|
|
144
146
|
headers: {
|
|
145
|
-
|
|
147
|
+
...authHeaders(ctx.config),
|
|
146
148
|
accept: "application/json",
|
|
147
149
|
"user-agent": userAgent(ctx.toolName)
|
|
148
150
|
}
|
|
@@ -159,7 +161,7 @@ async function httpPost(ctx, path, body) {
|
|
|
159
161
|
res = await fetch(url, {
|
|
160
162
|
method: "POST",
|
|
161
163
|
headers: {
|
|
162
|
-
|
|
164
|
+
...authHeaders(ctx.config),
|
|
163
165
|
accept: "application/json",
|
|
164
166
|
"content-type": "application/json",
|
|
165
167
|
"user-agent": userAgent(ctx.toolName)
|
|
@@ -203,6 +205,10 @@ function buildUrl(baseUrl, path, query) {
|
|
|
203
205
|
function userAgent(toolName) {
|
|
204
206
|
return `datadive-mcp/${PKG_VERSION} tool/${toolName}`;
|
|
205
207
|
}
|
|
208
|
+
function authHeaders(config) {
|
|
209
|
+
const c = config.credentials;
|
|
210
|
+
return c.kind === "bearer" ? { authorization: `Bearer ${c.token}` } : { "x-api-key": c.apiKey };
|
|
211
|
+
}
|
|
206
212
|
function unwrap(body) {
|
|
207
213
|
if (!isObject(body)) return body;
|
|
208
214
|
if (!("data" in body)) return body;
|
|
@@ -225,6 +231,7 @@ var listNichesTool = {
|
|
|
225
231
|
title: "List DataDive Niches",
|
|
226
232
|
description: "Use this first when the user asks about their niches, or to find a `nicheId` for use with `get_niche_keywords`, `get_niche_competitors`, or `get_ranking_juice`. Retrieves a paginated list of Niches. Each Niche represents a market segment or product category being tracked. Returns nicheId, heroKeyword, nicheLabel, marketplace (com/uk/de/...), and latestResearchDate per niche, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
|
|
227
233
|
inputSchema,
|
|
234
|
+
annotations: { readOnlyHint: true },
|
|
228
235
|
handler: async (args, ctx) => {
|
|
229
236
|
return await httpGet(
|
|
230
237
|
{ config: ctx.config, toolName: "list_niches" },
|
|
@@ -244,6 +251,7 @@ var getNicheKeywordsTool = {
|
|
|
244
251
|
title: "Get Master Keyword List for a Niche",
|
|
245
252
|
description: 'Use this when the user asks about keywords, search terms, or search volume for a niche. Retrieves the master keyword list for the specified Niche \u2014 the relevant search terms monitored for ranking and performance metrics. For each keyword, returns search volume, relevancy (numeric score or "Outlier"), and competitor ASIN organic ranks (asinRanks: { ASIN -> rank | null }). Also returns the latestResearchDate.',
|
|
246
253
|
inputSchema: inputSchema2,
|
|
254
|
+
annotations: { readOnlyHint: true },
|
|
247
255
|
handler: async (args, ctx) => {
|
|
248
256
|
return await httpGet(
|
|
249
257
|
{ config: ctx.config, toolName: "get_niche_keywords" },
|
|
@@ -262,6 +270,7 @@ var getNicheRootsTool = {
|
|
|
262
270
|
title: "Get Keyword Roots for a Niche",
|
|
263
271
|
description: "Use this when the user wants to find the highest-impact words across a niche's keywords, asks about 'keyword roots', or wants to prioritize terms for a listing. Retrieves the keyword lexical roots for the Niche \u2014 individual words or word-combinations extracted from the Master Keyword List (e.g. 'bluetooth headphones' yields roots 'bluetooth', 'headphones', 'bluetooth headphones'). Returns `roots` and `normalizedRoots` tables, each item with root, frequency (how many keywords contain it), broadSearchVolume (summed search volume of those keywords), and broadSearchVolumeRatio (0-1, relative to the top root), plus the per-keyword breakdowns and latestResearchDate.",
|
|
264
272
|
inputSchema: inputSchema3,
|
|
273
|
+
annotations: { readOnlyHint: true },
|
|
265
274
|
handler: async (args, ctx) => {
|
|
266
275
|
return await httpGet(
|
|
267
276
|
{ config: ctx.config, toolName: "get_niche_roots" },
|
|
@@ -280,6 +289,7 @@ var getNicheCompetitorsTool = {
|
|
|
280
289
|
title: "Get Competitors and Niche Statistics",
|
|
281
290
|
description: "Use this for competitor ASINs, sales/revenue benchmarks, or niche opportunity scoring. Retrieves the list of Competitors within the specified Niche along with Niche statistics. For the Niche: keyword statistics, opportunity evaluation, benchmark median values, and overall competitor strength assessment. For each Competitor: ASIN, sales, revenue, ratings, reviews, price, image URL, and ranking data.",
|
|
282
291
|
inputSchema: inputSchema4,
|
|
292
|
+
annotations: { readOnlyHint: true },
|
|
283
293
|
handler: async (args, ctx) => {
|
|
284
294
|
return await httpGet(
|
|
285
295
|
{ config: ctx.config, toolName: "get_niche_competitors" },
|
|
@@ -298,6 +308,7 @@ var getRankingJuiceTool = {
|
|
|
298
308
|
title: "Get Ranking Juices for a Niche",
|
|
299
309
|
description: "Use this when the user asks 'how do I rank higher', 'what's my ranking juice', or wants listing-optimization guidance. Retrieves the Ranking Juices for each Competitor within the specified Niche. Ranking Juice is DataDive's proprietary metric identifying the key factors driving search-ranking success for each Competitor's listing. Returns currentListing and optimizedListing breakdowns (rankingJuice + per-property contributions: title, bullets, description) plus per-competitor listing scores and the latestResearchDate.",
|
|
300
310
|
inputSchema: inputSchema5,
|
|
311
|
+
annotations: { readOnlyHint: true },
|
|
301
312
|
handler: async (args, ctx) => {
|
|
302
313
|
return await httpGet(
|
|
303
314
|
{ config: ctx.config, toolName: "get_ranking_juice" },
|
|
@@ -331,6 +342,10 @@ var BILLABLE_FEATURE_TYPES = [
|
|
|
331
342
|
"AI_COPYWRITER_PROMPTS",
|
|
332
343
|
"RANK_RADAR_KEYWORDS"
|
|
333
344
|
];
|
|
345
|
+
var CATALOG_STATUSES = ["Active", "all"];
|
|
346
|
+
var LISTING_CHANGE_TYPES = ["Price", "Content", "Image"];
|
|
347
|
+
var LISTING_CHANGE_SORT_BY = ["date", "type"];
|
|
348
|
+
var SORT_ORDER = ["ASC", "DESC"];
|
|
334
349
|
|
|
335
350
|
// src/tools/list-rank-radars.ts
|
|
336
351
|
var inputSchema6 = {
|
|
@@ -347,6 +362,7 @@ var listRankRadarsTool = {
|
|
|
347
362
|
title: "List Rank Radars",
|
|
348
363
|
description: "Use this to find a `rankRadarId` before calling `get_rank_radar_data`. Filter by `nicheId` if the user has already identified a niche. Retrieves a paginated list of Rank Radars \u2014 keyword-rank trackers monitoring organic and sponsored positions for specific ASINs over time. Each item includes id, asin (with id, krtId, asin), marketplace, keywordCount, title, imageUrl, and summary metrics: top10KW, top10SV, top50KW, top50SV. Supports pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
|
|
349
364
|
inputSchema: inputSchema6,
|
|
365
|
+
annotations: { readOnlyHint: true },
|
|
350
366
|
handler: async (args, ctx) => {
|
|
351
367
|
return await httpGet(
|
|
352
368
|
{ config: ctx.config, toolName: "list_rank_radars" },
|
|
@@ -375,6 +391,7 @@ var getRankRadarDataTool = {
|
|
|
375
391
|
title: "Get Keyword Rankings for a Rank Radar",
|
|
376
392
|
description: "Use this to analyze keyword ranking trends over time. Requires startDate and endDate (yyyy-mm-dd). Retrieves historical keyword ranking data for the specified Rank Radar within the date range. Returns an array of tracked keywords; each has id, keyword, searchVolume, ranks (per-day { date, organicRank, impressionRank }), and any highlight annotations. Use after `list_rank_radars` to discover a `rankRadarId`.",
|
|
377
393
|
inputSchema: inputSchema7,
|
|
394
|
+
annotations: { readOnlyHint: true },
|
|
378
395
|
handler: async (args, ctx) => {
|
|
379
396
|
return await httpGet(
|
|
380
397
|
{ config: ctx.config, toolName: "get_rank_radar_data" },
|
|
@@ -491,22 +508,140 @@ var getDiveStatusTool = {
|
|
|
491
508
|
}
|
|
492
509
|
};
|
|
493
510
|
|
|
494
|
-
// src/tools/
|
|
511
|
+
// src/tools/list-seller-profiles.ts
|
|
495
512
|
import { z as z11 } from "zod";
|
|
496
513
|
var inputSchema11 = {
|
|
497
|
-
|
|
498
|
-
|
|
514
|
+
currentPage: z11.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
515
|
+
pageSize: z11.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
516
|
+
};
|
|
517
|
+
var listSellerProfilesTool = {
|
|
518
|
+
name: "list_seller_profiles",
|
|
519
|
+
title: "List Connected Amazon Seller Profiles",
|
|
520
|
+
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).',
|
|
521
|
+
inputSchema: inputSchema11,
|
|
522
|
+
annotations: { readOnlyHint: true },
|
|
523
|
+
handler: async (args, ctx) => {
|
|
524
|
+
return await httpGet(
|
|
525
|
+
{ config: ctx.config, toolName: "list_seller_profiles" },
|
|
526
|
+
"/v1/seller_profiles",
|
|
527
|
+
{
|
|
528
|
+
currentPage: args.currentPage,
|
|
529
|
+
pageSize: args.pageSize
|
|
530
|
+
}
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
// src/tools/get-seller-catalog.ts
|
|
536
|
+
import { z as z13 } from "zod";
|
|
537
|
+
|
|
538
|
+
// src/tools/seller-scoped.ts
|
|
539
|
+
import { z as z12 } from "zod";
|
|
540
|
+
var sellerScopedInputSchema = {
|
|
541
|
+
sellerId: z12.string().min(1).describe(
|
|
542
|
+
"Amazon seller account ID. Get it from `list_seller_profiles`, or find it on the Connections page at https://2.datadive.tools."
|
|
499
543
|
),
|
|
500
|
-
marketplace:
|
|
501
|
-
|
|
544
|
+
marketplace: z12.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.')
|
|
545
|
+
};
|
|
546
|
+
function sellerMarketplacePath(sellerId, marketplace, suffix) {
|
|
547
|
+
return `/v1/seller_profiles/${encodeURIComponent(sellerId)}/marketplaces/${encodeURIComponent(
|
|
548
|
+
marketplace
|
|
549
|
+
)}/${suffix}`;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// src/tools/get-seller-catalog.ts
|
|
553
|
+
var inputSchema12 = {
|
|
554
|
+
...sellerScopedInputSchema,
|
|
555
|
+
search: z13.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
|
|
556
|
+
brand: z13.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
|
|
557
|
+
status: z13.enum(CATALOG_STATUSES).optional().describe('Listing status filter. "Active" (default) or "all".'),
|
|
558
|
+
currentPage: z13.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
559
|
+
pageSize: z13.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
560
|
+
};
|
|
561
|
+
var getSellerCatalogTool = {
|
|
562
|
+
name: "get_seller_catalog",
|
|
563
|
+
title: "List Catalog ASINs for a Seller",
|
|
564
|
+
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).",
|
|
565
|
+
inputSchema: inputSchema12,
|
|
566
|
+
annotations: { readOnlyHint: true },
|
|
567
|
+
handler: async (args, ctx) => {
|
|
568
|
+
return await httpGet(
|
|
569
|
+
{ config: ctx.config, toolName: "get_seller_catalog" },
|
|
570
|
+
sellerMarketplacePath(args.sellerId, args.marketplace, "catalog"),
|
|
571
|
+
{
|
|
572
|
+
search: args.search,
|
|
573
|
+
brand: args.brand,
|
|
574
|
+
status: args.status,
|
|
575
|
+
currentPage: args.currentPage,
|
|
576
|
+
pageSize: args.pageSize
|
|
577
|
+
}
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
// src/tools/get-seller-listing-changes.ts
|
|
583
|
+
import { z as z14 } from "zod";
|
|
584
|
+
var inputSchema13 = {
|
|
585
|
+
...sellerScopedInputSchema,
|
|
586
|
+
types: z14.array(z14.enum(LISTING_CHANGE_TYPES)).min(1).optional().describe('Filter to specific change types. Omit for all. Any of "Price", "Content", "Image".'),
|
|
587
|
+
asin: z14.string().min(1).optional().describe("Filter to an exact ASIN or parent ASIN."),
|
|
588
|
+
brand: z14.string().min(1).optional().describe("Case-insensitive partial match on brand name."),
|
|
589
|
+
search: z14.string().min(1).optional().describe("Case-insensitive partial match on product title or brand."),
|
|
590
|
+
startDate: z14.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."),
|
|
591
|
+
endDate: z14.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."),
|
|
592
|
+
sortBy: z14.enum(LISTING_CHANGE_SORT_BY).optional().describe('Sort field. "date" (default) or "type".'),
|
|
593
|
+
sortOrder: z14.enum(SORT_ORDER).optional().describe('Sort direction. "DESC" (default) or "ASC".'),
|
|
594
|
+
includeCorrelations: z14.boolean().optional().describe(
|
|
595
|
+
"When true, include the ranking/conversion `correlation` object per change (CVR and search-term movement before vs after). Defaults to false."
|
|
596
|
+
),
|
|
597
|
+
currentPage: z14.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
598
|
+
pageSize: z14.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
599
|
+
};
|
|
600
|
+
var getSellerListingChangesTool = {
|
|
601
|
+
name: "get_seller_listing_changes",
|
|
602
|
+
title: "List Listing Changes for a Seller",
|
|
603
|
+
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).",
|
|
604
|
+
inputSchema: inputSchema13,
|
|
605
|
+
annotations: { readOnlyHint: true },
|
|
606
|
+
handler: async (args, ctx) => {
|
|
607
|
+
return await httpGet(
|
|
608
|
+
{ config: ctx.config, toolName: "get_seller_listing_changes" },
|
|
609
|
+
sellerMarketplacePath(args.sellerId, args.marketplace, "listing-changes"),
|
|
610
|
+
{
|
|
611
|
+
// The API accepts `types` as a comma-separated list.
|
|
612
|
+
types: args.types?.join(","),
|
|
613
|
+
asin: args.asin,
|
|
614
|
+
brand: args.brand,
|
|
615
|
+
search: args.search,
|
|
616
|
+
startDate: args.startDate,
|
|
617
|
+
endDate: args.endDate,
|
|
618
|
+
sortBy: args.sortBy,
|
|
619
|
+
sortOrder: args.sortOrder,
|
|
620
|
+
includeCorrelations: args.includeCorrelations,
|
|
621
|
+
currentPage: args.currentPage,
|
|
622
|
+
pageSize: args.pageSize
|
|
623
|
+
}
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
|
|
628
|
+
// src/tools/get-asin-inventory-distribution.ts
|
|
629
|
+
import { z as z15 } from "zod";
|
|
630
|
+
var inputSchema14 = {
|
|
631
|
+
sellerId: z15.string().min(1).describe(
|
|
632
|
+
"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."
|
|
633
|
+
),
|
|
634
|
+
marketplace: z15.enum(SUPPORTED_MARKETPLACES).describe('Amazon marketplace code, e.g. "com" for amazon.com or "co.uk" for amazon.co.uk.'),
|
|
635
|
+
asin: z15.string().regex(/^[A-Z0-9]{10}$/, "ASIN must be 10 uppercase alphanumeric characters (e.g. B08XYZ1234).").describe(
|
|
502
636
|
"Amazon Standard Identification Number (10 uppercase alphanumeric characters, e.g. B08XYZ1234)."
|
|
503
637
|
)
|
|
504
638
|
};
|
|
505
639
|
var getAsinInventoryDistributionTool = {
|
|
506
640
|
name: "get_asin_inventory_distribution",
|
|
507
641
|
title: "Get Per-Fulfillment-Center Inventory Distribution for an ASIN",
|
|
508
|
-
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 the user's DataDive Connections page
|
|
509
|
-
inputSchema:
|
|
642
|
+
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.",
|
|
643
|
+
inputSchema: inputSchema14,
|
|
644
|
+
annotations: { readOnlyHint: true },
|
|
510
645
|
handler: async (args, ctx) => {
|
|
511
646
|
return await httpGet(
|
|
512
647
|
{ config: ctx.config, toolName: "get_asin_inventory_distribution" },
|
|
@@ -518,18 +653,18 @@ var getAsinInventoryDistributionTool = {
|
|
|
518
653
|
};
|
|
519
654
|
|
|
520
655
|
// src/tools/alert-query.ts
|
|
521
|
-
import { z as
|
|
656
|
+
import { z as z16 } from "zod";
|
|
522
657
|
var alertsQueryInputSchema = {
|
|
523
|
-
sellerId:
|
|
524
|
-
marketplace:
|
|
525
|
-
status:
|
|
658
|
+
sellerId: z16.string().min(1).max(50).optional().describe('Filter to a single connected Amazon seller account ID, e.g. "A1B2C3D4E5".'),
|
|
659
|
+
marketplace: z16.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.'),
|
|
660
|
+
status: z16.enum(ALERT_STATUSES).optional().describe(
|
|
526
661
|
"Lifecycle filter. `active` (default) returns unresolved alerts, `resolved` returns resolved alerts, `all` returns both. Dismissed alerts are never returned."
|
|
527
662
|
),
|
|
528
|
-
updatedSince:
|
|
663
|
+
updatedSince: z16.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(
|
|
529
664
|
"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."
|
|
530
665
|
),
|
|
531
|
-
currentPage:
|
|
532
|
-
pageSize:
|
|
666
|
+
currentPage: z16.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
667
|
+
pageSize: z16.number().int().min(1).max(50).optional().describe("Items per page (max 50). Defaults to 20.")
|
|
533
668
|
};
|
|
534
669
|
|
|
535
670
|
// src/tools/list-indexing-issue-alerts.ts
|
|
@@ -538,6 +673,7 @@ var listIndexingIssueAlertsTool = {
|
|
|
538
673
|
title: "List Indexing-Issue Alerts",
|
|
539
674
|
description: "Use this to find products that may have lost search visibility. Retrieves a paginated list of indexing-issue alerts across the user's connected Amazon seller accounts \u2014 an alert fires when one of their ASINs is no longer indexed for its tracked keywords. By default returns active (unresolved) alerts from the last 30 days; filter by `sellerId`, `marketplace`, `status`, or `updatedSince` (for incremental polling). Each item includes id, asin, title, imageUrl, isParent, sellerId, marketplace, lastAlertedAt, and resolvedAt. Supports pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
|
|
540
675
|
inputSchema: alertsQueryInputSchema,
|
|
676
|
+
annotations: { readOnlyHint: true },
|
|
541
677
|
handler: async (args, ctx) => {
|
|
542
678
|
return await httpGet(
|
|
543
679
|
{ config: ctx.config, toolName: "list_indexing_issue_alerts" },
|
|
@@ -560,6 +696,7 @@ var listBlindSpendAlertsTool = {
|
|
|
560
696
|
title: "List Blind-Spend (Wasted-Spend) Alerts",
|
|
561
697
|
description: "Use this to find wasted PPC ad spend. Retrieves a paginated list of blind-spend alerts across the user's connected Amazon seller accounts \u2014 an alert flags ad spend on customer search terms that produced little or no sales. By default returns active (unresolved) alerts from the last 30 days; filter by `sellerId`, `marketplace`, `status`, or `updatedSince` (for incremental polling). Each item includes id, asin, title, imageUrl, sellerId, marketplace, lastAlertedAt, resolvedAt, wastedSpend (total ad spend across unresolved terms), totalKeywordCount, unresolvedKeywordCount, and searchTerms \u2014 the unresolved wasted-spend search terms, each with term, spend, sales, clicks, cvr (0-1), and impressions. Supports pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).",
|
|
562
698
|
inputSchema: alertsQueryInputSchema,
|
|
699
|
+
annotations: { readOnlyHint: true },
|
|
563
700
|
handler: async (args, ctx) => {
|
|
564
701
|
return await httpGet(
|
|
565
702
|
{ config: ctx.config, toolName: "list_blind_spend_alerts" },
|
|
@@ -577,32 +714,34 @@ var listBlindSpendAlertsTool = {
|
|
|
577
714
|
};
|
|
578
715
|
|
|
579
716
|
// src/tools/get-quota.ts
|
|
580
|
-
var
|
|
717
|
+
var inputSchema15 = {};
|
|
581
718
|
var getQuotaTool = {
|
|
582
719
|
name: "get_quota",
|
|
583
720
|
title: "Get Quota Usage",
|
|
584
721
|
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.",
|
|
585
|
-
inputSchema:
|
|
722
|
+
inputSchema: inputSchema15,
|
|
723
|
+
annotations: { readOnlyHint: true },
|
|
586
724
|
handler: async (_args, ctx) => {
|
|
587
725
|
return await httpGet({ config: ctx.config, toolName: "get_quota" }, "/v1/quota");
|
|
588
726
|
}
|
|
589
727
|
};
|
|
590
728
|
|
|
591
729
|
// src/tools/list-usage.ts
|
|
592
|
-
import { z as
|
|
593
|
-
var
|
|
594
|
-
type:
|
|
595
|
-
search:
|
|
596
|
-
startDate:
|
|
597
|
-
endDate:
|
|
598
|
-
currentPage:
|
|
599
|
-
pageSize:
|
|
730
|
+
import { z as z17 } from "zod";
|
|
731
|
+
var inputSchema16 = {
|
|
732
|
+
type: z17.enum(BILLABLE_FEATURE_TYPES).optional().describe("Filter to a single billable feature type. Omit to return all types."),
|
|
733
|
+
search: z17.string().min(1).optional().describe("Case-insensitive partial match on the user's name or email."),
|
|
734
|
+
startDate: z17.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."),
|
|
735
|
+
endDate: z17.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."),
|
|
736
|
+
currentPage: z17.number().int().min(1).optional().describe("Page number, 1-indexed. Defaults to 1."),
|
|
737
|
+
pageSize: z17.number().int().min(1).max(200).optional().describe("Items per page (max 200). Defaults to 50.")
|
|
600
738
|
};
|
|
601
739
|
var listUsageTool = {
|
|
602
740
|
name: "list_usage",
|
|
603
741
|
title: "List Billable Usage Logs",
|
|
604
742
|
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).",
|
|
605
|
-
inputSchema:
|
|
743
|
+
inputSchema: inputSchema16,
|
|
744
|
+
annotations: { readOnlyHint: true },
|
|
606
745
|
handler: async (args, ctx) => {
|
|
607
746
|
return await httpGet(
|
|
608
747
|
{ config: ctx.config, toolName: "list_usage" },
|
|
@@ -631,6 +770,9 @@ var allTools = [
|
|
|
631
770
|
createRankRadarTool,
|
|
632
771
|
createNicheDiveTool,
|
|
633
772
|
getDiveStatusTool,
|
|
773
|
+
listSellerProfilesTool,
|
|
774
|
+
getSellerCatalogTool,
|
|
775
|
+
getSellerListingChangesTool,
|
|
634
776
|
getAsinInventoryDistributionTool,
|
|
635
777
|
listIndexingIssueAlertsTool,
|
|
636
778
|
listBlindSpendAlertsTool,
|
|
@@ -639,6 +781,9 @@ var allTools = [
|
|
|
639
781
|
];
|
|
640
782
|
|
|
641
783
|
// src/server.ts
|
|
784
|
+
function requiredScope(tool) {
|
|
785
|
+
return tool.annotations?.readOnlyHint === true ? SCOPE_READ : SCOPE_WRITE;
|
|
786
|
+
}
|
|
642
787
|
function buildServer(config) {
|
|
643
788
|
const server = new McpServer({
|
|
644
789
|
name: "datadive",
|
|
@@ -654,6 +799,18 @@ function buildServer(config) {
|
|
|
654
799
|
annotations: tool.annotations
|
|
655
800
|
},
|
|
656
801
|
async (args) => {
|
|
802
|
+
const scope = requiredScope(tool);
|
|
803
|
+
if (config.scopes && !config.scopes.includes(scope)) {
|
|
804
|
+
return {
|
|
805
|
+
isError: true,
|
|
806
|
+
content: [
|
|
807
|
+
{
|
|
808
|
+
type: "text",
|
|
809
|
+
text: `Insufficient scope: this tool requires the "${scope}" scope, which was not granted to this connection. Reconnect DataDive and enable the missing permission to use it.`
|
|
810
|
+
}
|
|
811
|
+
]
|
|
812
|
+
};
|
|
813
|
+
}
|
|
657
814
|
try {
|
|
658
815
|
const data = await tool.handler(args, { config });
|
|
659
816
|
const content = [
|