@hyperstar/mcp 0.1.12 → 0.1.14

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.
package/README.md CHANGED
@@ -5,6 +5,9 @@ workflows. It exposes scoped creator search, campaign roster, bulk email, and
5
5
  inbox tools for MCP clients using either a service-account key or local browser
6
6
  CLI login.
7
7
 
8
+ For first-tester setup, MCP client snippets, and safe starter prompts, use the
9
+ [headless agent quickstart](https://github.com/Hyperstar-org/backend/blob/main/docs/headless-agent-quickstart.md).
10
+
8
11
  ## Install
9
12
 
10
13
  Run the MCP server directly from npm:
@@ -120,6 +123,7 @@ where the next step is deterministic, `next_tool` and `next_arguments`.
120
123
  - `start_bulk_email`
121
124
  - `get_bulk_email_job`
122
125
  - `list_inbox_threads`
126
+ - `get_inbox_thread_messages`
123
127
  - `get_inbox_aggregates`
124
128
  - `update_inbox_thread_state`
125
129
  - `send_inbox_reply`
@@ -146,8 +150,13 @@ It also exposes matching prompts:
146
150
  npm install
147
151
  npm test
148
152
  npm run build
153
+ npm run smoke:npm
149
154
  ```
150
155
 
156
+ `npm run smoke:npm` verifies the published npm package can be resolved and that
157
+ both package binaries print help without requiring local build artifacts,
158
+ authentication, or real workflow API calls.
159
+
151
160
  ## Examples
152
161
 
153
162
  Search creators:
@@ -263,7 +272,21 @@ and the user has authorized the send:
263
272
  }
264
273
  ```
265
274
 
266
- Reply to an inbox thread. This performs a real send:
275
+ Read a full inbox thread before replying:
276
+
277
+ ```json
278
+ {
279
+ "tool": "get_inbox_thread_messages",
280
+ "arguments": {
281
+ "platform": "tiktok",
282
+ "thread_id": 456
283
+ }
284
+ }
285
+ ```
286
+
287
+ Reply to an inbox thread. This performs a real send. Only call this after the
288
+ user has authorized the reply and the agent has reviewed the full message
289
+ history:
267
290
 
268
291
  ```json
269
292
  {
@@ -273,7 +296,8 @@ Reply to an inbox thread. This performs a real send:
273
296
  "thread_id": 456,
274
297
  "subject": "Re: Partnership idea",
275
298
  "body_text": "Thanks for the reply. Here are the next details.",
276
- "idempotency_key": "thread-456-reply-2026-07-09"
299
+ "idempotency_key": "thread-456-reply-2026-07-09",
300
+ "send_confirmation": "user_authorized"
277
301
  }
278
302
  }
279
303
  ```
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type HyperstarToolHandlers } from "./tool-inputs.js";
3
+ /** Register inbox-focused Hyperstar MCP tools. */
4
+ export declare function registerInboxTools(server: McpServer, handlers: HyperstarToolHandlers): void;
@@ -0,0 +1,31 @@
1
+ import { TOOL_DESCRIPTIONS } from "./tool-descriptions.js";
2
+ import { GetInboxThreadMessagesInputSchema, InboxFiltersInputSchema, ListInboxThreadsInputSchema, SendInboxReplyInputSchema, UpdateInboxThreadStateInputSchema, } from "./tool-inputs.js";
3
+ import { toolResult } from "./tool-result.js";
4
+ /** Register inbox-focused Hyperstar MCP tools. */
5
+ export function registerInboxTools(server, handlers) {
6
+ server.registerTool("list_inbox_threads", {
7
+ title: "List Inbox Threads",
8
+ description: TOOL_DESCRIPTIONS.listInboxThreads,
9
+ inputSchema: ListInboxThreadsInputSchema,
10
+ }, async (input) => toolResult(await handlers.listInboxThreads(input)));
11
+ server.registerTool("get_inbox_thread_messages", {
12
+ title: "Get Inbox Thread Messages",
13
+ description: TOOL_DESCRIPTIONS.getInboxThreadMessages,
14
+ inputSchema: GetInboxThreadMessagesInputSchema,
15
+ }, async (input) => toolResult(await handlers.getInboxThreadMessages(input)));
16
+ server.registerTool("get_inbox_aggregates", {
17
+ title: "Get Inbox Aggregates",
18
+ description: TOOL_DESCRIPTIONS.getInboxAggregates,
19
+ inputSchema: InboxFiltersInputSchema,
20
+ }, async (input) => toolResult(await handlers.getInboxAggregates(input)));
21
+ server.registerTool("update_inbox_thread_state", {
22
+ title: "Update Inbox Thread State",
23
+ description: TOOL_DESCRIPTIONS.updateInboxThreadState,
24
+ inputSchema: UpdateInboxThreadStateInputSchema,
25
+ }, async (input) => toolResult(await handlers.updateInboxThreadState(input)));
26
+ server.registerTool("send_inbox_reply", {
27
+ title: "Send Inbox Reply",
28
+ description: TOOL_DESCRIPTIONS.sendInboxReply,
29
+ inputSchema: SendInboxReplyInputSchema,
30
+ }, async (input) => toolResult(await handlers.sendInboxReply(input)));
31
+ }
@@ -12,8 +12,9 @@ export declare const TOOL_DESCRIPTIONS: {
12
12
  readonly checkBulkEmailReadiness: "The final dry-run gate before bulk email. Requires recipient_target with exact IDs or a narrowed selection, checks those selected recipients, and returns sendability guidance.";
13
13
  readonly startBulkEmail: "REAL SEND: start a campaign bulk-email job after readiness has passed and the user authorized sending. Requires recipient_target, an explicit idempotency key, and send_confirmation: \"user_authorized\".";
14
14
  readonly getBulkEmailJob: "Read campaign bulk-email job status after start_bulk_email returns a job_id.";
15
- readonly listInboxThreads: "List Hyperstar inbox threads with filters for triage before reading or replying.";
15
+ readonly listInboxThreads: "List Hyperstar inbox thread summaries with filters for triage. Call get_inbox_thread_messages before drafting replies.";
16
+ readonly getInboxThreadMessages: "Read full message history for one Hyperstar inbox thread before deciding whether to reply.";
16
17
  readonly getInboxAggregates: "Read aggregate counts for Hyperstar inbox filters to summarize inbox state.";
17
18
  readonly updateInboxThreadState: "Archive, snooze, star, or set read state for one inbox thread without sending a message.";
18
- readonly sendInboxReply: "REAL SEND: send one user-authorized reply to a Hyperstar inbox thread with an explicit idempotency key.";
19
+ readonly sendInboxReply: "REAL SEND: send one user-authorized reply to a Hyperstar inbox thread with an explicit idempotency key and send_confirmation: \"user_authorized\".";
19
20
  };
