@hyperstar/mcp 0.1.9 → 0.1.11

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.
Files changed (57) hide show
  1. package/README.md +52 -34
  2. package/dist/auth/cli-auth-client.js +0 -1
  3. package/dist/auth/cli-auth-state.js +0 -1
  4. package/dist/auth/local-callback.js +0 -1
  5. package/dist/auth/pkce.js +0 -1
  6. package/dist/bulk-email-guidance.js +0 -1
  7. package/dist/bulk-email-inputs.d.ts +254 -0
  8. package/dist/bulk-email-inputs.js +168 -0
  9. package/dist/bulk-email-tool-body.d.ts +6 -0
  10. package/dist/bulk-email-tool-body.js +37 -0
  11. package/dist/campaign-creator-selection.js +0 -1
  12. package/dist/cli.js +2 -2
  13. package/dist/config.js +15 -2
  14. package/dist/discovery.js +0 -1
  15. package/dist/http.js +0 -1
  16. package/dist/index.js +0 -1
  17. package/dist/package-metadata.d.ts +2 -0
  18. package/dist/package-metadata.js +12 -0
  19. package/dist/schemas.js +0 -1
  20. package/dist/search-result-projection.d.ts +3 -1
  21. package/dist/search-result-projection.js +33 -3
  22. package/dist/server.js +5 -2
  23. package/dist/tool-descriptions.d.ts +4 -4
  24. package/dist/tool-descriptions.js +4 -5
  25. package/dist/tool-guidance.js +24 -7
  26. package/dist/tool-http-helpers.js +0 -1
  27. package/dist/tool-inputs.d.ts +324 -131
  28. package/dist/tool-inputs.js +87 -92
  29. package/dist/tool-result.js +0 -1
  30. package/dist/tools.js +11 -31
  31. package/dist/workflow-content.d.ts +1 -0
  32. package/dist/workflow-content.js +19 -3
  33. package/dist/workspaces.d.ts +1 -1
  34. package/dist/workspaces.js +9 -2
  35. package/package.json +24 -2
  36. package/dist/auth/cli-auth-client.js.map +0 -1
  37. package/dist/auth/cli-auth-state.js.map +0 -1
  38. package/dist/auth/local-callback.js.map +0 -1
  39. package/dist/auth/pkce.js.map +0 -1
  40. package/dist/bulk-email-guidance.js.map +0 -1
  41. package/dist/campaign-creator-selection.js.map +0 -1
  42. package/dist/cli.js.map +0 -1
  43. package/dist/config.js.map +0 -1
  44. package/dist/discovery.js.map +0 -1
  45. package/dist/http.js.map +0 -1
  46. package/dist/index.js.map +0 -1
  47. package/dist/schemas.js.map +0 -1
  48. package/dist/search-result-projection.js.map +0 -1
  49. package/dist/server.js.map +0 -1
  50. package/dist/tool-descriptions.js.map +0 -1
  51. package/dist/tool-guidance.js.map +0 -1
  52. package/dist/tool-http-helpers.js.map +0 -1
  53. package/dist/tool-inputs.js.map +0 -1
  54. package/dist/tool-result.js.map +0 -1
  55. package/dist/tools.js.map +0 -1
  56. package/dist/workflow-content.js.map +0 -1
  57. package/dist/workspaces.js.map +0 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@ CLI login.
7
7
 
8
8
  ## Install
9
9
 
10
- After `@hyperstar/mcp` is published to npm:
10
+ Run the MCP server directly from npm:
11
11
 
12
12
  ```sh
13
13
  npx -y --package @hyperstar/mcp hyperstar-mcp
@@ -37,16 +37,16 @@ npx -y --package @hyperstar/mcp hyperstar workspaces list --json
37
37
  npx -y --package @hyperstar/mcp hyperstar workspaces use org_123
38
38
  ```
39
39
 
40
- Before the first npm publish, dev e2e should use a local pack:
40
+ For unreleased local package testing, pack from the package directory and use
41
+ the filename printed by `npm pack`:
41
42
 
