rankcontrol 0.8.3 → 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 +11 -10
- package/src/client.mjs +2 -2
- package/src/login.mjs +0 -3
- package/src/mcp.mjs +21 -14
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
|
@@ -233,9 +233,9 @@ export function runCli(argv) {
|
|
|
233
233
|
|
|
234
234
|
program
|
|
235
235
|
.command("competitor-add")
|
|
236
|
-
.description("Track a competitor (max 10)")
|
|
236
|
+
.description("Track a competitor (max 10); the site is resolved from the name when omitted")
|
|
237
237
|
.argument("<name>", "Competitor name")
|
|
238
|
-
.argument("
|
|
238
|
+
.argument("[websiteUrl]", "Competitor website URL (optional)")
|
|
239
239
|
.action((name, websiteUrl) =>
|
|
240
240
|
api.addCompetitor(name, websiteUrl).then(out).catch(fail)
|
|
241
241
|
);
|
|
@@ -492,11 +492,10 @@ export function runCli(argv) {
|
|
|
492
492
|
|
|
493
493
|
program
|
|
494
494
|
.command("reschedule <contentId> <date>")
|
|
495
|
-
.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)")
|
|
496
496
|
.action((contentId, date) => {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
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);
|
|
500
499
|
});
|
|
501
500
|
|
|
502
501
|
program
|
|
@@ -572,20 +571,22 @@ export function runCli(argv) {
|
|
|
572
571
|
|
|
573
572
|
program
|
|
574
573
|
.command("repurpose-channels")
|
|
575
|
-
.description("Connected Postiz channels (ids needed for repurpose-push)")
|
|
574
|
+
.description("Connected Postiz and Buffer channels (ids needed for repurpose-push)")
|
|
576
575
|
.action(() => api.repurposeChannels().then(out).catch(fail));
|
|
577
576
|
|
|
578
577
|
program
|
|
579
578
|
.command("repurpose-push <draftId>")
|
|
580
|
-
.description("Send a draft to Postiz (dry run unless --confirm)")
|
|
581
|
-
.option("--
|
|
582
|
-
.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")
|
|
583
583
|
.option("--date <iso>", "ISO time for --when schedule")
|
|
584
584
|
.option("--confirm", "Actually push (publicly visible)")
|
|
585
585
|
.action((draftId, opts) =>
|
|
586
586
|
api
|
|
587
587
|
.repurposePush({
|
|
588
588
|
draftId,
|
|
589
|
+
scheduler: opts.scheduler,
|
|
589
590
|
integrationIds: opts.channels ? list(opts.channels) : undefined,
|
|
590
591
|
scheduleType: opts.when,
|
|
591
592
|
date: opts.date,
|
package/src/client.mjs
CHANGED
|
@@ -124,8 +124,8 @@ export const api = {
|
|
|
124
124
|
articleSettings: () => request("GET", "/api/v1/settings/articles"),
|
|
125
125
|
updateArticleSettings: (opts) =>
|
|
126
126
|
request("POST", "/api/v1/settings/articles", opts),
|
|
127
|
-
reschedule: (contentId,
|
|
128
|
-
request("POST", "/api/v1/content/reschedule", { contentId,
|
|
127
|
+
reschedule: (contentId, date) =>
|
|
128
|
+
request("POST", "/api/v1/content/reschedule", { contentId, date }),
|
|
129
129
|
sitePages: () => request("GET", "/api/v1/links/pages"),
|
|
130
130
|
detectSiteLinks: (source, url) =>
|
|
131
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",
|
|
@@ -236,10 +236,13 @@ export async function startMcpServer() {
|
|
|
236
236
|
|
|
237
237
|
server.tool(
|
|
238
238
|
"add_competitor",
|
|
239
|
-
"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.",
|
|
240
240
|
{
|
|
241
241
|
name: z.string().describe("Competitor name"),
|
|
242
|
-
websiteUrl: z
|
|
242
|
+
websiteUrl: z
|
|
243
|
+
.string()
|
|
244
|
+
.optional()
|
|
245
|
+
.describe("Competitor website URL (optional; resolved from the name when omitted)"),
|
|
243
246
|
},
|
|
244
247
|
run(({ name, websiteUrl }) => api.addCompetitor(name, websiteUrl))
|
|
245
248
|
);
|
|
@@ -446,7 +449,7 @@ export async function startMcpServer() {
|
|
|
446
449
|
|
|
447
450
|
const articleSettingsShape = {
|
|
448
451
|
autoPublish: z.boolean().optional(),
|
|
449
|
-
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."),
|
|
450
453
|
includeInfographics: z.boolean().optional().describe("AI hero + section images"),
|
|
451
454
|
titleInHeroImage: z.boolean().optional().describe("Write the post title into the hero image"),
|
|
452
455
|
includeSectionInfographics: z.boolean().optional().describe("Informational panels in 2,000+ word articles"),
|
|
@@ -481,12 +484,12 @@ export async function startMcpServer() {
|
|
|
481
484
|
|
|
482
485
|
server.tool(
|
|
483
486
|
"reschedule_article",
|
|
484
|
-
"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.",
|
|
485
488
|
{
|
|
486
489
|
contentId: z.string(),
|
|
487
|
-
|
|
490
|
+
date: z.string().describe("Target day as YYYY-MM-DD in the workspace's timezone"),
|
|
488
491
|
},
|
|
489
|
-
run(({ contentId,
|
|
492
|
+
run(({ contentId, date }) => api.reschedule(contentId, date))
|
|
490
493
|
);
|
|
491
494
|
|
|
492
495
|
server.tool(
|
|
@@ -579,26 +582,30 @@ export async function startMcpServer() {
|
|
|
579
582
|
|
|
580
583
|
server.tool(
|
|
581
584
|
"list_postiz_channels",
|
|
582
|
-
"Connected
|
|
585
|
+
"Connected social channels across Postiz and Buffer (scheduler, id, name, platform). Channel ids are required by push_repurpose_draft.",
|
|
583
586
|
{},
|
|
584
587
|
run(() => api.repurposeChannels())
|
|
585
588
|
);
|
|
586
589
|
|
|
587
590
|
server.tool(
|
|
588
591
|
"push_repurpose_draft",
|
|
589
|
-
"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.",
|
|
590
593
|
{
|
|
591
594
|
draftId: z.string(),
|
|
595
|
+
scheduler: z
|
|
596
|
+
.enum(["postiz", "buffer"])
|
|
597
|
+
.optional()
|
|
598
|
+
.describe("Which scheduler; required when both are connected"),
|
|
592
599
|
integrationIds: z
|
|
593
600
|
.array(z.string())
|
|
594
601
|
.optional()
|
|
595
602
|
.describe(
|
|
596
|
-
"
|
|
603
|
+
"Channel ids from list_postiz_channels (required with confirm)"
|
|
597
604
|
),
|
|
598
605
|
scheduleType: z
|
|
599
|
-
.enum(["now", "schedule", "draft"])
|
|
606
|
+
.enum(["now", "schedule", "draft", "queue"])
|
|
600
607
|
.optional()
|
|
601
|
-
.describe("Default now"),
|
|
608
|
+
.describe("Default now; queue is Buffer only"),
|
|
602
609
|
date: z.string().optional().describe("ISO time for scheduleType=schedule"),
|
|
603
610
|
confirm: z.boolean().optional(),
|
|
604
611
|
},
|
|
@@ -662,13 +669,13 @@ export async function startMcpServer() {
|
|
|
662
669
|
|
|
663
670
|
server.tool(
|
|
664
671
|
"list_backlinks",
|
|
665
|
-
"The workspace's backlink table, newest first.",
|
|
672
|
+
"The workspace's backlink table, newest first. Each row carries its source (index, outreach, managed, network).",
|
|
666
673
|
{
|
|
667
674
|
status: z
|
|
668
675
|
.string()
|
|
669
676
|
.optional()
|
|
670
677
|
.describe(
|
|
671
|
-
"Filter: discovered, verified, lost, identified, contacted, replied, link_placed, rejected"
|
|
678
|
+
"Filter: discovered, verified, lost, placed, identified, contacted, replied, link_placed, rejected"
|
|
672
679
|
),
|
|
673
680
|
},
|
|
674
681
|
run(({ status }) => api.backlinks(status))
|