@sellable/mcp 0.1.949 → 0.1.951-wip.sendercontext.20261001.1

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.
@@ -3,14 +3,14 @@
3
3
  * manifests. Keep this free of tool implementation imports so Next can bundle
4
4
  * it; registry parity is enforced in sellable-agent-tool-policy.test.ts.
5
5
  */
6
- export declare const SELLABLE_MCP_TOOL_NAMES: readonly ["add_column", "add_on_demand_leads", "add_rubric_item", "add_teammate", "add_to_comment_campaign", "add_to_connection_campaign", "add_to_inmail_campaign", "add_to_post_campaign", "archive_campaign", "attach_recommended_sequence", "attach_sequence", "bootstrap_create_campaign", "bootstrap_engage", "bootstrap_engage_multi", "bootstrap_find_leads", "bulk_enrich_with_prospeo", "calculate_linkedin_hook_preview", "cancel_campaign_message_preparation", "cancel_find_leads", "cancel_lead_import", "capture_post_idea", "check_inbox_reply_eligibility", "check_rubric", "commit_blueprint", "complete_sender_research", "confirm_harvest_job_companies", "confirm_lead_list", "confirm_prospeo_company_accounts", "copy_sender_config", "create_campaign", "create_on_demand_campaign", "create_on_demand_table", "create_table", "create_workflow_table", "create_workspace", "customer_program", "decide_agent_endpoint_proposal", "delete_column", "delete_rubric_item", "draft_rubrics", "duplicate_campaign", "enrich_with_prospeo", "export_table_csv", "export_workspace_csv", "fetch_company", "fetch_company_posts", "fetch_linkedin_posts", "fetch_linkedin_profile", "fetch_post_engagers", "fill_campaign_horizon", "get_active_workspace", "get_agent_endpoint_run", "get_agent_generation_routing", "get_agent_soul", "get_auth_status", "get_campaign", "get_campaign_context", "get_campaign_framework", "get_campaign_message_preparation_status", "get_campaign_messages_preview", "get_campaign_navigation_state", "get_campaign_refill_state", "get_campaign_table_schema", "get_campaign_waterfall", "get_campaigns", "get_column_schema", "get_company_info", "get_engage_memory", "get_engage_state", "get_engaged_posts", "get_find_leads_run", "get_inbox_thread", "get_integration_connection_status", "get_linkedin_profile", "get_or_create_direct_campaign_table", "get_post_draft", "get_post_find_leads_scout_registry", "get_post_idea", "get_prospeo_credits", "get_provider_prompt", "get_published_post", "get_refill_target_plan", "get_rows", "get_rows_minimal", "get_runtime_identity", "get_scheduler_fill_capacity", "get_sender", "get_sender_routing", "get_source_scout_registry", "get_subskill_asset", "get_subskill_prompt", "get_table_rows", "get_user_posts", "get_workspace", "import_leads", "init_on_demand_sequence", "integrations_call_tool", "integrations_describe_tool", "integrations_list_tools", "list_agent_endpoint_runs", "list_agent_endpoints", "list_column_types", "list_dnc_entries", "list_post_draft_iterations", "list_post_drafts", "list_post_ideas", "list_published_posts", "list_senders", "list_subskill_prompts", "list_tables", "list_workspaces", "load_csv_dnc_entries", "load_csv_domains", "load_csv_linkedin_leads", "lookup_sales_nav_filter", "mark_post_published", "migrate_flat_configs", "opportunity_sequence_enroll", "opportunity_sequence_next_page", "opportunity_sequence_record", "pause_campaign", "pause_direct_campaign", "pause_on_demand_campaign", "preflight_find_leads_provider", "prepare_campaign_ab_test", "publish_agent_endpoint", "queue_campaign_cells", "queue_cells", "record_campaign_review_batch", "record_engage_proven_search", "refill_sends", "refill_v3_advance", "refill_v3_continue", "refill_v3_edit_selected_campaign", "refill_v3_fill_ready", "refill_v3_world_state", "refresh_paid_inmail_credits", "refresh_sender_engagement", "reissue_find_leads_watch_link", "render_linkedin_post_preview", "reorder_columns", "resolve_campaign_fill_route", "revise_message_template_and_rerun", "rotate_agent_endpoint_key", "run_scheduler_sweep", "save_domain_filters", "save_hook_research", "save_post_draft", "save_rubrics", "search_apollo", "search_available_integrations", "search_engagement_posts", "search_harvest_jobs", "search_inbox_threads", "search_prospeo", "search_prospeo_companies", "search_sales_nav", "search_signals", "search_subskill_prompts", "select_campaign_cells", "select_necessary_rubrics", "select_promising_posts", "send_inbox_draft", "send_inbox_manual_reply", "set_active_workspace", "set_agent_generation_flags", "set_campaign_waterfall_order", "set_company_info", "set_engage_state", "set_engage_style_guide", "set_headline_icp_criteria", "set_sender_calendar_link", "set_sender_routing", "setup_evergreen_campaigns", "start_campaign", "start_campaign_message_preparation", "start_cli_login", "start_direct_campaign", "start_integration_connection", "start_on_demand_campaign", "stop_agent_endpoint_run", "update_agent_soul", "update_campaign", "update_campaign_brief", "update_cell", "update_column", "update_find_leads_run", "update_inbox_draft", "update_post_draft", "update_published_post_metrics", "update_rubric_item", "upsert_engage_tracked_person", "upsert_rubric", "validate_campaign1_kickoff_handoff", "verify_table_row", "wait_for_campaign_processing", "wait_for_campaign_table_ready", "wait_for_cli_login", "wait_for_find_leads_run", "wait_for_lead_list_ready", "wait_for_rubric_results"];
6
+ export declare const SELLABLE_MCP_TOOL_NAMES: readonly ["add_column", "add_on_demand_leads", "add_rubric_item", "add_teammate", "add_to_comment_campaign", "add_to_connection_campaign", "add_to_inmail_campaign", "add_to_post_campaign", "archive_campaign", "attach_recommended_sequence", "attach_sequence", "bootstrap_create_campaign", "bootstrap_engage", "bootstrap_engage_multi", "bootstrap_find_leads", "bulk_enrich_with_prospeo", "calculate_linkedin_hook_preview", "cancel_campaign_message_preparation", "cancel_find_leads", "cancel_lead_import", "capture_post_idea", "check_inbox_reply_eligibility", "check_rubric", "commit_blueprint", "complete_sender_research", "confirm_harvest_job_companies", "confirm_lead_list", "confirm_prospeo_company_accounts", "copy_sender_config", "create_campaign", "create_on_demand_campaign", "create_on_demand_table", "create_table", "create_workflow_table", "create_workspace", "customer_program", "decide_agent_endpoint_proposal", "delete_column", "delete_rubric_item", "draft_rubrics", "duplicate_campaign", "enrich_with_prospeo", "export_table_csv", "export_workspace_csv", "fetch_company", "fetch_company_posts", "fetch_linkedin_posts", "fetch_linkedin_profile", "fetch_post_engagers", "fill_campaign_horizon", "get_active_workspace", "get_agent_endpoint_run", "get_agent_generation_routing", "get_agent_soul", "get_auth_status", "get_campaign", "get_campaign_context", "get_campaign_framework", "get_campaign_message_preparation_status", "get_campaign_messages_preview", "get_campaign_navigation_state", "get_campaign_refill_state", "get_campaign_table_schema", "get_campaign_waterfall", "get_campaigns", "get_column_schema", "get_company_info", "get_engage_memory", "get_engage_state", "get_engaged_posts", "get_find_leads_run", "get_inbox_thread", "get_integration_connection_status", "get_linkedin_profile", "get_or_create_direct_campaign_table", "get_post_draft", "get_post_find_leads_scout_registry", "get_post_idea", "get_prospeo_credits", "get_provider_prompt", "get_published_post", "get_refill_target_plan", "get_rows", "get_rows_minimal", "get_runtime_identity", "get_scheduler_fill_capacity", "get_sender", "get_sender_routing", "get_source_scout_registry", "get_subskill_asset", "get_subskill_prompt", "get_table_rows", "get_user_posts", "get_workspace", "import_leads", "init_on_demand_sequence", "integrations_call_tool", "integrations_describe_tool", "integrations_list_tools", "list_agent_endpoint_runs", "list_agent_endpoints", "list_column_types", "list_dnc_entries", "list_post_draft_iterations", "list_post_drafts", "list_post_ideas", "list_published_posts", "list_senders", "list_subskill_prompts", "list_tables", "list_workspaces", "load_csv_dnc_entries", "load_csv_domains", "load_csv_linkedin_leads", "lookup_sales_nav_filter", "mark_post_published", "migrate_flat_configs", "opportunity_sequence_enroll", "opportunity_sequence_next_page", "opportunity_sequence_record", "pause_campaign", "pause_direct_campaign", "pause_on_demand_campaign", "preflight_find_leads_provider", "prepare_campaign_ab_test", "publish_agent_endpoint", "queue_campaign_cells", "queue_cells", "record_campaign_review_batch", "record_engage_proven_search", "refill_sends", "refill_v3_advance", "refill_v3_continue", "refill_v3_edit_selected_campaign", "refill_v3_fill_ready", "refill_v3_world_state", "refresh_paid_inmail_credits", "refresh_sender_engagement", "reissue_find_leads_watch_link", "render_linkedin_post_preview", "reorder_columns", "resolve_campaign_fill_route", "revise_message_template_and_rerun", "rotate_agent_endpoint_key", "run_scheduler_sweep", "save_domain_filters", "save_hook_research", "save_post_draft", "save_rubrics", "search_apollo", "search_available_integrations", "search_engagement_posts", "search_harvest_jobs", "search_inbox_threads", "search_prospeo", "search_prospeo_companies", "search_sales_nav", "search_signals", "search_subskill_prompts", "select_campaign_cells", "select_necessary_rubrics", "select_promising_posts", "send_inbox_draft", "send_inbox_manual_reply", "set_active_workspace", "set_agent_generation_flags", "set_campaign_waterfall_order", "set_company_info", "set_engage_state", "set_engage_style_guide", "set_headline_icp_criteria", "set_sender_calendar_link", "set_sender_context", "set_sender_routing", "setup_evergreen_campaigns", "start_campaign", "start_campaign_message_preparation", "start_cli_login", "start_direct_campaign", "start_integration_connection", "start_on_demand_campaign", "stop_agent_endpoint_run", "update_agent_soul", "update_campaign", "update_campaign_brief", "update_cell", "update_column", "update_find_leads_run", "update_inbox_draft", "update_post_draft", "update_published_post_metrics", "update_rubric_item", "upsert_engage_tracked_person", "upsert_rubric", "validate_campaign1_kickoff_handoff", "verify_table_row", "wait_for_campaign_processing", "wait_for_campaign_table_ready", "wait_for_cli_login", "wait_for_find_leads_run", "wait_for_lead_list_ready", "wait_for_rubric_results"];
7
7
  /**
8
8
  * Explicit mutation classification lives beside (but outside) the flat tool
9
9
  * registry. Neither this list nor `isMutatingTool` is an input to
10
10
  * `compileAgentToolPolicy`, so editing it cannot move the fleet-pinned
11
11
  * `policyHash`.
12
12
  */
