@cite42/mcp 0.1.17 → 0.4.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 +15 -15
- package/dist/stdio.cjs +60 -58
- package/dist/stdio.mjs +60 -58
- 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
|
|
9
|
+
- **Schedule weekly or monthly 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,20 +115,20 @@ 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
|
|
|
131
|
-
Cite42 exposes 26 MCP tools: 3 free account tools, 9
|
|
131
|
+
Cite42 exposes 26 MCP tools: 3 free account tools, 9 data tools, 6 research workflows, and 8 tracker tools.
|
|
132
132
|
|
|
133
133
|
### Account & pricing (free)
|
|
134
134
|
|
|
@@ -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
|
|
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 weekly or monthly 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 tool 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
|
-
|
|
187
|
+
Tool 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
|
@@ -17,10 +17,14 @@ function formatCredits(microCredits) {
|
|
|
17
17
|
Number.isInteger(credits) ? { maximumFractionDigits: 0 } : { minimumFractionDigits: 0, maximumFractionDigits: 2 }
|
|
18
18
|
);
|
|
19
19
|
}
|
|
20
|
+
function isTrackerCommitment(billing) {
|
|
21
|
+
return typeof billing.balanceMicro === "number" && typeof billing.costMicroPerRun === "number";
|
|
22
|
+
}
|
|
20
23
|
function toCite42McpResult(value) {
|
|
21
24
|
if (!isJsonObject(value)) return value;
|
|
22
25
|
const { billing, warning, ...result } = value;
|
|
23
26
|
if (!isJsonObject(billing)) return value;
|
|
27
|
+
if (isTrackerCommitment(billing)) return { ...result, billing };
|
|
24
28
|
const cost = isJsonObject(billing.cost) ? billing.cost.microCredits : void 0;
|
|
25
29
|
const balance = isJsonObject(billing.balance) ? billing.balance.microCredits : void 0;
|
|
26
30
|
if (typeof cost !== "number" || !Number.isFinite(cost) || typeof balance !== "number" || !Number.isFinite(balance)) {
|
|
@@ -143,11 +147,12 @@ var modelEnum = import_zod.z.enum(["chatgpt", "claude", "perplexity", "gemini",
|
|
|
143
147
|
var QUERY_MAX_CHARS = 2e3;
|
|
144
148
|
var SHORT_TEXT_MAX_CHARS = 200;
|
|
145
149
|
var TIMEFRAME_MAX_CHARS = 80;
|
|
146
|
-
var MODEL_SELECTION_DESCRIPTION = 'Optional model subset. Omit to query
|
|
150
|
+
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.';
|
|
151
|
+
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
152
|
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
|
|
153
|
+
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
154
|
var DATA_PRICING = "Data tools have flat per-call prices; call cite42_pricing for exact current rates.";
|
|
150
|
-
var WORKFLOW_PRICING = "Pricing: workflow cost is calculated from the
|
|
155
|
+
var WORKFLOW_PRICING = "Pricing: workflow cost is calculated from the tools that run successfully and the selected model rates.";
|
|
151
156
|
function workflowDescription(description) {
|
|
152
157
|
return `${description} ${MCP_USAGE_GUIDANCE} ${MODEL_PRICING} ${DATA_PRICING} ${WORKFLOW_PRICING}`;
|
|
153
158
|
}
|
|
@@ -175,6 +180,7 @@ function optionalShortText(description) {
|
|
|
175
180
|
return import_zod.z.string().trim().min(1).max(SHORT_TEXT_MAX_CHARS).optional().describe(description);
|
|
176
181
|
}
|
|
177
182
|
var modelsInput = import_zod.z.array(modelEnum).nonempty().optional().describe(MODEL_SELECTION_DESCRIPTION);
|
|
183
|
+
var fullSweepInput = import_zod.z.boolean().optional().describe(FULL_SWEEP_DESCRIPTION);
|
|
178
184
|
var competitorList = import_zod.z.array(shortText("Competitor brand/domain name.")).nonempty().max(20);
|
|
179
185
|
var aivCredits = (0, import_tools.createTool)({
|
|
180
186
|
id: "cite42_credits",
|
|
@@ -225,18 +231,20 @@ var aivSearch = (0, import_tools.createTool)({
|
|
|
225
231
|
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
232
|
inputSchema: import_zod.z.object({
|
|
227
233
|
query: queryText("The concise AI-search/market-research query to run against the selected models."),
|
|
228
|
-
models: modelsInput
|
|
234
|
+
models: modelsInput,
|
|
235
|
+
fullSweep: fullSweepInput
|
|
229
236
|
}),
|
|
230
237
|
execute: async (input) => aivPost("/search", input)
|
|
231
238
|
});
|
|
232
239
|
var aivCitations = (0, import_tools.createTool)({
|
|
233
240
|
id: "cite42_citations",
|
|
234
241
|
mcp: readOnly("Find AI citations"),
|
|
235
|
-
description: `Find which sources
|
|
242
|
+
description: `Find which sources a model's web search surfaces for a query. Aggregates cited URLs across models with per-model counts; optionally checks whether a specific URL is cited. Measures the retrieval corpus behind a prompt \u2014 which domains own your category in AI search, and where your content is missing \u2014 not the citation links a consumer chat UI renders for a logged-in user. ${MODEL_PRICING} ${MCP_USAGE_GUIDANCE}`,
|
|
236
243
|
inputSchema: import_zod.z.object({
|
|
237
244
|
query: queryText("The concise query to look up cited sources for."),
|
|
238
245
|
url: import_zod.z.string().trim().max(500).optional().describe("Optional URL to check whether it was cited."),
|
|
239
|
-
models: modelsInput
|
|
246
|
+
models: modelsInput,
|
|
247
|
+
fullSweep: fullSweepInput
|
|
240
248
|
}),
|
|
241
249
|
execute: async (input) => aivPost("/citations", input)
|
|
242
250
|
});
|
|
@@ -247,7 +255,8 @@ var aivRankings = (0, import_tools.createTool)({
|
|
|
247
255
|
inputSchema: import_zod.z.object({
|
|
248
256
|
query: queryText("The concise query to measure brand/competitor visibility on."),
|
|
249
257
|
competitors: competitorList.describe("Brand or domain names to track within the AI-generated answers."),
|
|
250
|
-
models: modelsInput
|
|
258
|
+
models: modelsInput,
|
|
259
|
+
fullSweep: fullSweepInput
|
|
251
260
|
}),
|
|
252
261
|
execute: async (input) => aivPost("/rankings", input)
|
|
253
262
|
});
|
|
@@ -259,7 +268,8 @@ var aivCompare = (0, import_tools.createTool)({
|
|
|
259
268
|
query: queryText("The concise query to compare the brand and competitors on."),
|
|
260
269
|
brand: shortText("Your brand, ranked alongside the competitors."),
|
|
261
270
|
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
|
|
271
|
+
models: modelsInput,
|
|
272
|
+
fullSweep: fullSweepInput
|
|
263
273
|
}),
|
|
264
274
|
execute: async (input) => aivPost("/compare", input)
|
|
265
275
|
});
|
|
@@ -270,7 +280,8 @@ var aivSentiment = (0, import_tools.createTool)({
|
|
|
270
280
|
inputSchema: import_zod.z.object({
|
|
271
281
|
query: queryText("The concise query whose answers are scored for brand sentiment."),
|
|
272
282
|
brand: shortText("The brand to score sentiment for in each model answer."),
|
|
273
|
-
models: modelsInput
|
|
283
|
+
models: modelsInput,
|
|
284
|
+
fullSweep: fullSweepInput
|
|
274
285
|
}),
|
|
275
286
|
execute: async (input) => aivPost("/sentiment", input)
|
|
276
287
|
});
|
|
@@ -354,7 +365,7 @@ var aivMapAiPrompts = (0, import_tools.createTool)({
|
|
|
354
365
|
id: "cite42_map_ai_prompts",
|
|
355
366
|
mcp: readOnly("Map buyer prompts for AI search"),
|
|
356
367
|
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."
|
|
368
|
+
"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
369
|
),
|
|
359
370
|
inputSchema: import_zod.z.object({
|
|
360
371
|
topic: queryText("Market, product category, buyer problem, or audience to map prompts for."),
|
|
@@ -391,7 +402,8 @@ var aivFindAiCitationGaps = (0, import_tools.createTool)({
|
|
|
391
402
|
brand: shortText("Brand or domain that should earn citations."),
|
|
392
403
|
url: import_zod.z.string().trim().max(500).optional().describe("Optional exact URL to check for citations."),
|
|
393
404
|
competitors: competitorList.optional().describe("Competitor brands/domains."),
|
|
394
|
-
models: modelsInput
|
|
405
|
+
models: modelsInput,
|
|
406
|
+
fullSweep: fullSweepInput
|
|
395
407
|
}),
|
|
396
408
|
execute: async (input) => aivPost("/workflows/ai-citation-gaps", input)
|
|
397
409
|
});
|
|
@@ -412,7 +424,7 @@ var aivGenerateContentBrief = (0, import_tools.createTool)({
|
|
|
412
424
|
});
|
|
413
425
|
var trackerIdInput = import_zod.z.string().trim().min(1).max(128).describe("Cite42 tracker ID returned by a tracker management or report tool.");
|
|
414
426
|
var trackerCadenceInput = import_zod.z.object({
|
|
415
|
-
frequency: import_zod.z.enum(["
|
|
427
|
+
frequency: import_zod.z.enum(["weekly", "monthly"]),
|
|
416
428
|
timezone: import_zod.z.string().trim().min(1).max(100).refine((timezone) => {
|
|
417
429
|
try {
|
|
418
430
|
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format();
|
|
@@ -422,7 +434,10 @@ var trackerCadenceInput = import_zod.z.object({
|
|
|
422
434
|
}
|
|
423
435
|
}, "Timezone must be a valid IANA timezone.").describe("IANA timezone, for example Europe/London or America/New_York."),
|
|
424
436
|
time: import_zod.z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).describe("Local run time in 24-hour HH:mm format."),
|
|
425
|
-
weekday: import_zod.z.number().int().min(0).max(6).optional().describe("Weekly
|
|
437
|
+
weekday: import_zod.z.number().int().min(0).max(6).optional().describe("Weekly trackers only. Day of week, where 0 is Sunday and 6 is Saturday."),
|
|
438
|
+
monthday: import_zod.z.number().int().min(1).max(31).optional().describe(
|
|
439
|
+
`Monthly trackers only. Day of month 1-31; a value past the end of a shorter month runs on that month's last day, so 31 always means "month end".`
|
|
440
|
+
)
|
|
426
441
|
}).superRefine((cadence, context) => {
|
|
427
442
|
if (cadence.frequency === "weekly" && cadence.weekday === void 0) {
|
|
428
443
|
context.addIssue({
|
|
@@ -431,59 +446,46 @@ var trackerCadenceInput = import_zod.z.object({
|
|
|
431
446
|
message: "weekday is required for a weekly tracker"
|
|
432
447
|
});
|
|
433
448
|
}
|
|
449
|
+
if (cadence.frequency === "monthly" && cadence.monthday === void 0) {
|
|
450
|
+
context.addIssue({
|
|
451
|
+
code: "custom",
|
|
452
|
+
path: ["monthday"],
|
|
453
|
+
message: "monthday is required for a monthly tracker"
|
|
454
|
+
});
|
|
455
|
+
}
|
|
434
456
|
});
|
|
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
|
-
]);
|
|
457
|
+
var trackerPrimitiveInput = import_zod.z.enum(["rankings", "compare", "citations", "sentiment"]).describe(
|
|
458
|
+
"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."
|
|
459
|
+
);
|
|
460
|
+
var trackerPromptsInput = import_zod.z.array(queryText("A prompt to run on every scheduled run.")).min(1).max(25).describe(
|
|
461
|
+
"Prompts to track, up to 30. 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."
|
|
462
|
+
);
|
|
470
463
|
var trackerDefinitionFields = {
|
|
471
|
-
name: import_zod.z.string().trim().min(1).max(100),
|
|
464
|
+
name: import_zod.z.string().trim().min(1).max(100).describe("Human-readable tracker name."),
|
|
472
465
|
cadence: trackerCadenceInput,
|
|
473
|
-
|
|
466
|
+
primitive: trackerPrimitiveInput,
|
|
467
|
+
prompts: trackerPromptsInput,
|
|
468
|
+
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."),
|
|
469
|
+
competitors: import_zod.z.array(shortText("Brand or domain name.")).max(20).nullish().describe(
|
|
470
|
+
"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."
|
|
471
|
+
),
|
|
472
|
+
url: import_zod.z.string().trim().min(1).max(500).nullish().describe("Citations only: a URL to report on as cited or not cited."),
|
|
473
|
+
models: import_zod.z.array(modelEnum).nonempty().nullish().describe(
|
|
474
|
+
"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."
|
|
475
|
+
)
|
|
474
476
|
};
|
|
475
|
-
var TRACKER_BILLING_DESCRIPTION = "Tracker management and stored-report reads are free. Each scheduled or manual run bills
|
|
477
|
+
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
478
|
var aivTrackerCreate = (0, import_tools.createTool)({
|
|
477
479
|
id: "cite42_tracker_create",
|
|
478
480
|
mcp: mutation("Create Cite42 tracker draft"),
|
|
479
|
-
description:
|
|
481
|
+
description: "Create a draft weekly or monthly 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
482
|
inputSchema: import_zod.z.object(trackerDefinitionFields),
|
|
481
483
|
execute: async (input) => aivPost("/trackers", input)
|
|
482
484
|
});
|
|
483
485
|
var aivTrackerUpdate = (0, import_tools.createTool)({
|
|
484
486
|
id: "cite42_tracker_update",
|
|
485
487
|
mcp: mutation("Update Cite42 tracker", { destructiveHint: true }),
|
|
486
|
-
description: `Replace a tracker configuration
|
|
488
|
+
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
489
|
inputSchema: import_zod.z.object({ trackerId: trackerIdInput, ...trackerDefinitionFields }),
|
|
488
490
|
execute: async ({ trackerId, ...input }) => aivPatch(`/trackers/${encodeURIComponent(trackerId)}`, input)
|
|
489
491
|
});
|
|
@@ -493,7 +495,7 @@ var aivTrackerActivate = (0, import_tools.createTool)({
|
|
|
493
495
|
openWorldHint: true,
|
|
494
496
|
idempotentHint: true
|
|
495
497
|
}),
|
|
496
|
-
description: `Activate or resume recurring
|
|
498
|
+
description: `Activate or resume recurring weekly or monthly tool 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
499
|
inputSchema: import_zod.z.object({
|
|
498
500
|
trackerId: trackerIdInput,
|
|
499
501
|
maxCostMicroPerRun: import_zod.z.number().int().nonnegative().describe("Exact estimatedCostMicroPerRun returned by the latest tracker record.")
|
|
@@ -523,7 +525,7 @@ var aivTrackerRun = (0, import_tools.createTool)({
|
|
|
523
525
|
openWorldHint: true,
|
|
524
526
|
idempotentHint: true
|
|
525
527
|
}),
|
|
526
|
-
description: `Queue one manual run using the tracker\u2019s saved
|
|
528
|
+
description: `Queue one manual run using the tracker\u2019s saved tool inputs. A repeated requestToken returns the same logical run. ${TRACKER_BILLING_DESCRIPTION}`,
|
|
527
529
|
inputSchema: import_zod.z.object({
|
|
528
530
|
trackerId: trackerIdInput,
|
|
529
531
|
requestToken: import_zod.z.string().trim().min(1).max(200).describe("Stable caller-generated token used to deduplicate a retried manual run.")
|
|
@@ -533,14 +535,14 @@ var aivTrackerRun = (0, import_tools.createTool)({
|
|
|
533
535
|
var aivTrackers = (0, import_tools.createTool)({
|
|
534
536
|
id: "cite42_trackers",
|
|
535
537
|
mcp: readOnly("List Cite42 trackers", false),
|
|
536
|
-
description: "List the connected account\u2019s trackers with status, cadence,
|
|
538
|
+
description: "List the connected account\u2019s trackers with status, cadence, tool configuration, cost estimate, accepted per-run maximum, and next/last run times. Free: costs 0 credits.",
|
|
537
539
|
inputSchema: import_zod.z.object({}),
|
|
538
540
|
execute: async () => aivGet("/trackers")
|
|
539
541
|
});
|
|
540
542
|
var aivTrackerReport = (0, import_tools.createTool)({
|
|
541
543
|
id: "cite42_tracker_report",
|
|
542
544
|
mcp: readOnly("Read Cite42 tracker report", false),
|
|
543
|
-
description: "Read bounded stored run history for one tracker, including per-
|
|
545
|
+
description: "Read bounded stored run history for one tracker, including per-tool results, changes from the prior comparable observation, statuses, sampled times, and costs. Free: costs 0 credits.",
|
|
544
546
|
inputSchema: import_zod.z.object({
|
|
545
547
|
trackerId: trackerIdInput,
|
|
546
548
|
limit: import_zod.z.number().int().min(1).max(20).optional().default(8).describe("Most recent runs to return, capped for LLM context.")
|
|
@@ -594,7 +596,7 @@ if (!resolveApiKey()) {
|
|
|
594
596
|
);
|
|
595
597
|
process.exit(1);
|
|
596
598
|
}
|
|
597
|
-
buildCite42Server("0.
|
|
599
|
+
buildCite42Server("0.4.0").startStdio().catch((error) => {
|
|
598
600
|
console.error("[@cite42/mcp] Fatal error:", error);
|
|
599
601
|
process.exit(1);
|
|
600
602
|
});
|
package/dist/stdio.mjs
CHANGED
|
@@ -16,10 +16,14 @@ function formatCredits(microCredits) {
|
|
|
16
16
|
Number.isInteger(credits) ? { maximumFractionDigits: 0 } : { minimumFractionDigits: 0, maximumFractionDigits: 2 }
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
|
+
function isTrackerCommitment(billing) {
|
|
20
|
+
return typeof billing.balanceMicro === "number" && typeof billing.costMicroPerRun === "number";
|
|
21
|
+
}
|
|
19
22
|
function toCite42McpResult(value) {
|
|
20
23
|
if (!isJsonObject(value)) return value;
|
|
21
24
|
const { billing, warning, ...result } = value;
|
|
22
25
|
if (!isJsonObject(billing)) return value;
|
|
26
|
+
if (isTrackerCommitment(billing)) return { ...result, billing };
|
|
23
27
|
const cost = isJsonObject(billing.cost) ? billing.cost.microCredits : void 0;
|
|
24
28
|
const balance = isJsonObject(billing.balance) ? billing.balance.microCredits : void 0;
|
|
25
29
|
if (typeof cost !== "number" || !Number.isFinite(cost) || typeof balance !== "number" || !Number.isFinite(balance)) {
|
|
@@ -142,11 +146,12 @@ var modelEnum = z.enum(["chatgpt", "claude", "perplexity", "gemini", "google_ai_
|
|
|
142
146
|
var QUERY_MAX_CHARS = 2e3;
|
|
143
147
|
var SHORT_TEXT_MAX_CHARS = 200;
|
|
144
148
|
var TIMEFRAME_MAX_CHARS = 80;
|
|
145
|
-
var MODEL_SELECTION_DESCRIPTION = 'Optional model subset. Omit to query
|
|
149
|
+
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.';
|
|
150
|
+
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
151
|
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
|
|
152
|
+
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
153
|
var DATA_PRICING = "Data tools have flat per-call prices; call cite42_pricing for exact current rates.";
|
|
149
|
-
var WORKFLOW_PRICING = "Pricing: workflow cost is calculated from the
|
|
154
|
+
var WORKFLOW_PRICING = "Pricing: workflow cost is calculated from the tools that run successfully and the selected model rates.";
|
|
150
155
|
function workflowDescription(description) {
|
|
151
156
|
return `${description} ${MCP_USAGE_GUIDANCE} ${MODEL_PRICING} ${DATA_PRICING} ${WORKFLOW_PRICING}`;
|
|
152
157
|
}
|
|
@@ -174,6 +179,7 @@ function optionalShortText(description) {
|
|
|
174
179
|
return z.string().trim().min(1).max(SHORT_TEXT_MAX_CHARS).optional().describe(description);
|
|
175
180
|
}
|
|
176
181
|
var modelsInput = z.array(modelEnum).nonempty().optional().describe(MODEL_SELECTION_DESCRIPTION);
|
|
182
|
+
var fullSweepInput = z.boolean().optional().describe(FULL_SWEEP_DESCRIPTION);
|
|
177
183
|
var competitorList = z.array(shortText("Competitor brand/domain name.")).nonempty().max(20);
|
|
178
184
|
var aivCredits = createTool({
|
|
179
185
|
id: "cite42_credits",
|
|
@@ -224,18 +230,20 @@ var aivSearch = createTool({
|
|
|
224
230
|
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
231
|
inputSchema: z.object({
|
|
226
232
|
query: queryText("The concise AI-search/market-research query to run against the selected models."),
|
|
227
|
-
models: modelsInput
|
|
233
|
+
models: modelsInput,
|
|
234
|
+
fullSweep: fullSweepInput
|
|
228
235
|
}),
|
|
229
236
|
execute: async (input) => aivPost("/search", input)
|
|
230
237
|
});
|
|
231
238
|
var aivCitations = createTool({
|
|
232
239
|
id: "cite42_citations",
|
|
233
240
|
mcp: readOnly("Find AI citations"),
|
|
234
|
-
description: `Find which sources
|
|
241
|
+
description: `Find which sources a model's web search surfaces for a query. Aggregates cited URLs across models with per-model counts; optionally checks whether a specific URL is cited. Measures the retrieval corpus behind a prompt \u2014 which domains own your category in AI search, and where your content is missing \u2014 not the citation links a consumer chat UI renders for a logged-in user. ${MODEL_PRICING} ${MCP_USAGE_GUIDANCE}`,
|
|
235
242
|
inputSchema: z.object({
|
|
236
243
|
query: queryText("The concise query to look up cited sources for."),
|
|
237
244
|
url: z.string().trim().max(500).optional().describe("Optional URL to check whether it was cited."),
|
|
238
|
-
models: modelsInput
|
|
245
|
+
models: modelsInput,
|
|
246
|
+
fullSweep: fullSweepInput
|
|
239
247
|
}),
|
|
240
248
|
execute: async (input) => aivPost("/citations", input)
|
|
241
249
|
});
|
|
@@ -246,7 +254,8 @@ var aivRankings = createTool({
|
|
|
246
254
|
inputSchema: z.object({
|
|
247
255
|
query: queryText("The concise query to measure brand/competitor visibility on."),
|
|
248
256
|
competitors: competitorList.describe("Brand or domain names to track within the AI-generated answers."),
|
|
249
|
-
models: modelsInput
|
|
257
|
+
models: modelsInput,
|
|
258
|
+
fullSweep: fullSweepInput
|
|
250
259
|
}),
|
|
251
260
|
execute: async (input) => aivPost("/rankings", input)
|
|
252
261
|
});
|
|
@@ -258,7 +267,8 @@ var aivCompare = createTool({
|
|
|
258
267
|
query: queryText("The concise query to compare the brand and competitors on."),
|
|
259
268
|
brand: shortText("Your brand, ranked alongside the competitors."),
|
|
260
269
|
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
|
|
270
|
+
models: modelsInput,
|
|
271
|
+
fullSweep: fullSweepInput
|
|
262
272
|
}),
|
|
263
273
|
execute: async (input) => aivPost("/compare", input)
|
|
264
274
|
});
|
|
@@ -269,7 +279,8 @@ var aivSentiment = createTool({
|
|
|
269
279
|
inputSchema: z.object({
|
|
270
280
|
query: queryText("The concise query whose answers are scored for brand sentiment."),
|
|
271
281
|
brand: shortText("The brand to score sentiment for in each model answer."),
|
|
272
|
-
models: modelsInput
|
|
282
|
+
models: modelsInput,
|
|
283
|
+
fullSweep: fullSweepInput
|
|
273
284
|
}),
|
|
274
285
|
execute: async (input) => aivPost("/sentiment", input)
|
|
275
286
|
});
|
|
@@ -353,7 +364,7 @@ var aivMapAiPrompts = createTool({
|
|
|
353
364
|
id: "cite42_map_ai_prompts",
|
|
354
365
|
mcp: readOnly("Map buyer prompts for AI search"),
|
|
355
366
|
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."
|
|
367
|
+
"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
368
|
),
|
|
358
369
|
inputSchema: z.object({
|
|
359
370
|
topic: queryText("Market, product category, buyer problem, or audience to map prompts for."),
|
|
@@ -390,7 +401,8 @@ var aivFindAiCitationGaps = createTool({
|
|
|
390
401
|
brand: shortText("Brand or domain that should earn citations."),
|
|
391
402
|
url: z.string().trim().max(500).optional().describe("Optional exact URL to check for citations."),
|
|
392
403
|
competitors: competitorList.optional().describe("Competitor brands/domains."),
|
|
393
|
-
models: modelsInput
|
|
404
|
+
models: modelsInput,
|
|
405
|
+
fullSweep: fullSweepInput
|
|
394
406
|
}),
|
|
395
407
|
execute: async (input) => aivPost("/workflows/ai-citation-gaps", input)
|
|
396
408
|
});
|
|
@@ -411,7 +423,7 @@ var aivGenerateContentBrief = createTool({
|
|
|
411
423
|
});
|
|
412
424
|
var trackerIdInput = z.string().trim().min(1).max(128).describe("Cite42 tracker ID returned by a tracker management or report tool.");
|
|
413
425
|
var trackerCadenceInput = z.object({
|
|
414
|
-
frequency: z.enum(["
|
|
426
|
+
frequency: z.enum(["weekly", "monthly"]),
|
|
415
427
|
timezone: z.string().trim().min(1).max(100).refine((timezone) => {
|
|
416
428
|
try {
|
|
417
429
|
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format();
|
|
@@ -421,7 +433,10 @@ var trackerCadenceInput = z.object({
|
|
|
421
433
|
}
|
|
422
434
|
}, "Timezone must be a valid IANA timezone.").describe("IANA timezone, for example Europe/London or America/New_York."),
|
|
423
435
|
time: z.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/).describe("Local run time in 24-hour HH:mm format."),
|
|
424
|
-
weekday: z.number().int().min(0).max(6).optional().describe("Weekly
|
|
436
|
+
weekday: z.number().int().min(0).max(6).optional().describe("Weekly trackers only. Day of week, where 0 is Sunday and 6 is Saturday."),
|
|
437
|
+
monthday: z.number().int().min(1).max(31).optional().describe(
|
|
438
|
+
`Monthly trackers only. Day of month 1-31; a value past the end of a shorter month runs on that month's last day, so 31 always means "month end".`
|
|
439
|
+
)
|
|
425
440
|
}).superRefine((cadence, context) => {
|
|
426
441
|
if (cadence.frequency === "weekly" && cadence.weekday === void 0) {
|
|
427
442
|
context.addIssue({
|
|
@@ -430,59 +445,46 @@ var trackerCadenceInput = z.object({
|
|
|
430
445
|
message: "weekday is required for a weekly tracker"
|
|
431
446
|
});
|
|
432
447
|
}
|
|
448
|
+
if (cadence.frequency === "monthly" && cadence.monthday === void 0) {
|
|
449
|
+
context.addIssue({
|
|
450
|
+
code: "custom",
|
|
451
|
+
path: ["monthday"],
|
|
452
|
+
message: "monthday is required for a monthly tracker"
|
|
453
|
+
});
|
|
454
|
+
}
|
|
433
455
|
});
|
|
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
|
-
]);
|
|
456
|
+
var trackerPrimitiveInput = z.enum(["rankings", "compare", "citations", "sentiment"]).describe(
|
|
457
|
+
"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."
|
|
458
|
+
);
|
|
459
|
+
var trackerPromptsInput = z.array(queryText("A prompt to run on every scheduled run.")).min(1).max(25).describe(
|
|
460
|
+
"Prompts to track, up to 30. 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."
|
|
461
|
+
);
|
|
469
462
|
var trackerDefinitionFields = {
|
|
470
|
-
name: z.string().trim().min(1).max(100),
|
|
463
|
+
name: z.string().trim().min(1).max(100).describe("Human-readable tracker name."),
|
|
471
464
|
cadence: trackerCadenceInput,
|
|
472
|
-
|
|
465
|
+
primitive: trackerPrimitiveInput,
|
|
466
|
+
prompts: trackerPromptsInput,
|
|
467
|
+
brand: z.string().trim().min(1).max(SHORT_TEXT_MAX_CHARS).nullish().describe("Required for compare and sentiment. Not used by rankings or citations."),
|
|
468
|
+
competitors: z.array(shortText("Brand or domain name.")).max(20).nullish().describe(
|
|
469
|
+
"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."
|
|
470
|
+
),
|
|
471
|
+
url: z.string().trim().min(1).max(500).nullish().describe("Citations only: a URL to report on as cited or not cited."),
|
|
472
|
+
models: z.array(modelEnum).nonempty().nullish().describe(
|
|
473
|
+
"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."
|
|
474
|
+
)
|
|
473
475
|
};
|
|
474
|
-
var TRACKER_BILLING_DESCRIPTION = "Tracker management and stored-report reads are free. Each scheduled or manual run bills
|
|
476
|
+
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
477
|
var aivTrackerCreate = createTool({
|
|
476
478
|
id: "cite42_tracker_create",
|
|
477
479
|
mcp: mutation("Create Cite42 tracker draft"),
|
|
478
|
-
description:
|
|
480
|
+
description: "Create a draft weekly or monthly 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
481
|
inputSchema: z.object(trackerDefinitionFields),
|
|
480
482
|
execute: async (input) => aivPost("/trackers", input)
|
|
481
483
|
});
|
|
482
484
|
var aivTrackerUpdate = createTool({
|
|
483
485
|
id: "cite42_tracker_update",
|
|
484
486
|
mcp: mutation("Update Cite42 tracker", { destructiveHint: true }),
|
|
485
|
-
description: `Replace a tracker configuration
|
|
487
|
+
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
488
|
inputSchema: z.object({ trackerId: trackerIdInput, ...trackerDefinitionFields }),
|
|
487
489
|
execute: async ({ trackerId, ...input }) => aivPatch(`/trackers/${encodeURIComponent(trackerId)}`, input)
|
|
488
490
|
});
|
|
@@ -492,7 +494,7 @@ var aivTrackerActivate = createTool({
|
|
|
492
494
|
openWorldHint: true,
|
|
493
495
|
idempotentHint: true
|
|
494
496
|
}),
|
|
495
|
-
description: `Activate or resume recurring
|
|
497
|
+
description: `Activate or resume recurring weekly or monthly tool 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
498
|
inputSchema: z.object({
|
|
497
499
|
trackerId: trackerIdInput,
|
|
498
500
|
maxCostMicroPerRun: z.number().int().nonnegative().describe("Exact estimatedCostMicroPerRun returned by the latest tracker record.")
|
|
@@ -522,7 +524,7 @@ var aivTrackerRun = createTool({
|
|
|
522
524
|
openWorldHint: true,
|
|
523
525
|
idempotentHint: true
|
|
524
526
|
}),
|
|
525
|
-
description: `Queue one manual run using the tracker\u2019s saved
|
|
527
|
+
description: `Queue one manual run using the tracker\u2019s saved tool inputs. A repeated requestToken returns the same logical run. ${TRACKER_BILLING_DESCRIPTION}`,
|
|
526
528
|
inputSchema: z.object({
|
|
527
529
|
trackerId: trackerIdInput,
|
|
528
530
|
requestToken: z.string().trim().min(1).max(200).describe("Stable caller-generated token used to deduplicate a retried manual run.")
|
|
@@ -532,14 +534,14 @@ var aivTrackerRun = createTool({
|
|
|
532
534
|
var aivTrackers = createTool({
|
|
533
535
|
id: "cite42_trackers",
|
|
534
536
|
mcp: readOnly("List Cite42 trackers", false),
|
|
535
|
-
description: "List the connected account\u2019s trackers with status, cadence,
|
|
537
|
+
description: "List the connected account\u2019s trackers with status, cadence, tool configuration, cost estimate, accepted per-run maximum, and next/last run times. Free: costs 0 credits.",
|
|
536
538
|
inputSchema: z.object({}),
|
|
537
539
|
execute: async () => aivGet("/trackers")
|
|
538
540
|
});
|
|
539
541
|
var aivTrackerReport = createTool({
|
|
540
542
|
id: "cite42_tracker_report",
|
|
541
543
|
mcp: readOnly("Read Cite42 tracker report", false),
|
|
542
|
-
description: "Read bounded stored run history for one tracker, including per-
|
|
544
|
+
description: "Read bounded stored run history for one tracker, including per-tool results, changes from the prior comparable observation, statuses, sampled times, and costs. Free: costs 0 credits.",
|
|
543
545
|
inputSchema: z.object({
|
|
544
546
|
trackerId: trackerIdInput,
|
|
545
547
|
limit: z.number().int().min(1).max(20).optional().default(8).describe("Most recent runs to return, capped for LLM context.")
|
|
@@ -593,7 +595,7 @@ if (!resolveApiKey()) {
|
|
|
593
595
|
);
|
|
594
596
|
process.exit(1);
|
|
595
597
|
}
|
|
596
|
-
buildCite42Server("0.
|
|
598
|
+
buildCite42Server("0.4.0").startStdio().catch((error) => {
|
|
597
599
|
console.error("[@cite42/mcp] Fatal error:", error);
|
|
598
600
|
process.exit(1);
|
|
599
601
|
});
|
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
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"description": "Cite42 MCP server: query the models behind ChatGPT, Claude, Perplexity & Gemini, plus Google AI Overviews, for brand rankings, AI citations, SEO keyword data, and Reddit/YouTube trends, and schedule weekly or monthly 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"
|