@@ -12,8 +12,9 @@ export const TOOL_DESCRIPTIONS = {
12
12
  checkBulkEmailReadiness: "The final dry-run gate before bulk email. Requires recipient_target with exact IDs or a narrowed selection, checks those selected recipients, and returns sendability guidance.",
13
13
  startBulkEmail: 'REAL SEND: start a campaign bulk-email job after readiness has passed and the user authorized sending. Requires recipient_target, an explicit idempotency key, and send_confirmation: "user_authorized".',
14
14
  getBulkEmailJob: "Read campaign bulk-email job status after start_bulk_email returns a job_id.",
15
- listInboxThreads: "List Hyperstar inbox threads with filters for triage before reading or replying.",
15
+ listInboxThreads: "List Hyperstar inbox thread summaries with filters for triage. Call get_inbox_thread_messages before drafting replies.",
16
+ getInboxThreadMessages: "Read full message history for one Hyperstar inbox thread before deciding whether to reply.",
16
17
  getInboxAggregates: "Read aggregate counts for Hyperstar inbox filters to summarize inbox state.",
17
18
  updateInboxThreadState: "Archive, snooze, star, or set read state for one inbox thread without sending a message.",
18
- sendInboxReply: "REAL SEND: send one user-authorized reply to a Hyperstar inbox thread with an explicit idempotency key.",
19
+ sendInboxReply: 'REAL SEND: send one user-authorized reply to a Hyperstar inbox thread with an explicit idempotency key and send_confirmation: "user_authorized".',
19
20
  };
