@sellable/mcp 0.1.72 → 0.1.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index-dev.js CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
@@ -19,6 +19,20 @@ type BootstrapBlockingError = {
19
19
  message: string;
20
20
  guidance: string;
21
21
  };
22
+ type CampaignFramework = ReturnType<typeof getCampaignFramework>;
23
+ type BootstrapFrameworkSummary = {
24
+ flowVersion: CampaignFramework["flowVersion"];
25
+ policyChars: number;
26
+ flowWorkflow: string | null;
27
+ flowStepCount: number | null;
28
+ providerOrder: string[];
29
+ providerIds: string[];
30
+ pluginIds: string[];
31
+ overrideKeys: string[];
32
+ warningCount: number;
33
+ warnings: string[];
34
+ loadFullFrameworkWith: string;
35
+ };
22
36
  type BootstrapCreateCampaignResult = {
23
37
  requiredChecks: BootstrapCheck[];
24
38
  blockingErrors: BootstrapBlockingError[];
@@ -28,7 +42,7 @@ type BootstrapCreateCampaignResult = {
28
42
  flowVersion: "v1" | "v2";
29
43
  campaignId: string | null;
30
44
  auth: AuthStatus | null;
31
- framework: ReturnType<typeof getCampaignFramework> | null;
45
+ framework: BootstrapFrameworkSummary | null;
32
46
  campaignContext: Awaited<ReturnType<typeof getCampaignContext>> | null;
33
47
  subskillCatalog: ListSubskillPromptsResponse | null;
34
48
  createCampaignSubskill: Omit<SubskillPromptResponse, "prompt"> | null;
@@ -34,6 +34,40 @@ function pushBlockingError(blockingErrors, check, message) {
34
34
  guidance: toGuidance(check, message),
35
35
  });
36
36
  }
