@sellable/mcp 0.1.498 → 0.1.500

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.
@@ -7,6 +7,9 @@ import { refreshPaidInmailCredits } from "./senders.js";
7
7
  const DEFAULT_SCHEDULER_FORWARD_HOURS = 48;
8
8
  const PAID_INMAIL_REFRESH_MAX_ATTEMPTS = 3;
9
9
  const PAID_INMAIL_REFRESH_RETRY_DELAY_MS = Number(process.env.SELLABLE_MCP_PAID_REFRESH_RETRY_DELAY_MS ?? "1000");
10
+ const SIGNAL_DISCOVERY_MIN_REFILL_POSTS = 3;
11
+ const SIGNAL_DISCOVERY_MAX_REFILL_POSTS = 10;
12
+ const SIGNAL_DISCOVERY_TARGET_ROWS_PER_POST = 150;
10
13
  function normalizeStrings(values) {
11
14
  if (!Array.isArray(values))
12
15
  return [];
@@ -491,7 +494,13 @@ function keywordsFromSignalTabs(tabs) {
491
494
  }
492
495
  return uniqueStrings([...selectedKeywords, ...fallbackKeywords]).slice(0, 5);
493
496
  }
494
- function postIdsFromSignalSearch(summary, maxPosts) {
497
+ function selectedPostIdsFromSignalTabs(tabs) {
498
+ return uniqueStrings(tabs.flatMap((tab) => (tab.posts ?? [])
499
+ .filter((post) => post.isSelected === true)
500
+ .map((post) => post.id)));
501
+ }
502
+ function postIdsFromSignalSearch(summary, maxPosts, options = {}) {
503
+ const excludedPostIds = new Set((options.excludePostIds ?? []).map((postId) => postId.toLowerCase()));
495
504
  const recommendedPostIds = stringArray(summary?.recommendedPostIds);
496
505
  const topPostIds = Array.isArray(summary?.topPosts)
497
506
  ? summary.topPosts
@@ -500,7 +509,12 @@ function postIdsFromSignalSearch(summary, maxPosts) {
500
509
  : null)
501
510
  .filter((id) => Boolean(id))
502
511
  : [];
503
- return uniqueStrings([...recommendedPostIds, ...topPostIds]).slice(0, Math.max(1, maxPosts));
512
+ return uniqueStrings([...recommendedPostIds, ...topPostIds])
513
+ .filter((postId) => !excludedPostIds.has(postId.toLowerCase()))
514
+ .slice(0, Math.max(1, maxPosts));
515
+ }
516
+ function maxSignalDiscoveryPostsForSourceLimit(sourceRowLimit) {
517
+ return Math.min(SIGNAL_DISCOVERY_MAX_REFILL_POSTS, Math.max(SIGNAL_DISCOVERY_MIN_REFILL_POSTS, Math.ceil(sourceRowLimit / SIGNAL_DISCOVERY_TARGET_ROWS_PER_POST)));
504
518
  }
505
519
  function refillPrepareRequestHash(params) {
506
520
  return [
@@ -553,7 +567,7 @@ async function continueSignalDiscoverySource(action) {
553
567
  numberValue(toolInput.targetRows) ??
554
568
  numberValue(action.targetRows) ??
555
569
  100)));
556
- const maxPostsToScrape = Math.min(5, Math.max(3, Math.ceil(sourceRowLimit / 500)));
570
+ const maxPostsToScrape = maxSignalDiscoveryPostsForSourceLimit(sourceRowLimit);
557
571
  if (!campaignOfferId || !sourceLeadListId || !sourceFingerprint) {
558
572
  return {
559
573
  status: "refused",
@@ -591,7 +605,9 @@ async function continueSignalDiscoverySource(action) {
591
605
  ? stringArray(sourceConfig?.headlineICPCriteria)
592
606
  : stringArray(sourceConfig?.rubricGuidelines);
593
607
  const tabsResponse = await api.get(`/api/v3/campaigns/${campaignOfferId}/signal-discovery/tabs`);
594
- const keywords = keywordsFromSignalTabs(tabsResponse.tabs ?? []);
608
+ const signalTabs = tabsResponse.tabs ?? [];
609
+ const keywords = keywordsFromSignalTabs(signalTabs);
610
+ const excludedPostIds = selectedPostIdsFromSignalTabs(signalTabs);
595
611
  if (keywords.length === 0) {
596
612
  return {
597
613
  status: "refused",
@@ -615,12 +631,12 @@ async function continueSignalDiscoverySource(action) {
615
631
  confirmed: true,
616
632
  limit: 50,
617
633
  });
618
- const selectedPostIds = postIdsFromSignalSearch(searchSummary, maxPostsToScrape);
634
+ const selectedPostIds = postIdsFromSignalSearch(searchSummary, maxPostsToScrape, { excludePostIds: excludedPostIds });
619
635
  if (selectedPostIds.length === 0) {
620
636
  return {
621
637
  status: "refused",
622
- refusalReason: "Signal Discovery search returned no recommended posts to continue the source",
623
- result: { keywords, searchSummary },
638
+ refusalReason: "Signal Discovery search returned no new recommended posts to continue the source",
639
+ result: { keywords, excludedPostIds, searchSummary },
624
640
  };
625
641
  }
626
642
  const selectionResult = await selectPromisingPosts({
@@ -659,7 +675,13 @@ async function continueSignalDiscoverySource(action) {
659
675
  status: "refused",
660
676
  refusalReason: stringValue(importRecord.message) ??
661
677
  `Signal Discovery import returned ${String(importRecord.error)}`,
662
- result: { keywords, selectedPostIds, selectionResult, importResult },
678
+ result: {
679
+ keywords,
680
+ excludedPostIds,
681
+ selectedPostIds,
682
+ selectionResult,
683
+ importResult,
684
+ },
663
685
  };
664
686
  }
665
687
  return {
@@ -670,6 +692,7 @@ async function continueSignalDiscoverySource(action) {
670
692
  previousSourceLeadListId: sourceLeadListId,
671
693
  sourceFingerprint,
672
694
  keywords,
695
+ excludedPostIds,
673
696
  selectedPostIds,
674
697
  sourceRowLimit,
675
698
  maxPostsToScrape: selectedPostIds.length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sellable/mcp",
3
- "version": "0.1.498",
3
+ "version": "0.1.500",
4
4
  "type": "module",
5
5
  "description": "Sellable MCP server for Claude Code and Codex campaign workflows",
6
6
  "main": "dist/index.js",