@@ -10,3 +10,5 @@ export declare function campaignRosterGuidance(payload: JsonObject, campaignId:
10
10
  export declare function readinessGuidance(payload: JsonObject, input: CheckBulkEmailReadinessInput): JsonObject;
11
11
  /** Add deterministic next-step guidance after listing inbox threads. */
12
12
  export declare function inboxThreadListGuidance(payload: JsonObject): JsonObject;
13
+ /** Add deterministic next-step guidance after reading full inbox messages. */
14
+ export declare function inboxThreadMessagesGuidance(payload: JsonObject): JsonObject;
@@ -68,11 +68,19 @@ export function inboxThreadListGuidance(payload) {
68
68
  return {
69
69
  ...payload,
70
70
  next_tools: [
71
+ "get_inbox_thread_messages",
71
72
  "get_inbox_aggregates",
72
73
  "update_inbox_thread_state",
73
- "send_inbox_reply",
74
74
  ],
75
- agent_guidance: "Use get_inbox_aggregates for counts, update_inbox_thread_state for archive/star/snooze/read-state changes, and send_inbox_reply only after explicit user authorization because send_inbox_reply performs a real send.",
75
+ agent_guidance: "Use get_inbox_thread_messages to read full message history before replying, get_inbox_aggregates for counts, update_inbox_thread_state for archive/star/snooze/read-state changes, and send_inbox_reply only after explicit user authorization because send_inbox_reply performs a real send.",
76
+ };
77
+ }
78
+ /** Add deterministic next-step guidance after reading full inbox messages. */
79
+ export function inboxThreadMessagesGuidance(payload) {
80
+ return {
81
+ ...payload,
82
+ next_tools: ["send_inbox_reply", "update_inbox_thread_state"],
83
+ agent_guidance: 'Review the full message history before drafting. send_inbox_reply performs a real send and requires send_confirmation: "user_authorized".',
76
84
  };
77
85
  }
78
86
  function recipientTargetArgument(ids, selection) {
@@ -465,6 +465,14 @@ export declare const UpdateInboxThreadStateInputSchema: z.ZodObject<{
465
465
  }>>;
466
466
  clear_read_state: z.ZodOptional<z.ZodBoolean>;
467
467
  }, z.core.$strict>;
468
+ export declare const GetInboxThreadMessagesInputSchema: z.ZodObject<{
469
+ platform: z.ZodEnum<{
470
+ tiktok: "tiktok";
471
+ instagram: "instagram";
472
+ buyer: "buyer";
473
+ }>;
474
+ thread_id: z.ZodNumber;
475
+ }, z.core.$strict>;
468
476
  export declare const SendInboxReplyInputSchema: z.ZodObject<{
469
477
  platform: z.ZodEnum<{
470
478
  tiktok: "tiktok";
@@ -477,6 +485,7 @@ export declare const SendInboxReplyInputSchema: z.ZodObject<{
477
485
  attachments: z.ZodOptional<z.ZodArray<z.ZodString>>;
478
486
  campaign_creator_id: z.ZodOptional<z.ZodNumber>;
479
487
  idempotency_key: z.ZodString;
488
+ send_confirmation: z.ZodLiteral<"user_authorized">;
480
489
  }, z.core.$strict>;
481
490
  type SearchCreatorsInput = z.infer<typeof SearchCreatorsInputSchema>;
482
491
  type ListWorkspacesInput = z.infer<typeof ListWorkspacesInputSchema>;
@@ -491,6 +500,7 @@ type BulkEmailJobInput = z.infer<typeof BulkEmailJobInputSchema>;
491
500
  type InboxFiltersInput = z.infer<typeof InboxFiltersInputSchema>;
492
501
  type ListInboxThreadsInput = z.infer<typeof ListInboxThreadsInputSchema>;
493
502
  type UpdateInboxThreadStateInput = z.infer<typeof UpdateInboxThreadStateInputSchema>;
503
+ type GetInboxThreadMessagesInput = z.infer<typeof GetInboxThreadMessagesInputSchema>;
494
504
  type SendInboxReplyInput = z.infer<typeof SendInboxReplyInputSchema>;
495
505
  export type HyperstarToolHandlers = {
496
506
  readonly hyperstarWhoami: () => Promise<JsonObject>;
@@ -509,5 +519,6 @@ export type HyperstarToolHandlers = {
509
519
  readonly listInboxThreads: (input: ListInboxThreadsInput) => Promise<JsonObject>;
510
520
  readonly getInboxAggregates: (input: InboxFiltersInput) => Promise<JsonObject>;
511
521
  readonly updateInboxThreadState: (input: UpdateInboxThreadStateInput) => Promise<JsonObject>;
522
+ readonly getInboxThreadMessages: (input: GetInboxThreadMessagesInput) => Promise<JsonObject>;
512
523
  readonly sendInboxReply: (input: SendInboxReplyInput) => Promise<JsonObject>;
513
524
  };
@@ -5,6 +5,7 @@ export { CheckBulkEmailReadinessDiscoveryInputSchema, CheckBulkEmailReadinessInp
5
5
  const PositivePageLimitSchema = z.number().int().min(1);
6
6
  const SearchPlatformSchema = z.enum(["tiktok", "instagram"]);
7
7
  const InboxPlatformSchema = z.enum(["tiktok", "instagram", "buyer"]);
8
+ const SendConfirmationSchema = z.literal("user_authorized");
8
9
  const SearchIdSchema = z.uuid();
9
10
  // Backend workspace identifiers are organization IDs such as "org_123", not UUIDs.
10
11
  const WorkspaceOrganizationIdSchema = z.string().trim().min(1);
@@ -246,6 +247,12 @@ export const UpdateInboxThreadStateInputSchema = z
246
247
  clear_read_state: z.boolean().optional(),
247
248
  })
248
249
  .strict();
250
+ export const GetInboxThreadMessagesInputSchema = z
251
+ .object({
252
+ platform: InboxPlatformSchema,
253
+ thread_id: z.number().int().positive(),
254
+ })
255
+ .strict();
249
256
  export const SendInboxReplyInputSchema = z
250
257
  .object({
251
258
  platform: InboxPlatformSchema,
@@ -255,5 +262,6 @@ export const SendInboxReplyInputSchema = z
255
262
  attachments: z.array(z.string().trim().min(1)).optional(),
256
263
  campaign_creator_id: z.number().int().positive().optional(),
257
264
  idempotency_key: IdempotencyKeySchema,
265
+ send_confirmation: SendConfirmationSchema,
258
266
  })
259
267
  .strict();
package/dist/tools.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { bulkEmailQueuedResult } from "./bulk-email-guidance.js";
2
2
  import { readinessRequestBody, startBulkEmailRequestBody, } from "./bulk-email-tool-body.js";
3
+ import { registerInboxTools } from "./inbox-tool-registration.js";
3
4
  import { projectSearchResultPage } from "./search-result-projection.js";
4
5
  import { TOOL_DESCRIPTIONS } from "./tool-descriptions.js";
5
- import { campaignImportGuidance, campaignRosterGuidance, inboxThreadListGuidance, readinessGuidance, searchGuidance, } from "./tool-guidance.js";
6
+ import { campaignImportGuidance, campaignRosterGuidance, inboxThreadListGuidance, inboxThreadMessagesGuidance, readinessGuidance, searchGuidance, } from "./tool-guidance.js";
6
7
  import { toolResult } from "./tool-result.js";
7
8
  import { compactJsonObject, pathSegment, queryFrom, } from "./tool-http-helpers.js";
8
9
  import { BulkEmailJobResponseSchema, BulkEmailReadinessResponseSchema, CampaignDetailResponseSchema, CampaignCreatorsResponseSchema, CampaignImportResponseSchema, CampaignsResponseSchema, InboxAggregatesResponseSchema, InboxThreadPageResponseSchema, InboxWorkspaceStateResponseSchema, JsonObjectSchema, SearchCreatedResponseSchema, SearchResultsResponseSchema, WhoamiResponseSchema, } from "./schemas.js";
9
- import { BulkEmailJobInputSchema, CampaignCreatorsInputSchema, CheckBulkEmailReadinessDiscoveryInputSchema, CheckBulkEmailReadinessInputSchema, CreateCampaignInputSchema, GetSearchResultsInputSchema, InboxFiltersInputSchema, ListCampaignsInputSchema, ListInboxThreadsInputSchema, ListWorkspacesInputSchema, SaveSearchResultsToCampaignInputSchema, SearchCreatorsInputSchema, SelectWorkspaceInputSchema, SendInboxReplyInputSchema, StartBulkEmailDiscoveryInputSchema, StartBulkEmailInputSchema, UpdateInboxThreadStateInputSchema, WorkflowGuideInputSchema, WhoamiInputSchema, normalizeCheckBulkEmailReadinessInput, normalizeStartBulkEmailInput, } from "./tool-inputs.js";
10
+ import { BulkEmailJobInputSchema, CampaignCreatorsInputSchema, CheckBulkEmailReadinessDiscoveryInputSchema, CheckBulkEmailReadinessInputSchema, CreateCampaignInputSchema, GetInboxThreadMessagesInputSchema, GetSearchResultsInputSchema, InboxFiltersInputSchema, ListInboxThreadsInputSchema, ListCampaignsInputSchema, ListWorkspacesInputSchema, SaveSearchResultsToCampaignInputSchema, SearchCreatorsInputSchema, SelectWorkspaceInputSchema, SendInboxReplyInputSchema, StartBulkEmailDiscoveryInputSchema, StartBulkEmailInputSchema, UpdateInboxThreadStateInputSchema, WorkflowGuideInputSchema, WhoamiInputSchema, normalizeCheckBulkEmailReadinessInput, normalizeStartBulkEmailInput, } from "./tool-inputs.js";
10
11
  import { getHyperstarWorkflowGuide, listAvailableWorkspaces, selectAvailableWorkspace, } from "./workspaces.js";
11
12
  /** Build workflow handlers around a Hyperstar HTTP client. */
12
13
  export function createToolHandlers(client, options = {}) {
@@ -118,6 +119,10 @@ export function createToolHandlers(client, options = {}) {
118
119
  const parsedInput = InboxFiltersInputSchema.parse(input);
119
120
  return InboxAggregatesResponseSchema.parse(await client.get("/v1/inbox/threads/aggregates", queryFrom(parsedInput)));
120
121
  },
122
+ getInboxThreadMessages: async (input) => {
123
+ const parsedInput = GetInboxThreadMessagesInputSchema.parse(input);
124
+ return inboxThreadMessagesGuidance(JsonObjectSchema.parse(await client.get(`/v1/inbox/threads/${pathSegment(parsedInput.platform)}/${pathSegment(parsedInput.thread_id)}/messages`)));
125
+ },
121
126
  updateInboxThreadState: async (input) => {
122
127
  const parsedInput = UpdateInboxThreadStateInputSchema.parse(input);
123
128
  return InboxWorkspaceStateResponseSchema.parse(await client.patch(`/v1/inbox/threads/${pathSegment(parsedInput.platform)}/${pathSegment(parsedInput.thread_id)}/state`, compactJsonObject([
@@ -137,6 +142,7 @@ export function createToolHandlers(client, options = {}) {
137
142
  ["attachments", parsedInput.attachments],
138
143
  ["campaign_creator_id", parsedInput.campaign_creator_id],
139
144
  ["idempotency_key", parsedInput.idempotency_key],
145
+ ["send_confirmation", parsedInput.send_confirmation],
140
146
  ])));
141
147
  },
142
148
  };
@@ -209,24 +215,5 @@ export function registerHyperstarTools(server, client, options = {}) {
209
215
  description: TOOL_DESCRIPTIONS.getBulkEmailJob,
210
216
  inputSchema: BulkEmailJobInputSchema,
211
217
  }, async (input) => toolResult(await handlers.getBulkEmailJob(input)));
212
- server.registerTool("list_inbox_threads", {
213
- title: "List Inbox Threads",
214
- description: TOOL_DESCRIPTIONS.listInboxThreads,
215
- inputSchema: ListInboxThreadsInputSchema,
216
- }, async (input) => toolResult(await handlers.listInboxThreads(input)));
217
- server.registerTool("get_inbox_aggregates", {
218
- title: "Get Inbox Aggregates",
219
- description: TOOL_DESCRIPTIONS.getInboxAggregates,
220
- inputSchema: InboxFiltersInputSchema,
221
- }, async (input) => toolResult(await handlers.getInboxAggregates(input)));
222
- server.registerTool("update_inbox_thread_state", {
223
- title: "Update Inbox Thread State",
224
- description: TOOL_DESCRIPTIONS.updateInboxThreadState,
225
- inputSchema: UpdateInboxThreadStateInputSchema,
226
- }, async (input) => toolResult(await handlers.updateInboxThreadState(input)));
227
- server.registerTool("send_inbox_reply", {
228
- title: "Send Inbox Reply",
229
- description: TOOL_DESCRIPTIONS.sendInboxReply,
230
- inputSchema: SendInboxReplyInputSchema,
231
- }, async (input) => toolResult(await handlers.sendInboxReply(input)));
218
+ registerInboxTools(server, handlers);
232
219
  }
@@ -11,7 +11,7 @@ export declare const CLI_WORKFLOW_SEQUENCE: readonly ["list_workspaces", "select
11
11
  export declare const SERVICE_ACCOUNT_WORKFLOW_SEQUENCE: readonly ["list_workspaces", "hyperstar_whoami", "search_creators", "list_campaigns or create_campaign", "save_search_results_to_campaign", "list_campaign_creators", "check_bulk_email_readiness", "WARNING: start_bulk_email and send_inbox_reply perform real sends."];
12
12
  export declare const CLI_SAFETY_NOTES: readonly ["Run list_workspaces and select_workspace before Product API calls that require workspace scope.", "Run hyperstar_whoami after selecting a workspace to confirm the authenticated principal.", "Run list_campaigns or create_campaign before save_search_results_to_campaign when the user has not supplied a campaign_id.", "check_bulk_email_readiness is the final dry-run gate before any bulk send.", "start_bulk_email and send_inbox_reply perform real sends."];
13
13
  export declare const SERVICE_ACCOUNT_SAFETY_NOTES: readonly ["Service-account auth is already workspace-scoped; select_workspace is only for local browser CLI auth.", "Run list_workspaces and hyperstar_whoami before workflow tools to confirm the authenticated workspace.", "Run list_campaigns or create_campaign before save_search_results_to_campaign when the user has not supplied a campaign_id.", "check_bulk_email_readiness is the final dry-run gate before any bulk send.", "start_bulk_email and send_inbox_reply perform real sends."];
14
- export declare const WORKFLOW_DATA_FLOW_NOTES: readonly ["search_creators returns a search_id plus compact creator summaries; save_search_results_to_campaign consumes search_id server-side, so agents do not need to load every creator row into context.", "check_bulk_email_readiness and start_bulk_email require recipient_target, either exact campaign_creator_ids or a narrowed campaign_creator_selection.", "Use list_campaign_creators after saving search results to find campaign_creator_ids for explicit recipient_target calls.", "Use list_inbox_threads for triage, update_inbox_thread_state for non-send state changes, and send_inbox_reply only after explicit user authorization."];
14
+ export declare const WORKFLOW_DATA_FLOW_NOTES: readonly ["search_creators returns a search_id plus compact creator summaries; save_search_results_to_campaign consumes search_id server-side, so agents do not need to load every creator row into context.", "check_bulk_email_readiness and start_bulk_email require recipient_target, either exact campaign_creator_ids or a narrowed campaign_creator_selection.", "Use list_campaign_creators after saving search results to find campaign_creator_ids for explicit recipient_target calls.", "Use list_inbox_threads for triage, get_inbox_thread_messages to read full message history before drafting replies, update_inbox_thread_state for non-send state changes, and send_inbox_reply only after explicit user authorization with send_confirmation: \"user_authorized\"."];
15
15
  export declare const SEARCH_FILTER_GUIDANCE: readonly ["filters is a structured JSON object validated by the MCP before it is forwarded to Product API search.", "Use range objects such as `follower_range: {\"min\": 1000, \"max\": 100000}`, `avg_engagement_rate`, `avg_views`, `gmv`, and `gpm`.", "Use boolean/string filters such as `has_email`, `email_contactability`, `is_verified`, `has_tiktok_shop`, `creator_gender`, `creator_language`, `category_1`, and Instagram `category_name`.", "Put broad niches, verticals, and countries in `query`/`region` unless a named structured filter applies; do not invent `country`, `categories`, `follower_count`, or `engagement_rate` filter keys.", "Use sort_by for relevance, follower_count, engagement_rate, avg_views, views_growth_rate, gmv, or gpm; gmv and gpm are TikTok-only.", "Keep filters narrow and serializable; use get_search_results pagination instead of loading every creator row into context."];
16
16
  export declare const workflowGuideResourceDefinitions: readonly [{
17
17
  readonly kind: "headless-workflow";
@@ -37,7 +37,7 @@ export const WORKFLOW_DATA_FLOW_NOTES = [
37
37
  "search_creators returns a search_id plus compact creator summaries; save_search_results_to_campaign consumes search_id server-side, so agents do not need to load every creator row into context.",
38
38
  "check_bulk_email_readiness and start_bulk_email require recipient_target, either exact campaign_creator_ids or a narrowed campaign_creator_selection.",
39
39
  "Use list_campaign_creators after saving search results to find campaign_creator_ids for explicit recipient_target calls.",
40
- "Use list_inbox_threads for triage, update_inbox_thread_state for non-send state changes, and send_inbox_reply only after explicit user authorization.",
40
+ 'Use list_inbox_threads for triage, get_inbox_thread_messages to read full message history before drafting replies, update_inbox_thread_state for non-send state changes, and send_inbox_reply only after explicit user authorization with send_confirmation: "user_authorized".',
41
41
  ];
42
42
  export const SEARCH_FILTER_GUIDANCE = [
43
43
  "filters is a structured JSON object validated by the MCP before it is forwarded to Product API search.",
@@ -150,9 +150,10 @@ export function buildWorkflowMarkdown(kind) {
150
150
  "# Inbox Workflow",
151
151
  "",
152
152
  "1. Call `list_inbox_threads` with filters such as platform, unread state, archive state, search text, or campaign/job identifiers.",
153
- "2. Call `get_inbox_aggregates` when the user asks for counts by common inbox filters.",
154
- "3. Use `update_inbox_thread_state` for archive, star, snooze, or read-state changes.",
155
- "4. Use `send_inbox_reply` only when the user has authorized a real reply. Include a stable `idempotency_key`.",
153
+ "2. Call `get_inbox_thread_messages` before drafting replies; thread lists contain summaries and previews, not full message history.",
154
+ "3. Call `get_inbox_aggregates` when the user asks for counts by common inbox filters.",
155
+ "4. Use `update_inbox_thread_state` for archive, star, snooze, or read-state changes.",
156
+ '5. Use `send_inbox_reply` only when the user has authorized a real reply. Include a stable `idempotency_key` and `send_confirmation: "user_authorized"`.',
156
157
  ].join("\n");
157
158
  }
158
159
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperstar/mcp",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "description": "Local stdio MCP server and CLI for Hyperstar headless workflows.",
@@ -33,6 +33,7 @@
33
33
  },
34
34
  "files": [
35
35
  "dist",
36
+ "scripts/npm-clean-room-smoke.mjs",
36
37
  "README.md",
37
38
  "package.json"
38
39
  ],
@@ -44,6 +45,7 @@
44
45
  "format:check": "prettier --check .",
45
46
  "prepack": "npm run build",
46
47
  "smoke:dev": "npm run build && node scripts/dev-smoke.mjs",
48
+ "smoke:npm": "node scripts/npm-clean-room-smoke.mjs",
47
49
  "test": "vitest run",
48
50
  "typecheck": "tsc -p tsconfig.json --noEmit"
49
51
  },
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { dirname, isAbsolute, resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
10
+ const packageJson = JSON.parse(
11
+ readFileSync(resolve(packageRoot, "package.json"), "utf8"),
12
+ );
13
+ const packageSpec =
14
+ process.env.HYPERSTAR_MCP_NPM_PACKAGE ??
15
+ `${packageJson.name}@${packageJson.version}`;
16
+ const scratchDir = mkdtempSync(resolve(tmpdir(), "hyperstar-mcp-npm-smoke-"));
17
+
18
+ function isLocalPackageSpec(value) {
19
+ return (
20
+ value.startsWith("file:") ||
21
+ value.startsWith(".") ||
22
+ value.endsWith(".tgz") ||
23
+ isAbsolute(value)
24
+ );
25
+ }
26
+
27
+ function logLocalPackageSpec(value) {
28
+ console.log(
29
+ JSON.stringify(
30
+ {
31
+ step: "local-package",
32
+ ok: true,
33
+ command: [],
34
+ stdout: [value],
35
+ },
36
+ null,
37
+ 2,
38
+ ),
39
+ );
40
+ }
41
+
42
+ function runStep(step, command, args) {
43
+ const result = spawnSync(command, args, {
44
+ cwd: scratchDir,
45
+ encoding: "utf8",
46
+ env: process.env,
47
+ shell: false,
48
+ });
49
+ const stdout = result.stdout.trim();
50
+ const stderr = result.stderr.trim();
51
+ if (result.status !== 0) {
52
+ console.error(
53
+ JSON.stringify(
54
+ {
55
+ step,
56
+ ok: false,
57
+ command: [command, ...args],
58
+ status: result.status,
59
+ stdout,
60
+ stderr,
61
+ },
62
+ null,
63
+ 2,
64
+ ),
65
+ );
66
+ process.exit(result.status ?? 1);
67
+ }
68
+ console.log(
69
+ JSON.stringify(
70
+ {
71
+ step,
72
+ ok: true,
73
+ command: [command, ...args],
74
+ stdout: stdout.split("\n").slice(0, 8),
75
+ },
76
+ null,
77
+ 2,
78
+ ),
79
+ );
80
+ }
81
+
82
+ try {
83
+ if (isLocalPackageSpec(packageSpec)) {
84
+ logLocalPackageSpec(packageSpec);
85
+ } else {
86
+ runStep("registry-version", "npm", [
87
+ "view",
88
+ packageSpec,
89
+ "version",
90
+ "--silent",
91
+ ]);
92
+ }
93
+ runStep("hyperstar-cli-help", "npm", [
94
+ "exec",
95
+ "--yes",
96
+ "--package",
97
+ packageSpec,
98
+ "--",
99
+ "hyperstar",
100
+ "--help",
101
+ ]);
102
+ runStep("hyperstar-mcp-help", "npm", [
103
+ "exec",
104
+ "--yes",
105
+ "--package",
106
+ packageSpec,
107
+ "--",
108
+ "hyperstar-mcp",
109
+ "--help",
110
+ ]);
111
+ } finally {
112
+ rmSync(scratchDir, { recursive: true, force: true });
113
+ }