supersendtx-mcp 0.6.32 → 0.6.34

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 +130 -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.34";
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,7 +195,6 @@ 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",
140
200
  "get_learnings",
@@ -194,7 +254,8 @@ var TALK_READ_TOOLS = ARC_MCP_TALK_GROWTH_TOOL_NAMES.map((growthTool) => ({
194
254
  description: `Read-only Ranla tool: ${growthTool.replace(/_/g, " ")}.`,
195
255
  inputSchema: openObjectSchema("Pass the tool arguments as fields."),
196
256
  kind: "growth",
197
- growthTool
257
+ growthTool,
258
+ readOnly: true
198
259
  }));
199
260
  var SETUP_TOOLS = [
200
261
  {
@@ -209,14 +270,40 @@ var SETUP_TOOLS = [
209
270
  required: ["slot"]
210
271
  },
211
272
  kind: "growth",
212
- growthTool: "request_connection"
273
+ growthTool: "request_connection",
274
+ readOnly: false
213
275
  }
214
276
  ];
215
277
  var META_TOOLS = [
216
278
  {
217
279
  name: "arc_list_threads",
218
- description: "List Ranla chat threads for this account.",
219
- inputSchema: openObjectSchema(),
280
+ 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.",
281
+ inputSchema: {
282
+ type: "object",
283
+ properties: {
284
+ limit: { type: "integer", description: "Threads per page. Default 50, max 100." },
285
+ before: {
286
+ type: "string",
287
+ description: "ISO timestamp from nextBefore: only threads last active before it."
288
+ }
289
+ }
290
+ },
291
+ kind: "meta"
292
+ },
293
+ {
294
+ name: "arc_get_thread",
295
+ 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.",
296
+ inputSchema: {
297
+ type: "object",
298
+ properties: {
299
+ threadId: { type: "string", description: "From arc_list_threads or arc_ensure_thread." },
300
+ before: {
301
+ type: "string",
302
+ description: "ISO timestamp from earlierBefore: the page of messages older than it."
303
+ }
304
+ },
305
+ required: ["threadId"]
306
+ },
220
307
  kind: "meta"
221
308
  },
222
309
  {
@@ -246,7 +333,7 @@ var META_TOOLS = [
246
333
  },
247
334
  {
248
335
  name: "arc_list_approvals",
249
- description: "List pending Ranla approval cards across threads.",
336
+ description: "List pending Ranla approvals across threads.",
250
337
  inputSchema: openObjectSchema(),
251
338
  kind: "meta"
252
339
  },
@@ -334,7 +421,9 @@ async function loadArcMcpTools(apiKey, baseUrl) {
334
421
  description: `${entry.mutates ? RANLA_AGENT_NAME : `${RANLA_AGENT_NAME}, read-only`}: ${entry.description}`,
335
422
  inputSchema: entry.inputSchema,
336
423
  kind: "growth",
337
- growthTool: entry.name
424
+ growthTool: entry.name,
425
+ // Missing `mutates` is not a promise of read-only: only an explicit false is.
426
+ readOnly: entry.mutates === false
338
427
  }))
339
428
  ];
340
429
  }
@@ -361,7 +450,13 @@ async function callArcMcpTool(apiKey, name, args, baseUrl) {
361
450
  }
362
451
  switch (name) {
363
452
  case "arc_list_threads": {
364
- const response = await arcFetch(origin, apiKey, "/api/growth/threads");
453
+ const query = new URLSearchParams();
454
+ const limit = optString(args, "limit");
455
+ const before = optString(args, "before");
456
+ if (limit) query.set("limit", limit);
457
+ if (before) query.set("before", before);
458
+ const suffix = query.toString() ? `?${query.toString()}` : "";
459
+ const response = await arcFetch(origin, apiKey, `/api/growth/threads${suffix}`);
365
460
  if (!response.ok) {
366
461
  throw new Error(
367
462
  typeof response.body.error === "string" ? response.body.error : `List threads failed (${response.status})`
@@ -369,6 +464,22 @@ async function callArcMcpTool(apiKey, name, args, baseUrl) {
369
464
  }
370
465
  return response.body;
371
466
  }
467
+ case "arc_get_thread": {
468
+ const threadId = optString(args, "threadId");
469
+ if (!threadId) throw new Error("Missing required argument: threadId");
470
+ const before = optString(args, "before");
471
+ const response = await arcFetch(
472
+ origin,
473
+ apiKey,
474
+ `/api/growth/threads/${encodeURIComponent(threadId)}${before ? `?before=${encodeURIComponent(before)}` : ""}`
475
+ );
476
+ if (!response.ok) {
477
+ throw new Error(
478
+ typeof response.body.error === "string" ? response.body.error : `Get thread failed (${response.status})`
479
+ );
480
+ }
481
+ return response.body;
482
+ }
372
483
  case "arc_ensure_thread": {
373
484
  const existingId = optString(args, "threadId");
374
485
  if (existingId) {
@@ -1135,11 +1246,16 @@ function createMcpServer(apiKey, baseUrl) {
1135
1246
  { capabilities: { tools: {} } }
1136
1247
  );
1137
1248
  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
- }))
1249
+ tools: [...MCP_TOOLS, ...await loadArcMcpTools(apiKey, baseUrl)].map((tool) => {
1250
+ const annotations = mcpToolAnnotations(tool);
1251
+ return {
1252
+ name: tool.name,
1253
+ title: annotations.title,
1254
+ description: tool.description,
1255
+ inputSchema: tool.inputSchema,
1256
+ annotations
1257
+ };
1258
+ })
1143
1259
  }));
1144
1260
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
1145
1261
  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.34",
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",