@sellable/mcp 0.1.267 → 0.1.269

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,14 +1,3 @@
1
- type FollowerBandFit = "in_target_band" | "below_target_band" | "above_target_band" | "unknown";
2
- type ReachSignals = {
3
- targetFollowerMin?: number;
4
- targetFollowerMax?: number;
5
- followerBandFit: FollowerBandFit;
6
- engagementPer1kFollowers: number | null;
7
- weightedEngagementPer1kFollowers: number | null;
8
- reachPenaltyMultiplier: number;
9
- reachAdjustedScore: number;
10
- confidence: "high" | "medium" | "low";
11
- };
12
1
  export type EngagementPost = {
13
2
  postId: string;
14
3
  url: string;
@@ -18,7 +7,6 @@ export type EngagementPost = {
18
7
  name: string;
19
8
  headline: string;
20
9
  profileUrl: string;
21
- followerCount?: number;
22
10
  };
23
11
  engagement: {
24
12
  likes: number;
@@ -26,7 +14,6 @@ export type EngagementPost = {
26
14
  shares: number;
27
15
  total: number;
28
16
  };
29
- reachSignals?: ReachSignals;
30
17
  contentPreview: string;
31
18
  };
32
19
  export type SearchEngagementPostsInput = {
@@ -36,8 +23,6 @@ export type SearchEngagementPostsInput = {
36
23
  minTotalEngagement?: number;
37
24
  maxPosts?: number;
38
25
  excludePostUrls?: string[];
39
- targetFollowerMin?: number;
40
- targetFollowerMax?: number;
41
26
  };
42
27
  export type SearchEngagementPostsResponse = {
43
28
  success: boolean;
@@ -85,18 +70,9 @@ export declare const engageDiscoveryToolDefinitions: {
85
70
  };
86
71
  description: string;
87
72
  };
88
- targetFollowerMin: {
89
- type: string;
90
- description: string;
91
- };
92
- targetFollowerMax: {
93
- type: string;
94
- description: string;
95
- };
96
73
  };
97
74
  required: string[];
98
75
  additionalProperties: boolean;
99
76
  };
100
77
  }[];
101
78
  export declare function searchEngagementPosts(input: SearchEngagementPostsInput): Promise<SearchEngagementPostsResponse>;
102
- export {};
@@ -32,14 +32,6 @@ export const engageDiscoveryToolDefinitions = [
32
32
  items: { type: "string" },
33
33
  description: "Optional list of post URLs to exclude (e.g. already engaged/scheduled).",
34
34
  },
35
- targetFollowerMin: {
36
- type: "number",
37
- description: "Optional lower bound for creator follower count when reach-normalizing hook/source quality.",
38
- },
39
- targetFollowerMax: {
40
- type: "number",
41
- description: "Optional upper bound for creator follower count when reach-normalizing hook/source quality.",
42
- },
43
35
  },
44
36
  required: ["keywords"],
45
37
  additionalProperties: false,
@@ -71,79 +63,6 @@ function safeNumber(value) {
71
63
  const n = typeof value === "number" ? value : Number(value);
72
64
  return Number.isFinite(n) ? n : 0;
73
65
  }
