@sendly/mcp 1.1.0 → 1.3.0
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/dist/index.js +71 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
+
var VERSION = "1.2.0";
|
|
7
8
|
var API_KEY = process.env.SENDLY_API_KEY;
|
|
8
9
|
var BASE_URL = process.env.SENDLY_BASE_URL || "https://sendly.live";
|
|
9
10
|
if (!API_KEY) {
|
|
@@ -12,7 +13,30 @@ if (!API_KEY) {
|
|
|
12
13
|
);
|
|
13
14
|
process.exit(1);
|
|
14
15
|
}
|
|
16
|
+
if (!BASE_URL.startsWith("https://") && !BASE_URL.startsWith("http://localhost") && !BASE_URL.startsWith("http://127.0.0.1")) {
|
|
17
|
+
process.stderr.write(
|
|
18
|
+
"SENDLY_BASE_URL must use HTTPS in production.\nHTTP is only allowed for localhost development.\n"
|
|
19
|
+
);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
var RATE_LIMIT_WINDOW_MS = 6e4;
|
|
23
|
+
var RATE_LIMIT_MAX = 30;
|
|
24
|
+
var rateLimitTokens = RATE_LIMIT_MAX;
|
|
25
|
+
var rateLimitResetAt = Date.now() + RATE_LIMIT_WINDOW_MS;
|
|
26
|
+
function checkRateLimit() {
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
if (now >= rateLimitResetAt) {
|
|
29
|
+
rateLimitTokens = RATE_LIMIT_MAX;
|
|
30
|
+
rateLimitResetAt = now + RATE_LIMIT_WINDOW_MS;
|
|
31
|
+
}
|
|
32
|
+
if (rateLimitTokens <= 0) return false;
|
|
33
|
+
rateLimitTokens--;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
15
36
|
async function api(method, path, body, query) {
|
|
37
|
+
if (!checkRateLimit()) {
|
|
38
|
+
throw new Error("Rate limited \u2014 too many requests. Wait a moment and try again.");
|
|
39
|
+
}
|
|
16
40
|
const url = new URL(`/api/v1${path}`, BASE_URL);
|
|
17
41
|
if (query) {
|
|
18
42
|
for (const [k, v] of Object.entries(query)) {
|
|
@@ -29,6 +53,12 @@ async function api(method, path, body, query) {
|
|
|
29
53
|
body: body ? JSON.stringify(body) : void 0
|
|
30
54
|
});
|
|
31
55
|
if (res.status === 204) return { success: true };
|
|
56
|
+
if (res.status === 429) {
|
|
57
|
+
const retryAfter = res.headers.get("Retry-After");
|
|
58
|
+
throw new Error(
|
|
59
|
+
`Rate limited by API. ${retryAfter ? `Retry after ${retryAfter} seconds.` : "Wait a moment and try again."}`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
32
62
|
const data = await res.json();
|
|
33
63
|
if (!res.ok) {
|
|
34
64
|
const msg = typeof data === "object" && data !== null ? data.error || data.message || JSON.stringify(data) : String(data);
|
|
@@ -50,7 +80,7 @@ function err(error) {
|
|
|
50
80
|
}
|
|
51
81
|
var server = new McpServer({
|
|
52
82
|
name: "sendly",
|
|
53
|
-
version:
|
|
83
|
+
version: VERSION
|
|
54
84
|
});
|
|
55
85
|
server.tool(
|
|
56
86
|
"send_sms",
|
|
@@ -163,6 +193,25 @@ server.tool(
|
|
|
163
193
|
}
|
|
164
194
|
}
|
|
165
195
|
);
|
|
196
|
+
server.tool(
|
|
197
|
+
"get_conversation_context",
|
|
198
|
+
"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.",
|
|
199
|
+
{
|
|
200
|
+
conversationId: z.string().describe("The conversation ID"),
|
|
201
|
+
maxMessages: z.number().optional().describe("Max messages to include (default 20, max 50)")
|
|
202
|
+
},
|
|
203
|
+
async ({ conversationId, maxMessages }) => {
|
|
204
|
+
try {
|
|
205
|
+
return ok(
|
|
206
|
+
await api("GET", `/conversations/${conversationId}/context`, void 0, {
|
|
207
|
+
max_messages: maxMessages?.toString()
|
|
208
|
+
})
|
|
209
|
+
);
|
|
210
|
+
} catch (e) {
|
|
211
|
+
return err(e);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
);
|
|
166
215
|
server.tool(
|
|
167
216
|
"get_conversation",
|
|
168
217
|
"Get a conversation thread by ID. Set includeMessages=true to load the message history.",
|
|
@@ -481,5 +530,26 @@ server.tool(
|
|
|
481
530
|
}
|
|
482
531
|
}
|
|
483
532
|
);
|
|
533
|
+
server.tool(
|
|
534
|
+
"generate_business_page",
|
|
535
|
+
"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.",
|
|
536
|
+
{
|
|
537
|
+
businessName: z.string().describe("Business name"),
|
|
538
|
+
useCase: z.string().optional().describe("Use case (e.g., Insurance Services, Appointment Reminders, 2FA)"),
|
|
539
|
+
useCaseSummary: z.string().optional().describe("Brief description of what the business does"),
|
|
540
|
+
contactEmail: z.string().optional().describe("Business contact email"),
|
|
541
|
+
contactPhone: z.string().optional().describe("Business phone number"),
|
|
542
|
+
businessAddress: z.string().optional().describe("City, State ZIP (e.g., Chicago, IL 60601)")
|
|
543
|
+
},
|
|
544
|
+
async (params) => {
|
|
545
|
+
try {
|
|
546
|
+
return ok(
|
|
547
|
+
await api("POST", "/enterprise/business-page/generate", params)
|
|
548
|
+
);
|
|
549
|
+
} catch (e) {
|
|
550
|
+
return err(e);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
);
|
|
484
554
|
var transport = new StdioServerTransport();
|
|
485
555
|
await server.connect(transport);
|