rankcontrol 0.8.2 → 0.9.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 +3 -3
- package/package.json +4 -1
- package/src/cli.mjs +16 -10
- package/src/client.mjs +3 -2
- package/src/login.mjs +0 -3
- package/src/mcp.mjs +29 -15
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ npx rankcontrol query-edit <queryId> "new text"
|
|
|
43
43
|
npx rankcontrol query-track <queryId> off # pause weekly checks (frees a slot)
|
|
44
44
|
npx rankcontrol query-remove <queryId>
|
|
45
45
|
npx rankcontrol competitors
|
|
46
|
-
npx rankcontrol competitor-add "Acme"
|
|
46
|
+
npx rankcontrol competitor-add "Acme" # site resolved from the name; pass a URL to override
|
|
47
47
|
npx rankcontrol competitor-remove <competitorId>
|
|
48
48
|
|
|
49
49
|
# Content pipeline
|
|
@@ -96,8 +96,8 @@ npx rankcontrol network-remove-placement <placementId> --confirm
|
|
|
96
96
|
npx rankcontrol repurpose [contentId] # published articles + social drafts (8 platforms)
|
|
97
97
|
npx rankcontrol repurpose-generate <contentId> --platforms twitter --confirm
|
|
98
98
|
npx rankcontrol repurpose-edit <draftId> --title "New hook"
|
|
99
|
-
npx rankcontrol repurpose-channels # Postiz channel ids
|
|
100
|
-
npx rankcontrol repurpose-push <draftId> --channels <id> --confirm
|
|
99
|
+
npx rankcontrol repurpose-channels # Postiz and Buffer channel ids
|
|
100
|
+
npx rankcontrol repurpose-push <draftId> --channels <id> --confirm # --scheduler postiz|buffer when both are connected
|
|
101
101
|
npx rankcontrol repurpose-mark-posted <draftId>
|
|
102
102
|
npx rankcontrol social # Reddit/X threads where your articles fit
|
|
103
103
|
npx rankcontrol social-stats
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rankcontrol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "RankControl CLI + MCP server: drive your SEO/AI-visibility workspace from the terminal or any AI agent",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"prepublishOnly": "node ../../scripts/check-package-parity.mjs"
|
|
8
|
+
},
|
|
6
9
|
"homepage": "https://rctrl.com",
|
|
7
10
|
"type": "module",
|
|
8
11
|
"bin": {
|
package/src/cli.mjs
CHANGED
|
@@ -63,6 +63,11 @@ export function runCli(argv) {
|
|
|
63
63
|
.description("Composite visibility score blending AI citation rate with Google and Bing rank share, with subscores")
|
|
64
64
|
.action(() => api.visibilityScore().then(out).catch(fail));
|
|
65
65
|
|
|
66
|
+
program
|
|
67
|
+
.command("expedition")
|
|
68
|
+
.description("First-run setup progress: five acts with per-step completion and the current act")
|
|
69
|
+
.action(() => api.expedition().then(out).catch(fail));
|
|
70
|
+
|
|
66
71
|
program
|
|
67
72
|
.command("visibility")
|
|
68
73
|
.description("Daily AI visibility score trend from the stored weekly citation checks")
|
|
@@ -228,9 +233,9 @@ export function runCli(argv) {
|
|
|
228
233
|
|
|
229
234
|
program
|
|
230
235
|
.command("competitor-add")
|
|
231
|
-
.description("Track a competitor (max 10)")
|
|
236
|
+
.description("Track a competitor (max 10); the site is resolved from the name when omitted")
|
|
232
237
|
.argument("<name>", "Competitor name")
|
|
233
|
-
.argument("
|
|
238
|
+
.argument("[websiteUrl]", "Competitor website URL (optional)")
|
|
234
239
|
.action((name, websiteUrl) =>
|
|
235
240
|
api.addCompetitor(name, websiteUrl).then(out).catch(fail)
|
|
236
241
|
);
|
|
@@ -487,11 +492,10 @@ export function runCli(argv) {
|
|
|
487
492
|
|
|
488
493
|
program
|
|
489
494
|
.command("reschedule <contentId> <date>")
|
|
490
|
-
.description("Move a planned article to a day (YYYY-MM-DD
|
|
495
|
+
.description("Move a planned article to a day (YYYY-MM-DD in the workspace's timezone; needs the flexible schedule)")
|
|
491
496
|
.action((contentId, date) => {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
return api.reschedule(contentId, day.getTime()).then(out).catch(fail);
|
|
497
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) return fail(new Error("Date must be YYYY-MM-DD"));
|
|
498
|
+
return api.reschedule(contentId, date).then(out).catch(fail);
|
|
495
499
|
});
|
|
496
500
|
|
|
497
501
|
program
|
|
@@ -567,20 +571,22 @@ export function runCli(argv) {
|
|
|
567
571
|
|
|
568
572
|
program
|
|
569
573
|
.command("repurpose-channels")
|
|
570
|
-
.description("Connected Postiz channels (ids needed for repurpose-push)")
|
|
574
|
+
.description("Connected Postiz and Buffer channels (ids needed for repurpose-push)")
|
|
571
575
|
.action(() => api.repurposeChannels().then(out).catch(fail));
|
|
572
576
|
|
|
573
577
|
program
|
|
574
578
|
.command("repurpose-push <draftId>")
|
|
575
|
-
.description("Send a draft to Postiz (dry run unless --confirm)")
|
|
576
|
-
.option("--
|
|
577
|
-
.option("--
|
|
579
|
+
.description("Send a draft to Postiz or Buffer (dry run unless --confirm)")
|
|
580
|
+
.option("--scheduler <postiz|buffer>", "Which scheduler (required when both are connected)")
|
|
581
|
+
.option("--channels <id1,id2>", "Channel ids (see repurpose-channels)")
|
|
582
|
+
.option("--when <now|schedule|draft|queue>", "Mode; queue is Buffer only", "now")
|
|
578
583
|
.option("--date <iso>", "ISO time for --when schedule")
|
|
579
584
|
.option("--confirm", "Actually push (publicly visible)")
|
|
580
585
|
.action((draftId, opts) =>
|
|
581
586
|
api
|
|
582
587
|
.repurposePush({
|
|
583
588
|
draftId,
|
|
589
|
+
scheduler: opts.scheduler,
|
|
584
590
|
integrationIds: opts.channels ? list(opts.channels) : undefined,
|
|
585
591
|
scheduleType: opts.when,
|
|
586
592
|
date: opts.date,
|
package/src/client.mjs
CHANGED
|
@@ -103,6 +103,7 @@ export const api = {
|
|
|
103
103
|
return request("GET", `/api/v1/citations${qs ? `?${qs}` : ""}`);
|
|
104
104
|
},
|
|
105
105
|
visibilityScore: () => request("GET", "/api/v1/visibility/score"),
|
|
106
|
+
expedition: () => request("GET", "/api/v1/expedition"),
|
|
106
107
|
planningCapacity: () => request("GET", "/api/v1/content/planning-capacity"),
|
|
107
108
|
jobs: (limit = 20) => request("GET", `/api/v1/jobs?limit=${limit}`),
|
|
108
109
|
content: () => request("GET", "/api/v1/content"),
|
|
@@ -123,8 +124,8 @@ export const api = {
|
|
|
123
124
|
articleSettings: () => request("GET", "/api/v1/settings/articles"),
|
|
124
125
|
updateArticleSettings: (opts) =>
|
|
125
126
|
request("POST", "/api/v1/settings/articles", opts),
|
|
126
|
-
reschedule: (contentId,
|
|
127
|
-
request("POST", "/api/v1/content/reschedule", { contentId,
|
|
127
|
+
reschedule: (contentId, date) =>
|
|
128
|
+
request("POST", "/api/v1/content/reschedule", { contentId, date }),
|
|
128
129
|
sitePages: () => request("GET", "/api/v1/links/pages"),
|
|
129
130
|
detectSiteLinks: (source, url) =>
|
|
130
131
|
request("POST", "/api/v1/links/detect", { source, url }),
|
package/src/login.mjs
CHANGED
|
@@ -13,13 +13,10 @@ const DEFAULT_SCOPES = [
|
|
|
13
13
|
"read:content",
|
|
14
14
|
"read:analytics",
|
|
15
15
|
"write:content",
|
|
16
|
-
"write:queries",
|
|
17
16
|
"write:backlinks",
|
|
18
17
|
"write:social",
|
|
19
18
|
"write:publish",
|
|
20
|
-
"manage:integrations",
|
|
21
19
|
"manage:org",
|
|
22
|
-
"trigger:agents",
|
|
23
20
|
];
|
|
24
21
|
|
|
25
22
|
function openBrowser(url) {
|
package/src/mcp.mjs
CHANGED
|
@@ -29,7 +29,7 @@ const plannedTitle = z.object({
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
export async function startMcpServer() {
|
|
32
|
-
const server = new McpServer({ name: "rankcontrol", version: "0.
|
|
32
|
+
const server = new McpServer({ name: "rankcontrol", version: "0.9.0" });
|
|
33
33
|
|
|
34
34
|
server.tool(
|
|
35
35
|
"get_overview_funnel",
|
|
@@ -62,6 +62,13 @@ export async function startMcpServer() {
|
|
|
62
62
|
run(() => api.visibilityScore())
|
|
63
63
|
);
|
|
64
64
|
|
|
65
|
+
server.tool(
|
|
66
|
+
"get_setup_progress",
|
|
67
|
+
"First-run setup progress for the workspace: five acts from first article live through analytics, each with per-step completion, plus the current act. Use to tell the user what remains to finish setting up.",
|
|
68
|
+
{},
|
|
69
|
+
run(() => api.expedition())
|
|
70
|
+
);
|
|
71
|
+
|
|
65
72
|
server.tool(
|
|
66
73
|
"get_citability_report",
|
|
67
74
|
"Published pages ranked by citability score (how extractable each page is for AI answers), worst first, each with its top open fixes. Use to decide which articles to improve.",
|
|
@@ -229,10 +236,13 @@ export async function startMcpServer() {
|
|
|
229
236
|
|
|
230
237
|
server.tool(
|
|
231
238
|
"add_competitor",
|
|
232
|
-
"Track a competitor (max 10). They enter weekly share-of-voice comparisons against the org's own citation rate.",
|
|
239
|
+
"Track a competitor (max 10). They enter weekly share-of-voice comparisons against the org's own citation rate. Omit websiteUrl to have the site resolved from the name.",
|
|
233
240
|
{
|
|
234
241
|
name: z.string().describe("Competitor name"),
|
|
235
|
-
websiteUrl: z
|
|
242
|
+
websiteUrl: z
|
|
243
|
+
.string()
|
|
244
|
+
.optional()
|
|
245
|
+
.describe("Competitor website URL (optional; resolved from the name when omitted)"),
|
|
236
246
|
},
|
|
237
247
|
run(({ name, websiteUrl }) => api.addCompetitor(name, websiteUrl))
|
|
238
248
|
);
|
|
@@ -432,14 +442,14 @@ export async function startMcpServer() {
|
|
|
432
442
|
|
|
433
443
|
server.tool(
|
|
434
444
|
"get_article_settings",
|
|
435
|
-
"The workspace article policy: per-article defaults (auto-write, auto-publish, images, title-in-hero, section infographics, Related Reading, YouTube, emojis, link counts, global instructions) plus the flexible-scheduling flag.",
|
|
445
|
+
"The workspace article policy: per-article defaults (auto-write, auto-publish, images, title-in-hero, section infographics, Related Reading, YouTube, emojis, link counts, global instructions) plus the flexible-scheduling flag. News and trending articles always publish manually, regardless of auto-publish.",
|
|
436
446
|
{},
|
|
437
447
|
run(() => api.articleSettings())
|
|
438
448
|
);
|
|
439
449
|
|
|
440
450
|
const articleSettingsShape = {
|
|
441
451
|
autoPublish: z.boolean().optional(),
|
|
442
|
-
autoGenerate: z.boolean().optional().describe("Off = fully manual mode: articles are written only on demand (generate_article) and publish only from the editor"),
|
|
452
|
+
autoGenerate: z.boolean().optional().describe("Off = fully manual mode: articles are written only on demand (generate_article) and publish only from the editor. Turning it on needs a connected publishing destination."),
|
|
443
453
|
includeInfographics: z.boolean().optional().describe("AI hero + section images"),
|
|
444
454
|
titleInHeroImage: z.boolean().optional().describe("Write the post title into the hero image"),
|
|
445
455
|
includeSectionInfographics: z.boolean().optional().describe("Informational panels in 2,000+ word articles"),
|
|
@@ -474,12 +484,12 @@ export async function startMcpServer() {
|
|
|
474
484
|
|
|
475
485
|
server.tool(
|
|
476
486
|
"reschedule_article",
|
|
477
|
-
"Move a PLANNED article to another day (requires the flexible schedule; max 5/day; the exact hour is placed automatically). Pass the target day as
|
|
487
|
+
"Move a PLANNED article to another day (requires the flexible schedule; max 5/day; the exact hour is placed automatically). Pass the target day as YYYY-MM-DD in the workspace's timezone.",
|
|
478
488
|
{
|
|
479
489
|
contentId: z.string(),
|
|
480
|
-
|
|
490
|
+
date: z.string().describe("Target day as YYYY-MM-DD in the workspace's timezone"),
|
|
481
491
|
},
|
|
482
|
-
run(({ contentId,
|
|
492
|
+
run(({ contentId, date }) => api.reschedule(contentId, date))
|
|
483
493
|
);
|
|
484
494
|
|
|
485
495
|
server.tool(
|
|
@@ -572,26 +582,30 @@ export async function startMcpServer() {
|
|
|
572
582
|
|
|
573
583
|
server.tool(
|
|
574
584
|
"list_postiz_channels",
|
|
575
|
-
"Connected
|
|
585
|
+
"Connected social channels across Postiz and Buffer (scheduler, id, name, platform). Channel ids are required by push_repurpose_draft.",
|
|
576
586
|
{},
|
|
577
587
|
run(() => api.repurposeChannels())
|
|
578
588
|
);
|
|
579
589
|
|
|
580
590
|
server.tool(
|
|
581
591
|
"push_repurpose_draft",
|
|
582
|
-
"Send a repurpose draft to connected
|
|
592
|
+
"Send a repurpose draft to a connected scheduler's channels: Postiz or Buffer (post now, schedule, save as a draft, or add to Buffer's queue). PUBLICLY VISIBLE side effect: defaults to a DRY RUN; a human should approve before calling again with confirm=true.",
|
|
583
593
|
{
|
|
584
594
|
draftId: z.string(),
|
|
595
|
+
scheduler: z
|
|
596
|
+
.enum(["postiz", "buffer"])
|
|
597
|
+
.optional()
|
|
598
|
+
.describe("Which scheduler; required when both are connected"),
|
|
585
599
|
integrationIds: z
|
|
586
600
|
.array(z.string())
|
|
587
601
|
.optional()
|
|
588
602
|
.describe(
|
|
589
|
-
"
|
|
603
|
+
"Channel ids from list_postiz_channels (required with confirm)"
|
|
590
604
|
),
|
|
591
605
|
scheduleType: z
|
|
592
|
-
.enum(["now", "schedule", "draft"])
|
|
606
|
+
.enum(["now", "schedule", "draft", "queue"])
|
|
593
607
|
.optional()
|
|
594
|
-
.describe("Default now"),
|
|
608
|
+
.describe("Default now; queue is Buffer only"),
|
|
595
609
|
date: z.string().optional().describe("ISO time for scheduleType=schedule"),
|
|
596
610
|
confirm: z.boolean().optional(),
|
|
597
611
|
},
|
|
@@ -655,13 +669,13 @@ export async function startMcpServer() {
|
|
|
655
669
|
|
|
656
670
|
server.tool(
|
|
657
671
|
"list_backlinks",
|
|
658
|
-
"The workspace's backlink table, newest first.",
|
|
672
|
+
"The workspace's backlink table, newest first. Each row carries its source (index, outreach, managed, network).",
|
|
659
673
|
{
|
|
660
674
|
status: z
|
|
661
675
|
.string()
|
|
662
676
|
.optional()
|
|
663
677
|
.describe(
|
|
664
|
-
"Filter: discovered, verified, lost, identified, contacted, replied, link_placed, rejected"
|
|
678
|
+
"Filter: discovered, verified, lost, placed, identified, contacted, replied, link_placed, rejected"
|
|
665
679
|
),
|
|
666
680
|
},
|
|
667
681
|
run(({ status }) => api.backlinks(status))
|