supersendtx-mcp 0.6.35 → 0.6.39
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 +6 -7
- package/dist/index.js +43 -143
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# supersendtx-mcp
|
|
2
2
|
|
|
3
|
-
MCP server for [SuperSend TX](https://
|
|
3
|
+
MCP server for [SuperSend TX](https://ranla.ai) — send email and manage domains, webhooks, suppressions, and templates from Cursor, Claude Code, and other MCP clients, and talk to Ranla, the agent in the same account.
|
|
4
4
|
|
|
5
|
-
**Install:** `npx -y supersendtx-mcp` · **Docs:** https://docs.
|
|
5
|
+
**Install:** `npx -y supersendtx-mcp` · **Docs:** https://docs.ranla.ai/ai/mcp
|
|
6
6
|
|
|
7
7
|
## Transports
|
|
8
8
|
|
|
@@ -138,16 +138,15 @@ These call the app host and need a full-access key (OAuth sign-in and dashboard
|
|
|
138
138
|
| `arc_get_thread` | Read a thread's messages, artifacts and waiting approvals |
|
|
139
139
|
| `arc_message` | Send text to Ranla and wait for the turn — returns `text`, `artifacts`, and `pendingApproval` |
|
|
140
140
|
| `arc_list_approvals` / `arc_approve` / `arc_reject` | Answer approvals |
|
|
141
|
-
| `
|
|
142
|
-
| `arc_<tool>` | Read-only Ranla tools, listed from the account with input schemas. Results say in `forCaller` when a step needs `arc_message` |
|
|
141
|
+
| `arc_ranla` | Run one of Ranla's commands (`campaign list`, `page propose`, `connection request`). The description lists them from the account; `help <noun>` gives every field. What needs sign-off comes back as `pendingApproval` |
|
|
143
142
|
|
|
144
|
-
Flow: `arc_ensure_thread
|
|
143
|
+
Flow: `arc_ensure_thread`, then `arc_message` or `arc_ranla`, and if a step needs approval, `arc_list_approvals`, then `arc_approve` or `arc_reject`.
|
|
145
144
|
|
|
146
|
-
An SEO page approval also carries `actionId`:
|
|
145
|
+
An SEO page approval also carries `actionId`: run `arc_ranla` with `page get` and that `actionId` to read the draft, for example to build the page in your own repo.
|
|
147
146
|
|
|
148
147
|
Every tool has a `title` and `readOnlyHint` / `destructiveHint` annotations, so clients can run reads without asking and confirm writes.
|
|
149
148
|
|
|
150
|
-
Full reference: https://docs.
|
|
149
|
+
Full reference: https://docs.ranla.ai/ai/mcp
|
|
151
150
|
|
|
152
151
|
## License
|
|
153
152
|
|
package/dist/index.js
CHANGED
|
@@ -84,7 +84,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprot
|
|
|
84
84
|
import { SuperSendTX } from "supersendtx";
|
|
85
85
|
|
|
86
86
|
// package.json
|
|
87
|
-
var version = "0.6.
|
|
87
|
+
var version = "0.6.39";
|
|
88
88
|
|
|
89
89
|
// src/annotations.ts
|
|
90
90
|
var READ = { readOnlyHint: true };
|
|
@@ -118,6 +118,8 @@ var FIXED_TOOL_HINTS = {
|
|
|
118
118
|
arc_get_thread: { ...READ, title: "Read a Ranla thread" },
|
|
119
119
|
arc_ensure_thread: { ...WRITE, title: "Open a Ranla thread", idempotentHint: true },
|
|
120
120
|
arc_message: { ...WRITE, title: "Message Ranla" },
|
|
121
|
+
/** A command can change the account; what reaches people or spends stops at an approval. */
|
|
122
|
+
arc_ranla: { ...WRITE, title: "Run a Ranla command", openWorldHint: true },
|
|
121
123
|
arc_list_approvals: { ...READ, title: "List pending approvals" },
|
|
122
124
|
/** Approving can send email to real people. */
|
|
123
125
|
arc_approve: { ...WRITE, title: "Approve a Ranla step", openWorldHint: true },
|
|
@@ -142,102 +144,6 @@ function mcpToolAnnotations(tool) {
|
|
|
142
144
|
return tool.readOnly === true ? { title, ...READ } : { title, ...WRITE };
|
|
143
145
|
}
|
|
144
146
|
|
|
145
|
-
// src/arc-talk-tools.ts
|
|
146
|
-
var ARC_MCP_TALK_GROWTH_TOOL_NAMES = [
|
|
147
|
-
// Cold-start SEO plan: a minute of vendor data and a planning call. Read-only, so MCP carries it.
|
|
148
|
-
"seo_plan",
|
|
149
|
-
"get_business_brief",
|
|
150
|
-
"get_brand",
|
|
151
|
-
"read_url",
|
|
152
|
-
// A canvas document Ranla wrote, as it stands now. Writing to one mutates
|
|
153
|
-
// and goes through `arc_message` like every other write.
|
|
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",
|
|
159
|
-
"get_setup_status",
|
|
160
|
-
"get_growth_health",
|
|
161
|
-
"get_revenue_health",
|
|
162
|
-
"list_product_events",
|
|
163
|
-
"list_product_analytics",
|
|
164
|
-
"inspect_analytics_person",
|
|
165
|
-
"inspect_attribute_values",
|
|
166
|
-
"list_campaigns",
|
|
167
|
-
"list_goals",
|
|
168
|
-
"list_lists",
|
|
169
|
-
"list_segments",
|
|
170
|
-
"get_segment",
|
|
171
|
-
"get_tracking_snippet",
|
|
172
|
-
"verify_tracking_install",
|
|
173
|
-
"list_automations",
|
|
174
|
-
"get_automation",
|
|
175
|
-
"get_setup_run",
|
|
176
|
-
"propose_lifecycle_events",
|
|
177
|
-
"list_plan",
|
|
178
|
-
"get_job_schedule",
|
|
179
|
-
"list_opportunities",
|
|
180
|
-
"list_ideas",
|
|
181
|
-
"get_comms_prefs",
|
|
182
|
-
"list_audience",
|
|
183
|
-
"preview_segment",
|
|
184
|
-
"estimate_reach",
|
|
185
|
-
"list_templates",
|
|
186
|
-
"get_template",
|
|
187
|
-
"list_on_site_messages",
|
|
188
|
-
"get_on_site_message",
|
|
189
|
-
"get_on_site_theme",
|
|
190
|
-
"show_install",
|
|
191
|
-
"get_campaign_performance",
|
|
192
|
-
"get_campaign_evaluation",
|
|
193
|
-
"list_ad_campaigns",
|
|
194
|
-
"get_ad_spend",
|
|
195
|
-
"get_ad_delivery_diagnostics",
|
|
196
|
-
"get_search_term_evidence",
|
|
197
|
-
"list_proposed_ad_campaigns",
|
|
198
|
-
"read_campaign_design",
|
|
199
|
-
"list_campaign_replies",
|
|
200
|
-
// The inbox as conversations, with the replies Ranla drafted.
|
|
201
|
-
"read_inbox",
|
|
202
|
-
"get_learnings",
|
|
203
|
-
"read_repo_activity",
|
|
204
|
-
"get_codebase_digest",
|
|
205
|
-
"read_product_context",
|
|
206
|
-
"search_codebase",
|
|
207
|
-
"read_repo_file",
|
|
208
|
-
"site_traffic_summary",
|
|
209
|
-
"site_traffic_breakdown",
|
|
210
|
-
"site_traffic_efforts",
|
|
211
|
-
// Search reads. Read-only on the customer's own data, same as the analytics
|
|
212
|
-
// reads above: the role that owns them recommends and never publishes.
|
|
213
|
-
"seo_search_performance",
|
|
214
|
-
"seo_opportunities",
|
|
215
|
-
"seo_site_authority",
|
|
216
|
-
"seo_site_structure",
|
|
217
|
-
"seo_page_detail",
|
|
218
|
-
"seo_page_package",
|
|
219
|
-
// Reddit reads. Our own tables only: the inbox, a thread with its drafts,
|
|
220
|
-
// what ranks on Google, post ideas, connected accounts, the activity log.
|
|
221
|
-
"reddit_desk_status",
|
|
222
|
-
"reddit_opportunities",
|
|
223
|
-
"reddit_thread_detail",
|
|
224
|
-
"reddit_rankings",
|
|
225
|
-
"reddit_ai_citations",
|
|
226
|
-
"reddit_post_ideas",
|
|
227
|
-
"reddit_accounts",
|
|
228
|
-
"reddit_activity",
|
|
229
|
-
"reddit_leads",
|
|
230
|
-
"seo_competitors",
|
|
231
|
-
"seo_lift_queue",
|
|
232
|
-
// AI search reads. Our own tables only: how the answer engines see the product.
|
|
233
|
-
"aeo_status",
|
|
234
|
-
"aeo_prompts",
|
|
235
|
-
"aeo_answer",
|
|
236
|
-
"aeo_sources",
|
|
237
|
-
"aeo_competitors",
|
|
238
|
-
"aeo_actions"
|
|
239
|
-
];
|
|
240
|
-
|
|
241
147
|
// src/arc-mcp.ts
|
|
242
148
|
var DEFAULT_APP_ORIGIN = "https://app.supersendtx.com";
|
|
243
149
|
var DEFAULT_RANLA_APP_ORIGIN = "https://app.ranla.ai";
|
|
@@ -251,31 +157,27 @@ function openObjectSchema(description) {
|
|
|
251
157
|
};
|
|
252
158
|
}
|
|
253
159
|
var RANLA_AGENT_NAME = "Ranla";
|
|
254
|
-
var
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
{
|
|
264
|
-
name: "arc_request_connection",
|
|
265
|
-
description: "Surface a Connect card for a missing integration slot.",
|
|
160
|
+
var RANLA_COMMAND_TOOL_NAME = "arc_ranla";
|
|
161
|
+
var RANLA_COMMAND_INTRO = `Run one of ${RANLA_AGENT_NAME}'s commands in this account. command is "<noun> <verb>", such as "campaign list" or "page propose", and input holds its fields. "help <noun>" lists a noun's commands with every field. A command that needs sign-off does nothing yet: it comes back with pendingApproval, and arc_approve with its messageId applies it. For strategy, or anything that needs ${RANLA_AGENT_NAME}'s judgement or research, use arc_message.`;
|
|
162
|
+
function ranlaCommandTool(index) {
|
|
163
|
+
return {
|
|
164
|
+
name: RANLA_COMMAND_TOOL_NAME,
|
|
165
|
+
description: index ? `${RANLA_COMMAND_INTRO}
|
|
166
|
+
|
|
167
|
+
Commands:
|
|
168
|
+
${index}` : RANLA_COMMAND_INTRO,
|
|
266
169
|
inputSchema: {
|
|
267
170
|
type: "object",
|
|
268
171
|
properties: {
|
|
269
|
-
|
|
270
|
-
|
|
172
|
+
command: { type: "string", description: '"<noun> <verb>", or "help <noun>".' },
|
|
173
|
+
input: { type: "object", description: "The command's fields.", additionalProperties: true },
|
|
174
|
+
threadId: { type: "string", description: "Ranla thread to record it in (from arc_ensure_thread). Optional." }
|
|
271
175
|
},
|
|
272
|
-
required: ["
|
|
176
|
+
required: ["command"]
|
|
273
177
|
},
|
|
274
|
-
kind: "
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
];
|
|
178
|
+
kind: "meta"
|
|
179
|
+
};
|
|
180
|
+
}
|
|
279
181
|
var META_TOOLS = [
|
|
280
182
|
{
|
|
281
183
|
name: "arc_list_threads",
|
|
@@ -364,7 +266,7 @@ var META_TOOLS = [
|
|
|
364
266
|
kind: "meta"
|
|
365
267
|
}
|
|
366
268
|
];
|
|
367
|
-
var ARC_MCP_TOOLS = [...META_TOOLS,
|
|
269
|
+
var ARC_MCP_TOOLS = [...META_TOOLS, ranlaCommandTool()];
|
|
368
270
|
function resolveArcAppOrigin(options = {}) {
|
|
369
271
|
const fromEnv = process.env.RANLA_APP_URL?.trim() || process.env.SUPERSENDTX_APP_URL?.trim() || "";
|
|
370
272
|
if (fromEnv) return fromEnv.replace(/\/$/, "");
|
|
@@ -394,40 +296,22 @@ var CATALOG_TIMEOUT_MS = 4e3;
|
|
|
394
296
|
function isArcMcpToolName(name) {
|
|
395
297
|
return name.startsWith(ARC_TOOL_PREFIX);
|
|
396
298
|
}
|
|
397
|
-
function
|
|
398
|
-
if (!value || typeof value !== "object") return false;
|
|
399
|
-
const entry = value;
|
|
400
|
-
const schema = entry.inputSchema;
|
|
401
|
-
return typeof entry.name === "string" && /^[a-z0-9_]+$/.test(entry.name) && typeof entry.description === "string" && !!schema && typeof schema === "object" && schema.type === "object";
|
|
402
|
-
}
|
|
403
|
-
async function fetchGrowthToolCatalog(apiKey, baseUrl) {
|
|
299
|
+
async function fetchCommandIndex(apiKey, baseUrl) {
|
|
404
300
|
try {
|
|
405
301
|
const origin = resolveArcAppOrigin({ baseUrl, apiKey });
|
|
406
|
-
const response = await arcFetch(origin, apiKey, "/api/growth/
|
|
302
|
+
const response = await arcFetch(origin, apiKey, "/api/growth/commands", {
|
|
407
303
|
signal: AbortSignal.timeout(CATALOG_TIMEOUT_MS)
|
|
408
304
|
});
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return entries.length > 0 ? entries : null;
|
|
305
|
+
const index = response.body.index;
|
|
306
|
+
return response.ok && typeof index === "string" && index.trim() ? index : null;
|
|
412
307
|
} catch {
|
|
413
308
|
return null;
|
|
414
309
|
}
|
|
415
310
|
}
|
|
416
311
|
async function loadArcMcpTools(apiKey, baseUrl) {
|
|
417
|
-
const
|
|
418
|
-
if (!
|
|
419
|
-
return [
|
|
420
|
-
...META_TOOLS,
|
|
421
|
-
...catalog.map((entry) => ({
|
|
422
|
-
name: `${ARC_TOOL_PREFIX}${entry.name}`,
|
|
423
|
-
description: `${entry.mutates ? RANLA_AGENT_NAME : `${RANLA_AGENT_NAME}, read-only`}: ${entry.description}`,
|
|
424
|
-
inputSchema: entry.inputSchema,
|
|
425
|
-
kind: "growth",
|
|
426
|
-
growthTool: entry.name,
|
|
427
|
-
// Missing `mutates` is not a promise of read-only: only an explicit false is.
|
|
428
|
-
readOnly: entry.mutates === false
|
|
429
|
-
}))
|
|
430
|
-
];
|
|
312
|
+
const index = await fetchCommandIndex(apiKey, baseUrl);
|
|
313
|
+
if (!index) return ARC_MCP_TOOLS;
|
|
314
|
+
return [...META_TOOLS, ranlaCommandTool(index)];
|
|
431
315
|
}
|
|
432
316
|
function optString(args, key) {
|
|
433
317
|
if (args[key] == null) return void 0;
|
|
@@ -437,6 +321,22 @@ function optString(args, key) {
|
|
|
437
321
|
async function callArcMcpTool(apiKey, name, args, baseUrl) {
|
|
438
322
|
if (!isArcMcpToolName(name)) return null;
|
|
439
323
|
const origin = resolveArcAppOrigin({ baseUrl, apiKey });
|
|
324
|
+
if (name === RANLA_COMMAND_TOOL_NAME) {
|
|
325
|
+
const command = optString(args, "command");
|
|
326
|
+
if (!command) throw new Error("Missing required argument: command");
|
|
327
|
+
const input = args.input && typeof args.input === "object" && !Array.isArray(args.input) ? args.input : {};
|
|
328
|
+
const threadId = optString(args, "threadId");
|
|
329
|
+
const response = await arcFetch(origin, apiKey, "/api/growth/commands", {
|
|
330
|
+
method: "POST",
|
|
331
|
+
body: JSON.stringify({ command, input, ...threadId ? { threadId } : {} })
|
|
332
|
+
});
|
|
333
|
+
if (!response.ok) {
|
|
334
|
+
throw new Error(
|
|
335
|
+
typeof response.body.error === "string" ? response.body.error : `Ranla command failed (${response.status})`
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
return response.body;
|
|
339
|
+
}
|
|
440
340
|
if (!META_TOOLS.some((tool) => tool.name === name)) {
|
|
441
341
|
const growthTool = name.slice(ARC_TOOL_PREFIX.length);
|
|
442
342
|
const response = await arcFetch(origin, apiKey, `/api/growth/tools/${encodeURIComponent(growthTool)}`, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "supersendtx-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.39",
|
|
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",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"url": "git+https://github.com/Super-Send/superTX-app.git",
|
|
18
18
|
"directory": "packages/supersendtx-mcp"
|
|
19
19
|
},
|
|
20
|
-
"homepage": "https://docs.
|
|
20
|
+
"homepage": "https://docs.ranla.ai/ai/mcp",
|
|
21
21
|
"keywords": [
|
|
22
22
|
"mcp",
|
|
23
23
|
"email",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
43
|
-
"supersendtx": "0.15.
|
|
43
|
+
"supersendtx": "0.15.7"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"tsup": "^8.5.0",
|