37
+ function summarizeFramework(framework) {
38
+ const flow = framework.flow;
39
+ const steps = Array.isArray(flow.steps) ? flow.steps : null;
40
+ const providerOrder = Array.isArray(framework.providerRegistry.defaultProviderOrder)
41
+ ? framework.providerRegistry.defaultProviderOrder
42
+ : [];
43
+ const providerIds = Array.isArray(framework.providerRegistry.providers)
44
+ ? framework.providerRegistry.providers
45
+ .map((provider) => provider.id)
46
+ .filter((id) => typeof id === "string")
47
+ : Object.keys(framework.providers || {});
48
+ const pluginIds = Array.isArray(framework.plugins)
49
+ ? framework.plugins
50
+ .map((plugin) => plugin && typeof plugin === "object" && "id" in plugin
51
+ ? plugin.id
52
+ : null)
53
+ .filter((id) => typeof id === "string")
54
+ : [];
55
+ return {
56
+ flowVersion: framework.flowVersion,
57
+ policyChars: framework.policy.length,
58
+ flowWorkflow: typeof flow.workflow === "string" ? flow.workflow : null,
59
+ flowStepCount: steps ? steps.length : null,
60
+ providerOrder,
61
+ providerIds,
62
+ pluginIds,
63
+ overrideKeys: Object.keys(framework.overrides || {}),
64
+ warningCount: framework.warnings.length,
65
+ warnings: framework.warnings,
66
+ loadFullFrameworkWith: framework.flowVersion === "v2"
67
+ ? 'get_subskill_asset({ subskillName: "create-campaign-v2", assetPath: "core/flow.v2.json", offset?, limit? })'
68
+ : 'get_campaign_framework({ flowVersion: "v1", includePlugins: true })',
69
+ };
70
+ }
37
71
  export const bootstrapToolDefinitions = [
38
72
  {
39
73
  name: "bootstrap_create_campaign",
@@ -257,7 +291,7 @@ export async function bootstrapCreateCampaign(input = {}) {
257
291
  flowVersion,
258
292
  campaignId,
259
293
  auth,
260
- framework,
294
+ framework: framework ? summarizeFramework(framework) : null,
261
295
  campaignContext,
262
296
  subskillCatalog,
263
297
  createCampaignSubskill: subskillMeta,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.72",
3
+ "version": "0.1.74",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",
@@ -161,11 +161,44 @@ gates. Never use it to collect open text input like LinkedIn URLs, company
161
161
  domains, notes, pasted context, campaign ideas, or feedback. For open text, ask
162
162
  in normal chat and wait for the user to paste the value.
163
163
 
164
+ For campaign setup, every structured question is single-choice in both Claude
165
+ Code and Codex. Use mutually exclusive options, set or assume
166
+ `multiSelect: false`, and do not use checkbox or multi-select wording. If the
167
+ user needs a blended/custom answer, route them through `Other / custom` or a
168
+ free-text follow-up in normal chat.
169
+
164
170
  Customer-facing language must call this "a couple setup choices" during normal
165
171
  campaign progress. Use "quick question panel" only when explaining a missing
166
172
  Codex/Claude setup capability. Do not tell customers about `request_user_input`,
167
173
  Default mode, plugin caches, prompt loading, or skill file versions.
168
174
 
175
+ ## Host Runtime Functions
176
+
177
+ Treat host capabilities as concrete functions, not prose conventions:
178
+
179
+ - `ask_user`: Claude Code uses `AskUserQuestion`; Codex uses
180
+ `request_user_input`. Use this for multiple-choice intake, campaign-focus
181
+ choices, source decisions, and approvals. Campaign setup questions are
182
+ single-choice only; do not use multi-select or checkbox variants. Never
183
+ render numbered plain-chat choices in an interactive session when the
184
+ structured question function is exposed.
185
+ - `load_subprompt`: call
186
+ `mcp__sellable__get_subskill_prompt({ subskillName, offset?, limit? })` and
187
+ continue chunks until `hasMore` is false.
188
+ - `load_subprompt_asset`: call
189
+ `mcp__sellable__get_subskill_asset({ subskillName, assetPath, offset?, limit? })`
190
+ and continue chunks until `hasMore` is false.
191
+ - `load_source_scout_registry`: call
192
+ `mcp__sellable__get_source_scout_registry({})` before any scout dispatch.
193
+ - `launch_source_scout`: Claude Code uses `Task` with `subagent_type` equal to
194
+ the registry `name`; Codex uses named custom agents such as
195
+ `source-scout-linkedin-engagement`, `source-scout-sales-nav`, and
196
+ `source-scout-prospeo-contact` when subagents are available.
197
+
198
+ If a required interactive host function is missing, stop and explain the
199
+ Sellable install/reload problem. Do not silently simulate structured choices,
200
+ subprompt loading, or source-scout dispatch with local scripts.
201
+
169
202
  Never narrate local draft housekeeping to the user. If you create directories,
170
203
  save drafts, write artifacts, or persist intermediate state, translate it into
171
204
  the campaign benefit: consistent brief, approved lead source, reviewed message,
@@ -84,10 +84,15 @@ Validated draft directory:
84
84
  `[features].default_mode_request_user_input = true`, not available in
85
85
  `codex exec`). Treat them as equivalent approval/intake gates and persist the
86
86
  same draft artifacts after the user answers. Use this structured gate only for
87
- multiple-choice decisions or approval gates. Never use it to collect open text
88
- input like LinkedIn URLs, company domains, notes, pasted context, campaign
89
- ideas, or feedback. For open text, ask in normal chat and wait for the user to
90
- paste the value. If an interactive
87
+ single-choice decisions with fixed options or approval gates. Every campaign
88
+ setup gate must be single-choice in both hosts: set or assume
89
+ `multiSelect: false`, use mutually exclusive option labels, and do not ask
90
+ for checkbox or multi-select answers. If a blended answer is useful, present
91
+ the blend as one explicit option or route the user through `Other / custom`
92
+ and a free-text follow-up in chat. Never use it to collect open text input
93
+ like LinkedIn URLs, company domains, notes, pasted context, campaign ideas,
94
+ or feedback. For open text, ask in normal chat and wait for the user to paste
95
+ the value. If an interactive
91
96
  Codex session does not expose `request_user_input`, do not silently degrade to
92
97
  a plain chat question; stop and tell the user:
93
98
 
@@ -234,12 +239,30 @@ Validated draft directory:
234
239
 
235
240
  Sender options should include connected sender names if available, `same as
236
241
  me`, `I’ll paste a different sender profile`, and `Other / custom`.
237
-
238
- After the user confirms the subject and sender, run one lightweight company
239
- lookup if it has not already run, then ask the campaign setup questions. The
240
- setup questions should use the confirmed company context so they do not feel
241
- generic. If identity is still unavailable, use neutral/custom intake options
242
- instead of guessed vertical-specific options.
242
+ When the answer can be represented as choices, ask it with the host-native
243
+ structured question gate. Do not render sender or campaign-focus choices as a
244
+ numbered plain-chat list in interactive Claude Code or Codex. Plain chat is
245
+ only for free-text values like a pasted LinkedIn URL, company domain, CSV
246
+ path, or custom explanation.
247
+
248
+ After the user confirms the subject and sender, check whether the company
249
+ context implies more than one campaignable product line, service, or offer.
250
+ If so, ask one structured campaign-focus question before the setup packet
251
+ (for example, "Which Sellable offer is this campaign for?") with no more than
252
+ three specific options plus `Other / custom`. Do not ask this as plain
253
+ numbered chat.
254
+
255
+ Then run one lightweight sender/company research pass before asking buyer,
256
+ offer, proof, or source questions. Prefer loading
257
+ `get_subskill_prompt({ subskillName: "research-sender" })`, running its
258
+ fetch/profile/company/WebSearch batch when those tools are exposed, and
259
+ calling `complete_sender_research`. If WebSearch is unavailable, use the
260
+ available MCP profile/company/post tools, call `complete_sender_research`
261
+ with the evidence counts found, and continue with explicit gaps. The setup
262
+ questions should use the confirmed company context and researched proof /
263
+ positioning options so they do not feel generic. If identity is still
264
+ unavailable, use neutral/custom intake options instead of guessed
265
+ vertical-specific options.
243
266
 
244
267
  - Before the identity gate, use this customer-facing shape:
245
268
 
@@ -272,9 +295,10 @@ me`, `I’ll paste a different sender profile`, and `Other / custom`.
272
295
  draft-directory inspection/creation. `list_senders` is allowed once before the
273
296
  first identity gate as a quiet token/sender inference shortcut, and once means
274
297
  once: do not call it again after a LinkedIn lookup if it already ran. Do
275
- draft-directory setup only after the founder answers. After launch identity is
276
- confirmed, the setup packet must ask buyer, offer/CTA, proof, and lead source,
277
- and all four questions must include an `Other / custom` option.
298
+ draft-directory setup only after the founder answers. After launch identity,
299
+ sender, and any ambiguous campaign focus are confirmed, the setup packet must
300
+ use the structured question gate and ask buyer, offer/CTA, proof, and lead
301
+ source. All four questions must include an `Other / custom` option.
278
302
  - After the founder answers the first strategy/source packet, explain the next
279
303
  stage only: campaign brief creation and brief approval. Use this shape:
280
304
 
@@ -437,6 +461,14 @@ should test for this campaign. Those can run in parallel and usually take
437
461
  `lead-sample.json`). Approval waits for both `lead-filter.md` and
438
462
  `message-validation.md`, then reconciles that the selected message basis rows
439
463
  still pass the final filter.
464
+ - `lead-sample.json` from `find leads` is always the message sample source.
465
+ `filter leads` must not create a different message sample or cause message
466
+ generation to fetch new prospects. The filter only marks which find-leads
467
+ sample rows are valid, names false-positive patterns, and provides the
468
+ production keep/exclude rules. Message generation may start prep or
469
+ provisional candidate work from probable good-fit rows in `lead-sample.json`,
470
+ but the final `message-validation.md` winner must cite basis rows from
471
+ `lead-sample.json` that still pass `lead-filter.md`.
440
472
  - Parallel means real parallel execution, not optimistic progress copy. For the
441
473
  lead-source scout, first call `get_source_scout_registry` and use the
442
474
  returned canonical `name` values. In Codex, explicitly spawn one named custom scout per
@@ -444,10 +476,10 @@ should test for this campaign. Those can run in parallel and usually take
444
476
  (display: LinkedIn Engagement Scout, powered by the `signal-discovery`
445
477
  provider prompt), `source-scout-sales-nav` (Sales Nav Scout), and
446
478
  `source-scout-prospeo-contact` (Prospeo Contact Scout). For Claude Code, explicitly
447
- invoke the generated `.claude/agents/source-scout-*.md` Task/Agent subagents
448
- for all credible lanes in one assistant message; they are installed from the
449
- same canonical Sellable agent registry and carry explicit Sellable MCP tool
450
- allowlists. The parent thread should not preload every provider prompt before
479
+ invoke the generated `source-scout-*` Task/Agent subagents installed by
480
+ `@sellable/install` for all credible lanes in one assistant message; they are
481
+ installed from the same canonical Sellable agent registry and carry explicit
482
+ Sellable MCP tool allowlists. The parent thread should not preload every provider prompt before
451
483
  spawning scouts; each scout loads only its own provider prompt. If host
452
484
  subagents are unavailable, use independent MCP/tool calls
453
485
  in the same model turn or dedicated Sellable MCP tools that perform
@@ -541,11 +573,19 @@ emphasis, tone, lead-source preference), always make it clear the user can give
541
573
  a custom answer. Add an explicit `Other / custom` option to each subjective
542
574
  question. Do not rely on prose like "you can add detail" as the only custom
543
575
  path. Do not add custom options to the final six-choice commit gate.
576
+ Every subjective setup question is single-choice in both Claude Code and
577
+ Codex. Use mutually exclusive options, set or assume `multiSelect: false`, and
578
+ never use checkbox or multi-select wording that invites selecting several
579
+ options. If a blended answer is likely, make the blend a single named option or
580
+ route it through `Other / custom` and a normal-chat follow-up.
581
+ Do not batch setup as a Claude Code multi-select wizard; buyer, offer/CTA,
582
+ proof, and lead source remain single-choice questions even when the host
583
+ displays them in a sequence.
544
584
  Use customer-facing question wording:
545
585
 
546
- - target prospects: `Who should be the target prospects for this campaign? Pick one or combine.`
547
- - main CTA / offer: `What should the main CTA or offer be? Pick one or combine.`
548
- - proof emphasis: `Which proof point would most increase this buyer's confidence in {{company}}? Multi-select fine.`
586
+ - target prospects: `Who should be the target prospects for this campaign?`
587
+ - main CTA / offer: `What should the main CTA or offer be?`
588
+ - proof emphasis: `Which proof point would most increase this buyer's confidence in {{company}}?`
549
589
  - lead source: `How should we get the people for this campaign?`
550
590
 
551
591
  Ask the lead-source question as the last question in the first strategy
@@ -127,6 +127,35 @@
127
127
  "mustNotInferFromNameOnly": true,
128
128
  "fallback": "ask identity gate first; use neutral/custom intake options only when identity remains unavailable"
129
129
  },
130
+ {
131
+ "action": "confirm_ambiguous_campaign_focus_with_structured_gate",
132
+ "when": "after_identity_before_founder_strategy_source_packet",
133
+ "allowedTools": ["AskUserQuestion", "request_user_input"],
134
+ "questionType": "single-choice only; plain chat only for free-text custom detail",
135
+ "multiSelect": false,
136
+ "maxOptions": 4,
137
+ "requiredOption": "Other / custom",
138
+ "chatRenderRule": "If the sender/company context has multiple campaignable product lines, services, or offers, ask exactly one single-choice structured campaign-focus question before buyer/offer/proof/source. Do not ask numbered plain-chat choices when the structured question tool is available. Do not use checkbox or multi-select wording."
139
+ },
140
+ {
141
+ "action": "run_sender_company_research_before_strategy_packet",
142
+ "target": "research-sender",
143
+ "when": "after_identity_and_campaign_focus_before_founder_strategy_source_packet",
144
+ "allowedTools": [
145
+ "get_subskill_prompt",
146
+ "fetch_linkedin_profile",
147
+ "fetch_company",
148
+ "fetch_linkedin_posts",
149
+ "fetch_company_posts",
150
+ "WebFetch",
151
+ "WebSearch",
152
+ "ToolSearch",
153
+ "complete_sender_research"
154
+ ],
155
+ "requiredCompletion": "complete_sender_research",
156
+ "fallback": "If WebSearch is unavailable, use available MCP profile/company/post tools, call complete_sender_research with observed counts, and carry explicit proof gaps into the setup packet.",
157
+ "chatRenderRule": "Use researched company positioning and proof options in the buyer/offer/proof/source setup packet; do not ask generic strategy questions from company name alone."
158
+ },
130
159
  {
131
160
  "action": "render_post_intake_progress_preamble",
132
161
  "after": "founder_strategy_source_packet_answered",
@@ -194,6 +223,7 @@
194
223
  ],
195
224
  "forbiddenOptionLabelReason": "Provider/source mechanics are internal to the 'Find people for me' path; first-time users should choose the job they need done, not the scouting provider.",
196
225
  "customInput": true,
226
+ "multiSelect": false,
197
227
  "lastQuestionInFirstStrategyBatch": true
198
228
  }
