@hyperstar/mcp 0.1.6 → 0.1.10

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 (55) hide show
  1. package/README.md +84 -24
  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 -6
  13. package/dist/config.js +15 -2
  14. package/dist/discovery.d.ts +6 -0
  15. package/dist/discovery.js +62 -0
  16. package/dist/http.js +0 -1
  17. package/dist/index.js +0 -1
  18. package/dist/package-metadata.d.ts +2 -0
  19. package/dist/package-metadata.js +12 -0
  20. package/dist/schemas.js +0 -1
  21. package/dist/search-result-projection.d.ts +3 -1
  22. package/dist/search-result-projection.js +33 -3
  23. package/dist/server.js +7 -2
  24. package/dist/tool-descriptions.d.ts +19 -0
  25. package/dist/tool-descriptions.js +19 -0
  26. package/dist/tool-guidance.d.ts +10 -0
  27. package/dist/tool-guidance.js +82 -0
  28. package/dist/tool-http-helpers.js +0 -1
  29. package/dist/tool-inputs.d.ts +324 -95
  30. package/dist/tool-inputs.js +87 -92
  31. package/dist/tool-result.js +0 -1
  32. package/dist/tools.js +34 -52
  33. package/dist/workflow-content.d.ts +43 -0
  34. package/dist/workflow-content.js +153 -0
  35. package/dist/workspaces.d.ts +1 -1
  36. package/dist/workspaces.js +12 -45
  37. package/package.json +25 -2
  38. package/dist/auth/cli-auth-client.js.map +0 -1
  39. package/dist/auth/cli-auth-state.js.map +0 -1
  40. package/dist/auth/local-callback.js.map +0 -1
  41. package/dist/auth/pkce.js.map +0 -1
  42. package/dist/bulk-email-guidance.js.map +0 -1
  43. package/dist/campaign-creator-selection.js.map +0 -1
  44. package/dist/cli.js.map +0 -1
  45. package/dist/config.js.map +0 -1
  46. package/dist/http.js.map +0 -1
  47. package/dist/index.js.map +0 -1
  48. package/dist/schemas.js.map +0 -1
  49. package/dist/search-result-projection.js.map +0 -1
  50. package/dist/server.js.map +0 -1
  51. package/dist/tool-http-helpers.js.map +0 -1
  52. package/dist/tool-inputs.js.map +0 -1
  53. package/dist/tool-result.js.map +0 -1
  54. package/dist/tools.js.map +0 -1
  55. 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
@@ -22,6 +22,13 @@ Because the package exposes multiple binaries, use the explicit
22
22
  `--package @hyperstar/mcp hyperstar-mcp` form instead of bare
23
23
  `npx -y @hyperstar/mcp` in MCP client configs.
24
24
 
25
+ If a client spawns through an environment that loses npm's temporary `.bin`
26
+ path, use the equivalent `npm exec` form:
27
+
28
+ ```sh
29
+ npm exec --yes --package @hyperstar/mcp -- hyperstar-mcp
30
+ ```
31
+
25
32
  For a one-off browser login without a global install:
26
33
 
27
34
  ```sh
@@ -30,16 +37,16 @@ npx -y --package @hyperstar/mcp hyperstar workspaces list --json
30
37
  npx -y --package @hyperstar/mcp hyperstar workspaces use org_123
31
38
  ```
32
39
 
33
- 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`:
34
42
 
35
43
  ```sh
36
44
  cd projects/hyperstar-mcp
37
- npm pack --dry-run
38
- npm pack
39
- npx -y --package ./hyperstar-mcp-0.1.0.tgz hyperstar-mcp
40
- npx -y --package ./hyperstar-mcp-0.1.0.tgz hyperstar login
41
- npx -y --package ./hyperstar-mcp-0.1.0.tgz hyperstar workspaces list --json
42
- 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
43
50
  ```
44
51
 
45
52
  The first public scoped publish must use public access. This package sets
@@ -48,8 +55,9 @@ credentials are configured.
48
55
 
49
56
  ## MCP client config
50
57
 
51
- Service-account auth is the simplest non-interactive setup. Configure the MCP
52
- 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:
53
61
 
54
62
  ```json
55
63
  {
@@ -58,7 +66,6 @@ server with a Hyperstar Product API base URL and service-account API key:
58
66
  "command": "npx",
59
67
  "args": ["-y", "--package", "@hyperstar/mcp", "hyperstar-mcp"],
60
68
  "env": {
61
- "HYPERSTAR_API_BASE_URL": "https://api.hyperstar.example",
62
69
  "HYPERSTAR_API_KEY": "hstar_test_public-prefix.secret"
63
70
  }
64
71
  }
