@sellable/mcp 0.1.364 → 0.1.366

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.
@@ -1,7 +1,11 @@
1
1
  export type WorkspaceExportDataset = "people" | "events";
2
+ export type WorkspaceExportIntent = "reached_out_people" | "people_and_events" | "outreach_event_log" | "dashboard_messages" | "connections_sent";
3
+ type WorkspaceExportActionType = "INVITE" | "DM" | "INMAIL_OPEN" | "INMAIL_CLOSED" | "VIEW_PROFILE" | "COMMENT";
2
4
  export interface ExportWorkspaceCsvInput {
3
5
  exportType?: "outreach";
6
+ exportIntent?: WorkspaceExportIntent;
4
7
  datasets?: WorkspaceExportDataset[];
8
+ actionTypes?: WorkspaceExportActionType[];
5
9
  outputDir?: string;
6
10
  fromSentAt?: string;
7
11
  toSentAt?: string;
@@ -19,6 +23,11 @@ export declare const workspaceExportToolDefinitions: {
19
23
  enum: string[];
20
24
  description: string;
21
25
  };
26
+ exportIntent: {
27
+ type: string;
28
+ enum: string[];
29
+ description: string;
30
+ };
22
31
  datasets: {
23
32
  type: string;
24
33
  items: {
@@ -27,6 +36,14 @@ export declare const workspaceExportToolDefinitions: {
27
36
  };
28
37
  description: string;
29
38
  };
39
+ actionTypes: {
40
+ type: string;
41
+ items: {
42
+ type: string;
43
+ enum: string[];
44
+ };
45
+ description: string;
46
+ };
30
47
  outputDir: {
31
48
  type: string;
32
49
  description: string;
@@ -70,6 +87,16 @@ export declare function exportWorkspaceCsv(input?: ExportWorkspaceCsvInput): Pro
70
87
  status: "complete" | "failed";
71
88
  }[];
72
89
  exportCutoff: string;
90
+ exportIntent: WorkspaceExportIntent | null;
91
+ intentDescription: string | null;
92
+ countDefinitions: {
93
+ reachedOutPeople: string;
94
+ outreachEvents: string;
95
+ dashboardMessagesSent: string;
96
+ connectionsSent: string;
97
+ profileViews: string;
98
+ };
99
+ counts: Record<string, unknown>;
73
100
  workspace: {
74
101
  id?: string;
75
102
  name?: string | null;
@@ -80,3 +107,4 @@ export declare function exportWorkspaceCsv(input?: ExportWorkspaceCsvInput): Pro
80
107
  error: string;
81
108
  }[];
82
109
  }>;
110
+ export {};
@@ -11,7 +11,13 @@ export const workspaceExportToolDefinitions = [
11
11
  {
12
12
  name: "export_workspace_csv",
13
13
  description: "Export active-workspace outreach CSVs to local files on the MCP host. " +
14
- "Phase 70 supports workflow-table-backed reached-out people and event-level outreach only. " +
14
+ "Use exportIntent to disambiguate counts before exporting: " +
15
+ "`reached_out_people` means one deduped row per unique prospect who received an actual contact attempt (invite, DM, open InMail, or paid/closed InMail) and is the right choice for 'everyone we reached out to'; " +
16
+ "`dashboard_messages` means message events only (DMs + InMails), matching the dashboard Messages Sent card; " +
17
+ "`connections_sent` means invite events only; " +
18
+ "`outreach_event_log` means one row per action event for audit/reconciliation; " +
19
+ "`people_and_events` exports the reached-out people CSV plus matching contact event rows. " +
20
+ "If the user asks for 'sent', 'reached out', or cites conflicting dashboard/export numbers, ask which intent they want before calling the tool. " +
15
21
  "Requires a valid Sellable API key plus active workspace; run list_workspaces and set_active_workspace first if needed. " +
16
22
  "The manifest includes file paths, counts, filters, package/runtime metadata, and skipped/deferred scope notes. " +
17
23
  "CSV files may contain prospect and outreach metadata, so store them appropriately.",
@@ -23,10 +29,36 @@ export const workspaceExportToolDefinitions = [
23
29
  enum: ["outreach"],
24
30
  description: "Export family. Phase 70 supports outreach only.",
25
31
  },
32
+ exportIntent: {
33
+ type: "string",
34
+ enum: [
35
+ "reached_out_people",
36
+ "people_and_events",
37
+ "outreach_event_log",
38
+ "dashboard_messages",
39
+ "connections_sent",
40
+ ],
41
+ description: "Recommended semantic export. Use reached_out_people for 'everyone we reached out to'; dashboard_messages for the dashboard Messages Sent card; connections_sent for invites; outreach_event_log for audit rows; people_and_events for both reached people and matching contact events.",
42
+ },
26
43
  datasets: {
27
44
  type: "array",
28
45
  items: { type: "string", enum: ["people", "events"] },
29
- description: "Datasets to export. Defaults to both people and events.",
46
+ description: "Low-level datasets to export. people is one deduped prospect row; events is one action row. Omit this and use exportIntent for clearer behavior.",
47
+ },
48
+ actionTypes: {
49
+ type: "array",
50
+ items: {
51
+ type: "string",
52
+ enum: [
53
+ "INVITE",
54
+ "DM",
55
+ "INMAIL_OPEN",
56
+ "INMAIL_CLOSED",
57
+ "VIEW_PROFILE",
58
+ "COMMENT",
59
+ ],
60
+ },
61
+ description: "Optional low-level action filter. Usually omit and use exportIntent. Dashboard messages are DM + INMAIL_OPEN + INMAIL_CLOSED; reached-out/contact events are INVITE + DM + INMAIL_OPEN + INMAIL_CLOSED.",
30
62
  },
31
63
  outputDir: {
32
64
  type: "string",
@@ -56,6 +88,52 @@ export const workspaceExportToolDefinitions = [
56
88
  },
57
89
  },
58
90
  ];
91
+ const CONTACT_ACTION_TYPES = [
92
+ "INVITE",
93
+ "DM",
94
+ "INMAIL_OPEN",
95
+ "INMAIL_CLOSED",
96
+ ];
97
+ const DASHBOARD_MESSAGE_ACTION_TYPES = [
98
+ "DM",
99
+ "INMAIL_OPEN",
100
+ "INMAIL_CLOSED",
101
+ ];
102
+ const ALL_ACTION_TYPES = [
103
+ "INVITE",
104
+ "DM",
105
+ "INMAIL_OPEN",
106
+ "INMAIL_CLOSED",
107
+ "VIEW_PROFILE",
108
+ "COMMENT",
109
+ ];
110
+ const EXPORT_INTENT_CONFIG = {
111
+ reached_out_people: {
112
+ datasets: ["people"],
113
+ actionTypes: CONTACT_ACTION_TYPES,
114
+ description: "Unique prospects who received at least one actual contact attempt: invite, DM, open InMail, or paid/closed InMail.",
115
+ },
116
+ people_and_events: {
117
+ datasets: ["people", "events"],
118
+ actionTypes: CONTACT_ACTION_TYPES,
119
+ description: "Reached-out people plus one matching contact event row per invite, DM, open InMail, or paid/closed InMail.",
120
+ },
121
+ outreach_event_log: {
122
+ datasets: ["events"],
123
+ actionTypes: ALL_ACTION_TYPES,
124
+ description: "Raw workflow-table-backed action event log for audit/reconciliation, including profile views and comments when present.",
125
+ },
126
+ dashboard_messages: {
127
+ datasets: ["events"],
128
+ actionTypes: DASHBOARD_MESSAGE_ACTION_TYPES,
129
+ description: "Message events only, matching the dashboard Messages Sent card: DMs + open InMails + paid/closed InMails.",
130
+ },
131
+ connections_sent: {
132
+ datasets: ["events"],
133
+ actionTypes: ["INVITE"],
134
+ description: "Connection invite events only, matching the dashboard Connections Sent sub-count.",
135
+ },
136
+ };
59
137
  function assertIsoDate(value, field) {
60
138
  if (value === undefined || value === null || value === "")
61
139
  return undefined;
@@ -97,17 +175,57 @@ function normalizeDatasets(value) {
97
175
  return dataset;
98
176
  });
99
177
  }
178
+ function normalizeExportIntent(value) {
179
+ if (value === undefined || value === null || value === "")
180
+ return undefined;
181
+ if (value !== "reached_out_people" &&
182
+ value !== "people_and_events" &&
183
+ value !== "outreach_event_log" &&
184
+ value !== "dashboard_messages" &&
185
+ value !== "connections_sent") {
186
+ throw new Error("exportIntent is not supported");
187
+ }
188
+ return value;
189
+ }
190
+ function normalizeActionTypes(value) {
191
+ if (value === undefined || value === null)
192
+ return [];
193
+ if (!Array.isArray(value))
194
+ throw new Error("actionTypes must be an array");
195
+ if (value.length === 0)
196
+ throw new Error("actionTypes cannot be empty");
197
+ const unique = Array.from(new Set(value));
198
+ return unique.map((actionType) => {
199
+ if (actionType !== "INVITE" &&
200
+ actionType !== "DM" &&
201
+ actionType !== "INMAIL_OPEN" &&
202
+ actionType !== "INMAIL_CLOSED" &&
203
+ actionType !== "VIEW_PROFILE" &&
204
+ actionType !== "COMMENT") {
205
+ throw new Error("actionTypes contains an unsupported action type");
206
+ }
207
+ return actionType;
208
+ });
209
+ }
100
210
  function validateInput(input) {
101
211
  if (input.exportType && input.exportType !== "outreach") {
102
212
  throw new Error("exportType must be outreach");
103
213
  }
214
+ const exportIntent = normalizeExportIntent(input.exportIntent);
215
+ const intentConfig = exportIntent ? EXPORT_INTENT_CONFIG[exportIntent] : null;
104
216
  const fromSentAt = assertIsoDate(input.fromSentAt, "fromSentAt");
105
217
  const toSentAt = assertIsoDate(input.toSentAt, "toSentAt");
106
218
  if (fromSentAt && toSentAt && new Date(fromSentAt) > new Date(toSentAt)) {
107
219
  throw new Error("fromSentAt must be before toSentAt");
108
220
  }
221
+ const actionTypes = normalizeActionTypes(input.actionTypes);
109
222
  return {
110
- datasets: normalizeDatasets(input.datasets),
223
+ exportIntent,
224
+ intentDescription: intentConfig?.description ?? null,
225
+ datasets: input.datasets === undefined || input.datasets === null
226
+ ? (intentConfig?.datasets ?? ["people", "events"])
227
+ : normalizeDatasets(input.datasets),
228
+ actionTypes: actionTypes.length > 0 ? actionTypes : (intentConfig?.actionTypes ?? []),
111
229
  outputDir: input.outputDir,
112
230
  fromSentAt,
113
231
  toSentAt,
@@ -115,6 +233,15 @@ function validateInput(input) {
115
233
  tableIds: assertIdList(input.tableIds, "tableIds"),
116
234
  };
117
235
  }
236
+ function countDefinitions() {
237
+ return {
238
+ reachedOutPeople: "Deduped prospects with at least one actual contact attempt: INVITE, DM, INMAIL_OPEN, or INMAIL_CLOSED. This is what 'everyone we reached out to' should mean.",
239
+ outreachEvents: "One row per exported LinkedInOutreach action event after workspace, date, campaign/table, and action-type filters.",
240
+ dashboardMessagesSent: "Dashboard Messages Sent equals DM + INMAIL_OPEN + INMAIL_CLOSED events. It excludes connection invites.",
241
+ connectionsSent: "Dashboard Connections Sent equals INVITE events. Accepted invites show separately as Connections Made.",
242
+ profileViews: "VIEW_PROFILE is an action event for audit logs, but it is not a contact attempt and should not define reached-out people.",
243
+ };
244
+ }
118
245
  async function packageVersion() {
119
246
  const entryDir = process.argv[1]
120
247
  ? path.dirname(path.resolve(process.argv[1]))
@@ -226,6 +353,7 @@ export async function exportWorkspaceCsv(input = {}) {
226
353
  toSentAt: validated.toSentAt,
227
354
  campaignIds: validated.campaignIds,
228
355
  tableIds: validated.tableIds,
356
+ actionTypes: validated.actionTypes,
229
357
  });
230
358
  const metadataEndpoint = `/api/v3/mcp/workspace-export/outreach/metadata${metadataQuery}`;
231
359
  const backendMetadata = await api.get(metadataEndpoint);
@@ -243,6 +371,7 @@ export async function exportWorkspaceCsv(input = {}) {
243
371
  toSentAt: validated.toSentAt,
244
372
  campaignIds: validated.campaignIds,
245
373
  tableIds: validated.tableIds,
374
+ actionTypes: validated.actionTypes,
246
375
  });
247
376
  const endpoint = `/api/v3/mcp/workspace-export/outreach${query}`;
248
377
  const filePath = path.join(runDir, `outreach-${dataset}.csv`);
@@ -278,6 +407,9 @@ export async function exportWorkspaceCsv(input = {}) {
278
407
  status: failures.length > 0 ? "partial" : "success",
279
408
  tool: "export_workspace_csv",
280
409
  exportType: "outreach",
410
+ exportIntent: validated.exportIntent ?? "legacy_datasets",
411
+ intentDescription: validated.intentDescription,
412
+ countDefinitions: countDefinitions(),
281
413
  startedAt,
282
414
  completedAt: new Date().toISOString(),
283
415
  package: {
@@ -298,6 +430,7 @@ export async function exportWorkspaceCsv(input = {}) {
298
430
  toSentAt: validated.toSentAt ?? null,
299
431
  campaignIds: validated.campaignIds,
300
432
  tableIds: validated.tableIds,
433
+ actionTypes: validated.actionTypes,
301
434
  exportCutoff,
302
435
  },
303
436
  outputDir: runDir,
@@ -318,6 +451,10 @@ export async function exportWorkspaceCsv(input = {}) {
318
451
  status: file.status,
319
452
  })),
320
453
  exportCutoff,
454
+ exportIntent: validated.exportIntent ?? null,
455
+ intentDescription: validated.intentDescription,
456
+ countDefinitions: countDefinitions(),
457
+ counts: backendMetadata.counts ?? {},
321
458
  workspace: backendMetadata.workspace ?? {
322
459
  id: workspaceId,
323
460
  name: config.activeWorkspaceName || config.workspaceName || null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.364",
3
+ "version": "0.1.366",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",