@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 +14 -14
- package/dist/stdio.cjs +39 -51
- package/dist/stdio.mjs +39 -51
- package/package.json +6 -3
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
|
|
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
|
-
- *"
|
|
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,
|
|
118
|
+
> Across ChatGPT, Perplexity, and Gemini, the most-cited sources are:
|
|
119
119
|
>
|
|
120
|
-
> 1. **freshbooks.com** (cited by
|
|
121
|
-
> 2. **wave.com** (cited by 3
|
|
122
|
-
> 3. **zoho.com/invoice** (cited by 3
|
|
123
|
-
> 4. **bonsai.com** (cited by
|
|
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
|
|
166
|
-
- `cite42_tracker_update`: replace
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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.
|
|
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.
|
|
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"
|