@cite42/mcp 0.1.17 → 0.3.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 CHANGED
@@ -6,7 +6,7 @@ Once connected, Claude Code, Claude Desktop, Codex CLI, or Cursor can:
6
6
 
7
7
  - **Query AI models** (ChatGPT, Claude, Perplexity, Gemini, Google AI Overviews) and see each model's answer and the sources it cites
8
8
  - **Track brand visibility** in AI answers: rankings, head-to-head comparisons, and sentiment
9
- - **Schedule daily or weekly trackers** for rankings, comparisons, citations, and sentiment, then ask your agent for stored run history
9
+ - **Schedule daily or weekly trackers**: one measurement — rankings, comparisons, citations, or sentiment — over a list of prompts, with email when something changes, then ask your agent for stored run history
10
10
  - **Pull SEO data**: keyword search volume, CPC, competition, and Google Trends
11
11
  - **Mine Reddit and YouTube** for audience questions, pain points, and rising content
12
12
  - **Run full research workflows**: content gaps, topic demand, AI prompt maps, competitor analysis, and content briefs in a single call
@@ -105,7 +105,7 @@ After connecting or restarting, just ask in plain language. Your agent picks the
105
105
  - *"Get search volume, CPC, and related keywords for 'ai seo tools'."*
106
106
  - *"What are people on Reddit complaining about regarding email marketing tools?"*
107
107
  - *"Run a content brief for the topic 'ai website builder'."*
108
- - *"Create a weekly Cite42 tracker for our rankings, citations, and sentiment. Show me the estimated credits per run before activating it."*
108
+ - *"Map the prompts worth tracking for our category, then create a weekly Cite42 rankings tracker for the best five. Show me the estimated credits per run before activating it."*
109
109
 
110
110
  ### Example: what a session looks like
111
111
 
@@ -115,16 +115,16 @@ After connecting or restarting, just ask in plain language. Your agent picks the
115
115
 
116
116
  **Your agent calls** `cite42_citations` **and answers something like:**
117
117
 
118
- > Across ChatGPT, Claude, Perplexity, and Gemini, the most-cited sources are:
118
+ > Across ChatGPT, Perplexity, and Gemini, the most-cited sources are:
119
119
  >
120
- > 1. **freshbooks.com** (cited by 4/5 models)
121
- > 2. **wave.com** (cited by 3/5 models)
122
- > 3. **zoho.com/invoice** (cited by 3/5 models)
123
- > 4. **bonsai.com** (cited by 2/5 models)
120
+ > 1. **freshbooks.com** (cited by 3/3 models)
121
+ > 2. **wave.com** (cited by 2/3 models)
122
+ > 3. **zoho.com/invoice** (cited by 2/3 models)
123
+ > 4. **bonsai.com** (cited by 1/3 models)
124
124
  >
125
125
  > Your domain wasn't cited by any model. FreshBooks is cited mainly via comparison/"best of" listicles. A citation-gap analysis (`cite42_find_ai_citation_gaps`) can show which queries you're missing from.
126
126
 
127
- The exact brands and sources change by query and model. A good result should show the tool used, model/source evidence, and a short summary you can act on.
127
+ That is the three default surfaces; ask for `fullSweep` to add Claude and Google AI Overviews. The exact brands and sources change by query and model. A good result should show the tool used, model/source evidence, and a short summary you can act on.
128
128
 
129
129
  ## Tools
130
130
 
@@ -155,19 +155,19 @@ Cite42 exposes 26 MCP tools: 3 free account tools, 9 primitive data tools, 6 res
155
155
 
156
156
  - `cite42_find_content_opportunities`: find content gaps across AI search, keywords, trends, Reddit, YouTube, and citations.
157
157
  - `cite42_analyze_topic_demand`: combine keyword data, trends, AI answers, and audience conversations for a topic.
158
- - `cite42_map_ai_prompts`: discover and cluster buyer prompts worth tracking in AI search.
158
+ - `cite42_map_ai_prompts`: discover and cluster buyer prompts worth tracking in AI search. Its prompts are what `cite42_tracker_create` takes, so the two chain naturally.
159
159
  - `cite42_analyze_competitor_content`: analyze AI rankings, comparisons, citations, and topic coverage for competitors.
160
160
  - `cite42_find_ai_citation_gaps`: find queries where AI answers cite competitors but not your brand, domain, or URL.
161
161
  - `cite42_generate_content_brief`: collect AI answers, citations, keyword demand, and social signals into brief-ready data.
162
162
 
163
163
  ### Trackers
164
164
 