199
229
  ],
@@ -479,7 +509,7 @@
479
509
  "parallel only if real parallel branches were launched"
480
510
  ],
481
511
  "timeEstimate": "~2-3 min",
482
- "chatRenderRule": "If real parallel MCP/tool branches or host subagents were actually launched, say: 'I’m kicking off two workstreams now' and list 'Tighten the fit filter' and 'Message generation'. If not, do not mention parallel/background work; say: 'I’ll tighten the filter first, then run message generation from the same approved brief and sample leads.' Never claim parallelism unless parallel execution actually started. User-facing stage name is message generation; message-validation.md is only the internal artifact."
512
+ "chatRenderRule": "If real parallel MCP/tool branches or host subagents were actually launched, say: 'I’m kicking off two workstreams now' and list 'Tighten the fit filter' and 'Message generation from the find-leads sample'. If not, do not mention parallel/background work; say: 'I’ll tighten the filter first, then run message generation from the same find-leads sample.' Never claim parallelism unless parallel execution actually started. User-facing stage name is message generation; message-validation.md is only the internal artifact. Message generation must use lead-sample.json from find-leads; filter leads only gates which of those sampled rows remain valid."
483
513
  },
484
514
  {
485
515
  "action": "ask_continue_revise_or_confirm_only_if_needed",
@@ -605,11 +635,12 @@
605
635
  "action": "run_or_reconcile_subskill",
606
636
  "target": "generate-messages",
607
637
  "mode": "DRY MODE",
638
+ "sampleSource": "lead-sample.json from find-leads",
608
639
  "toolCallRequiredBeforeArtifacts": [
609
640
  "get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) until hasMore=false"
610
641
  ],
611
642
  "skipIfFreshArtifactExists": "message-validation.md",
612
- "reconcileWith": "lead-filter.md"
643
+ "reconcileWith": "lead-filter.md; lead-sample.json remains the sample source"
613
644
  },
