@sendly/mcp
Advanced tools
+71
-1
@@ -7,2 +7,3 @@ #!/usr/bin/env node | ||
| import { z } from "zod"; | ||
| var VERSION = "1.2.0"; | ||
| var API_KEY = process.env.SENDLY_API_KEY; | ||
@@ -16,3 +17,26 @@ var BASE_URL = process.env.SENDLY_BASE_URL || "https://sendly.live"; | ||
| } | ||
| if (!BASE_URL.startsWith("https://") && !BASE_URL.startsWith("http://localhost") && !BASE_URL.startsWith("http://127.0.0.1")) { | ||
| process.stderr.write( | ||
| "SENDLY_BASE_URL must use HTTPS in production.\nHTTP is only allowed for localhost development.\n" | ||
| ); | ||
| process.exit(1); | ||
| } | ||
| var RATE_LIMIT_WINDOW_MS = 6e4; | ||
| var RATE_LIMIT_MAX = 30; | ||
| var rateLimitTokens = RATE_LIMIT_MAX; | ||
| var rateLimitResetAt = Date.now() + RATE_LIMIT_WINDOW_MS; | ||
| function checkRateLimit() { | ||
| const now = Date.now(); | ||
| if (now >= rateLimitResetAt) { | ||
| rateLimitTokens = RATE_LIMIT_MAX; | ||
| rateLimitResetAt = now + RATE_LIMIT_WINDOW_MS; | ||
| } | ||
| if (rateLimitTokens <= 0) return false; | ||
| rateLimitTokens--; | ||
| return true; | ||
| } | ||
| async function api(method, path, body, query) { | ||
| if (!checkRateLimit()) { | ||
| throw new Error("Rate limited \u2014 too many requests. Wait a moment and try again."); | ||
| } | ||
| const url = new URL(`/api/v1${path}`, BASE_URL); | ||
@@ -34,2 +58,8 @@ if (query) { | ||
| if (res.status === 204) return { success: true }; | ||
| if (res.status === 429) { | ||
| const retryAfter = res.headers.get("Retry-After"); | ||
| throw new Error( | ||
| `Rate limited by API. ${retryAfter ? `Retry after ${retryAfter} seconds.` : "Wait a moment and try again."}` | ||
| ); | ||
| } | ||
| const data = await res.json(); | ||
@@ -56,3 +86,3 @@ if (!res.ok) { | ||
| name: "sendly", | ||
| version: "1.0.0" | ||
| version: VERSION | ||
| }); | ||
@@ -171,2 +201,21 @@ server.tool( | ||
| server.tool( | ||
| "get_conversation_context", | ||
| "Get LLM-ready formatted conversation context. Returns a pre-formatted text string with timestamped messages, AI classification, and business context \u2014 ready to paste into a prompt. More efficient than get_conversation for AI agents.", | ||
| { | ||
| conversationId: z.string().describe("The conversation ID"), | ||
| maxMessages: z.number().optional().describe("Max messages to include (default 20, max 50)") | ||
| }, | ||
| async ({ conversationId, maxMessages }) => { | ||
| try { | ||
| return ok( | ||
| await api("GET", `/conversations/${conversationId}/context`, void 0, { | ||
| max_messages: maxMessages?.toString() | ||
| }) | ||
| ); | ||
| } catch (e) { | ||
| return err(e); | ||
| } | ||
| } | ||
| ); | ||
| server.tool( | ||
| "get_conversation", | ||
@@ -489,3 +538,24 @@ "Get a conversation thread by ID. Set includeMessages=true to load the message history.", | ||
| ); | ||
| server.tool( | ||
| "generate_business_page", | ||
| "Generate a hosted business landing page for verification. Use when a business doesn't have their own website. Returns a URL at sendly.live/biz/{slug} that satisfies carrier website requirements.", | ||
| { | ||
| businessName: z.string().describe("Business name"), | ||
| useCase: z.string().optional().describe("Use case (e.g., Insurance Services, Appointment Reminders, 2FA)"), | ||
| useCaseSummary: z.string().optional().describe("Brief description of what the business does"), | ||
| contactEmail: z.string().optional().describe("Business contact email"), | ||
| contactPhone: z.string().optional().describe("Business phone number"), | ||
| businessAddress: z.string().optional().describe("City, State ZIP (e.g., Chicago, IL 60601)") | ||
| }, | ||
| async (params) => { | ||
| try { | ||
| return ok( | ||
| await api("POST", "/enterprise/business-page/generate", params) | ||
| ); | ||
| } catch (e) { | ||
| return err(e); | ||
| } | ||
| } | ||
| ); | ||
| var transport = new StdioServerTransport(); | ||
| await server.connect(transport); |
+1
-1
| { | ||
| "name": "@sendly/mcp", | ||
| "version": "1.1.0", | ||
| "version": "1.2.0", | ||
| "description": "Sendly MCP Server — SMS for AI agents. Send messages, manage conversations, verify phone numbers.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
21957
14.29%555
14.43%