@sendly/mcp
Advanced tools
+81
-2
@@ -6,2 +6,5 @@ #!/usr/bin/env node | ||
| import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; | ||
| import { readFileSync } from "fs"; | ||
| import { dirname, resolve } from "path"; | ||
| import { fileURLToPath } from "url"; | ||
@@ -1243,2 +1246,74 @@ // src/tools.ts | ||
| server2.tool( | ||
| "reset_webhook_circuit", | ||
| "Reset the circuit breaker for a webhook so deliveries resume immediately instead of waiting for the automatic 5-minute recovery.", | ||
| { webhookId: z.string().describe("The webhook ID") }, | ||
| async ({ webhookId }) => { | ||
| try { | ||
| return ok(await api2("POST", `/webhooks/${webhookId}/reset-circuit`)); | ||
| } catch (e) { | ||
| return err(e); | ||
| } | ||
| } | ||
| ); | ||
| server2.tool( | ||
| "redeliver_webhook", | ||
| "Replay failed or cancelled webhook deliveries from the audit log for a time window. Use after a customer endpoint has recovered from an outage. Reset the circuit first if it is open. Each replay preserves the original event_id for dedup.", | ||
| { | ||
| webhookId: z.string().describe("The webhook ID"), | ||
| since: z.string().optional().describe( | ||
| "Earliest delivery time, ISO-8601. Defaults to 24h ago." | ||
| ), | ||
| until: z.string().optional().describe("Latest delivery time, ISO-8601. Defaults to now."), | ||
| eventTypes: z.array(z.string()).optional().describe("Filter by event type. Defaults to all."), | ||
| statuses: z.array(z.string()).optional().describe( | ||
| "Replay deliveries in any of these statuses. Defaults to ['failed', 'cancelled']." | ||
| ), | ||
| limit: z.number().int().positive().max(1e4).optional().describe("Max deliveries to requeue. Defaults to 1000, max 10000.") | ||
| }, | ||
| async ({ webhookId, since, until, eventTypes, statuses, limit }) => { | ||
| try { | ||
| const body = {}; | ||
| if (since !== void 0) body.since = since; | ||
| if (until !== void 0) body.until = until; | ||
| if (eventTypes !== void 0) body.event_types = eventTypes; | ||
| if (statuses !== void 0) body.statuses = statuses; | ||
| if (limit !== void 0) body.limit = limit; | ||
| return ok( | ||
| await api2("POST", `/webhooks/${webhookId}/redeliver`, body) | ||
| ); | ||
| } catch (e) { | ||
| return err(e); | ||
| } | ||
| } | ||
| ); | ||
| server2.tool( | ||
| "backfill_webhook", | ||
| "Synthesize webhook events from the underlying message log for events that have no audit row (the case redeliver_webhook cannot recover). Use this when a circuit-breaker outage left events with no delivery record. Synthesized events have fresh IDs \u2014 clients should dedupe by event.data.object.id (the message ID). Reset the circuit first if it is open.", | ||
| { | ||
| webhookId: z.string().describe("The webhook ID"), | ||
| since: z.string().optional().describe( | ||
| "Earliest message time, ISO-8601. Defaults to 24h ago." | ||
| ), | ||
| until: z.string().optional().describe("Latest message time, ISO-8601. Defaults to now."), | ||
| eventTypes: z.array(z.string()).optional().describe( | ||
| "Filter by event type. Defaults to subscribed message events." | ||
| ), | ||
| limit: z.number().int().positive().max(1e4).optional().describe("Max events to synthesize. Defaults to 1000, max 10000.") | ||
| }, | ||
| async ({ webhookId, since, until, eventTypes, limit }) => { | ||
| try { | ||
| const body = {}; | ||
| if (since !== void 0) body.since = since; | ||
| if (until !== void 0) body.until = until; | ||
| if (eventTypes !== void 0) body.event_types = eventTypes; | ||
| if (limit !== void 0) body.limit = limit; | ||
| return ok( | ||
| await api2("POST", `/webhooks/${webhookId}/backfill`, body) | ||
| ); | ||
| } catch (e) { | ||
| return err(e); | ||
| } | ||
| } | ||
| ); | ||
| server2.tool( | ||
| "send_otp", | ||
@@ -1422,3 +1497,7 @@ "Send an OTP verification code via SMS. Use for phone verification, 2FA, or identity confirmation. In sandbox mode (test API key), the code is returned in the response.", | ||
| // src/index.ts | ||
| var VERSION = "2.0.0"; | ||
| var __dirname = dirname(fileURLToPath(import.meta.url)); | ||
| var pkg = JSON.parse( | ||
| readFileSync(resolve(__dirname, "../package.json"), "utf8") | ||
| ); | ||
| var VERSION = pkg.version; | ||
| var API_KEY = process.env.SENDLY_API_KEY; | ||
@@ -1464,3 +1543,3 @@ var BASE_URL = process.env.SENDLY_BASE_URL || "https://sendly.live"; | ||
| Authorization: `Bearer ${API_KEY}`, | ||
| "User-Agent": "@sendly/mcp/2.0.2" | ||
| "User-Agent": `@sendly/mcp/${VERSION}` | ||
| }; | ||
@@ -1467,0 +1546,0 @@ if (body) headers["Content-Type"] = "application/json"; |
+1
-1
| { | ||
| "name": "@sendly/mcp", | ||
| "version": "2.2.0", | ||
| "version": "2.3.0", | ||
| "description": "Sendly MCP Server — Full SMS platform for AI agents. Messaging, contacts, campaigns, templates, webhooks, OTP verification, and more.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+2
-2
| # @sendly/mcp | ||
| Full SMS platform for AI agents — 82 tools for messaging, contacts, campaigns, templates, webhooks, OTP verification, conversations, labels, drafts, and more via [Model Context Protocol](https://modelcontextprotocol.io). | ||
| Full SMS platform for AI agents — 85 tools for messaging, contacts, campaigns, templates, webhooks, OTP verification, conversations, labels, drafts, and more via [Model Context Protocol](https://modelcontextprotocol.io). | ||
@@ -244,3 +244,3 @@ ## Quick Setup | ||
| - [MCP Tools Reference](https://sendly.live/docs/ai/mcp-tools) — all 82 tools with schemas | ||
| - [MCP Tools Reference](https://sendly.live/docs/ai/mcp-tools) — all 85 tools with schemas | ||
| - [Agent Skills](https://sendly.live/skills) — SKILL.md files for 8 platforms | ||
@@ -247,0 +247,0 @@ - [API Reference](https://sendly.live/docs/api) |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
64038
6.01%1568
5.38%4
33.33%