13
- export declare const MUTATING_TOOL_NAMES: readonly ["create_table", "publish_agent_endpoint", "rotate_agent_endpoint_key", "decide_agent_endpoint_proposal", "stop_agent_endpoint_run", "pause_campaign", "archive_campaign", "update_campaign", "fill_campaign_horizon", "set_campaign_waterfall_order", "set_sender_routing", "set_agent_generation_flags", "send_inbox_draft", "send_inbox_manual_reply", "load_csv_dnc_entries", "update_cell", "queue_cells", "check_rubric", "queue_campaign_cells", "start_campaign_message_preparation", "opportunity_sequence_enroll", "opportunity_sequence_record", "refill_v3_fill_ready", "refill_v3_edit_selected_campaign", "refill_v3_advance", "refill_v3_continue"];
13
+ export declare const MUTATING_TOOL_NAMES: readonly ["create_table", "publish_agent_endpoint", "rotate_agent_endpoint_key", "decide_agent_endpoint_proposal", "stop_agent_endpoint_run", "pause_campaign", "archive_campaign", "update_campaign", "fill_campaign_horizon", "set_campaign_waterfall_order", "set_sender_routing", "set_sender_context", "set_agent_generation_flags", "send_inbox_draft", "send_inbox_manual_reply", "load_csv_dnc_entries", "update_cell", "queue_cells", "check_rubric", "queue_campaign_cells", "start_campaign_message_preparation", "opportunity_sequence_enroll", "opportunity_sequence_record", "refill_v3_fill_ready", "refill_v3_edit_selected_campaign", "refill_v3_advance", "refill_v3_continue"];
14
14
  export declare function isMutatingTool(name: string): boolean;
