@datadive-tools/mcp 0.9.0 → 0.11.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 +41 -16
- package/dist/index.js +342 -73
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +27 -4
- package/dist/lib.js +342 -73
- package/dist/lib.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/server.ts","../src/tools/list-niches.ts","../src/http/errors.ts","../package.json","../src/http/client.ts","../src/tools/get-niche-keywords.ts","../src/tools/get-niche-roots.ts","../src/tools/get-niche-competitors.ts","../src/tools/get-ranking-juice.ts","../src/tools/list-rank-radars.ts","../src/types/api.ts","../src/tools/get-rank-radar-data.ts","../src/tools/create-rank-radar.ts","../src/tools/confirm.ts","../src/tools/create-niche-dive.ts","../src/tools/redive-niche.ts","../src/tools/get-dive-status.ts","../src/tools/list-seller-profiles.ts","../src/tools/get-seller-catalog.ts","../src/tools/seller-scoped.ts","../src/tools/get-seller-listing-changes.ts","../src/tools/get-asin-inventory-distribution.ts","../src/tools/alert-query.ts","../src/tools/list-indexing-issue-alerts.ts","../src/tools/list-blind-spend-alerts.ts","../src/tools/get-quota.ts","../src/tools/list-usage.ts","../src/tools/index.ts"],"sourcesContent":["/**\n * Entry point for the datadive-mcp stdio binary.\n *\n * Wired via package.json#bin -> dist/index.js so `npx -y @datadive-tools/mcp` runs it.\n * The shebang is added at build time by tsup.config.ts (banner.js).\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { loadConfig } from \"./config.js\";\nimport { buildServer } from \"./server.js\";\n\nasync function main() {\n let config;\n try {\n config = loadConfig();\n } catch (err) {\n // Config errors (e.g. missing API key) are user-facing; print to stderr and exit\n // non-zero so the MCP client surfaces a real error rather than silently disconnecting.\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[datadive-mcp] ${msg}\\n`);\n process.exit(1);\n }\n\n const server = buildServer(config);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n const msg = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`[datadive-mcp] fatal: ${msg}\\n`);\n process.exit(1);\n});\n","/**\n * Loads and validates the server's runtime configuration from environment.\n *\n * Required:\n * DATADIVE_API_KEY - your DataDive API key (generate at 2.datadive.tools/api-key)\n *\n * Optional:\n * DATADIVE_API_BASE_URL - override the API base URL (defaults to https://api.datadive.tools)\n * DATADIVE_AUTO_CONFIRM_WRITES - skip the confirm gate on token-spending write tools\n * (create_niche_dive, create_rank_radar). Truthy =\n * \"1\"/\"true\"/\"yes\" (case-insensitive). Defaults to false.\n *\n * The env-based loader always produces api-key credentials (the local stdio path).\n * A remote host embedding this package builds a Config directly with bearer\n * credentials (the OAuth path) — see Credentials.\n */\n\n/** OAuth scope covering the read tools. */\nexport const SCOPE_READ = \"datadive.read\";\n/** OAuth scope covering the token-spending write tools. */\nexport const SCOPE_WRITE = \"datadive.write\";\n\n/**\n * How the HTTP client authenticates against the DataDive /v1 API.\n * - \"api-key\": the local stdio path — sends `x-api-key` (DATADIVE_API_KEY).\n * - \"bearer\": the remote/OAuth path — sends `Authorization: Bearer <token>`.\n * The token is read at request time, so a host may mutate the\n * credentials object to rotate a refreshed access token in place.\n */\nexport type Credentials = { kind: \"api-key\"; apiKey: string } | { kind: \"bearer\"; token: string };\n\nexport interface Config {\n credentials: Credentials;\n baseUrl: string;\n /**\n * When true, the token-spending write tools proceed without requiring `confirm: true`.\n * A user-controlled \"don't ask me again\" opt-out, set via DATADIVE_AUTO_CONFIRM_WRITES.\n */\n autoConfirmWrites: boolean;\n /**\n * OAuth scopes granted to this session. When set, tool calls are gated:\n * read tools require SCOPE_READ and write tools SCOPE_WRITE (see server.ts).\n * Undefined (the stdio/api-key path) means no scope gating.\n */\n scopes?: readonly string[];\n}\n\nconst DEFAULT_BASE_URL = \"https://api.datadive.tools\";\nconst KEY_HELP_URL = \"https://2.datadive.tools/api-key\";\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\n const apiKey = (env.DATADIVE_API_KEY ?? \"\").trim();\n if (!apiKey) {\n throw new Error(\n `DATADIVE_API_KEY environment variable is required. Generate one at ${KEY_HELP_URL} ` +\n `and add it to your MCP client config under env.DATADIVE_API_KEY.`,\n );\n }\n\n const rawBase = (env.DATADIVE_API_BASE_URL ?? DEFAULT_BASE_URL).trim();\n // Normalize: strip trailing slashes so we can append \"/v1/...\" paths cleanly.\n const baseUrl = rawBase.replace(/\\/+$/, \"\");\n\n const autoConfirmWrites = [\"1\", \"true\", \"yes\"].includes(\n (env.DATADIVE_AUTO_CONFIRM_WRITES ?? \"\").trim().toLowerCase(),\n );\n\n return { credentials: { kind: \"api-key\", apiKey }, baseUrl, autoConfirmWrites };\n}\n","/**\n * Builds the MCP server: registers each tool from the registry with the SDK.\n *\n * Error handling: if a tool handler throws (most commonly an ApiError from the\n * HTTP client), we surface the message back through MCP as a tool-level error\n * (`isError: true`). The LLM sees the message and can react — e.g. tell the\n * user to regenerate their API key on a 401.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { AnyTool } from \"./tools/types.js\";\nimport { SCOPE_READ, SCOPE_WRITE, type Config } from \"./config.js\";\nimport { allTools } from \"./tools/index.js\";\nimport { ApiError } from \"./http/errors.js\";\nimport { PKG_VERSION, takeUpgradeNotice } from \"./http/client.js\";\n\n/**\n * Returns the OAuth scope a tool call needs: SCOPE_READ for read-only tools,\n * SCOPE_WRITE for the token-spending writes. Classification comes from the\n * `readOnlyHint` annotation, which every tool in the registry carries.\n */\nexport function requiredScope(tool: Pick<AnyTool, \"annotations\">): string {\n return tool.annotations?.readOnlyHint === true ? SCOPE_READ : SCOPE_WRITE;\n}\n\nexport function buildServer(config: Config): McpServer {\n const server = new McpServer({\n name: \"datadive\",\n version: PKG_VERSION,\n });\n\n for (const tool of allTools) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: tool.annotations,\n },\n async (args: Record<string, unknown>) => {\n // OAuth scope gate (remote path only — config.scopes is unset over stdio).\n // The backend enforces scopes on /v1 as well; failing here just gives the\n // model a clearer error without a round-trip.\n const scope = requiredScope(tool);\n if (config.scopes && !config.scopes.includes(scope)) {\n return {\n isError: true,\n content: [\n {\n type: \"text\" as const,\n text:\n `Insufficient scope: this tool requires the \"${scope}\" scope, which was not granted ` +\n `to this connection. Reconnect DataDive and enable the missing permission to use it.`,\n },\n ],\n };\n }\n try {\n const data = await tool.handler(args, { config });\n const content = [\n {\n type: \"text\" as const,\n text: JSON.stringify(data, null, 2),\n },\n ];\n // If the backend signaled a newer release, append a one-time upgrade\n // nudge so the model relays it to the user. takeUpgradeNotice() returns\n // null after the first time, so this fires at most once per session.\n const notice = takeUpgradeNotice();\n if (notice) {\n content.push({\n type: \"text\" as const,\n text: `⚠️ datadive-mcp update available — ${notice}`,\n });\n }\n return { content };\n } catch (err) {\n return {\n isError: true,\n content: [\n {\n type: \"text\" as const,\n text: err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err),\n },\n ],\n };\n }\n },\n );\n }\n\n return server;\n}\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { NicheList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n currentPage: z\n .number()\n .int()\n .min(1)\n .optional()\n .describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\"Items per page (max 100). Defaults to 20.\"),\n};\n\nexport const listNichesTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_niches\",\n title: \"List DataDive Niches\",\n description:\n \"Use this first when the user asks about their niches, or to find a `nicheId` for use with \" +\n \"`get_niche_keywords`, `get_niche_competitors`, or `get_ranking_juice`. \" +\n \"Retrieves a paginated list of Niches. Each Niche represents a market segment or product \" +\n \"category being tracked. Returns nicheId, heroKeyword, nicheLabel, marketplace (com/uk/de/...), \" +\n \"and latestResearchDate per niche, plus pagination metadata (currentPage, pageSize, total, \" +\n \"lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<NicheList>(\n { config: ctx.config, toolName: \"list_niches\" },\n \"/v1/niches\",\n { currentPage: args.currentPage, pageSize: args.pageSize },\n );\n },\n};\n","/**\n * Error mapping for the DataDive /v1 API.\n *\n * The MCP server is a thin HTTP shim — when api.datadive.tools returns an error,\n * we want the LLM (and ultimately the user) to see a useful, actionable message\n * rather than \"Request failed\". `ApiError.fromHttp` turns an HTTP status + body\n * into one of those messages.\n *\n * Source of truth for status semantics:\n * datadive-backend/src/external-api/auth/api-key.guard.ts (401/402 logic)\n * datadive-backend/src/external-api/external-api-v1.controller.ts (per-route docs)\n */\n\nconst KEY_HELP_URL = \"https://2.datadive.tools/api-key\";\n\nexport type ApiErrorKind =\n | \"auth\"\n | \"payment\"\n | \"forbidden\"\n | \"not_found\"\n | \"bad_request\"\n | \"rate_limit\"\n | \"server\"\n | \"network\"\n | \"http\";\n\nexport class ApiError extends Error {\n public readonly kind: ApiErrorKind;\n public readonly status: number;\n public readonly body: unknown;\n\n constructor(kind: ApiErrorKind, status: number, message: string, body?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.kind = kind;\n this.status = status;\n this.body = body;\n }\n\n static fromHttp(status: number, body: unknown): ApiError {\n const serverMsg = extractMessage(body);\n\n if (status === 400) {\n return new ApiError(\n \"bad_request\",\n 400,\n serverMsg ? `Bad request: ${serverMsg}` : \"Bad request: invalid parameters.\",\n body,\n );\n }\n if (status === 401) {\n return new ApiError(\n \"auth\",\n 401,\n `Authentication failed: your DATADIVE_API_KEY is invalid or expired. Generate a new key at ${KEY_HELP_URL}.`,\n body,\n );\n }\n if (status === 402) {\n return new ApiError(\n \"payment\",\n 402,\n \"Subscription is inactive or paused. Resume billing at https://2.datadive.tools to use the API.\",\n body,\n );\n }\n if (status === 403) {\n return new ApiError(\n \"forbidden\",\n 403,\n serverMsg\n ? `Forbidden: ${serverMsg}`\n : \"Forbidden: this API key cannot access the requested resource.\",\n body,\n );\n }\n if (status === 404) {\n return new ApiError(\"not_found\", 404, serverMsg ? `Not found: ${serverMsg}` : \"Resource not found.\", body);\n }\n if (status === 429) {\n return new ApiError(\"rate_limit\", 429, \"Rate limit exceeded. Wait a few seconds and retry.\", body);\n }\n if (status >= 500) {\n return new ApiError(\n \"server\",\n status,\n `DataDive API error (${status}). ${serverMsg ?? \"Please try again.\"}`.trim(),\n body,\n );\n }\n return new ApiError(\"http\", status, serverMsg ?? `HTTP ${status}`, body);\n }\n}\n\nfunction extractMessage(body: unknown): string | null {\n if (!body || typeof body !== \"object\") return null;\n const b = body as Record<string, unknown>;\n // NestJS conventional error shapes: { message: string | string[] } or { error, message }.\n if (typeof b.message === \"string\") return b.message;\n if (Array.isArray(b.message) && b.message.every((m) => typeof m === \"string\")) {\n return (b.message as string[]).join(\"; \");\n }\n if (typeof b.error === \"string\") return b.error;\n return null;\n}\n","{\n \"name\": \"@datadive-tools/mcp\",\n \"version\": \"0.9.0\",\n \"description\": \"MCP server for DataDive — query your niches, keywords, competitors, and Rank Radar data from Claude Desktop, Claude Code, or Cursor.\",\n \"type\": \"module\",\n \"bin\": {\n \"datadive-mcp\": \"./dist/index.js\"\n },\n \"main\": \"./dist/lib.js\",\n \"types\": \"./dist/lib.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/lib.d.ts\",\n \"import\": \"./dist/lib.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"engines\": {\n \"node\": \">=22\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:coverage\": \"vitest run --coverage --coverage.include=\\\"src/**\\\" --coverage.reporter=json-summary --coverage.reporter=text\",\n \"test:watch\": \"vitest\",\n \"test:smoke\": \"DATADIVE_SMOKE=1 vitest run test/smoke.test.ts\",\n \"lint\": \"eslint .\",\n \"typecheck\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run lint && npm run typecheck && npm run test && npm run build\",\n \"prepare\": \"npm run build\"\n },\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"datadive\",\n \"amazon\",\n \"claude\"\n ],\n \"author\": \"DataDive\",\n \"license\": \"MIT\",\n \"homepage\": \"https://github.com/Data-Dive-Tools/datadive-mcp\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Data-Dive-Tools/datadive-mcp.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/Data-Dive-Tools/datadive-mcp/issues\"\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.0.0\",\n \"zod\": \"^3.23.0\"\n },\n \"devDependencies\": {\n \"@changesets/cli\": \"^2.27.0\",\n \"@types/node\": \"^20.0.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.0.0\",\n \"@typescript-eslint/parser\": \"^8.0.0\",\n \"@vitest/coverage-v8\": \"4.1.8\",\n \"eslint\": \"^9.0.0\",\n \"prettier\": \"^3.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"~5.9.0\",\n \"vitest\": \"4.1.8\"\n }\n}\n","/**\n * HTTP client for the DataDive /v1 API.\n *\n * Responsibilities:\n * 1. Attach auth from config.credentials to every request — `x-api-key` (local\n * stdio path) or `Authorization: Bearer` (remote/OAuth path).\n * 2. Tag the User-Agent with the package version + tool name so backend access\n * logs can attribute usage per tool (no separate telemetry endpoint needed).\n * 3. Map HTTP errors via `ApiError.fromHttp`.\n * 4. Unwrap the `ResponseDto<T>` envelope (`{ message?, success?, data }`)\n * while preserving bare `PaginationResponse<T>` bodies (which need their\n * pagination metadata to reach the LLM).\n *\n * Envelope detection (verified against datadive-backend):\n * - GET /v1/niches -> NicheList extends PaginationResponse<NicheItem> (BARE)\n * - GET /v1/niches/rank-radars -> ResponseDto<ExternalRankRadarListResponseDto>\n * where the inner DTO IS itself paginated.\n * - All other read endpoints -> ResponseDto<T> (UNWRAP)\n *\n * Heuristic: if the body has `data` AND any pagination key (currentPage, lastPage,\n * hasNext, hasPrev, pageSize, total), it's a PaginationResponse — return as-is.\n * Otherwise if the body has `data`, it's a ResponseDto envelope — return body.data.\n *\n * Cite: datadive-backend/src/common/pagination/pagination.dto.ts (pagination keys)\n * datadive-backend/src/common/dto/response.dto.ts (envelope shape)\n */\n\nimport type { Config } from \"../config.js\";\nimport { ApiError } from \"./errors.js\";\nimport { version } from \"../../package.json\";\n\n/**\n * The running package version, inlined from package.json at build time by\n * tsup/esbuild (and by vite under vitest). Single source of truth — no manual\n * bump here, so it can never drift from what's published to npm.\n *\n * Written into the User-Agent header, and compared against the backend's\n * advertised latest version (see noteLatestVersion) to nudge stale installs.\n */\nexport const PKG_VERSION: string = version;\n\n/** Response header the DataDive backend uses to advertise the latest published MCP version. */\nconst LATEST_VERSION_HEADER = \"x-datadive-mcp-latest\";\n\n/**\n * One-time upgrade notice. Armed when the backend advertises a version newer than\n * the one we're running; consumed (and then suppressed for the rest of the session)\n * by takeUpgradeNotice(). We surface it once per process so we don't nag on every call.\n */\nlet pendingUpgradeNotice: string | null = null;\nlet hasWarned = false;\n\n/**\n * Compares dotted version strings (major.minor.patch). Pre-release / build suffixes\n * are stripped — a stable release is the signal we act on. Returns true iff `latest`\n * is strictly newer than `current`. Exported for unit testing.\n */\nexport function isNewerVersion(latest: string, current: string): boolean {\n const parse = (v: string) => v.split(/[-+]/)[0]!.split(\".\").map((n) => parseInt(n, 10) || 0);\n const a = parse(latest);\n const b = parse(current);\n for (let i = 0; i < 3; i++) {\n const da = a[i] ?? 0;\n const db = b[i] ?? 0;\n if (da !== db) return da > db;\n }\n return false;\n}\n\n/** Records the advertised latest version; arms a one-time notice if we're behind. Never throws. */\nfunction noteLatestVersion(latest: string | null): void {\n if (!latest || hasWarned) return;\n if (isNewerVersion(latest, PKG_VERSION)) {\n pendingUpgradeNotice =\n `A newer version of datadive-mcp is available (${latest}; you are running ${PKG_VERSION}). ` +\n `Update by reinstalling the latest from npm — e.g. \\`npm install -g @datadive-tools/mcp@latest\\` — ` +\n `or bump the @datadive-tools/mcp version in your MCP client config, then restart the client.`;\n }\n}\n\n/**\n * Returns a pending upgrade notice once, then suppresses further notices for the rest\n * of the session. Called when assembling a tool result so the LLM can relay it to the\n * user. Returns null when there's nothing to surface.\n */\nexport function takeUpgradeNotice(): string | null {\n if (!pendingUpgradeNotice) return null;\n const notice = pendingUpgradeNotice;\n pendingUpgradeNotice = null;\n hasWarned = true;\n return notice;\n}\n\nconst PAGINATION_KEYS = [\"currentPage\", \"lastPage\", \"hasNext\", \"hasPrev\", \"pageSize\", \"total\"] as const;\n\nexport interface RequestContext {\n config: Config;\n /** Tool name used to tag the User-Agent header for per-tool log attribution. */\n toolName: string;\n}\n\nexport async function httpGet<T>(ctx: RequestContext, path: string, query?: Record<string, unknown>): Promise<T> {\n const url = buildUrl(ctx.config.baseUrl, path, query);\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"GET\",\n headers: {\n ...authHeaders(ctx.config),\n accept: \"application/json\",\n \"user-agent\": userAgent(ctx.toolName),\n },\n });\n } catch (e) {\n throw networkError(url, e);\n }\n\n return handleResponse<T>(res);\n}\n\n/**\n * POST a JSON body to a /v1 write endpoint. Mirrors httpGet (same auth/UA headers,\n * version notice, error mapping, and envelope unwrapping) but adds a JSON request\n * body. The create endpoints return bare objects (no ResponseDto envelope), which\n * unwrap() passes through untouched.\n */\nexport async function httpPost<T>(ctx: RequestContext, path: string, body: unknown): Promise<T> {\n const url = buildUrl(ctx.config.baseUrl, path);\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...authHeaders(ctx.config),\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"user-agent\": userAgent(ctx.toolName),\n },\n body: JSON.stringify(body),\n });\n } catch (e) {\n throw networkError(url, e);\n }\n\n return handleResponse<T>(res);\n}\n\nfunction networkError(url: string, e: unknown): ApiError {\n const msg = e instanceof Error ? e.message : String(e);\n return new ApiError(\"network\", 0, `Network error reaching ${url}: ${msg}`);\n}\n\n/**\n * Shared response handling for httpGet/httpPost: arm the one-time upgrade notice,\n * parse the body, map HTTP errors via ApiError, and unwrap the ResponseDto envelope.\n */\nasync function handleResponse<T>(res: Response): Promise<T> {\n // The backend advertises the latest published MCP version on every response\n // (including errors); arm a one-time upgrade notice if we're behind.\n noteLatestVersion(res.headers.get(LATEST_VERSION_HEADER));\n\n const text = await res.text();\n let body: unknown;\n try {\n body = text.length > 0 ? JSON.parse(text) : null;\n } catch {\n body = text;\n }\n\n if (!res.ok) {\n throw ApiError.fromHttp(res.status, body);\n }\n\n return unwrap<T>(body);\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: Record<string, unknown>): string {\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n const u = new URL(`${baseUrl}${normalizedPath}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined || v === null) continue;\n u.searchParams.set(k, String(v));\n }\n }\n return u.toString();\n}\n\nfunction userAgent(toolName: string): string {\n return `datadive-mcp/${PKG_VERSION} tool/${toolName}`;\n}\n\n/**\n * Auth header(s) for the configured credentials: `x-api-key` on the local stdio\n * path, `Authorization: Bearer` on the remote/OAuth path. The credentials object\n * is read per request so a host can rotate a refreshed bearer token in place.\n */\nfunction authHeaders(config: Config): Record<string, string> {\n const c = config.credentials;\n return c.kind === \"bearer\" ? { authorization: `Bearer ${c.token}` } : { \"x-api-key\": c.apiKey };\n}\n\n/**\n * Unwraps a ResponseDto<T> envelope while preserving bare PaginationResponse bodies.\n * Exported for unit testing.\n */\nexport function unwrap<T>(body: unknown): T {\n if (!isObject(body)) return body as T;\n if (!(\"data\" in body)) return body as T;\n // If pagination metadata is present at the top level, return as-is.\n for (const key of PAGINATION_KEYS) {\n if (key in body) return body as T;\n }\n // Plain ResponseDto envelope -> unwrap.\n return (body as { data: T }).data;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { GetMasterKeywordListResult } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getNicheKeywordsTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_niche_keywords\",\n title: \"Get Master Keyword List for a Niche\",\n description:\n \"Use this when the user asks about keywords, search terms, or search volume for a niche. \" +\n \"Retrieves the master keyword list for the specified Niche — the relevant search terms \" +\n \"monitored for ranking and performance metrics. For each keyword, returns search volume, \" +\n \"relevancy (numeric score or \\\"Outlier\\\"), and competitor ASIN organic ranks \" +\n \"(asinRanks: { ASIN -> rank | null }). Also returns the latestResearchDate.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<GetMasterKeywordListResult>(\n { config: ctx.config, toolName: \"get_niche_keywords\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/keywords`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { NicheRoots } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getNicheRootsTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_niche_roots\",\n title: \"Get Keyword Roots for a Niche\",\n description:\n \"Use this when the user wants to find the highest-impact words across a niche's keywords, asks about \" +\n \"'keyword roots', or wants to prioritize terms for a listing. \" +\n \"Retrieves the keyword lexical roots for the Niche — individual words or word-combinations extracted \" +\n \"from the Master Keyword List (e.g. 'bluetooth headphones' yields roots 'bluetooth', 'headphones', \" +\n \"'bluetooth headphones'). Returns `roots` and `normalizedRoots` tables, each item with root, frequency \" +\n \"(how many keywords contain it), broadSearchVolume (summed search volume of those keywords), and \" +\n \"broadSearchVolumeRatio (0-1, relative to the top root), plus the per-keyword breakdowns and \" +\n \"latestResearchDate.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<NicheRoots>(\n { config: ctx.config, toolName: \"get_niche_roots\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/roots`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { GetCompetitorListResult } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getNicheCompetitorsTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_niche_competitors\",\n title: \"Get Competitors and Niche Statistics\",\n description:\n \"Use this for competitor ASINs, sales/revenue benchmarks, or niche opportunity scoring. \" +\n \"Retrieves the list of Competitors within the specified Niche along with Niche statistics. \" +\n \"For the Niche: keyword statistics, opportunity evaluation, benchmark median values, and \" +\n \"overall competitor strength assessment. For each Competitor: ASIN, sales, revenue, ratings, \" +\n \"reviews, price, image URL, and ranking data.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<GetCompetitorListResult>(\n { config: ctx.config, toolName: \"get_niche_competitors\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/competitors`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { NicheRankingJuices } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getRankingJuiceTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_ranking_juice\",\n title: \"Get Ranking Juices for a Niche\",\n description:\n \"Use this when the user asks 'how do I rank higher', 'what's my ranking juice', or wants \" +\n \"listing-optimization guidance. \" +\n \"Retrieves the Ranking Juices for each Competitor within the specified Niche. Ranking Juice is \" +\n \"DataDive's proprietary metric identifying the key factors driving search-ranking success \" +\n \"for each Competitor's listing. Returns currentListing and optimizedListing breakdowns \" +\n \"(rankingJuice + per-property contributions: title, bullets, description) plus per-competitor \" +\n \"listing scores and the latestResearchDate.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<NicheRankingJuices>(\n { config: ctx.config, toolName: \"get_ranking_juice\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/ranking-juices`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport { RANK_RADAR_STATUSES, type RankRadarList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\"Items per page (max 100). Defaults to 20.\"),\n nicheId: z\n .string()\n .min(1)\n .optional()\n .describe(\"Filter Rank Radars by Niche identifier. Use after `list_niches`.\"),\n status: z\n .enum(RANK_RADAR_STATUSES)\n .optional()\n .describe(\n \"Filter by Rank Radar status. By default, returns only ACTIVE Rank Radars. Allowed: ACTIVE, PAUSED, ARCHIVED.\",\n ),\n searchText: z.string().optional().describe(\"Filter Rank Radars by ASIN or product title.\"),\n};\n\nexport const listRankRadarsTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_rank_radars\",\n title: \"List Rank Radars\",\n description:\n \"Use this to find a `rankRadarId` before calling `get_rank_radar_data`. Filter by `nicheId` \" +\n \"if the user has already identified a niche. \" +\n \"Retrieves a paginated list of Rank Radars — keyword-rank trackers monitoring organic and \" +\n \"sponsored positions for specific ASINs over time. Each item includes id, asin (with id, krtId, \" +\n \"asin), marketplace, keywordCount, title, imageUrl, and summary metrics: top10KW, top10SV, \" +\n \"top50KW, top50SV. Supports pagination metadata (currentPage, pageSize, total, lastPage, \" +\n \"hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<RankRadarList>(\n { config: ctx.config, toolName: \"list_rank_radars\" },\n \"/v1/niches/rank-radars\",\n {\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n nicheId: args.nicheId,\n status: args.status,\n searchText: args.searchText,\n },\n );\n },\n};\n","/**\n * Hand-mirrored DTOs from the DataDive backend (v1 external API).\n *\n * Source of truth (in this priority order):\n * 1. Live Swagger: https://developer.datadive.tools/docs#/v1\n * (machine-readable: https://developer.datadive.tools/docs-json)\n * 2. Backend DTOs: datadive-backend/src/external-api/dto/*.ts\n * datadive-backend/src/niche-research/dto/rank-radar.response.dto.ts\n * datadive-backend/src/common/dto/response.dto.ts\n * datadive-backend/src/common/pagination/pagination.dto.ts\n * 3. Controller: datadive-backend/src/external-api/external-api-v1.controller.ts\n *\n * Last synced: 2026-06-19.\n *\n * The MCP server forwards JSON straight to the LLM, so deeply-nested DTOs are\n * intentionally typed loosely (with `unknown` or pass-through Records) where\n * the TS surface doesn't add safety. Pin tighter as use cases require.\n */\n\n// ─── Envelope + pagination ───────────────────────────────────────────────────\n\n/** Generic NestJS response envelope: { message?, success?, data? }. */\nexport interface ResponseEnvelope<T> {\n message?: string;\n /** @deprecated — use HTTP status as the success indicator. */\n success?: boolean;\n data?: T | null;\n}\n\n/** Standard pagination envelope returned by list endpoints. */\nexport interface PaginationResponse<T> {\n data: T[];\n currentPage: number;\n pageSize: number;\n total: number;\n lastPage: number;\n hasNext: boolean;\n hasPrev: boolean;\n}\n\n/**\n * ISO-8601 date or timestamp, mirroring the backend's `@IsISO8601()` (date-only\n * allowed). Shared by the date/timestamp query params across /v1 tools\n * (alerts `updatedSince`, usage `startDate`/`endDate`).\n */\nexport const ISO_8601 = /^\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\n/**\n * Marketplace codes accepted by `marketplace` params across /v1 endpoints\n * (alerts, inventory). Mirrors the backend's `SupportedMarketplaceEnum`.\n */\nexport const SUPPORTED_MARKETPLACES = [\n \"com\",\n \"ca\",\n \"co.uk\",\n \"com.mx\",\n \"in\",\n \"fr\",\n \"de\",\n \"es\",\n \"it\",\n \"co.jp\",\n] as const;\n\n// ─── /v1/niches (NicheList = PaginationResponse<NicheItem>) ─────────────────\n\nexport interface NicheItem {\n nicheId: string;\n heroKeyword: string;\n nicheLabel: string;\n /** Amazon marketplace TLD: \"com\", \"uk\", \"de\", etc. */\n marketplace: string;\n /** ISO date string when the Niche was last researched. Nullable. */\n latestResearchDate: string | null;\n}\n\nexport type NicheList = PaginationResponse<NicheItem>;\n\n// ─── /v1/niches/:nicheId/keywords (GetMasterKeywordListResult, wrapped) ─────\n\n/** Map of competitor ASIN -> rank (or null when unranked). */\nexport type AsinRanks = Record<string, number | null>;\n\nexport interface MasterKeyword {\n keyword: string;\n searchVolume: number;\n /** \"Outlier\" or a numeric relevancy score. */\n relevancy: string | number;\n asinRanks: AsinRanks;\n}\n\nexport interface GetMasterKeywordListResult {\n keywords: MasterKeyword[];\n /** ISO date string. */\n latestResearchDate: string;\n}\n\n// ─── /v1/niches/:nicheId/competitors (GetCompetitorListResult, wrapped) ─────\n\n/**\n * Niche competitors response — many nested DTOs from the backend (MklStatisticsData,\n * OpportunityEvaluation, MklBenchmark, CompetitorsStrength, AsinListItem). We type\n * the outer shape and leave the inner objects as opaque records; the LLM sees the\n * full JSON and can reason about it. Tighten if a specific consumer needs it.\n */\nexport interface GetCompetitorListResult {\n /** Marketplace enum (AmazonStore in backend). */\n marketplace: string;\n statistics: Record<string, unknown>;\n opportunityEvaluation: Record<string, unknown>;\n benchmark: Record<string, unknown>;\n competitorsStrength: Record<string, unknown>;\n competitors: Array<Record<string, unknown>>;\n latestResearchDate: string;\n}\n\n// ─── /v1/niches/:nicheId/ranking-juices (NicheRankingJuices, wrapped) ───────\n\nexport interface RankingJuiceProperty {\n rankingJuice: number;\n}\n\nexport interface ListingRankingJuice {\n rankingJuice: number;\n title: RankingJuiceProperty;\n bullets: RankingJuiceProperty;\n description: RankingJuiceProperty;\n}\n\nexport interface CompetitorRankingJuice {\n asin: string;\n listing: ListingRankingJuice;\n}\n\nexport interface NicheRankingJuices {\n currentListing: ListingRankingJuice;\n optimizedListing: ListingRankingJuice;\n competitors: CompetitorRankingJuice[];\n latestResearchDate: string;\n}\n\n// ─── /v1/niches/rank-radars (ExternalRankRadarListResponseDto, wrapped) ─────\n\nexport interface RankRadarAsin {\n id: string;\n krtId: string;\n asin: string;\n}\n\nexport interface RankRadarItem {\n id: string;\n asin: RankRadarAsin;\n marketplace: string;\n keywordCount: number;\n title: string;\n imageUrl: string;\n top10KW: number | null;\n top10SV: number | null;\n top50KW: number | null;\n top50SV: number | null;\n}\n\nexport type RankRadarList = PaginationResponse<RankRadarItem>;\n\n/** Allowed values for the `status` query param on /v1/niches/rank-radars. */\nexport const RANK_RADAR_STATUSES = [\"ACTIVE\", \"PAUSED\", \"ARCHIVED\"] as const;\nexport type RankRadarStatus = (typeof RANK_RADAR_STATUSES)[number];\n\n// ─── POST /v1/niches/rank-radars (CreateRankRadarSuccessResponseDto, bare) ──\n\n/** Result of creating a Rank Radar. */\nexport interface CreateRankRadarResult {\n rankRadarId: string;\n}\n\n// ─── POST /v1/niches/dives (CreateNicheDiveSuccessResponseDto, bare) ────────\n\n/** Result of kicking off a Niche Dive. Poll get_dive_status with `diveId`. */\nexport interface CreateNicheDiveResult {\n diveId: string;\n /** ISO-8601 estimated completion timestamp. */\n estimatedCompletionDate: string;\n}\n\n// ─── POST /v1/niches/:nicheId/redive (RediveNicheResponseDto, wrapped) ──────\n\n/**\n * Result of re-diving an existing niche. Same shape as CreateNicheDiveResult and\n * polled the same way, but the niche keeps its existing `nicheId` — so unlike a new\n * dive, `get_dive_status` success carries no id the caller did not already have.\n */\nexport interface RediveNicheResult {\n diveId: string;\n /** ISO-8601 estimated completion timestamp. */\n estimatedCompletionDate: string;\n}\n\n// ─── GET /v1/niches/dives/:diveId (NicheDiveStatus*ResponseDto, bare oneOf) ──\n\n/**\n * Niche Dive status, discriminated on `status`:\n * - \"in_progress\" → estimatedCompletionDate\n * - \"success\" → nicheId + token usage (feed nicheId to list_niches / get_niche_*)\n * - \"error\" → error message\n */\nexport type DiveStatus =\n | { diveId: string; status: \"in_progress\"; estimatedCompletionDate: string }\n | { diveId: string; status: \"success\"; nicheId: string; tokensUsed: number; tokensLeft: number }\n | { diveId: string; status: \"error\"; error: string };\n\n// ─── /v1/niches/rank-radars/:rankRadarId (KrtKeywordResponseDto[], wrapped) ─\n\nexport interface KrtAsinRank {\n /** ISO date string. */\n date: string;\n organicRank?: number | null;\n impressionRank?: number | null;\n}\n\nexport interface KrtKeyword {\n id: string;\n keyword: string;\n searchVolume: number | null;\n ranks: KrtAsinRank[];\n /** Highlight annotations (KrtHighlightDto in backend); kept opaque for MVP. */\n highlights: Array<Record<string, unknown>>;\n}\n\nexport type RankRadarKeywordList = KrtKeyword[];\n\n// ─── /v1/sellers/:sellerId/marketplaces/:marketplace/asins/:asin/inventory ────\n// (InventoryByFcResponseDto, wrapped)\n\nexport interface InventoryByFcItem {\n /** Fulfillment center code. */\n fc: string;\n /** State or region code where the FC is located, when known. */\n state: string;\n /** Units of sellable inventory currently at this FC. May be 0 when stock is out. */\n availableStock: number;\n /** Share of the ASIN's total sellable inventory at this FC, as a 0..1 fraction. */\n availableStockPercentage: number;\n}\n\nexport interface InventoryByFcResponse {\n asin: string;\n sellerId: string;\n /** Marketplace enum: \"com\", \"ca\", \"co.uk\", \"com.mx\", \"in\", \"fr\", \"de\", \"es\", \"it\", \"co.jp\". */\n marketplace: string;\n /** ISO timestamp of last successful ingestion. Null if none in the last 30 days. */\n lastUpdatedAt: string | null;\n totalSellableUnits: number;\n distribution: InventoryByFcItem[];\n}\n\n// ─── /v1/alerts/indexing-issues (IndexingIssueAlertListDto, bare) ───────────\n\n/**\n * Allowed values for the `status` query param on the /v1/alerts/* endpoints.\n * `active` is the server-side default; dismissed alerts are never returned.\n */\nexport const ALERT_STATUSES = [\"active\", \"resolved\", \"all\"] as const;\nexport type AlertStatus = (typeof ALERT_STATUSES)[number];\n\nexport interface IndexingIssueAlertItem {\n id: number;\n /** The ASIN that is no longer indexed for its tracked keywords. */\n asin: string;\n title: string | null;\n imageUrl: string | null;\n /** True when the ASIN is a parent of a variation family. */\n isParent: boolean;\n sellerId: string;\n marketplace: string;\n /** ISO timestamp of when the alert was most recently surfaced. Updates each time it re-fires. */\n lastAlertedAt: string;\n /** ISO timestamp of when the alert was resolved, or null if still active. */\n resolvedAt: string | null;\n}\n\nexport type IndexingIssueAlertList = PaginationResponse<IndexingIssueAlertItem>;\n\n// ─── /v1/alerts/blind-spend (BlindSpendAlertListDto, bare) ──────────────────\n\nexport interface BlindSpendSearchTerm {\n /** The customer search term that wasted spend. */\n term: string;\n spend: number | null;\n sales: number | null;\n clicks: number | null;\n /** Conversion rate as a 0..1 fraction. */\n cvr: number | null;\n impressions: number | null;\n}\n\nexport interface BlindSpendAlertItem {\n id: number;\n asin: string | null;\n title: string | null;\n imageUrl: string | null;\n sellerId: string;\n marketplace: string;\n /** ISO timestamp of when the alert was most recently surfaced. Updates each time it re-fires. */\n lastAlertedAt: string;\n /** ISO timestamp of when the alert was resolved, or null if still active. */\n resolvedAt: string | null;\n /** Total ad spend across the unresolved wasted-spend search terms. */\n wastedSpend: number;\n totalKeywordCount: number;\n unresolvedKeywordCount: number;\n /** The unresolved wasted-spend search terms. */\n searchTerms: BlindSpendSearchTerm[];\n}\n\nexport type BlindSpendAlertList = PaginationResponse<BlindSpendAlertItem>;\n\n// ─── Billable features (shared by /v1/quota and /v1/usage) ───────────────────\n\n/**\n * Billable feature types. These are both the keys of the `quota.features` object\n * and the allowed values of the `type` filter on /v1/usage. Mirrors the backend's\n * billable-feature enum.\n */\nexport const BILLABLE_FEATURE_TYPES = [\n \"DIVED_ASINS\",\n \"PRODUCT_BRIEF_ASINS\",\n \"AI_COPYWRITER_PROMPTS\",\n \"RANK_RADAR_KEYWORDS\",\n] as const;\nexport type BillableFeatureType = (typeof BILLABLE_FEATURE_TYPES)[number];\n\n// ─── /v1/niches/:nicheId/roots (ExternalRootResponseDto[], wrapped) ─────────\n\nexport interface RootsTableItem {\n /** A word or word-combination extracted from the master keyword list. */\n root: string;\n /** Count of keywords in the master list that contain this root. */\n frequency: number;\n /** Sum of search volume across all original keywords containing this root. */\n broadSearchVolume: number;\n /** broadSearchVolume / maxBroadSearchVolume, as a 0..1 fraction. */\n broadSearchVolumeRatio: number;\n}\n\n/**\n * Keyword-roots analysis for a Niche. `keywords` and `consolidatedKeywords` are\n * the per-keyword breakdowns (kept opaque — the LLM sees the full JSON); `roots`\n * and `normalizedRoots` are the ranked root tables.\n *\n * Note: the OpenAPI spec types the endpoint's `data` as an array, but the live\n * API returns a single object (verified against api-qa, 2026-06-17). The client's\n * unwrap() strips the `{ data }` envelope, so the tool returns this object directly.\n */\nexport interface NicheRoots {\n keywords: Array<Record<string, unknown>>;\n consolidatedKeywords: Array<Record<string, unknown>>;\n roots: RootsTableItem[];\n normalizedRoots: RootsTableItem[];\n /** ISO date string of the last successful research for the niche. */\n latestResearchDate: string;\n}\n\n// ─── /v1/quota (ExternalQuotaResponseDto) ───────────────────────────────────\n\nexport interface QuotaFeature {\n /** Current usage count. Null when not applicable to the plan. */\n used: number | null;\n /** Quota capacity. Null when unlimited / not applicable. */\n capacity: number | null;\n}\n\nexport interface Quota {\n /** ISO-8601 timestamp of the next quota reset. Null when no reset is scheduled. */\n nextRefreshDate: string | null;\n /** Per-billable-feature usage and capacity. */\n features: Record<BillableFeatureType, QuotaFeature>;\n}\n\n// ─── /v1/usage (ExternalUsageLogListDto, bare PaginationResponse) ───────────\n\nexport interface UsageLogItem {\n /** Name of the user who performed the action. Null when unknown. */\n name: string | null;\n email: string;\n /** Number of tokens consumed. */\n qty: number;\n type: BillableFeatureType;\n /** Specific action performed (e.g. \"RANK_RADAR_CREATE\"). Null when not set. */\n action: string | null;\n nicheId: string | null;\n nicheName: string | null;\n rankRadarId: string | null;\n /** ISO timestamp of when the usage was recorded. */\n date: string;\n}\n\nexport type UsageLogList = PaginationResponse<UsageLogItem>;\n\n// ─── /v1/seller_profiles (SellerProfileListDto, bare PaginationResponse) ─────\n\nexport interface SellerProfileItem {\n /** Amazon seller account ID — the value other seller-scoped tools require. */\n sellerId: string;\n /** Display name of the connected seller account. */\n sellerName: string;\n /** Marketplace code, e.g. \"com\", \"co.uk\" (see SUPPORTED_MARKETPLACES). */\n marketplace: string;\n /** True when Amazon Advertising API credentials are connected for this account. */\n hasAdApi: boolean;\n /** ISO timestamp of when the account was connected to DataDive. */\n createdAt: string;\n}\n\nexport type SellerProfileList = PaginationResponse<SellerProfileItem>;\n\n// ─── /v1/seller_profiles/:sellerId/marketplaces/:marketplace/catalog ──────────\n// (CatalogAsinListDto, bare PaginationResponse)\n\n/** Allowed values for the `status` query param on the seller catalog endpoint. */\nexport const CATALOG_STATUSES = [\"Active\", \"all\"] as const;\nexport type CatalogStatus = (typeof CATALOG_STATUSES)[number];\n\nexport interface CatalogAsinItem {\n asin: string;\n title: string;\n /** Parent ASIN for a variation child, or null for standalone/parent ASINs. */\n parentAsin: string | null;\n brand: string | null;\n /** Listing status, e.g. \"Active\". */\n status: string;\n imageUrl: string | null;\n /** True when the ASIN has variation children. Null when unknown. */\n hasVariations: boolean | null;\n}\n\nexport type SellerCatalogList = PaginationResponse<CatalogAsinItem>;\n\n// ─── /v1/seller_profiles/:sellerId/marketplaces/:marketplace/listing-changes ──\n// (ListingChangeExternalListDto, bare PaginationResponse)\n\n/** Allowed values for the `types` query param on the listing-changes endpoint. */\nexport const LISTING_CHANGE_TYPES = [\"Price\", \"Content\", \"Image\"] as const;\nexport type ListingChangeType = (typeof LISTING_CHANGE_TYPES)[number];\n\n/** Allowed values for the `sortBy` query param on the listing-changes endpoint. */\nexport const LISTING_CHANGE_SORT_BY = [\"date\", \"type\"] as const;\nexport type ListingChangeSortBy = (typeof LISTING_CHANGE_SORT_BY)[number];\n\n/** Generic sort direction, shared where a `sortOrder` query param is accepted. */\nexport const SORT_ORDER = [\"ASC\", \"DESC\"] as const;\nexport type SortOrder = (typeof SORT_ORDER)[number];\n\n/**\n * Optional ranking/conversion correlation for a listing change, present only when\n * `includeCorrelations=true`. Sub-objects (salesCvr, top10/top50SearchTerms) carry\n * before/after figures and are kept loosely typed per this file's convention.\n */\nexport interface ListingChangeCorrelation {\n /** \"PENDING\" | \"AVAILABLE\" | \"UNAVAILABLE_NO_ACTIVE_RANK_RADAR\" | \"UNAVAILABLE\". */\n status: string;\n message: string | null;\n salesCvr: Record<string, unknown> | null;\n top10SearchTerms: Record<string, unknown> | null;\n top50SearchTerms: Record<string, unknown> | null;\n}\n\nexport interface ListingChangeItem {\n asin: string;\n title: string | null;\n imageUrl: string | null;\n /** ISO timestamp of when the change was detected. */\n date: string;\n /** \"Price\" | \"Content\" | \"Image\". */\n type: string;\n /** Sub-type for content changes (e.g. title/bullets/description); null otherwise. */\n contentType: string | null;\n description: string;\n /** Prior value of the changed field. Shape varies by change type; kept opaque. */\n previousValue: Record<string, unknown> | null;\n /** New value of the changed field. Shape varies by change type; kept opaque. */\n newValue: Record<string, unknown> | null;\n /** Present only when `includeCorrelations=true`; null otherwise. */\n correlation: ListingChangeCorrelation | null;\n}\n\nexport type ListingChangeList = PaginationResponse<ListingChangeItem>;\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { RankRadarKeywordList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\n/** yyyy-mm-dd matcher used by both startDate and endDate. */\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst inputSchema = {\n rankRadarId: z\n .string()\n .uuid()\n .describe(\"The Rank Radar UUID (from `list_rank_radars`).\"),\n startDate: z\n .string()\n .regex(ISO_DATE, \"startDate must be in yyyy-mm-dd format\")\n .describe(\"Start date for the ranking data range, yyyy-mm-dd (e.g. 2024-03-26).\"),\n endDate: z\n .string()\n .regex(ISO_DATE, \"endDate must be in yyyy-mm-dd format\")\n .describe(\"End date for the ranking data range, yyyy-mm-dd (e.g. 2024-04-26). Must be on or after startDate.\"),\n};\n\nexport const getRankRadarDataTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_rank_radar_data\",\n title: \"Get Keyword Rankings for a Rank Radar\",\n description:\n \"Use this to analyze keyword ranking trends over time. Requires startDate and endDate (yyyy-mm-dd). \" +\n \"Retrieves historical keyword ranking data for the specified Rank Radar within the date range. \" +\n \"Returns an array of tracked keywords; each has id, keyword, searchVolume, ranks (per-day \" +\n \"{ date, organicRank, impressionRank }), and any highlight annotations. Use after \" +\n \"`list_rank_radars` to discover a `rankRadarId`.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<RankRadarKeywordList>(\n { config: ctx.config, toolName: \"get_rank_radar_data\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}`,\n { startDate: args.startDate, endDate: args.endDate },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport type { CreateRankRadarResult } from \"../types/api.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n asin: z.string().min(1).describe(\"The ASIN to track keyword rankings for.\"),\n numberOfKeywords: z\n .number()\n .int()\n .min(1)\n .describe(\"How many keywords to track (minimum 1). More keywords = broader tracking but more Search Term tokens.\"),\n nicheId: z\n .string()\n .min(1)\n .describe(\"The niche this Rank Radar belongs to. Get one from `list_niches`.\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\n \"Must be true to proceed — creating a Rank Radar spends Search Term tokens. Confirm the cost with the user first.\",\n ),\n};\n\nexport const createRankRadarTool: ToolDefinition<typeof inputSchema> = {\n name: \"create_rank_radar\",\n title: \"Create a Rank Radar\",\n description:\n \"Use this to start tracking organic and sponsored keyword rankings for an ASIN within a niche. \" +\n \"⚠️ Spends Search Term tokens (cost scales with `numberOfKeywords`) and cannot be undone — set \" +\n \"`confirm: true` only after the user approves the cost. Returns the new `rankRadarId`; read its data \" +\n \"later with `get_rank_radar_data`. Requires `asin`, `numberOfKeywords` (min 1), and a `nicheId` from `list_niches`.\",\n inputSchema,\n // destructiveHint is technically \"additive\" here (this creates a new Rank Radar, it\n // doesn't destroy data), but we flag it destructive so clients prompt before the\n // irreversible token spend. The confirm gate enforces this server-side regardless.\n annotations: { readOnlyHint: false, destructiveHint: true },\n handler: async (args, ctx) => {\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n `Creating this Rank Radar consumes Search Term tokens — one per keyword tracked (${args.numberOfKeywords} requested).`,\n );\n if (pending) return pending;\n\n return await httpPost<CreateRankRadarResult>(\n { config: ctx.config, toolName: \"create_rank_radar\" },\n \"/v1/niches/rank-radars\",\n {\n asin: args.asin,\n numberOfKeywords: args.numberOfKeywords,\n nicheId: args.nicheId,\n },\n );\n },\n};\n","/**\n * Shared confirm gate for token-spending write tools (create_niche_dive,\n * create_rank_radar).\n *\n * Creating a Dive or Rank Radar consumes billable tokens irreversibly, so these\n * tools require an explicit `confirm: true` before they hit the API. When the flag\n * is missing, requireConfirmation returns a structured (non-error) payload telling\n * the model to confirm the token spend with the user and re-call with `confirm: true`;\n * the handler returns that payload early WITHOUT calling the API.\n *\n * The user can opt out of the gate entirely by setting DATADIVE_AUTO_CONFIRM_WRITES\n * (see config.ts) — a persistent \"don't ask me again\".\n */\n\nimport type { HandlerContext } from \"./types.js\";\n\nexport interface ConfirmationRequired {\n status: \"confirmation_required\";\n message: string;\n /** Human-readable note on what this action will cost in tokens. */\n costNote: string;\n}\n\n/**\n * Returns a ConfirmationRequired payload if the action is not yet confirmed, or null\n * if the handler should proceed (confirm:true was passed, or the opt-out is set).\n */\nexport function requireConfirmation(\n confirm: boolean | undefined,\n ctx: HandlerContext,\n costNote: string,\n): ConfirmationRequired | null {\n if (confirm === true || ctx.config.autoConfirmWrites) return null;\n return {\n status: \"confirmation_required\",\n message:\n \"This action spends billable tokens and cannot be undone. Confirm the cost with \" +\n \"the user, then call again with `confirm: true` to proceed.\",\n costNote,\n };\n}\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport { SUPPORTED_MARKETPLACES, type CreateNicheDiveResult } from \"../types/api.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .describe(\n \"Amazon marketplace of the seed ASIN. Note these are full domain suffixes: com, ca, \" +\n \"co.uk, com.mx, in, fr, de, es, it, co.jp (e.g. UK is `co.uk`, not `uk`).\",\n ),\n asin: z\n .string()\n .min(1)\n .describe(\"The seed ASIN to start the dive from (e.g. B08N5WRWNW). Competitors are built around it.\"),\n numberOfCompetitors: z\n .number()\n .int()\n .min(2)\n .describe(\"How many competitors to analyze (minimum 2). More competitors = deeper niche but more dive tokens.\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\"Must be true to proceed — creating a dive spends dive tokens. Confirm the cost with the user first.\"),\n};\n\nexport const createNicheDiveTool: ToolDefinition<typeof inputSchema> = {\n name: \"create_niche_dive\",\n title: \"Create a Niche Dive\",\n description:\n \"Use this to start new niche research from a seed ASIN. ⚠️ Spends dive tokens (cost scales with \" +\n \"`numberOfCompetitors`) and cannot be undone — set `confirm: true` only after the user approves the cost. \" +\n \"The dive runs asynchronously: this returns immediately with a `diveId` and an `estimatedCompletionDate`. \" +\n \"Poll `get_dive_status` with that `diveId` until it reports `success` (which carries the new `nicheId` for \" +\n \"use with `list_niches`, `get_niche_keywords`, etc.). Requires `marketplace`, `asin`, and `numberOfCompetitors` (min 2).\",\n inputSchema,\n // destructiveHint is technically \"additive\" here (a dive creates a new niche, it\n // doesn't destroy data), but we flag it destructive so clients prompt before the\n // irreversible token spend. The confirm gate enforces this server-side regardless.\n annotations: { readOnlyHint: false, destructiveHint: true },\n handler: async (args, ctx) => {\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n `Creating this dive consumes dive tokens — one batch per competitor analyzed (${args.numberOfCompetitors} requested).`,\n );\n if (pending) return pending;\n\n return await httpPost<CreateNicheDiveResult>(\n { config: ctx.config, toolName: \"create_niche_dive\" },\n \"/v1/niches/dives\",\n {\n marketplace: args.marketplace,\n asin: args.asin,\n numberOfCompetitors: args.numberOfCompetitors,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport { ApiError } from \"../http/errors.js\";\nimport type { RediveNicheResult } from \"../types/api.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The niche to re-dive. Get one from `list_niches`.\"),\n mode: z\n .enum([\"same_competitors\", \"discover\"])\n .describe(\n \"How to pick the competitors for the refreshed dive. `same_competitors` re-runs the niche's \" +\n \"current competitor set and takes no other argument — use it to refresh stale data. `discover` \" +\n \"searches for a fresh competitor set, which is what you want when the niche has changed.\",\n ),\n numberOfCompetitors: z\n .number()\n .int()\n .min(2)\n .optional()\n .describe(\n \"Total ASINs the refreshed dive should contain, including `lockedAsins`. Required in `discover` \" +\n \"mode and rejected in `same_competitors` mode. Dive tokens are spent per ASIN.\",\n ),\n heroAsin: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"`discover` mode only. Seed product the competitor discovery starts from. Defaults to the niche's \" +\n \"highest-selling competitor, preferring one the user's own connected seller account owns.\",\n ),\n lockedAsins: z\n .array(z.string().min(1))\n .optional()\n .describe(\n \"`discover` mode only. ASINs that must stay in the competitor set regardless of what discovery \" +\n \"finds. Cannot exceed `numberOfCompetitors`, and cannot overlap `excludedAsins`.\",\n ),\n excludedAsins: z\n .array(z.string().min(1))\n .optional()\n .describe(\"`discover` mode only. ASINs discovery must never select.\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\n \"Must be true to proceed — a re-dive spends dive tokens. Confirm the cost with the user first.\",\n ),\n};\n\nexport const rediveNicheTool: ToolDefinition<typeof inputSchema> = {\n name: \"redive_niche\",\n title: \"Re-dive an Existing Niche\",\n description:\n \"Use this to refresh an existing niche's research with current Amazon data, instead of creating a new \" +\n \"niche with `create_niche_dive`. ⚠️ Spends dive tokens (one batch per ASIN dived) and cannot be undone — \" +\n \"set `confirm: true` only after the user approves the cost. Two modes: `same_competitors` re-dives the \" +\n \"niche's current competitor set (no other argument needed) and `discover` finds a fresh set, sized by \" +\n \"`numberOfCompetitors` and steerable with `heroAsin` / `lockedAsins` / `excludedAsins`. Runs \" +\n \"asynchronously: returns a `diveId` and an `estimatedCompletionDate` — poll `get_dive_status` with that \" +\n \"`diveId` until it reports `success`. The niche keeps its `nicheId`, so existing rank radars and reports \" +\n \"follow the refreshed data.\",\n inputSchema,\n // Same reasoning as create_niche_dive: a re-dive is additive rather than destructive, but the\n // irreversible token spend is what clients need to prompt on. The confirm gate enforces it regardless.\n annotations: { readOnlyHint: false, destructiveHint: true },\n handler: async (args, ctx) => {\n // `numberOfCompetitors` cannot be required in the schema (same_competitors mode forbids it), so\n // check it here — before the confirm gate, because a gate that cannot state the cost is worse\n // than no gate. Everything else is left to the API, which names the offending field.\n if (args.mode === \"discover\" && args.numberOfCompetitors === undefined) {\n throw new ApiError(\n \"bad_request\",\n 400,\n \"numberOfCompetitors is required in `discover` mode — it is how many ASINs the refreshed dive \" +\n \"will contain, and therefore what it costs. Ask the user how many competitors they want \" +\n '(minimum 2), or use mode \"same_competitors\" to re-dive the niche\\'s current set.',\n );\n }\n\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n args.mode === \"discover\"\n ? `Re-diving this niche consumes dive tokens — one batch per ASIN analyzed (${args.numberOfCompetitors} requested).`\n : \"Re-diving this niche consumes dive tokens — one batch per competitor currently in the niche. \" +\n \"Use `get_niche_competitors` first if the user needs the exact count.\",\n );\n if (pending) return pending;\n\n // Mode-specific fields are forwarded as given rather than filtered by mode: the API rejects a\n // field that does not belong to the requested mode with a message naming it, which is a clearer\n // correction for the model than silently dropping what it asked for. `undefined` fields are\n // dropped by JSON.stringify, so an unused optional never reaches the wire.\n return await httpPost<RediveNicheResult>(\n { config: ctx.config, toolName: \"redive_niche\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/redive`,\n {\n mode: args.mode,\n numberOfCompetitors: args.numberOfCompetitors,\n heroAsin: args.heroAsin,\n lockedAsins: args.lockedAsins,\n excludedAsins: args.excludedAsins,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { DiveStatus } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n diveId: z\n .string()\n .min(1)\n .describe(\"The dive identifier returned by `create_niche_dive` or `redive_niche`.\"),\n};\n\nexport const getDiveStatusTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_dive_status\",\n title: \"Get Niche Dive Status\",\n description:\n \"Use this to poll a niche dive started with `create_niche_dive` or `redive_niche` until it finishes. \" +\n \"Returns one of three shapes keyed by `status`: `in_progress` (with `estimatedCompletionDate`), \" +\n \"`success` (with the `nicheId` plus `tokensUsed`/`tokensLeft`), or `error` (with an `error` message). \" +\n \"On `success`, use the `nicheId` with `list_niches`, `get_niche_keywords`, `get_niche_competitors`, etc. \" +\n \"(for a re-dive it is the same niche you passed in, now carrying refreshed data). \" +\n \"Re-poll periodically — dives can take minutes; the `estimatedCompletionDate` hints at when to check.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<DiveStatus>(\n { config: ctx.config, toolName: \"get_dive_status\" },\n `/v1/niches/dives/${encodeURIComponent(args.diveId)}`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { SellerProfileList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n\nexport const listSellerProfilesTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_seller_profiles\",\n title: \"List Connected Amazon Seller Profiles\",\n description:\n \"Use this when the user asks which Amazon seller accounts are connected, or as the discovery \" +\n \"step to find the `sellerId` and `marketplace` required by `get_asin_inventory_distribution`, \" +\n \"`get_seller_catalog`, `get_seller_listing_changes`, and the alert tools. \" +\n \"Returns a paginated list of the organization's connected seller profiles — each item has \" +\n \"sellerId, sellerName, marketplace (e.g. \\\"com\\\", \\\"co.uk\\\"), hasAdApi (whether Advertising API \" +\n \"credentials are connected), and createdAt — plus pagination metadata (currentPage, pageSize, \" +\n \"total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<SellerProfileList>(\n { config: ctx.config, toolName: \"list_seller_profiles\" },\n \"/v1/seller_profiles\",\n {\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport { CATALOG_STATUSES, type SellerCatalogList } from \"../types/api.js\";\nimport { sellerMarketplacePath, sellerScopedInputSchema } from \"./seller-scoped.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n ...sellerScopedInputSchema,\n search: z\n .string()\n .min(1)\n .optional()\n .describe(\"Case-insensitive partial match on product title or brand.\"),\n brand: z.string().min(1).optional().describe(\"Case-insensitive partial match on brand name.\"),\n status: z\n .enum(CATALOG_STATUSES)\n .optional()\n .describe('Listing status filter. \"Active\" (default) or \"all\".'),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n\nexport const getSellerCatalogTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_seller_catalog\",\n title: \"List Catalog ASINs for a Seller\",\n description:\n \"Use this when the user wants to browse or search a seller's own Amazon catalog — the products \" +\n \"they sell on a given marketplace. Requires a `sellerId` + `marketplace` (use `list_seller_profiles` \" +\n \"to discover them). Filter by `search` (title/brand), `brand`, and `status` (Active by default, or \" +\n \"all). Returns a paginated list where each item has asin, title, parentAsin, brand, status, imageUrl, \" +\n \"and hasVariations, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<SellerCatalogList>(\n { config: ctx.config, toolName: \"get_seller_catalog\" },\n sellerMarketplacePath(args.sellerId, args.marketplace, \"catalog\"),\n {\n search: args.search,\n brand: args.brand,\n status: args.status,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","/**\n * Shared input fields + path builder for the seller+marketplace-scoped tools\n * (/v1/seller_profiles/:sellerId/marketplaces/:marketplace/*). Defined once here\n * and spread into each tool's inputSchema, mirroring alert-query.ts.\n */\n\nimport { z } from \"zod\";\nimport { SUPPORTED_MARKETPLACES } from \"../types/api.js\";\n\nexport const sellerScopedInputSchema = {\n sellerId: z\n .string()\n .min(1)\n .describe(\n \"Amazon seller account ID. Get it from `list_seller_profiles`, or find it on the Connections page at https://2.datadive.tools.\",\n ),\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .describe('Amazon marketplace code, e.g. \"com\" for amazon.com or \"co.uk\" for amazon.co.uk.'),\n};\n\n/** Builds a `/v1/seller_profiles/:sellerId/marketplaces/:marketplace/<suffix>` path with both params URL-encoded. */\nexport function sellerMarketplacePath(sellerId: string, marketplace: string, suffix: string): string {\n return `/v1/seller_profiles/${encodeURIComponent(sellerId)}/marketplaces/${encodeURIComponent(\n marketplace,\n )}/${suffix}`;\n}\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport {\n ISO_8601,\n LISTING_CHANGE_SORT_BY,\n LISTING_CHANGE_TYPES,\n SORT_ORDER,\n type ListingChangeList,\n} from \"../types/api.js\";\nimport { sellerMarketplacePath, sellerScopedInputSchema } from \"./seller-scoped.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n ...sellerScopedInputSchema,\n types: z\n .array(z.enum(LISTING_CHANGE_TYPES))\n .min(1)\n .optional()\n .describe('Filter to specific change types. Omit for all. Any of \"Price\", \"Content\", \"Image\".'),\n asin: z\n .string()\n .min(1)\n .optional()\n .describe(\"Filter to an exact ASIN or parent ASIN.\"),\n brand: z.string().min(1).optional().describe(\"Case-insensitive partial match on brand name.\"),\n search: z\n .string()\n .min(1)\n .optional()\n .describe(\"Case-insensitive partial match on product title or brand.\"),\n startDate: z\n .string()\n .regex(ISO_8601, \"startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only changes detected on or after this time.\"),\n endDate: z\n .string()\n .regex(ISO_8601, \"endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only changes detected on or before this time.\"),\n sortBy: z\n .enum(LISTING_CHANGE_SORT_BY)\n .optional()\n .describe('Sort field. \"date\" (default) or \"type\".'),\n sortOrder: z.enum(SORT_ORDER).optional().describe('Sort direction. \"DESC\" (default) or \"ASC\".'),\n includeCorrelations: z\n .boolean()\n .optional()\n .describe(\n \"When true, include the ranking/conversion `correlation` object per change (CVR and search-term \" +\n \"movement before vs after). Defaults to false.\",\n ),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n\nexport const getSellerListingChangesTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_seller_listing_changes\",\n title: \"List Listing Changes for a Seller\",\n description:\n \"Use this when the user asks what changed on their Amazon listings — price, content (title/bullets/\" +\n \"description), or image edits — for a seller account. Requires a `sellerId` + `marketplace` (use \" +\n \"`list_seller_profiles` to discover them). Filter by `types`, `asin`/parent ASIN, `brand`, `search`, \" +\n \"and a `startDate`/`endDate` range; sort with `sortBy`/`sortOrder`. Set `includeCorrelations: true` to \" +\n \"attach the ranking/conversion impact per change. Returns a paginated list where each item has asin, \" +\n \"title, imageUrl, date, type, contentType, description, previousValue, newValue, and (optionally) \" +\n \"correlation, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<ListingChangeList>(\n { config: ctx.config, toolName: \"get_seller_listing_changes\" },\n sellerMarketplacePath(args.sellerId, args.marketplace, \"listing-changes\"),\n {\n // The API accepts `types` as a comma-separated list.\n types: args.types?.join(\",\"),\n asin: args.asin,\n brand: args.brand,\n search: args.search,\n startDate: args.startDate,\n endDate: args.endDate,\n sortBy: args.sortBy,\n sortOrder: args.sortOrder,\n includeCorrelations: args.includeCorrelations,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport { SUPPORTED_MARKETPLACES, type InventoryByFcResponse } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n sellerId: z\n .string()\n .min(1)\n .describe(\n \"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.\",\n ),\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .describe('Amazon marketplace code, e.g. \"com\" for amazon.com or \"co.uk\" for amazon.co.uk.'),\n asin: z\n .string()\n .regex(/^[A-Z0-9]{10}$/, \"ASIN must be 10 uppercase alphanumeric characters (e.g. B08XYZ1234).\")\n .describe(\n \"Amazon Standard Identification Number (10 uppercase alphanumeric characters, e.g. B08XYZ1234).\",\n ),\n};\n\nexport const getAsinInventoryDistributionTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_asin_inventory_distribution\",\n title: \"Get Per-Fulfillment-Center Inventory Distribution for an ASIN\",\n description:\n \"Use this when the user asks about inventory levels, stock by fulfillment center, \" +\n \"or where their units are sitting across Amazon's fulfillment network for a specific ASIN. \" +\n \"Requires the Amazon sellerId (from `list_seller_profiles` or the user's DataDive Connections \" +\n \"page at https://2.datadive.tools), the marketplace code, and the ASIN. Returns totalSellableUnits \" +\n \"and a per-FC `distribution` array (fc, state, availableStock, availableStockPercentage). \" +\n \"`lastUpdatedAt` may be null when no successful inventory ingestion has occurred in the last 30 days.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<InventoryByFcResponse>(\n { config: ctx.config, toolName: \"get_asin_inventory_distribution\" },\n `/v1/sellers/${encodeURIComponent(args.sellerId)}/marketplaces/${encodeURIComponent(\n args.marketplace,\n )}/asins/${encodeURIComponent(args.asin)}/inventory`,\n );\n },\n};\n","/**\n * Shared input schema for the /v1/alerts/* list tools. Both alert endpoints\n * accept the same backend query DTO (ExternalAlertsQueryDto), so the input\n * contract is defined once here and imported by each tool.\n */\n\nimport { z } from \"zod\";\nimport { ALERT_STATUSES, ISO_8601, SUPPORTED_MARKETPLACES } from \"../types/api.js\";\n\nexport const alertsQueryInputSchema = {\n sellerId: z\n .string()\n .min(1)\n .max(50)\n .optional()\n .describe('Filter to a single connected Amazon seller account ID, e.g. \"A1B2C3D4E5\".'),\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .optional()\n .describe('Filter to a single Amazon marketplace code, e.g. \"com\" for amazon.com or \"co.uk\" for amazon.co.uk.'),\n status: z\n .enum(ALERT_STATUSES)\n .optional()\n .describe(\n \"Lifecycle filter. `active` (default) returns unresolved alerts, `resolved` returns resolved alerts, \" +\n \"`all` returns both. Dismissed alerts are never returned.\",\n ),\n updatedSince: z\n .string()\n .regex(ISO_8601, \"updatedSince must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)\")\n .optional()\n .describe(\n \"ISO-8601 timestamp; return only alerts surfaced at or after this time (filters on `lastAlertedAt`). \" +\n \"Use it to incrementally sync since your last poll. Defaults to the last 30 days when omitted.\",\n ),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n","import { httpGet } from \"../http/client.js\";\nimport type { IndexingIssueAlertList } from \"../types/api.js\";\nimport { alertsQueryInputSchema } from \"./alert-query.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nexport const listIndexingIssueAlertsTool: ToolDefinition<typeof alertsQueryInputSchema> = {\n name: \"list_indexing_issue_alerts\",\n title: \"List Indexing-Issue Alerts\",\n description:\n \"Use this to find products that may have lost search visibility. Retrieves a paginated list of \" +\n \"indexing-issue alerts across the user's connected Amazon seller accounts — an alert fires when one of \" +\n \"their ASINs is no longer indexed for its tracked keywords. By default returns active (unresolved) alerts \" +\n \"from the last 30 days; filter by `sellerId`, `marketplace`, `status`, or `updatedSince` (for incremental \" +\n \"polling). Each item includes id, asin, title, imageUrl, isParent, sellerId, marketplace, lastAlertedAt, \" +\n \"and resolvedAt. Supports pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema: alertsQueryInputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<IndexingIssueAlertList>(\n { config: ctx.config, toolName: \"list_indexing_issue_alerts\" },\n \"/v1/alerts/indexing-issues\",\n {\n sellerId: args.sellerId,\n marketplace: args.marketplace,\n status: args.status,\n updatedSince: args.updatedSince,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { httpGet } from \"../http/client.js\";\nimport type { BlindSpendAlertList } from \"../types/api.js\";\nimport { alertsQueryInputSchema } from \"./alert-query.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nexport const listBlindSpendAlertsTool: ToolDefinition<typeof alertsQueryInputSchema> = {\n name: \"list_blind_spend_alerts\",\n title: \"List Blind-Spend (Wasted-Spend) Alerts\",\n description:\n \"Use this to find wasted PPC ad spend. Retrieves a paginated list of blind-spend alerts across the \" +\n \"user's connected Amazon seller accounts — an alert flags ad spend on customer search terms that produced \" +\n \"little or no sales. By default returns active (unresolved) alerts from the last 30 days; filter by \" +\n \"`sellerId`, `marketplace`, `status`, or `updatedSince` (for incremental polling). Each item includes id, \" +\n \"asin, title, imageUrl, sellerId, marketplace, lastAlertedAt, resolvedAt, wastedSpend (total ad spend \" +\n \"across unresolved terms), totalKeywordCount, unresolvedKeywordCount, and searchTerms — the unresolved \" +\n \"wasted-spend search terms, each with term, spend, sales, clicks, cvr (0-1), and impressions. Supports \" +\n \"pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema: alertsQueryInputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<BlindSpendAlertList>(\n { config: ctx.config, toolName: \"list_blind_spend_alerts\" },\n \"/v1/alerts/blind-spend\",\n {\n sellerId: args.sellerId,\n marketplace: args.marketplace,\n status: args.status,\n updatedSince: args.updatedSince,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { httpGet } from \"../http/client.js\";\nimport type { Quota } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {};\n\nexport const getQuotaTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_quota\",\n title: \"Get Quota Usage\",\n description:\n \"Use this when the user asks how much quota or how many tokens they have left, or before creating a \" +\n \"Niche Dive or Rank Radar to check remaining headroom. \" +\n \"Returns current quota usage and capacity for each billable feature — DIVED_ASINS (Dive tokens), \" +\n \"RANK_RADAR_KEYWORDS (tracked keywords), PRODUCT_BRIEF_ASINS, and AI_COPYWRITER_PROMPTS — each with \" +\n \"`used` and `capacity` (null when unlimited / not applicable), plus `nextRefreshDate` (ISO-8601 \" +\n \"timestamp of the next quota reset, or null). Takes no arguments.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (_args, ctx) => {\n return await httpGet<Quota>({ config: ctx.config, toolName: \"get_quota\" }, \"/v1/quota\");\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport { BILLABLE_FEATURE_TYPES, ISO_8601, type UsageLogList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n type: z\n .enum(BILLABLE_FEATURE_TYPES)\n .optional()\n .describe(\"Filter to a single billable feature type. Omit to return all types.\"),\n search: z\n .string()\n .min(1)\n .optional()\n .describe(\"Case-insensitive partial match on the user's name or email.\"),\n startDate: z\n .string()\n .regex(ISO_8601, \"startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only usage logs recorded on or after this time.\"),\n endDate: z\n .string()\n .regex(ISO_8601, \"endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only usage logs recorded on or before this time.\"),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .optional()\n .describe(\"Items per page (max 200). Defaults to 50.\"),\n};\n\nexport const listUsageTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_usage\",\n title: \"List Billable Usage Logs\",\n description:\n \"Use this when the user asks who consumed tokens, how their quota was spent, or wants an audit of \" +\n \"billable activity over a date range. \" +\n \"Retrieves a paginated list of billable feature usage logs for the organization — each entry is a \" +\n \"token-consumption event (a dive, rank-radar creation, AI copywriter prompt, etc.). Filter by `type` \" +\n \"(billable feature), `search` (user name/email), and `startDate`/`endDate`. Each item includes name, \" +\n \"email, qty (tokens consumed), type, action (specific operation), nicheId/nicheName, rankRadarId, and \" +\n \"date, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<UsageLogList>(\n { config: ctx.config, toolName: \"list_usage\" },\n \"/v1/usage\",\n {\n type: args.type,\n search: args.search,\n startDate: args.startDate,\n endDate: args.endDate,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","/**\n * Tool registry. The order here is the order tools appear in `tools/list`\n * over the MCP transport, which biases LLM discovery — keep \"discovery\" tools\n * (the ones that return IDs needed by other tools) at the top.\n */\n\nimport { listNichesTool } from \"./list-niches.js\";\nimport { getNicheKeywordsTool } from \"./get-niche-keywords.js\";\nimport { getNicheRootsTool } from \"./get-niche-roots.js\";\nimport { getNicheCompetitorsTool } from \"./get-niche-competitors.js\";\nimport { getRankingJuiceTool } from \"./get-ranking-juice.js\";\nimport { listRankRadarsTool } from \"./list-rank-radars.js\";\nimport { getRankRadarDataTool } from \"./get-rank-radar-data.js\";\nimport { createRankRadarTool } from \"./create-rank-radar.js\";\nimport { createNicheDiveTool } from \"./create-niche-dive.js\";\nimport { rediveNicheTool } from \"./redive-niche.js\";\nimport { getDiveStatusTool } from \"./get-dive-status.js\";\nimport { listSellerProfilesTool } from \"./list-seller-profiles.js\";\nimport { getSellerCatalogTool } from \"./get-seller-catalog.js\";\nimport { getSellerListingChangesTool } from \"./get-seller-listing-changes.js\";\nimport { getAsinInventoryDistributionTool } from \"./get-asin-inventory-distribution.js\";\nimport { listIndexingIssueAlertsTool } from \"./list-indexing-issue-alerts.js\";\nimport { listBlindSpendAlertsTool } from \"./list-blind-spend-alerts.js\";\nimport { getQuotaTool } from \"./get-quota.js\";\nimport { listUsageTool } from \"./list-usage.js\";\nimport type { AnyTool } from \"./types.js\";\n\n// Note on the cast: each ToolDefinition<S> has a handler typed to its specific\n// schema; the registry needs a uniform shape. The MCP SDK validates incoming\n// args against `inputSchema` before calling the handler, so the cast is safe.\nexport const allTools: ReadonlyArray<AnyTool> = [\n listNichesTool,\n getNicheKeywordsTool,\n getNicheRootsTool,\n getNicheCompetitorsTool,\n getRankingJuiceTool,\n listRankRadarsTool,\n getRankRadarDataTool,\n createRankRadarTool,\n createNicheDiveTool,\n rediveNicheTool,\n getDiveStatusTool,\n listSellerProfilesTool,\n getSellerCatalogTool,\n getSellerListingChangesTool,\n getAsinInventoryDistributionTool,\n listIndexingIssueAlertsTool,\n listBlindSpendAlertsTool,\n getQuotaTool,\n listUsageTool,\n] as unknown as ReadonlyArray<AnyTool>;\n\nexport type { ToolDefinition, AnyTool } from \"./types.js\";\n"],"mappings":"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","names":["KEY_HELP_URL","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","z","inputSchema","z","inputSchema","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/config.ts","../src/server.ts","../src/tools/list-niches.ts","../src/http/errors.ts","../package.json","../src/http/client.ts","../src/tools/get-niche-keywords.ts","../src/tools/get-niche-roots.ts","../src/tools/get-niche-competitors.ts","../src/tools/get-ranking-juice.ts","../src/tools/list-rank-radars.ts","../src/types/api.ts","../src/tools/get-rank-radar-data.ts","../src/tools/create-rank-radar.ts","../src/tools/confirm.ts","../src/tools/create-niche-dive.ts","../src/tools/redive-niche.ts","../src/tools/get-dive-status.ts","../src/tools/delete-niche.ts","../src/tools/pause-rank-radar.ts","../src/tools/resume-rank-radar.ts","../src/tools/delete-rank-radar.ts","../src/tools/add-rank-radar-search-terms.ts","../src/tools/pause-rank-radar-search-terms.ts","../src/tools/resume-rank-radar-search-terms.ts","../src/tools/generate-listing-copy.ts","../src/tools/get-listing-copy-generation-status.ts","../src/tools/list-seller-profiles.ts","../src/tools/get-seller-catalog.ts","../src/tools/seller-scoped.ts","../src/tools/get-seller-listing-changes.ts","../src/tools/get-asin-inventory-distribution.ts","../src/tools/alert-query.ts","../src/tools/list-indexing-issue-alerts.ts","../src/tools/list-blind-spend-alerts.ts","../src/tools/get-quota.ts","../src/tools/list-usage.ts","../src/tools/index.ts"],"sourcesContent":["/**\n * Entry point for the datadive-mcp stdio binary.\n *\n * Wired via package.json#bin -> dist/index.js so `npx -y @datadive-tools/mcp` runs it.\n * The shebang is added at build time by tsup.config.ts (banner.js).\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { loadConfig } from \"./config.js\";\nimport { buildServer } from \"./server.js\";\n\nasync function main() {\n let config;\n try {\n config = loadConfig();\n } catch (err) {\n // Config errors (e.g. missing API key) are user-facing; print to stderr and exit\n // non-zero so the MCP client surfaces a real error rather than silently disconnecting.\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[datadive-mcp] ${msg}\\n`);\n process.exit(1);\n }\n\n const server = buildServer(config);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n const msg = err instanceof Error ? (err.stack ?? err.message) : String(err);\n process.stderr.write(`[datadive-mcp] fatal: ${msg}\\n`);\n process.exit(1);\n});\n","/**\n * Loads and validates the server's runtime configuration from environment.\n *\n * Required:\n * DATADIVE_API_KEY - your DataDive API key (generate at 2.datadive.tools/api-key)\n *\n * Optional:\n * DATADIVE_API_BASE_URL - override the API base URL (defaults to https://api.datadive.tools)\n * DATADIVE_AUTO_CONFIRM_WRITES - skip the confirm gate on token-spending write tools\n * (create_niche_dive, create_rank_radar). Truthy =\n * \"1\"/\"true\"/\"yes\" (case-insensitive). Defaults to false.\n *\n * The env-based loader always produces api-key credentials (the local stdio path).\n * A remote host embedding this package builds a Config directly with bearer\n * credentials (the OAuth path) — see Credentials.\n */\n\n/** OAuth scope covering the read tools. */\nexport const SCOPE_READ = \"datadive.read\";\n/** OAuth scope covering the token-spending write tools. */\nexport const SCOPE_WRITE = \"datadive.write\";\n\n/**\n * How the HTTP client authenticates against the DataDive /v1 API.\n * - \"api-key\": the local stdio path — sends `x-api-key` (DATADIVE_API_KEY).\n * - \"bearer\": the remote/OAuth path — sends `Authorization: Bearer <token>`.\n * The token is read at request time, so a host may mutate the\n * credentials object to rotate a refreshed access token in place.\n */\nexport type Credentials = { kind: \"api-key\"; apiKey: string } | { kind: \"bearer\"; token: string };\n\nexport interface Config {\n credentials: Credentials;\n baseUrl: string;\n /**\n * When true, the token-spending write tools proceed without requiring `confirm: true`.\n * A user-controlled \"don't ask me again\" opt-out, set via DATADIVE_AUTO_CONFIRM_WRITES.\n */\n autoConfirmWrites: boolean;\n /**\n * OAuth scopes granted to this session. When set, tool calls are gated:\n * read tools require SCOPE_READ and write tools SCOPE_WRITE (see server.ts).\n * Undefined (the stdio/api-key path) means no scope gating.\n */\n scopes?: readonly string[];\n}\n\nconst DEFAULT_BASE_URL = \"https://api.datadive.tools\";\nconst KEY_HELP_URL = \"https://2.datadive.tools/api-key\";\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): Config {\n const apiKey = (env.DATADIVE_API_KEY ?? \"\").trim();\n if (!apiKey) {\n throw new Error(\n `DATADIVE_API_KEY environment variable is required. Generate one at ${KEY_HELP_URL} ` +\n `and add it to your MCP client config under env.DATADIVE_API_KEY.`,\n );\n }\n\n const rawBase = (env.DATADIVE_API_BASE_URL ?? DEFAULT_BASE_URL).trim();\n // Normalize: strip trailing slashes so we can append \"/v1/...\" paths cleanly.\n const baseUrl = rawBase.replace(/\\/+$/, \"\");\n\n const autoConfirmWrites = [\"1\", \"true\", \"yes\"].includes(\n (env.DATADIVE_AUTO_CONFIRM_WRITES ?? \"\").trim().toLowerCase(),\n );\n\n return { credentials: { kind: \"api-key\", apiKey }, baseUrl, autoConfirmWrites };\n}\n","/**\n * Builds the MCP server: registers each tool from the registry with the SDK.\n *\n * Error handling: if a tool handler throws (most commonly an ApiError from the\n * HTTP client), we surface the message back through MCP as a tool-level error\n * (`isError: true`). The LLM sees the message and can react — e.g. tell the\n * user to regenerate their API key on a 401.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { AnyTool } from \"./tools/types.js\";\nimport { SCOPE_READ, SCOPE_WRITE, type Config } from \"./config.js\";\nimport { allTools } from \"./tools/index.js\";\nimport { ApiError } from \"./http/errors.js\";\nimport { PKG_VERSION, takeUpgradeNotice } from \"./http/client.js\";\n\n/**\n * Returns the OAuth scope a tool call needs: SCOPE_READ for read-only tools,\n * SCOPE_WRITE for the token-spending writes. Classification comes from the\n * `readOnlyHint` annotation, which every tool in the registry carries.\n */\nexport function requiredScope(tool: Pick<AnyTool, \"annotations\">): string {\n return tool.annotations?.readOnlyHint === true ? SCOPE_READ : SCOPE_WRITE;\n}\n\nexport function buildServer(config: Config): McpServer {\n const server = new McpServer({\n name: \"datadive\",\n version: PKG_VERSION,\n });\n\n for (const tool of allTools) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: tool.annotations,\n },\n async (args: Record<string, unknown>) => {\n // OAuth scope gate (remote path only — config.scopes is unset over stdio).\n // The backend enforces scopes on /v1 as well; failing here just gives the\n // model a clearer error without a round-trip.\n const scope = requiredScope(tool);\n if (config.scopes && !config.scopes.includes(scope)) {\n return {\n isError: true,\n content: [\n {\n type: \"text\" as const,\n text:\n `Insufficient scope: this tool requires the \"${scope}\" scope, which was not granted ` +\n `to this connection. Reconnect DataDive and enable the missing permission to use it.`,\n },\n ],\n };\n }\n try {\n const data = await tool.handler(args, { config });\n const content = [\n {\n type: \"text\" as const,\n text: JSON.stringify(data, null, 2),\n },\n ];\n // If the backend signaled a newer release, append a one-time upgrade\n // nudge so the model relays it to the user. takeUpgradeNotice() returns\n // null after the first time, so this fires at most once per session.\n const notice = takeUpgradeNotice();\n if (notice) {\n content.push({\n type: \"text\" as const,\n text: `⚠️ datadive-mcp update available — ${notice}`,\n });\n }\n return { content };\n } catch (err) {\n return {\n isError: true,\n content: [\n {\n type: \"text\" as const,\n text: err instanceof ApiError ? err.message : err instanceof Error ? err.message : String(err),\n },\n ],\n };\n }\n },\n );\n }\n\n return server;\n}\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { NicheList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\n/** Mirrors the API's `PaginationParams.MAX_PAGE_SIZE`; larger values are rejected there with 400. */\nexport const LIST_NICHES_MAX_PAGE_SIZE = 50;\n\n/**\n * `searchText`, `searchAsin`, `orderBy` and `sortOrder` were added in 0.10.0 (RS-11517) on the\n * assumption that `GET /v1/niches` accepted them. It does not: the route binds only `currentPage`\n * and `pageSize` (datadive-backend `external-api-v1.controller.ts`), and Nest drops unknown query\n * params silently, so the tool advertised filters that never filtered anything.\n *\n * The same endpoint also ignores paging: `listNichesForExternalApi` combines `skip`/`take` with\n * `getRawMany()` over a joined builder, which makes TypeORM discard LIMIT/OFFSET (RS-11494). Every\n * call returns the whole account while the response metadata describes a page.\n *\n * Verified against prod on 2026-08-28 with an org of 907 niches: `pageSize: 2` returned all 907\n * rows, page 2 was byte-identical to page 1, and `searchText: \"dog hat\"` was byte-identical again\n * (only 80 of the 907 rows even contain that text).\n *\n * So the inputs are withdrawn and the description states the paging limitation, rather than letting\n * an assistant trust filters that do nothing. Both are restored once the API fix ships (planned\n * 2026-09-07) — see the follow-up branch `RS-11517-restore-niche-filters-sep-07`.\n */\nconst inputSchema = {\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(LIST_NICHES_MAX_PAGE_SIZE)\n .optional()\n .describe(`Items per page (max ${LIST_NICHES_MAX_PAGE_SIZE}). Defaults to 20.`),\n};\n\nexport const listNichesTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_niches\",\n title: \"List DataDive Niches\",\n description:\n \"Use this first when the user asks about their niches, or to find a `nicheId` for use with \" +\n \"`get_niche_keywords`, `get_niche_competitors`, or `get_ranking_juice`. \" +\n \"Retrieves the Niches of the account. Each Niche represents a market segment or product \" +\n \"category being tracked. Returns nicheId, heroKeyword, nicheLabel, marketplace (com/uk/de/...), \" +\n \"and latestResearchDate per niche, plus pagination metadata (currentPage, pageSize, total, \" +\n \"lastPage, hasNext, hasPrev). \" +\n \"TEMPORARY LIMITATION: the API does not apply paging yet, so a single call returns every niche \" +\n \"of the account whatever `currentPage` and `pageSize` say, and the pagination metadata describes \" +\n \"a page that was not applied. Read the whole `data` array and filter it yourself; do not walk \" +\n \"`currentPage` while `hasNext` is true, because every page repeats the same rows. Accounts with \" +\n \"many niches therefore return a large response. There is no server-side search or sorting.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<NicheList>({ config: ctx.config, toolName: \"list_niches\" }, \"/v1/niches\", {\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n });\n },\n};\n","/**\n * Error mapping for the DataDive /v1 API.\n *\n * The MCP server is a thin HTTP shim — when api.datadive.tools returns an error,\n * we want the LLM (and ultimately the user) to see a useful, actionable message\n * rather than \"Request failed\". `ApiError.fromHttp` turns an HTTP status + body\n * into one of those messages.\n *\n * Source of truth for status semantics:\n * datadive-backend/src/external-api/auth/api-key.guard.ts (401/402 logic)\n * datadive-backend/src/external-api/external-api-v1.controller.ts (per-route docs)\n */\n\nconst KEY_HELP_URL = \"https://2.datadive.tools/api-key\";\n\nexport type ApiErrorKind =\n | \"auth\"\n | \"payment\"\n | \"forbidden\"\n | \"not_found\"\n | \"bad_request\"\n | \"rate_limit\"\n | \"server\"\n | \"network\"\n | \"http\";\n\nexport class ApiError extends Error {\n public readonly kind: ApiErrorKind;\n public readonly status: number;\n public readonly body: unknown;\n\n constructor(kind: ApiErrorKind, status: number, message: string, body?: unknown) {\n super(message);\n this.name = \"ApiError\";\n this.kind = kind;\n this.status = status;\n this.body = body;\n }\n\n static fromHttp(status: number, body: unknown): ApiError {\n const serverMsg = extractMessage(body);\n\n if (status === 400) {\n return new ApiError(\n \"bad_request\",\n 400,\n serverMsg ? `Bad request: ${serverMsg}` : \"Bad request: invalid parameters.\",\n body,\n );\n }\n if (status === 401) {\n return new ApiError(\n \"auth\",\n 401,\n `Authentication failed: your DATADIVE_API_KEY is invalid or expired. Generate a new key at ${KEY_HELP_URL}.`,\n body,\n );\n }\n if (status === 402) {\n return new ApiError(\n \"payment\",\n 402,\n \"Subscription is inactive or paused. Resume billing at https://2.datadive.tools to use the API.\",\n body,\n );\n }\n if (status === 403) {\n return new ApiError(\n \"forbidden\",\n 403,\n serverMsg\n ? `Forbidden: ${serverMsg}`\n : \"Forbidden: this API key cannot access the requested resource.\",\n body,\n );\n }\n if (status === 404) {\n return new ApiError(\"not_found\", 404, serverMsg ? `Not found: ${serverMsg}` : \"Resource not found.\", body);\n }\n if (status === 429) {\n return new ApiError(\"rate_limit\", 429, \"Rate limit exceeded. Wait a few seconds and retry.\", body);\n }\n if (status >= 500) {\n return new ApiError(\n \"server\",\n status,\n `DataDive API error (${status}). ${serverMsg ?? \"Please try again.\"}`.trim(),\n body,\n );\n }\n return new ApiError(\"http\", status, serverMsg ?? `HTTP ${status}`, body);\n }\n}\n\nfunction extractMessage(body: unknown): string | null {\n if (!body || typeof body !== \"object\") return null;\n const b = body as Record<string, unknown>;\n // NestJS conventional error shapes: { message: string | string[] } or { error, message }.\n if (typeof b.message === \"string\") return b.message;\n if (Array.isArray(b.message) && b.message.every((m) => typeof m === \"string\")) {\n return (b.message as string[]).join(\"; \");\n }\n if (typeof b.error === \"string\") return b.error;\n return null;\n}\n","{\n \"name\": \"@datadive-tools/mcp\",\n \"version\": \"0.11.0\",\n \"description\": \"MCP server for DataDive — query your niches, keywords, competitors, and Rank Radar data from Claude Desktop, Claude Code, or Cursor.\",\n \"type\": \"module\",\n \"bin\": {\n \"datadive-mcp\": \"./dist/index.js\"\n },\n \"main\": \"./dist/lib.js\",\n \"types\": \"./dist/lib.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/lib.d.ts\",\n \"import\": \"./dist/lib.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"engines\": {\n \"node\": \">=22\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"test\": \"vitest run\",\n \"test:coverage\": \"vitest run --coverage --coverage.include=\\\"src/**\\\" --coverage.reporter=json-summary --coverage.reporter=text\",\n \"test:watch\": \"vitest\",\n \"test:smoke\": \"DATADIVE_SMOKE=1 vitest run test/smoke.test.ts\",\n \"lint\": \"eslint .\",\n \"typecheck\": \"tsc --noEmit\",\n \"prepublishOnly\": \"npm run lint && npm run typecheck && npm run test && npm run build\",\n \"prepare\": \"npm run build\"\n },\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"datadive\",\n \"amazon\",\n \"claude\"\n ],\n \"author\": \"DataDive\",\n \"license\": \"MIT\",\n \"homepage\": \"https://github.com/Data-Dive-Tools/datadive-mcp\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/Data-Dive-Tools/datadive-mcp.git\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/Data-Dive-Tools/datadive-mcp/issues\"\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.0.0\",\n \"zod\": \"^3.23.0\"\n },\n \"devDependencies\": {\n \"@changesets/cli\": \"^2.27.0\",\n \"@types/node\": \"^20.0.0\",\n \"@typescript-eslint/eslint-plugin\": \"^8.0.0\",\n \"@typescript-eslint/parser\": \"^8.0.0\",\n \"@vitest/coverage-v8\": \"4.1.8\",\n \"eslint\": \"^9.0.0\",\n \"prettier\": \"^3.0.0\",\n \"tsup\": \"^8.0.0\",\n \"typescript\": \"~5.9.0\",\n \"vitest\": \"4.1.8\"\n }\n}\n","/**\n * HTTP client for the DataDive /v1 API.\n *\n * Responsibilities:\n * 1. Attach auth from config.credentials to every request — `x-api-key` (local\n * stdio path) or `Authorization: Bearer` (remote/OAuth path).\n * 2. Tag the User-Agent with the package version + tool name so backend access\n * logs can attribute usage per tool (no separate telemetry endpoint needed).\n * 3. Map HTTP errors via `ApiError.fromHttp`.\n * 4. Unwrap the `ResponseDto<T>` envelope (`{ message?, success?, data }`)\n * while preserving bare `PaginationResponse<T>` bodies (which need their\n * pagination metadata to reach the LLM).\n *\n * Envelope detection (verified against datadive-backend):\n * - GET /v1/niches -> NicheList extends PaginationResponse<NicheItem> (BARE)\n * - GET /v1/niches/rank-radars -> ResponseDto<ExternalRankRadarListResponseDto>\n * where the inner DTO IS itself paginated.\n * - All other read endpoints -> ResponseDto<T> (UNWRAP)\n *\n * Heuristic: if the body has `data` AND any pagination key (currentPage, lastPage,\n * hasNext, hasPrev, pageSize, total), it's a PaginationResponse — return as-is.\n * Otherwise if the body has `data`, it's a ResponseDto envelope — return body.data.\n *\n * Cite: datadive-backend/src/common/pagination/pagination.dto.ts (pagination keys)\n * datadive-backend/src/common/dto/response.dto.ts (envelope shape)\n */\n\nimport type { Config } from \"../config.js\";\nimport { ApiError } from \"./errors.js\";\nimport { version } from \"../../package.json\";\n\n/**\n * The running package version, inlined from package.json at build time by\n * tsup/esbuild (and by vite under vitest). Single source of truth — no manual\n * bump here, so it can never drift from what's published to npm.\n *\n * Written into the User-Agent header, and compared against the backend's\n * advertised latest version (see noteLatestVersion) to nudge stale installs.\n */\nexport const PKG_VERSION: string = version;\n\n/** Response header the DataDive backend uses to advertise the latest published MCP version. */\nconst LATEST_VERSION_HEADER = \"x-datadive-mcp-latest\";\n\n/**\n * One-time upgrade notice. Armed when the backend advertises a version newer than\n * the one we're running; consumed (and then suppressed for the rest of the session)\n * by takeUpgradeNotice(). We surface it once per process so we don't nag on every call.\n */\nlet pendingUpgradeNotice: string | null = null;\nlet hasWarned = false;\n\n/**\n * Compares dotted version strings (major.minor.patch). Pre-release / build suffixes\n * are stripped — a stable release is the signal we act on. Returns true iff `latest`\n * is strictly newer than `current`. Exported for unit testing.\n */\nexport function isNewerVersion(latest: string, current: string): boolean {\n const parse = (v: string) => v.split(/[-+]/)[0]!.split(\".\").map((n) => parseInt(n, 10) || 0);\n const a = parse(latest);\n const b = parse(current);\n for (let i = 0; i < 3; i++) {\n const da = a[i] ?? 0;\n const db = b[i] ?? 0;\n if (da !== db) return da > db;\n }\n return false;\n}\n\n/** Records the advertised latest version; arms a one-time notice if we're behind. Never throws. */\nfunction noteLatestVersion(latest: string | null): void {\n if (!latest || hasWarned) return;\n if (isNewerVersion(latest, PKG_VERSION)) {\n pendingUpgradeNotice =\n `A newer version of datadive-mcp is available (${latest}; you are running ${PKG_VERSION}). ` +\n `Update by reinstalling the latest from npm — e.g. \\`npm install -g @datadive-tools/mcp@latest\\` — ` +\n `or bump the @datadive-tools/mcp version in your MCP client config, then restart the client.`;\n }\n}\n\n/**\n * Returns a pending upgrade notice once, then suppresses further notices for the rest\n * of the session. Called when assembling a tool result so the LLM can relay it to the\n * user. Returns null when there's nothing to surface.\n */\nexport function takeUpgradeNotice(): string | null {\n if (!pendingUpgradeNotice) return null;\n const notice = pendingUpgradeNotice;\n pendingUpgradeNotice = null;\n hasWarned = true;\n return notice;\n}\n\nconst PAGINATION_KEYS = [\"currentPage\", \"lastPage\", \"hasNext\", \"hasPrev\", \"pageSize\", \"total\"] as const;\n\nexport interface RequestContext {\n config: Config;\n /** Tool name used to tag the User-Agent header for per-tool log attribution. */\n toolName: string;\n}\n\nexport async function httpGet<T>(ctx: RequestContext, path: string, query?: Record<string, unknown>): Promise<T> {\n const url = buildUrl(ctx.config.baseUrl, path, query);\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"GET\",\n headers: {\n ...authHeaders(ctx.config),\n accept: \"application/json\",\n \"user-agent\": userAgent(ctx.toolName),\n },\n });\n } catch (e) {\n throw networkError(url, e);\n }\n\n return handleResponse<T>(res);\n}\n\n/**\n * POST a JSON body to a /v1 write endpoint. Mirrors httpGet (same auth/UA headers,\n * version notice, error mapping, and envelope unwrapping) but adds a JSON request\n * body. The create endpoints return bare objects (no ResponseDto envelope), which\n * unwrap() passes through untouched.\n */\nexport async function httpPost<T>(ctx: RequestContext, path: string, body: unknown): Promise<T> {\n const url = buildUrl(ctx.config.baseUrl, path);\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"POST\",\n headers: {\n ...authHeaders(ctx.config),\n accept: \"application/json\",\n \"content-type\": \"application/json\",\n \"user-agent\": userAgent(ctx.toolName),\n },\n body: JSON.stringify(body),\n });\n } catch (e) {\n throw networkError(url, e);\n }\n\n return handleResponse<T>(res);\n}\n\n/**\n * DELETE a /v1 resource. Same auth/UA headers, version notice and error mapping as\n * httpGet. The delete endpoints answer 204 No Content, so handleResponse resolves to\n * `null` — callers return their own confirmation payload rather than that null.\n */\nexport async function httpDelete<T>(ctx: RequestContext, path: string): Promise<T> {\n const url = buildUrl(ctx.config.baseUrl, path);\n\n let res: Response;\n try {\n res = await fetch(url, {\n method: \"DELETE\",\n headers: {\n ...authHeaders(ctx.config),\n accept: \"application/json\",\n \"user-agent\": userAgent(ctx.toolName),\n },\n });\n } catch (e) {\n throw networkError(url, e);\n }\n\n return handleResponse<T>(res);\n}\n\nfunction networkError(url: string, e: unknown): ApiError {\n const msg = e instanceof Error ? e.message : String(e);\n return new ApiError(\"network\", 0, `Network error reaching ${url}: ${msg}`);\n}\n\n/**\n * Shared response handling for httpGet/httpPost: arm the one-time upgrade notice,\n * parse the body, map HTTP errors via ApiError, and unwrap the ResponseDto envelope.\n */\nasync function handleResponse<T>(res: Response): Promise<T> {\n // The backend advertises the latest published MCP version on every response\n // (including errors); arm a one-time upgrade notice if we're behind.\n noteLatestVersion(res.headers.get(LATEST_VERSION_HEADER));\n\n const text = await res.text();\n let body: unknown;\n try {\n body = text.length > 0 ? JSON.parse(text) : null;\n } catch {\n body = text;\n }\n\n if (!res.ok) {\n throw ApiError.fromHttp(res.status, body);\n }\n\n return unwrap<T>(body);\n}\n\nfunction buildUrl(baseUrl: string, path: string, query?: Record<string, unknown>): string {\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n const u = new URL(`${baseUrl}${normalizedPath}`);\n if (query) {\n for (const [k, v] of Object.entries(query)) {\n if (v === undefined || v === null) continue;\n u.searchParams.set(k, String(v));\n }\n }\n return u.toString();\n}\n\nfunction userAgent(toolName: string): string {\n return `datadive-mcp/${PKG_VERSION} tool/${toolName}`;\n}\n\n/**\n * Auth header(s) for the configured credentials: `x-api-key` on the local stdio\n * path, `Authorization: Bearer` on the remote/OAuth path. The credentials object\n * is read per request so a host can rotate a refreshed bearer token in place.\n */\nfunction authHeaders(config: Config): Record<string, string> {\n const c = config.credentials;\n return c.kind === \"bearer\" ? { authorization: `Bearer ${c.token}` } : { \"x-api-key\": c.apiKey };\n}\n\n/**\n * Unwraps a ResponseDto<T> envelope while preserving bare PaginationResponse bodies.\n * Exported for unit testing.\n */\nexport function unwrap<T>(body: unknown): T {\n if (!isObject(body)) return body as T;\n if (!(\"data\" in body)) return body as T;\n // If pagination metadata is present at the top level, return as-is.\n for (const key of PAGINATION_KEYS) {\n if (key in body) return body as T;\n }\n // Plain ResponseDto envelope -> unwrap.\n return (body as { data: T }).data;\n}\n\nfunction isObject(v: unknown): v is Record<string, unknown> {\n return typeof v === \"object\" && v !== null && !Array.isArray(v);\n}\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { GetMasterKeywordListResult } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getNicheKeywordsTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_niche_keywords\",\n title: \"Get Master Keyword List for a Niche\",\n description:\n \"Use this when the user asks about keywords, search terms, or search volume for a niche. \" +\n \"Retrieves the master keyword list for the specified Niche — the relevant search terms \" +\n \"monitored for ranking and performance metrics. For each keyword, returns search volume, \" +\n \"relevancy (numeric score or \\\"Outlier\\\"), and competitor ASIN organic ranks \" +\n \"(asinRanks: { ASIN -> rank | null }). Also returns the latestResearchDate.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<GetMasterKeywordListResult>(\n { config: ctx.config, toolName: \"get_niche_keywords\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/keywords`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { NicheRoots } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getNicheRootsTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_niche_roots\",\n title: \"Get Keyword Roots for a Niche\",\n description:\n \"Use this when the user wants to find the highest-impact words across a niche's keywords, asks about \" +\n \"'keyword roots', or wants to prioritize terms for a listing. \" +\n \"Retrieves the keyword lexical roots for the Niche — individual words or word-combinations extracted \" +\n \"from the Master Keyword List (e.g. 'bluetooth headphones' yields roots 'bluetooth', 'headphones', \" +\n \"'bluetooth headphones'). Returns `roots` and `normalizedRoots` tables, each item with root, frequency \" +\n \"(how many keywords contain it), broadSearchVolume (summed search volume of those keywords), and \" +\n \"broadSearchVolumeRatio (0-1, relative to the top root), plus the per-keyword breakdowns and \" +\n \"latestResearchDate.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<NicheRoots>(\n { config: ctx.config, toolName: \"get_niche_roots\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/roots`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { GetCompetitorListResult } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getNicheCompetitorsTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_niche_competitors\",\n title: \"Get Competitors and Niche Statistics\",\n description:\n \"Use this for competitor ASINs, product titles, BSR, sales/revenue benchmarks, or niche \" +\n \"opportunity scoring. Retrieves the list of Competitors within the specified Niche along with \" +\n \"Niche statistics. For the Niche: keyword statistics, opportunity evaluation, benchmark median \" +\n \"values, and overall competitor strength assessment. For each Competitor: ASIN, product title, \" +\n \"BSR, category and full category tree, sales, revenue, ratings, reviews, price, number of \" +\n \"variations, image URL, total Ranking Juice, and ranking data. BSR supports ranking \" +\n \"Competitors by sales position within mentioned category. For the Ranking Juice breakdown by \" +\n \"title, bullets and description, use `get_ranking_juice`.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<GetCompetitorListResult>(\n { config: ctx.config, toolName: \"get_niche_competitors\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/competitors`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { NicheRankingJuices } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The unique identifier of the Niche (from `list_niches`).\"),\n};\n\nexport const getRankingJuiceTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_ranking_juice\",\n title: \"Get Ranking Juices for a Niche\",\n description:\n \"Use this when the user asks 'how do I rank higher', 'what's my ranking juice', or wants \" +\n \"listing-optimization guidance. \" +\n \"Retrieves the Ranking Juices for each Competitor within the specified Niche. Ranking Juice is \" +\n \"DataDive's proprietary metric identifying the key factors driving search-ranking success \" +\n \"for each Competitor's listing. Returns currentListing and optimizedListing breakdowns \" +\n \"(rankingJuice + per-property contributions: title, bullets, description) plus per-competitor \" +\n \"listing scores and the latestResearchDate.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<NicheRankingJuices>(\n { config: ctx.config, toolName: \"get_ranking_juice\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/ranking-juices`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport {\n RANK_RADAR_STATUSES,\n type ApiRankRadarItem,\n type ApiRankRadarList,\n type RankRadarItem,\n type RankRadarList,\n} from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\n/** Mirrors the API's `PaginationParams.MAX_PAGE_SIZE`; a larger value is not honoured there. */\nexport const LIST_RANK_RADARS_MAX_PAGE_SIZE = 50;\n\nconst inputSchema = {\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(LIST_RANK_RADARS_MAX_PAGE_SIZE)\n .optional()\n .describe(`Items per page (max ${LIST_RANK_RADARS_MAX_PAGE_SIZE}). Defaults to 20.`),\n nicheId: z\n .string()\n .min(1)\n .optional()\n .describe(\"Filter Rank Radars by Niche identifier. Use after `list_niches`.\"),\n status: z\n .enum(RANK_RADAR_STATUSES)\n .optional()\n .describe(\n \"Which Rank Radars to return. ACTIVE (the default) are tracking; PAUSED have been stopped \" +\n \"but keep their history and can be resumed; ARCHIVED have been deleted; ALL means active \" +\n \"and paused together. Archived Rank Radars are returned only by an explicit ARCHIVED.\",\n ),\n searchText: z.string().optional().describe(\"Filter Rank Radars by ASIN or product title.\"),\n};\n\n/**\n * Drops the `krt_asin` row's internal identifiers and keeps the ASIN itself. The API sends\n * `asin: { id, krtId, asin }`, where `id` is an internal row id and `krtId` repeats the item's own\n * `id`; neither is accepted by any tool, so both are noise in the model's context.\n */\nfunction minimize(item: ApiRankRadarItem): RankRadarItem {\n const { asin, ...rest } = item;\n\n return { ...rest, asin: asin?.asin };\n}\n\nexport const listRankRadarsTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_rank_radars\",\n title: \"List Rank Radars\",\n description:\n \"Use this to find a `rankRadarId` before calling `get_rank_radar_data`. Filter by `nicheId` \" +\n \"if the user has already identified a niche. \" +\n \"Retrieves a paginated list of Rank Radars — keyword-rank trackers monitoring organic and \" +\n \"sponsored positions for specific ASINs over time. Each item includes id, status (ACTIVE, \" +\n \"PAUSED or ARCHIVED), asin, marketplace, keywordCount, title, imageUrl, and summary metrics: \" +\n \"top10KW, top10SV, top50KW, top50SV. Supports pagination metadata (currentPage, pageSize, \" +\n `total, lastPage, hasNext, hasPrev); a page holds at most ${LIST_RANK_RADARS_MAX_PAGE_SIZE} ` +\n \"Rank Radars, so a large account needs several calls.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx): Promise<RankRadarList> => {\n const page = await httpGet<ApiRankRadarList>(\n { config: ctx.config, toolName: \"list_rank_radars\" },\n \"/v1/niches/rank-radars\",\n {\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n nicheId: args.nicheId,\n status: args.status,\n searchText: args.searchText,\n },\n );\n\n return { ...page, data: (page.data ?? []).map(minimize) };\n },\n};\n","/**\n * Hand-mirrored DTOs from the DataDive backend (v1 external API).\n *\n * Source of truth (in this priority order):\n * 1. Live Swagger: https://developer.datadive.tools/docs#/v1\n * (machine-readable: https://developer.datadive.tools/docs-json)\n * 2. Backend DTOs: datadive-backend/src/external-api/dto/*.ts\n * datadive-backend/src/niche-research/dto/rank-radar.response.dto.ts\n * datadive-backend/src/common/dto/response.dto.ts\n * datadive-backend/src/common/pagination/pagination.dto.ts\n * 3. Controller: datadive-backend/src/external-api/external-api-v1.controller.ts\n *\n * Last synced: 2026-06-19.\n *\n * The MCP server forwards JSON straight to the LLM, so deeply-nested DTOs are\n * intentionally typed loosely (with `unknown` or pass-through Records) where\n * the TS surface doesn't add safety. Pin tighter as use cases require.\n */\n\n// ─── Envelope + pagination ───────────────────────────────────────────────────\n\n/** Generic NestJS response envelope: { message?, success?, data? }. */\nexport interface ResponseEnvelope<T> {\n message?: string;\n /** @deprecated — use HTTP status as the success indicator. */\n success?: boolean;\n data?: T | null;\n}\n\n/** Standard pagination envelope returned by list endpoints. */\nexport interface PaginationResponse<T> {\n data: T[];\n currentPage: number;\n pageSize: number;\n total: number;\n lastPage: number;\n hasNext: boolean;\n hasPrev: boolean;\n}\n\n/**\n * ISO-8601 date or timestamp, mirroring the backend's `@IsISO8601()` (date-only\n * allowed). Shared by the date/timestamp query params across /v1 tools\n * (alerts `updatedSince`, usage `startDate`/`endDate`).\n */\nexport const ISO_8601 = /^\\d{4}-\\d{2}-\\d{2}(?:T\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})?)?$/;\n\n/**\n * Marketplace codes accepted by `marketplace` params across /v1 endpoints\n * (alerts, inventory). Mirrors the backend's `SupportedMarketplaceEnum`.\n */\nexport const SUPPORTED_MARKETPLACES = [\n \"com\",\n \"ca\",\n \"co.uk\",\n \"com.mx\",\n \"in\",\n \"fr\",\n \"de\",\n \"es\",\n \"it\",\n \"co.jp\",\n] as const;\n\n// ─── /v1/niches (NicheList = PaginationResponse<NicheItem>) ─────────────────\n\nexport interface NicheItem {\n nicheId: string;\n heroKeyword: string;\n nicheLabel: string;\n /** Amazon marketplace TLD: \"com\", \"uk\", \"de\", etc. */\n marketplace: string;\n /** ISO date string when the Niche was last researched. Nullable. */\n latestResearchDate: string | null;\n}\n\nexport type NicheList = PaginationResponse<NicheItem>;\n\n// ─── /v1/niches/:nicheId/keywords (GetMasterKeywordListResult, wrapped) ─────\n\n/** Map of competitor ASIN -> rank (or null when unranked). */\nexport type AsinRanks = Record<string, number | null>;\n\nexport interface MasterKeyword {\n keyword: string;\n searchVolume: number;\n /** \"Outlier\" or a numeric relevancy score. */\n relevancy: string | number;\n asinRanks: AsinRanks;\n}\n\nexport interface GetMasterKeywordListResult {\n keywords: MasterKeyword[];\n /** ISO date string. */\n latestResearchDate: string;\n}\n\n// ─── /v1/niches/:nicheId/competitors (GetCompetitorListResult, wrapped) ─────\n\n/**\n * A single Competitor row. The fields documented here are the ones worth naming for\n * a consumer; the index signature keeps the rest of the payload (keyword and ads\n * diagnostics, evaluation labels) available to the LLM without enumerating it.\n */\nexport interface Competitor {\n asin: string;\n /** Product title. */\n title: string;\n /** Best Sellers Rank. Null when Amazon publishes none for the product. */\n bsr: number | null;\n /** Amazon category name. Null when unknown. */\n category: string | null;\n /** Full category path, e.g. \"Pet Supplies > Dogs > Apparel & Accessories\". Null when unknown. */\n categoryTree: string | null;\n brand: string | null;\n price: number | null;\n rating: number | null;\n reviewCount: number | null;\n sales: number | null;\n revenue: number | null;\n numberOfVariations: number;\n /**\n * Total Ranking Juice for this listing. Use `get_ranking_juice` for the breakdown\n * by title, bullets and description.\n */\n listingRankingJuice: { value: number };\n [key: string]: unknown;\n}\n\n/**\n * Niche competitors response — several nested DTOs from the backend (MklStatisticsData,\n * OpportunityEvaluation, MklBenchmark, CompetitorsStrength). We type the outer shape and\n * the competitor rows, and leave the niche-level aggregates as opaque records; the LLM\n * sees the full JSON and can reason about it. Tighten if a specific consumer needs it.\n */\nexport interface GetCompetitorListResult {\n /** Marketplace enum (AmazonStore in backend). */\n marketplace: string;\n statistics: Record<string, unknown>;\n opportunityEvaluation: Record<string, unknown>;\n benchmark: Record<string, unknown>;\n competitorsStrength: Record<string, unknown>;\n competitors: Competitor[];\n latestResearchDate: string;\n}\n\n// ─── /v1/niches/:nicheId/ranking-juices (NicheRankingJuices, wrapped) ───────\n\nexport interface RankingJuiceProperty {\n rankingJuice: number;\n}\n\nexport interface ListingRankingJuice {\n rankingJuice: number;\n title: RankingJuiceProperty;\n bullets: RankingJuiceProperty;\n description: RankingJuiceProperty;\n}\n\nexport interface CompetitorRankingJuice {\n asin: string;\n listing: ListingRankingJuice;\n}\n\nexport interface NicheRankingJuices {\n currentListing: ListingRankingJuice;\n optimizedListing: ListingRankingJuice;\n competitors: CompetitorRankingJuice[];\n latestResearchDate: string;\n}\n\n// ─── /v1/niches/rank-radars (ExternalRankRadarListResponseDto, wrapped) ─────\n\n/**\n * States a Rank Radar can be reported in. Matches the backend's `ExternalRankRadarStatus`, so a\n * value read out of a response can be fed straight back into the `status` filter.\n *\n * `PAUSED` is what the API's own archive endpoint produces (tracking stopped, data kept);\n * `ARCHIVED` is a Rank Radar that was deleted.\n */\nexport const RANK_RADAR_STATES = [\"ACTIVE\", \"PAUSED\", \"ARCHIVED\"] as const;\nexport type RankRadarState = (typeof RANK_RADAR_STATES)[number];\n\n/**\n * Accepted values of the `status` query param on /v1/niches/rank-radars. `ALL` is filter-only —\n * it selects active and paused together and never appears as an item's own status.\n *\n * The API rejects anything else with 400 (datadive-backend `ExternalKeywordRankTrackerStatus`).\n * Before RS-11518 it accepted only `ALL`/`PAUSED` and silently answered anything else with the\n * active set, so this list must not be widened ahead of the API.\n */\nexport const RANK_RADAR_STATUSES = [...RANK_RADAR_STATES, \"ALL\"] as const;\nexport type RankRadarStatus = (typeof RANK_RADAR_STATUSES)[number];\n\n/** A row exactly as `/v1/niches/rank-radars` sends it, before the tool minimizes it. */\nexport interface ApiRankRadarItem {\n id: string;\n status: RankRadarState;\n /** Only `asin` carries information; `id`/`krtId` are internal row identifiers. */\n asin: { id: string; krtId: string; asin: string };\n marketplace: string;\n keywordCount: number;\n title: string;\n imageUrl: string;\n top10KW: number | null;\n top10SV: number | null;\n top50KW: number | null;\n top50SV: number | null;\n}\n\n/** A row as `list_rank_radars` returns it: `asin` flattened to the ASIN, no internal identifiers. */\nexport interface RankRadarItem extends Omit<ApiRankRadarItem, \"asin\"> {\n asin: string;\n}\n\nexport type ApiRankRadarList = PaginationResponse<ApiRankRadarItem>;\nexport type RankRadarList = PaginationResponse<RankRadarItem>;\n\n// ─── POST /v1/niches/rank-radars (CreateRankRadarSuccessResponseDto, bare) ──\n\n/** Result of creating a Rank Radar. */\nexport interface CreateRankRadarResult {\n rankRadarId: string;\n}\n\n// ─── POST /v1/niches/dives (CreateNicheDiveSuccessResponseDto, bare) ────────\n\n/** Result of kicking off a Niche Dive. Poll get_dive_status with `diveId`. */\nexport interface CreateNicheDiveResult {\n diveId: string;\n /** ISO-8601 estimated completion timestamp. */\n estimatedCompletionDate: string;\n}\n\n// ─── POST /v1/niches/:nicheId/redive (RediveNicheResponseDto, wrapped) ──────\n\n/**\n * Result of re-diving an existing niche. Same shape as CreateNicheDiveResult and\n * polled the same way, but the niche keeps its existing `nicheId` — so unlike a new\n * dive, `get_dive_status` success carries no id the caller did not already have.\n */\nexport interface RediveNicheResult {\n diveId: string;\n /** ISO-8601 estimated completion timestamp. */\n estimatedCompletionDate: string;\n}\n\n// ─── GET /v1/niches/dives/:diveId (NicheDiveStatus*ResponseDto, bare oneOf) ──\n\n/**\n * Niche Dive status, discriminated on `status`:\n * - \"in_progress\" → estimatedCompletionDate\n * - \"success\" → nicheId + token usage (feed nicheId to list_niches / get_niche_*)\n * - \"error\" → error message\n */\nexport type DiveStatus =\n | { diveId: string; status: \"in_progress\"; estimatedCompletionDate: string }\n | { diveId: string; status: \"success\"; nicheId: string; tokensUsed: number; tokensLeft: number }\n | { diveId: string; status: \"error\"; error: string };\n\n// ─── /v1/niches/rank-radars/:rankRadarId (KrtKeywordResponseDto[], wrapped) ─\n\nexport interface KrtAsinRank {\n /** ISO date string. */\n date: string;\n organicRank?: number | null;\n impressionRank?: number | null;\n}\n\nexport interface KrtKeyword {\n id: string;\n keyword: string;\n searchVolume: number | null;\n ranks: KrtAsinRank[];\n /** Highlight annotations (KrtHighlightDto in backend); kept opaque for MVP. */\n highlights: Array<Record<string, unknown>>;\n}\n\nexport type RankRadarKeywordList = KrtKeyword[];\n\n// ─── /v1/sellers/:sellerId/marketplaces/:marketplace/asins/:asin/inventory ────\n// (InventoryByFcResponseDto, wrapped)\n\nexport interface InventoryByFcItem {\n /** Fulfillment center code. */\n fc: string;\n /** State or region code where the FC is located, when known. */\n state: string;\n /** Units of sellable inventory currently at this FC. May be 0 when stock is out. */\n availableStock: number;\n /** Share of the ASIN's total sellable inventory at this FC, as a 0..1 fraction. */\n availableStockPercentage: number;\n}\n\nexport interface InventoryByFcResponse {\n asin: string;\n sellerId: string;\n /** Marketplace enum: \"com\", \"ca\", \"co.uk\", \"com.mx\", \"in\", \"fr\", \"de\", \"es\", \"it\", \"co.jp\". */\n marketplace: string;\n /** ISO timestamp of last successful ingestion. Null if none in the last 30 days. */\n lastUpdatedAt: string | null;\n totalSellableUnits: number;\n distribution: InventoryByFcItem[];\n}\n\n// ─── /v1/alerts/indexing-issues (IndexingIssueAlertListDto, bare) ───────────\n\n/**\n * Allowed values for the `status` query param on the /v1/alerts/* endpoints.\n * `active` is the server-side default; dismissed alerts are never returned.\n */\nexport const ALERT_STATUSES = [\"active\", \"resolved\", \"all\"] as const;\nexport type AlertStatus = (typeof ALERT_STATUSES)[number];\n\nexport interface IndexingIssueAlertItem {\n id: number;\n /** The ASIN that is no longer indexed for its tracked keywords. */\n asin: string;\n title: string | null;\n imageUrl: string | null;\n /** True when the ASIN is a parent of a variation family. */\n isParent: boolean;\n sellerId: string;\n marketplace: string;\n /** ISO timestamp of when the alert was most recently surfaced. Updates each time it re-fires. */\n lastAlertedAt: string;\n /** ISO timestamp of when the alert was resolved, or null if still active. */\n resolvedAt: string | null;\n}\n\nexport type IndexingIssueAlertList = PaginationResponse<IndexingIssueAlertItem>;\n\n// ─── /v1/alerts/blind-spend (BlindSpendAlertListDto, bare) ──────────────────\n\nexport interface BlindSpendSearchTerm {\n /** The customer search term that wasted spend. */\n term: string;\n spend: number | null;\n sales: number | null;\n clicks: number | null;\n /** Conversion rate as a 0..1 fraction. */\n cvr: number | null;\n impressions: number | null;\n}\n\nexport interface BlindSpendAlertItem {\n id: number;\n asin: string | null;\n title: string | null;\n imageUrl: string | null;\n sellerId: string;\n marketplace: string;\n /** ISO timestamp of when the alert was most recently surfaced. Updates each time it re-fires. */\n lastAlertedAt: string;\n /** ISO timestamp of when the alert was resolved, or null if still active. */\n resolvedAt: string | null;\n /** Total ad spend across the unresolved wasted-spend search terms. */\n wastedSpend: number;\n totalKeywordCount: number;\n unresolvedKeywordCount: number;\n /** The unresolved wasted-spend search terms. */\n searchTerms: BlindSpendSearchTerm[];\n}\n\nexport type BlindSpendAlertList = PaginationResponse<BlindSpendAlertItem>;\n\n// ─── Billable features (shared by /v1/quota and /v1/usage) ───────────────────\n\n/**\n * Billable feature types. These are both the keys of the `quota.features` object\n * and the allowed values of the `type` filter on /v1/usage. Mirrors the backend's\n * billable-feature enum.\n */\nexport const BILLABLE_FEATURE_TYPES = [\n \"DIVED_ASINS\",\n \"PRODUCT_BRIEF_ASINS\",\n \"AI_COPYWRITER_PROMPTS\",\n \"RANK_RADAR_KEYWORDS\",\n] as const;\nexport type BillableFeatureType = (typeof BILLABLE_FEATURE_TYPES)[number];\n\n// ─── /v1/niches/:nicheId/roots (ExternalRootResponseDto[], wrapped) ─────────\n\nexport interface RootsTableItem {\n /** A word or word-combination extracted from the master keyword list. */\n root: string;\n /** Count of keywords in the master list that contain this root. */\n frequency: number;\n /** Sum of search volume across all original keywords containing this root. */\n broadSearchVolume: number;\n /** broadSearchVolume / maxBroadSearchVolume, as a 0..1 fraction. */\n broadSearchVolumeRatio: number;\n}\n\n/**\n * Keyword-roots analysis for a Niche. `keywords` and `consolidatedKeywords` are\n * the per-keyword breakdowns (kept opaque — the LLM sees the full JSON); `roots`\n * and `normalizedRoots` are the ranked root tables.\n *\n * Note: the OpenAPI spec types the endpoint's `data` as an array, but the live\n * API returns a single object (verified against api-qa, 2026-06-17). The client's\n * unwrap() strips the `{ data }` envelope, so the tool returns this object directly.\n */\nexport interface NicheRoots {\n keywords: Array<Record<string, unknown>>;\n consolidatedKeywords: Array<Record<string, unknown>>;\n roots: RootsTableItem[];\n normalizedRoots: RootsTableItem[];\n /** ISO date string of the last successful research for the niche. */\n latestResearchDate: string;\n}\n\n// ─── /v1/quota (ExternalQuotaResponseDto) ───────────────────────────────────\n\nexport interface QuotaFeature {\n /** Current usage count. Null when not applicable to the plan. */\n used: number | null;\n /** Quota capacity. Null when unlimited / not applicable. */\n capacity: number | null;\n}\n\nexport interface Quota {\n /** ISO-8601 timestamp of the next quota reset. Null when no reset is scheduled. */\n nextRefreshDate: string | null;\n /** Per-billable-feature usage and capacity. */\n features: Record<BillableFeatureType, QuotaFeature>;\n}\n\n// ─── /v1/usage (ExternalUsageLogListDto, bare PaginationResponse) ───────────\n\nexport interface UsageLogItem {\n /** Name of the user who performed the action. Null when unknown. */\n name: string | null;\n email: string;\n /** Number of tokens consumed. */\n qty: number;\n type: BillableFeatureType;\n /** Specific action performed (e.g. \"RANK_RADAR_CREATE\"). Null when not set. */\n action: string | null;\n nicheId: string | null;\n nicheName: string | null;\n rankRadarId: string | null;\n /** ISO timestamp of when the usage was recorded. */\n date: string;\n}\n\nexport type UsageLogList = PaginationResponse<UsageLogItem>;\n\n// ─── /v1/seller_profiles (SellerProfileListDto, bare PaginationResponse) ─────\n\nexport interface SellerProfileItem {\n /** Amazon seller account ID — the value other seller-scoped tools require. */\n sellerId: string;\n /** Display name of the connected seller account. */\n sellerName: string;\n /** Marketplace code, e.g. \"com\", \"co.uk\" (see SUPPORTED_MARKETPLACES). */\n marketplace: string;\n /** True when Amazon Advertising API credentials are connected for this account. */\n hasAdApi: boolean;\n /** ISO timestamp of when the account was connected to DataDive. */\n createdAt: string;\n}\n\nexport type SellerProfileList = PaginationResponse<SellerProfileItem>;\n\n// ─── /v1/seller_profiles/:sellerId/marketplaces/:marketplace/catalog ──────────\n// (CatalogAsinListDto, bare PaginationResponse)\n\n/** Allowed values for the `status` query param on the seller catalog endpoint. */\nexport const CATALOG_STATUSES = [\"Active\", \"all\"] as const;\nexport type CatalogStatus = (typeof CATALOG_STATUSES)[number];\n\nexport interface CatalogAsinItem {\n asin: string;\n title: string;\n /** Parent ASIN for a variation child, or null for standalone/parent ASINs. */\n parentAsin: string | null;\n brand: string | null;\n /** Listing status, e.g. \"Active\". */\n status: string;\n imageUrl: string | null;\n /** True when the ASIN has variation children. Null when unknown. */\n hasVariations: boolean | null;\n}\n\nexport type SellerCatalogList = PaginationResponse<CatalogAsinItem>;\n\n// ─── /v1/seller_profiles/:sellerId/marketplaces/:marketplace/listing-changes ──\n// (ListingChangeExternalListDto, bare PaginationResponse)\n\n/** Allowed values for the `types` query param on the listing-changes endpoint. */\nexport const LISTING_CHANGE_TYPES = [\"Price\", \"Content\", \"Image\"] as const;\nexport type ListingChangeType = (typeof LISTING_CHANGE_TYPES)[number];\n\n/** Allowed values for the `sortBy` query param on the listing-changes endpoint. */\nexport const LISTING_CHANGE_SORT_BY = [\"date\", \"type\"] as const;\nexport type ListingChangeSortBy = (typeof LISTING_CHANGE_SORT_BY)[number];\n\n/** Generic sort direction, shared where a `sortOrder` query param is accepted. */\nexport const SORT_ORDER = [\"ASC\", \"DESC\"] as const;\nexport type SortOrder = (typeof SORT_ORDER)[number];\n\n/**\n * Optional ranking/conversion correlation for a listing change, present only when\n * `includeCorrelations=true`. Sub-objects (salesCvr, top10/top50SearchTerms) carry\n * before/after figures and are kept loosely typed per this file's convention.\n */\nexport interface ListingChangeCorrelation {\n /** \"PENDING\" | \"AVAILABLE\" | \"UNAVAILABLE_NO_ACTIVE_RANK_RADAR\" | \"UNAVAILABLE\". */\n status: string;\n message: string | null;\n salesCvr: Record<string, unknown> | null;\n top10SearchTerms: Record<string, unknown> | null;\n top50SearchTerms: Record<string, unknown> | null;\n}\n\nexport interface ListingChangeItem {\n asin: string;\n title: string | null;\n imageUrl: string | null;\n /** ISO timestamp of when the change was detected. */\n date: string;\n /** \"Price\" | \"Content\" | \"Image\". */\n type: string;\n /** Sub-type for content changes (e.g. title/bullets/description); null otherwise. */\n contentType: string | null;\n description: string;\n /** Prior value of the changed field. Shape varies by change type; kept opaque. */\n previousValue: Record<string, unknown> | null;\n /** New value of the changed field. Shape varies by change type; kept opaque. */\n newValue: Record<string, unknown> | null;\n /** Present only when `includeCorrelations=true`; null otherwise. */\n correlation: ListingChangeCorrelation | null;\n}\n\nexport type ListingChangeList = PaginationResponse<ListingChangeItem>;\n\n// ─── /v1/niches/rank-radars/:rankRadarId/search-terms (ExternalAddSearchTermsResponseDto, wrapped) ──\n\n/**\n * Where each submitted search term ended up. `manual` and `suggestions` are terms the\n * niche had never seen; the rest were already known to the niche in that role.\n * Mirrors the backend's SearchTermOriginBreakdown.\n */\nexport interface SearchTermOriginBreakdown {\n tracked: string[];\n paused: string[];\n outliers: string[];\n residues: string[];\n ppc: string[];\n /** Terms the niche has never seen, typed in by the user. */\n manual: string[];\n /** Terms the niche has never seen, accepted from the Rank Radar keyword suggestions. */\n suggestions: string[];\n}\n\nexport interface AddSearchTermsResult {\n originBreakdown: SearchTermOriginBreakdown;\n /** search term -> rankRadarKeywordId, the id the archive/resume search-term tools take. */\n keywordToRankRadarKeywordIdMap: Record<string, string>;\n}\n\n/** Backend cap on how many search terms one add call accepts (ADD_SEARCH_TERM_MAX_LIMIT). */\nexport const ADD_SEARCH_TERMS_MAX = 1000;\n\n// ─── /v1/niches/:nicheId/ai-copywriter (AI Copywriter, wrapped) ─────────────\n\n/**\n * Generation strategies the AI Copywriter accepts. Mirrors the backend's\n * GenerateAiCopywriterPromptOption enum.\n */\nexport const LISTING_COPY_STRATEGIES = [\"cosmo\", \"ranking-juice\", \"nlp\", \"cosmo-rufus\"] as const;\nexport type ListingCopyStrategy = (typeof LISTING_COPY_STRATEGIES)[number];\n\n/** Returned immediately when a generation is enqueued. */\nexport interface CreateListingCopyResult {\n generationId: string;\n status: \"generating\";\n}\n\nexport interface ListingCopyResult {\n title: string;\n bullets: string[];\n description: string;\n itemHighlights: string;\n /** Ranking Juice metrics for the generated copy (value + per-field contribution). */\n rankingJuice: Record<string, unknown>;\n methodology_breakdown?: unknown;\n}\n\n/** Polled status of a generation. Discriminated on `status`, like DiveStatus. */\nexport type ListingCopyGenerationStatus =\n | { generationId: string; status: \"generating\" }\n | { generationId: string; status: \"complete\"; result: ListingCopyResult }\n | { generationId: string; status: \"failed\"; error: string };\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { RankRadarKeywordList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\n/** yyyy-mm-dd matcher used by both startDate and endDate. */\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}$/;\n\nconst inputSchema = {\n rankRadarId: z\n .string()\n .uuid()\n .describe(\"The Rank Radar UUID (from `list_rank_radars`).\"),\n startDate: z\n .string()\n .regex(ISO_DATE, \"startDate must be in yyyy-mm-dd format\")\n .describe(\"Start date for the ranking data range, yyyy-mm-dd (e.g. 2024-03-26).\"),\n endDate: z\n .string()\n .regex(ISO_DATE, \"endDate must be in yyyy-mm-dd format\")\n .describe(\"End date for the ranking data range, yyyy-mm-dd (e.g. 2024-04-26). Must be on or after startDate.\"),\n};\n\nexport const getRankRadarDataTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_rank_radar_data\",\n title: \"Get Keyword Rankings for a Rank Radar\",\n description:\n \"Use this to analyze keyword ranking trends over time. Requires startDate and endDate (yyyy-mm-dd). \" +\n \"Retrieves historical keyword ranking data for the specified Rank Radar within the date range. \" +\n \"Returns an array of tracked keywords; each has id, keyword, searchVolume, ranks (per-day \" +\n \"{ date, organicRank, impressionRank }), and any highlight annotations. Use after \" +\n \"`list_rank_radars` to discover a `rankRadarId`.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<RankRadarKeywordList>(\n { config: ctx.config, toolName: \"get_rank_radar_data\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}`,\n { startDate: args.startDate, endDate: args.endDate },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport type { CreateRankRadarResult } from \"../types/api.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n asin: z.string().min(1).describe(\"The ASIN to track keyword rankings for.\"),\n numberOfKeywords: z\n .number()\n .int()\n .min(1)\n .describe(\"How many keywords to track (minimum 1). More keywords = broader tracking but more Search Term tokens.\"),\n nicheId: z\n .string()\n .min(1)\n .describe(\"The niche this Rank Radar belongs to. Get one from `list_niches`.\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\n \"Must be true to proceed — creating a Rank Radar spends Search Term tokens. Confirm the cost with the user first.\",\n ),\n};\n\nexport const createRankRadarTool: ToolDefinition<typeof inputSchema> = {\n name: \"create_rank_radar\",\n title: \"Create a Rank Radar\",\n description:\n \"Use this to start tracking organic and sponsored keyword rankings for an ASIN within a niche. \" +\n \"⚠️ Spends Search Term tokens (cost scales with `numberOfKeywords`) and cannot be undone — set \" +\n \"`confirm: true` only after the user approves the cost. Not safe to retry: each call spends tokens again \" +\n \"and creates a separate Rank Radar — if a call errors or times out, check `list_rank_radars` instead of \" +\n \"re-calling. Returns the new `rankRadarId`; read its data \" +\n \"later with `get_rank_radar_data`. Requires `asin`, `numberOfKeywords` (min 1), and a `nicheId` from `list_niches`.\",\n inputSchema,\n // destructiveHint is technically \"additive\" here (this creates a new Rank Radar, it\n // doesn't destroy data), but we flag it destructive so clients prompt before the\n // irreversible token spend. The confirm gate enforces this server-side regardless.\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n `Creating this Rank Radar consumes Search Term tokens — one per keyword tracked (${args.numberOfKeywords} requested).`,\n );\n if (pending) return pending;\n\n return await httpPost<CreateRankRadarResult>(\n { config: ctx.config, toolName: \"create_rank_radar\" },\n \"/v1/niches/rank-radars\",\n {\n asin: args.asin,\n numberOfKeywords: args.numberOfKeywords,\n nicheId: args.nicheId,\n },\n );\n },\n};\n","/**\n * Shared confirm gate for token-spending write tools (create_niche_dive,\n * create_rank_radar).\n *\n * Creating a Dive or Rank Radar consumes billable tokens irreversibly, so these\n * tools require an explicit `confirm: true` before they hit the API. When the flag\n * is missing, requireConfirmation returns a structured (non-error) payload telling\n * the model to confirm the token spend with the user and re-call with `confirm: true`;\n * the handler returns that payload early WITHOUT calling the API.\n *\n * The user can opt out of the gate entirely by setting DATADIVE_AUTO_CONFIRM_WRITES\n * (see config.ts) — a persistent \"don't ask me again\".\n */\n\nimport type { HandlerContext } from \"./types.js\";\n\nexport interface ConfirmationRequired {\n status: \"confirmation_required\";\n message: string;\n /** Human-readable note on what this action will cost in tokens. */\n costNote: string;\n}\n\n/**\n * Returns a ConfirmationRequired payload if the action is not yet confirmed, or null\n * if the handler should proceed (confirm:true was passed, or the opt-out is set).\n */\nexport function requireConfirmation(\n confirm: boolean | undefined,\n ctx: HandlerContext,\n costNote: string,\n): ConfirmationRequired | null {\n if (confirm === true || ctx.config.autoConfirmWrites) return null;\n return {\n status: \"confirmation_required\",\n message:\n \"This action spends billable tokens and cannot be undone. Confirm the cost with \" +\n \"the user, then call again with `confirm: true` to proceed.\",\n costNote,\n };\n}\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport { SUPPORTED_MARKETPLACES, type CreateNicheDiveResult } from \"../types/api.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .describe(\n \"Amazon marketplace of the seed ASIN. Note these are full domain suffixes: com, ca, \" +\n \"co.uk, com.mx, in, fr, de, es, it, co.jp (e.g. UK is `co.uk`, not `uk`).\",\n ),\n asin: z\n .string()\n .min(1)\n .describe(\"The seed ASIN to start the dive from (e.g. B08N5WRWNW). Competitors are built around it.\"),\n numberOfCompetitors: z\n .number()\n .int()\n .min(2)\n .describe(\"How many competitors to analyze (minimum 2). More competitors = deeper niche but more dive tokens.\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\"Must be true to proceed — creating a dive spends dive tokens. Confirm the cost with the user first.\"),\n};\n\nexport const createNicheDiveTool: ToolDefinition<typeof inputSchema> = {\n name: \"create_niche_dive\",\n title: \"Create a Niche Dive\",\n description:\n \"Use this to start new niche research from a seed ASIN. ⚠️ Spends dive tokens (cost scales with \" +\n \"`numberOfCompetitors`) and cannot be undone — set `confirm: true` only after the user approves the cost. \" +\n \"Not safe to retry: each call spends tokens again and starts a separate dive — if a call errors or times \" +\n \"out, poll `get_dive_status` (or check `list_niches`) instead of re-calling. \" +\n \"The dive runs asynchronously: this returns immediately with a `diveId` and an `estimatedCompletionDate`. \" +\n \"Poll `get_dive_status` with that `diveId` until it reports `success` (which carries the new `nicheId` for \" +\n \"use with `list_niches`, `get_niche_keywords`, etc.). Requires `marketplace`, `asin`, and `numberOfCompetitors` (min 2).\",\n inputSchema,\n // destructiveHint is technically \"additive\" here (a dive creates a new niche, it\n // doesn't destroy data), but we flag it destructive so clients prompt before the\n // irreversible token spend. The confirm gate enforces this server-side regardless.\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n `Creating this dive consumes dive tokens — one batch per competitor analyzed (${args.numberOfCompetitors} requested).`,\n );\n if (pending) return pending;\n\n return await httpPost<CreateNicheDiveResult>(\n { config: ctx.config, toolName: \"create_niche_dive\" },\n \"/v1/niches/dives\",\n {\n marketplace: args.marketplace,\n asin: args.asin,\n numberOfCompetitors: args.numberOfCompetitors,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport { ApiError } from \"../http/errors.js\";\nimport type { RediveNicheResult } from \"../types/api.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The niche to re-dive. Get one from `list_niches`.\"),\n mode: z\n .enum([\"same_competitors\", \"discover\"])\n .describe(\n \"How to pick the competitors for the refreshed dive. `same_competitors` re-runs the niche's \" +\n \"current competitor set and takes no other argument — use it to refresh stale data. `discover` \" +\n \"searches for a fresh competitor set, which is what you want when the niche has changed.\",\n ),\n numberOfCompetitors: z\n .number()\n .int()\n .min(2)\n .optional()\n .describe(\n \"Total ASINs the refreshed dive should contain, including `lockedAsins`. Required in `discover` \" +\n \"mode and rejected in `same_competitors` mode. Dive tokens are spent per ASIN.\",\n ),\n heroAsin: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"`discover` mode only. Seed product the competitor discovery starts from. Defaults to the niche's \" +\n \"highest-selling competitor, preferring one the user's own connected seller account owns.\",\n ),\n lockedAsins: z\n .array(z.string().min(1))\n .optional()\n .describe(\n \"`discover` mode only. ASINs that must stay in the competitor set regardless of what discovery \" +\n \"finds. Cannot exceed `numberOfCompetitors`, and cannot overlap `excludedAsins`.\",\n ),\n excludedAsins: z\n .array(z.string().min(1))\n .optional()\n .describe(\"`discover` mode only. ASINs discovery must never select.\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\n \"Must be true to proceed — a re-dive spends dive tokens. Confirm the cost with the user first.\",\n ),\n};\n\nexport const rediveNicheTool: ToolDefinition<typeof inputSchema> = {\n name: \"redive_niche\",\n title: \"Re-dive an Existing Niche\",\n description:\n \"Use this to refresh an existing niche's research with current Amazon data, instead of creating a new \" +\n \"niche with `create_niche_dive`. ⚠️ Spends dive tokens (one batch per ASIN dived) and cannot be undone — \" +\n \"set `confirm: true` only after the user approves the cost. Not safe to retry: each call spends tokens \" +\n \"again and starts a separate re-dive — if a call errors or times out, poll `get_dive_status` instead of \" +\n \"re-calling. Two modes: `same_competitors` re-dives the \" +\n \"niche's current competitor set (no other argument needed) and `discover` finds a fresh set, sized by \" +\n \"`numberOfCompetitors` and steerable with `heroAsin` / `lockedAsins` / `excludedAsins`. Runs \" +\n \"asynchronously: returns a `diveId` and an `estimatedCompletionDate` — poll `get_dive_status` with that \" +\n \"`diveId` until it reports `success`. The niche keeps its `nicheId`, so existing rank radars and reports \" +\n \"follow the refreshed data.\",\n inputSchema,\n // Same reasoning as create_niche_dive: a re-dive is additive rather than destructive, but the\n // irreversible token spend is what clients need to prompt on. The confirm gate enforces it regardless.\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n // `numberOfCompetitors` cannot be required in the schema (same_competitors mode forbids it), so\n // check it here — before the confirm gate, because a gate that cannot state the cost is worse\n // than no gate. Everything else is left to the API, which names the offending field.\n if (args.mode === \"discover\" && args.numberOfCompetitors === undefined) {\n throw new ApiError(\n \"bad_request\",\n 400,\n \"numberOfCompetitors is required in `discover` mode — it is how many ASINs the refreshed dive \" +\n \"will contain, and therefore what it costs. Ask the user how many competitors they want \" +\n '(minimum 2), or use mode \"same_competitors\" to re-dive the niche\\'s current set.',\n );\n }\n\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n args.mode === \"discover\"\n ? `Re-diving this niche consumes dive tokens — one batch per ASIN analyzed (${args.numberOfCompetitors} requested).`\n : \"Re-diving this niche consumes dive tokens — one batch per competitor currently in the niche. \" +\n \"Use `get_niche_competitors` first if the user needs the exact count.\",\n );\n if (pending) return pending;\n\n // Mode-specific fields are forwarded as given rather than filtered by mode: the API rejects a\n // field that does not belong to the requested mode with a message naming it, which is a clearer\n // correction for the model than silently dropping what it asked for. `undefined` fields are\n // dropped by JSON.stringify, so an unused optional never reaches the wire.\n return await httpPost<RediveNicheResult>(\n { config: ctx.config, toolName: \"redive_niche\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/redive`,\n {\n mode: args.mode,\n numberOfCompetitors: args.numberOfCompetitors,\n heroAsin: args.heroAsin,\n lockedAsins: args.lockedAsins,\n excludedAsins: args.excludedAsins,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { DiveStatus } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n diveId: z\n .string()\n .min(1)\n .describe(\"The dive identifier returned by `create_niche_dive` or `redive_niche`.\"),\n};\n\nexport const getDiveStatusTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_dive_status\",\n title: \"Get Niche Dive Status\",\n description:\n \"Use this to poll a niche dive started with `create_niche_dive` or `redive_niche` until it finishes. \" +\n \"Returns one of three shapes keyed by `status`: `in_progress` (with `estimatedCompletionDate`), \" +\n \"`success` (with the `nicheId` plus `tokensUsed`/`tokensLeft`), or `error` (with an `error` message). \" +\n \"On `success`, use the `nicheId` with `list_niches`, `get_niche_keywords`, `get_niche_competitors`, etc. \" +\n \"(for a re-dive it is the same niche you passed in, now carrying refreshed data). \" +\n \"Re-poll periodically — dives can take minutes; the `estimatedCompletionDate` hints at when to check.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<DiveStatus>(\n { config: ctx.config, toolName: \"get_dive_status\" },\n `/v1/niches/dives/${encodeURIComponent(args.diveId)}`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpDelete } from \"../http/client.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The niche to delete. Get one from `list_niches`.\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\n \"Must be true to proceed — deleting a niche destroys its keywords, competitors and dive history \" +\n \"permanently. Confirm with the user first.\",\n ),\n};\n\nexport const deleteNicheTool: ToolDefinition<typeof inputSchema> = {\n name: \"delete_niche\",\n title: \"Delete a Niche\",\n description:\n \"Use this to permanently remove a niche the user no longer needs. ⚠️ Deletes the niche and everything \" +\n \"attached to it — keywords, competitors, all dives and diveboxes — and cannot be undone; the dive tokens \" +\n \"already spent on it are NOT refunded. Set `confirm: true` only after the user approves. A niche used by \" +\n \"any Rank Radar cannot be deleted: the call fails with a conflict, so delete or archive those rank radars \" +\n \"first (`list_rank_radars` with the `nicheId` filter shows them).\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n \"Deleting this niche removes its keywords, competitors and all dive history permanently. \" +\n \"The dive tokens already spent on it are not refunded.\",\n );\n if (pending) return pending;\n\n // The endpoint answers 204 No Content, so there is no payload to return. Hand the\n // model an explicit result instead of `null`, which reads as a failed call.\n await httpDelete<null>(\n { config: ctx.config, toolName: \"delete_niche\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}`,\n );\n return { status: \"deleted\", nicheId: args.nicheId };\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n rankRadarId: z.string().uuid().describe(\"The Rank Radar UUID to pause (from `list_rank_radars`).\"),\n};\n\nexport const pauseRankRadarTool: ToolDefinition<typeof inputSchema> = {\n name: \"pause_rank_radar\",\n title: \"Pause a Rank Radar\",\n description:\n \"Use this to pause tracking on a Rank Radar without losing anything — the action the DataDive API calls \" +\n \"'archiving', so when a user asks to archive a Rank Radar, this is the tool they mean. Tracking stops for \" +\n \"all of its keywords and the Daily Tracked Keywords quota they held is freed for other Rank Radars, while \" +\n \"the Rank Radar and its ranking history are kept. Fully reversible with `resume_rank_radar`, so it needs \" +\n \"no `confirm` — prefer it over `delete_rank_radar` whenever the user may want the data back. Paused Rank \" +\n \"Radars show up in `list_rank_radars` under `status: PAUSED` — not `ARCHIVED`, which covers Rank Radars \" +\n \"removed with `delete_rank_radar`. Safe to repeat: pausing an already paused Rank Radar succeeds and \" +\n \"changes nothing.\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n // 204 No Content — return an explicit result rather than the `null` body.\n await httpPost<null>(\n { config: ctx.config, toolName: \"pause_rank_radar\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}/archive`,\n {},\n );\n return { status: \"paused\", rankRadarId: args.rankRadarId };\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n rankRadarId: z.string().uuid().describe(\"The Rank Radar UUID to resume (from `list_rank_radars`).\"),\n};\n\nexport const resumeRankRadarTool: ToolDefinition<typeof inputSchema> = {\n name: \"resume_rank_radar\",\n title: \"Resume a Paused Rank Radar\",\n description:\n \"Use this to restart tracking on a Rank Radar that was paused with `pause_rank_radar` (shown by \" +\n \"`list_rank_radars` under `status: PAUSED`; a deleted, `ARCHIVED` one cannot be resumed). Keywords are \" +\n \"resumed as far as the available Daily Tracked Keywords quota allows, most relevant first — so with a \" +\n \"tight quota only part of the original keyword set comes back; check `get_quota` first if that matters. \" +\n \"Reversible with `pause_rank_radar`, so it needs no `confirm`. Fails with a bad-request error when \" +\n \"there is no quota left at all. Safe to repeat: resuming an already active Rank Radar changes nothing.\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n await httpPost<null>(\n { config: ctx.config, toolName: \"resume_rank_radar\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}/resume`,\n {},\n );\n return { status: \"resumed\", rankRadarId: args.rankRadarId };\n },\n};\n","import { z } from \"zod\";\nimport { httpDelete } from \"../http/client.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n rankRadarId: z.string().uuid().describe(\"The Rank Radar UUID to delete (from `list_rank_radars`).\"),\n confirm: z\n .boolean()\n .optional()\n .describe(\n \"Must be true to proceed — deleting a Rank Radar destroys its ranking history permanently. \" +\n \"Confirm with the user first.\",\n ),\n};\n\nexport const deleteRankRadarTool: ToolDefinition<typeof inputSchema> = {\n name: \"delete_rank_radar\",\n title: \"Delete a Rank Radar\",\n description:\n \"Use this to permanently remove a Rank Radar and all of its keyword ranking history. ⚠️ Cannot be undone \" +\n \"— there is no restore endpoint; set `confirm: true` only after the user approves. It does free the Daily \" +\n \"Tracked Keywords quota those keywords held. Deleted Rank Radars are what `list_rank_radars` returns for \" +\n \"`status: ARCHIVED`. If the user only wants to stop tracking for a while and keep the history — or asks \" +\n \"to 'archive' the Rank Radar, which is what the DataDive API calls pausing — use `pause_rank_radar` \" +\n \"instead: it frees the same quota and is reversible with `resume_rank_radar`.\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n \"Deleting this Rank Radar removes its keyword ranking history permanently. It frees the Daily \" +\n \"Tracked Keywords quota, but the history cannot be recovered — `pause_rank_radar` frees the \" +\n \"same quota and is reversible.\",\n );\n if (pending) return pending;\n\n await httpDelete<null>(\n { config: ctx.config, toolName: \"delete_rank_radar\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}`,\n );\n return { status: \"deleted\", rankRadarId: args.rankRadarId };\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport { ADD_SEARCH_TERMS_MAX, type AddSearchTermsResult } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n rankRadarId: z\n .string()\n .uuid()\n .describe(\"The Rank Radar UUID to add the search terms to (from `list_rank_radars`).\"),\n searchTerms: z\n .array(z.string().min(1))\n .min(1)\n .max(ADD_SEARCH_TERMS_MAX)\n .describe(\n `The keywords to start tracking, 1–${ADD_SEARCH_TERMS_MAX} per call. Must be unique. Amazon-ignored ` +\n \"special characters (@, #, _, parentheses, …) are rejected.\",\n ),\n};\n\nexport const addRankRadarSearchTermsTool: ToolDefinition<typeof inputSchema> = {\n name: \"add_rank_radar_search_terms\",\n title: \"Add Search Terms to a Rank Radar\",\n description:\n \"Use this to start tracking extra keywords on an existing Rank Radar, instead of creating a new one with \" +\n \"`create_rank_radar`. New keywords are added to the niche and tracked from now on; any matching keyword \" +\n \"that was previously paused is resumed. Each newly tracked keyword takes one Daily Tracked Keywords slot, \" +\n \"and the call fails if the quota is exhausted — check `get_quota` first. Reversible with \" +\n \"`pause_rank_radar_search_terms`, which frees the slots again, so it needs no `confirm`. Returns \" +\n \"`originBreakdown` (where each submitted term ended up) and `keywordToRankRadarKeywordIdMap`, whose ids \" +\n \"are what the pause/resume search-term tools take.\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n return await httpPost<AddSearchTermsResult>(\n { config: ctx.config, toolName: \"add_rank_radar_search_terms\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}/search-terms`,\n { searchTerms: args.searchTerms },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n rankRadarId: z.string().uuid().describe(\"The Rank Radar UUID the search terms belong to.\"),\n rankRadarKeywordIds: z\n .array(z.string().uuid())\n .min(1)\n .describe(\n \"The Rank Radar keyword ids to pause. These are the `id` values from `get_rank_radar_data`, or the \" +\n \"values of `keywordToRankRadarKeywordIdMap` returned by `add_rank_radar_search_terms` — not the \" +\n \"keyword text.\",\n ),\n};\n\nexport const pauseRankRadarSearchTermsTool: ToolDefinition<typeof inputSchema> = {\n name: \"pause_rank_radar_search_terms\",\n title: \"Pause Search Terms of a Rank Radar\",\n description:\n \"Use this to stop tracking individual keywords on a Rank Radar while keeping the Rank Radar itself \" +\n \"active — the way to trim a keyword set the user finds too broad. The DataDive API calls this \" +\n \"'archiving' the search terms; nothing is lost. The paused keywords keep their history and their Daily \" +\n \"Tracked Keywords slots are freed for other keywords. Reversible with `resume_rank_radar_search_terms`, \" +\n \"so it needs no `confirm`. Takes keyword ids, not keyword text: get them from `get_rank_radar_data`. \" +\n \"Safe to repeat: already-paused terms are left unchanged.\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n // 204 No Content — return an explicit result rather than the `null` body.\n await httpPost<null>(\n { config: ctx.config, toolName: \"pause_rank_radar_search_terms\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}/search-terms/archive`,\n { rankRadarKeywordIds: args.rankRadarKeywordIds },\n );\n return {\n status: \"paused\",\n rankRadarId: args.rankRadarId,\n rankRadarKeywordIds: args.rankRadarKeywordIds,\n };\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n rankRadarId: z.string().uuid().describe(\"The Rank Radar UUID the search terms belong to.\"),\n rankRadarKeywordIds: z\n .array(z.string().uuid())\n .min(1)\n .describe(\n \"The Rank Radar keyword ids to resume. These are the `id` values from `get_rank_radar_data`, or the \" +\n \"values of `keywordToRankRadarKeywordIdMap` returned by `add_rank_radar_search_terms` — not the \" +\n \"keyword text.\",\n ),\n};\n\nexport const resumeRankRadarSearchTermsTool: ToolDefinition<typeof inputSchema> = {\n name: \"resume_rank_radar_search_terms\",\n title: \"Resume Paused Search Terms of a Rank Radar\",\n description:\n \"Use this to start tracking keywords again that were paused with `pause_rank_radar_search_terms`. Each \" +\n \"resumed keyword takes back one Daily Tracked Keywords slot, and the call fails if the quota is \" +\n \"exhausted — check `get_quota` first. Reversible with `pause_rank_radar_search_terms`, so it needs no \" +\n \"`confirm`. Takes keyword ids, not keyword text: get them from `get_rank_radar_data`. Safe to repeat: \" +\n \"already-active terms are left unchanged.\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n await httpPost<null>(\n { config: ctx.config, toolName: \"resume_rank_radar_search_terms\" },\n `/v1/niches/rank-radars/${encodeURIComponent(args.rankRadarId)}/search-terms/resume`,\n { rankRadarKeywordIds: args.rankRadarKeywordIds },\n );\n return {\n status: \"resumed\",\n rankRadarId: args.rankRadarId,\n rankRadarKeywordIds: args.rankRadarKeywordIds,\n };\n },\n};\n","import { z } from \"zod\";\nimport { httpPost } from \"../http/client.js\";\nimport { LISTING_COPY_STRATEGIES, type CreateListingCopyResult } from \"../types/api.js\";\nimport { requireConfirmation } from \"./confirm.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z\n .string()\n .min(1)\n .describe(\"The niche whose keyword data the copy is written from. Get one from `list_niches`.\"),\n strategy: z\n .enum(LISTING_COPY_STRATEGIES)\n .describe(\n \"Which generation strategy to use. `cosmo` targets Amazon's COSMO relevance model, `ranking-juice` \" +\n \"optimises for DataDive's ranking-juice score, `nlp` writes for keyword coverage, and `cosmo-rufus` \" +\n \"targets COSMO plus the Rufus shopping assistant. Ask the user if they have no preference.\",\n ),\n currentListing: z\n .record(z.union([z.string(), z.array(z.string())]))\n .describe(\n \"The seller's existing listing to rewrite, as an object — e.g. \" +\n '`{ \"title\": \"...\", \"description\": \"...\", \"bullets\": [\"...\", \"...\"] }`. Pass what the user has; ' +\n \"it is the starting point the generated copy builds on.\",\n ),\n confirm: z\n .boolean()\n .optional()\n .describe(\n \"Must be true to proceed — each generation spends one AI Copywriter prompt from the quota. \" +\n \"Confirm the cost with the user first.\",\n ),\n};\n\nexport const generateListingCopyTool: ToolDefinition<typeof inputSchema> = {\n name: \"generate_listing_copy\",\n title: \"Generate Listing Copy for a Niche\",\n description:\n \"Use this to draft an optimised Amazon listing — title, bullets and description — from a niche's keyword \" +\n \"research and the seller's current listing. It writes text only: nothing is published to Amazon, and the \" +\n \"user still has to paste the result into Seller Central. ⚠️ Spends one AI Copywriter prompt from the \" +\n \"quota per call and cannot be undone — set `confirm: true` only after the user approves. Not safe to \" +\n \"retry: each call spends another prompt, so if a call errors or times out, poll \" +\n \"`get_listing_copy_generation_status` instead of re-calling. Runs asynchronously: returns a \" +\n \"`generationId` — poll `get_listing_copy_generation_status` with it until the status is `complete`.\",\n inputSchema,\n annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false },\n handler: async (args, ctx) => {\n const pending = requireConfirmation(\n args.confirm,\n ctx,\n \"Generating listing copy consumes one AI Copywriter prompt from the organization's quota. \" +\n \"Check the remaining balance with `get_quota` if the user needs it.\",\n );\n if (pending) return pending;\n\n // The API field is still named `prompt`/`listingToInclude`; the tool surface uses\n // `strategy`/`currentListing` because those read as what they are to a model.\n return await httpPost<CreateListingCopyResult>(\n { config: ctx.config, toolName: \"generate_listing_copy\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/ai-copywriter`,\n { prompt: args.strategy, listingToInclude: args.currentListing },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { ListingCopyGenerationStatus } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n nicheId: z.string().min(1).describe(\"The niche the generation was started for.\"),\n generationId: z.string().min(1).describe(\"The `generationId` returned by `generate_listing_copy`.\"),\n};\n\nexport const getListingCopyGenerationStatusTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_listing_copy_generation_status\",\n title: \"Get Listing Copy Generation Status\",\n description:\n \"Use this to poll a listing-copy draft started with `generate_listing_copy` until it finishes. Returns \" +\n \"one of three shapes keyed by `status`: `generating` (still running — poll again in a few seconds), \" +\n \"`complete` (with `result`, carrying the generated `title`, `bullets`, `description`, `itemHighlights` \" +\n \"and its `rankingJuice` score), or `failed` (with an `error` message). Polling is free — it does not \" +\n \"spend another AI Copywriter prompt, so always poll rather than re-calling `generate_listing_copy`.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<ListingCopyGenerationStatus>(\n { config: ctx.config, toolName: \"get_listing_copy_generation_status\" },\n `/v1/niches/${encodeURIComponent(args.nicheId)}/ai-copywriter/${encodeURIComponent(args.generationId)}`,\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport type { SellerProfileList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n\nexport const listSellerProfilesTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_seller_profiles\",\n title: \"List Connected Amazon Seller Profiles\",\n description:\n \"Use this when the user asks which Amazon seller accounts are connected, or as the discovery \" +\n \"step to find the `sellerId` and `marketplace` required by `get_asin_inventory_distribution`, \" +\n \"`get_seller_catalog`, `get_seller_listing_changes`, and the alert tools. \" +\n \"Returns a paginated list of the organization's connected seller profiles — each item has \" +\n \"sellerId, sellerName, marketplace (e.g. \\\"com\\\", \\\"co.uk\\\"), hasAdApi (whether Advertising API \" +\n \"credentials are connected), and createdAt — plus pagination metadata (currentPage, pageSize, \" +\n \"total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<SellerProfileList>(\n { config: ctx.config, toolName: \"list_seller_profiles\" },\n \"/v1/seller_profiles\",\n {\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport { CATALOG_STATUSES, type SellerCatalogList } from \"../types/api.js\";\nimport { sellerMarketplacePath, sellerScopedInputSchema } from \"./seller-scoped.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n ...sellerScopedInputSchema,\n search: z\n .string()\n .min(1)\n .optional()\n .describe(\"Case-insensitive partial match on product title or brand.\"),\n brand: z.string().min(1).optional().describe(\"Case-insensitive partial match on brand name.\"),\n status: z\n .enum(CATALOG_STATUSES)\n .optional()\n .describe('Listing status filter. \"Active\" (default) or \"all\".'),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n\nexport const getSellerCatalogTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_seller_catalog\",\n title: \"List Catalog ASINs for a Seller\",\n description:\n \"Use this when the user wants to browse or search a seller's own Amazon catalog — the products \" +\n \"they sell on a given marketplace. Requires a `sellerId` + `marketplace` (use `list_seller_profiles` \" +\n \"to discover them). Filter by `search` (title/brand), `brand`, and `status` (Active by default, or \" +\n \"all). Returns a paginated list where each item has asin, title, parentAsin, brand, status, imageUrl, \" +\n \"and hasVariations, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<SellerCatalogList>(\n { config: ctx.config, toolName: \"get_seller_catalog\" },\n sellerMarketplacePath(args.sellerId, args.marketplace, \"catalog\"),\n {\n search: args.search,\n brand: args.brand,\n status: args.status,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","/**\n * Shared input fields + path builder for the seller+marketplace-scoped tools\n * (/v1/seller_profiles/:sellerId/marketplaces/:marketplace/*). Defined once here\n * and spread into each tool's inputSchema, mirroring alert-query.ts.\n */\n\nimport { z } from \"zod\";\nimport { SUPPORTED_MARKETPLACES } from \"../types/api.js\";\n\nexport const sellerScopedInputSchema = {\n sellerId: z\n .string()\n .min(1)\n .describe(\n \"Amazon seller account ID. Get it from `list_seller_profiles`, or find it on the Connections page at https://2.datadive.tools.\",\n ),\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .describe('Amazon marketplace code, e.g. \"com\" for amazon.com or \"co.uk\" for amazon.co.uk.'),\n};\n\n/** Builds a `/v1/seller_profiles/:sellerId/marketplaces/:marketplace/<suffix>` path with both params URL-encoded. */\nexport function sellerMarketplacePath(sellerId: string, marketplace: string, suffix: string): string {\n return `/v1/seller_profiles/${encodeURIComponent(sellerId)}/marketplaces/${encodeURIComponent(\n marketplace,\n )}/${suffix}`;\n}\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport {\n ISO_8601,\n LISTING_CHANGE_SORT_BY,\n LISTING_CHANGE_TYPES,\n SORT_ORDER,\n type ListingChangeList,\n} from \"../types/api.js\";\nimport { sellerMarketplacePath, sellerScopedInputSchema } from \"./seller-scoped.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n ...sellerScopedInputSchema,\n types: z\n .array(z.enum(LISTING_CHANGE_TYPES))\n .min(1)\n .optional()\n .describe('Filter to specific change types. Omit for all. Any of \"Price\", \"Content\", \"Image\".'),\n asin: z\n .string()\n .min(1)\n .optional()\n .describe(\"Filter to an exact ASIN or parent ASIN.\"),\n brand: z.string().min(1).optional().describe(\"Case-insensitive partial match on brand name.\"),\n search: z\n .string()\n .min(1)\n .optional()\n .describe(\"Case-insensitive partial match on product title or brand.\"),\n startDate: z\n .string()\n .regex(ISO_8601, \"startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only changes detected on or after this time.\"),\n endDate: z\n .string()\n .regex(ISO_8601, \"endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only changes detected on or before this time.\"),\n sortBy: z\n .enum(LISTING_CHANGE_SORT_BY)\n .optional()\n .describe('Sort field. \"date\" (default) or \"type\".'),\n sortOrder: z.enum(SORT_ORDER).optional().describe('Sort direction. \"DESC\" (default) or \"ASC\".'),\n includeCorrelations: z\n .boolean()\n .optional()\n .describe(\n \"When true, include the ranking/conversion `correlation` object per change (CVR and search-term \" +\n \"movement before vs after). Defaults to false.\",\n ),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n\nexport const getSellerListingChangesTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_seller_listing_changes\",\n title: \"List Listing Changes for a Seller\",\n description:\n \"Use this when the user asks what changed on their Amazon listings — price, content (title/bullets/\" +\n \"description), or image edits — for a seller account. Requires a `sellerId` + `marketplace` (use \" +\n \"`list_seller_profiles` to discover them). Filter by `types`, `asin`/parent ASIN, `brand`, `search`, \" +\n \"and a `startDate`/`endDate` range; sort with `sortBy`/`sortOrder`. Set `includeCorrelations: true` to \" +\n \"attach the ranking/conversion impact per change. Returns a paginated list where each item has asin, \" +\n \"title, imageUrl, date, type, contentType, description, previousValue, newValue, and (optionally) \" +\n \"correlation, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<ListingChangeList>(\n { config: ctx.config, toolName: \"get_seller_listing_changes\" },\n sellerMarketplacePath(args.sellerId, args.marketplace, \"listing-changes\"),\n {\n // The API accepts `types` as a comma-separated list.\n types: args.types?.join(\",\"),\n asin: args.asin,\n brand: args.brand,\n search: args.search,\n startDate: args.startDate,\n endDate: args.endDate,\n sortBy: args.sortBy,\n sortOrder: args.sortOrder,\n includeCorrelations: args.includeCorrelations,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport { SUPPORTED_MARKETPLACES, type InventoryByFcResponse } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n sellerId: z\n .string()\n .min(1)\n .describe(\n \"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.\",\n ),\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .describe('Amazon marketplace code, e.g. \"com\" for amazon.com or \"co.uk\" for amazon.co.uk.'),\n asin: z\n .string()\n .regex(/^[A-Z0-9]{10}$/, \"ASIN must be 10 uppercase alphanumeric characters (e.g. B08XYZ1234).\")\n .describe(\n \"Amazon Standard Identification Number (10 uppercase alphanumeric characters, e.g. B08XYZ1234).\",\n ),\n};\n\nexport const getAsinInventoryDistributionTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_asin_inventory_distribution\",\n title: \"Get Per-Fulfillment-Center Inventory Distribution for an ASIN\",\n description:\n \"Use this when the user asks about inventory levels, stock by fulfillment center, \" +\n \"or where their units are sitting across Amazon's fulfillment network for a specific ASIN. \" +\n \"Requires the Amazon sellerId (from `list_seller_profiles` or the user's DataDive Connections \" +\n \"page at https://2.datadive.tools), the marketplace code, and the ASIN. Returns totalSellableUnits \" +\n \"and a per-FC `distribution` array (fc, state, availableStock, availableStockPercentage). \" +\n \"`lastUpdatedAt` may be null when no successful inventory ingestion has occurred in the last 30 days.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<InventoryByFcResponse>(\n { config: ctx.config, toolName: \"get_asin_inventory_distribution\" },\n `/v1/sellers/${encodeURIComponent(args.sellerId)}/marketplaces/${encodeURIComponent(\n args.marketplace,\n )}/asins/${encodeURIComponent(args.asin)}/inventory`,\n );\n },\n};\n","/**\n * Shared input schema for the /v1/alerts/* list tools. Both alert endpoints\n * accept the same backend query DTO (ExternalAlertsQueryDto), so the input\n * contract is defined once here and imported by each tool.\n */\n\nimport { z } from \"zod\";\nimport { ALERT_STATUSES, ISO_8601, SUPPORTED_MARKETPLACES } from \"../types/api.js\";\n\nexport const alertsQueryInputSchema = {\n sellerId: z\n .string()\n .min(1)\n .max(50)\n .optional()\n .describe('Filter to a single connected Amazon seller account ID, e.g. \"A1B2C3D4E5\".'),\n marketplace: z\n .enum(SUPPORTED_MARKETPLACES)\n .optional()\n .describe('Filter to a single Amazon marketplace code, e.g. \"com\" for amazon.com or \"co.uk\" for amazon.co.uk.'),\n status: z\n .enum(ALERT_STATUSES)\n .optional()\n .describe(\n \"Lifecycle filter. `active` (default) returns unresolved alerts, `resolved` returns resolved alerts, \" +\n \"`all` returns both. Dismissed alerts are never returned.\",\n ),\n updatedSince: z\n .string()\n .regex(ISO_8601, \"updatedSince must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)\")\n .optional()\n .describe(\n \"ISO-8601 timestamp; return only alerts surfaced at or after this time (filters on `lastAlertedAt`). \" +\n \"Use it to incrementally sync since your last poll. Defaults to the last 30 days when omitted.\",\n ),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(\"Items per page (max 50). Defaults to 20.\"),\n};\n","import { httpGet } from \"../http/client.js\";\nimport type { IndexingIssueAlertList } from \"../types/api.js\";\nimport { alertsQueryInputSchema } from \"./alert-query.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nexport const listIndexingIssueAlertsTool: ToolDefinition<typeof alertsQueryInputSchema> = {\n name: \"list_indexing_issue_alerts\",\n title: \"List Indexing-Issue Alerts\",\n description:\n \"Use this to find products that may have lost search visibility. Retrieves a paginated list of \" +\n \"indexing-issue alerts across the user's connected Amazon seller accounts — an alert fires when one of \" +\n \"their ASINs is no longer indexed for its tracked keywords. By default returns active (unresolved) alerts \" +\n \"from the last 30 days; filter by `sellerId`, `marketplace`, `status`, or `updatedSince` (for incremental \" +\n \"polling). Each item includes id, asin, title, imageUrl, isParent, sellerId, marketplace, lastAlertedAt, \" +\n \"and resolvedAt. Supports pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema: alertsQueryInputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<IndexingIssueAlertList>(\n { config: ctx.config, toolName: \"list_indexing_issue_alerts\" },\n \"/v1/alerts/indexing-issues\",\n {\n sellerId: args.sellerId,\n marketplace: args.marketplace,\n status: args.status,\n updatedSince: args.updatedSince,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { httpGet } from \"../http/client.js\";\nimport type { BlindSpendAlertList } from \"../types/api.js\";\nimport { alertsQueryInputSchema } from \"./alert-query.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nexport const listBlindSpendAlertsTool: ToolDefinition<typeof alertsQueryInputSchema> = {\n name: \"list_blind_spend_alerts\",\n title: \"List Blind-Spend (Wasted-Spend) Alerts\",\n description:\n \"Use this to find wasted PPC ad spend. Retrieves a paginated list of blind-spend alerts across the \" +\n \"user's connected Amazon seller accounts — an alert flags ad spend on customer search terms that produced \" +\n \"little or no sales. By default returns active (unresolved) alerts from the last 30 days; filter by \" +\n \"`sellerId`, `marketplace`, `status`, or `updatedSince` (for incremental polling). Each item includes id, \" +\n \"asin, title, imageUrl, sellerId, marketplace, lastAlertedAt, resolvedAt, wastedSpend (total ad spend \" +\n \"across unresolved terms), totalKeywordCount, unresolvedKeywordCount, and searchTerms — the unresolved \" +\n \"wasted-spend search terms, each with term, spend, sales, clicks, cvr (0-1), and impressions. Supports \" +\n \"pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema: alertsQueryInputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<BlindSpendAlertList>(\n { config: ctx.config, toolName: \"list_blind_spend_alerts\" },\n \"/v1/alerts/blind-spend\",\n {\n sellerId: args.sellerId,\n marketplace: args.marketplace,\n status: args.status,\n updatedSince: args.updatedSince,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","import { httpGet } from \"../http/client.js\";\nimport type { Quota } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {};\n\nexport const getQuotaTool: ToolDefinition<typeof inputSchema> = {\n name: \"get_quota\",\n title: \"Get Quota Usage\",\n description:\n \"Use this when the user asks how much quota or how many tokens they have left, or before creating a \" +\n \"Niche Dive or Rank Radar to check remaining headroom. \" +\n \"Returns current quota usage and capacity for each billable feature — DIVED_ASINS (Dive tokens), \" +\n \"RANK_RADAR_KEYWORDS (tracked keywords), PRODUCT_BRIEF_ASINS, and AI_COPYWRITER_PROMPTS — each with \" +\n \"`used` and `capacity` (null when unlimited / not applicable), plus `nextRefreshDate` (ISO-8601 \" +\n \"timestamp of the next quota reset, or null). Takes no arguments.\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (_args, ctx) => {\n return await httpGet<Quota>({ config: ctx.config, toolName: \"get_quota\" }, \"/v1/quota\");\n },\n};\n","import { z } from \"zod\";\nimport { httpGet } from \"../http/client.js\";\nimport { BILLABLE_FEATURE_TYPES, ISO_8601, type UsageLogList } from \"../types/api.js\";\nimport type { ToolDefinition } from \"./types.js\";\n\nconst inputSchema = {\n type: z\n .enum(BILLABLE_FEATURE_TYPES)\n .optional()\n .describe(\"Filter to a single billable feature type. Omit to return all types.\"),\n search: z\n .string()\n .min(1)\n .optional()\n .describe(\"Case-insensitive partial match on the user's name or email.\"),\n startDate: z\n .string()\n .regex(ISO_8601, \"startDate must be an ISO-8601 date or timestamp (e.g. 2026-05-01 or 2026-05-01T00:00:00Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only usage logs recorded on or after this time.\"),\n endDate: z\n .string()\n .regex(ISO_8601, \"endDate must be an ISO-8601 date or timestamp (e.g. 2026-05-31 or 2026-05-31T23:59:59Z)\")\n .optional()\n .describe(\"ISO-8601 date/timestamp; return only usage logs recorded on or before this time.\"),\n currentPage: z.number().int().min(1).optional().describe(\"Page number, 1-indexed. Defaults to 1.\"),\n pageSize: z\n .number()\n .int()\n .min(1)\n .max(200)\n .optional()\n .describe(\"Items per page (max 200). Defaults to 50.\"),\n};\n\nexport const listUsageTool: ToolDefinition<typeof inputSchema> = {\n name: \"list_usage\",\n title: \"List Billable Usage Logs\",\n description:\n \"Use this when the user asks who consumed tokens, how their quota was spent, or wants an audit of \" +\n \"billable activity over a date range. \" +\n \"Retrieves a paginated list of billable feature usage logs for the organization — each entry is a \" +\n \"token-consumption event (a dive, rank-radar creation, AI copywriter prompt, etc.). Filter by `type` \" +\n \"(billable feature), `search` (user name/email), and `startDate`/`endDate`. Each item includes name, \" +\n \"email, qty (tokens consumed), type, action (specific operation), nicheId/nicheName, rankRadarId, and \" +\n \"date, plus pagination metadata (currentPage, pageSize, total, lastPage, hasNext, hasPrev).\",\n inputSchema,\n annotations: { readOnlyHint: true },\n handler: async (args, ctx) => {\n return await httpGet<UsageLogList>(\n { config: ctx.config, toolName: \"list_usage\" },\n \"/v1/usage\",\n {\n type: args.type,\n search: args.search,\n startDate: args.startDate,\n endDate: args.endDate,\n currentPage: args.currentPage,\n pageSize: args.pageSize,\n },\n );\n },\n};\n","/**\n * Tool registry. The order here is the order tools appear in `tools/list`\n * over the MCP transport, which biases LLM discovery — keep \"discovery\" tools\n * (the ones that return IDs needed by other tools) at the top.\n */\n\nimport { listNichesTool } from \"./list-niches.js\";\nimport { getNicheKeywordsTool } from \"./get-niche-keywords.js\";\nimport { getNicheRootsTool } from \"./get-niche-roots.js\";\nimport { getNicheCompetitorsTool } from \"./get-niche-competitors.js\";\nimport { getRankingJuiceTool } from \"./get-ranking-juice.js\";\nimport { listRankRadarsTool } from \"./list-rank-radars.js\";\nimport { getRankRadarDataTool } from \"./get-rank-radar-data.js\";\nimport { createRankRadarTool } from \"./create-rank-radar.js\";\nimport { createNicheDiveTool } from \"./create-niche-dive.js\";\nimport { rediveNicheTool } from \"./redive-niche.js\";\nimport { getDiveStatusTool } from \"./get-dive-status.js\";\nimport { deleteNicheTool } from \"./delete-niche.js\";\nimport { pauseRankRadarTool } from \"./pause-rank-radar.js\";\nimport { resumeRankRadarTool } from \"./resume-rank-radar.js\";\nimport { deleteRankRadarTool } from \"./delete-rank-radar.js\";\nimport { addRankRadarSearchTermsTool } from \"./add-rank-radar-search-terms.js\";\nimport { pauseRankRadarSearchTermsTool } from \"./pause-rank-radar-search-terms.js\";\nimport { resumeRankRadarSearchTermsTool } from \"./resume-rank-radar-search-terms.js\";\nimport { generateListingCopyTool } from \"./generate-listing-copy.js\";\nimport { getListingCopyGenerationStatusTool } from \"./get-listing-copy-generation-status.js\";\nimport { listSellerProfilesTool } from \"./list-seller-profiles.js\";\nimport { getSellerCatalogTool } from \"./get-seller-catalog.js\";\nimport { getSellerListingChangesTool } from \"./get-seller-listing-changes.js\";\nimport { getAsinInventoryDistributionTool } from \"./get-asin-inventory-distribution.js\";\nimport { listIndexingIssueAlertsTool } from \"./list-indexing-issue-alerts.js\";\nimport { listBlindSpendAlertsTool } from \"./list-blind-spend-alerts.js\";\nimport { getQuotaTool } from \"./get-quota.js\";\nimport { listUsageTool } from \"./list-usage.js\";\nimport type { AnyTool } from \"./types.js\";\n\n// Note on the cast: each ToolDefinition<S> has a handler typed to its specific\n// schema; the registry needs a uniform shape. The MCP SDK validates incoming\n// args against `inputSchema` before calling the handler, so the cast is safe.\nexport const allTools: ReadonlyArray<AnyTool> = [\n listNichesTool,\n getNicheKeywordsTool,\n getNicheRootsTool,\n getNicheCompetitorsTool,\n getRankingJuiceTool,\n listRankRadarsTool,\n getRankRadarDataTool,\n createRankRadarTool,\n createNicheDiveTool,\n rediveNicheTool,\n getDiveStatusTool,\n addRankRadarSearchTermsTool,\n pauseRankRadarSearchTermsTool,\n resumeRankRadarSearchTermsTool,\n pauseRankRadarTool,\n resumeRankRadarTool,\n deleteRankRadarTool,\n deleteNicheTool,\n generateListingCopyTool,\n getListingCopyGenerationStatusTool,\n listSellerProfilesTool,\n getSellerCatalogTool,\n getSellerListingChangesTool,\n getAsinInventoryDistributionTool,\n listIndexingIssueAlertsTool,\n listBlindSpendAlertsTool,\n getQuotaTool,\n listUsageTool,\n] as unknown as ReadonlyArray<AnyTool>;\n\nexport type { ToolDefinition, AnyTool } from \"./types.js\";\n"],"mappings":"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","names":["KEY_HELP_URL","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","z","z","inputSchema","z","z","inputSchema","z","z","inputSchema","z","z","z","inputSchema","z","inputSchema","z"]}
|