@jclvsh/dropspace 2.31.0 → 2.33.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @jclvsh/dropspace
2
2
 
3
- MCP server and CLI for the [Dropspace](https://www.dropspace.dev) API. Lets AI agents (Claude Code, Cursor) and humans manage launches, personas, media generation, boosts, autopilot, recommendations, reddit engagement, platform connections, API keys, and webhooks. 69 tools across 14 categories.
3
+ MCP server and CLI for the [Dropspace](https://www.dropspace.dev) API. Lets AI agents (Claude Code, Cursor) and humans manage launches, personas, media generation, boosts, autopilot, recommendations, reddit engagement, platform connections, API keys, and webhooks. The full tool list, by category, is in the tables below and at https://www.dropspace.dev/docs#mcp-server.
4
4
 
5
5
  ## Installation
6
6
 
@@ -44,23 +44,23 @@ dropspace launches --help
44
44
 
45
45
  ### CLI Commands
46
46
 
47
- | Group | Commands | Count |
48
- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----- |
49
- | launches | list, create, get, update, delete, publish, retry, generate, retry-content, analytics, batch-analytics, status, delete-post, delete-posts | 14 |
50
- | personas | list, create, get, update, delete, analyze | 6 |
51
- | media | generate, job | 2 |
52
- | connections | list | 1 |
53
- | boosts | create, list, get, activate, cancel, performance | 6 |
54
- | reddit | suggest, config, configure, discover, comments, approve, reject, standing, activate, deactivate | 10 |
55
- | credits | balance, purchase | 2 |
56
- | keys | me, list, create, rename, revoke, rotate | 6 |
57
- | webhooks | list, create, get, update, delete, test, rotate-secret, deliveries | 8 |
58
- | status | dropspace official account connections | 1 |
59
- | usage | plan, limits, and billing period | 1 |
60
- | changelog | machine-readable release history | 1 |
61
- | audience | follower counts + growth per connected account | 1 |
62
- | autopilot | status, configure, review, approve, reject, run, runs | 7 |
63
- | recommendations (recs) | list, apply, dismiss | 3 |
47
+ | Group | Commands |
48
+ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
49
+ | launches | list, create, get, update, delete, publish, retry, generate, retry-content, analytics, batch-analytics, status, delete-post, delete-posts |
50
+ | personas | list, create, get, update, delete, analyze |
51
+ | media | generate, job, library list, library add, library describe, library delete |
52
+ | connections | list |
53
+ | boosts | create, list, get, activate, cancel, performance |
54
+ | reddit | suggest |
55
+ | credits | balance, purchase |
56
+ | keys | me, list, create, rename, revoke, rotate |
57
+ | webhooks | list, create, get, update, delete, test, rotate-secret, deliveries |
58
+ | status | dropspace official account connections |
59
+ | usage | plan, limits, and billing period |
60
+ | changelog | machine-readable release history |
61
+ | audience | follower counts + growth per connected account |
62
+ | autopilot | status, configure, review, approve, reject, run, runs |
63
+ | recommendations (recs) | list, apply, dismiss |
64
64
 
65
65
  ## MCP Server Configuration
66
66
 
@@ -119,7 +119,7 @@ https://api.dropspace.dev/mcp
119
119
 
120
120
  - Transport: MCP streamable HTTP, `POST` only, stateless, JSON responses.
121
121
  - Auth: `Authorization: Bearer ds_live_...` (the same API key; its scopes apply).
122
- - Tools: a subset of this package. API key, webhook, boost, credit, and reddit engagement tools are only available through the npm package.
122
+ - Tools: a subset of this package. API key, webhook, boost, credit, and reddit tools are only available through the npm package.
123
123
  - Every tool is annotated by class: `read` (`readOnlyHint: true`), `write`, or `sensitive` (`destructiveHint: true`: it publishes, schedules, or deletes on a connected platform, or deletes a launch or persona with no restore). The class of each hosted tool is listed at https://www.dropspace.dev/docs#mcp-hosted, and the source of truth is `src/tool-policy.ts`.
124
124
  - `create_launch` sends a content-derived `Idempotency-Key`, so a retry after a timeout returns the launch already created instead of creating a second one (the result says so).
125
125
  - Each MCP request counts once against the key's rate limit, and each tool call counts once more when it reaches the REST API.
@@ -163,6 +163,8 @@ Other agents that read skill files can use the same file from `node_modules/@jcl
163
163
  | `product_description` | string | Yes | Product description |
164
164
  | `platforms` | string[] | Yes | Platforms to publish to (1-9) |
165
165
  | `product_url` | string | No | Product URL |
166
+ | `track_links` | boolean | No | Swap `product_url` in X, LinkedIn, Facebook, Reddit, and YouTube copy for a tracked link; clicks show as `metrics.tracked_clicks` in analytics |
167
+ | `save_to_library` | boolean | No | Add the launch's uploaded media to the media library so autopilot can post it |
166
168
  | `scheduled_date` | string | No | ISO 8601 datetime (15+ min in the future) |
167
169
  | `persona_id` | string | No | Persona ID for tone/style |
168
170
  | `dropspace_platforms` | string[] | No | Platforms to post via official dropspace accounts |
@@ -222,24 +224,31 @@ Other agents that read skill files can use the same file from `node_modules/@jcl
222
224
 
223
225
  #### `update_persona` Parameters
224
226
 
225
- | Parameter | Type | Required | Description |
226
- | ------------------- | ------ | -------- | ----------------------------------- |
227
- | `id` | string | Yes | Persona ID |
228
- | `name` | string | No | New persona name (1-100 characters) |
229
- | `custom_samples` | array | No | Custom writing samples (max 50) |
230
- | `twitter_samples` | array | No | Twitter writing samples (max 50) |
231
- | `reddit_samples` | array | No | Reddit writing samples (max 50) |
232
- | `facebook_samples` | array | No | Facebook writing samples (max 50) |
233
- | `instagram_samples` | array | No | Instagram writing samples (max 50) |
234
- | `tiktok_samples` | array | No | TikTok writing samples (max 50) |
235
- | `linkedin_samples` | array | No | LinkedIn writing samples (max 50) |
227
+ | Parameter | Type | Required | Description |
228
+ | ------------------- | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
229
+ | `id` | string | Yes | Persona ID |
230
+ | `name` | string | No | New persona name (1-100 characters) |
231
+ | `custom_samples` | array | No | Custom writing samples (max 50) |
232
+ | `twitter_samples` | array | No | Twitter writing samples (max 50) |
233
+ | `reddit_samples` | array | No | Reddit writing samples (max 50) |
234
+ | `facebook_samples` | array | No | Facebook writing samples (max 50) |
235
+ | `instagram_samples` | array | No | Instagram writing samples (max 50) |
236
+ | `tiktok_samples` | array | No | TikTok writing samples (max 50) |
237
+ | `linkedin_samples` | array | No | LinkedIn writing samples (max 50) |
238
+ | `brand_kit` | object \| null | No | Autopilot slide styling: `text_color`, `outline_color` (#RRGGBB), `logo_url`, `logo_position` (`top-left`, `top-right`, `bottom-left`, `bottom-right`); null clears it |
236
239
 
237
240
  ### Media
238
241
 
239
- | Tool | Description |
240
- | ---------------- | -------------------------------------------------------------------------- |
241
- | `generate_media` | Submit an AI image or video generation job for a launch (returns a job ID) |
242
- | `get_media_job` | Poll a media generation job's status; returns the result URL when complete |
242
+ | Tool | Description |
243
+ | --------------------------- | ----------------------------------------------------------------------------- |
244
+ | `generate_media` | Submit an AI image or video generation job for a launch (returns a job ID) |
245
+ | `get_media_job` | Poll a media generation job's status; returns the result URL when complete |
246
+ | `list_media_library` | List the user's own photos and videos that autopilot can post (stdio only) |
247
+ | `add_to_media_library` | Add photos or videos by public URL, with an optional description (stdio only) |
248
+ | `update_media_library_item` | Set what a library item shows; autopilot chooses media by it (stdio only) |
249
+ | `delete_media_library_item` | Remove an item from the library (stdio only) |
250
+
251
+ `generate_media` takes `use_product_image: true` to build an image from the launch's product page image. The four library tools are not on the hosted endpoint yet.
243
252
 
244
253
  ### Connections
245
254
 
@@ -249,14 +258,14 @@ Other agents that read skill files can use the same file from `node_modules/@jcl
249
258
 
250
259
  ### Boosts
251
260
 
252
- | Tool | Description |
253
- | ----------------------- | ------------------------------------------------------------------------- |
254
- | `create_boost` | Create a paid Meta boost on an organic Facebook/Instagram launch (paused) |
255
- | `list_boosts` | List all boosts attached to a launch |
256
- | `get_boost` | Get a boost by ID with current performance metrics |
257
- | `activate_boost` | Activate a paused boost — ad spend begins immediately |
258
- | `cancel_boost` | Cancel a boost (deletes the Meta campaign, irreversible) |
259
- | `get_boost_performance` | Fetch latest boost metrics (impressions, clicks, reach, spend) |
261
+ | Tool | Description |
262
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263
+ | `create_boost` | Create a paid Meta boost (paused): `objective` engagement or traffic (Facebook link posts), optional `max_cpc_cents` cap. Running boosts pause themselves when click-through decays |
264
+ | `list_boosts` | List all boosts attached to a launch |
265
+ | `get_boost` | Get a boost by ID with current performance metrics |
266
+ | `activate_boost` | Activate a paused boost — ad spend begins immediately |
267
+ | `cancel_boost` | Cancel a boost (deletes the Meta campaign, irreversible) |
268
+ | `get_boost_performance` | Fetch latest boost metrics (impressions, clicks, reach, spend) |
260
269
 
261
270
  ### Credits
262
271
 
@@ -305,10 +314,10 @@ Other agents that read skill files can use the same file from `node_modules/@jcl
305
314
 
306
315
  #### `create_webhook` Parameters
307
316
 
308
- | Parameter | Type | Required | Description |
309
- | --------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
310
- | `url` | string | Yes | HTTPS webhook URL |
311
- | `events` | string[] | Yes | Events to subscribe to: `launch.completed`, `launch.failed`, `launch.partial`, `media.ready`, `persona.analyzed`, `post.deleted` |
317
+ | Parameter | Type | Required | Description |
318
+ | --------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
319
+ | `url` | string | Yes | Public HTTPS webhook URL. A Slack incoming webhook URL (`https://hooks.slack.com/services/...`) receives readable Slack messages instead of signed JSON |
320
+ | `events` | string[] | Yes | Events to subscribe to: `launch.completed`, `launch.failed`, `launch.partial`, `media.ready`, `persona.analyzed`, `post.deleted`, `recommendation.created`, `connection.expired`, `connection.restored` |
312
321
 
313
322
  #### `update_webhook` Parameters
314
323
 
@@ -360,7 +369,7 @@ Counts come from each platform's official API and are cached for 15 minutes. You
360
369
  | `run_autopilot_now` | Trigger a manual autopilot run immediately (2 per hour) |
361
370
  | `list_autopilot_runs` | Recent run history with per-step stats and errors |
362
371
 
363
- Autopilot is available on pro and premium plans.
372
+ Autopilot is available on pro and premium plans. `configure_autopilot` takes `media_source` (`ai`, `library`, `mixed`) to post the user's own photos and videos, and `product_image_url` for AI slides built from the real product.
364
373
 
365
374
  ### Recommendations
366
375
 
@@ -28,6 +28,7 @@ export function registerLaunchCommands(program, client) {
28
28
  .option("--platforms <list>", "comma-separated platforms", parseList)
29
29
  .option("--url <url>", "product URL")
30
30
  .option("--track-links", "post the product URL as tracked redirects; clicks show in analytics")
31
+ .option("--save-to-library", "add this launch's uploaded media to your media library for autopilot")
31
32
  .option("--schedule <datetime>", "ISO 8601 schedule datetime")
32
33
  .option("--persona <id>", "persona ID for tone/style")
33
34
  .option("--dropspace-platforms <list>", "platforms for official dropspace accounts", parseList)
@@ -50,6 +51,8 @@ export function registerLaunchCommands(program, client) {
50
51
  body.product_url = opts.url;
51
52
  if (opts.trackLinks)
52
53
  body.track_links = true;
54
+ if (opts.saveToLibrary)
55
+ body.save_to_library = true;
53
56
  if (opts.schedule)
54
57
  body.scheduled_date = opts.schedule;
55
58
  if (opts.persona)
@@ -56,6 +56,7 @@ export function registerPersonaCommands(program, client) {
56
56
  .option("--instagram-samples <json>", "instagram writing samples as JSON array")
57
57
  .option("--tiktok-samples <json>", "tiktok writing samples as JSON array")
58
58
  .option("--linkedin-samples <json>", "linkedin writing samples as JSON array")
59
+ .option("--brand-kit <json>", 'autopilot slide styling, e.g. \'{"text_color":"#FFFFFF","logo_url":"https://...","logo_position":"top-right"}\'; "none" clears it')
59
60
  .action(async (id, opts) => {
60
61
  try {
61
62
  const body = {};
@@ -75,6 +76,8 @@ export function registerPersonaCommands(program, client) {
75
76
  body.tiktok_samples = JSON.parse(opts.tiktokSamples);
76
77
  if (opts.linkedinSamples)
77
78
  body.linkedin_samples = JSON.parse(opts.linkedinSamples);
79
+ if (opts.brandKit)
80
+ body.brand_kit = opts.brandKit === "none" ? null : JSON.parse(opts.brandKit);
78
81
  const result = await client.patch(`/personas/${id}`, body);
79
82
  formatOutput(result, getFormat(program.opts()));
80
83
  }
@@ -1,4 +1,5 @@
1
1
  import { formatOutput, handleError, getFormat } from "../cli-utils.js";
2
+ import { REDDIT_ENGAGEMENT_PAUSED } from "../tool-policy.js";
2
3
  /**
3
4
  * Reddit discovery + engagement CLI — the earn-standing-before-promoting loop:
4
5
  * suggest → activate → discover → approve/reject → standing. Requires an api key whose
@@ -7,7 +8,9 @@ import { formatOutput, handleError, getFormat } from "../cli-utils.js";
7
8
  export function registerRedditCommands(program, client) {
8
9
  const reddit = program
9
10
  .command("reddit")
10
- .description("subreddit discovery + engagement (earn standing before promoting)");
11
+ .description(REDDIT_ENGAGEMENT_PAUSED
12
+ ? "subreddit discovery"
13
+ : "subreddit discovery + engagement (earn standing before promoting)");
11
14
  reddit
12
15
  .command("suggest")
13
16
  .description("ICP-matched subreddit discovery with per-community standing badges")
@@ -21,6 +24,9 @@ export function registerRedditCommands(program, client) {
21
24
  handleError(error);
22
25
  }
23
26
  });
27
+ // Engagement commands are paused with the API (see REDDIT_ENGAGEMENT_PAUSED).
28
+ if (REDDIT_ENGAGEMENT_PAUSED)
29
+ return;
24
30
  reddit
25
31
  .command("config")
26
32
  .description("read the engagement settings (enabled, caps, gate state, comment voice)")
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { createPolicyRegistrar } from "./policy.js";
3
- import { TOOL_POLICY } from "./tool-policy.js";
3
+ import { PAUSED_TOOLS, TOOL_POLICY } from "./tool-policy.js";
4
4
  import { registerLaunchTools } from "./tools/launches.js";
5
5
  import { registerPersonaTools } from "./tools/personas.js";
6
6
  import { registerConnectionTools } from "./tools/connections.js";
@@ -15,9 +15,19 @@ import { registerAutopilotTools } from "./tools/autopilot.js";
15
15
  import { registerRecommendationTools } from "./tools/recommendations.js";
16
16
  import { registerRedditTools } from "./tools/reddit.js";
17
17
  import { registerAudienceTools } from "./tools/audience.js";
18
+ /** Drops tools whose feature is paused before the policy registrar sees them (see PAUSED_TOOLS). */
19
+ function skipPausedTools(registrar) {
20
+ return {
21
+ tool(name, ...rest) {
22
+ if (PAUSED_TOOLS.has(name))
23
+ return;
24
+ return registrar.tool(name, ...rest);
25
+ },
26
+ };
27
+ }
18
28
  export function createDropspaceServer({ client, version, profile }) {
19
29
  const server = new McpServer({ name: "dropspace", version });
20
- const registrar = createPolicyRegistrar(server, TOOL_POLICY, profile);
30
+ const registrar = skipPausedTools(createPolicyRegistrar(server, TOOL_POLICY, profile));
21
31
  registerLaunchTools(registrar, client);
22
32
  registerPersonaTools(registrar, client);
23
33
  registerConnectionTools(registrar, client);
@@ -18,8 +18,17 @@ import { type PolicyTable, type ToolClass, type ToolPolicy, type ToolProfile } f
18
18
  export type { ToolClass, ToolPolicy, ToolProfile };
19
19
  export declare const TOOL_CATEGORIES: readonly ["launches", "posts", "personas", "media", "connections", "boosts", "credits", "API keys", "webhooks", "dropspace", "usage", "autopilot", "recommendations", "reddit engagement", "audience"];
20
20
  export type ToolCategory = (typeof TOOL_CATEGORIES)[number];
21
+ /**
22
+ * Reddit engagement is paused (dropspace's reddit_engagement_disabled switch, 10/15/2026): the API
23
+ * answers 404 on /reddit/engagement/*, so the package neither serves nor counts these tools, and
24
+ * the CLI drops their commands. Set false, with the switch, to bring them back.
25
+ */
26
+ export declare const REDDIT_ENGAGEMENT_PAUSED = true;
27
+ /** Tools classified but not served while their feature is paused. */
28
+ export declare const PAUSED_TOOLS: ReadonlySet<string>;
29
+ /** The tools the package serves - every docs count and category list reads this. */
21
30
  export declare const TOOL_POLICY: PolicyTable<ToolCategory>;
22
- export declare const toolPolicy: (name: string) => ToolPolicy<"launches" | "personas" | "connections" | "webhooks" | "usage" | "credits" | "media" | "boosts" | "autopilot" | "recommendations" | "audience" | "dropspace" | "posts" | "API keys" | "reddit engagement">;
31
+ export declare const toolPolicy: (name: string) => ToolPolicy<"launches" | "personas" | "connections" | "webhooks" | "usage" | "credits" | "media" | "boosts" | "audience" | "posts" | "API keys" | "dropspace" | "autopilot" | "recommendations" | "reddit engagement">;
23
32
  export declare const isToolEnabled: (name: string, profile: ToolProfile) => boolean;
24
33
  export declare const toolAnnotations: (name: string) => {
25
34
  readOnlyHint: boolean;
@@ -42,7 +42,7 @@ const localOnly = (category, toolClass) => ({
42
42
  hosted: false,
43
43
  category,
44
44
  });
45
- export const TOOL_POLICY = {
45
+ const FULL_TOOL_POLICY = {
46
46
  // launches
47
47
  list_launches: hosted("launches", "read"),
48
48
  get_launch: hosted("launches", "read"),
@@ -144,6 +144,27 @@ export const TOOL_POLICY = {
144
144
  test_webhook: localOnly("webhooks", "write"),
145
145
  rotate_webhook_secret: localOnly("webhooks", "sensitive"),
146
146
  };
147
+ /**
148
+ * Reddit engagement is paused (dropspace's reddit_engagement_disabled switch, 10/15/2026): the API
149
+ * answers 404 on /reddit/engagement/*, so the package neither serves nor counts these tools, and
150
+ * the CLI drops their commands. Set false, with the switch, to bring them back.
151
+ */
152
+ export const REDDIT_ENGAGEMENT_PAUSED = true;
153
+ const REDDIT_ENGAGEMENT_TOOLS = [
154
+ "get_engagement_config",
155
+ "get_engagement_standing",
156
+ "list_engagement_comments",
157
+ "update_engagement_config",
158
+ "discover_engagement",
159
+ "reject_engagement_comment",
160
+ "activate_engagement_subreddit",
161
+ "deactivate_engagement_subreddit",
162
+ "approve_engagement_comment",
163
+ ];
164
+ /** Tools classified but not served while their feature is paused. */
165
+ export const PAUSED_TOOLS = new Set(REDDIT_ENGAGEMENT_PAUSED ? REDDIT_ENGAGEMENT_TOOLS : []);
166
+ /** The tools the package serves - every docs count and category list reads this. */
167
+ export const TOOL_POLICY = Object.fromEntries(Object.entries(FULL_TOOL_POLICY).filter(([name]) => !PAUSED_TOOLS.has(name)));
147
168
  export const toolPolicy = (name) => toolPolicyFor(TOOL_POLICY, name);
148
169
  export const isToolEnabled = (name, profile) => isToolEnabledIn(TOOL_POLICY, name, profile);
149
170
  export const toolAnnotations = (name) => toolAnnotationsFor(TOOL_POLICY, name);
@@ -90,6 +90,10 @@ export function registerLaunchTools(server, client) {
90
90
  .url()
91
91
  .optional()
92
92
  .describe("product URL — scraped to generate content; can substitute for product_description"),
93
+ save_to_library: z
94
+ .boolean()
95
+ .optional()
96
+ .describe("add the launch's uploaded media to the user's media library so autopilot can post it"),
93
97
  track_links: z
94
98
  .boolean()
95
99
  .optional()
@@ -59,7 +59,7 @@ export function registerRedditTools(server, client) {
59
59
  return fail(error);
60
60
  }
61
61
  });
62
- server.tool("discover_engagement", "draft helpful comments for ONE of the account's active subreddits (call again to cover more). fills the review queue; posts NOTHING — approval is the separate, rail-gated step. `skipped` names why nothing ran (kill_switch | not_enabled | gate_not_passed).", {}, async () => {
62
+ server.tool("discover_engagement", "draft helpful comments for ONE of the account's active subreddits (call again to cover more). fills the review queue; posts NOTHING — approval is the separate, rail-gated step. `skipped` names why nothing ran (kill_switch | not_enabled | gate_not_passed | account_hidden).", {}, async () => {
63
63
  try {
64
64
  return ok(await client.post("/reddit/engagement/discover"));
65
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jclvsh/dropspace",
3
- "version": "2.31.0",
3
+ "version": "2.33.0",
4
4
  "description": "MCP server and CLI for the Dropspace API",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,7 +10,7 @@ Dropspace turns one brief into per-platform posts and publishes them on the user
10
10
  ## Setup
11
11
 
12
12
  - MCP over stdio: `npx -y -p @jclvsh/dropspace dropspace-mcp` with `DROPSPACE_API_KEY=ds_live_...` in the environment. Every tool is available.
13
- - Hosted MCP: `https://api.dropspace.dev/mcp` (streamable HTTP, POST, `Authorization: Bearer ds_live_...`). Serves a subset: no credits, boosts, API keys, webhooks, or reddit engagement tools.
13
+ - Hosted MCP: `https://api.dropspace.dev/mcp` (streamable HTTP, POST, `Authorization: Bearer ds_live_...`). Serves a subset: no credits, boosts, API keys, webhooks, or reddit tools.
14
14
  - CLI: `npx -y @jclvsh/dropspace <command>` (the same key). `dropspace --help` lists commands.
15
15
  - No key? The user creates one at https://www.dropspace.dev/settings/api. A key's scopes limit what it can do (`read`, `write`, `generate`, `send`, `admin`); a 403 names the missing scope.
16
16
 
@@ -25,6 +25,7 @@ Dropspace turns one brief into per-platform posts and publishes them on the user
25
25
  1. `create_launch` with `product_url` and/or `product_description` and `platforms`. Dropspace reads the product page and writes copy per platform.
26
26
  - Use `platform_contents` or `custom_content` when the user already wrote the copy.
27
27
  - `track_links: true` posts the product URL as a tracked redirect on X, LinkedIn, Facebook, Reddit, and YouTube; clicks then appear in analytics. Tell the user their posts will show a dropspace.dev link.
28
+ - `save_to_library: true` adds the launch's uploaded photos and videos to the user's media library, where autopilot can post them.
28
29
  - `scheduled_date` (ISO 8601, at least 15 minutes out) schedules it; without one, the launch waits for `publish_launch`.
29
30
  2. Review: `get_launch` shows the generated copy per platform. Show it to the user before publishing anything they have not seen. Edit with `update_launch`; regenerate one platform with `retry_launch_content`.
30
31
  3. Media (optional): `generate_media` with `type: "image"` and `use_product_image: true` makes an image of the real product from its page; `get_media_job` polls it. Videos are 8-second clips (`type: "video"` or `"script_video"`).
@@ -35,13 +36,17 @@ Dropspace turns one brief into per-platform posts and publishes them on the user
35
36
 
36
37
  - Recommendations: `list_recommendations` after posts have run a day or two; `apply_recommendation` turns one into a draft launch, an autopilot post, or a paused boost. Nothing publishes or spends on apply.
37
38
  - Autopilot (pro and premium plans): `get_autopilot`, `configure_autopilot`, `run_autopilot_now`. In review mode it drafts posts for approval: `list_autopilot_review`, `approve_autopilot_draft`, `reject_autopilot_draft`.
39
+ - `media_source`: `ai` (generated slides and video), `library` (only the user's own media), or `mixed` (library first). `product_image_url` makes AI slides show the real product.
40
+ - Media library (stdio server and CLI only): `list_media_library`, `add_to_media_library`, `update_media_library_item`, `delete_media_library_item`. Autopilot chooses items by their description, so describe each one; videos must have one.
41
+ - Brand kit: `update_persona` with `brand_kit` (text and outline colors, a corner logo) styles the image slides autopilot posts as that persona.
38
42
  - Audience: `get_audience` for follower counts on Instagram, TikTok, and YouTube.
43
+ - Webhooks: `create_webhook` with a Slack incoming webhook URL (`https://hooks.slack.com/services/...`) posts events to that channel as readable messages.
39
44
 
40
45
  ## Spending money - confirm first
41
46
 
42
47
  These cost the user money. Get an explicit yes for the amount before calling them:
43
48
 
44
- - `activate_boost` starts real ad spend on the user's Meta ad account. `create_boost` makes it paused; activation is the spend.
49
+ - `activate_boost` starts real ad spend on the user's Meta ad account. `create_boost` makes it paused; activation is the spend. Offer `max_cpc_cents` so the boost pauses itself if clicks get expensive; `objective: "traffic"` needs a Facebook link post.
45
50
  - `purchase_credit_pack`.
46
51
 
47
52
  ## Errors that need the user