165
- - `cite42_tracker_create`: create a draft daily or weekly tracker from saved rankings, compare, citations, and sentiment inputs. Creating a draft is free and does not schedule calls.
166
- - `cite42_tracker_update`: replace a tracker's name, cadence, and saved primitive inputs. Updating configuration is free.
167
- - `cite42_tracker_activate`: activate or resume recurring runs. You must pass the exact `estimatedCostMicroPerRun` from the latest tracker record as `maxCostMicroPerRun`; activation authorizes billed scheduled runs until the tracker is paused.
165
+ - `cite42_tracker_create`: create a draft daily or weekly tracker — one measurement applied to a list of prompts. No prompt list yet? Run `cite42_map_ai_prompts` first. Creating a draft is free and does not schedule calls.
166
+ - `cite42_tracker_update`: replace the whole tracker definition — name, cadence, measurement, prompts and configuration. A tracker read returns the same shape, so read it, change what you need, and send it back. Updating is free.
167
+ - `cite42_tracker_activate`: activate or resume recurring runs. You must pass the exact `estimatedCostMicroPerRun` from the latest tracker record as `maxCostMicroPerRun`; activation authorizes billed scheduled runs until the tracker is paused. The response carries the balance, the cost per run and `runsDryAt` — the date the balance stops covering your active trackers — and activation is refused when the balance cannot cover even the next run.
168
168
  - `cite42_tracker_pause`: stop future scheduled runs while preserving configuration and history. Pausing is free.
169
169
  - `cite42_tracker_delete`: permanently delete a draft or paused tracker and its run history. Active trackers must be paused first, and deletion is rejected while a run is queued or running. Financial usage records remain available.
170
- - `cite42_tracker_run`: queue one manual run from the saved primitive inputs. The underlying primitive calls are billed at their normal rates.
170
+ - `cite42_tracker_run`: queue one manual run of every saved prompt. Each prompt is billed as its own primitive call at the normal rate.
171
171
  - `cite42_trackers`: list trackers, schedules, statuses, cost estimates, and last/next run times. Reading the list is free.
172
172
  - `cite42_tracker_report`: read bounded stored run history with results, changes, status, sampled time, and costs (latest 8 by default, up to 20). Reading stored reports is free.
173
173
 
@@ -184,7 +184,7 @@ npx -y @cite42/mcp --api-key=cite42_live_your_key_here --api-base=https://www.ci
184
184
 
185
185
  ## Billing
186
186
 
