@jclvsh/dropspace 2.43.0 → 2.49.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 +17 -0
- package/dist/cli.js +2 -0
- package/dist/commands/account.js +42 -0
- package/dist/commands/songs.d.ts +3 -0
- package/dist/commands/songs.js +97 -0
- package/dist/index.js +0 -0
- package/dist/server.js +8 -0
- package/dist/tool-policy.js +9 -0
- package/dist/tools/reports.d.ts +5 -0
- package/dist/tools/reports.js +63 -0
- package/dist/tools/songs.d.ts +4 -0
- package/dist/tools/songs.js +71 -0
- package/dist/tools/trial-reels.js +11 -1
- package/dist/workflows.d.ts +15 -0
- package/dist/workflows.js +50 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -382,6 +382,23 @@ Reddit engagement (drafted comments in other communities) is paused: its tools a
|
|
|
382
382
|
| `scan_trial_reels` | Scan an account's reels again and queue the strong ones (local package only) |
|
|
383
383
|
| `update_trial_reel` | Approve, hold, re-caption, or remove a waiting reel, or approve all (send scope to approve; local package only) |
|
|
384
384
|
|
|
385
|
+
### Reports and workflows
|
|
386
|
+
|
|
387
|
+
| Tool | Description |
|
|
388
|
+
| ---- | ----------- |
|
|
389
|
+
| `save_report` | Save a markdown report to the user's reports page (local package only) |
|
|
390
|
+
| `list_reports` | Saved reports, newest first, without bodies (local package only) |
|
|
391
|
+
| `get_report` | One report with its markdown body (local package only) |
|
|
392
|
+
| `list_songs` | Your songs for song to reels, newest first (pro and premium; local package only) |
|
|
393
|
+
| `get_song` | One song with its waveform peaks (local package only) |
|
|
394
|
+
| `add_song` | Add a song from a YouTube or SoundCloud link, or a public audio file url (local package only) |
|
|
395
|
+
| `get_song_lyrics` | The song's lyrics with word timings, transcribed once (local package only) |
|
|
396
|
+
| `delete_song` | Delete a song and its audio file (local package only) |
|
|
397
|
+
|
|
398
|
+
Prompts (local package only): `instagram_audit`, `best_times_plan`, `winners_report`. Each tells the AI app which tools to call and ends with `save_report`.
|
|
399
|
+
|
|
400
|
+
CLI: `dropspace reports list|get|delete`.
|
|
401
|
+
|
|
385
402
|
#### `get_audience` Parameters
|
|
386
403
|
|
|
387
404
|
| Parameter | Type | Required | Description |
|
package/dist/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ import { registerAutopilotCommands } from "./commands/autopilot.js";
|
|
|
19
19
|
import { registerRecommendationCommands } from "./commands/recommendations.js";
|
|
20
20
|
import { registerAudienceCommands } from "./commands/audience.js";
|
|
21
21
|
import { registerAccountCommands } from "./commands/account.js";
|
|
22
|
+
import { registerSongCommands } from "./commands/songs.js";
|
|
22
23
|
const program = new Command()
|
|
23
24
|
.name("dropspace")
|
|
24
25
|
.description("dropspace CLI - manage launches, personas, boosts, and more")
|
|
@@ -42,6 +43,7 @@ try {
|
|
|
42
43
|
registerRecommendationCommands(program, client);
|
|
43
44
|
registerRedditCommands(program, client);
|
|
44
45
|
registerAccountCommands(program, client);
|
|
46
|
+
registerSongCommands(program, client);
|
|
45
47
|
await program.parseAsync(process.argv);
|
|
46
48
|
}
|
|
47
49
|
catch (error) {
|
package/dist/commands/account.js
CHANGED
|
@@ -39,4 +39,46 @@ export function registerAccountCommands(program, client) {
|
|
|
39
39
|
handleError(error);
|
|
40
40
|
}
|
|
41
41
|
});
|
|
42
|
+
const reports = program.command("reports").description("your saved reports (from the MCP workflows or the API)");
|
|
43
|
+
reports
|
|
44
|
+
.command("list")
|
|
45
|
+
.description("saved reports, newest first, without their bodies")
|
|
46
|
+
.option("--page <n>", "page number")
|
|
47
|
+
.option("--page-size <n>", "reports per page (max 100)")
|
|
48
|
+
.action(async (options) => {
|
|
49
|
+
try {
|
|
50
|
+
const params = {};
|
|
51
|
+
if (options.page)
|
|
52
|
+
params.page = options.page;
|
|
53
|
+
if (options.pageSize)
|
|
54
|
+
params.page_size = options.pageSize;
|
|
55
|
+
formatOutput(await client.get("/reports", params), getFormat(program.opts()));
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
handleError(error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
reports
|
|
62
|
+
.command("get <id>")
|
|
63
|
+
.description("one report with its markdown body")
|
|
64
|
+
.action(async (id) => {
|
|
65
|
+
try {
|
|
66
|
+
formatOutput(await client.get(`/reports/${id}`), getFormat(program.opts()));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
handleError(error);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
reports
|
|
73
|
+
.command("delete <id>")
|
|
74
|
+
.description("delete a saved report")
|
|
75
|
+
.action(async (id) => {
|
|
76
|
+
try {
|
|
77
|
+
await client.delete(`/reports/${id}`);
|
|
78
|
+
formatOutput({ data: { id, deleted: true } }, getFormat(program.opts()));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
handleError(error);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
42
84
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { formatOutput, handleError, getFormat } from "../cli-utils.js";
|
|
2
|
+
export function registerSongCommands(program, client) {
|
|
3
|
+
const songs = program.command("songs").description("your songs for song-to-reels (pro and premium)");
|
|
4
|
+
songs
|
|
5
|
+
.command("list")
|
|
6
|
+
.description("your songs, newest first")
|
|
7
|
+
.option("--page <n>", "page number")
|
|
8
|
+
.option("--page-size <n>", "songs per page (max 100)")
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
try {
|
|
11
|
+
const params = {};
|
|
12
|
+
if (opts.page)
|
|
13
|
+
params.page = opts.page;
|
|
14
|
+
if (opts.pageSize)
|
|
15
|
+
params.page_size = opts.pageSize;
|
|
16
|
+
formatOutput(await client.get("/songs", params), getFormat(program.opts()));
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
handleError(error);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
songs
|
|
23
|
+
.command("get <id>")
|
|
24
|
+
.description("one song, with its waveform peaks")
|
|
25
|
+
.action(async (id) => {
|
|
26
|
+
try {
|
|
27
|
+
formatOutput(await client.get(`/songs/${id}`), getFormat(program.opts()));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
handleError(error);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
songs
|
|
34
|
+
.command("add")
|
|
35
|
+
.description("add a song from a youtube or soundcloud link, or a public audio file url")
|
|
36
|
+
.option("--link <url>", "a youtube or soundcloud link")
|
|
37
|
+
.option("--audio <url>", "a public audio file url (mp3, m4a, wav, aac, flac, ogg)")
|
|
38
|
+
.option("-t, --title <text>", "the title; the link's or file's name otherwise")
|
|
39
|
+
.action(async (opts) => {
|
|
40
|
+
try {
|
|
41
|
+
if (!opts.link && !opts.audio)
|
|
42
|
+
throw new Error("give --link or --audio");
|
|
43
|
+
const body = opts.link ? { link_url: opts.link } : { audio_url: opts.audio };
|
|
44
|
+
if (opts.title)
|
|
45
|
+
body.title = opts.title;
|
|
46
|
+
formatOutput(await client.post("/songs", body), getFormat(program.opts()));
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
handleError(error);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
songs
|
|
53
|
+
.command("lyrics <id>")
|
|
54
|
+
.description("the song's lyrics with word timings (transcribed on first request)")
|
|
55
|
+
.action(async (id) => {
|
|
56
|
+
try {
|
|
57
|
+
formatOutput(await client.get(`/songs/${id}/lyrics`), getFormat(program.opts()));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
handleError(error);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
songs
|
|
64
|
+
.command("rename <id>")
|
|
65
|
+
.description("rename a song")
|
|
66
|
+
.requiredOption("-t, --title <text>", "the new title")
|
|
67
|
+
.action(async (id, opts) => {
|
|
68
|
+
try {
|
|
69
|
+
formatOutput(await client.patch(`/songs/${id}`, { title: opts.title }), getFormat(program.opts()));
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
handleError(error);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
songs
|
|
76
|
+
.command("retry <id>")
|
|
77
|
+
.description("run a failed import again")
|
|
78
|
+
.action(async (id) => {
|
|
79
|
+
try {
|
|
80
|
+
formatOutput(await client.post(`/songs/${id}/retry`), getFormat(program.opts()));
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
handleError(error);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
songs
|
|
87
|
+
.command("delete <id>")
|
|
88
|
+
.description("delete a song and its audio file")
|
|
89
|
+
.action(async (id) => {
|
|
90
|
+
try {
|
|
91
|
+
formatOutput(await client.delete(`/songs/${id}`), getFormat(program.opts()));
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
handleError(error);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/server.js
CHANGED
|
@@ -18,6 +18,8 @@ import { registerAudienceTools } from "./tools/audience.js";
|
|
|
18
18
|
import { registerCommentTools } from "./tools/comments.js";
|
|
19
19
|
import { registerAccountTools } from "./tools/account.js";
|
|
20
20
|
import { registerTrialReelTools } from "./tools/trial-reels.js";
|
|
21
|
+
import { registerReportTools, registerWorkflowPrompts } from "./tools/reports.js";
|
|
22
|
+
import { registerSongTools } from "./tools/songs.js";
|
|
21
23
|
/** Drops tools whose feature is paused before the policy registrar sees them (see PAUSED_TOOLS). */
|
|
22
24
|
function skipPausedTools(registrar) {
|
|
23
25
|
return {
|
|
@@ -48,5 +50,11 @@ export function createDropspaceServer({ client, version, profile }) {
|
|
|
48
50
|
registerCommentTools(registrar, client);
|
|
49
51
|
registerAccountTools(registrar, client);
|
|
50
52
|
registerTrialReelTools(registrar, client);
|
|
53
|
+
registerReportTools(registrar, client);
|
|
54
|
+
registerSongTools(registrar, client);
|
|
55
|
+
// Prompts are not tools, so the policy registrar does not filter them: the hosted profile
|
|
56
|
+
// (frozen for the Meta review) gets none.
|
|
57
|
+
if (profile === "full")
|
|
58
|
+
registerWorkflowPrompts(server);
|
|
51
59
|
return server;
|
|
52
60
|
}
|
package/dist/tool-policy.js
CHANGED
|
@@ -133,6 +133,15 @@ const FULL_TOOL_POLICY = {
|
|
|
133
133
|
update_trial_reel_program: localOnly("audience", "sensitive"),
|
|
134
134
|
scan_trial_reels: localOnly("audience", "write"),
|
|
135
135
|
update_trial_reel: localOnly("audience", "sensitive"),
|
|
136
|
+
save_report: localOnly("account", "write"),
|
|
137
|
+
list_reports: localOnly("account", "read"),
|
|
138
|
+
get_report: localOnly("account", "read"),
|
|
139
|
+
// songs (song to reels): local-only while the hosted set is frozen for the Meta review
|
|
140
|
+
list_songs: localOnly("media", "read"),
|
|
141
|
+
get_song: localOnly("media", "read"),
|
|
142
|
+
add_song: localOnly("media", "write"),
|
|
143
|
+
get_song_lyrics: localOnly("media", "read"),
|
|
144
|
+
delete_song: localOnly("media", "write"),
|
|
136
145
|
get_usage: hosted("usage", "read"),
|
|
137
146
|
get_changelog: hosted("usage", "read"),
|
|
138
147
|
get_dropspace_status: hosted("dropspace", "read"),
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { DropspaceClient } from "../client.js";
|
|
3
|
+
/** The workflow prompts (Instagram audit, best-times plan, winners report). Local package only. */
|
|
4
|
+
export declare function registerWorkflowPrompts(server: McpServer): void;
|
|
5
|
+
export declare function registerReportTools(server: McpServer, client: DropspaceClient): void;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { WORKFLOWS } from "../workflows.js";
|
|
3
|
+
function text(result) {
|
|
4
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
5
|
+
}
|
|
6
|
+
function fail(error) {
|
|
7
|
+
return {
|
|
8
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
9
|
+
isError: true,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/** The workflow prompts (Instagram audit, best-times plan, winners report). Local package only. */
|
|
13
|
+
export function registerWorkflowPrompts(server) {
|
|
14
|
+
for (const workflow of WORKFLOWS) {
|
|
15
|
+
server.registerPrompt(workflow.name, { title: workflow.title, description: workflow.description }, () => ({
|
|
16
|
+
description: workflow.description,
|
|
17
|
+
messages: [{ role: "user", content: { type: "text", text: workflow.prompt } }],
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function registerReportTools(server, client) {
|
|
22
|
+
server.tool("save_report", "save a markdown report to the user's dropspace reports (the reports page in the app): the end step of the instagram_audit, best_times_plan, and winners_report workflows. title up to 200 characters, body up to 100,000. at most 200 saved reports; past that the save is refused and the user deletes some first. returns the saved report with its id.", {
|
|
23
|
+
title: z.string().min(1).max(200).describe("report title, e.g. 'Instagram audit - 10/29/2026'"),
|
|
24
|
+
body: z.string().min(1).max(100_000).describe("the report in markdown"),
|
|
25
|
+
kind: z.enum(["audit", "best_times", "winners", "other"]).optional().describe("which workflow made it"),
|
|
26
|
+
}, async ({ title, body, kind }) => {
|
|
27
|
+
try {
|
|
28
|
+
return text(await client.post("/reports", {
|
|
29
|
+
title,
|
|
30
|
+
body,
|
|
31
|
+
kind: kind ?? "other",
|
|
32
|
+
source: "mcp",
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return fail(error);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
server.tool("list_reports", "the user's saved reports, newest first, without their bodies: id, title, kind (audit, best_times, winners, other), source (mcp or api), created_at. read one with get_report.", {
|
|
40
|
+
page: z.number().int().min(1).optional(),
|
|
41
|
+
page_size: z.number().int().min(1).max(100).optional(),
|
|
42
|
+
}, async ({ page, page_size }) => {
|
|
43
|
+
try {
|
|
44
|
+
const params = {};
|
|
45
|
+
if (page)
|
|
46
|
+
params.page = String(page);
|
|
47
|
+
if (page_size)
|
|
48
|
+
params.page_size = String(page_size);
|
|
49
|
+
return text(await client.get("/reports", params));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return fail(error);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
server.tool("get_report", "one saved report with its markdown body.", { id: z.string().uuid().describe("report id from list_reports or save_report") }, async ({ id }) => {
|
|
56
|
+
try {
|
|
57
|
+
return text(await client.get(`/reports/${id}`));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return fail(error);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { DropspaceClient } from "../client.js";
|
|
3
|
+
/** Songs for song-to-reels (pro and premium). Local package only. */
|
|
4
|
+
export declare function registerSongTools(server: McpServer, client: DropspaceClient): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
function text(result) {
|
|
3
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
4
|
+
}
|
|
5
|
+
function fail(error) {
|
|
6
|
+
return {
|
|
7
|
+
content: [{ type: "text", text: `error: ${error instanceof Error ? error.message : String(error)}` }],
|
|
8
|
+
isError: true,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Songs for song-to-reels (pro and premium). Local package only. */
|
|
12
|
+
export function registerSongTools(server, client) {
|
|
13
|
+
server.tool("list_songs", "the user's songs, newest first: id, title, source (upload, link, or url), source_url (the link or audio url it came from), status (pending while the audio is fetched and converted, ready, or failed with error), duration_seconds, size_bytes, audio_url once ready, lyrics_status (none until lyrics are first requested, ready, or failed), created_at. at most 100 songs per user. pro and premium plans.", {
|
|
14
|
+
page: z.number().int().min(1).optional(),
|
|
15
|
+
page_size: z.number().int().min(1).max(100).optional(),
|
|
16
|
+
}, async ({ page, page_size }) => {
|
|
17
|
+
try {
|
|
18
|
+
const params = {};
|
|
19
|
+
if (page)
|
|
20
|
+
params.page = String(page);
|
|
21
|
+
if (page_size)
|
|
22
|
+
params.page_size = String(page_size);
|
|
23
|
+
return text(await client.get("/songs", params));
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
return fail(error);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
server.tool("get_song", "one song with its waveform: everything list_songs shows plus peaks (400 numbers from 0 to 1 across the song, for picking a snippet by ear and eye).", { song_id: z.string().uuid() }, async ({ song_id }) => {
|
|
30
|
+
try {
|
|
31
|
+
return text(await client.get(`/songs/${song_id}`));
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
return fail(error);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
server.tool("add_song", "add a song from a youtube or soundcloud link, or from a public audio file url (mp3, m4a, wav, aac, flac, ogg, up to 50 MB). the audio is fetched and converted in the background (longer than 15 minutes is cut there): the song comes back pending; read it again with get_song until it is ready or failed (a youtube refusal says to upload the file instead). the song must be the user's own. pro and premium plans; at most 100 songs.", {
|
|
38
|
+
link_url: z.string().url().optional().describe("a youtube or soundcloud link"),
|
|
39
|
+
audio_url: z.string().url().optional().describe("a public audio file url, when not using a link"),
|
|
40
|
+
title: z.string().min(1).max(200).optional().describe("the title; the link's or file's name otherwise"),
|
|
41
|
+
}, async ({ link_url, audio_url, title }) => {
|
|
42
|
+
try {
|
|
43
|
+
if (!link_url && !audio_url)
|
|
44
|
+
throw new Error("give link_url or audio_url");
|
|
45
|
+
const body = link_url ? { link_url } : { audio_url };
|
|
46
|
+
if (title)
|
|
47
|
+
body.title = title;
|
|
48
|
+
return text(await client.post("/songs", body));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return fail(error);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
server.tool("get_song_lyrics", "the song's lyrics with word timings, transcribed on first request (takes up to a minute for a long song) and kept: language, text, and words with start and end seconds. a song with no recognizable words comes back with empty words. the song must be ready.", { song_id: z.string().uuid() }, async ({ song_id }) => {
|
|
55
|
+
try {
|
|
56
|
+
return text(await client.get(`/songs/${song_id}/lyrics`));
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return fail(error);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
server.tool("delete_song", "delete a song and its audio file.", { song_id: z.string().uuid() }, async ({ song_id }) => {
|
|
63
|
+
try {
|
|
64
|
+
await client.delete(`/songs/${song_id}`);
|
|
65
|
+
return text({ id: song_id, deleted: true });
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
return fail(error);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -28,6 +28,11 @@ export function registerTrialReelTools(server, client) {
|
|
|
28
28
|
.optional()
|
|
29
29
|
.describe("never queue a reel whose caption contains one of these"),
|
|
30
30
|
timezone: z.string().optional().describe("IANA timezone for posting hours, e.g. America/New_York"),
|
|
31
|
+
edit_kinds: z
|
|
32
|
+
.array(z.enum(["reshare", "background", "subtitles", "hook", "sticker"]))
|
|
33
|
+
.min(1)
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("kinds new reels take in turn: reshare (the original video), background (on a colored background), subtitles (burned-in; only reels with speech and no captions on screen), hook (a strong line from later in the reel played first), or sticker (a reply-to-comment card with a comment claude writes)"),
|
|
31
36
|
}, async ({ connection_id, ...settings }) => {
|
|
32
37
|
try {
|
|
33
38
|
return text(await client.put(`/trial-reels/programs/${connection_id}`, settings));
|
|
@@ -48,13 +53,17 @@ export function registerTrialReelTools(server, client) {
|
|
|
48
53
|
id: z.string().uuid().optional().describe("item id from list_trial_reels (not needed with approve_all)"),
|
|
49
54
|
approved: z.boolean().optional(),
|
|
50
55
|
caption: z.string().max(2200).optional(),
|
|
56
|
+
kind: z
|
|
57
|
+
.enum(["reshare", "background", "subtitles", "hook", "sticker"])
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("post the original (reshare) or an edited version (background, subtitles, hook, sticker); a new kind holds the reel"),
|
|
51
60
|
remove: z.boolean().optional(),
|
|
52
61
|
approve_all: z
|
|
53
62
|
.string()
|
|
54
63
|
.uuid()
|
|
55
64
|
.optional()
|
|
56
65
|
.describe("instead of one item: a connection_id whose waiting reels with captions are all approved"),
|
|
57
|
-
}, async ({ id, approved, caption, remove, approve_all }) => {
|
|
66
|
+
}, async ({ id, approved, caption, kind, remove, approve_all }) => {
|
|
58
67
|
try {
|
|
59
68
|
if (approve_all) {
|
|
60
69
|
return text(await client.post(`/trial-reels/programs/${approve_all}/approve-all`));
|
|
@@ -68,6 +77,7 @@ export function registerTrialReelTools(server, client) {
|
|
|
68
77
|
return text(await client.patch(`/trial-reels/items/${id}`, {
|
|
69
78
|
...(approved !== undefined ? { approved } : {}),
|
|
70
79
|
...(caption !== undefined ? { caption } : {}),
|
|
80
|
+
...(kind !== undefined ? { kind } : {}),
|
|
71
81
|
}));
|
|
72
82
|
}
|
|
73
83
|
catch (error) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow prompts: ready-made instructions an AI app (Claude, ChatGPT, any MCP client) runs
|
|
3
|
+
* against the dropspace MCP tools, ending with save_report. The MCP server serves them as MCP
|
|
4
|
+
* prompts, and the docs page imports this file (@jclvsh/dropspace/workflows) to show the same text.
|
|
5
|
+
*/
|
|
6
|
+
export interface Workflow {
|
|
7
|
+
/** MCP prompt name. */
|
|
8
|
+
name: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
/** The report kind save_report is asked to use. */
|
|
12
|
+
kind: "audit" | "best_times" | "winners";
|
|
13
|
+
prompt: string;
|
|
14
|
+
}
|
|
15
|
+
export declare const WORKFLOWS: Workflow[];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow prompts: ready-made instructions an AI app (Claude, ChatGPT, any MCP client) runs
|
|
3
|
+
* against the dropspace MCP tools, ending with save_report. The MCP server serves them as MCP
|
|
4
|
+
* prompts, and the docs page imports this file (@jclvsh/dropspace/workflows) to show the same text.
|
|
5
|
+
*/
|
|
6
|
+
export const WORKFLOWS = [
|
|
7
|
+
{
|
|
8
|
+
name: "instagram_audit",
|
|
9
|
+
title: "Instagram audit",
|
|
10
|
+
description: "where the account stands: growth, who it reaches, what its reels have in common, what to fix first",
|
|
11
|
+
kind: "audit",
|
|
12
|
+
prompt: [
|
|
13
|
+
"Audit my Instagram account using the dropspace tools.",
|
|
14
|
+
"1. Call get_audience with include_instagram_insights true and window_days 30: followers, net growth per day, follows vs unfollows, and the share of reach from non-followers.",
|
|
15
|
+
"2. Call get_audience_demographics: who follows and who engages (age, gender, top countries and cities).",
|
|
16
|
+
"3. Call get_reels_read; if there is no read from the last 7 days, call generate_reels_read. Use its findings on what the strong and weak reels share.",
|
|
17
|
+
"4. Call get_best_posting_hours for when followers are online.",
|
|
18
|
+
"Write the audit in markdown with these sections: Where the account stands (numbers, with dates), Who it reaches, What works, What doesn't, The three changes to make this week. Quote numbers exactly as the tools returned them and say when a tool had no data.",
|
|
19
|
+
'Then call save_report with kind "audit" and a title like "Instagram audit - <today\'s date>".',
|
|
20
|
+
].join("\n"),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "best_times_plan",
|
|
24
|
+
title: "Best-times plan",
|
|
25
|
+
description: "a weekly posting schedule built from when the followers are online",
|
|
26
|
+
kind: "best_times",
|
|
27
|
+
prompt: [
|
|
28
|
+
"Build my weekly Instagram posting plan using the dropspace tools.",
|
|
29
|
+
"1. Call get_best_posting_hours with my timezone: the hourly averages, the weekday-by-hour grid, and the busiest hours.",
|
|
30
|
+
"2. Call get_autopilot to see the posting times autopilot uses now, if it is set up.",
|
|
31
|
+
"3. Call list_launches for the last 30 days to see when I actually posted.",
|
|
32
|
+
"Write the plan in markdown: a table of days and times (at most 2 a day, at least 2 hours apart), why each time was picked (followers online at that hour), and what to change from the current schedule. Say plainly if Instagram had no online-followers numbers (accounts under 100 followers) and fall back to 9 am, 1 pm, and 6 pm.",
|
|
33
|
+
'Then call save_report with kind "best_times" and a title like "Posting plan - week of <date>".',
|
|
34
|
+
].join("\n"),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "winners_report",
|
|
38
|
+
title: "Winners report",
|
|
39
|
+
description: "the posts and trial reels that won lately, what they share, and what to make next",
|
|
40
|
+
kind: "winners",
|
|
41
|
+
prompt: [
|
|
42
|
+
"Find my recent winners using the dropspace tools.",
|
|
43
|
+
"1. Call list_launches for the last 30 days, then get_launch_analytics on the posted ones to get views, likes, comments, shares, and saves.",
|
|
44
|
+
"2. Call get_trial_reel_analytics and list_trial_reels for trial reels and their 1, 3, and 7-day results (won or retired).",
|
|
45
|
+
"3. Call get_reels_read for the latest read of the account's reels.",
|
|
46
|
+
"Rank posts by views relative to the account's usual (the median of the posts you read). Write the report in markdown: the top five with their numbers and links, what they share (topic, hook, format, length, posting time), the bottom three and what they share, and three posts to make next. Use only the numbers the tools returned.",
|
|
47
|
+
'Then call save_report with kind "winners" and a title like "Winners - <month>".',
|
|
48
|
+
].join("\n"),
|
|
49
|
+
},
|
|
50
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jclvsh/dropspace",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.49.0",
|
|
4
4
|
"description": "MCP server and CLI for the Dropspace API",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
"types": "./dist/tool-policy.d.ts",
|
|
28
28
|
"default": "./dist/tool-policy.js"
|
|
29
29
|
},
|
|
30
|
+
"./workflows": {
|
|
31
|
+
"types": "./dist/workflows.d.ts",
|
|
32
|
+
"default": "./dist/workflows.js"
|
|
33
|
+
},
|
|
30
34
|
"./package.json": "./package.json"
|
|
31
35
|
},
|
|
32
36
|
"bin": {
|