614
645
  {
615
646
  "action": "write_artifact",
@@ -626,6 +657,7 @@
626
657
  ],
627
658
  "toolRules": [
628
659
  "Before writing message-validation.md, message-review.md, approval-packet.md, or a commit-gate AskUserQuestion, the current run must read 100% of the real generate-messages prompt via chunked get_subskill_prompt({ subskillName: \"generate-messages\", offset, limit }) calls.",
660
+ "Lead Sample Basis must cite rows from lead-sample.json produced by find-leads. lead-filter.md only gates those rows; it is not a source for a new sample.",
629
661
  "Do not hand-write message-validation.md from message-prep.md, message-candidate-drafts.md, or general campaign knowledge.",
630
662
  "message-validation.md must prove the full generate-messages workflow ran: Gold Standard Strategy Map, Proof Inventory, Token Fill Rules, Token Adherence Table, Angle Drafts, Kill / Combine Review, Finalists, Finalizer Pass, Gold-Standard Quality Gate, Skeptical Prospect Review, Winner Gate, and a raw sendable Selected Winner are required before message-review can recommend approve-message.",
631
663
  "If the Codex-hosted output is plausible but weaker than the loaded gold-standard examples, stop at message-review with revise-messaging. Do not continue to approval or mint just because the mechanical flow worked.",
@@ -234,8 +234,10 @@ The sample set must:
234
234
 
235
235
  - contain 2-3 sample messages
236
236
  - use the find-leads message handoff rows or probable good-fit rows from
237
- `lead-sample.json`; if `lead-filter.md` already exists, use only leads that
238
- still pass it
237
+ `lead-sample.json`; if `lead-filter.md` exists, use only those same
238
+ find-leads sample rows when they still pass it
239
+ - never replace the find-leads sample with a filter-generated sample, fresh
240
+ LinkedIn rows, new Sales Nav previews, or new Prospeo rows
239
241
  - use only supported tokens documented in the brief
240
242
  - contain no unresolved `{{token}}` placeholders
241
243
  - resolve every token used in the sample output
@@ -79,6 +79,11 @@ Required dry-mode contract:
79
79
  - do not mutate DB-backed campaign state
80
80
  - do not fetch fresh web or LinkedIn research
81
81
  - use only `brief.md`, `lead-filter.md`, and `lead-sample.json`
82
+ - treat `lead-sample.json` from find-leads as the message sample source; do not
83
+ ask filter-leads for a new sample, create a new sample, or fetch additional
84
+ prospects for dry-mode message generation
85
+ - use `lead-filter.md` only to decide which find-leads sample rows remain valid
86
+ for the final winner and which false-positive patterns must be avoided
82
87
  - generate 2-3 sample messages inline
83
88
  - write findings to `message-validation.md`
84
89
  - start `message-validation.md` with `Mode: DRY MODE (no DB mutation)`
@@ -94,7 +99,8 @@ Read:
94
99
 
95
100
  - `brief.md`
96
101
  - `lead-filter.md`
97
- - `lead-sample.json`
102
+ - `lead-sample.json` from the find-leads step; this is the only allowed sample
103
+ source for dry-mode message generation
98
104
  - `mcp/sellable/skills/create-campaign-brief/references/phase75-active-runtime-message-pack.md`
99
105
  - `mcp/sellable/skills/create-campaign-v2/references/validation-criteria.md`
100
106
  - `mcp/sellable/skills/create-campaign-v2/references/thomas-revision-filters.md`
@@ -1,9 +0,0 @@
1
- {
2
- "parallelMode": "wide",
3
- "agentCount": 6,
4
- "maxToolCallsPerAgent": 2,
5
- "senderMaxAgents": 2,
6
- "senderMaxToolCallsPerAgent": 3,
7
- "progressMode": true,
8
- "debugMode": true
9
- }