@jclvsh/dropspace 2.20.0 → 2.22.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
@@ -55,6 +55,8 @@ dropspace launches --help
55
55
  | webhooks | list, create, get, update, delete, test, rotate-secret, deliveries | 8 |
56
56
  | status | dropspace official account connections | 1 |
57
57
  | usage | plan, limits, and billing period | 1 |
58
+ | changelog | machine-readable release history | 1 |
59
+ | audience | follower counts + growth per connected account | 1 |
58
60
  | autopilot | status, configure, review, approve, reject, run, runs | 7 |
59
61
  | recommendations (recs) | list, apply, dismiss | 3 |
60
62
 
@@ -126,39 +128,39 @@ Add to your `.cursor/mcp.json`:
126
128
 
127
129
  #### `create_launch` Parameters
128
130
 
129
- | Parameter | Type | Required | Description |
130
- | ----------------------------- | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
131
- | `title` | string | Yes | Launch title |
132
- | `product_description` | string | Yes | Product description |
133
- | `platforms` | string[] | Yes | Platforms to publish to (1-9) |
134
- | `product_url` | string | No | Product URL |
135
- | `scheduled_date` | string | No | ISO 8601 datetime (15+ min in the future) |
136
- | `persona_id` | string | No | Persona ID for tone/style |
137
- | `dropspace_platforms` | string[] | No | Platforms to post via official dropspace accounts |
138
- | `media_mode` | string | No | `"images"` or `"video"` |
139
- | `user_platform_accounts` | object | No | Map of platform key to token_id (UUID). Simple keys: `twitter`, `reddit`, `instagram`, `tiktok`. LinkedIn: `linkedin:personal` or `linkedin:organization:<org_id>`. Facebook: `facebook:page:<page_id>` |
140
- | `platform_contents` | object | No | Per-platform content (mutually exclusive with `custom_content`). Reddit accepts `metadata.subreddits` (string[], max 10, no `r/` prefix — one post per subreddit, each retryable) and `metadata.first_comment` (max 1500) |
141
- | `custom_content` | string \| string[] | No | Single content for all platforms, or array of tweets for twitter thread mode (mutually exclusive with `platform_contents`) |
142
- | `custom_content_reddit_title` | string | No | Reddit title (required with `custom_content` + reddit) |
143
- | `media` | object[] | No | Inline media, URL or base64 (1-10 items, mutually exclusive with `media_assets`) |
144
- | `media_assets` | object[] | No | Pre-uploaded media references (mutually exclusive with `media`) |
145
- | `media_attach_platforms` | string[] | No | Platforms to attach media to |
146
- | `generate_ai_videos` | string[] | No | Auto-generate AI videos (`"instagram"`, `"tiktok"`) |
131
+ | Parameter | Type | Required | Description |
132
+ | ----------------------------- | ------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
133
+ | `title` | string | Yes | Launch title |
134
+ | `product_description` | string | Yes | Product description |
135
+ | `platforms` | string[] | Yes | Platforms to publish to (1-9) |
136
+ | `product_url` | string | No | Product URL |
137
+ | `scheduled_date` | string | No | ISO 8601 datetime (15+ min in the future) |
138
+ | `persona_id` | string | No | Persona ID for tone/style |
139
+ | `dropspace_platforms` | string[] | No | Platforms to post via official dropspace accounts |
140
+ | `media_mode` | string | No | `"images"` or `"video"` |
141
+ | `user_platform_accounts` | object | No | Map of platform key to token_id (UUID). Simple keys: `twitter`, `reddit`, `instagram`, `tiktok`, `youtube`. LinkedIn: `linkedin:personal` or `linkedin:organization:<org_id>`. Facebook: a bare `facebook` key works when you have exactly one Page (send any connection id from `connections list` as the value); to target a specific Page among several, use `facebook:page:<page_id>` or `facebook:<page_id>`, where `page_id` is the entity_id of that Page from `connections list`; several Pages with no `page_id` returns a 400 listing them |
142
+ | `platform_contents` | object | No | Per-platform content (mutually exclusive with `custom_content`). Reddit accepts `metadata.subreddits` (string[], max 10, no `r/` prefix — one post per subreddit, each retryable) and `metadata.first_comment` (max 1500) |
143
+ | `custom_content` | string \| string[] | No | Single content for all platforms, or array of tweets for twitter thread mode (mutually exclusive with `platform_contents`) |
144
+ | `custom_content_reddit_title` | string | No | Reddit title (required with `custom_content` + reddit) |
145
+ | `media` | object[] | No | Inline media, URL or base64 (1-10 items, mutually exclusive with `media_assets`) |
146
+ | `media_assets` | object[] | No | Pre-uploaded media references (mutually exclusive with `media`) |
147
+ | `media_attach_platforms` | string[] | No | Platforms to attach media to |
148
+ | `generate_ai_videos` | string[] | No | Auto-generate AI videos (`"instagram"`, `"tiktok"`) |
147
149
 
