@jclvsh/dropspace 2.3.1 → 2.6.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/dist/cli.js +3 -1
- package/dist/commands/boosts.d.ts +3 -0
- package/dist/commands/boosts.js +90 -0
- package/dist/index.js +3 -1
- package/dist/tools/boosts.d.ts +8 -0
- package/dist/tools/boosts.js +105 -0
- package/dist/tools/launches.js +1 -1
- package/dist/types.d.ts +25 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13,9 +13,10 @@ import { registerDropspaceCommands } from "./commands/dropspace.js";
|
|
|
13
13
|
import { registerUsageCommands } from "./commands/usage.js";
|
|
14
14
|
import { registerCreditCommands } from "./commands/credits.js";
|
|
15
15
|
import { registerMediaCommands } from "./commands/media.js";
|
|
16
|
+
import { registerBoostCommands } from "./commands/boosts.js";
|
|
16
17
|
const program = new Command()
|
|
17
18
|
.name("dropspace")
|
|
18
|
-
.description("dropspace CLI - manage launches, personas, and more")
|
|
19
|
+
.description("dropspace CLI - manage launches, personas, boosts, and more")
|
|
19
20
|
.version(pkg.version)
|
|
20
21
|
.option("--json", "output as JSON (default)")
|
|
21
22
|
.option("--format <fmt>", "output format: json, table", "json");
|
|
@@ -30,6 +31,7 @@ try {
|
|
|
30
31
|
registerUsageCommands(program, client);
|
|
31
32
|
registerCreditCommands(program, client);
|
|
32
33
|
registerMediaCommands(program, client);
|
|
34
|
+
registerBoostCommands(program, client);
|
|
33
35
|
await program.parseAsync(process.argv);
|
|
34
36
|
}
|
|
35
37
|
catch (error) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { formatOutput, handleError, getFormat } from "../cli-utils.js";
|
|
2
|
+
export function registerBoostCommands(program, client) {
|
|
3
|
+
const boosts = program.command("boosts").alias("b").description("manage paid boosts on meta");
|
|
4
|
+
boosts
|
|
5
|
+
.command("create")
|
|
6
|
+
.description("create a paid boost on meta for an organic facebook or instagram launch (starts PAUSED)")
|
|
7
|
+
.requiredOption("-l, --launch-id <id>", "launch id with a successful facebook OR instagram post")
|
|
8
|
+
.requiredOption("-b, --daily-budget-cents <n>", "daily budget in cents (100 - 10000 = $1 - $100)")
|
|
9
|
+
.requiredOption("-d, --duration-days <n>", "campaign duration in days (1 - 7)")
|
|
10
|
+
.option("--platform <platform>", "ad platform: facebook | instagram", "facebook")
|
|
11
|
+
.option("--objective <objective>", "campaign objective (phase 1: engagement)", "engagement")
|
|
12
|
+
.option("--ad-account-id <id>", "explicit meta ad account id (e.g. 'act_123'); defaults to the first active one")
|
|
13
|
+
.action(async (opts) => {
|
|
14
|
+
try {
|
|
15
|
+
const body = {
|
|
16
|
+
platform: opts.platform ?? "facebook",
|
|
17
|
+
daily_budget_cents: parseInt(opts.dailyBudgetCents, 10),
|
|
18
|
+
duration_days: parseInt(opts.durationDays, 10),
|
|
19
|
+
objective: opts.objective ?? "engagement",
|
|
20
|
+
};
|
|
21
|
+
if (opts.adAccountId)
|
|
22
|
+
body.ad_account_id = opts.adAccountId;
|
|
23
|
+
const result = await client.post(`/launches/${opts.launchId}/boost`, body);
|
|
24
|
+
formatOutput(result, getFormat(program.opts()));
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
handleError(error);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
boosts
|
|
31
|
+
.command("list <launch-id>")
|
|
32
|
+
.description("list all boosts attached to a specific launch")
|
|
33
|
+
.action(async (launchId) => {
|
|
34
|
+
try {
|
|
35
|
+
const result = await client.get(`/launches/${launchId}/boost`);
|
|
36
|
+
formatOutput(result, getFormat(program.opts()));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
handleError(error);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
boosts
|
|
43
|
+
.command("get <launch-id> <boost-id>")
|
|
44
|
+
.description("get a boost by id with current performance metrics")
|
|
45
|
+
.action(async (launchId, boostId) => {
|
|
46
|
+
try {
|
|
47
|
+
const result = await client.get(`/launches/${launchId}/boost/${boostId}`);
|
|
48
|
+
formatOutput(result, getFormat(program.opts()));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
handleError(error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
boosts
|
|
55
|
+
.command("activate <launch-id> <boost-id>")
|
|
56
|
+
.description("flip a PAUSED boost to ACTIVE on meta — ad spend starts immediately")
|
|
57
|
+
.action(async (launchId, boostId) => {
|
|
58
|
+
try {
|
|
59
|
+
const result = await client.post(`/launches/${launchId}/boost/${boostId}/activate`, {});
|
|
60
|
+
formatOutput(result, getFormat(program.opts()));
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
handleError(error);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
boosts
|
|
67
|
+
.command("cancel <launch-id> <boost-id>")
|
|
68
|
+
.description("cancel a boost — deletes the campaign on meta and soft-deletes the record")
|
|
69
|
+
.action(async (launchId, boostId) => {
|
|
70
|
+
try {
|
|
71
|
+
await client.delete(`/launches/${launchId}/boost/${boostId}`);
|
|
72
|
+
formatOutput({ success: true, boost_id: boostId }, getFormat(program.opts()));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
handleError(error);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
boosts
|
|
79
|
+
.command("performance <launch-id> <boost-id>")
|
|
80
|
+
.description("fetch latest performance metrics (impressions, clicks, reach, spend)")
|
|
81
|
+
.action(async (launchId, boostId) => {
|
|
82
|
+
try {
|
|
83
|
+
const result = await client.get(`/launches/${launchId}/boost/${boostId}`);
|
|
84
|
+
formatOutput(result, getFormat(program.opts()));
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
handleError(error);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -11,9 +11,10 @@ import { registerDropspaceTools } from "./tools/dropspace.js";
|
|
|
11
11
|
import { registerUsageTools } from "./tools/usage.js";
|
|
12
12
|
import { registerCreditTools } from "./tools/credits.js";
|
|
13
13
|
import { registerMediaTools } from "./tools/media.js";
|
|
14
|
+
import { registerBoostTools } from "./tools/boosts.js";
|
|
14
15
|
const server = new McpServer({
|
|
15
16
|
name: "dropspace",
|
|
16
|
-
version: "2.
|
|
17
|
+
version: "2.5.0",
|
|
17
18
|
});
|
|
18
19
|
const client = new DropspaceClient();
|
|
19
20
|
registerLaunchTools(server, client);
|
|
@@ -25,5 +26,6 @@ registerDropspaceTools(server, client);
|
|
|
25
26
|
registerUsageTools(server, client);
|
|
26
27
|
registerCreditTools(server, client);
|
|
27
28
|
registerMediaTools(server, client);
|
|
29
|
+
registerBoostTools(server, client);
|
|
28
30
|
const transport = new StdioServerTransport();
|
|
29
31
|
await server.connect(transport);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launch boost MCP tools — paid amplification of organic Facebook launches.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1: facebook only, engagement objective, auto-targeting. API key auth.
|
|
5
|
+
*/
|
|
6
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import { DropspaceClient } from "../client.js";
|
|
8
|
+
export declare function registerBoostTools(server: McpServer, client: DropspaceClient): void;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launch boost MCP tools — paid amplification of organic Facebook launches.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1: facebook only, engagement objective, auto-targeting. API key auth.
|
|
5
|
+
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
const platformSchema = z.enum(["facebook", "instagram"]).describe("ad platform — facebook or instagram (IG boost requires the IG business account linked to one of the user's facebook pages)");
|
|
8
|
+
const objectiveSchema = z.literal("engagement").describe("campaign objective (phase 1: engagement only)");
|
|
9
|
+
const launchIdParam = z.string().uuid().describe("launch ID to boost — must have a successful facebook post");
|
|
10
|
+
const boostIdParam = z.string().uuid().describe("boost ID");
|
|
11
|
+
export function registerBoostTools(server, client) {
|
|
12
|
+
server.tool("create_boost", "create a paid boost on meta for an organic facebook or instagram launch. boost starts PAUSED — call activate_boost to start ad spend.", {
|
|
13
|
+
launch_id: launchIdParam,
|
|
14
|
+
daily_budget_cents: z
|
|
15
|
+
.number()
|
|
16
|
+
.int()
|
|
17
|
+
.min(100)
|
|
18
|
+
.max(10000)
|
|
19
|
+
.describe("daily ad budget in cents ($1 - $100). billed by meta to the user's ad account."),
|
|
20
|
+
duration_days: z.number().int().min(1).max(7).describe("how many days the boost runs (1-7)"),
|
|
21
|
+
platform: platformSchema.optional(),
|
|
22
|
+
objective: objectiveSchema.optional(),
|
|
23
|
+
ad_account_id: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("explicit meta ad account id (e.g. 'act_123'). defaults to the first active ad account."),
|
|
27
|
+
}, async (args) => {
|
|
28
|
+
try {
|
|
29
|
+
const result = await client.post(`/launches/${args.launch_id}/boost`, {
|
|
30
|
+
platform: args.platform ?? "facebook",
|
|
31
|
+
daily_budget_cents: args.daily_budget_cents,
|
|
32
|
+
duration_days: args.duration_days,
|
|
33
|
+
objective: args.objective ?? "engagement",
|
|
34
|
+
...(args.ad_account_id ? { ad_account_id: args.ad_account_id } : {}),
|
|
35
|
+
});
|
|
36
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
server.tool("list_boosts", "list all boosts attached to a specific launch", { launch_id: launchIdParam }, async ({ launch_id }) => {
|
|
46
|
+
try {
|
|
47
|
+
const result = await client.get(`/launches/${launch_id}/boost`);
|
|
48
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return {
|
|
52
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
53
|
+
isError: true,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
server.tool("get_boost", "fetch a single boost by id with current performance metrics", { launch_id: launchIdParam, boost_id: boostIdParam }, async ({ launch_id, boost_id }) => {
|
|
58
|
+
try {
|
|
59
|
+
const result = await client.get(`/launches/${launch_id}/boost/${boost_id}`);
|
|
60
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
return {
|
|
64
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
65
|
+
isError: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
server.tool("activate_boost", "flip a PAUSED boost to ACTIVE on meta. ad spend begins immediately. cannot be undone — use cancel_boost to stop.", { launch_id: launchIdParam, boost_id: boostIdParam }, async ({ launch_id, boost_id }) => {
|
|
70
|
+
try {
|
|
71
|
+
const result = await client.post(`/launches/${launch_id}/boost/${boost_id}/activate`, {});
|
|
72
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
77
|
+
isError: true,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
server.tool("cancel_boost", "cancel a boost — deletes the campaign on meta and soft-deletes the dropspace record. final, irreversible.", { launch_id: launchIdParam, boost_id: boostIdParam }, async ({ launch_id, boost_id }) => {
|
|
82
|
+
try {
|
|
83
|
+
await client.delete(`/launches/${launch_id}/boost/${boost_id}`);
|
|
84
|
+
return { content: [{ type: "text", text: JSON.stringify({ success: true, boost_id }, null, 2) }] };
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
return {
|
|
88
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
89
|
+
isError: true,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
server.tool("get_boost_performance", "fetch latest performance metrics for a boost (impressions, clicks, reach, spend). triggers a meta-side refresh if data is stale.", { launch_id: launchIdParam, boost_id: boostIdParam }, async ({ launch_id, boost_id }) => {
|
|
94
|
+
try {
|
|
95
|
+
const result = await client.get(`/launches/${launch_id}/boost/${boost_id}`);
|
|
96
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
101
|
+
isError: true,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
package/dist/tools/launches.js
CHANGED
|
@@ -102,7 +102,7 @@ export function registerLaunchTools(server, client) {
|
|
|
102
102
|
platform_contents: z
|
|
103
103
|
.record(z.string(), z.record(z.string(), z.unknown()))
|
|
104
104
|
.optional()
|
|
105
|
-
.describe('per-platform content — each value needs "content" (string). Reddit also requires "title" (max 300). Twitter supports "thread" (string[], max 6, each ≤280 or ≤25,000 for X Premium) instead of "content". TikTok supports "tiktok_settings": { "privacy_level": "PUBLIC_TO_EVERYONE"|"FOLLOWER_OF_CREATOR"|"MUTUAL_FOLLOW_FRIENDS"|"SELF_ONLY" (required before publish), "allow_comments"?, "allow_duet"?, "allow_stitch"?, "is_commercial"?, "is_your_brand"?, "is_branded_content"?, "auto_add_music"? (booleans) }. YouTube requires video and supports "title" (max 100 chars). character limits: Twitter 280/tweet (25,000 for X Premium), LinkedIn 3000, Instagram 2200, Reddit 3000, Facebook 3000, TikTok 4000, YouTube 5000 (description). X Premium status is auto-detected from the connected account. mutually exclusive with custom_content'),
|
|
105
|
+
.describe('per-platform content — each value needs "content" (string). Reddit also requires "title" (max 300). Twitter supports "thread" (string[], max 6, each ≤280 or ≤25,000 for X Premium) instead of "content". TikTok supports "tiktok_settings": { "publish_mode"?: "direct"|"draft" (default "direct"; "draft" uploads to the TikTok inbox to finish and publish in-app), "privacy_level": "PUBLIC_TO_EVERYONE"|"FOLLOWER_OF_CREATOR"|"MUTUAL_FOLLOW_FRIENDS"|"SELF_ONLY" (required before publish in direct mode; not required for draft), "allow_comments"?, "allow_duet"?, "allow_stitch"?, "is_commercial"?, "is_your_brand"?, "is_branded_content"?, "auto_add_music"? (booleans, direct mode) }. YouTube requires video and supports "title" (max 100 chars). character limits: Twitter 280/tweet (25,000 for X Premium), LinkedIn 3000, Instagram 2200, Reddit 3000, Facebook 3000, TikTok 4000, YouTube 5000 (description). X Premium status is auto-detected from the connected account. mutually exclusive with custom_content'),
|
|
106
106
|
custom_content: z
|
|
107
107
|
.union([z.string().min(1), z.array(z.string()).min(1).max(6)])
|
|
108
108
|
.optional()
|
package/dist/types.d.ts
CHANGED
|
@@ -316,3 +316,28 @@ export interface MediaGenerationJob {
|
|
|
316
316
|
}
|
|
317
317
|
/** Response when rotating an API key (includes new raw key, shown once) */
|
|
318
318
|
export type RotateApiKeyResponse = CreateApiKeyResponse;
|
|
319
|
+
/** Launch boost resource */
|
|
320
|
+
export interface Boost {
|
|
321
|
+
id: string;
|
|
322
|
+
launch_id: string;
|
|
323
|
+
platform: "facebook" | "instagram";
|
|
324
|
+
post_id: string;
|
|
325
|
+
status: "paused" | "active" | "completed" | "failed" | "canceled";
|
|
326
|
+
objective: "engagement";
|
|
327
|
+
daily_budget_cents: number;
|
|
328
|
+
duration_days: number;
|
|
329
|
+
meta_ad_account_id: string;
|
|
330
|
+
meta_campaign_id: string | null;
|
|
331
|
+
meta_adset_id: string | null;
|
|
332
|
+
meta_ad_id: string | null;
|
|
333
|
+
impressions: number | null;
|
|
334
|
+
clicks: number | null;
|
|
335
|
+
reach: number | null;
|
|
336
|
+
total_spend_cents: number | null;
|
|
337
|
+
failure_reason: string | null;
|
|
338
|
+
created_at: string;
|
|
339
|
+
updated_at: string;
|
|
340
|
+
activated_at: string | null;
|
|
341
|
+
ended_at: string | null;
|
|
342
|
+
last_synced_at: string | null;
|
|
343
|
+
}
|