42
43
  ```sh
43
44
  cd projects/hyperstar-mcp
44
- npm pack --dry-run
45
- npm pack
46
- npx -y --package ./hyperstar-mcp-0.1.0.tgz hyperstar-mcp
47
- npx -y --package ./hyperstar-mcp-0.1.0.tgz hyperstar login
48
- npx -y --package ./hyperstar-mcp-0.1.0.tgz hyperstar workspaces list --json
49
- npx -y --package ./hyperstar-mcp-0.1.0.tgz hyperstar workspaces use org_123
45
+ TARBALL=$(npm pack --silent)
46
+ npx -y --package "./$TARBALL" hyperstar-mcp
47
+ npx -y --package "./$TARBALL" hyperstar login
48
+ npx -y --package "./$TARBALL" hyperstar workspaces list --json
49
+ npx -y --package "./$TARBALL" hyperstar workspaces use org_123
50
50
  ```
51
51
 
52
52
  The first public scoped publish must use public access. This package sets
@@ -55,8 +55,9 @@ credentials are configured.
55
55
 
56
56
  ## MCP client config
57
57
 
58
- Service-account auth is the simplest non-interactive setup. Configure the MCP
59
- server with a Hyperstar Product API base URL and service-account API key:
58
+ Service-account auth is the simplest non-interactive setup. For production,
59
+ configure only the service-account API key and use the package's default
60
+ Hyperstar Product API base URL:
60
61
 
61
62
  ```json
62
63
  {
@@ -65,7 +66,6 @@ server with a Hyperstar Product API base URL and service-account API key:
65
66
  "command": "npx",
66
67
  "args": ["-y", "--package", "@hyperstar/mcp", "hyperstar-mcp"],
67
68
  "env": {
68
- "HYPERSTAR_API_BASE_URL": "https://api.hyperstar.example",
69
69
  "HYPERSTAR_API_KEY": "hstar_test_public-prefix.secret"
70
70
  }
71
71
  }
@@ -85,11 +85,7 @@ login state created by the `hyperstar` CLI:
85
85
  "mcpServers": {
86
86
  "hyperstar": {
87
87
  "command": "npx",
88
- "args": ["-y", "--package", "@hyperstar/mcp", "hyperstar-mcp"],
89
- "env": {
90
- "HYPERSTAR_API_BASE_URL": "https://api.hyperstar.example",
91
- "HYPERSTAR_APP_BASE_URL": "https://app.hyperstar.example"
92
- }
88
+ "args": ["-y", "--package", "@hyperstar/mcp", "hyperstar-mcp"]
93
89
  }
94
90
  }
95
91
  }
@@ -152,21 +148,6 @@ npm test
152
148
  npm run build
153
149
  ```
154
150
 
155
- Internal MCP smoke testing requires an approved fixture supplied through local
156
- environment variables:
157
-
158
- ```sh
159
- HYPERSTAR_API_BASE_URL=<internal-api-url> \
160
- HYPERSTAR_APP_BASE_URL=<internal-app-url> \
161
- HYPERSTAR_MCP_SMOKE_INTERNAL_FIXTURE=1 \
162
- HYPERSTAR_MCP_SMOKE_CAMPAIGN_ID=<campaign_id> \
163
- HYPERSTAR_MCP_SMOKE_CAMPAIGN_NAME=<campaign_name> \
164
- HYPERSTAR_MCP_SMOKE_RECIPIENT_ID=<campaign_creator_id> \
165
- npm run smoke:dev
166
- ```
167
-
168
- To include the real-send check, add `HYPERSTAR_MCP_SMOKE_REAL_SEND=1`.
169
-
170
151
  ## Examples
171
152
 
172
153
  Search creators:
@@ -184,6 +165,16 @@ Search creators:
184
165
  }
185
166
  ```
186
167
 
168
+ Common search filters are supplied as a structured JSON object. Useful fields
169
+ include `follower_range` as `{ "min": 1000, "max": 100000 }`,
170
+ `avg_engagement_rate`, `avg_views`, `has_email`, `email_contactability`,
171
+ `is_verified`, `has_tiktok_shop`, `creator_gender`, `creator_language`,
172
+ `category_1`, Instagram `category_name`, `gmv`, and `gpm`; `gmv` and `gpm` are
173
+ TikTok-only. Put broad niches and countries in `query` / `region` unless a
174
+ named structured filter applies. Use `sort_by` for `relevance`,
175
+ `follower_count`, `engagement_rate`, `avg_views`, `views_growth_rate`, `gmv`, or
176
+ `gpm`.
177
+
187
178
  Search tools return compact creator summaries by default so agents do not load
188
179
  full profile payloads into context. Use `get_search_results` with
189
180
  `detail_level: "full"` only when a page of raw creator rows is explicitly
@@ -228,9 +219,32 @@ Save search results to a campaign roster:
228
219
  }
229
220
  ```