148
150
  #### `update_launch` Parameters
149
151
 
150
- | Parameter | Type | Required | Description |
151
- | ------------------------ | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
152
- | `id` | string | Yes | Launch ID |
153
- | `scheduled_date` | string \| null | No | ISO 8601 datetime or null to clear |
154
- | `status` | string | No | `"draft"`, `"manual"`, `"trigger"`, `"scheduled"`, `"cancelled"` |
155
- | `platform_contents` | object | No | Per-platform content overrides |
156
- | `dropspace_platforms` | string[] | No | Official dropspace account platforms |
157
- | `user_platform_accounts` | object | No | Map of platform key to token_id (UUID). Simple keys: `twitter`, `reddit`, `instagram`, `tiktok`. LinkedIn: `linkedin:personal` or `linkedin:organization:<org_id>`. Facebook: `facebook:page:<page_id>` |
158
- | `media` | object[] | No | Inline media (URL or base64, 1-10 items) |
159
- | `media_assets` | object[] | No | Pre-uploaded media references |
160
- | `media_attach_platforms` | string[] | No | Platforms to attach media to |
161
- | `media_mode` | string | No | `"images"` or `"video"` |
152
+ | Parameter | Type | Required | Description |
153
+ | ------------------------ | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
154
+ | `id` | string | Yes | Launch ID |
155
+ | `scheduled_date` | string \| null | No | ISO 8601 datetime or null to clear |
156
+ | `status` | string | No | `"draft"`, `"manual"`, `"trigger"`, `"scheduled"`, `"cancelled"` |
157
+ | `platform_contents` | object | No | Per-platform content overrides |
158
+ | `dropspace_platforms` | string[] | No | Official dropspace account platforms |
159
+ | `user_platform_accounts` | object | No | Map of platform key to token_id (UUID). Simple keys: `twitter`, `reddit`, `instagram`, `tiktok`, `youtube`. LinkedIn: `linkedin:personal` or `linkedin:organization:<org_id>`. Facebook: a bare `facebook` key works when you have exactly one Page (send any connection id from `connections list` as the value); to target a specific Page among several, use `facebook:page:<page_id>` or `facebook:<page_id>`, where `page_id` is the entity_id of that Page from `connections list`; several Pages with no `page_id` returns a 400 listing them |
160
+ | `media` | object[] | No | Inline media (URL or base64, 1-10 items) |
161
+ | `media_assets` | object[] | No | Pre-uploaded media references |
162
+ | `media_attach_platforms` | string[] | No | Platforms to attach media to |
163
+ | `media_mode` | string | No | `"images"` or `"video"` |
162
164
 
163
165
  #### `generate_content` Parameters
164
166
 
@@ -296,9 +298,26 @@ Add to your `.cursor/mcp.json`:
296
298
 
297
299
  ### Usage
298
300
 
299
- | Tool | Description |
300
- | ----------- | ------------------------------------------------------- |
301
- | `get_usage` | Get your current plan, usage limits, and billing period |
301
+ | Tool | Description |
302
+ | --------------- | --------------------------------------------------------------------------------------------------- |
303
+ | `get_usage` | Get your current plan, usage limits, and billing period |
304
+ | `get_changelog` | Machine-readable release history: version, date, type, summary, migration notes on breaking entries |
305
+
306
+ ### Audience
307
+
308
+ | Tool | Description |
309
+ | -------------- | ------------------------------------------------------------------------------------------------------ |
310
+ | `get_audience` | Follower counts and growth per connected account (Instagram, YouTube, TikTok, Twitter, Facebook Pages) |
311
+
312
+ #### `get_audience` Parameters
313
+
314
+ | Parameter | Type | Required | Description |
315
+ | ------------- | ------ | -------- | ----------------------------------------- |
316
+ | `window_days` | number | No | Growth window in days (1-365, default 30) |
317
+
318
+ CLI: `dropspace audience [-w, --window-days <n>]`.
319
+
320
+ Counts come from each platform's official API and are cached for 15 minutes. YouTube works differently: each read spends a shared daily quota, so a connected account's YouTube count is refreshed in the background about once an hour, and an on-demand read re-fetches at most every 6 hours if that background refresh is delayed; if the daily quota is exhausted, a YouTube read returns the most recent successful count instead of null. `current.captured_at` is always when that count was actually read from the platform, not when it was served from cache - use it to judge freshness, not the response time. LinkedIn is not covered (its API exposes no personal-profile follower count).
302
321
 