74
- function round3(value) {
75
- return Number(value.toFixed(3));
76
- }
77
- function parseFollowerCount(author) {
78
- const direct = safeNumber(author?.followerCount);
79
- if (direct > 0)
80
- return direct;
81
- const text = String(author?.followers || author?.followerCountText || "");
82
- const match = text.match(/([\d,.]+)\s*([kKmM])?/);
83
- if (!match)
84
- return undefined;
85
- const base = Number(match[1].replace(/,/g, ""));
86
- if (!Number.isFinite(base) || base <= 0)
87
- return undefined;
88
- const suffix = match[2]?.toLowerCase();
89
- if (suffix === "m")
90
- return Math.round(base * 1_000_000);
91
- if (suffix === "k")
92
- return Math.round(base * 1_000);
93
- return Math.round(base);
94
- }
95
- function reachPenaltyMultiplier(followerCount, targetFollowerMin, targetFollowerMax) {
96
- if (!followerCount || !targetFollowerMin || !targetFollowerMax)
97
- return 0.4;
98
- if (followerCount >= targetFollowerMin && followerCount <= targetFollowerMax) {
99
- return 1;
100
- }
101
- if (followerCount < targetFollowerMin)
102
- return 0.75;
103
- if (followerCount <= targetFollowerMax * 2)
104
- return 0.65;
105
- if (followerCount <= targetFollowerMax * 5)
106
- return 0.35;
107
- return 0.15;
108
- }
109
- function followerBandFit(followerCount, targetFollowerMin, targetFollowerMax) {
110
- if (!followerCount || !targetFollowerMin || !targetFollowerMax) {
111
- return "unknown";
112
- }
113
- if (followerCount >= targetFollowerMin && followerCount <= targetFollowerMax) {
114
- return "in_target_band";
115
- }
116
- if (followerCount < targetFollowerMin)
117
- return "below_target_band";
118
- return "above_target_band";
119
- }
120
- function buildReachSignals(params) {
121
- const { followerCount, likes, comments, shares, total } = params;
122
- const hasTarget = Boolean(params.targetFollowerMin && params.targetFollowerMax);
123
- if (!hasTarget && !followerCount)
124
- return undefined;
125
- const weightedEngagement = likes + comments * 4 + shares * 12;
126
- const penalty = reachPenaltyMultiplier(followerCount, params.targetFollowerMin, params.targetFollowerMax);
127
- const engagementPer1kFollowers = followerCount
128
- ? round3((total / followerCount) * 1000)
129
- : null;
130
- const weightedEngagementPer1kFollowers = followerCount
131
- ? round3((weightedEngagement / followerCount) * 1000)
132
- : null;
133
- const reachAdjustedScore = weightedEngagementPer1kFollowers === null
134
- ? 0
135
- : round3(weightedEngagementPer1kFollowers * penalty);
136
- return {
137
- targetFollowerMin: params.targetFollowerMin,
138
- targetFollowerMax: params.targetFollowerMax,
139
- followerBandFit: followerBandFit(followerCount, params.targetFollowerMin, params.targetFollowerMax),
140
- engagementPer1kFollowers,
141
- weightedEngagementPer1kFollowers,
142
- reachPenaltyMultiplier: penalty,
143
- reachAdjustedScore,
144
- confidence: followerCount ? (hasTarget ? "high" : "medium") : "low",
145
- };
146
- }
147
66
  export async function searchEngagementPosts(input) {
148
67
  const api = getApi();
149
68
  const keywords = (input.keywords || [])
@@ -160,12 +79,6 @@ export async function searchEngagementPosts(input) {
160
79
  const maxPosts = typeof input.maxPosts === "number" && input.maxPosts > 0
161
80
  ? Math.min(50, input.maxPosts)
162
81
  : 25;
163
- const targetFollowerMin = typeof input.targetFollowerMin === "number" && input.targetFollowerMin > 0
164
- ? input.targetFollowerMin
165
- : undefined;
166
- const targetFollowerMax = typeof input.targetFollowerMax === "number" && input.targetFollowerMax > 0
167
- ? input.targetFollowerMax
168
- : undefined;
169
82
  const exclude = new Set((input.excludePostUrls || [])
170
83
  .map((u) => normalizePostUrl(u))
171
84
  .filter(Boolean));
@@ -174,10 +87,10 @@ export async function searchEngagementPosts(input) {
174
87
  keywords: keywords.map((keyword) => ({ keyword })),
175
88
  page,
176
89
  });
177
- const rawPosts = Array.isArray(response?.posts)
178
- ? response.posts
179
- : Array.isArray(response?.topPostsForLLM)
180
- ? response.topPostsForLLM
90
+ const rawPosts = Array.isArray(response?.topPostsForLLM)
91
+ ? response.topPostsForLLM
92
+ : Array.isArray(response?.posts)
93
+ ? response.posts
181
94
  : [];
182
95
  const now = Date.now();
183
96
  const oldestMs = now - maxAgeDays * 24 * 60 * 60 * 1000;
@@ -208,16 +121,6 @@ export async function searchEngagementPosts(input) {
208
121
  tooOld += 1;
209
122
  continue;
210
123
  }
211
- const followerCount = parseFollowerCount(p?.author);
212
- const reachSignals = buildReachSignals({
213
- followerCount,
214
- likes,
215
- comments,
216
- shares,
217
- total,
218
- targetFollowerMin,
219
- targetFollowerMax,
220
- });
221
124
  kept.push({
222
125
  postId: String(p?.id || ""),
223
126
  url,
@@ -229,26 +132,18 @@ export async function searchEngagementPosts(input) {
229
132
  name: String(p?.author?.name || ""),
230
133
  headline: String(p?.author?.headline || ""),
231
134
  profileUrl: String(p?.author?.profileUrl || ""),
232
- ...(followerCount ? { followerCount } : {}),
233
135
  },
234
136
  engagement: { likes, comments, shares, total },
235
- ...(reachSignals ? { reachSignals } : {}),
236
137
  contentPreview: previewText(String(p?.content || ""), 220),
237
138
  });
139
+ if (kept.length >= maxPosts)
140
+ break;
238
141
  }
239
- const hasReachTarget = Boolean(targetFollowerMin && targetFollowerMax);
240
- kept.sort((a, b) => {
241
- if (hasReachTarget) {
242
- const reachDelta = (b.reachSignals?.reachAdjustedScore || 0) -
243
- (a.reachSignals?.reachAdjustedScore || 0);
244
- if (reachDelta !== 0)
245
- return reachDelta;
246
- }
247
- return b.engagement.total - a.engagement.total;
248
- });
142
+ // Sort by total engagement desc for a predictable shortlist.
143
+ kept.sort((a, b) => b.engagement.total - a.engagement.total);
249
144
  return {
250
145
  success: true,
251
- posts: kept.slice(0, maxPosts),
146
+ posts: kept,
252
147
  totalReturned: rawPosts.length,
253
148
  filteredOut: { tooOld, excluded, tooLowEngagement },
254
149
  };
