@cite42/mcp 0.1.16 → 0.1.17
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 +24 -3
- package/dist/stdio.cjs +177 -3
- package/dist/stdio.mjs +177 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +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
10
|
- **Pull SEO data**: keyword search volume, CPC, competition, and Google Trends
|
|
10
11
|
- **Mine Reddit and YouTube** for audience questions, pain points, and rising content
|
|
11
12
|
- **Run full research workflows**: content gaps, topic demand, AI prompt maps, competitor analysis, and content briefs in a single call
|
|
@@ -91,7 +92,7 @@ You do not need to keep `npx -y @cite42/mcp` running in a separate terminal. Loc
|
|
|
91
92
|
- **Claude Code:** run `claude mcp list` and check that `cite42` shows as ✔ connected. Or type `/mcp` inside a session.
|
|
92
93
|
- **Claude Desktop:** open **Settings → Connectors** and confirm Cite42 is connected.
|
|
93
94
|
- **Codex CLI:** run `codex mcp list`.
|
|
94
|
-
- **Cursor:** look for `cite42` in the MCP/tools indicator.
|
|
95
|
+
- **Cursor:** look for `cite42` in the MCP/tools indicator. 26 tools should be listed.
|
|
95
96
|
|
|
96
97
|
If it doesn't appear, see [Troubleshooting](#troubleshooting).
|
|
97
98
|
|
|
@@ -104,6 +105,7 @@ After connecting or restarting, just ask in plain language. Your agent picks the
|
|
|
104
105
|
- *"Get search volume, CPC, and related keywords for 'ai seo tools'."*
|
|
105
106
|
- *"What are people on Reddit complaining about regarding email marketing tools?"*
|
|
106
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."*
|
|
107
109
|
|
|
108
110
|
### Example: what a session looks like
|
|
109
111
|
|
|
@@ -124,7 +126,15 @@ After connecting or restarting, just ask in plain language. Your agent picks the
|
|
|
124
126
|
|
|
125
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.
|
|
126
128
|
|
|
127
|
-
##
|
|
129
|
+
## Tools
|
|
130
|
+
|
|
131
|
+
Cite42 exposes 26 MCP tools: 3 free account tools, 9 primitive data tools, 6 research workflows, and 8 tracker tools.
|
|
132
|
+
|
|
133
|
+
### Account & pricing (free)
|
|
134
|
+
|
|
135
|
+
- `cite42_credits`: check the connected account's remaining Cite42 credit balance.
|
|
136
|
+
- `cite42_pricing`: get current per-tool prices and estimate a planned set of calls.
|
|
137
|
+
- `cite42_usage`: read recent call status and cost together with the remaining balance.
|
|
128
138
|
|
|
129
139
|
### AI search visibility
|
|
130
140
|
|
|
@@ -150,6 +160,17 @@ The exact brands and sources change by query and model. A good result should sho
|
|
|
150
160
|
- `cite42_find_ai_citation_gaps`: find queries where AI answers cite competitors but not your brand, domain, or URL.
|
|
151
161
|
- `cite42_generate_content_brief`: collect AI answers, citations, keyword demand, and social signals into brief-ready data.
|
|
152
162
|
|
|
163
|
+
### Trackers
|
|
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.
|
|
168
|
+
- `cite42_tracker_pause`: stop future scheduled runs while preserving configuration and history. Pausing is free.
|
|
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.
|
|
171
|
+
- `cite42_trackers`: list trackers, schedules, statuses, cost estimates, and last/next run times. Reading the list is free.
|
|
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
|
+
|
|
153
174
|
## Configuration
|
|
154
175
|
|
|
155
176
|
- `CITE42_API_KEY`: required. Your API key from [www.cite42.dev/app/keys](https://www.cite42.dev/app/keys).
|
|
@@ -163,7 +184,7 @@ npx -y @cite42/mcp --api-key=cite42_live_your_key_here --api-base=https://www.ci
|
|
|
163
184
|
|
|
164
185
|
## Billing
|
|
165
186
|
|
|
166
|
-
|
|
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).
|
|
167
188
|
|
|
168
189
|
## Troubleshooting
|
|
169
190
|
|
package/dist/stdio.cjs
CHANGED
|
@@ -87,9 +87,27 @@ function getBaseUrl() {
|
|
|
87
87
|
return readFlag("api-base") ?? process.env.CITE42_API_BASE ?? DEFAULT_BASE_URL;
|
|
88
88
|
}
|
|
89
89
|
async function aivPost(path, body) {
|
|
90
|
+
return aivWrite("POST", path, body);
|
|
91
|
+
}
|
|
92
|
+
async function aivPatch(path, body) {
|
|
93
|
+
return aivWrite("PATCH", path, body);
|
|
94
|
+
}
|
|
95
|
+
async function aivDelete(path) {
|
|
96
|
+
const url = `${getBaseUrl()}${path.startsWith("/") ? path : `/${path}`}`;
|
|
97
|
+
const res = await fetch(url, {
|
|
98
|
+
method: "DELETE",
|
|
99
|
+
headers: { Authorization: `Bearer ${getApiKey()}` }
|
|
100
|
+
});
|
|
101
|
+
if (!res.ok) {
|
|
102
|
+
const body = await res.text();
|
|
103
|
+
throw new Error(errorMessageForMcp(res.status, path, body));
|
|
104
|
+
}
|
|
105
|
+
return toCite42McpResult(await res.json());
|
|
106
|
+
}
|
|
107
|
+
async function aivWrite(method, path, body) {
|
|
90
108
|
const url = `${getBaseUrl()}${path.startsWith("/") ? path : `/${path}`}`;
|
|
91
109
|
const res = await fetch(url, {
|
|
92
|
-
method
|
|
110
|
+
method,
|
|
93
111
|
headers: {
|
|
94
112
|
"Content-Type": "application/json",
|
|
95
113
|
Authorization: `Bearer ${getApiKey()}`
|
|
@@ -136,6 +154,17 @@ function workflowDescription(description) {
|
|
|
136
154
|
function readOnly(title, openWorldHint = true) {
|
|
137
155
|
return { annotations: { title, readOnlyHint: true, openWorldHint } };
|
|
138
156
|
}
|
|
157
|
+
function mutation(title, options = {}) {
|
|
158
|
+
return {
|
|
159
|
+
annotations: {
|
|
160
|
+
title,
|
|
161
|
+
readOnlyHint: false,
|
|
162
|
+
destructiveHint: options.destructiveHint ?? false,
|
|
163
|
+
openWorldHint: options.openWorldHint ?? false,
|
|
164
|
+
...options.idempotentHint === void 0 ? {} : { idempotentHint: options.idempotentHint }
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}
|
|
139
168
|
function queryText(description) {
|
|
140
169
|
return import_zod.z.string().trim().min(1).max(QUERY_MAX_CHARS).describe(description);
|
|
141
170
|
}
|
|
@@ -381,6 +410,143 @@ var aivGenerateContentBrief = (0, import_tools.createTool)({
|
|
|
381
410
|
}),
|
|
382
411
|
execute: async (input) => aivPost("/workflows/content-brief", input)
|
|
383
412
|
});
|
|
413
|
+
var trackerIdInput = import_zod.z.string().trim().min(1).max(128).describe("Cite42 tracker ID returned by a tracker management or report tool.");
|
|
414
|
+
var trackerCadenceInput = import_zod.z.object({
|
|
415
|
+
frequency: import_zod.z.enum(["daily", "weekly"]),
|
|
416
|
+
timezone: import_zod.z.string().trim().min(1).max(100).refine((timezone) => {
|
|
417
|
+
try {
|
|
418
|
+
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format();
|
|
419
|
+
return true;
|
|
420
|
+
} catch {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
}, "Timezone must be a valid IANA timezone.").describe("IANA timezone, for example Europe/London or America/New_York."),
|
|
424
|
+
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 day, where 0 is Sunday and 6 is Saturday.")
|
|
426
|
+
}).superRefine((cadence, context) => {
|
|
427
|
+
if (cadence.frequency === "weekly" && cadence.weekday === void 0) {
|
|
428
|
+
context.addIssue({
|
|
429
|
+
code: "custom",
|
|
430
|
+
path: ["weekday"],
|
|
431
|
+
message: "weekday is required for a weekly tracker"
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
});
|
|
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
|
+
]);
|
|
470
|
+
var trackerDefinitionFields = {
|
|
471
|
+
name: import_zod.z.string().trim().min(1).max(100),
|
|
472
|
+
cadence: trackerCadenceInput,
|
|
473
|
+
items: import_zod.z.array(trackerItemInput).min(1).max(25).describe("Primitive calls to execute and store on each scheduled run.")
|
|
474
|
+
};
|
|
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.";
|
|
476
|
+
var aivTrackerCreate = (0, import_tools.createTool)({
|
|
477
|
+
id: "cite42_tracker_create",
|
|
478
|
+
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}`,
|
|
480
|
+
inputSchema: import_zod.z.object(trackerDefinitionFields),
|
|
481
|
+
execute: async (input) => aivPost("/trackers", input)
|
|
482
|
+
});
|
|
483
|
+
var aivTrackerUpdate = (0, import_tools.createTool)({
|
|
484
|
+
id: "cite42_tracker_update",
|
|
485
|
+
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}`,
|
|
487
|
+
inputSchema: import_zod.z.object({ trackerId: trackerIdInput, ...trackerDefinitionFields }),
|
|
488
|
+
execute: async ({ trackerId, ...input }) => aivPatch(`/trackers/${encodeURIComponent(trackerId)}`, input)
|
|
489
|
+
});
|
|
490
|
+
var aivTrackerActivate = (0, import_tools.createTool)({
|
|
491
|
+
id: "cite42_tracker_activate",
|
|
492
|
+
mcp: mutation("Activate Cite42 tracker", {
|
|
493
|
+
openWorldHint: true,
|
|
494
|
+
idempotentHint: true
|
|
495
|
+
}),
|
|
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}`,
|
|
497
|
+
inputSchema: import_zod.z.object({
|
|
498
|
+
trackerId: trackerIdInput,
|
|
499
|
+
maxCostMicroPerRun: import_zod.z.number().int().nonnegative().describe("Exact estimatedCostMicroPerRun returned by the latest tracker record.")
|
|
500
|
+
}),
|
|
501
|
+
execute: async ({ trackerId, maxCostMicroPerRun }) => aivPost(`/trackers/${encodeURIComponent(trackerId)}/activate`, { maxCostMicroPerRun })
|
|
502
|
+
});
|
|
503
|
+
var aivTrackerPause = (0, import_tools.createTool)({
|
|
504
|
+
id: "cite42_tracker_pause",
|
|
505
|
+
mcp: mutation("Pause Cite42 tracker", { idempotentHint: true }),
|
|
506
|
+
description: "Pause future recurring calls for a tracker while preserving its configuration and stored run history. Free: costs 0 credits.",
|
|
507
|
+
inputSchema: import_zod.z.object({ trackerId: trackerIdInput }),
|
|
508
|
+
execute: async ({ trackerId }) => aivPost(`/trackers/${encodeURIComponent(trackerId)}/pause`, {})
|
|
509
|
+
});
|
|
510
|
+
var aivTrackerDelete = (0, import_tools.createTool)({
|
|
511
|
+
id: "cite42_tracker_delete",
|
|
512
|
+
mcp: mutation("Permanently delete Cite42 tracker", {
|
|
513
|
+
destructiveHint: true,
|
|
514
|
+
idempotentHint: true
|
|
515
|
+
}),
|
|
516
|
+
description: "Permanently delete a draft or paused tracker, including its saved configuration and run history. Active trackers must be paused first, and deletion is rejected while a run is queued or running. Financial usage and credit ledger records are retained. Free: costs 0 credits.",
|
|
517
|
+
inputSchema: import_zod.z.object({ trackerId: trackerIdInput }),
|
|
518
|
+
execute: async ({ trackerId }) => aivDelete(`/trackers/${encodeURIComponent(trackerId)}`)
|
|
519
|
+
});
|
|
520
|
+
var aivTrackerRun = (0, import_tools.createTool)({
|
|
521
|
+
id: "cite42_tracker_run",
|
|
522
|
+
mcp: mutation("Run Cite42 tracker now", {
|
|
523
|
+
openWorldHint: true,
|
|
524
|
+
idempotentHint: true
|
|
525
|
+
}),
|
|
526
|
+
description: `Queue one manual run using the tracker\u2019s saved primitive inputs. A repeated requestToken returns the same logical run. ${TRACKER_BILLING_DESCRIPTION}`,
|
|
527
|
+
inputSchema: import_zod.z.object({
|
|
528
|
+
trackerId: trackerIdInput,
|
|
529
|
+
requestToken: import_zod.z.string().trim().min(1).max(200).describe("Stable caller-generated token used to deduplicate a retried manual run.")
|
|
530
|
+
}),
|
|
531
|
+
execute: async ({ trackerId, requestToken }) => aivPost(`/trackers/${encodeURIComponent(trackerId)}/runs`, { requestToken })
|
|
532
|
+
});
|
|
533
|
+
var aivTrackers = (0, import_tools.createTool)({
|
|
534
|
+
id: "cite42_trackers",
|
|
535
|
+
mcp: readOnly("List Cite42 trackers", false),
|
|
536
|
+
description: "List the connected account\u2019s trackers with status, cadence, primitive configuration, cost estimate, accepted per-run maximum, and next/last run times. Free: costs 0 credits.",
|
|
537
|
+
inputSchema: import_zod.z.object({}),
|
|
538
|
+
execute: async () => aivGet("/trackers")
|
|
539
|
+
});
|
|
540
|
+
var aivTrackerReport = (0, import_tools.createTool)({
|
|
541
|
+
id: "cite42_tracker_report",
|
|
542
|
+
mcp: readOnly("Read Cite42 tracker report", false),
|
|
543
|
+
description: "Read bounded stored run history for one tracker, including per-primitive results, changes from the prior comparable observation, statuses, sampled times, and costs. Free: costs 0 credits.",
|
|
544
|
+
inputSchema: import_zod.z.object({
|
|
545
|
+
trackerId: trackerIdInput,
|
|
546
|
+
limit: import_zod.z.number().int().min(1).max(20).optional().default(8).describe("Most recent runs to return, capped for LLM context.")
|
|
547
|
+
}),
|
|
548
|
+
execute: async ({ trackerId, limit }) => aivGet(`/trackers/${encodeURIComponent(trackerId)}/report?limit=${limit}`)
|
|
549
|
+
});
|
|
384
550
|
|
|
385
551
|
// src/server.ts
|
|
386
552
|
var CITE42_TOOLS = {
|
|
@@ -401,7 +567,15 @@ var CITE42_TOOLS = {
|
|
|
401
567
|
cite42_map_ai_prompts: aivMapAiPrompts,
|
|
402
568
|
cite42_analyze_competitor_content: aivAnalyzeCompetitorContent,
|
|
403
569
|
cite42_find_ai_citation_gaps: aivFindAiCitationGaps,
|
|
404
|
-
cite42_generate_content_brief: aivGenerateContentBrief
|
|
570
|
+
cite42_generate_content_brief: aivGenerateContentBrief,
|
|
571
|
+
cite42_tracker_create: aivTrackerCreate,
|
|
572
|
+
cite42_tracker_update: aivTrackerUpdate,
|
|
573
|
+
cite42_tracker_activate: aivTrackerActivate,
|
|
574
|
+
cite42_tracker_pause: aivTrackerPause,
|
|
575
|
+
cite42_tracker_delete: aivTrackerDelete,
|
|
576
|
+
cite42_tracker_run: aivTrackerRun,
|
|
577
|
+
cite42_trackers: aivTrackers,
|
|
578
|
+
cite42_tracker_report: aivTrackerReport
|
|
405
579
|
};
|
|
406
580
|
function buildCite42Server(version) {
|
|
407
581
|
return new import_mcp.MCPServer({
|
|
@@ -420,7 +594,7 @@ if (!resolveApiKey()) {
|
|
|
420
594
|
);
|
|
421
595
|
process.exit(1);
|
|
422
596
|
}
|
|
423
|
-
buildCite42Server("0.1.
|
|
597
|
+
buildCite42Server("0.1.17").startStdio().catch((error) => {
|
|
424
598
|
console.error("[@cite42/mcp] Fatal error:", error);
|
|
425
599
|
process.exit(1);
|
|
426
600
|
});
|
package/dist/stdio.mjs
CHANGED
|
@@ -86,9 +86,27 @@ function getBaseUrl() {
|
|
|
86
86
|
return readFlag("api-base") ?? process.env.CITE42_API_BASE ?? DEFAULT_BASE_URL;
|
|
87
87
|
}
|
|
88
88
|
async function aivPost(path, body) {
|
|
89
|
+
return aivWrite("POST", path, body);
|
|
90
|
+
}
|
|
91
|
+
async function aivPatch(path, body) {
|
|
92
|
+
return aivWrite("PATCH", path, body);
|
|
93
|
+
}
|
|
94
|
+
async function aivDelete(path) {
|
|
95
|
+
const url = `${getBaseUrl()}${path.startsWith("/") ? path : `/${path}`}`;
|
|
96
|
+
const res = await fetch(url, {
|
|
97
|
+
method: "DELETE",
|
|
98
|
+
headers: { Authorization: `Bearer ${getApiKey()}` }
|
|
99
|
+
});
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
const body = await res.text();
|
|
102
|
+
throw new Error(errorMessageForMcp(res.status, path, body));
|
|
103
|
+
}
|
|
104
|
+
return toCite42McpResult(await res.json());
|
|
105
|
+
}
|
|
106
|
+
async function aivWrite(method, path, body) {
|
|
89
107
|
const url = `${getBaseUrl()}${path.startsWith("/") ? path : `/${path}`}`;
|
|
90
108
|
const res = await fetch(url, {
|
|
91
|
-
method
|
|
109
|
+
method,
|
|
92
110
|
headers: {
|
|
93
111
|
"Content-Type": "application/json",
|
|
94
112
|
Authorization: `Bearer ${getApiKey()}`
|
|
@@ -135,6 +153,17 @@ function workflowDescription(description) {
|
|
|
135
153
|
function readOnly(title, openWorldHint = true) {
|
|
136
154
|
return { annotations: { title, readOnlyHint: true, openWorldHint } };
|
|
137
155
|
}
|
|
156
|
+
function mutation(title, options = {}) {
|
|
157
|
+
return {
|
|
158
|
+
annotations: {
|
|
159
|
+
title,
|
|
160
|
+
readOnlyHint: false,
|
|
161
|
+
destructiveHint: options.destructiveHint ?? false,
|
|
162
|
+
openWorldHint: options.openWorldHint ?? false,
|
|
163
|
+
...options.idempotentHint === void 0 ? {} : { idempotentHint: options.idempotentHint }
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
138
167
|
function queryText(description) {
|
|
139
168
|
return z.string().trim().min(1).max(QUERY_MAX_CHARS).describe(description);
|
|
140
169
|
}
|
|
@@ -380,6 +409,143 @@ var aivGenerateContentBrief = createTool({
|
|
|
380
409
|
}),
|
|
381
410
|
execute: async (input) => aivPost("/workflows/content-brief", input)
|
|
382
411
|
});
|
|
412
|
+
var trackerIdInput = z.string().trim().min(1).max(128).describe("Cite42 tracker ID returned by a tracker management or report tool.");
|
|
413
|
+
var trackerCadenceInput = z.object({
|
|
414
|
+
frequency: z.enum(["daily", "weekly"]),
|
|
415
|
+
timezone: z.string().trim().min(1).max(100).refine((timezone) => {
|
|
416
|
+
try {
|
|
417
|
+
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format();
|
|
418
|
+
return true;
|
|
419
|
+
} catch {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
}, "Timezone must be a valid IANA timezone.").describe("IANA timezone, for example Europe/London or America/New_York."),
|
|
423
|
+
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 day, where 0 is Sunday and 6 is Saturday.")
|
|
425
|
+
}).superRefine((cadence, context) => {
|
|
426
|
+
if (cadence.frequency === "weekly" && cadence.weekday === void 0) {
|
|
427
|
+
context.addIssue({
|
|
428
|
+
code: "custom",
|
|
429
|
+
path: ["weekday"],
|
|
430
|
+
message: "weekday is required for a weekly tracker"
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
});
|
|
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
|
+
]);
|
|
469
|
+
var trackerDefinitionFields = {
|
|
470
|
+
name: z.string().trim().min(1).max(100),
|
|
471
|
+
cadence: trackerCadenceInput,
|
|
472
|
+
items: z.array(trackerItemInput).min(1).max(25).describe("Primitive calls to execute and store on each scheduled run.")
|
|
473
|
+
};
|
|
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.";
|
|
475
|
+
var aivTrackerCreate = createTool({
|
|
476
|
+
id: "cite42_tracker_create",
|
|
477
|
+
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}`,
|
|
479
|
+
inputSchema: z.object(trackerDefinitionFields),
|
|
480
|
+
execute: async (input) => aivPost("/trackers", input)
|
|
481
|
+
});
|
|
482
|
+
var aivTrackerUpdate = createTool({
|
|
483
|
+
id: "cite42_tracker_update",
|
|
484
|
+
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}`,
|
|
486
|
+
inputSchema: z.object({ trackerId: trackerIdInput, ...trackerDefinitionFields }),
|
|
487
|
+
execute: async ({ trackerId, ...input }) => aivPatch(`/trackers/${encodeURIComponent(trackerId)}`, input)
|
|
488
|
+
});
|
|
489
|
+
var aivTrackerActivate = createTool({
|
|
490
|
+
id: "cite42_tracker_activate",
|
|
491
|
+
mcp: mutation("Activate Cite42 tracker", {
|
|
492
|
+
openWorldHint: true,
|
|
493
|
+
idempotentHint: true
|
|
494
|
+
}),
|
|
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}`,
|
|
496
|
+
inputSchema: z.object({
|
|
497
|
+
trackerId: trackerIdInput,
|
|
498
|
+
maxCostMicroPerRun: z.number().int().nonnegative().describe("Exact estimatedCostMicroPerRun returned by the latest tracker record.")
|
|
499
|
+
}),
|
|
500
|
+
execute: async ({ trackerId, maxCostMicroPerRun }) => aivPost(`/trackers/${encodeURIComponent(trackerId)}/activate`, { maxCostMicroPerRun })
|
|
501
|
+
});
|
|
502
|
+
var aivTrackerPause = createTool({
|
|
503
|
+
id: "cite42_tracker_pause",
|
|
504
|
+
mcp: mutation("Pause Cite42 tracker", { idempotentHint: true }),
|
|
505
|
+
description: "Pause future recurring calls for a tracker while preserving its configuration and stored run history. Free: costs 0 credits.",
|
|
506
|
+
inputSchema: z.object({ trackerId: trackerIdInput }),
|
|
507
|
+
execute: async ({ trackerId }) => aivPost(`/trackers/${encodeURIComponent(trackerId)}/pause`, {})
|
|
508
|
+
});
|
|
509
|
+
var aivTrackerDelete = createTool({
|
|
510
|
+
id: "cite42_tracker_delete",
|
|
511
|
+
mcp: mutation("Permanently delete Cite42 tracker", {
|
|
512
|
+
destructiveHint: true,
|
|
513
|
+
idempotentHint: true
|
|
514
|
+
}),
|
|
515
|
+
description: "Permanently delete a draft or paused tracker, including its saved configuration and run history. Active trackers must be paused first, and deletion is rejected while a run is queued or running. Financial usage and credit ledger records are retained. Free: costs 0 credits.",
|
|
516
|
+
inputSchema: z.object({ trackerId: trackerIdInput }),
|
|
517
|
+
execute: async ({ trackerId }) => aivDelete(`/trackers/${encodeURIComponent(trackerId)}`)
|
|
518
|
+
});
|
|
519
|
+
var aivTrackerRun = createTool({
|
|
520
|
+
id: "cite42_tracker_run",
|
|
521
|
+
mcp: mutation("Run Cite42 tracker now", {
|
|
522
|
+
openWorldHint: true,
|
|
523
|
+
idempotentHint: true
|
|
524
|
+
}),
|
|
525
|
+
description: `Queue one manual run using the tracker\u2019s saved primitive inputs. A repeated requestToken returns the same logical run. ${TRACKER_BILLING_DESCRIPTION}`,
|
|
526
|
+
inputSchema: z.object({
|
|
527
|
+
trackerId: trackerIdInput,
|
|
528
|
+
requestToken: z.string().trim().min(1).max(200).describe("Stable caller-generated token used to deduplicate a retried manual run.")
|
|
529
|
+
}),
|
|
530
|
+
execute: async ({ trackerId, requestToken }) => aivPost(`/trackers/${encodeURIComponent(trackerId)}/runs`, { requestToken })
|
|
531
|
+
});
|
|
532
|
+
var aivTrackers = createTool({
|
|
533
|
+
id: "cite42_trackers",
|
|
534
|
+
mcp: readOnly("List Cite42 trackers", false),
|
|
535
|
+
description: "List the connected account\u2019s trackers with status, cadence, primitive configuration, cost estimate, accepted per-run maximum, and next/last run times. Free: costs 0 credits.",
|
|
536
|
+
inputSchema: z.object({}),
|
|
537
|
+
execute: async () => aivGet("/trackers")
|
|
538
|
+
});
|
|
539
|
+
var aivTrackerReport = createTool({
|
|
540
|
+
id: "cite42_tracker_report",
|
|
541
|
+
mcp: readOnly("Read Cite42 tracker report", false),
|
|
542
|
+
description: "Read bounded stored run history for one tracker, including per-primitive results, changes from the prior comparable observation, statuses, sampled times, and costs. Free: costs 0 credits.",
|
|
543
|
+
inputSchema: z.object({
|
|
544
|
+
trackerId: trackerIdInput,
|
|
545
|
+
limit: z.number().int().min(1).max(20).optional().default(8).describe("Most recent runs to return, capped for LLM context.")
|
|
546
|
+
}),
|
|
547
|
+
execute: async ({ trackerId, limit }) => aivGet(`/trackers/${encodeURIComponent(trackerId)}/report?limit=${limit}`)
|
|
548
|
+
});
|
|
383
549
|
|
|
384
550
|
// src/server.ts
|
|
385
551
|
var CITE42_TOOLS = {
|
|
@@ -400,7 +566,15 @@ var CITE42_TOOLS = {
|
|
|
400
566
|
cite42_map_ai_prompts: aivMapAiPrompts,
|
|
401
567
|
cite42_analyze_competitor_content: aivAnalyzeCompetitorContent,
|
|
402
568
|
cite42_find_ai_citation_gaps: aivFindAiCitationGaps,
|
|
403
|
-
cite42_generate_content_brief: aivGenerateContentBrief
|
|
569
|
+
cite42_generate_content_brief: aivGenerateContentBrief,
|
|
570
|
+
cite42_tracker_create: aivTrackerCreate,
|
|
571
|
+
cite42_tracker_update: aivTrackerUpdate,
|
|
572
|
+
cite42_tracker_activate: aivTrackerActivate,
|
|
573
|
+
cite42_tracker_pause: aivTrackerPause,
|
|
574
|
+
cite42_tracker_delete: aivTrackerDelete,
|
|
575
|
+
cite42_tracker_run: aivTrackerRun,
|
|
576
|
+
cite42_trackers: aivTrackers,
|
|
577
|
+
cite42_tracker_report: aivTrackerReport
|
|
404
578
|
};
|
|
405
579
|
function buildCite42Server(version) {
|
|
406
580
|
return new MCPServer({
|
|
@@ -419,7 +593,7 @@ if (!resolveApiKey()) {
|
|
|
419
593
|
);
|
|
420
594
|
process.exit(1);
|
|
421
595
|
}
|
|
422
|
-
buildCite42Server("0.1.
|
|
596
|
+
buildCite42Server("0.1.17").startStdio().catch((error) => {
|
|
423
597
|
console.error("[@cite42/mcp] Fatal error:", error);
|
|
424
598
|
process.exit(1);
|
|
425
599
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cite42/mcp",
|
|
3
3
|
"mcpName": "dev.cite42/mcp",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.17",
|
|
5
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.",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"type": "module",
|