303
322
  ### Autopilot
304
323
 
@@ -2,7 +2,7 @@ import { formatOutput, handleError, getFormat } from "../cli-utils.js";
2
2
  export function registerAudienceCommands(program, client) {
3
3
  program
4
4
  .command("audience")
5
- .description("follower counts + growth per connected account (instagram, youtube, tiktok, twitter, facebook pages)")
5
+ .description("follower counts + growth per connected account (instagram, youtube, tiktok, twitter, facebook pages); counts cache for 15 min, youtube refreshes about hourly in the background, and captured_at is always the actual read time, not the serve time")
6
6
  .option("-w, --window-days <n>", "growth window in days (max 365)", "30")
7
7
  .action(async (opts) => {
8
8
  try {
@@ -34,7 +34,7 @@ export function registerLaunchCommands(program, client) {
34
34
  .option("--platform-contents <json>", 'per-platform content as JSON string. reddit supports metadata.subreddits (string[], max 10, no r/ prefix — one post per subreddit) and metadata.first_comment (max 1500). e.g. \'{"reddit":{"title":"...","content":"...","metadata":{"subreddits":["SideProject"],"first_comment":"..."}}}\'')
35
35
  .option("--custom-content <text>", "single content string for all platforms")
36
36
  .option("--reddit-title <title>", "reddit title (with custom-content)")
37
- .option("--user-accounts <json>", 'platform→token_id map as JSON. simple key = primary account ("instagram"); composite key targets a specific account when you have several ("instagram:<account_id>", ids from `connections list`) — works for instagram/tiktok/youtube/twitter/reddit')
37
+ .option("--user-accounts <json>", 'platform→token_id map as JSON. simple key = primary account ("instagram"); composite key targets a specific account when you have several ("instagram:<account_id>", ids from `connections list`) - works for instagram/tiktok/youtube/twitter/reddit. facebook: bare "facebook" for your one Page, or "facebook:page:<page_id>" / "facebook:<page_id>" for a specific one (id from `connections list`); several Pages and no page_id returns a 400. linkedin: "linkedin:personal" or "linkedin:organization:<org_id>"')
38
38
  .option("--attribution-mode <mode>", 'how official-account posts credit you: "handle" (default) or "anonymous"')
39
39
  .option("--publish", "immediately publish after creation")
40
40
  .option("--wait", "wait for publishing to complete (requires --publish)")
@@ -104,7 +104,7 @@ export function registerLaunchCommands(program, client) {
104
104
  .option("--platform-contents <json>", 'per-platform content as JSON string. reddit supports metadata.subreddits (string[], max 10, no r/ prefix — one post per subreddit) and metadata.first_comment (max 1500). e.g. \'{"reddit":{"title":"...","content":"...","metadata":{"subreddits":["SideProject"],"first_comment":"..."}}}\'')
105
105
  .option("--dropspace-platforms <list>", "official dropspace account platforms", parseList)
106
106
  .option("--attribution-mode <mode>", 'how official-account posts credit you: "handle" (default) or "anonymous"')
107
- .option("--user-accounts <json>", 'platform→token_id map as JSON. simple key = primary account ("instagram"); composite key targets a specific account when you have several ("instagram:<account_id>", ids from `connections list`) — works for instagram/tiktok/youtube/twitter/reddit')
107
+ .option("--user-accounts <json>", 'platform→token_id map as JSON. simple key = primary account ("instagram"); composite key targets a specific account when you have several ("instagram:<account_id>", ids from `connections list`) - works for instagram/tiktok/youtube/twitter/reddit. facebook: bare "facebook" for your one Page, or "facebook:page:<page_id>" / "facebook:<page_id>" for a specific one (id from `connections list`); several Pages and no page_id returns a 400. linkedin: "linkedin:personal" or "linkedin:organization:<org_id>"')
108
108
  .option("--media-mode <mode>", "media mode: images or video")
109
109
  .action(async (id, opts) => {
110
110
  try {
@@ -12,4 +12,16 @@ export function registerUsageCommands(program, client) {
12
12
  handleError(error);
13
13
  }
14
14
  });
15
+ program
16
+ .command("changelog")
17
+ .description("machine-readable release history: version, date, type, summary, migration notes on breaking entries")
18
+ .action(async () => {
19
+ try {
20
+ const result = await client.get("/changelog");
21
+ formatOutput(result, getFormat(program.opts()));
22
+ }
23
+ catch (error) {
24
+ handleError(error);
25
+ }
26
+ });
15
27
  }
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
2
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
5
  import { DropspaceClient } from "./client.js";
@@ -16,9 +17,11 @@ import { registerAutopilotTools } from "./tools/autopilot.js";
16
17
  import { registerRecommendationTools } from "./tools/recommendations.js";
17
18
  import { registerRedditTools } from "./tools/reddit.js";
18
19
  import { registerAudienceTools } from "./tools/audience.js";
20
+ const requireFromHere = createRequire(import.meta.url);
21
+ const pkg = requireFromHere("../package.json");
19
22
  const server = new McpServer({
20
23
  name: "dropspace",
21
- version: "2.20.0",
24
+ version: pkg.version,
22
25
  });
23
26
  const client = new DropspaceClient();
24
27
  registerLaunchTools(server, client);
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  export function registerAudienceTools(server, client) {
3
- server.tool("get_audience", 'follower counts + growth for every connected instagram / youtube / tiktok / twitter account and facebook page. one entry per ACCOUNT, not per platform: "entity_id" is null for your primary account on a platform and set for each additional one, so a user with two instagram accounts returns two instagram entries. counts are read live from each platform API (cached ~15 min); "series" is the stored history. growth is measured from the OLDEST stored point inside the window, which — because history points are recorded when you read, at most one per account per day — can be much younger than window_days: read "effective_window_days" (the span the delta actually covers) and "baseline_captured_at" before comparing growth across accounts. linkedin is not covered (its API exposes no personal-profile follower count).', {
3
+ server.tool("get_audience", 'follower counts + growth for every connected instagram / youtube / tiktok / twitter account and facebook page. one entry per ACCOUNT, not per platform: "entity_id" is null for your primary account on a platform and set for each additional one, so a user with two instagram accounts returns two instagram entries. counts come from each platform\'s official API and are cached for 15 minutes. youtube works differently: each youtube read spends a shared daily quota, so while you keep reading a creator\'s audience, their youtube count is refreshed in the background about once an hour, and an on-demand read re-fetches at most every 6 hours if that refresh is delayed; if the daily quota is exhausted, a youtube read returns the most recent successful count instead of null. "current.captured_at" is always when that count was actually read from the platform, never when it was served: use it to judge freshness. "series" is the stored history. growth is measured from the OLDEST stored point inside the window, which — because history points are recorded when you read, at most one per account per day — can be much younger than window_days: read "effective_window_days" (the span the delta actually covers) and "baseline_captured_at" before comparing growth across accounts. linkedin is not covered (its API exposes no personal-profile follower count).', {
4
4
  window_days: z
5
5
  .number()
6
6
  .int()
@@ -106,7 +106,7 @@ export function registerLaunchTools(server, client) {
106
106
  user_platform_accounts: z
107
107
  .record(z.string(), z.string())
108
108
  .optional()
109
- .describe('map of platform key to token_id (UUID). a simple key targets your primary/only account: "twitter", "reddit", "instagram", "tiktok", "youtube". to target a SPECIFIC account when you have more than one on the same platform, use a composite key: Instagram/TikTok/YouTube/Twitter/Reddit use "instagram:<account_id>" / "tiktok:<account_id>" / "youtube:<account_id>" / "twitter:<account_id>" / "reddit:<account_id>" where <account_id> is the account entity_id from list_connections; LinkedIn uses "linkedin:personal" or "linkedin:organization:<org_id>"; Facebook uses "facebook:page:<page_id>". multiple keys allowed (e.g. post to two Instagram accounts at once). for Reddit, each targeted account runs its own subreddit fan-out'),
109
+ .describe('map of platform key to token_id (UUID). a simple key targets your primary/only account: "twitter", "reddit", "instagram", "tiktok", "youtube". to target a SPECIFIC account when you have more than one on the same platform, use a composite key: Instagram/TikTok/YouTube/Twitter/Reddit use "instagram:<account_id>" / "tiktok:<account_id>" / "youtube:<account_id>" / "twitter:<account_id>" / "reddit:<account_id>" where <account_id> is the account entity_id from list_connections; LinkedIn uses "linkedin:personal" or "linkedin:organization:<org_id>"; Facebook posts go to a Page: a bare "facebook" key works when you have exactly one Page, using any connection id from list_connections as the value (the Page row id or the person row id) so dropspace resolves the Page; to target a specific Page among several, use a composite key, "facebook:page:<page_id>" or "facebook:<page_id>", where <page_id> is that Page row entity_id from list_connections; with several Pages and no page_id the API returns a 400 listing them. multiple keys allowed (e.g. post to two Instagram accounts at once). for Reddit, each targeted account runs its own subreddit fan-out'),
110
110
  platform_contents: z
111
111
  .record(z.string(), z.record(z.string(), z.unknown()))
112
112
  .optional()
@@ -233,7 +233,7 @@ export function registerLaunchTools(server, client) {
233
233
  user_platform_accounts: z
234
234
  .record(z.string(), z.string())
235
235
  .optional()
236
- .describe('map of platform key to token_id (UUID). a simple key targets your primary/only account: "twitter", "reddit", "instagram", "tiktok", "youtube". to target a SPECIFIC account when you have more than one on the same platform, use a composite key: Instagram/TikTok/YouTube/Twitter/Reddit use "instagram:<account_id>" / "tiktok:<account_id>" / "youtube:<account_id>" / "twitter:<account_id>" / "reddit:<account_id>" where <account_id> is the account entity_id from list_connections; LinkedIn uses "linkedin:personal" or "linkedin:organization:<org_id>"; Facebook uses "facebook:page:<page_id>". multiple keys allowed (e.g. post to two Instagram accounts at once). for Reddit, each targeted account runs its own subreddit fan-out'),
236
+ .describe('map of platform key to token_id (UUID). a simple key targets your primary/only account: "twitter", "reddit", "instagram", "tiktok", "youtube". to target a SPECIFIC account when you have more than one on the same platform, use a composite key: Instagram/TikTok/YouTube/Twitter/Reddit use "instagram:<account_id>" / "tiktok:<account_id>" / "youtube:<account_id>" / "twitter:<account_id>" / "reddit:<account_id>" where <account_id> is the account entity_id from list_connections; LinkedIn uses "linkedin:personal" or "linkedin:organization:<org_id>"; Facebook posts go to a Page: a bare "facebook" key works when you have exactly one Page, using any connection id from list_connections as the value (the Page row id or the person row id) so dropspace resolves the Page; to target a specific Page among several, use a composite key, "facebook:page:<page_id>" or "facebook:<page_id>", where <page_id> is that Page row entity_id from list_connections; with several Pages and no page_id the API returns a 400 listing them. multiple keys allowed (e.g. post to two Instagram accounts at once). for Reddit, each targeted account runs its own subreddit fan-out'),
237
237
  media: z
238
238
  .array(inlineMediaItemSchema)
239
239
  .min(1)
@@ -6,7 +6,9 @@ const WEBHOOK_EVENTS = [
6
6
  "media.ready",
7
7
  "persona.analyzed",
8
8
  "post.deleted",
9
+ "recommendation.created",
9
10
  "connection.expired",
11
+ "connection.restored",
10
12
  ];
11
13
  export function registerWebhookTools(server, client) {
12
14
  server.tool("list_webhooks", "list your webhooks (requires admin scope)", {}, async () => {
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@jclvsh/dropspace",
3
- "version": "2.20.0",
3
+ "version": "2.22.0",
4
4
  "description": "MCP server and CLI for the Dropspace API",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/JCLVSH/monorepo.git",
8
+ "directory": "apps/dropspace/packages/mcp"
9
+ },
5
10
  "type": "module",
6
11
  "main": "dist/index.js",
7
12
  "types": "dist/index.d.ts",