@sellable/mcp 0.1.235 → 0.1.237

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
@@ -20,7 +20,7 @@ There are three public Sellable entrypoints shared across hosts:
20
20
 
21
21
  - `sellable:create-campaign`
22
22
  - `sellable:interview`
23
- - `sellable:load-voice`
23
+ - `sellable:create-post`
24
24
 
25
25
  The create-campaign public wrapper at
26
26
  `mcp/sellable/skills/create-campaign/SKILL.md` handles auth/bootstrap and loads
@@ -33,12 +33,15 @@ from:
33
33
 
34
34
  - `mcp/sellable/skills/interview/SKILL.md`
35
35
 
36
- The load-voice public wrapper loads the current voice/company memory for use in
37
- writing, application answers, posts, replies, and reviews from:
36
+ The create-post public wrapper captures raw ideas, loads voice internally,
37
+ researches hooks, validates proof/AI tells, and saves content artifacts under
38
+ `~/.sellable/content/linkedin/**` from:
38
39
 
39
- - `mcp/sellable/skills/load-voice/SKILL.md`
40
+ - `mcp/sellable/skills/create-post/SKILL.md`
40
41
 
41
- Keep `create-campaign-v2` internal. Do not advertise it as a public command.
42
+ Keep `create-campaign-v2` and `load-voice` internal. Do not advertise either as
43
+ a public command. `load-voice` remains a direct MCP utility for generic writing
44
+ surfaces that are not posts or campaigns.
42
45
 
43
46
  ### 1. One-Command Agent Install
44
47
 
@@ -134,8 +137,8 @@ The installer does the full local setup:
134
137
 
135
138
  After the installer passes, fully quit and reopen Codex Desktop. Start a new
136
139
  thread and select `Sellable Create Campaign`, `Sellable Identity Interview`, or
137
- `Sellable Load Voice`; or invoke `$sellable:create-campaign`,
138
- `$sellable:interview`, or `$sellable:load-voice`. If the app still says
140
+ `Sellable Create Post`; or invoke `$sellable:create-campaign`,
141
+ `$sellable:interview`, or `$sellable:create-post`. If the app still says
139
142
  `mcp__sellable__*` tools are missing after the installer passes, check that
140
143
  `~/.codex/config.toml` contains both `[marketplaces.sellable]` and
141
144
  `[plugins."sellable@sellable"]`.
@@ -148,24 +151,26 @@ Use these names consistently:
148
151
 
149
152
  - Claude Code command: `/sellable:create-campaign`
150
153
  - Claude Code command: `/sellable:interview`
151
- - Claude Code command: `/sellable:load-voice`
154
+ - Claude Code command: `/sellable:create-post`
152
155
  - Codex command: `$sellable:create-campaign`
153
156
  - Codex command: `$sellable:interview`
154
- - Codex command: `$sellable:load-voice`
157
+ - Codex command: `$sellable:create-post`
155
158
  - Codex Desktop plugin: `sellable@sellable`
156
159
  - Codex visible skill: `Sellable Create Campaign`
157
160
  - Codex visible skill: `Sellable Identity Interview`
158
- - Codex visible skill: `Sellable Load Voice`
161
+ - Codex visible skill: `Sellable Create Post`
159
162
  - Codex skill frontmatter name: `create-campaign`
160
163
  - Codex skill frontmatter name: `interview`
161
- - Codex skill frontmatter name: `load-voice`
164
+ - Codex skill frontmatter name: `create-post`
162
165
  - MCP server name: `sellable`
163
166
  - Internal workflow prompt: `create-campaign-v2`
164
167
 
165
168
  Never tell users to run `/sellable:create-campaign-v2`,
166
- `$sellable:create-campaign-v2`, or `$sellable:sellable:create-campaign`.
167
- `create-campaign-v2` is an internal MCP subskill loaded by
168
- `get_subskill_prompt({ subskillName: "create-campaign-v2" })`.
169
+ `$sellable:create-campaign-v2`, `$sellable:load-voice`, or
170
+ `$sellable:sellable:create-campaign`. `create-campaign-v2` is an internal MCP
171
+ subskill loaded by `get_subskill_prompt({ subskillName: "create-campaign-v2" })`.
172
+ `load-voice` is an internal direct MCP utility; `create-post` loads voice
173
+ silently for post drafting.
169
174
 
170
175
  ## Structured Question Parity
171
176
 
