@sellable/mcp 0.1.560 → 0.1.562-phase981.202607110535

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
@@ -16,8 +16,9 @@ Each message gets 5+ minutes of Claude attention with deep research - no other t
16
16
 
17
17
  ### Prompt Source Of Truth
18
18
 
19
- There are eight public Sellable entrypoints shared across hosts:
19
+ There are nine public Sellable entrypoints shared across hosts:
20
20
 
21
+ - `sellable:find-leads`
21
22
  - `sellable:create-campaign`
22
23
  - `sellable:create-ab-test`
23
24
  - `sellable:create-evergreen-campaigns`
@@ -33,6 +34,11 @@ the approval-gated workflow from:
33
34
 
34
35
  - `mcp/sellable/skills/create-campaign-v2/SKILL.md`
35
36
 
37
+ The find-leads public wrapper builds a live exportable source list without a
38
+ campaign and loads its durable approval-gated workflow from:
39
+
40
+ - `mcp/sellable/skills/find-leads-v2/SKILL.md`
41
+
36
42
  The create-ab-test public wrapper prepares clean A/B split lead lists and
37
43
  review-copy campaigns from:
38
44
 
@@ -205,7 +211,7 @@ thread and select `Sellable Create Campaign`, `Sellable Create A/B Test`,
205
211
  `Sellable Create Evergreen Campaigns`, `Sellable Foundation`,
206
212
  `Sellable Content`, `Sellable Create Post`, `Sellable Refresh Sender Engagement`,
207
213
  or `Sellable Refill Sends`; or invoke
208
- `$sellable:create-campaign`, `$sellable:create-ab-test`,
214
+ `$sellable:find-leads`, `$sellable:create-campaign`, `$sellable:create-ab-test`,
209
215
  `$sellable:create-evergreen-campaigns`, `$sellable:foundation`,
210
216
  `$sellable:content`, `$sellable:create-post`,
211
217
  `$sellable:refresh-sender-engagement`, or `$sellable:refill-sends`. If the app still says
@@ -220,6 +226,7 @@ the Sellable MCP tools for Codex Desktop.
220
226
  Use these names consistently:
221
227
 
222
228
  - Claude Code command: `/sellable:create-campaign`
229
+ - Claude Code command: `/sellable:find-leads`
223
230
  - Claude Code command: `/sellable:create-ab-test`
224
231
  - Claude Code command: `/sellable:create-evergreen-campaigns`
225
232
  - Claude Code command: `/sellable:foundation`
@@ -228,6 +235,7 @@ Use these names consistently:
228
235
  - Claude Code command: `/sellable:refresh-sender-engagement`
229
236
  - Claude Code command: `/sellable:refill-sends`
230
237
  - Codex command: `$sellable:create-campaign`
238
+ - Codex command: `$sellable:find-leads`
231
239
  - Codex command: `$sellable:create-ab-test`
232
240
  - Codex command: `$sellable:create-evergreen-campaigns`
233
241
  - Codex command: `$sellable:foundation`
@@ -237,6 +245,7 @@ Use these names consistently:
237
245
  - Codex command: `$sellable:refill-sends`
238
246
  - Codex Desktop plugin: `sellable@sellable`
239
247
  - Codex visible skill: `Sellable Create Campaign`
248
+ - Codex visible skill: `Sellable Find Leads`
240
249
  - Codex visible skill: `Sellable Create A/B Test`
241
250
  - Codex visible skill: `Sellable Create Evergreen Campaigns`
242
251
  - Codex visible skill: `Sellable Foundation`
@@ -245,6 +254,7 @@ Use these names consistently:
245
254
  - Codex visible skill: `Sellable Refresh Sender Engagement`
246
255
  - Codex visible skill: `Sellable Refill Sends`
247
256
  - Codex skill frontmatter name: `create-campaign`
257
+ - Codex skill frontmatter name: `find-leads`
248
258
  - Codex skill frontmatter name: `create-ab-test`
249
259
  - Codex skill frontmatter name: `create-evergreen-campaigns`
250
260
  - Codex skill frontmatter name: `foundation`
