supersendtx-mcp 0.6.32 → 0.6.35

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 (3) hide show
  1. package/README.md +7 -2
  2. package/dist/index.js +132 -14
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -134,14 +134,19 @@ These call the app host and need a full-access key (OAuth sign-in and dashboard
134
134
 
135
135
  | Tool | Purpose |
136
136
  |------|---------|
137
- | `arc_ensure_thread` / `arc_list_threads` | Durable thread for this client |
137
+ | `arc_ensure_thread` / `arc_list_threads` | Durable thread for this client; list threads a page at a time |
138
+ | `arc_get_thread` | Read a thread's messages, artifacts and waiting approvals |
138
139
  | `arc_message` | Send text to Ranla and wait for the turn — returns `text`, `artifacts`, and `pendingApproval` |
139
140
  | `arc_list_approvals` / `arc_approve` / `arc_reject` | Answer approvals |
140
141
  | `arc_request_connection` | Connect link for a missing integration |
141
- | `arc_<tool>` | Read-only Ranla tools, listed from the account with input schemas |
142
+ | `arc_<tool>` | Read-only Ranla tools, listed from the account with input schemas. Results say in `forCaller` when a step needs `arc_message` |
142
143
 
143
144
  Flow: `arc_ensure_thread` → `arc_message` → if a step needs approval, `arc_list_approvals` → `arc_approve` or `arc_reject`.
144
145
 
146
+ An SEO page approval also carries `actionId`: pass it to `arc_seo_page_package` to read the draft, for example to build the page in your own repo.
147
+
148
+ Every tool has a `title` and `readOnlyHint` / `destructiveHint` annotations, so clients can run reads without asking and confirm writes.
149
+
145
150
  Full reference: https://docs.supersendtx.com/ai/mcp
146
151
 
147
152
  ## License
package/dist/index.js CHANGED
@@ -84,7 +84,63 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
84
84
  import { SuperSendTX } from "supersendtx";
85
85
 
86
86
  // package.json
87
- var version = "0.6.32";
87
+ var version = "0.6.35";
88
+
89
+ // src/annotations.ts
90
+ var READ = { readOnlyHint: true };
91
+ var WRITE = { readOnlyHint: false, destructiveHint: false };
92
+ var DELETE = { readOnlyHint: false, destructiveHint: true };
93
+ var FIXED_TOOL_HINTS = {
94
+ send_email: { ...WRITE, openWorldHint: true },
95
+ list_emails: READ,
96
+ get_email: READ,
97
+ cancel_email: DELETE,
98
+ list_received_emails: READ,
99
+ get_received_email: READ,
100
+ list_domains: READ,
101
+ get_domain: READ,
102
+ create_domain: WRITE,
103
+ apply_domain_dns: { ...WRITE, title: "Apply domain DNS records", openWorldHint: true },
104
+ verify_domain: { ...WRITE, idempotentHint: true },
105
+ list_webhooks: READ,
106
+ create_webhook: WRITE,
107
+ delete_webhook: DELETE,
108
+ list_suppressions: READ,
109
+ add_suppression: { ...WRITE, idempotentHint: true },
110
+ remove_suppression: DELETE,
111
+ list_templates: READ,
112
+ get_template: READ,
113
+ create_template: WRITE,
114
+ publish_template: WRITE,
115
+ send_test_webhook_event: { ...WRITE, openWorldHint: true },
116
+ get_deliverability: READ,
117
+ arc_list_threads: { ...READ, title: "List Ranla threads" },
118
+ arc_get_thread: { ...READ, title: "Read a Ranla thread" },
119
+ arc_ensure_thread: { ...WRITE, title: "Open a Ranla thread", idempotentHint: true },
120
+ arc_message: { ...WRITE, title: "Message Ranla" },
121
+ arc_list_approvals: { ...READ, title: "List pending approvals" },
122
+ /** Approving can send email to real people. */
123
+ arc_approve: { ...WRITE, title: "Approve a Ranla step", openWorldHint: true },
124
+ /** Rejecting discards the proposal. */
125
+ arc_reject: { ...DELETE, title: "Reject a Ranla step" }
126
+ };
127
+ var UPPER = /* @__PURE__ */ new Set(["ai", "aeo", "api", "dns", "id", "seo", "url"]);
128
+ function titleFromToolName(name) {
129
+ const words = name.replace(/^arc_/, "").split("_").filter(Boolean);
130
+ return words.map((word, index) => {
131
+ if (UPPER.has(word)) return word.toUpperCase();
132
+ return index === 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word;
133
+ }).join(" ");
134
+ }
135
+ function mcpToolAnnotations(tool) {
136
+ const fixed = FIXED_TOOL_HINTS[tool.name];
137
+ if (fixed) {
138
+ const { title: title2, ...hints } = fixed;
139
+ return { title: title2 ?? titleFromToolName(tool.name), ...hints };
140
+ }
141
+ const title = titleFromToolName(tool.name);
142
+ return tool.readOnly === true ? { title, ...READ } : { title, ...WRITE };
143
+ }
88
144
 
89
145
  // src/arc-talk-tools.ts
90
146
  var ARC_MCP_TALK_GROWTH_TOOL_NAMES = [
@@ -96,6 +152,10 @@ var ARC_MCP_TALK_GROWTH_TOOL_NAMES = [
96
152
  // A canvas document Ranla wrote, as it stands now. Writing to one mutates
97
153
  // and goes through `arc_message` like every other write.
98
154
  "read_canvas",
155
+ // The ids read_canvas takes; nothing else an external agent can call returns them.
156
+ "list_documents",
157
+ // Every image in the account, with public URLs an external agent can use as they are.
158
+ "list_library_images",
99
159
  "get_setup_status",
100
160
  "get_growth_health",
101
161
  "get_revenue_health",
@@ -107,6 +167,7 @@ var ARC_MCP_TALK_GROWTH_TOOL_NAMES = [
107
167
  "list_goals",
108
168
  "list_lists",
109
169
  "list_segments",
170
+ "get_segment",
110
171
  "get_tracking_snippet",
111
172
  "verify_tracking_install",
112
173
  "list_automations",
@@ -134,9 +195,10 @@ var ARC_MCP_TALK_GROWTH_TOOL_NAMES = [
134
195
  "get_ad_delivery_diagnostics",
135
196
  "get_search_term_evidence",
136
197
  "list_proposed_ad_campaigns",
137
- "recommend_ad_landings",
138
198
  "read_campaign_design",
139
199
  "list_campaign_replies",
200
+ // The inbox as conversations, with the replies Ranla drafted.
201
+ "read_inbox",
140
202
  "get_learnings",
141
203
  "read_repo_activity",
142
204
  "get_codebase_digest",
@@ -194,7 +256,8 @@ var TALK_READ_TOOLS = ARC_MCP_TALK_GROWTH_TOOL_NAMES.map((growthTool) => ({
194
256
  description: `Read-only Ranla tool: ${growthTool.replace(/_/g, " ")}.`,
195
257
  inputSchema: openObjectSchema("Pass the tool arguments as fields."),
196
258
  kind: "growth",
197
- growthTool
259
+ growthTool,
260
+ readOnly: true
198
261
  }));
199
262
  var SETUP_TOOLS = [
200
263
  {
@@ -209,14 +272,40 @@ var SETUP_TOOLS = [
209
272
  required: ["slot"]
210
273
  },
211
274
  kind: "growth",
212
- growthTool: "request_connection"
275
+ growthTool: "request_connection",
276
+ readOnly: false
213
277
  }
214
278
  ];
215
279
  var META_TOOLS = [
216
280
  {
217
281
  name: "arc_list_threads",
218
- description: "List Ranla chat threads for this account.",
219
- inputSchema: openObjectSchema(),
282
+ description: "List Ranla chat threads for this account, most recent first: id, title, desk, last activity and a one-line preview. Pass nextBefore as before for older threads. Read one with arc_get_thread.",
283
+ inputSchema: {
284
+ type: "object",
285
+ properties: {
286
+ limit: { type: "integer", description: "Threads per page. Default 50, max 100." },
287
+ before: {
288
+ type: "string",
289
+ description: "ISO timestamp from nextBefore: only threads last active before it."
290
+ }
291
+ }
292
+ },
293
+ kind: "meta"
294
+ },
295
+ {
296
+ name: "arc_get_thread",
297
+ description: "Read a Ranla thread: its messages (role, text, tools used, artifacts by id, approvals waiting on arc_approve), newest page first. Pass earlierBefore as before for older messages.",
298
+ inputSchema: {
299
+ type: "object",
300
+ properties: {
301
+ threadId: { type: "string", description: "From arc_list_threads or arc_ensure_thread." },
302
+ before: {
303
+ type: "string",
304
+ description: "ISO timestamp from earlierBefore: the page of messages older than it."
305
+ }
306
+ },
307
+ required: ["threadId"]
308
+ },
220
309
  kind: "meta"
221
310
  },
222
311
  {
@@ -246,7 +335,7 @@ var META_TOOLS = [
246
335
  },
247
336
  {
248
337
  name: "arc_list_approvals",
249
- description: "List pending Ranla approval cards across threads.",
338
+ description: "List pending Ranla approvals across threads.",
250
339
  inputSchema: openObjectSchema(),
251
340
  kind: "meta"
252
341
  },
@@ -334,7 +423,9 @@ async function loadArcMcpTools(apiKey, baseUrl) {
334
423
  description: `${entry.mutates ? RANLA_AGENT_NAME : `${RANLA_AGENT_NAME}, read-only`}: ${entry.description}`,
335
424
  inputSchema: entry.inputSchema,
336
425
  kind: "growth",
337
- growthTool: entry.name
426
+ growthTool: entry.name,
427
+ // Missing `mutates` is not a promise of read-only: only an explicit false is.
428
+ readOnly: entry.mutates === false
338
429
  }))
339
430
  ];
340
431
  }
@@ -361,7 +452,13 @@ async function callArcMcpTool(apiKey, name, args, baseUrl) {
361
452
  }
362
453
  switch (name) {
363
454
  case "arc_list_threads": {
364
- const response = await arcFetch(origin, apiKey, "/api/growth/threads");
455
+ const query = new URLSearchParams();
456
+ const limit = optString(args, "limit");
457
+ const before = optString(args, "before");
458
+ if (limit) query.set("limit", limit);
459
+ if (before) query.set("before", before);
460
+ const suffix = query.toString() ? `?${query.toString()}` : "";
461
+ const response = await arcFetch(origin, apiKey, `/api/growth/threads${suffix}`);
365
462
  if (!response.ok) {
366
463
  throw new Error(
367
464
  typeof response.body.error === "string" ? response.body.error : `List threads failed (${response.status})`
@@ -369,6 +466,22 @@ async function callArcMcpTool(apiKey, name, args, baseUrl) {
369
466
  }
370
467
  return response.body;
371
468
  }
469
+ case "arc_get_thread": {
470
+ const threadId = optString(args, "threadId");
471
+ if (!threadId) throw new Error("Missing required argument: threadId");
472
+ const before = optString(args, "before");
473
+ const response = await arcFetch(
474
+ origin,
475
+ apiKey,
476
+ `/api/growth/threads/${encodeURIComponent(threadId)}${before ? `?before=${encodeURIComponent(before)}` : ""}`
477
+ );
478
+ if (!response.ok) {
479
+ throw new Error(
480
+ typeof response.body.error === "string" ? response.body.error : `Get thread failed (${response.status})`
481
+ );
482
+ }
483
+ return response.body;
484
+ }
372
485
  case "arc_ensure_thread": {
373
486
  const existingId = optString(args, "threadId");
374
487
  if (existingId) {
@@ -1135,11 +1248,16 @@ function createMcpServer(apiKey, baseUrl) {
1135
1248
  { capabilities: { tools: {} } }
1136
1249
  );
1137
1250
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
1138
- tools: [...MCP_TOOLS, ...await loadArcMcpTools(apiKey, baseUrl)].map((tool) => ({
1139
- name: tool.name,
1140
- description: tool.description,
1141
- inputSchema: tool.inputSchema
1142
- }))
1251
+ tools: [...MCP_TOOLS, ...await loadArcMcpTools(apiKey, baseUrl)].map((tool) => {
1252
+ const annotations = mcpToolAnnotations(tool);
1253
+ return {
1254
+ name: tool.name,
1255
+ title: annotations.title,
1256
+ description: tool.description,
1257
+ inputSchema: tool.inputSchema,
1258
+ annotations
1259
+ };
1260
+ })
1143
1261
  }));
1144
1262
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
1145
1263
  const result = await callMcpTool(client, request.params.name, request.params.arguments ?? {}, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "supersendtx-mcp",
3
- "version": "0.6.32",
3
+ "version": "0.6.35",
4
4
  "description": "SuperSend TX MCP server — mail tools plus Ranla agent-first tools for Cursor and Claude",
5
5
  "license": "MIT",
6
6
  "type": "module",