@datadive-tools/mcp 0.7.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/dist/index.js +37 -4
- 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/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" },
|
|
@@ -351,6 +362,7 @@ var listRankRadarsTool = {
|
|
|
351
362
|
title: "List Rank Radars",
|
|
352
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).",
|
|
353
364
|
inputSchema: inputSchema6,
|
|
365
|
+
annotations: { readOnlyHint: true },
|
|
354
366
|
handler: async (args, ctx) => {
|
|
355
367
|
return await httpGet(
|
|
356
368
|
{ config: ctx.config, toolName: "list_rank_radars" },
|
|
@@ -379,6 +391,7 @@ var getRankRadarDataTool = {
|
|
|
379
391
|
title: "Get Keyword Rankings for a Rank Radar",
|
|
380
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`.",
|
|
381
393
|
inputSchema: inputSchema7,
|
|
394
|
+
annotations: { readOnlyHint: true },
|
|
382
395
|
handler: async (args, ctx) => {
|
|
383
396
|
return await httpGet(
|
|
384
397
|
{ config: ctx.config, toolName: "get_rank_radar_data" },
|
|
@@ -628,6 +641,7 @@ var getAsinInventoryDistributionTool = {
|
|
|
628
641
|
title: "Get Per-Fulfillment-Center Inventory Distribution for an ASIN",
|
|
629
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.",
|
|
630
643
|
inputSchema: inputSchema14,
|
|
644
|
+
annotations: { readOnlyHint: true },
|
|
631
645
|
handler: async (args, ctx) => {
|
|
632
646
|
return await httpGet(
|
|
633
647
|
{ config: ctx.config, toolName: "get_asin_inventory_distribution" },
|
|
@@ -659,6 +673,7 @@ var listIndexingIssueAlertsTool = {
|
|
|
659
673
|
title: "List Indexing-Issue Alerts",
|
|
660
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).",
|
|
661
675
|
inputSchema: alertsQueryInputSchema,
|
|
676
|
+
annotations: { readOnlyHint: true },
|
|
662
677
|
handler: async (args, ctx) => {
|
|
663
678
|
return await httpGet(
|
|
664
679
|
{ config: ctx.config, toolName: "list_indexing_issue_alerts" },
|
|
@@ -681,6 +696,7 @@ var listBlindSpendAlertsTool = {
|
|
|
681
696
|
title: "List Blind-Spend (Wasted-Spend) Alerts",
|
|
682
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).",
|
|
683
698
|
inputSchema: alertsQueryInputSchema,
|
|
699
|
+
annotations: { readOnlyHint: true },
|
|
684
700
|
handler: async (args, ctx) => {
|
|
685
701
|
return await httpGet(
|
|
686
702
|
{ config: ctx.config, toolName: "list_blind_spend_alerts" },
|
|
@@ -704,6 +720,7 @@ var getQuotaTool = {
|
|
|
704
720
|
title: "Get Quota Usage",
|
|
705
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.",
|
|
706
722
|
inputSchema: inputSchema15,
|
|
723
|
+
annotations: { readOnlyHint: true },
|
|
707
724
|
handler: async (_args, ctx) => {
|
|
708
725
|
return await httpGet({ config: ctx.config, toolName: "get_quota" }, "/v1/quota");
|
|
709
726
|
}
|
|
@@ -724,6 +741,7 @@ var listUsageTool = {
|
|
|
724
741
|
title: "List Billable Usage Logs",
|
|
725
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).",
|
|
726
743
|
inputSchema: inputSchema16,
|
|
744
|
+
annotations: { readOnlyHint: true },
|
|
727
745
|
handler: async (args, ctx) => {
|
|
728
746
|
return await httpGet(
|
|
729
747
|
{ config: ctx.config, toolName: "list_usage" },
|
|
@@ -763,6 +781,9 @@ var allTools = [
|
|
|
763
781
|
];
|
|
764
782
|
|
|
765
783
|
// src/server.ts
|
|
784
|
+
function requiredScope(tool) {
|
|
785
|
+
return tool.annotations?.readOnlyHint === true ? SCOPE_READ : SCOPE_WRITE;
|
|
786
|
+
}
|
|
766
787
|
function buildServer(config) {
|
|
767
788
|
const server = new McpServer({
|
|
768
789
|
name: "datadive",
|
|
@@ -778,6 +799,18 @@ function buildServer(config) {
|
|
|
778
799
|
annotations: tool.annotations
|
|
779
800
|
},
|
|
780
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
|
+
}
|
|
781
814
|
try {
|
|
782
815
|
const data = await tool.handler(args, { config });
|
|
783
816
|
const content = [
|