15
15
  export declare const CROSS_WORKSPACE_EXPLICIT_WORKSPACE_REQUIRED: {
16
16
  readonly code: "cross_workspace_explicit_workspace_required";
@@ -176,6 +176,7 @@ export const SELLABLE_MCP_TOOL_NAMES = [
176
176
  "set_engage_style_guide",
177
177
  "set_headline_icp_criteria",
178
178
  "set_sender_calendar_link",
179
+ "set_sender_context",
179
180
  "set_sender_routing",
180
181
  "setup_evergreen_campaigns",
181
182
  "start_campaign",
@@ -227,6 +228,9 @@ export const MUTATING_TOOL_NAMES = [
227
228
  "fill_campaign_horizon",
228
229
  "set_campaign_waterfall_order",
229
230
  "set_sender_routing",
231
+ // A sender's personal context is what the message writer says about a real
232
+ // person, so a cross-workspace caller must name the workspace it means.
233
+ "set_sender_context",
230
234
  "set_agent_generation_flags",
231
235
  "send_inbox_draft",
232
236
  "send_inbox_manual_reply",
package/dist/server.js CHANGED
@@ -34,9 +34,9 @@ import { handleReorderColumns } from "./tools/column-reorder.js";
34
34
  import { handleGetColumnSchema, handleListColumnTypes, } from "./tools/column-schema.js";
35
35
  import { handleUpdateColumn } from "./tools/column-update.js";
36
36
  import { getCompanyInfoTool, setCompanyInfoTool, } from "./tools/company-info.js";
37
- import { customerProgramTool } from "./tools/customer-program.js";
38
37
  import { calculateLinkedInHookPreviewTool, capturePostIdeaTool, getPostDraftTool, getPostIdeaTool, getPublishedPostTool, listPostDraftIterationsTool, listPostDraftsTool, listPostIdeasTool, listPublishedPostsTool, markPostPublishedTool, renderLinkedInPostPreviewTool, saveHookResearchTool, savePostDraftTool, updatePostDraftTool, updatePublishedPostMetricsTool, } from "./tools/content-posts.js";
39
38
  import { getCampaignContext, hydrateCampaignContextFromCampaign, markCampaignContextDirty, } from "./tools/context.js";
39
+ import { customerProgramTool } from "./tools/customer-program.js";
40
40
  import { addToCommentCampaign, addToConnectionCampaign, addToInmailCampaign, addToPostCampaign, getEngagedPosts, getOrCreateDirectCampaignTable, pauseDirectCampaign, startDirectCampaign, } from "./tools/direct-campaigns.js";
41
41
  import { bootstrapEngage, bootstrapEngageMulti, } from "./tools/engage-bootstrap.js";
42
42
  import { searchEngagementPosts } from "./tools/engage-discovery.js";
@@ -75,7 +75,7 @@ import { getRuntimeIdentityTool } from "./tools/runtime-identity.js";
75
75
  import { getSchedulerFillCapacity } from "./tools/scheduler-fill-capacity.js";
76
76
  import { runSchedulerSweep } from "./tools/scheduler-run.js";
77
77
  import { getSenderRoutingTool, setSenderRoutingTool, } from "./tools/sender-routing.js";
78
- import { getSender, listSenders, refreshPaidInmailCredits, setSenderCalendarLink, } from "./tools/senders.js";
78
+ import { getSender, listSenders, refreshPaidInmailCredits, setSenderCalendarLink, setSenderContext, } from "./tools/senders.js";
79
79
  import { attachRecommendedSequence, attachSequence, createWorkflowTable, } from "./tools/sequencer.js";
80
80
  import { setupEvergreenCampaigns } from "./tools/setup-evergreen-campaigns.js";
81
81
  import { createTable, exportTableCsv, listTables } from "./tools/tables.js";
@@ -201,8 +201,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
201
201
  ok: false,
202
202
  error: boundary.authorization.code,
203
203
  decision: "DENY",
204
- ...(boundary.authorization.code === "managed_agent_auth_unavailable"
205
- ? { message: "Managed authentication is unavailable. The runtime supervisor must restore it; do not copy personal credentials, start browser login, or retry a Calendar mutation automatically." }
204
+ ...(boundary.authorization.code ===
205
+ "managed_agent_auth_unavailable"
206
+ ? {
207
+ message: "Managed authentication is unavailable. The runtime supervisor must restore it; do not copy personal credentials, start browser login, or retry a Calendar mutation automatically.",
208
+ }
206
209
  : {}),
207
210
  }),
208
211
  },
@@ -862,6 +865,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
862
865
  case "set_sender_calendar_link":
863
866
  result = await setSenderCalendarLink(args);
864
867
  break;
868
+ case "set_sender_context":
869
+ result = await setSenderContext(args);
870
+ break;
865
871
  case "refresh_paid_inmail_credits":
866
872
  result = await refreshPaidInmailCredits(args);
867
873
  break;
@@ -7486,6 +7486,8 @@ export declare const allTools: ({
7486
7486
  expectedCalendarLink?: undefined;
7487
7487
  expectedCalendarLinkRevision?: undefined;
7488
7488
  force?: undefined;
7489
+ senderContext?: undefined;
7490
+ expectedUpdatedAt?: undefined;
7489
7491
  };
7490
7492
  required: string[];
7491
7493
  additionalProperties: boolean;
@@ -7527,6 +7529,8 @@ export declare const allTools: ({
7527
7529
  threshold?: undefined;
7528
7530
  maxStalenessSeconds?: undefined;
7529
7531
  paidInmailCreditsMaxStalenessSeconds?: undefined;
7532
+ senderContext?: undefined;
7533
+ expectedUpdatedAt?: undefined;
7530
7534
  };
7531
7535
  required: string[];
7532
7536
  additionalProperties: boolean;
@@ -7610,6 +7614,89 @@ export declare const allTools: ({
7610
7614
  idempotentHint: boolean;
7611
7615
  openWorldHint: boolean;
7612
7616
  };
7617
+ } | {
7618
+ name: string;
7619
+ description: string;
7620
+ inputSchema: {
7621
+ type: string;
7622
+ properties: {
7623
+ senderId: {
7624
+ type: string;
7625
+ minLength: number;
7626
+ description?: undefined;
7627
+ };
7628
+ workspaceId: {
7629
+ type: string;
7630
+ minLength: number;
7631
+ description?: undefined;
7632
+ };
7633
+ senderContext: {
7634
+ type: string[];
7635
+ properties: {
7636
+ workStories: {
7637
+ readonly type: "array";
7638
+ readonly maxItems: 10;
7639
+ readonly items: {
7640
+ readonly type: "string";
7641
+ readonly minLength: 1;
7642
+ readonly maxLength: 3000;
7643
+ };
7644
+ };
7645
+ personalNotes: {
7646
+ readonly type: "array";
7647
+ readonly maxItems: 10;
7648
+ readonly items: {
7649
+ readonly type: "string";
7650
+ readonly minLength: 1;
7651
+ readonly maxLength: 3000;
7652
+ };
7653
+ };
7654
+ voiceExamples: {
7655
+ readonly type: "array";
7656
+ readonly maxItems: 10;
7657
+ readonly items: {
7658
+ readonly type: "string";
7659
+ readonly minLength: 1;
7660
+ readonly maxLength: 3000;
7661
+ };
7662
+ };
7663
+ sourcesAndLimits: {
7664
+ readonly type: "array";
7665
+ readonly maxItems: 10;
7666
+ readonly items: {
7667
+ readonly type: "string";
7668
+ readonly minLength: 1;
7669
+ readonly maxLength: 3000;
7670
+ };
7671
+ };
7672
+ };
7673
+ additionalProperties: boolean;
7674
+ };
7675
+ expectedUpdatedAt: {
7676
+ readonly type: readonly ["string", "null"];
7677
+ };
7678
+ actionType?: undefined;
7679
+ campaignId?: undefined;
7680
+ tableId?: undefined;
7681
+ columnId?: undefined;
7682
+ threshold?: undefined;
7683
+ maxStalenessSeconds?: undefined;
7684
+ paidInmailCreditsMaxStalenessSeconds?: undefined;
7685
+ calendarLink?: undefined;
7686
+ expectedCalendarLink?: undefined;
7687
+ expectedCalendarLinkRevision?: undefined;
7688
+ force?: undefined;
7689
+ };
7690
+ required: string[];
7691
+ additionalProperties: boolean;
7692
+ };
7693
+ annotations: {
7694
+ readOnlyHint: boolean;
7695
+ destructiveHint: boolean;
7696
+ idempotentHint: boolean;
7697
+ openWorldHint: boolean;
7698
+ };
7699
+ outputSchema?: undefined;
7613
7700
  } | {
7614
7701
  name: string;
7615
7702
  description: string;
@@ -180,6 +180,8 @@ export declare const senderToolDefinitions: ({
180
180
  expectedCalendarLink?: undefined;
181
181
  expectedCalendarLinkRevision?: undefined;
182
182
  force?: undefined;
183
+ senderContext?: undefined;
184
+ expectedUpdatedAt?: undefined;
183
185
  };
184
186
  required: never[];
185
187
  additionalProperties: boolean;
@@ -213,6 +215,8 @@ export declare const senderToolDefinitions: ({
213
215
  expectedCalendarLink?: undefined;
214
216
  expectedCalendarLinkRevision?: undefined;
215
217
  force?: undefined;
218
+ senderContext?: undefined;
219
+ expectedUpdatedAt?: undefined;
216
220
  };
217
221
  required: string[];
218
222
  additionalProperties: boolean;
@@ -267,6 +271,8 @@ export declare const senderToolDefinitions: ({
267
271
  expectedCalendarLink?: undefined;
268
272
  expectedCalendarLinkRevision?: undefined;
269
273
  force?: undefined;
274
+ senderContext?: undefined;
275
+ expectedUpdatedAt?: undefined;
270
276
  };
271
277
  required: string[];
272
278
  additionalProperties: boolean;
@@ -308,6 +314,8 @@ export declare const senderToolDefinitions: ({
308
314
  threshold?: undefined;
309
315
  maxStalenessSeconds?: undefined;
310
316
  paidInmailCreditsMaxStalenessSeconds?: undefined;
317
+ senderContext?: undefined;
318
+ expectedUpdatedAt?: undefined;
311
319
  };
312
320
  required: string[];
313
321
  additionalProperties: boolean;
@@ -391,9 +399,108 @@ export declare const senderToolDefinitions: ({
391
399
  idempotentHint: boolean;
392
400
  openWorldHint: boolean;
393
401
  };
402
+ } | {
403
+ name: string;
404
+ description: string;
405
+ inputSchema: {
406
+ type: string;
407
+ properties: {
408
+ senderId: {
409
+ type: string;
410
+ minLength: number;
411
+ description?: undefined;
412
+ };
413
+ workspaceId: {
414
+ type: string;
415
+ minLength: number;
416
+ description?: undefined;
417
+ };
418
+ senderContext: {
419
+ type: string[];
420
+ properties: {
421
+ workStories: {
422
+ readonly type: "array";
423
+ readonly maxItems: 10;
424
+ readonly items: {
425
+ readonly type: "string";
426
+ readonly minLength: 1;
427
+ readonly maxLength: 3000;
428
+ };
429
+ };
430
+ personalNotes: {
431
+ readonly type: "array";
432
+ readonly maxItems: 10;
433
+ readonly items: {
434
+ readonly type: "string";
435
+ readonly minLength: 1;
436
+ readonly maxLength: 3000;
437
+ };
438
+ };
439
+ voiceExamples: {
440
+ readonly type: "array";
441
+ readonly maxItems: 10;
442
+ readonly items: {
443
+ readonly type: "string";
444
+ readonly minLength: 1;
445
+ readonly maxLength: 3000;
446
+ };
447
+ };
448
+ sourcesAndLimits: {
449
+ readonly type: "array";
450
+ readonly maxItems: 10;
451
+ readonly items: {
452
+ readonly type: "string";
453
+ readonly minLength: 1;
454
+ readonly maxLength: 3000;
455
+ };
456
+ };
457
+ };
458
+ additionalProperties: boolean;
459
+ };
460
+ expectedUpdatedAt: {
461
+ readonly type: readonly ["string", "null"];
462
+ };
463
+ actionType?: undefined;
464
+ campaignId?: undefined;
465
+ tableId?: undefined;
466
+ columnId?: undefined;
467
+ threshold?: undefined;
468
+ maxStalenessSeconds?: undefined;
469
+ paidInmailCreditsMaxStalenessSeconds?: undefined;
470
+ calendarLink?: undefined;
471
+ expectedCalendarLink?: undefined;
472
+ expectedCalendarLinkRevision?: undefined;
473
+ force?: undefined;
474
+ };
475
+ required: string[];
476
+ additionalProperties: boolean;
477
+ };
478
+ annotations: {
479
+ readOnlyHint: boolean;
480
+ destructiveHint: boolean;
481
+ idempotentHint: boolean;
482
+ openWorldHint: boolean;
483
+ };
484
+ outputSchema?: undefined;
394
485
  })[];
395
486
  export declare function listSenders(input?: ListSendersInput): Promise<ListSendersResponse>;
396
487
  export declare function setSenderCalendarLink(input: SetSenderCalendarLinkInput): Promise<SenderCalendarLinkToolResult>;
488
+ declare const SENDER_CONTEXT_LISTS: readonly ["workStories", "personalNotes", "voiceExamples", "sourcesAndLimits"];
489
+ export type SetSenderContextInput = {
490
+ senderId: string;
491
+ workspaceId: string;
492
+ senderContext: Partial<Record<(typeof SENDER_CONTEXT_LISTS)[number], string[]>> | null;
493
+ expectedUpdatedAt: string | null;
494
+ };
495
+ /** Save one sender's personal context through the compare-before-write route. */
496
+ export declare function setSenderContext(input: SetSenderContextInput): Promise<{
497
+ isError?: true | undefined;
498
+ structuredContent: Record<string, unknown>;
499
+ content: {
500
+ type: "text";
501
+ text: string;
502
+ }[];
503
+ }>;
397
504
  export declare function getSender(input: GetSenderInput): Promise<SenderDetailResponse>;
398
505
  export declare function refreshPaidInmailCredits(input: RefreshPaidInmailCreditsInput): Promise<RefreshPaidInmailCreditsResponse>;
399
506
  export {};
@@ -2,6 +2,11 @@ import { getApi, SellableApiError } from "../api.js";
2
2
  import { isLinkedInProfileInput, normalizeLinkedInProfileInput, } from "./linkedin-url.js";
3
3
  import { normalizeExplicitWorkspaceId, workspaceRequestOptions, } from "./workspace-context.js";
4
4
  const nullableStringSchema = { type: ["string", "null"] };
5
+ const senderContextEntriesInputSchema = {
6
+ type: "array",
7
+ maxItems: 10,
8
+ items: { type: "string", minLength: 1, maxLength: 3000 },
9
+ };
5
10
  const setSenderCalendarLinkOutputSchema = {
6
11
  type: "object",
7
12
  oneOf: [
@@ -346,6 +351,41 @@ export const senderToolDefinitions = [
346
351
  openWorldHint: false,
347
352
  },
348
353
  },
354
+ {
355
+ name: "set_sender_context",
356
+ description: "Save one exact outbound sender's personal context: the four lists the message writer uses as that sender's fact bank and voice (workStories, personalNotes, voiceExamples, sourcesAndLimits). This is what a sender interview produces. It replaces the sender's whole saved context; it cannot touch another sender, the calendar link, writing style, dos/donts or Company Info. Read get_sender first and pass generationContext.updatedAt as expectedUpdatedAt (null when nothing is saved yet). Limits: at most 10 entries per list, 3000 characters per entry, 15000 UTF-8 bytes in all. Only write what the sender said or confirmed; never copy one sender's facts into another's. Pass senderContext: null to clear it.",
357
+ inputSchema: {
358
+ type: "object",
359
+ properties: {
360
+ senderId: { type: "string", minLength: 1 },
361
+ workspaceId: { type: "string", minLength: 1 },
362
+ senderContext: {
363
+ type: ["object", "null"],
364
+ properties: {
365
+ workStories: senderContextEntriesInputSchema,
366
+ personalNotes: senderContextEntriesInputSchema,
367
+ voiceExamples: senderContextEntriesInputSchema,
368
+ sourcesAndLimits: senderContextEntriesInputSchema,
369
+ },
370
+ additionalProperties: false,
371
+ },
372
+ expectedUpdatedAt: nullableStringSchema,
373
+ },
374
+ required: [
375
+ "senderId",
376
+ "workspaceId",
377
+ "senderContext",
378
+ "expectedUpdatedAt",
379
+ ],
380
+ additionalProperties: false,
381
+ },
382
+ annotations: {
383
+ readOnlyHint: false,
384
+ destructiveHint: true,
385
+ idempotentHint: true,
386
+ openWorldHint: false,
387
+ },
388
+ },
349
389
  ];
350
390
  export async function listSenders(input = {}) {
351
391
  const api = getApi();
@@ -497,6 +537,134 @@ export async function setSenderCalendarLink(input) {
497
537
  });
498
538
  }
499
539
  }
540
+ const SENDER_CONTEXT_LISTS = [
541
+ "workStories",
542
+ "personalNotes",
543
+ "voiceExamples",
544
+ "sourcesAndLimits",
545
+ ];
546
+ function senderContextResult(value) {
547
+ return {
548
+ structuredContent: value,
549
+ content: [{ type: "text", text: JSON.stringify(value) }],
550
+ ...(value.ok === true ? {} : { isError: true }),
551
+ };
552
+ }
553
+ function isSenderContextInput(value) {
554
+ if (value === null)
555
+ return true;
556
+ if (!value || typeof value !== "object" || Array.isArray(value))
557
+ return false;
558
+ return Object.entries(value).every(([key, entries]) => SENDER_CONTEXT_LISTS.includes(key) &&
559
+ Array.isArray(entries) &&
560
+ entries.every((entry) => typeof entry === "string"));
561
+ }
562
+ /** Save one sender's personal context through the compare-before-write route. */
563
+ export async function setSenderContext(input) {
564
+ const candidate = input;
565
+ const record = candidate && typeof candidate === "object" && !Array.isArray(candidate)
566
+ ? candidate
567
+ : null;
568
+ const senderId = record && typeof record.senderId === "string" ? record.senderId.trim() : "";
569
+ const workspaceId = record && typeof record.workspaceId === "string"
570
+ ? record.workspaceId.trim()
571
+ : "";
572
+ const hasOwn = (key) => record !== null && Object.prototype.hasOwnProperty.call(record, key);
573
+ if (!record ||
574
+ !senderId ||
575
+ !workspaceId ||
576
+ !hasOwn("senderContext") ||
577
+ !isSenderContextInput(record.senderContext) ||
578
+ !hasOwn("expectedUpdatedAt") ||
579
+ !isNullableString(record.expectedUpdatedAt)) {
580
+ return senderContextResult({
581
+ ok: false,
582
+ code: "invalid_arguments",
583
+ message: "senderId, workspaceId, senderContext and expectedUpdatedAt are required with the documented types.",
584
+ status: null,
585
+ uncertain: false,
586
+ currentUpdatedAt: null,
587
+ guidance: "Read get_sender and retry with generationContext.updatedAt as expectedUpdatedAt (null when nothing is saved).",
588
+ });
589
+ }
590
+ const api = getApi();
591
+ try {
592
+ const receipt = await api.put(`/api/v3/mcp/senders/${encodeURIComponent(senderId)}/sender-context`, {
593
+ workspaceId,
594
+ senderContext: record.senderContext,
595
+ expectedUpdatedAt: record.expectedUpdatedAt,
596
+ }, workspaceRequestOptions(workspaceId));
597
+ if (!receipt ||
598
+ typeof receipt !== "object" ||
599
+ typeof receipt.updatedAt !== "string") {
600
+ return senderContextResult({
601
+ ok: false,
602
+ code: "malformed_sender_context_response",
603
+ message: "The sender-context save returned an incomplete receipt, so the final state is uncertain.",
604
+ status: null,
605
+ uncertain: true,
606
+ currentUpdatedAt: null,
607
+ guidance: "Reread get_sender and reconcile before retrying.",
608
+ });
609
+ }
610
+ return senderContextResult({
611
+ ok: true,
612
+ senderId,
613
+ workspaceId,
614
+ senderContext: receipt.senderContext ?? null,
615
+ previousUpdatedAt: record.expectedUpdatedAt,
616
+ updatedAt: receipt.updatedAt,
617
+ cleared: receipt.cleared === true,
618
+ });
619
+ }
620
+ catch (error) {
621
+ if (error instanceof SellableApiError) {
622
+ const body = parseErrorBody(error.body);
623
+ const code = safeErrorCode(body?.code, `http_${error.status}`);
624
+ const isConflict = error.status === 409 && code === "sender_context_conflict";
625
+ const uncertain = error.status >= 500;
626
+ return senderContextResult({
627
+ ok: false,
628
+ code,
629
+ message: isConflict
630
+ ? "The sender's saved context changed after it was read."
631
+ : uncertain
632
+ ? "The sender-context save did not complete with a confirmed result."
633
+ : error.isAuthError
634
+ ? "Sellable authentication or authorization failed."
635
+ : error.status === 404
636
+ ? "The sender was not found in the explicit workspace."
637
+ : "The sender context was not saved.",
638
+ status: error.status,
639
+ uncertain,
640
+ currentUpdatedAt: isConflict && isNullableString(body?.currentUpdatedAt)
641
+ ? body.currentUpdatedAt
642
+ : null,
643
+ guidance: isConflict
644
+ ? "Reread get_sender, merge with the newer context, and retry with its updatedAt."
645
+ : uncertain
646
+ ? "Reread get_sender and reconcile before retrying."
647
+ : error.isAuthError
648
+ ? error.guidance ||
649
+ "Authenticate again and retry the same scoped request."
650
+ : error.status === 400
651
+ ? "Keep each list to 10 entries of at most 3000 characters and 15000 UTF-8 bytes in all, then retry."
652
+ : error.status === 404
653
+ ? "Reread list_senders in the explicit workspace and select an existing sender."
654
+ : null,
655
+ });
656
+ }
657
+ return senderContextResult({
658
+ ok: false,
659
+ code: "sender_context_uncertain",
660
+ message: "The sender-context request ended without a confirmed response, so the final state is uncertain.",
661
+ status: null,
662
+ uncertain: true,
663
+ currentUpdatedAt: null,
664
+ guidance: "Reread get_sender and reconcile before retrying.",
665
+ });
666
+ }
667
+ }
500
668
  export async function getSender(input) {
501
669
  const api = getApi();
502
670
  const requestOptions = workspaceRequestOptions(input.workspaceId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.949",
3
+ "version": "0.1.951-wip.sendercontext.20261001.1",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code, Codex, and Hermes campaign workflows",
6
6
  "main": "dist/index.js",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: first-campaign-onboarding
3
- description: Help a new Sellable workspace create its first LinkedIn outbound campaign. A yes reply to the welcome starts the existing create-campaign workflow immediately, with DNC and sender setup at the appropriate gates. Send one next-step scheduled reminder for at most 14 days. Use for welcome-thread replies, first-campaign setup, self-serve onboarding, or scheduled onboarding check-ins.
3
+ description: Help a new Sellable workspace create its first LinkedIn outbound campaign. A LinkedIn profile URL (or a yes) in reply to the welcome starts the existing create-campaign workflow immediately, with DNC and sender setup at the appropriate gates. Send one next-step scheduled reminder for at most 14 days. Use for welcome-thread replies, first-campaign setup, self-serve onboarding, or scheduled onboarding check-ins.
4
4
  ---
5
5
 
6
6
  # First Campaign Onboarding
@@ -39,12 +39,29 @@ Classify the run before taking action.
39
39
 
40
40
  ### Interactive
41
41
 
42
- The user is present and can answer. A plain **yes** in the thread whose parent
43
- asks "Ready to create your first LinkedIn outbound campaign?" is explicit
44
- permission to begin preparation, not permission to send. Acknowledge briefly
45
- and enter **Create-Campaign Handoff** in the same turn. Do not ask for a second
46
- command, a mention, sender connection, DNC list, or integrations first. The
47
- existing workflow owns intake; do not invent a second onboarding questionnaire.
42
+ The user is present and can answer. The welcome is the thread parent. It asks
43
+ one question, "what's your LinkedIn profile URL?", and says the profile is used
44
+ to research the customer and their company. (A welcome sent before October 2026
45
+ asks "Ready to create your first LinkedIn outbound campaign?" instead; treat a
46
+ reply to either the same way.)
47
+
48
+ A reply in that thread is explicit permission to begin preparation, not
49
+ permission to send. It needs no mention of this Agent.
50
+
51
+ - **The reply contains a LinkedIn profile URL.** Before any tool call, send
52
+ one short line so the customer is not left looking at silence: "Got it.
53
+ Researching you and your company now. I'll be back in a couple of minutes
54
+ with a first campaign to review." Then enter **Create-Campaign Handoff** in
55
+ the same turn, with that URL as the sender identity.
56
+ - **The reply is a yes, or anything else that agrees, with no profile URL.**
57
+ Ask for it once, in one sentence: "What's your LinkedIn profile URL? I'll
58
+ use it to research you and your company." Ask nothing else.
59
+ - **The reply is a question.** Answer it briefly, then repeat the one ask.
60
+
61
+ Do not ask for a second command, a mention, sender connection, DNC list, or
62
+ integrations first. The existing workflow owns intake; do not invent a second
63
+ onboarding questionnaire. Never guess the company name; use it only once the
64
+ profile or the workspace gives it.
48
65
 
49
66
  Use the parent thread to interpret short replies; a yes elsewhere is not
50
67
  blanket authorization. Keep replies in the same thread. To start a separate
@@ -172,7 +189,8 @@ sent separately when the Agent becomes ready; do not repeat it daily.
172
189
  1. **Invite not accepted:** include the refreshed invite link and ask them to
173
190
  join. Do not combine it with a sender/DNC checklist.
174
191
  2. **Member present, no draft:** "Want to get your first LinkedIn outbound
175
- campaign started? Reply yes in this thread and we'll work out who to reach."
192
+ campaign started? Reply in this thread with your LinkedIn profile URL. I'll
193
+ research you and your company and bring back a first campaign to review."
176
194
  Do not block this invitation on a connected sender or DNC list.
177
195
  3. **Draft present:** name the most advanced draft and the saved next action
178
196
  (for example, approving its brief). Offer to continue it, never duplicate it.
@@ -186,8 +204,8 @@ approval. Do not repeat multiple safety disclaimers.
186
204
 
187
205
  ## Create-Campaign Handoff
188
206
 
189
- After explicit agreement (including yes to the welcome), continue in the same
190
- conversation; do not ask the user to run another command.
207
+ After explicit agreement (a profile URL or a yes in the welcome thread),
208
+ continue in the same conversation; do not ask the user to run another command.
191
209
 
192
210
  1. Call `bootstrap_create_campaign({ flowVersion:"v2", host:<current host> })`.
193
211
  2. Load `get_subskill_prompt({ subskillName:"create-campaign-v2" })` to