187
- Primitive and workflow calls consume Cite42 credits from your account. Tracker management and stored-history reads are free; every scheduled or manual tracker run bills its saved primitive calls at their normal rates. Activating or resuming a tracker requires explicit confirmation of the current maximum credits per run and authorizes recurring billed calls until you pause it. Calls fail with a clear error if the API key is missing or credits run out. See [www.cite42.dev/pricing](https://www.cite42.dev/pricing).
187
+ Primitive and workflow calls consume Cite42 credits from your account. Tracker management and stored-history reads are free; every scheduled or manual tracker run bills one call per saved prompt at the normal rate. Activating or resuming a tracker requires explicit confirmation of the current maximum credits per run and authorizes recurring billed calls until you pause it. Calls fail with a clear error if the API key is missing or credits run out. See [www.cite42.dev/pricing](https://www.cite42.dev/pricing).
188
188
 
189
189
  ## Troubleshooting
190
190
 
package/dist/stdio.cjs CHANGED
@@ -143,9 +143,10 @@ var modelEnum = import_zod.z.enum(["chatgpt", "claude", "perplexity", "gemini",
143
143
  var QUERY_MAX_CHARS = 2e3;
144
144
  var SHORT_TEXT_MAX_CHARS = 200;
145
145
  var TIMEFRAME_MAX_CHARS = 80;
146
- var MODEL_SELECTION_DESCRIPTION = 'Optional model subset. Omit to query all five AI surfaces (chatgpt, claude, perplexity, gemini, google_ai_overview). Pass ["perplexity"] or ["perplexity","claude"] when the user asks for specific surfaces or wants to reduce cost.';
146
+ var MODEL_SELECTION_DESCRIPTION = 'Optional model subset. Omit to query the default surfaces (chatgpt, perplexity, gemini). Pass ["perplexity"] or ["perplexity","claude"] when the user names specific surfaces or wants to reduce cost. The full set is chatgpt, claude, perplexity, gemini, google_ai_overview \u2014 to query all of them, set fullSweep instead of listing them.';
147
+ var FULL_SWEEP_DESCRIPTION = "Query all five AI surfaces instead of the three default ones, adding claude and google_ai_overview. Costs more per call. Use it when the user wants complete cross-model coverage, is comparing surfaces against each other, or asks specifically about Claude or Google AI Overviews. Ignored when models is given.";
147
148
  var MCP_USAGE_GUIDANCE = "Covers AI-search, citation, SEO, keyword, trend, and market-research data. If a plan needs more than 20 endpoint calls or may use more than 500 Cite42 credits, estimate the credits and ask the user before continuing. When a result contains cite42Credits, that figure is Cite42 credits, not a model or host balance. Free (0 credits): cite42_credits for the remaining balance, cite42_pricing for exact per-tool prices and cost estimates, cite42_usage for recent calls.";
148
- var MODEL_PRICING = "Pricing is per selected AI surface; omitting models queries all five. Use the models field when the user names a surface or asks to reduce credit use, and call cite42_pricing for exact current rates.";
149
+ var MODEL_PRICING = "Pricing is per selected AI surface. Omitting models queries the three default surfaces (chatgpt, perplexity, gemini); fullSweep: true queries all five, adding claude and google_ai_overview for more credits. Use the models field when the user names a surface or asks to reduce credit use, and call cite42_pricing for exact current rates.";
149
150
  var DATA_PRICING = "Data tools have flat per-call prices; call cite42_pricing for exact current rates.";
150
151
  var WORKFLOW_PRICING = "Pricing: workflow cost is calculated from the primitive endpoints that run successfully and the selected model rates.";
151
152
  function workflowDescription(description) {
@@ -175,6 +176,7 @@ function optionalShortText(description) {
175
176
  return import_zod.z.string().trim().min(1).max(SHORT_TEXT_MAX_CHARS).optional().describe(description);
176
177
  }
177
178
  var modelsInput = import_zod.z.array(modelEnum).nonempty().optional().describe(MODEL_SELECTION_DESCRIPTION);
179
+ var fullSweepInput = import_zod.z.boolean().optional().describe(FULL_SWEEP_DESCRIPTION);
178
180
  var competitorList = import_zod.z.array(shortText("Competitor brand/domain name.")).nonempty().max(20);
179
181
  var aivCredits = (0, import_tools.createTool)({
180
182
  id: "cite42_credits",
@@ -225,7 +227,8 @@ var aivSearch = (0, import_tools.createTool)({
225
227
  description: `Run a query against AI models (ChatGPT, Claude, Perplexity, Gemini, and Google AI Overviews) and return each model's answer along with the sources it cited. ${MODEL_PRICING} ${MCP_USAGE_GUIDANCE}`,
226
228
  inputSchema: import_zod.z.object({
227
229
  query: queryText("The concise AI-search/market-research query to run against the selected models."),
228
- models: modelsInput
230
+ models: modelsInput,
231
+ fullSweep: fullSweepInput
229
232
  }),
230
233
  execute: async (input) => aivPost("/search", input)
231
234
  });
@@ -236,7 +239,8 @@ var aivCitations = (0, import_tools.createTool)({
236
239
  inputSchema: import_zod.z.object({
237
240
  query: queryText("The concise query to look up cited sources for."),
238
241
  url: import_zod.z.string().trim().max(500).optional().describe("Optional URL to check whether it was cited."),
239
- models: modelsInput
242
+ models: modelsInput,
243
+ fullSweep: fullSweepInput
240
244
  }),
241
245
  execute: async (input) => aivPost("/citations", input)
242
246
  });
@@ -247,7 +251,8 @@ var aivRankings = (0, import_tools.createTool)({
247
251
  inputSchema: import_zod.z.object({
248
252
  query: queryText("The concise query to measure brand/competitor visibility on."),
249
253
  competitors: competitorList.describe("Brand or domain names to track within the AI-generated answers."),
250
- models: modelsInput
254
+ models: modelsInput,
255
+ fullSweep: fullSweepInput
251
256
  }),
252
257
  execute: async (input) => aivPost("/rankings", input)
253
258
  });
@@ -259,7 +264,8 @@ var aivCompare = (0, import_tools.createTool)({
259
264
  query: queryText("The concise query to compare the brand and competitors on."),
260
265
  brand: shortText("Your brand, ranked alongside the competitors."),
261
266
  competitors: import_zod.z.array(shortText("Competitor brand/domain name.")).nonempty().max(10).describe("Competitor brand/domain names (up to 10) to rank against your brand."),
262
- models: modelsInput
267
+ models: modelsInput,
268
+ fullSweep: fullSweepInput
263
269
  }),
264
270
  execute: async (input) => aivPost("/compare", input)
265
271
  });
@@ -270,7 +276,8 @@ var aivSentiment = (0, import_tools.createTool)({
270
276
  inputSchema: import_zod.z.object({
271
277
  query: queryText("The concise query whose answers are scored for brand sentiment."),
272
278
  brand: shortText("The brand to score sentiment for in each model answer."),
273
- models: modelsInput
279
+ models: modelsInput,
280
+ fullSweep: fullSweepInput
274
281
  }),
275
282
  execute: async (input) => aivPost("/sentiment", input)
276
283
  });
@@ -354,7 +361,7 @@ var aivMapAiPrompts = (0, import_tools.createTool)({
354
361
  id: "cite42_map_ai_prompts",
355
362
  mcp: readOnly("Map buyer prompts for AI search"),
356
363
  description: workflowDescription(
357
- "Run the AI-prompt-map workflow: discover and cluster buyer prompts worth tracking from AI search, SEO demand, trends, citations, Reddit, and YouTube data."
364
+ "Run the AI-prompt-map workflow: discover and cluster buyer prompts worth tracking from AI search, SEO demand, trends, citations, Reddit, and YouTube data. The prompts it returns are exactly what cite42_tracker_create takes, so offer to start tracking the best of them once the user has seen the map."
358
365
  ),
359
366
  inputSchema: import_zod.z.object({
360
367
  topic: queryText("Market, product category, buyer problem, or audience to map prompts for."),
@@ -391,7 +398,8 @@ var aivFindAiCitationGaps = (0, import_tools.createTool)({
391
398
  brand: shortText("Brand or domain that should earn citations."),
392
399
  url: import_zod.z.string().trim().max(500).optional().describe("Optional exact URL to check for citations."),
393
400
  competitors: competitorList.optional().describe("Competitor brands/domains."),
394
- models: modelsInput
401
+ models: modelsInput,
402
+ fullSweep: fullSweepInput
395
403
  }),
396
404
  execute: async (input) => aivPost("/workflows/ai-citation-gaps", input)
397
405
  });
@@ -432,58 +440,38 @@ var trackerCadenceInput = import_zod.z.object({
432
440
  });
433
441
  }
434
442
  });
435
- var trackerItemInput = import_zod.z.discriminatedUnion("primitive", [
436
- import_zod.z.object({
437
- primitive: import_zod.z.literal("rankings"),
438
- input: import_zod.z.object({
439
- query: queryText("Prompt to measure brand visibility for."),
440
- competitors: competitorList,
441
- models: modelsInput
442
- })
443
- }),
444
- import_zod.z.object({
445
- primitive: import_zod.z.literal("compare"),
446
- input: import_zod.z.object({
447
- query: queryText("Prompt to compare the brand and competitors for."),
448
- brand: shortText("Primary brand to compare."),
449
- competitors: import_zod.z.array(shortText("Competitor brand/domain name.")).nonempty().max(10),
450
- models: modelsInput
451
- })
452
- }),
453
- import_zod.z.object({
454
- primitive: import_zod.z.literal("citations"),
455
- input: import_zod.z.object({
456
- query: queryText("Prompt whose cited sources should be tracked."),
457
- url: import_zod.z.string().trim().max(500).optional(),
458
- models: modelsInput
459
- })
460
- }),
461
- import_zod.z.object({
462
- primitive: import_zod.z.literal("sentiment"),
463
- input: import_zod.z.object({
464
- query: queryText("Prompt whose brand sentiment should be tracked."),
465
- brand: shortText("Brand whose sentiment should be scored."),
466
- models: modelsInput
467
- })
468
- })
469
- ]);
443
+ var trackerPrimitiveInput = import_zod.z.enum(["rankings", "compare", "citations", "sentiment"]).describe(
444
+ "What to measure on every prompt: rankings (which brands get named), compare (the brand against named competitors), citations (which sources are cited), or sentiment (how one brand is described). One measurement per tracker."
445
+ );
446
+ var trackerPromptsInput = import_zod.z.array(queryText("A prompt to run on every scheduled run.")).min(1).max(25).describe(
447
+ "Prompts to track, up to 25. Each is billed as its own call on every run. Keep the wording stable so runs stay comparable, avoid dates, and do not repeat a prompt."
448
+ );
470
449
  var trackerDefinitionFields = {
471
- name: import_zod.z.string().trim().min(1).max(100),
450
+ name: import_zod.z.string().trim().min(1).max(100).describe("Human-readable tracker name."),
472
451
  cadence: trackerCadenceInput,
473
- items: import_zod.z.array(trackerItemInput).min(1).max(25).describe("Primitive calls to execute and store on each scheduled run.")
452
+ primitive: trackerPrimitiveInput,
453
+ prompts: trackerPromptsInput,
454
+ brand: import_zod.z.string().trim().min(1).max(SHORT_TEXT_MAX_CHARS).nullish().describe("Required for compare and sentiment. Not used by rankings or citations."),
455
+ competitors: import_zod.z.array(shortText("Brand or domain name.")).max(20).nullish().describe(
456
+ "Required for rankings (the brands to track, including your own) and compare (the rivals to rank against, up to 10). Not used by citations or sentiment."
457
+ ),
458
+ url: import_zod.z.string().trim().min(1).max(500).nullish().describe("Citations only: a URL to report on as cited or not cited."),
459
+ models: import_zod.z.array(modelEnum).nonempty().nullish().describe(
460
+ "AI surfaces to query for every prompt. Omit for the three defaults (chatgpt, perplexity, gemini), or list surfaces explicitly \u2014 including all five \u2014 to fix the recurring cost."
461
+ )
474
462
  };
475
- var TRACKER_BILLING_DESCRIPTION = "Tracker management and stored-report reads are free. Each scheduled or manual run bills the underlying primitive calls at their normal per-model rates; the activation response and tracker record expose the accepted maximum cost per run.";
463
+ var TRACKER_BILLING_DESCRIPTION = "Tracker management and stored-report reads are free. Each scheduled or manual run bills one call per prompt at the normal per-model rates; the activation response and tracker record expose the accepted maximum cost per run.";
476
464
  var aivTrackerCreate = (0, import_tools.createTool)({
477
465
  id: "cite42_tracker_create",
478
466
  mcp: mutation("Create Cite42 tracker draft"),
479
- description: `Create a draft daily or weekly tracker from ordinary rankings, compare, citations, and sentiment primitive inputs. Creation does not start recurring calls. ${TRACKER_BILLING_DESCRIPTION}`,
467
+ description: "Create a draft daily or weekly tracker: one measurement \u2014 rankings, compare, citations, or sentiment \u2014 applied to a list of prompts, with the brand, competitors and AI surfaces set once for all of them. When the user has no prompt list yet, run cite42_map_ai_prompts first and track the prompts it recommends. Creation does not start recurring calls. " + TRACKER_BILLING_DESCRIPTION,
480
468
  inputSchema: import_zod.z.object(trackerDefinitionFields),
481
469
  execute: async (input) => aivPost("/trackers", input)
482
470
  });
483
471
  var aivTrackerUpdate = (0, import_tools.createTool)({
484
472
  id: "cite42_tracker_update",
485
473
  mcp: mutation("Update Cite42 tracker", { destructiveHint: true }),
486
- description: `Replace a tracker configuration with a validated name, cadence, and primitive item set. An edit returns the tracker to draft so its new recurring cost can be confirmed. ${TRACKER_BILLING_DESCRIPTION}`,
474
+ description: `Replace a tracker definition: name, cadence, measurement, prompts and configuration. Send the complete definition, not a partial patch \u2014 a tracker read returns this exact shape, so read it, change what you need, and send it back. An edit returns the tracker to draft so its new recurring cost can be confirmed. ${TRACKER_BILLING_DESCRIPTION}`,
487
475
  inputSchema: import_zod.z.object({ trackerId: trackerIdInput, ...trackerDefinitionFields }),
488
476
  execute: async ({ trackerId, ...input }) => aivPatch(`/trackers/${encodeURIComponent(trackerId)}`, input)
489
477
  });
@@ -493,7 +481,7 @@ var aivTrackerActivate = (0, import_tools.createTool)({
493
481
  openWorldHint: true,
494
482
  idempotentHint: true
495
483
  }),
496
- description: `Activate or resume recurring daily or weekly primitive calls. maxCostMicroPerRun must equal the tracker estimate being accepted; activation authorizes future billed calls until the tracker is paused. ${TRACKER_BILLING_DESCRIPTION}`,
484
+ description: `Activate or resume recurring daily or weekly primitive calls. maxCostMicroPerRun must equal the tracker estimate being accepted; activation authorizes future billed calls until the tracker is paused. The response carries a billing block with the balance, the cost per run, and runsDryAt \u2014 the date the balance stops covering the account's active trackers \u2014 so relay it. Activation is refused when the balance cannot cover even the next run; tell the user to top up at cite42.dev/app/billing. ${TRACKER_BILLING_DESCRIPTION}`,
497
485
  inputSchema: import_zod.z.object({
498
486
  trackerId: trackerIdInput,
499
487
  maxCostMicroPerRun: import_zod.z.number().int().nonnegative().describe("Exact estimatedCostMicroPerRun returned by the latest tracker record.")
@@ -594,7 +582,7 @@ if (!resolveApiKey()) {
594
582
  );
595
583
  process.exit(1);
596
584
  }
597
- buildCite42Server("0.1.17").startStdio().catch((error) => {
585
+ buildCite42Server("0.3.0").startStdio().catch((error) => {
598
586
  console.error("[@cite42/mcp] Fatal error:", error);
599
587
  process.exit(1);
600
588
  });
package/dist/stdio.mjs CHANGED
@@ -142,9 +142,10 @@ var modelEnum = z.enum(["chatgpt", "claude", "perplexity", "gemini", "google_ai_
142
142
  var QUERY_MAX_CHARS = 2e3;
143
143
  var SHORT_TEXT_MAX_CHARS = 200;
144
144
  var TIMEFRAME_MAX_CHARS = 80;
145
- var MODEL_SELECTION_DESCRIPTION = 'Optional model subset. Omit to query all five AI surfaces (chatgpt, claude, perplexity, gemini, google_ai_overview). Pass ["perplexity"] or ["perplexity","claude"] when the user asks for specific surfaces or wants to reduce cost.';
145
+ var MODEL_SELECTION_DESCRIPTION = 'Optional model subset. Omit to query the default surfaces (chatgpt, perplexity, gemini). Pass ["perplexity"] or ["perplexity","claude"] when the user names specific surfaces or wants to reduce cost. The full set is chatgpt, claude, perplexity, gemini, google_ai_overview \u2014 to query all of them, set fullSweep instead of listing them.';
146
+ var FULL_SWEEP_DESCRIPTION = "Query all five AI surfaces instead of the three default ones, adding claude and google_ai_overview. Costs more per call. Use it when the user wants complete cross-model coverage, is comparing surfaces against each other, or asks specifically about Claude or Google AI Overviews. Ignored when models is given.";
146
147
  var MCP_USAGE_GUIDANCE = "Covers AI-search, citation, SEO, keyword, trend, and market-research data. If a plan needs more than 20 endpoint calls or may use more than 500 Cite42 credits, estimate the credits and ask the user before continuing. When a result contains cite42Credits, that figure is Cite42 credits, not a model or host balance. Free (0 credits): cite42_credits for the remaining balance, cite42_pricing for exact per-tool prices and cost estimates, cite42_usage for recent calls.";
147
- var MODEL_PRICING = "Pricing is per selected AI surface; omitting models queries all five. Use the models field when the user names a surface or asks to reduce credit use, and call cite42_pricing for exact current rates.";
148
+ var MODEL_PRICING = "Pricing is per selected AI surface. Omitting models queries the three default surfaces (chatgpt, perplexity, gemini); fullSweep: true queries all five, adding claude and google_ai_overview for more credits. Use the models field when the user names a surface or asks to reduce credit use, and call cite42_pricing for exact current rates.";
148
149
  var DATA_PRICING = "Data tools have flat per-call prices; call cite42_pricing for exact current rates.";
149
150
  var WORKFLOW_PRICING = "Pricing: workflow cost is calculated from the primitive endpoints that run successfully and the selected model rates.";
150
151
  function workflowDescription(description) {
@@ -174,6 +175,7 @@ function optionalShortText(description) {
174
175
  return z.string().trim().min(1).max(SHORT_TEXT_MAX_CHARS).optional().describe(description);
175
176
  }
176
177
  var modelsInput = z.array(modelEnum).nonempty().optional().describe(MODEL_SELECTION_DESCRIPTION);
178
+ var fullSweepInput = z.boolean().optional().describe(FULL_SWEEP_DESCRIPTION);
177
179
  var competitorList = z.array(shortText("Competitor brand/domain name.")).nonempty().max(20);
178
180
  var aivCredits = createTool({
179
181
  id: "cite42_credits",
@@ -224,7 +226,8 @@ var aivSearch = createTool({
224
226
  description: `Run a query against AI models (ChatGPT, Claude, Perplexity, Gemini, and Google AI Overviews) and return each model's answer along with the sources it cited. ${MODEL_PRICING} ${MCP_USAGE_GUIDANCE}`,
225
227
  inputSchema: z.object({
226
228
  query: queryText("The concise AI-search/market-research query to run against the selected models."),
227
- models: modelsInput
229
+ models: modelsInput,
230
+ fullSweep: fullSweepInput
228
231
  }),
229
232
  execute: async (input) => aivPost("/search", input)
230
233
  });
@@ -235,7 +238,8 @@ var aivCitations = createTool({
235
238
  inputSchema: z.object({
236
239
  query: queryText("The concise query to look up cited sources for."),
237
240
  url: z.string().trim().max(500).optional().describe("Optional URL to check whether it was cited."),
238
- models: modelsInput
241
+ models: modelsInput,
242
+ fullSweep: fullSweepInput
239
243
  }),
240
244
  execute: async (input) => aivPost("/citations", input)
241
245
  });
@@ -246,7 +250,8 @@ var aivRankings = createTool({
246
250
  inputSchema: z.object({
247
251
  query: queryText("The concise query to measure brand/competitor visibility on."),
248
252
  competitors: competitorList.describe("Brand or domain names to track within the AI-generated answers."),
249
- models: modelsInput
253
+ models: modelsInput,
254
+ fullSweep: fullSweepInput
250
255
  }),
251
256
  execute: async (input) => aivPost("/rankings", input)
252
257
  });
@@ -258,7 +263,8 @@ var aivCompare = createTool({
258
263
  query: queryText("The concise query to compare the brand and competitors on."),
259
264
  brand: shortText("Your brand, ranked alongside the competitors."),
260
265
  competitors: z.array(shortText("Competitor brand/domain name.")).nonempty().max(10).describe("Competitor brand/domain names (up to 10) to rank against your brand."),
261
- models: modelsInput
266
+ models: modelsInput,
267
+ fullSweep: fullSweepInput
262
268
  }),
263
269
  execute: async (input) => aivPost("/compare", input)
264
270
  });
@@ -269,7 +275,8 @@ var aivSentiment = createTool({
269
275
  inputSchema: z.object({
270
276
  query: queryText("The concise query whose answers are scored for brand sentiment."),
271
277
  brand: shortText("The brand to score sentiment for in each model answer."),
272
- models: modelsInput
278
+ models: modelsInput,
279
+ fullSweep: fullSweepInput
273
280
  }),
274
281
  execute: async (input) => aivPost("/sentiment", input)
275
282
  });
@@ -353,7 +360,7 @@ var aivMapAiPrompts = createTool({
353
360
  id: "cite42_map_ai_prompts",
354
361
  mcp: readOnly("Map buyer prompts for AI search"),
355
362
  description: workflowDescription(
356
- "Run the AI-prompt-map workflow: discover and cluster buyer prompts worth tracking from AI search, SEO demand, trends, citations, Reddit, and YouTube data."
363
+ "Run the AI-prompt-map workflow: discover and cluster buyer prompts worth tracking from AI search, SEO demand, trends, citations, Reddit, and YouTube data. The prompts it returns are exactly what cite42_tracker_create takes, so offer to start tracking the best of them once the user has seen the map."
357
364
  ),
358
365
  inputSchema: z.object({
359
366
  topic: queryText("Market, product category, buyer problem, or audience to map prompts for."),
@@ -390,7 +397,8 @@ var aivFindAiCitationGaps = createTool({
390
397
  brand: shortText("Brand or domain that should earn citations."),
391
398
  url: z.string().trim().max(500).optional().describe("Optional exact URL to check for citations."),
392
399
  competitors: competitorList.optional().describe("Competitor brands/domains."),
393
- models: modelsInput
400
+ models: modelsInput,
401
+ fullSweep: fullSweepInput
394
402
  }),
395
403
  execute: async (input) => aivPost("/workflows/ai-citation-gaps", input)
396
404
  });
@@ -431,58 +439,38 @@ var trackerCadenceInput = z.object({
431
439
  });
432
440
  }
433
441
  });
434
- var trackerItemInput = z.discriminatedUnion("primitive", [
435
- z.object({
436
- primitive: z.literal("rankings"),
437
- input: z.object({
438
- query: queryText("Prompt to measure brand visibility for."),
439
- competitors: competitorList,
440
- models: modelsInput
441
- })
442
- }),
443
- z.object({
444
- primitive: z.literal("compare"),
445
- input: z.object({
446
- query: queryText("Prompt to compare the brand and competitors for."),
447
- brand: shortText("Primary brand to compare."),
448
- competitors: z.array(shortText("Competitor brand/domain name.")).nonempty().max(10),
449
- models: modelsInput
450
- })
451
- }),
452
- z.object({
453
- primitive: z.literal("citations"),
454
- input: z.object({
455
- query: queryText("Prompt whose cited sources should be tracked."),
456
- url: z.string().trim().max(500).optional(),
457
- models: modelsInput
458
- })
459
- }),
460
- z.object({
461
- primitive: z.literal("sentiment"),
462
- input: z.object({
463
- query: queryText("Prompt whose brand sentiment should be tracked."),
464
- brand: shortText("Brand whose sentiment should be scored."),
465
- models: modelsInput
466
- })
467
- })
468
- ]);
442
+ var trackerPrimitiveInput = z.enum(["rankings", "compare", "citations", "sentiment"]).describe(
443
+ "What to measure on every prompt: rankings (which brands get named), compare (the brand against named competitors), citations (which sources are cited), or sentiment (how one brand is described). One measurement per tracker."
444
+ );
445
+ var trackerPromptsInput = z.array(queryText("A prompt to run on every scheduled run.")).min(1).max(25).describe(
446
+ "Prompts to track, up to 25. Each is billed as its own call on every run. Keep the wording stable so runs stay comparable, avoid dates, and do not repeat a prompt."
447
+ );
469
448
  var trackerDefinitionFields = {
470
- name: z.string().trim().min(1).max(100),
449
+ name: z.string().trim().min(1).max(100).describe("Human-readable tracker name."),
471
450
  cadence: trackerCadenceInput,
472
- items: z.array(trackerItemInput).min(1).max(25).describe("Primitive calls to execute and store on each scheduled run.")
451
+ primitive: trackerPrimitiveInput,
452
+ prompts: trackerPromptsInput,
453
+ brand: z.string().trim().min(1).max(SHORT_TEXT_MAX_CHARS).nullish().describe("Required for compare and sentiment. Not used by rankings or citations."),
454
+ competitors: z.array(shortText("Brand or domain name.")).max(20).nullish().describe(
455
+ "Required for rankings (the brands to track, including your own) and compare (the rivals to rank against, up to 10). Not used by citations or sentiment."
456
+ ),
457
+ url: z.string().trim().min(1).max(500).nullish().describe("Citations only: a URL to report on as cited or not cited."),
458
+ models: z.array(modelEnum).nonempty().nullish().describe(
459
+ "AI surfaces to query for every prompt. Omit for the three defaults (chatgpt, perplexity, gemini), or list surfaces explicitly \u2014 including all five \u2014 to fix the recurring cost."
460
+ )
473
461
  };
474
- var TRACKER_BILLING_DESCRIPTION = "Tracker management and stored-report reads are free. Each scheduled or manual run bills the underlying primitive calls at their normal per-model rates; the activation response and tracker record expose the accepted maximum cost per run.";
462
+ var TRACKER_BILLING_DESCRIPTION = "Tracker management and stored-report reads are free. Each scheduled or manual run bills one call per prompt at the normal per-model rates; the activation response and tracker record expose the accepted maximum cost per run.";
475
463
  var aivTrackerCreate = createTool({
476
464
  id: "cite42_tracker_create",
477
465
  mcp: mutation("Create Cite42 tracker draft"),
478
- description: `Create a draft daily or weekly tracker from ordinary rankings, compare, citations, and sentiment primitive inputs. Creation does not start recurring calls. ${TRACKER_BILLING_DESCRIPTION}`,
466
+ description: "Create a draft daily or weekly tracker: one measurement \u2014 rankings, compare, citations, or sentiment \u2014 applied to a list of prompts, with the brand, competitors and AI surfaces set once for all of them. When the user has no prompt list yet, run cite42_map_ai_prompts first and track the prompts it recommends. Creation does not start recurring calls. " + TRACKER_BILLING_DESCRIPTION,
479
467
  inputSchema: z.object(trackerDefinitionFields),
480
468
  execute: async (input) => aivPost("/trackers", input)
481
469
  });
482
470
  var aivTrackerUpdate = createTool({
483
471
  id: "cite42_tracker_update",
484
472
  mcp: mutation("Update Cite42 tracker", { destructiveHint: true }),
485
- description: `Replace a tracker configuration with a validated name, cadence, and primitive item set. An edit returns the tracker to draft so its new recurring cost can be confirmed. ${TRACKER_BILLING_DESCRIPTION}`,
473
+ description: `Replace a tracker definition: name, cadence, measurement, prompts and configuration. Send the complete definition, not a partial patch \u2014 a tracker read returns this exact shape, so read it, change what you need, and send it back. An edit returns the tracker to draft so its new recurring cost can be confirmed. ${TRACKER_BILLING_DESCRIPTION}`,
486
474
  inputSchema: z.object({ trackerId: trackerIdInput, ...trackerDefinitionFields }),
487
475
  execute: async ({ trackerId, ...input }) => aivPatch(`/trackers/${encodeURIComponent(trackerId)}`, input)
488
476
  });
@@ -492,7 +480,7 @@ var aivTrackerActivate = createTool({
492
480
  openWorldHint: true,
493
481
  idempotentHint: true
494
482
  }),
495
- description: `Activate or resume recurring daily or weekly primitive calls. maxCostMicroPerRun must equal the tracker estimate being accepted; activation authorizes future billed calls until the tracker is paused. ${TRACKER_BILLING_DESCRIPTION}`,
483
+ description: `Activate or resume recurring daily or weekly primitive calls. maxCostMicroPerRun must equal the tracker estimate being accepted; activation authorizes future billed calls until the tracker is paused. The response carries a billing block with the balance, the cost per run, and runsDryAt \u2014 the date the balance stops covering the account's active trackers \u2014 so relay it. Activation is refused when the balance cannot cover even the next run; tell the user to top up at cite42.dev/app/billing. ${TRACKER_BILLING_DESCRIPTION}`,
496
484
  inputSchema: z.object({
497
485
  trackerId: trackerIdInput,
498
486
  maxCostMicroPerRun: z.number().int().nonnegative().describe("Exact estimatedCostMicroPerRun returned by the latest tracker record.")
@@ -593,7 +581,7 @@ if (!resolveApiKey()) {
593
581
  );
594
582
  process.exit(1);
595
583
  }
596
- buildCite42Server("0.1.17").startStdio().catch((error) => {
584
+ buildCite42Server("0.3.0").startStdio().catch((error) => {
597
585
  console.error("[@cite42/mcp] Fatal error:", error);
598
586
  process.exit(1);
599
587
  });
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@cite42/mcp",
3
3
  "mcpName": "dev.cite42/mcp",
4
- "version": "0.1.17",
5
- "description": "Cite42 MCP server: query ChatGPT, Claude, Perplexity, Gemini & Google AI Overviews for brand rankings, AI citations, SEO keyword data, and Reddit/YouTube trends. Works with Claude Code, Claude Desktop, Codex CLI, Cursor, and any MCP client.",
4
+ "version": "0.3.0",
5
+ "description": "Cite42 MCP server: query ChatGPT, Claude, Perplexity, Gemini & Google AI Overviews for brand rankings, AI citations, SEO keyword data, and Reddit/YouTube trends, and schedule daily or weekly trackers that watch a list of prompts over time. Works with Claude Code, Claude Desktop, Codex CLI, Cursor, and any MCP client.",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "bin": {
@@ -60,7 +60,10 @@
60
60
  "claude-code",
61
61
  "cursor",
62
62
  "reddit",
63
- "youtube"
63
+ "youtube",
64
+ "ai-visibility-tracking",
65
+ "rank-tracking",
66
+ "scheduled-monitoring"
64
67
  ],
65
68
  "engines": {
66
69
  "node": ">=18"