@@ -254,6 +264,7 @@ Use these names consistently:
254
264
  - Codex skill frontmatter name: `refill-sends`
255
265
  - MCP server name: `sellable`
256
266
  - Internal workflow prompt: `create-campaign-v2`
267
+ - Internal workflow prompt: `find-leads-v2`
257
268
  - Internal workflow prompt: `refill-sends-workflow`
258
269
  - Internal/backward-compatible memory prompt: `interview`
259
270
 
@@ -1027,23 +1027,6 @@ async function gatePlan(input, deps, ctx) {
1027
1027
  fallbackSummary: line.fallbackSummary,
1028
1028
  }));
1029
1029
  const fingerprint = fingerprintFromPlan(plan);
1030
- const approvedTargetShapeRevision = stringValue(input.approval?.targetShapeRevision);
1031
- if (approvedTargetShapeRevision &&
1032
- approvedTargetShapeRevision !== fingerprint.targetShapeRevision) {
1033
- return {
1034
- status: "blocked",
1035
- blocker: "scope_drift",
1036
- runId: ctx.runId,
1037
- fence: ctx.fence,
1038
- gate: ctx.gate,
1039
- guidance: "The canonical refill target shape changed after approval; render a new packet before another primitive.",
1040
- report: {
1041
- approvedTargetShapeRevision,
1042
- freshTargetShapeRevision: fingerprint.targetShapeRevision,
1043
- },
1044
- journalPath: ctx.journalPath,
1045
- };
1046
- }
1047
1030
  const persistedCircuit = recordValue(runStateProgress(ctx).prepCircuit);