package/dist/index-dev.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
package/dist/server.js CHANGED
@@ -10,6 +10,7 @@ import { getCampaignTableSchema, queueCampaignCells, reviseMessageTemplateAndRer
10
10
  import { queueCells, updateCell } from "./tools/cells.js";
11
11
  import { handleStartCliLogin, handleWaitForCliLogin, } from "./tools/cli-login.js";
12
12
  import { getCampaignContext, hydrateCampaignContextFromCampaign, markCampaignContextDirty, } from "./tools/context.js";
13
+ import { capturePostIdeaTool, getPostDraftTool, getPostIdeaTool, listPostDraftsTool, listPostIdeasTool, listPublishedPostsTool, markPostPublishedTool, saveHookResearchTool, savePostDraftTool, } from "./tools/content-posts.js";
13
14
  import { addToCommentCampaign, addToConnectionCampaign, addToInmailCampaign, getEngagedPosts, getOrCreateDirectCampaignTable, pauseDirectCampaign, startDirectCampaign, } from "./tools/direct-campaigns.js";
14
15
  import { bootstrapEngage, bootstrapEngageMulti, } from "./tools/engage-bootstrap.js";
15
16
  import { searchEngagementPosts } from "./tools/engage-discovery.js";
@@ -222,6 +223,33 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
222
223
  case "get_campaign_navigation_state":
223
224
  result = await getCampaignNavigationState(args);
224
225
  break;
226
+ case "capture_post_idea":
227
+ result = capturePostIdeaTool(args);
228
+ break;
229
+ case "list_post_ideas":
230
+ result = listPostIdeasTool(args);
231
+ break;
232
+ case "get_post_idea":
233
+ result = getPostIdeaTool(args);
234
+ break;
235
+ case "save_hook_research":
236
+ result = saveHookResearchTool(args);
237
+ break;
238
+ case "save_post_draft":
239
+ result = savePostDraftTool(args);
240
+ break;
241
+ case "list_post_drafts":
242
+ result = listPostDraftsTool(args);
243
+ break;
244
+ case "get_post_draft":
245
+ result = getPostDraftTool(args);
246
+ break;
247
+ case "mark_post_published":
248
+ result = markPostPublishedTool(args);
249
+ break;
250
+ case "list_published_posts":
251
+ result = listPublishedPostsTool(args);
252
+ break;
225
253
  case "get_table_rows":
226
254
  result = await getTableRows(args?.tableId, {
227
255
  limit: args?.limit,
@@ -49,24 +49,27 @@ function getCampaignBuilderWatchModeFromUrl(watchUrl) {
49
49
  export function getCampaignBuilderWatchModeDriverLabel(mode = getCampaignBuilderWatchModeParam()) {
50
50
  return mode === "codex" ? "Codex" : "Claude Code";
51
51
  }
52
- function centerWatchHandoffHeadline(headline) {
53
- const width = 60;
52
+ function buildWatchHandoffHeadlineBox(headline) {
53
+ const width = 52;
54
54
  const visibleHeadline = headline.length > width ? headline.slice(0, width) : headline;
55
55
  const padding = Math.max(width - visibleHeadline.length, 0);
56
56
  const left = Math.floor(padding / 2);
57
57
  const right = padding - left;
58
+ const border = `+${"-".repeat(width + 2)}+`;
58
59
  return [
59
- `╔${"".repeat(width + 2)}╗`,
60
- `║ ${" ".repeat(left)}${visibleHeadline}${" ".repeat(right)} ║`,
61
- `╚${"".repeat(width + 2)}╝`,
60
+ "```text",
61
+ border,
62
+ `| ${" ".repeat(left)}${visibleHeadline}${" ".repeat(right)} |`,
63
+ border,
64
+ "```",
62
65
  ].join("\n");
63
66
  }
64
67
  export function buildCampaignWatchHandoffMarkdown(watchUrl, mode = getCampaignBuilderWatchModeFromUrl(watchUrl) ??
65
68
  getCampaignBuilderWatchModeParam()) {
66
69
  const driverLabel = getCampaignBuilderWatchModeDriverLabel(mode);
67
- const headline = `OPEN THIS LINK TO WATCH ${driverLabel.toUpperCase()} BUILD THE CAMPAIGN LIVE`;
70
+ const headline = `WATCH ${driverLabel.toUpperCase()} BUILD THE CAMPAIGN LIVE`;
68
71
  return [
69
- centerWatchHandoffHeadline(headline),
72
+ buildWatchHandoffHeadlineBox(headline),
70
73
  "",
71
74
  `[Open live campaign builder](${watchUrl})`,
72
75
  "",
@@ -0,0 +1,308 @@
1
+ type ContentKind = "idea" | "hook_research" | "draft" | "published_post";
2
+ type ArtifactMetadata = {
3
+ id: string;
4
+ type: ContentKind;
5
+ status: string;
6
+ createdAt: string;
7
+ updatedAt: string;
8
+ title?: string;
9
+ ideaId?: string;
10
+ hookResearchId?: string;
11
+ publishUrl?: string;
12
+ publishedAt?: string;
13
+ sourceType?: string;
14
+ sourceUrl?: string;
15
+ [key: string]: unknown;
16
+ };
17
+ export type ContentPostSummary = {
18
+ id: string;
19
+ type: ContentKind;
20
+ status: string;
21
+ path: string;
22
+ title?: string;
23
+ updatedAt: string;
24
+ preview: string;
25
+ };
26
+ export declare const contentPostToolDefinitions: ({
27
+ name: string;
28
+ description: string;
29
+ inputSchema: {
30
+ type: string;
31
+ properties: {
32
+ rawSource: {
33
+ type: string;
34
+ description: string;
35
+ };
36
+ title: {
37
+ type: string;
38
+ };
39
+ distilledBrief: {
40
+ type: string;
41
+ description: string;
42
+ };
43
+ ideaId: {
44
+ type: string;
45
+ description: string;
46
+ };
47
+ sourceType: {
48
+ type: string;
49
+ };
50
+ sourceUrl: {
51
+ type: string;
52
+ };
53
+ capturedAt: {
54
+ type: string;
55
+ };
56
+ researchId?: undefined;
57
+ topic?: undefined;
58
+ keywords?: undefined;
59
+ sourcePosts?: undefined;
60
+ selectedPatterns?: undefined;
61
+ notes?: undefined;
62
+ createdAt?: undefined;
63
+ draftId?: undefined;
64
+ hookResearchId?: undefined;
65
+ body?: undefined;
66
+ validationReceipt?: undefined;
67
+ status?: undefined;
68
+ };
69
+ required: string[];
70
+ additionalProperties: boolean;
71
+ };
72
+ } | {
73
+ name: string;
74
+ description: string;
75
+ inputSchema: {
76
+ type: string;
77
+ properties: {
78
+ limit: {
79
+ type: string;
80
+ };
81
+ };
82
+ required: never[];
83
+ additionalProperties: boolean;
84
+ };
85
+ } | {
86
+ name: string;
87
+ description: string;
88
+ inputSchema: {
89
+ type: string;
90
+ properties: {
91
+ [x: string]: {
92
+ type: string;
93
+ };
94
+ };
95
+ required: string[];
96
+ additionalProperties: boolean;
97
+ };
98
+ } | {
99
+ name: string;
100
+ description: string;
101
+ inputSchema: {
102
+ type: string;
103
+ properties: {
104
+ researchId: {
105
+ type: string;
106
+ };
107
+ ideaId: {
108
+ type: string;
109
+ description?: undefined;
110
+ };
111
+ topic: {
112
+ type: string;
113
+ };
114
+ keywords: {
115
+ type: string;
116
+ items: {
117
+ type: string;
118
+ };
119
+ };
120
+ sourcePosts: {
121
+ type: string;
122
+ items: {
123
+ type: string;
124
+ additionalProperties: boolean;
125
+ };
126
+ };
127
+ selectedPatterns: {
128
+ type: string;
129
+ items: {
130
+ type: string;
131
+ };
132
+ };
133
+ notes: {
134
+ type: string;
135
+ };
136
+ createdAt: {
137
+ type: string;
138
+ };
139
+ rawSource?: undefined;
140
+ title?: undefined;
141
+ distilledBrief?: undefined;
142
+ sourceType?: undefined;
143
+ sourceUrl?: undefined;
144
+ capturedAt?: undefined;
145
+ draftId?: undefined;
146
+ hookResearchId?: undefined;
147
+ body?: undefined;
148
+ validationReceipt?: undefined;
149
+ status?: undefined;
150
+ };
151
+ required: never[];
152
+ additionalProperties: boolean;
153
+ };
154
+ } | {
155
+ name: string;
156
+ description: string;
157
+ inputSchema: {
158
+ type: string;
159
+ properties: {
160
+ draftId: {
161
+ type: string;
162
+ };
163
+ ideaId: {
164
+ type: string;
165
+ description?: undefined;
166
+ };
167
+ hookResearchId: {
168
+ type: string;
169
+ };
170
+ title: {
171
+ type: string;
172
+ };
173
+ body: {
174
+ type: string;
175
+ };
176
+ validationReceipt: {
177
+ description: string;
178
+ };
179
+ createdAt: {
180
+ type: string;
181
+ };
182
+ status: {
183
+ type: string;
184
+ };
185
+ rawSource?: undefined;
186
+ distilledBrief?: undefined;
187
+ sourceType?: undefined;
188
+ sourceUrl?: undefined;
189
+ capturedAt?: undefined;
190
+ researchId?: undefined;
191
+ topic?: undefined;
192
+ keywords?: undefined;
193
+ sourcePosts?: undefined;
194
+ selectedPatterns?: undefined;
195
+ notes?: undefined;
196
+ };
197
+ required: string[];
198
+ additionalProperties: boolean;
199
+ };
200
+ })[];
201
+ export declare function resolveContentRoot(): string;
202
+ export declare function ensureContentLayout(root?: string): {
203
+ root: string;
204
+ directories: {
205
+ ideas: "linkedin/ideas";
206
+ hookResearch: "linkedin/research/hooks";
207
+ drafts: "linkedin/drafts";
208
+ published: "linkedin/published";
209
+ commentLibrary: "linkedin/comments/library";
210
+ };
211
+ };
212
+ export declare function capturePostIdeaTool(input: {
213
+ rawSource: string;
214
+ title?: string;
215
+ distilledBrief?: string;
216
+ ideaId?: string;
217
+ sourceType?: string;
218
+ sourceUrl?: string;
219
+ capturedAt?: string;
220
+ }): {
221
+ id: string;
222
+ path: string;
223
+ status: string;
224
+ createdAt: string;
225
+ updatedAt: string;
226
+ preview: string;
227
+ rawSourceExact: boolean;
228
+ };
229
+ export declare function listPostIdeasTool(input?: {
230
+ limit?: number;
231
+ }): ContentPostSummary[];
232
+ export declare function getPostIdeaTool(input: {
233
+ ideaId: string;
234
+ }): {
235
+ id: string;
236
+ path: string;
237
+ metadata: ArtifactMetadata;
238
+ markdown: string;
239
+ rawSource: string | undefined;
240
+ };
241
+ export declare function saveHookResearchTool(input: {
242
+ researchId?: string;
243
+ ideaId?: string;
244
+ topic?: string;
245
+ keywords?: string[];
246
+ sourcePosts?: Array<Record<string, unknown>>;
247
+ selectedPatterns?: string[];
248
+ notes?: string;
249
+ createdAt?: string;
250
+ }): {
251
+ id: string;
252
+ path: string;
253
+ status: string;
254
+ ideaId: string | undefined;
255
+ updatedAt: string;
256
+ preview: string;
257
+ };
258
+ export declare function savePostDraftTool(input: {
259
+ draftId?: string;
260
+ ideaId: string;
261
+ hookResearchId?: string;
262
+ title?: string;
263
+ body: string;
264
+ validationReceipt: unknown;
265
+ createdAt?: string;
266
+ status?: string;
267
+ }): {
268
+ id: string;
269
+ path: string;
270
+ status: string;
271
+ ideaId: string;
272
+ hookResearchId: string | undefined;
273
+ updatedAt: string;
274
+ preview: string;
275
+ };
276
+ export declare function listPostDraftsTool(input?: {
277
+ limit?: number;
278
+ }): ContentPostSummary[];
279
+ export declare function getPostDraftTool(input: {
280
+ draftId: string;
281
+ }): {
282
+ id: string;
283
+ path: string;
284
+ metadata: ArtifactMetadata;
285
+ markdown: string;
286
+ rawSource: string | undefined;
287
+ };
288
+ export declare function markPostPublishedTool(input: {
289
+ draftId?: string;
290
+ publishUrl: string;
291
+ activityId?: string;
292
+ publishedAt?: string;
293
+ finalText?: string;
294
+ title?: string;
295
+ }): {
296
+ id: string;
297
+ path: string;
298
+ status: string;
299
+ draftId: string | undefined;
300
+ publishUrl: string;
301
+ publishedAt: string;
302
+ updatedAt: string;
303
+ preview: string;
304
+ };
305
+ export declare function listPublishedPostsTool(input?: {
306
+ limit?: number;
307
+ }): ContentPostSummary[];
308
+ export {};