230
221
 
222
+ Check recipient readiness before any bulk send:
223
+
224
+ ```json
225
+ {
226
+ "tool": "check_bulk_email_readiness",
227
+ "arguments": {
228
+ "campaign_id": 123,
229
+ "recipient_target": {
230
+ "type": "ids",
231
+ "campaign_creator_ids": [7, 8]
232
+ }
233
+ }
234
+ }
235
+ ```
236
+
237
+ For filtered campaign roster sends, use `recipient_target.type: "selection"`
238
+ and pass `campaign_creator_selection`. The selection must include at least one
239
+ narrowing filter such as `workflow_filter: "email_not_sent"`, `platform`,
240
+ `source_type`, `search`, or `has_video`; empty selections,
241
+ `workflow_filter: "all"` by itself, and exclusions-only selections are
242
+ rejected.
243
+
231
244
  Start a bulk email job. This performs a real send. The response includes the
232
245
  queued job fields plus `next_tool: "get_bulk_email_job"` and `next_arguments`
233
- so agents know how to check progress:
246
+ so agents know how to check progress. Only call this after readiness has passed
247
+ and the user has authorized the send:
234
248
 
235
249
  ```json
236
250
  {
@@ -239,8 +253,12 @@ so agents know how to check progress:
239
253
  "campaign_id": 123,
240
254
  "subject": "Partnership idea",
241
255
  "body_text": "Hi, we would like to introduce our new campaign.",
242
- "campaign_creator_ids": [7, 8],
243
- "idempotency_key": "campaign-123-intro-2026-07-09"
256
+ "recipient_target": {
257
+ "type": "ids",
258
+ "campaign_creator_ids": [7, 8]
259
+ },
260
+ "idempotency_key": "campaign-123-intro-2026-07-09",
261
+ "send_confirmation": "user_authorized"
244
262
  }
245
263
  }
246
264
  ```
@@ -211,4 +211,3 @@ export function tokenPairToAuthState(tokenPair, options) {
211
211
  }),
212
212
  };
213
213
  }
214
- //# sourceMappingURL=cli-auth-client.js.map
@@ -173,4 +173,3 @@ function isAlreadyExistsError(error) {
173
173
  "code" in error &&
174
174
  error.code === "EEXIST");
175
175
  }
176
- //# sourceMappingURL=cli-auth-state.js.map
@@ -87,4 +87,3 @@ async function closeServer(server) {
87
87
  });
88
88
  });
89
89
  }