1048
1031
  if (stringValue(action.type) === "prepare_messages" &&
1049
1032
  openCircuitWaits(persistedCircuit, deps.now?.() ?? new Date())) {
package/dist/server.js CHANGED
@@ -29,6 +29,7 @@ import { getEngageStateTool, setEngageStateTool, } from "./tools/engage-state.js
29
29
  import { bulkEnrichWithProspeo, enrichWithProspeo, getProspeoCredits, } from "./tools/enrichment.js";
30
30
  import { getRefillPlanV2 } from "./tools/evergreen-refill-plan.js";
31
31
  import { getCampaignFramework } from "./tools/framework.js";
32
+ import { bootstrapFindLeads, cancelFindLeads, getFindLeadsRun, reissueFindLeadsWatchLink, updateFindLeadsRun, waitForFindLeadsRun, } from "./tools/find-leads-runs.js";
32
33
  import { confirmHarvestJobCompanies, searchHarvestJobs, } from "./tools/harvest-jobs.js";
33
34
  import { checkInboxReplyEligibility, getInboxThread, searchInboxThreads, sendInboxDraft, sendInboxManualReply, updateInboxDraft, } from "./tools/inbox.js";
34
35
  import { cancelLeadImport, confirmLeadList, confirmProspeoCompanyAccounts, getProviderPrompt, importLeads, listDncEntriesTool, loadCsvDncEntriesTool, loadCsvDomains, loadCsvLinkedinLeads, lookupSalesNavFilter, saveDomainFilters, searchApollo, searchProspeo, searchProspeoCompanies, searchSalesNav, searchSignals, selectPromisingPosts, setHeadlineICPCriteria, } from "./tools/leads.js";
@@ -36,6 +37,7 @@ import { fetchCompany, fetchCompanyPosts, fetchLinkedInPosts, fetchLinkedInProfi
36
37
  import { getCampaignNavigationState } from "./tools/navigation.js";
37
38
  import { addOnDemandLeads, createOnDemandCampaign, createOnDemandTable, initOnDemandSequence, pauseOnDemandCampaign, startOnDemandCampaign, } from "./tools/one-off.js";
38
39
  import { upsertRubric } from "./tools/processing.js";
40
+ import { preflightFindLeadsProvider } from "./tools/provider-preflight.js";
39
41
  import { completeSenderResearch, getPostFindLeadsScoutRegistry, getSourceScoutRegistry, getSubskillAsset, getSubskillPrompt, listSubskillPrompts, searchSubskillPrompts, } from "./tools/prompts.js";
40
42
  import { waitForCampaignTableReady, waitForLeadListReady, } from "./tools/readiness.js";
41
43
  import { refreshSenderEngagementCommand } from "./tools/refresh-sender-engagement.js";
@@ -146,6 +148,27 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
146
148
  case "wait_for_cli_login":
147
149
  result = await handleWaitForCliLogin(args);
148
150
  break;
151
+ case "bootstrap_find_leads":
152
+ result = await bootstrapFindLeads(args);
153
+ break;
154
+ case "get_find_leads_run":
155
+ result = await getFindLeadsRun(args);
156
+ break;
157
+ case "update_find_leads_run":
158
+ result = await updateFindLeadsRun(args);
159
+ break;
160
+ case "wait_for_find_leads_run":
161
+ result = await waitForFindLeadsRun(args);
162
+ break;
163
+ case "cancel_find_leads":
164
+ result = await cancelFindLeads(args);
165
+ break;
166
+ case "reissue_find_leads_watch_link":
167
+ result = await reissueFindLeadsWatchLink(args);
168
+ break;
169
+ case "preflight_find_leads_provider":
170
+ result = await preflightFindLeadsProvider(args);
171
+ break;
149
172
  case "bootstrap_engage":
150
173
  result = await bootstrapEngage(args);
151
174
  break;
@@ -0,0 +1,151 @@
1
+ type FindLeadsSnapshot = {
2
+ runId: string;
3
+ leadListId?: string;
4
+ version?: number;
5
+ status: string;
6
+ terminal?: boolean;
7
+ cancelled?: boolean;
8
+ [key: string]: unknown;
9
+ };
10
+ export declare const findLeadsRunToolDefinitions: readonly [{
11
+ readonly name: "bootstrap_find_leads";
12
+ readonly description: "Create or resume a durable campaignless Find Leads run and its lead list. No campaign or workingCampaignId is required.";
13
+ readonly inputSchema: {
14
+ readonly type: "object";
15
+ readonly properties: {
16
+ readonly bootstrapKey: {
17
+ readonly type: "string";
18
+ };
19
+ readonly name: {
20
+ readonly type: "string";
21
+ };
22
+ };
23
+ readonly required: readonly ["bootstrapKey"];
24
+ readonly additionalProperties: false;
25
+ };
26
+ }, {
27
+ readonly name: "get_find_leads_run";
28
+ readonly description: "Read the authoritative durable Find Leads snapshot.";
29
+ readonly inputSchema: {
30
+ readonly type: "object";
31
+ readonly properties: {
32
+ readonly runId: {
33
+ readonly type: "string";
34
+ };
35
+ };
36
+ readonly required: readonly ["runId"];
37
+ readonly additionalProperties: false;
38
+ };
39
+ }, {
40
+ readonly name: "update_find_leads_run";
41
+ readonly description: "Persist a provider/source/import approval receipt using optimistic concurrency.";
42
+ readonly inputSchema: {
43
+ readonly type: "object";
44
+ readonly properties: {
45
+ readonly runId: {
46
+ readonly type: "string";
47
+ };
48
+ readonly version: {
49
+ readonly type: "number";
50
+ };
51
+ readonly action: {
52
+ readonly type: "string";
53
+ readonly enum: readonly ["approve_provider", "approve_source_plan", "approve_import"];
54
+ };
55
+ readonly provider: {
56
+ readonly type: "string";
57
+ readonly enum: readonly ["sales-nav", "prospeo", "signal-discovery"];
58
+ };
59
+ readonly receipt: {
60
+ readonly type: "object";
61
+ };
62
+ };
63
+ readonly required: readonly ["runId", "version", "action", "receipt"];
64
+ readonly additionalProperties: false;
65
+ };
66
+ }, {
67
+ readonly name: "wait_for_find_leads_run";
68
+ readonly description: "Poll durable Find Leads state until it changes or reaches a terminal state. Progress notifications are optional hints only.";
69
+ readonly inputSchema: {
70
+ readonly type: "object";
71
+ readonly properties: {
72
+ readonly runId: {
73
+ readonly type: "string";
74
+ };
75
+ readonly timeoutMs: {
76
+ readonly type: "number";
77
+ };
78
+ readonly intervalMs: {
79
+ readonly type: "number";
80
+ };
81
+ };
82
+ readonly required: readonly ["runId"];
83
+ readonly additionalProperties: false;
84
+ };
85
+ }, {
86
+ readonly name: "cancel_find_leads";
87
+ readonly description: "Cancel a mutable Find Leads run with its current version.";
88
+ readonly inputSchema: {
89
+ readonly type: "object";
90
+ readonly properties: {
91
+ readonly runId: {
92
+ readonly type: "string";
93
+ };
94
+ readonly version: {
95
+ readonly type: "number";
96
+ };
97
+ };
98
+ readonly required: readonly ["runId", "version"];
99
+ readonly additionalProperties: false;
100
+ };
101
+ }, {
102
+ readonly name: "reissue_find_leads_watch_link";
103
+ readonly description: "Rotate and return a fresh one-time live Find Leads watch handoff. Old links are revoked and cannot be reconstructed.";
104
+ readonly inputSchema: {
105
+ readonly type: "object";
106
+ readonly properties: {
107
+ readonly runId: {
108
+ readonly type: "string";
109
+ };
110
+ readonly mode: {
111
+ readonly type: "string";
112
+ readonly enum: readonly ["claude", "codex", "hermes"];
113
+ };
114
+ };
115
+ readonly required: readonly ["runId"];
116
+ readonly additionalProperties: false;
117
+ };
118
+ }];
119
+ export declare function bootstrapFindLeads(input: {
120
+ bootstrapKey: string;
121
+ name?: string;
122
+ }): Promise<FindLeadsSnapshot>;
123
+ export declare function getFindLeadsRun(input: {
124
+ runId: string;
125
+ }): Promise<FindLeadsSnapshot>;
126
+ export declare function updateFindLeadsRun(input: {
127
+ runId: string;
128
+ version: number;
129
+ action: "approve_provider" | "approve_source_plan" | "approve_import";
130
+ provider?: "sales-nav" | "prospeo" | "signal-discovery";
131
+ receipt: Record<string, unknown>;
132
+ }): Promise<FindLeadsSnapshot>;
133
+ export declare function cancelFindLeads(input: {
134
+ runId: string;
135
+ version: number;
136
+ }): Promise<FindLeadsSnapshot>;
137
+ export declare function reissueFindLeadsWatchLink(input: {
138
+ runId: string;
139
+ mode?: "claude" | "codex" | "hermes";
140
+ }): Promise<{
141
+ runId: string;
142
+ leadListId: string;
143
+ watchUrl: string;
144
+ expiresAt: string;
145
+ }>;
146
+ export declare function waitForFindLeadsRun(input: {
147
+ runId: string;
148
+ timeoutMs?: number;
149
+ intervalMs?: number;
150
+ }): Promise<FindLeadsSnapshot>;
151
+ export {};
@@ -0,0 +1,98 @@
1
+ import { getApi } from "../api.js";
2
+ import { getConfig } from "../auth.js";
3
+ function requestOptions() {
4
+ const config = getConfig();
5
+ const workspaceId = config.activeWorkspaceId || config.workspaceId || null;
6
+ if (!workspaceId) {
7
+ throw new Error("No active Sellable workspace. Run list_workspaces then set_active_workspace and retry Find Leads.");
8
+ }
9
+ return { workspaceId };
10
+ }
11
+ export const findLeadsRunToolDefinitions = [
12
+ {
13
+ name: "bootstrap_find_leads",
14
+ description: "Create or resume a durable campaignless Find Leads run and its lead list. No campaign or workingCampaignId is required.",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: { bootstrapKey: { type: "string" }, name: { type: "string" } },
18
+ required: ["bootstrapKey"],
19
+ additionalProperties: false,
20
+ },
21
+ },
22
+ {
23
+ name: "get_find_leads_run",
24
+ description: "Read the authoritative durable Find Leads snapshot.",
25
+ inputSchema: { type: "object", properties: { runId: { type: "string" } }, required: ["runId"], additionalProperties: false },
26
+ },
27
+ {
28
+ name: "update_find_leads_run",
29
+ description: "Persist a provider/source/import approval receipt using optimistic concurrency.",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ runId: { type: "string" }, version: { type: "number" },
34
+ action: { type: "string", enum: ["approve_provider", "approve_source_plan", "approve_import"] },
35
+ provider: { type: "string", enum: ["sales-nav", "prospeo", "signal-discovery"] },
36
+ receipt: { type: "object" },
37
+ },
38
+ required: ["runId", "version", "action", "receipt"],
39
+ additionalProperties: false,
40
+ },
41
+ },
42
+ {
43
+ name: "wait_for_find_leads_run",
44
+ description: "Poll durable Find Leads state until it changes or reaches a terminal state. Progress notifications are optional hints only.",
45
+ inputSchema: {
46
+ type: "object",
47
+ properties: { runId: { type: "string" }, timeoutMs: { type: "number" }, intervalMs: { type: "number" } },
48
+ required: ["runId"], additionalProperties: false,
49
+ },
50
+ },
51
+ {
52
+ name: "cancel_find_leads",
53
+ description: "Cancel a mutable Find Leads run with its current version.",
54
+ inputSchema: { type: "object", properties: { runId: { type: "string" }, version: { type: "number" } }, required: ["runId", "version"], additionalProperties: false },
55
+ },
56
+ {
57
+ name: "reissue_find_leads_watch_link",
58
+ description: "Rotate and return a fresh one-time live Find Leads watch handoff. Old links are revoked and cannot be reconstructed.",
59
+ inputSchema: { type: "object", properties: { runId: { type: "string" }, mode: { type: "string", enum: ["claude", "codex", "hermes"] } }, required: ["runId"], additionalProperties: false },
60
+ },
61
+ ];
62
+ export async function bootstrapFindLeads(input) {
63
+ return getApi().post("/api/v3/find-leads/runs", input, requestOptions());
64
+ }
65
+ export async function getFindLeadsRun(input) {
66
+ return getApi().get(`/api/v3/find-leads/runs/${encodeURIComponent(input.runId)}`, requestOptions());
67
+ }
68
+ export async function updateFindLeadsRun(input) {
69
+ const kind = input.action === "approve_provider"
70
+ ? "provider_prompt"
71
+ : input.action === "approve_source_plan"
72
+ ? "source_plan"
73
+ : "import_approval";
74
+ const provider = input.provider?.replace(/-/g, "_");
75
+ return getApi().patch(`/api/v3/find-leads/runs/${encodeURIComponent(input.runId)}`, { action: "record_receipt", version: input.version, kind, receipt: input.receipt, ...(provider ? { provider } : {}) }, requestOptions());
76
+ }
77
+ export async function cancelFindLeads(input) {
78
+ return getApi().patch(`/api/v3/find-leads/runs/${encodeURIComponent(input.runId)}`, { action: "cancel", version: input.version }, requestOptions());
79
+ }
80
+ export async function reissueFindLeadsWatchLink(input) {
81
+ return getApi().post(`/api/v3/find-leads/runs/${encodeURIComponent(input.runId)}/watch-token`, { mode: input.mode ?? (process.env.CODEX_HOME ? "codex" : "claude") }, requestOptions());
82
+ }
83
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
84
+ export async function waitForFindLeadsRun(input) {
85
+ const timeoutMs = Math.min(Math.max(input.timeoutMs ?? 55_000, 25), 55_000);
86
+ const intervalMs = Math.min(Math.max(input.intervalMs ?? 2_000, 1), 10_000);
87
+ const startedAt = Date.now();
88
+ let previousVersion;
89
+ let snapshot = await getFindLeadsRun(input);
90
+ while (!snapshot.terminal && !snapshot.cancelled && Date.now() - startedAt < timeoutMs) {
91
+ if (previousVersion !== undefined && snapshot.version !== previousVersion)
92
+ return snapshot;
93
+ previousVersion = snapshot.version;
94
+ await sleep(intervalMs);
95
+ snapshot = await getFindLeadsRun(input);
96
+ }
97
+ return snapshot;
98
+ }