@@ -0,0 +1,182 @@
1
+ type ArtifactFormat = "markdown" | "csv" | "both";
2
+ export interface SearchHarvestJobsInput {
3
+ search?: string;
4
+ searches?: string[];
5
+ location?: string;
6
+ geoId?: string;
7
+ postedLimit?: "24h" | "week" | "month";
8
+ sortBy?: "relevance" | "date";
9
+ workplaceType?: string | string[];
10
+ employmentType?: string | string[];
11
+ experienceLevel?: string | string[];
12
+ easyApply?: boolean;
13
+ under10Applicants?: boolean;
14
+ salary?: string;
15
+ pages?: number;
16
+ maxRows?: number;
17
+ artifactFormat?: ArtifactFormat;
18
+ outputDir?: string;
19
+ fileBaseName?: string;
20
+ }
21
+ export interface ConfirmHarvestJobCompaniesInput {
22
+ searchToken?: string;
23
+ selectedJobIds?: string[];
24
+ name?: string;
25
+ outputDir?: string;
26
+ fileBaseName?: string;
27
+ [key: string]: unknown;
28
+ }
29
+ export declare const harvestJobToolDefinitions: ({
30
+ name: string;
31
+ description: string;
32
+ inputSchema: {
33
+ type: string;
34
+ properties: {
35
+ search: {
36
+ type: string;
37
+ };
38
+ searches: {
39
+ type: string;
40
+ items: {
41
+ type: string;
42
+ };
43
+ };
44
+ location: {
45
+ type: string;
46
+ };
47
+ geoId: {
48
+ type: string;
49
+ };
50
+ postedLimit: {
51
+ type: string;
52
+ enum: string[];
53
+ };
54
+ sortBy: {
55
+ type: string;
56
+ enum: string[];
57
+ };
58
+ workplaceType: {
59
+ oneOf: ({
60
+ type: string;
61
+ items?: undefined;
62
+ } | {
63
+ type: string;
64
+ items: {
65
+ type: string;
66
+ };
67
+ })[];
68
+ };
69
+ employmentType: {
70
+ oneOf: ({
71
+ type: string;
72
+ items?: undefined;
73
+ } | {
74
+ type: string;
75
+ items: {
76
+ type: string;
77
+ };
78
+ })[];
79
+ };
80
+ experienceLevel: {
81
+ oneOf: ({
82
+ type: string;
83
+ items?: undefined;
84
+ } | {
85
+ type: string;
86
+ items: {
87
+ type: string;
88
+ };
89
+ })[];
90
+ };
91
+ easyApply: {
92
+ type: string;
93
+ };
94
+ under10Applicants: {
95
+ type: string;
96
+ };
97
+ salary: {
98
+ type: string;
99
+ };
100
+ pages: {
101
+ type: string;
102
+ description: string;
103
+ };
104
+ maxRows: {
105
+ type: string;
106
+ description: string;
107
+ };
108
+ artifactFormat: {
109
+ type: string;
110
+ enum: string[];
111
+ description: string;
112
+ };
113
+ outputDir: {
114
+ type: string;
115
+ description: string;
116
+ };
117
+ fileBaseName: {
118
+ type: string;
119
+ description: string;
120
+ };
121
+ searchToken?: undefined;
122
+ selectedJobIds?: undefined;
123
+ name?: undefined;
124
+ };
125
+ required: never[];
126
+ additionalProperties: boolean;
127
+ };
128
+ } | {
129
+ name: string;
130
+ description: string;
131
+ inputSchema: {
132
+ type: string;
133
+ properties: {
134
+ searchToken: {
135
+ type: string;
136
+ description: string;
137
+ };
138
+ selectedJobIds: {
139
+ type: string;
140
+ items: {
141
+ type: string;
142
+ };
143
+ description: string;
144
+ };
145
+ name: {
146
+ type: string;
147
+ };
148
+ outputDir: {
149
+ type: string;
150
+ description: string;
151
+ };
152
+ fileBaseName: {
153
+ type: string;
154
+ description: string;
155
+ };
156
+ search?: undefined;
157
+ searches?: undefined;
158
+ location?: undefined;
159
+ geoId?: undefined;
160
+ postedLimit?: undefined;
161
+ sortBy?: undefined;
162
+ workplaceType?: undefined;
163
+ employmentType?: undefined;
164
+ experienceLevel?: undefined;
165
+ easyApply?: undefined;
166
+ under10Applicants?: undefined;
167
+ salary?: undefined;
168
+ pages?: undefined;
169
+ maxRows?: undefined;
170
+ artifactFormat?: undefined;
171
+ };
172
+ required: string[];
173
+ additionalProperties: boolean;
174
+ };
175
+ })[];
176
+ export declare function searchHarvestJobs(input: SearchHarvestJobsInput): Promise<{
177
+ [k: string]: unknown;
178
+ }>;
179
+ export declare function confirmHarvestJobCompanies(input: ConfirmHarvestJobCompaniesInput): Promise<{
180
+ [k: string]: unknown;
181
+ }>;
182
+ export {};