90
- //# sourceMappingURL=local-callback.js.map
package/dist/auth/pkce.js CHANGED
@@ -11,4 +11,3 @@ export function createPkcePair() {
11
11
  export function createState() {
12
12
  return randomBytes(32).toString("base64url");
13
13
  }
14
- //# sourceMappingURL=pkce.js.map
@@ -33,4 +33,3 @@ function numberValue(value) {
33
33
  ? value
34
34
  : undefined;
35
35
  }
36
- //# sourceMappingURL=bulk-email-guidance.js.map
@@ -0,0 +1,254 @@
1
+ import { z } from "zod";
2
+ export declare const EmailSubjectSchema: z.ZodString;
3
+ export declare const IdempotencyKeySchema: z.ZodString;
4
+ declare const CampaignCreatorSelectionSchema: z.ZodObject<{
5
+ workflow_filter: z.ZodOptional<z.ZodEnum<{
6
+ all: "all";
7
+ email: "email";
8
+ email_not_sent: "email_not_sent";
9
+ email_sent: "email_sent";
10
+ replied: "replied";
11
+ negotiating: "negotiating";
12
+ rejected_hold: "rejected_hold";
13
+ rejected: "rejected";
14
+ hold: "hold";
15
+ contract: "contract";
16
+ contracted: "contracted";
17
+ content: "content";
18
+ draft: "draft";
19
+ final_approved: "final_approved";
20
+ posted: "posted";
21
+ }>>;
22
+ outreach_stage: z.ZodOptional<z.ZodString>;
23
+ platform: z.ZodOptional<z.ZodEnum<{
24
+ tiktok: "tiktok";
25
+ instagram: "instagram";
26
+ }>>;
27
+ country: z.ZodOptional<z.ZodString>;
28
+ has_video: z.ZodOptional<z.ZodBoolean>;
29
+ source_type: z.ZodOptional<z.ZodEnum<{
30
+ saved_list: "saved_list";
31
+ file_upload: "file_upload";
32
+ manual_add: "manual_add";
33
+ form_submission: "form_submission";
34
+ discovery_search: "discovery_search";
35
+ }>>;
36
+ search: z.ZodOptional<z.ZodString>;
37
+ excluded_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
38
+ }, z.core.$strict>;
39
+ export type CampaignCreatorSelectionInput = z.infer<typeof CampaignCreatorSelectionSchema>;
40
+ export declare const CheckBulkEmailReadinessInputSchema: z.ZodUnion<readonly [z.ZodObject<{
41
+ campaign_id: z.ZodNumber;
42
+ campaign_creator_ids: z.ZodArray<z.ZodNumber>;
43
+ }, z.core.$strict>, z.ZodObject<{
44
+ campaign_id: z.ZodNumber;
45
+ campaign_creator_selection: z.ZodObject<{
46
+ workflow_filter: z.ZodOptional<z.ZodEnum<{
47
+ all: "all";
48
+ email: "email";
49
+ email_not_sent: "email_not_sent";
50
+ email_sent: "email_sent";
51
+ replied: "replied";
52
+ negotiating: "negotiating";
53
+ rejected_hold: "rejected_hold";
54
+ rejected: "rejected";
55
+ hold: "hold";
56
+ contract: "contract";
57
+ contracted: "contracted";
58
+ content: "content";
59
+ draft: "draft";
60
+ final_approved: "final_approved";
61
+ posted: "posted";
62
+ }>>;
63
+ outreach_stage: z.ZodOptional<z.ZodString>;
64
+ platform: z.ZodOptional<z.ZodEnum<{
65
+ tiktok: "tiktok";
66
+ instagram: "instagram";
67
+ }>>;
68
+ country: z.ZodOptional<z.ZodString>;
69
+ has_video: z.ZodOptional<z.ZodBoolean>;
70
+ source_type: z.ZodOptional<z.ZodEnum<{
71
+ saved_list: "saved_list";
72
+ file_upload: "file_upload";
73
+ manual_add: "manual_add";
74
+ form_submission: "form_submission";
75
+ discovery_search: "discovery_search";
76
+ }>>;
77
+ search: z.ZodOptional<z.ZodString>;
78
+ excluded_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
79
+ }, z.core.$strict>;
80
+ }, z.core.$strict>]>;
81
+ export declare const CheckBulkEmailReadinessDiscoveryInputSchema: z.ZodObject<{
82
+ campaign_id: z.ZodNumber;
83
+ recipient_target: z.ZodUnion<readonly [z.ZodObject<{
84
+ type: z.ZodLiteral<"ids">;
85
+ campaign_creator_ids: z.ZodArray<z.ZodNumber>;
86
+ }, z.core.$strict>, z.ZodObject<{
87
+ type: z.ZodLiteral<"selection">;
88
+ campaign_creator_selection: z.ZodObject<{
89
+ workflow_filter: z.ZodOptional<z.ZodEnum<{
90
+ all: "all";
91
+ email: "email";
92
+ email_not_sent: "email_not_sent";
93
+ email_sent: "email_sent";
94
+ replied: "replied";
95
+ negotiating: "negotiating";
96
+ rejected_hold: "rejected_hold";
97
+ rejected: "rejected";
98
+ hold: "hold";
99
+ contract: "contract";
100
+ contracted: "contracted";
101
+ content: "content";
102
+ draft: "draft";
103
+ final_approved: "final_approved";
104
+ posted: "posted";
105
+ }>>;
106
+ outreach_stage: z.ZodOptional<z.ZodString>;
107
+ platform: z.ZodOptional<z.ZodEnum<{
108
+ tiktok: "tiktok";
109
+ instagram: "instagram";
110
+ }>>;
111
+ country: z.ZodOptional<z.ZodString>;
112
+ has_video: z.ZodOptional<z.ZodBoolean>;
113
+ source_type: z.ZodOptional<z.ZodEnum<{
114
+ saved_list: "saved_list";
115
+ file_upload: "file_upload";
116
+ manual_add: "manual_add";
117
+ form_submission: "form_submission";
118
+ discovery_search: "discovery_search";
119
+ }>>;
120
+ search: z.ZodOptional<z.ZodString>;
121
+ excluded_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
122
+ }, z.core.$strict>;
123
+ }, z.core.$strict>]>;
124
+ }, z.core.$strict>;
125
+ export declare const StartBulkEmailInputSchema: z.ZodUnion<readonly [z.ZodObject<{
126
+ campaign_id: z.ZodNumber;
127
+ subject: z.ZodString;
128
+ body_text: z.ZodString;
129
+ from_email: z.ZodOptional<z.ZodString>;
130
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString>>;
131
+ form_id: z.ZodOptional<z.ZodNumber>;
132
+ form_language: z.ZodOptional<z.ZodEnum<{
133
+ en: "en";
134
+ ko: "ko";
135
+ jp: "jp";
136
+ }>>;
137
+ idempotency_key: z.ZodString;
138
+ send_confirmation: z.ZodLiteral<"user_authorized">;
139
+ campaign_creator_ids: z.ZodArray<z.ZodNumber>;
140
+ }, z.core.$strict>, z.ZodObject<{
141
+ campaign_id: z.ZodNumber;
142
+ subject: z.ZodString;
143
+ body_text: z.ZodString;
144
+ from_email: z.ZodOptional<z.ZodString>;
145
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString>>;
146
+ form_id: z.ZodOptional<z.ZodNumber>;
147
+ form_language: z.ZodOptional<z.ZodEnum<{
148
+ en: "en";
149
+ ko: "ko";
150
+ jp: "jp";
151
+ }>>;
152
+ idempotency_key: z.ZodString;
153
+ send_confirmation: z.ZodLiteral<"user_authorized">;
154
+ campaign_creator_selection: z.ZodObject<{
155
+ workflow_filter: z.ZodOptional<z.ZodEnum<{
156
+ all: "all";
157
+ email: "email";
158
+ email_not_sent: "email_not_sent";
159
+ email_sent: "email_sent";
160
+ replied: "replied";
161
+ negotiating: "negotiating";
162
+ rejected_hold: "rejected_hold";
163
+ rejected: "rejected";
164
+ hold: "hold";
165
+ contract: "contract";
166
+ contracted: "contracted";
167
+ content: "content";
168
+ draft: "draft";
169
+ final_approved: "final_approved";
170
+ posted: "posted";
171
+ }>>;
172
+ outreach_stage: z.ZodOptional<z.ZodString>;
173
+ platform: z.ZodOptional<z.ZodEnum<{
174
+ tiktok: "tiktok";
175
+ instagram: "instagram";
176
+ }>>;
177
+ country: z.ZodOptional<z.ZodString>;
178
+ has_video: z.ZodOptional<z.ZodBoolean>;
179
+ source_type: z.ZodOptional<z.ZodEnum<{
180
+ saved_list: "saved_list";
181
+ file_upload: "file_upload";
182
+ manual_add: "manual_add";
183
+ form_submission: "form_submission";
184
+ discovery_search: "discovery_search";
185
+ }>>;
186
+ search: z.ZodOptional<z.ZodString>;
187
+ excluded_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
188
+ }, z.core.$strict>;
189
+ }, z.core.$strict>]>;
190
+ export declare const StartBulkEmailDiscoveryInputSchema: z.ZodObject<{
191
+ campaign_id: z.ZodNumber;
192
+ subject: z.ZodString;
193
+ body_text: z.ZodString;
194
+ from_email: z.ZodOptional<z.ZodString>;
195
+ attachments: z.ZodOptional<z.ZodArray<z.ZodString>>;
196
+ form_id: z.ZodOptional<z.ZodNumber>;
197
+ form_language: z.ZodOptional<z.ZodEnum<{
198
+ en: "en";
199
+ ko: "ko";
200
+ jp: "jp";
201
+ }>>;
202
+ idempotency_key: z.ZodString;
203
+ send_confirmation: z.ZodLiteral<"user_authorized">;
204
+ recipient_target: z.ZodUnion<readonly [z.ZodObject<{
205
+ type: z.ZodLiteral<"ids">;
206
+ campaign_creator_ids: z.ZodArray<z.ZodNumber>;
207
+ }, z.core.$strict>, z.ZodObject<{
208
+ type: z.ZodLiteral<"selection">;
209
+ campaign_creator_selection: z.ZodObject<{
210
+ workflow_filter: z.ZodOptional<z.ZodEnum<{
211
+ all: "all";
212
+ email: "email";
213
+ email_not_sent: "email_not_sent";
214
+ email_sent: "email_sent";
215
+ replied: "replied";
216
+ negotiating: "negotiating";
217
+ rejected_hold: "rejected_hold";
218
+ rejected: "rejected";
219
+ hold: "hold";
220
+ contract: "contract";
221
+ contracted: "contracted";
222
+ content: "content";
223
+ draft: "draft";
224
+ final_approved: "final_approved";
225
+ posted: "posted";
226
+ }>>;
227
+ outreach_stage: z.ZodOptional<z.ZodString>;
228
+ platform: z.ZodOptional<z.ZodEnum<{
229
+ tiktok: "tiktok";
230
+ instagram: "instagram";
231
+ }>>;
232
+ country: z.ZodOptional<z.ZodString>;
233
+ has_video: z.ZodOptional<z.ZodBoolean>;
234
+ source_type: z.ZodOptional<z.ZodEnum<{
235
+ saved_list: "saved_list";
236
+ file_upload: "file_upload";
237
+ manual_add: "manual_add";
238
+ form_submission: "form_submission";
239
+ discovery_search: "discovery_search";
240
+ }>>;
241
+ search: z.ZodOptional<z.ZodString>;
242
+ excluded_ids: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
243
+ }, z.core.$strict>;
244
+ }, z.core.$strict>]>;
245
+ }, z.core.$strict>;
246
+ export type CheckBulkEmailReadinessInput = z.infer<typeof CheckBulkEmailReadinessInputSchema>;
247
+ export type StartBulkEmailInput = z.infer<typeof StartBulkEmailInputSchema>;
248
+ type CheckBulkEmailReadinessDiscoveryInput = z.infer<typeof CheckBulkEmailReadinessDiscoveryInputSchema>;
249
+ type StartBulkEmailDiscoveryInput = z.infer<typeof StartBulkEmailDiscoveryInputSchema>;
250
+ /** Convert the MCP-discoverable recipient target into the Product API shape. */
251
+ export declare function normalizeCheckBulkEmailReadinessInput(input: CheckBulkEmailReadinessDiscoveryInput): CheckBulkEmailReadinessInput;
252
+ /** Convert the MCP-discoverable send target into the Product API shape. */
253
+ export declare function normalizeStartBulkEmailInput(input: StartBulkEmailDiscoveryInput): StartBulkEmailInput;
254
+ export {};
@@ -0,0 +1,168 @@
1
+ import { z } from "zod";
2
+ export const EmailSubjectSchema = z.string().trim().min(1).max(500);
3
+ const HeaderValueSchema = z
4
+ .string()
5
+ .trim()
6
+ .regex(/^[\x21-\x7e]+$/, {
7
+ message: "value contains unsupported header characters",
8
+ });
9
+ export const IdempotencyKeySchema = HeaderValueSchema.min(1).max(128);
10
+ const SendConfirmationSchema = z.literal("user_authorized");
11
+ const CampaignCreatorWorkflowFilterSchema = z.enum([
12
+ "all",
13
+ "email",
14
+ "email_not_sent",
15
+ "email_sent",
16
+ "replied",
17
+ "negotiating",
18
+ "rejected_hold",
19
+ "rejected",
20
+ "hold",
21
+ "contract",
22
+ "contracted",
23
+ "content",
24
+ "draft",
25
+ "final_approved",
26
+ "posted",
27
+ ]);
28
+ const PositiveIdListSchema = z.array(z.number().int().positive()).min(1);
29
+ const CampaignCreatorSelectionSchema = z
30
+ .object({
31
+ workflow_filter: CampaignCreatorWorkflowFilterSchema.optional(),
32
+ outreach_stage: z.string().trim().min(1).optional(),
33
+ platform: z.enum(["tiktok", "instagram"]).optional(),
34
+ country: z.string().trim().min(1).optional(),
35
+ has_video: z.boolean().optional(),
36
+ source_type: z
37
+ .enum([
38
+ "saved_list",
39
+ "file_upload",
40
+ "manual_add",
41
+ "form_submission",
42
+ "discovery_search",
43
+ ])
44
+ .optional(),
45
+ search: z.string().trim().min(1).optional(),
46
+ excluded_ids: z.array(z.number().int().positive()).optional(),
47
+ })
48
+ .strict()
49
+ .superRefine((value, context) => {
50
+ const hasNarrowingFilter = (value.workflow_filter !== undefined &&
51
+ value.workflow_filter !== "all") ||
52
+ value.outreach_stage !== undefined ||
53
+ value.platform !== undefined ||
54
+ value.country !== undefined ||
55
+ value.has_video !== undefined ||
56
+ value.source_type !== undefined ||
57
+ value.search !== undefined;
58
+ if (!hasNarrowingFilter) {
59
+ context.addIssue({
60
+ code: "custom",
61
+ message: "campaign_creator_selection must include a narrowing filter; use campaign_creator_ids for exact rows",
62
+ });
63
+ }
64
+ });
65
+ const CampaignRecipientIdsTargetSchema = z
66
+ .object({
67
+ campaign_creator_ids: PositiveIdListSchema,
68
+ })
69
+ .strict();
70
+ const CampaignRecipientSelectionTargetSchema = z
71
+ .object({
72
+ campaign_creator_selection: CampaignCreatorSelectionSchema,
73
+ })
74
+ .strict();
75
+ const CampaignRecipientTargetSchema = z.union([
76
+ z
77
+ .object({
78
+ type: z.literal("ids"),
79
+ campaign_creator_ids: PositiveIdListSchema.describe("Exact campaign creator row IDs selected from list_campaign_creators."),
80
+ })
81
+ .strict(),
82
+ z
83
+ .object({
84
+ type: z.literal("selection"),
85
+ campaign_creator_selection: CampaignCreatorSelectionSchema.describe("Narrowed campaign roster selection, such as workflow_filter: email_not_sent."),
86
+ })
87
+ .strict(),
88
+ ]);
89
+ function validateFormLanguage(value, context) {
90
+ if (value.form_language !== undefined && value.form_id === undefined) {
91
+ context.addIssue({
92
+ code: "custom",
93
+ message: "form_id is required when form_language is provided",
94
+ path: ["form_id"],
95
+ });
96
+ }
97
+ }
98
+ const CheckBulkEmailReadinessBaseSchema = z.object({
99
+ campaign_id: z.number().int().positive(),
100
+ });
101
+ export const CheckBulkEmailReadinessInputSchema = z.union([
102
+ CheckBulkEmailReadinessBaseSchema.merge(CampaignRecipientIdsTargetSchema),
103
+ CheckBulkEmailReadinessBaseSchema.merge(CampaignRecipientSelectionTargetSchema),
104
+ ]);
105
+ export const CheckBulkEmailReadinessDiscoveryInputSchema = CheckBulkEmailReadinessBaseSchema.extend({
106
+ recipient_target: CampaignRecipientTargetSchema,
107
+ }).strict();
108
+ const StartBulkEmailBaseSchema = z
109
+ .object({
110
+ campaign_id: z.number().int().positive(),
111
+ subject: EmailSubjectSchema,
112
+ body_text: z.string().trim().min(1),
113
+ from_email: z.string().trim().min(1).optional(),
114
+ attachments: z.array(z.string().trim().min(1)).optional(),
115
+ form_id: z.number().int().positive().optional(),
116
+ form_language: z.enum(["en", "ko", "jp"]).optional(),
117
+ idempotency_key: IdempotencyKeySchema,
118
+ send_confirmation: SendConfirmationSchema,
119
+ })
120
+ .strict();
121
+ export const StartBulkEmailInputSchema = z
122
+ .union([
123
+ StartBulkEmailBaseSchema.merge(CampaignRecipientIdsTargetSchema),
124
+ StartBulkEmailBaseSchema.merge(CampaignRecipientSelectionTargetSchema),
125
+ ])
126
+ .superRefine(validateFormLanguage);
127
+ export const StartBulkEmailDiscoveryInputSchema = StartBulkEmailBaseSchema.extend({
128
+ recipient_target: CampaignRecipientTargetSchema,
129
+ })
130
+ .strict()
131
+ .superRefine(validateFormLanguage);
132
+ /** Convert the MCP-discoverable recipient target into the Product API shape. */
133
+ export function normalizeCheckBulkEmailReadinessInput(input) {
134
+ if (input.recipient_target.type === "ids") {
135
+ return {
136
+ campaign_id: input.campaign_id,
137
+ campaign_creator_ids: input.recipient_target.campaign_creator_ids,
138
+ };
139
+ }
140
+ return {
141
+ campaign_id: input.campaign_id,
142
+ campaign_creator_selection: input.recipient_target.campaign_creator_selection,
143
+ };
144
+ }
145
+ /** Convert the MCP-discoverable send target into the Product API shape. */
146
+ export function normalizeStartBulkEmailInput(input) {
147
+ const base = {
148
+ campaign_id: input.campaign_id,
149
+ subject: input.subject,
150
+ body_text: input.body_text,
151
+ from_email: input.from_email,
152
+ attachments: input.attachments,
153
+ form_id: input.form_id,
154
+ form_language: input.form_language,
155
+ idempotency_key: input.idempotency_key,
156
+ send_confirmation: input.send_confirmation,
157
+ };
158
+ if (input.recipient_target.type === "ids") {
159
+ return {
160
+ ...base,
161
+ campaign_creator_ids: input.recipient_target.campaign_creator_ids,
162
+ };
163
+ }
164
+ return {
165
+ ...base,
166
+ campaign_creator_selection: input.recipient_target.campaign_creator_selection,
167
+ };
168
+ }
@@ -0,0 +1,6 @@
1
+ import type { JsonObject } from "./http.js";
2
+ import type { CheckBulkEmailReadinessInput, StartBulkEmailInput } from "./tool-inputs.js";
3
+ /** Build the campaign bulk-email readiness request body. */
4
+ export declare function readinessRequestBody(input: CheckBulkEmailReadinessInput): JsonObject;
5
+ /** Build the campaign bulk-email job request body. */
6
+ export declare function startBulkEmailRequestBody(input: StartBulkEmailInput): JsonObject;
@@ -0,0 +1,37 @@
1
+ import { campaignCreatorSelectionBody } from "./campaign-creator-selection.js";
2
+ import { compactJsonObject } from "./tool-http-helpers.js";
3
+ /** Build the campaign bulk-email readiness request body. */
4
+ export function readinessRequestBody(input) {
5
+ return campaignRecipientTargetBody(input);
6
+ }
7
+ /** Build the campaign bulk-email job request body. */
8
+ export function startBulkEmailRequestBody(input) {
9
+ return compactJsonObject([
10
+ ["subject", input.subject],
11
+ ["body_text", input.body_text],
12
+ ["from_email", input.from_email],
13
+ ["attachments", input.attachments],
14
+ ...campaignRecipientTargetEntries(input),
15
+ ["form_id", input.form_id],
16
+ ["form_language", input.form_language],
17
+ ["idempotency_key", input.idempotency_key],
18
+ ["send_confirmation", input.send_confirmation],
19
+ ]);
20
+ }
21
+ function campaignRecipientTargetBody(input) {
22
+ return compactJsonObject(campaignRecipientTargetEntries(input));
23
+ }
24
+ function campaignRecipientTargetEntries(input) {
25
+ return [
26
+ [
27
+ "campaign_creator_ids",
28
+ "campaign_creator_ids" in input ? input.campaign_creator_ids : undefined,
29
+ ],
30
+ [
31
+ "campaign_creator_selection",
32
+ campaignCreatorSelectionBody("campaign_creator_selection" in input
33
+ ? input.campaign_creator_selection
34
+ : undefined),
35
+ ],
36
+ ];
37
+ }
@@ -15,4 +15,3 @@ export function campaignCreatorSelectionBody(selection) {
15
15
  ["excluded_ids", selection.excluded_ids],
16
16
  ]);
17
17
  }
18
- //# sourceMappingURL=campaign-creator-selection.js.map
package/dist/cli.js CHANGED
@@ -98,8 +98,9 @@ export async function runCli(argv = process.argv.slice(2), dependencies = {}) {
98
98
  }
99
99
  /** Parse CLI arguments using Node's standard parser. */
100
100
  function parseCommand(argv) {
101
+ const args = argv[0] === "--" ? argv.slice(1) : argv;
101
102
  const parsed = parseArgs({
102
- args: [...argv],
103
+ args: [...args],
103
104
  allowPositionals: true,
104
105
  options: {
105
106
  json: { type: "boolean", default: false },
@@ -320,4 +321,3 @@ if (isDirectCliInvocation(import.meta.url, process.argv[1])) {
320
321
  process.exitCode = exitCode;
321
322
  });
322
323
  }
323
- //# sourceMappingURL=cli.js.map