@@ -78,11 +85,7 @@ login state created by the `hyperstar` CLI:
78
85
  "mcpServers": {
79
86
  "hyperstar": {
80
87
  "command": "npx",
81
- "args": ["-y", "--package", "@hyperstar/mcp", "hyperstar-mcp"],
82
- "env": {
83
- "HYPERSTAR_API_BASE_URL": "https://autopilot.dev.hyper-star.org",
84
- "HYPERSTAR_APP_BASE_URL": "https://app.dev.hyper-star.org"
85
- }
88
+ "args": ["-y", "--package", "@hyperstar/mcp", "hyperstar-mcp"]
86
89
  }
87
90
  }
88
91
  }
@@ -94,13 +97,15 @@ a workspace with `hyperstar workspaces use <organization_id>` or use the MCP
94
97
  `list_workspaces` and `select_workspace` tools. Service-account mode is already
95
98
  workspace-scoped, so `get_hyperstar_workflow_guide` omits `select_workspace`
96
99
  when `HYPERSTAR_API_KEY` is used. For non-production login, set matching
97
- `HYPERSTAR_API_BASE_URL` and `HYPERSTAR_APP_BASE_URL`; the CLI maps the standard
98
- dev API origin to the dev app automatically, while custom API origins must
99
- provide an explicit app base URL. `HYPERSTAR_API_KEY` takes precedence over local
100
- CLI auth when both are present.
100
+ `HYPERSTAR_API_BASE_URL` and `HYPERSTAR_APP_BASE_URL` explicitly.
101
+ `HYPERSTAR_API_KEY` takes precedence over local CLI auth when both are present.
101
102
 
102
103
  ## Tools
103
104
 
105
+ Agents should start by reading `get_hyperstar_workflow_guide` or the MCP
106
+ resources/prompts listed below. Tool responses include `agent_guidance` and,
107
+ where the next step is deterministic, `next_tool` and `next_arguments`.
108
+
104
109
  - `hyperstar_whoami`
105
110
  - `list_workspaces`
106
111
  - `select_workspace`
@@ -119,6 +124,22 @@ CLI auth when both are present.
119
124
  - `update_inbox_thread_state`
120
125
  - `send_inbox_reply`
121
126
 
127
+ ## Resources and prompts
128
+
129
+ The server exposes static workflow guides as MCP resources:
130
+
131
+ - `hyperstar://guide/headless-workflow`
132
+ - `hyperstar://guide/search-to-campaign`
133
+ - `hyperstar://guide/bulk-email-safety`
134
+ - `hyperstar://guide/inbox`
135
+
136
+ It also exposes matching prompts:
137
+
138
+ - `hyperstar_headless_workflow`
139
+ - `hyperstar_search_to_campaign`
140
+ - `hyperstar_bulk_email_safety`
141
+ - `hyperstar_inbox_workflow`
142
+
122
143
  ## Development
123
144
 
124
145
  ```sh
@@ -144,11 +165,23 @@ Search creators:
144
165
  }
145
166
  ```
146
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
+
147
178
  Search tools return compact creator summaries by default so agents do not load
148
179
  full profile payloads into context. Use `get_search_results` with
149
180
  `detail_level: "full"` only when a page of raw creator rows is explicitly
150
181
  needed. Bulk actions such as `save_search_results_to_campaign` use `search_id`
151
- server-side and do not require the agent to materialize every creator.
182
+ server-side and do not require the agent to materialize every creator. The
183
+ search response includes `agent_guidance` and `next_tools` so an unfamiliar
184
+ agent can continue without knowing the UI flow.
152
185
 
153
186
  List or create a campaign before saving creators:
154
187
 
@@ -186,9 +219,32 @@ Save search results to a campaign roster:
186
219
  }
187
220
  ```
188
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
+
189
244
  Start a bulk email job. This performs a real send. The response includes the
190
245
  queued job fields plus `next_tool: "get_bulk_email_job"` and `next_arguments`
191
- 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:
192
248
 
193
249
  ```json
194
250
  {
@@ -197,8 +253,12 @@ so agents know how to check progress:
197
253
  "campaign_id": 123,
198
254
  "subject": "Partnership idea",
199
255
  "body_text": "Hi, we would like to introduce our new campaign.",
200
- "campaign_creator_ids": [7, 8],
201
- "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"
202
262
  }
203
263
  }
204
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;