@codespar/mcp-zenvia 0.1.2 → 0.2.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 +186 -5
- package/package.json +5 -2
- package/server.json +3 -3
- package/src/index.ts +177 -5
package/dist/index.js
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* MCP Server for Zenvia — multi-channel messaging (SMS, WhatsApp, RCS).
|
|
3
|
+
* MCP Server for Zenvia — multi-channel messaging (SMS, WhatsApp, RCS, Email, Voice, Facebook).
|
|
4
4
|
*
|
|
5
5
|
* Tools:
|
|
6
6
|
* - send_sms: Send an SMS message
|
|
7
7
|
* - send_whatsapp: Send a WhatsApp message
|
|
8
8
|
* - send_rcs: Send an RCS message
|
|
9
|
+
* - send_email: Send a transactional email
|
|
10
|
+
* - send_voice: Send a voice message (TTS or pre-recorded audio)
|
|
11
|
+
* - send_facebook_message: Send a Facebook Messenger message
|
|
9
12
|
* - get_message_status: Get message delivery status
|
|
10
13
|
* - list_channels: List available messaging channels
|
|
11
14
|
* - create_subscription: Create a webhook subscription for events
|
|
15
|
+
* - list_subscriptions: List all webhook subscriptions
|
|
16
|
+
* - delete_subscription: Delete a webhook subscription
|
|
12
17
|
* - list_contacts: List contacts
|
|
18
|
+
* - create_contact: Create a contact in the contact base
|
|
19
|
+
* - delete_contact: Delete a contact
|
|
13
20
|
* - send_template: Send a WhatsApp template message
|
|
21
|
+
* - list_templates: List approved WhatsApp templates
|
|
22
|
+
* - get_report_entries: Get message report entries by date range
|
|
23
|
+
* - add_opt_out: Add a phone number to the opt-out list
|
|
14
24
|
*
|
|
15
25
|
* Environment:
|
|
16
26
|
* ZENVIA_API_TOKEN — API token from https://app.zenvia.com/
|
|
@@ -35,9 +45,11 @@ async function zenviaRequest(method, path, body) {
|
|
|
35
45
|
const err = await res.text();
|
|
36
46
|
throw new Error(`Zenvia API ${res.status}: ${err}`);
|
|
37
47
|
}
|
|
38
|
-
|
|
48
|
+
// Some endpoints (DELETE) return empty
|
|
49
|
+
const text = await res.text();
|
|
50
|
+
return text ? JSON.parse(text) : { ok: true };
|
|
39
51
|
}
|
|
40
|
-
const server = new Server({ name: "mcp-zenvia", version: "0.
|
|
52
|
+
const server = new Server({ name: "mcp-zenvia", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
41
53
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
42
54
|
tools: [
|
|
43
55
|
{
|
|
@@ -79,6 +91,48 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
79
91
|
required: ["from", "to", "text"],
|
|
80
92
|
},
|
|
81
93
|
},
|
|
94
|
+
{
|
|
95
|
+
name: "send_email",
|
|
96
|
+
description: "Send a transactional email",
|
|
97
|
+
inputSchema: {
|
|
98
|
+
type: "object",
|
|
99
|
+
properties: {
|
|
100
|
+
from: { type: "string", description: "Sender email address (verified domain)" },
|
|
101
|
+
to: { type: "string", description: "Recipient email address" },
|
|
102
|
+
subject: { type: "string", description: "Email subject" },
|
|
103
|
+
html: { type: "string", description: "HTML body of the email" },
|
|
104
|
+
text: { type: "string", description: "Plain text body (fallback)" },
|
|
105
|
+
},
|
|
106
|
+
required: ["from", "to", "subject"],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "send_voice",
|
|
111
|
+
description: "Send a voice message via TTS or pre-recorded audio URL",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
from: { type: "string", description: "Sender ID (Voice channel)" },
|
|
116
|
+
to: { type: "string", description: "Recipient phone number with country code" },
|
|
117
|
+
text: { type: "string", description: "Text to be spoken (TTS) — use either text or audioUrl" },
|
|
118
|
+
audioUrl: { type: "string", description: "URL of pre-recorded audio file — use either text or audioUrl" },
|
|
119
|
+
},
|
|
120
|
+
required: ["from", "to"],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "send_facebook_message",
|
|
125
|
+
description: "Send a Facebook Messenger message",
|
|
126
|
+
inputSchema: {
|
|
127
|
+
type: "object",
|
|
128
|
+
properties: {
|
|
129
|
+
from: { type: "string", description: "Sender ID (Facebook page)" },
|
|
130
|
+
to: { type: "string", description: "Recipient PSID (page-scoped user ID)" },
|
|
131
|
+
text: { type: "string", description: "Message text" },
|
|
132
|
+
},
|
|
133
|
+
required: ["from", "to", "text"],
|
|
134
|
+
},
|
|
135
|
+
},
|
|
82
136
|
{
|
|
83
137
|
name: "get_message_status",
|
|
84
138
|
description: "Get message delivery status by ID",
|
|
@@ -102,12 +156,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
102
156
|
type: "object",
|
|
103
157
|
properties: {
|
|
104
158
|
url: { type: "string", description: "Webhook URL to receive events" },
|
|
105
|
-
channel: { type: "string", enum: ["sms", "whatsapp", "rcs"], description: "Channel to subscribe to" },
|
|
159
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs", "email", "voice", "facebook"], description: "Channel to subscribe to" },
|
|
106
160
|
eventType: { type: "string", enum: ["MESSAGE", "MESSAGE_STATUS"], description: "Event type" },
|
|
107
161
|
},
|
|
108
162
|
required: ["url", "channel", "eventType"],
|
|
109
163
|
},
|
|
110
164
|
},
|
|
165
|
+
{
|
|
166
|
+
name: "list_subscriptions",
|
|
167
|
+
description: "List all webhook subscriptions",
|
|
168
|
+
inputSchema: { type: "object", properties: {} },
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
name: "delete_subscription",
|
|
172
|
+
description: "Delete a webhook subscription by ID",
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: "object",
|
|
175
|
+
properties: {
|
|
176
|
+
id: { type: "string", description: "Subscription ID" },
|
|
177
|
+
},
|
|
178
|
+
required: ["id"],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
111
181
|
{
|
|
112
182
|
name: "list_contacts",
|
|
113
183
|
description: "List contacts from the contact base",
|
|
@@ -119,6 +189,31 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
119
189
|
},
|
|
120
190
|
},
|
|
121
191
|
},
|
|
192
|
+
{
|
|
193
|
+
name: "create_contact",
|
|
194
|
+
description: "Create a contact in the contact base",
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: "object",
|
|
197
|
+
properties: {
|
|
198
|
+
name: { type: "string", description: "Contact full name" },
|
|
199
|
+
phone: { type: "string", description: "Phone number with country code" },
|
|
200
|
+
email: { type: "string", description: "Email address" },
|
|
201
|
+
groupId: { type: "string", description: "Optional group ID to add the contact to" },
|
|
202
|
+
},
|
|
203
|
+
required: ["name"],
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: "delete_contact",
|
|
208
|
+
description: "Delete a contact by ID",
|
|
209
|
+
inputSchema: {
|
|
210
|
+
type: "object",
|
|
211
|
+
properties: {
|
|
212
|
+
id: { type: "string", description: "Contact ID" },
|
|
213
|
+
},
|
|
214
|
+
required: ["id"],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
122
217
|
{
|
|
123
218
|
name: "send_template",
|
|
124
219
|
description: "Send a WhatsApp template message (pre-approved)",
|
|
@@ -136,6 +231,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
136
231
|
required: ["from", "to", "templateId"],
|
|
137
232
|
},
|
|
138
233
|
},
|
|
234
|
+
{
|
|
235
|
+
name: "list_templates",
|
|
236
|
+
description: "List approved message templates (WhatsApp/SMS/RCS)",
|
|
237
|
+
inputSchema: {
|
|
238
|
+
type: "object",
|
|
239
|
+
properties: {
|
|
240
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs"], description: "Filter templates by channel" },
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
name: "get_report_entries",
|
|
246
|
+
description: "Get message report entries within a date range",
|
|
247
|
+
inputSchema: {
|
|
248
|
+
type: "object",
|
|
249
|
+
properties: {
|
|
250
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs", "email", "voice", "facebook"], description: "Channel to report on" },
|
|
251
|
+
startDate: { type: "string", description: "ISO 8601 start date (e.g. 2026-04-01)" },
|
|
252
|
+
endDate: { type: "string", description: "ISO 8601 end date (e.g. 2026-04-24)" },
|
|
253
|
+
},
|
|
254
|
+
required: ["channel", "startDate", "endDate"],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "add_opt_out",
|
|
259
|
+
description: "Add a phone number to the opt-out list (suppresses future messages)",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: "object",
|
|
262
|
+
properties: {
|
|
263
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs", "voice"], description: "Channel for the opt-out" },
|
|
264
|
+
from: { type: "string", description: "Sender ID the opt-out applies to" },
|
|
265
|
+
phone: { type: "string", description: "Phone number to opt out (with country code)" },
|
|
266
|
+
},
|
|
267
|
+
required: ["channel", "from", "phone"],
|
|
268
|
+
},
|
|
269
|
+
},
|
|
139
270
|
],
|
|
140
271
|
}));
|
|
141
272
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -148,12 +279,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
148
279
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/whatsapp/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
|
|
149
280
|
case "send_rcs":
|
|
150
281
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/rcs/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
|
|
282
|
+
case "send_email": {
|
|
283
|
+
const contents = [];
|
|
284
|
+
if (args?.html)
|
|
285
|
+
contents.push({ type: "email", html: args.html, subject: args?.subject });
|
|
286
|
+
else if (args?.text)
|
|
287
|
+
contents.push({ type: "email", text: args.text, subject: args?.subject });
|
|
288
|
+
else
|
|
289
|
+
contents.push({ type: "email", subject: args?.subject });
|
|
290
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/email/messages", { from: args?.from, to: args?.to, contents }), null, 2) }] };
|
|
291
|
+
}
|
|
292
|
+
case "send_voice": {
|
|
293
|
+
const contents = [];
|
|
294
|
+
if (args?.audioUrl)
|
|
295
|
+
contents.push({ type: "audio", url: args.audioUrl });
|
|
296
|
+
else if (args?.text)
|
|
297
|
+
contents.push({ type: "text", text: args.text });
|
|
298
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/voice/messages", { from: args?.from, to: args?.to, contents }), null, 2) }] };
|
|
299
|
+
}
|
|
300
|
+
case "send_facebook_message":
|
|
301
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/facebook/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
|
|
151
302
|
case "get_message_status":
|
|
152
303
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/reports/${args?.id}`), null, 2) }] };
|
|
153
304
|
case "list_channels":
|
|
154
305
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", "/channels"), null, 2) }] };
|
|
155
306
|
case "create_subscription":
|
|
156
307
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/subscriptions", { webhook: { url: args?.url }, criteria: { channel: args?.channel }, eventType: args?.eventType }), null, 2) }] };
|
|
308
|
+
case "list_subscriptions":
|
|
309
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", "/subscriptions"), null, 2) }] };
|
|
310
|
+
case "delete_subscription":
|
|
311
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("DELETE", `/subscriptions/${args?.id}`), null, 2) }] };
|
|
157
312
|
case "list_contacts": {
|
|
158
313
|
const params = new URLSearchParams();
|
|
159
314
|
if (args?.page)
|
|
@@ -162,8 +317,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
162
317
|
params.set("size", String(args.size));
|
|
163
318
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/contacts?${params}`), null, 2) }] };
|
|
164
319
|
}
|
|
320
|
+
case "create_contact": {
|
|
321
|
+
const body = { name: args?.name };
|
|
322
|
+
if (args?.phone)
|
|
323
|
+
body.phone = args.phone;
|
|
324
|
+
if (args?.email)
|
|
325
|
+
body.email = args.email;
|
|
326
|
+
if (args?.groupId)
|
|
327
|
+
body.groupId = args.groupId;
|
|
328
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/contacts", body), null, 2) }] };
|
|
329
|
+
}
|
|
330
|
+
case "delete_contact":
|
|
331
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("DELETE", `/contacts/${args?.id}`), null, 2) }] };
|
|
165
332
|
case "send_template":
|
|
166
333
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/whatsapp/messages", { from: args?.from, to: args?.to, contents: [{ type: "template", templateId: args?.templateId, fields: args?.fields || {} }] }), null, 2) }] };
|
|
334
|
+
case "list_templates": {
|
|
335
|
+
const params = new URLSearchParams();
|
|
336
|
+
if (args?.channel)
|
|
337
|
+
params.set("channel", String(args.channel));
|
|
338
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/templates?${params}`), null, 2) }] };
|
|
339
|
+
}
|
|
340
|
+
case "get_report_entries": {
|
|
341
|
+
const params = new URLSearchParams();
|
|
342
|
+
params.set("startDate", String(args?.startDate));
|
|
343
|
+
params.set("endDate", String(args?.endDate));
|
|
344
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/reports/${args?.channel}/entries?${params}`), null, 2) }] };
|
|
345
|
+
}
|
|
346
|
+
case "add_opt_out":
|
|
347
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", `/channels/${args?.channel}/senders/${args?.from}/opt-outs`, { phone: args?.phone }), null, 2) }] };
|
|
167
348
|
default:
|
|
168
349
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
169
350
|
}
|
|
@@ -190,7 +371,7 @@ async function main() {
|
|
|
190
371
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
191
372
|
t.onclose = () => { if (t.sessionId)
|
|
192
373
|
transports.delete(t.sessionId); };
|
|
193
|
-
const s = new Server({ name: "mcp-zenvia", version: "0.
|
|
374
|
+
const s = new Server({ name: "mcp-zenvia", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
194
375
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
195
376
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
196
377
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-zenvia",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Zenvia — SMS, WhatsApp, RCS messaging and
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP server for Zenvia — SMS, WhatsApp, RCS, Email, Voice, Facebook messaging, templates and reports",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
"sms",
|
|
26
26
|
"whatsapp",
|
|
27
27
|
"rcs",
|
|
28
|
+
"email",
|
|
29
|
+
"voice",
|
|
30
|
+
"facebook",
|
|
28
31
|
"brazil"
|
|
29
32
|
],
|
|
30
33
|
"mcpName": "io.github.codespar/mcp-zenvia"
|
package/server.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
3
|
"name": "io.github.codespar/mcp-zenvia",
|
|
4
|
-
"description": "MCP server for Zenvia — SMS, WhatsApp, RCS messaging and
|
|
4
|
+
"description": "MCP server for Zenvia — SMS, WhatsApp, RCS, Email, Voice, Facebook messaging, templates and reports",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/codespar/mcp-dev-brasil",
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/communication/zenvia"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.2.0",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@codespar/mcp-zenvia",
|
|
15
|
-
"version": "0.
|
|
15
|
+
"version": "0.2.0",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* MCP Server for Zenvia — multi-channel messaging (SMS, WhatsApp, RCS).
|
|
4
|
+
* MCP Server for Zenvia — multi-channel messaging (SMS, WhatsApp, RCS, Email, Voice, Facebook).
|
|
5
5
|
*
|
|
6
6
|
* Tools:
|
|
7
7
|
* - send_sms: Send an SMS message
|
|
8
8
|
* - send_whatsapp: Send a WhatsApp message
|
|
9
9
|
* - send_rcs: Send an RCS message
|
|
10
|
+
* - send_email: Send a transactional email
|
|
11
|
+
* - send_voice: Send a voice message (TTS or pre-recorded audio)
|
|
12
|
+
* - send_facebook_message: Send a Facebook Messenger message
|
|
10
13
|
* - get_message_status: Get message delivery status
|
|
11
14
|
* - list_channels: List available messaging channels
|
|
12
15
|
* - create_subscription: Create a webhook subscription for events
|
|
16
|
+
* - list_subscriptions: List all webhook subscriptions
|
|
17
|
+
* - delete_subscription: Delete a webhook subscription
|
|
13
18
|
* - list_contacts: List contacts
|
|
19
|
+
* - create_contact: Create a contact in the contact base
|
|
20
|
+
* - delete_contact: Delete a contact
|
|
14
21
|
* - send_template: Send a WhatsApp template message
|
|
22
|
+
* - list_templates: List approved WhatsApp templates
|
|
23
|
+
* - get_report_entries: Get message report entries by date range
|
|
24
|
+
* - add_opt_out: Add a phone number to the opt-out list
|
|
15
25
|
*
|
|
16
26
|
* Environment:
|
|
17
27
|
* ZENVIA_API_TOKEN — API token from https://app.zenvia.com/
|
|
@@ -42,11 +52,13 @@ async function zenviaRequest(method: string, path: string, body?: unknown): Prom
|
|
|
42
52
|
const err = await res.text();
|
|
43
53
|
throw new Error(`Zenvia API ${res.status}: ${err}`);
|
|
44
54
|
}
|
|
45
|
-
|
|
55
|
+
// Some endpoints (DELETE) return empty
|
|
56
|
+
const text = await res.text();
|
|
57
|
+
return text ? JSON.parse(text) : { ok: true };
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
const server = new Server(
|
|
49
|
-
{ name: "mcp-zenvia", version: "0.
|
|
61
|
+
{ name: "mcp-zenvia", version: "0.2.0" },
|
|
50
62
|
{ capabilities: { tools: {} } }
|
|
51
63
|
);
|
|
52
64
|
|
|
@@ -91,6 +103,48 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
91
103
|
required: ["from", "to", "text"],
|
|
92
104
|
},
|
|
93
105
|
},
|
|
106
|
+
{
|
|
107
|
+
name: "send_email",
|
|
108
|
+
description: "Send a transactional email",
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
from: { type: "string", description: "Sender email address (verified domain)" },
|
|
113
|
+
to: { type: "string", description: "Recipient email address" },
|
|
114
|
+
subject: { type: "string", description: "Email subject" },
|
|
115
|
+
html: { type: "string", description: "HTML body of the email" },
|
|
116
|
+
text: { type: "string", description: "Plain text body (fallback)" },
|
|
117
|
+
},
|
|
118
|
+
required: ["from", "to", "subject"],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "send_voice",
|
|
123
|
+
description: "Send a voice message via TTS or pre-recorded audio URL",
|
|
124
|
+
inputSchema: {
|
|
125
|
+
type: "object",
|
|
126
|
+
properties: {
|
|
127
|
+
from: { type: "string", description: "Sender ID (Voice channel)" },
|
|
128
|
+
to: { type: "string", description: "Recipient phone number with country code" },
|
|
129
|
+
text: { type: "string", description: "Text to be spoken (TTS) — use either text or audioUrl" },
|
|
130
|
+
audioUrl: { type: "string", description: "URL of pre-recorded audio file — use either text or audioUrl" },
|
|
131
|
+
},
|
|
132
|
+
required: ["from", "to"],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "send_facebook_message",
|
|
137
|
+
description: "Send a Facebook Messenger message",
|
|
138
|
+
inputSchema: {
|
|
139
|
+
type: "object",
|
|
140
|
+
properties: {
|
|
141
|
+
from: { type: "string", description: "Sender ID (Facebook page)" },
|
|
142
|
+
to: { type: "string", description: "Recipient PSID (page-scoped user ID)" },
|
|
143
|
+
text: { type: "string", description: "Message text" },
|
|
144
|
+
},
|
|
145
|
+
required: ["from", "to", "text"],
|
|
146
|
+
},
|
|
147
|
+
},
|
|
94
148
|
{
|
|
95
149
|
name: "get_message_status",
|
|
96
150
|
description: "Get message delivery status by ID",
|
|
@@ -114,12 +168,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
114
168
|
type: "object",
|
|
115
169
|
properties: {
|
|
116
170
|
url: { type: "string", description: "Webhook URL to receive events" },
|
|
117
|
-
channel: { type: "string", enum: ["sms", "whatsapp", "rcs"], description: "Channel to subscribe to" },
|
|
171
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs", "email", "voice", "facebook"], description: "Channel to subscribe to" },
|
|
118
172
|
eventType: { type: "string", enum: ["MESSAGE", "MESSAGE_STATUS"], description: "Event type" },
|
|
119
173
|
},
|
|
120
174
|
required: ["url", "channel", "eventType"],
|
|
121
175
|
},
|
|
122
176
|
},
|
|
177
|
+
{
|
|
178
|
+
name: "list_subscriptions",
|
|
179
|
+
description: "List all webhook subscriptions",
|
|
180
|
+
inputSchema: { type: "object", properties: {} },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: "delete_subscription",
|
|
184
|
+
description: "Delete a webhook subscription by ID",
|
|
185
|
+
inputSchema: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: {
|
|
188
|
+
id: { type: "string", description: "Subscription ID" },
|
|
189
|
+
},
|
|
190
|
+
required: ["id"],
|
|
191
|
+
},
|
|
192
|
+
},
|
|
123
193
|
{
|
|
124
194
|
name: "list_contacts",
|
|
125
195
|
description: "List contacts from the contact base",
|
|
@@ -131,6 +201,31 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
131
201
|
},
|
|
132
202
|
},
|
|
133
203
|
},
|
|
204
|
+
{
|
|
205
|
+
name: "create_contact",
|
|
206
|
+
description: "Create a contact in the contact base",
|
|
207
|
+
inputSchema: {
|
|
208
|
+
type: "object",
|
|
209
|
+
properties: {
|
|
210
|
+
name: { type: "string", description: "Contact full name" },
|
|
211
|
+
phone: { type: "string", description: "Phone number with country code" },
|
|
212
|
+
email: { type: "string", description: "Email address" },
|
|
213
|
+
groupId: { type: "string", description: "Optional group ID to add the contact to" },
|
|
214
|
+
},
|
|
215
|
+
required: ["name"],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: "delete_contact",
|
|
220
|
+
description: "Delete a contact by ID",
|
|
221
|
+
inputSchema: {
|
|
222
|
+
type: "object",
|
|
223
|
+
properties: {
|
|
224
|
+
id: { type: "string", description: "Contact ID" },
|
|
225
|
+
},
|
|
226
|
+
required: ["id"],
|
|
227
|
+
},
|
|
228
|
+
},
|
|
134
229
|
{
|
|
135
230
|
name: "send_template",
|
|
136
231
|
description: "Send a WhatsApp template message (pre-approved)",
|
|
@@ -148,6 +243,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
148
243
|
required: ["from", "to", "templateId"],
|
|
149
244
|
},
|
|
150
245
|
},
|
|
246
|
+
{
|
|
247
|
+
name: "list_templates",
|
|
248
|
+
description: "List approved message templates (WhatsApp/SMS/RCS)",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs"], description: "Filter templates by channel" },
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
name: "get_report_entries",
|
|
258
|
+
description: "Get message report entries within a date range",
|
|
259
|
+
inputSchema: {
|
|
260
|
+
type: "object",
|
|
261
|
+
properties: {
|
|
262
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs", "email", "voice", "facebook"], description: "Channel to report on" },
|
|
263
|
+
startDate: { type: "string", description: "ISO 8601 start date (e.g. 2026-04-01)" },
|
|
264
|
+
endDate: { type: "string", description: "ISO 8601 end date (e.g. 2026-04-24)" },
|
|
265
|
+
},
|
|
266
|
+
required: ["channel", "startDate", "endDate"],
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "add_opt_out",
|
|
271
|
+
description: "Add a phone number to the opt-out list (suppresses future messages)",
|
|
272
|
+
inputSchema: {
|
|
273
|
+
type: "object",
|
|
274
|
+
properties: {
|
|
275
|
+
channel: { type: "string", enum: ["sms", "whatsapp", "rcs", "voice"], description: "Channel for the opt-out" },
|
|
276
|
+
from: { type: "string", description: "Sender ID the opt-out applies to" },
|
|
277
|
+
phone: { type: "string", description: "Phone number to opt out (with country code)" },
|
|
278
|
+
},
|
|
279
|
+
required: ["channel", "from", "phone"],
|
|
280
|
+
},
|
|
281
|
+
},
|
|
151
282
|
],
|
|
152
283
|
}));
|
|
153
284
|
|
|
@@ -162,20 +293,61 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
162
293
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/whatsapp/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
|
|
163
294
|
case "send_rcs":
|
|
164
295
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/rcs/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
|
|
296
|
+
case "send_email": {
|
|
297
|
+
const contents: any[] = [];
|
|
298
|
+
if (args?.html) contents.push({ type: "email", html: args.html, subject: args?.subject });
|
|
299
|
+
else if (args?.text) contents.push({ type: "email", text: args.text, subject: args?.subject });
|
|
300
|
+
else contents.push({ type: "email", subject: args?.subject });
|
|
301
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/email/messages", { from: args?.from, to: args?.to, contents }), null, 2) }] };
|
|
302
|
+
}
|
|
303
|
+
case "send_voice": {
|
|
304
|
+
const contents: any[] = [];
|
|
305
|
+
if (args?.audioUrl) contents.push({ type: "audio", url: args.audioUrl });
|
|
306
|
+
else if (args?.text) contents.push({ type: "text", text: args.text });
|
|
307
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/voice/messages", { from: args?.from, to: args?.to, contents }), null, 2) }] };
|
|
308
|
+
}
|
|
309
|
+
case "send_facebook_message":
|
|
310
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/facebook/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
|
|
165
311
|
case "get_message_status":
|
|
166
312
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/reports/${args?.id}`), null, 2) }] };
|
|
167
313
|
case "list_channels":
|
|
168
314
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", "/channels"), null, 2) }] };
|
|
169
315
|
case "create_subscription":
|
|
170
316
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/subscriptions", { webhook: { url: args?.url }, criteria: { channel: args?.channel }, eventType: args?.eventType }), null, 2) }] };
|
|
317
|
+
case "list_subscriptions":
|
|
318
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", "/subscriptions"), null, 2) }] };
|
|
319
|
+
case "delete_subscription":
|
|
320
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("DELETE", `/subscriptions/${args?.id}`), null, 2) }] };
|
|
171
321
|
case "list_contacts": {
|
|
172
322
|
const params = new URLSearchParams();
|
|
173
323
|
if (args?.page) params.set("page", String(args.page));
|
|
174
324
|
if (args?.size) params.set("size", String(args.size));
|
|
175
325
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/contacts?${params}`), null, 2) }] };
|
|
176
326
|
}
|
|
327
|
+
case "create_contact": {
|
|
328
|
+
const body: any = { name: args?.name };
|
|
329
|
+
if (args?.phone) body.phone = args.phone;
|
|
330
|
+
if (args?.email) body.email = args.email;
|
|
331
|
+
if (args?.groupId) body.groupId = args.groupId;
|
|
332
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/contacts", body), null, 2) }] };
|
|
333
|
+
}
|
|
334
|
+
case "delete_contact":
|
|
335
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("DELETE", `/contacts/${args?.id}`), null, 2) }] };
|
|
177
336
|
case "send_template":
|
|
178
337
|
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/whatsapp/messages", { from: args?.from, to: args?.to, contents: [{ type: "template", templateId: args?.templateId, fields: args?.fields || {} }] }), null, 2) }] };
|
|
338
|
+
case "list_templates": {
|
|
339
|
+
const params = new URLSearchParams();
|
|
340
|
+
if (args?.channel) params.set("channel", String(args.channel));
|
|
341
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/templates?${params}`), null, 2) }] };
|
|
342
|
+
}
|
|
343
|
+
case "get_report_entries": {
|
|
344
|
+
const params = new URLSearchParams();
|
|
345
|
+
params.set("startDate", String(args?.startDate));
|
|
346
|
+
params.set("endDate", String(args?.endDate));
|
|
347
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/reports/${args?.channel}/entries?${params}`), null, 2) }] };
|
|
348
|
+
}
|
|
349
|
+
case "add_opt_out":
|
|
350
|
+
return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", `/channels/${args?.channel}/senders/${args?.from}/opt-outs`, { phone: args?.phone }), null, 2) }] };
|
|
179
351
|
default:
|
|
180
352
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
181
353
|
}
|
|
@@ -198,7 +370,7 @@ async function main() {
|
|
|
198
370
|
if (!sid && isInitializeRequest(req.body)) {
|
|
199
371
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
200
372
|
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
201
|
-
const s = new Server({ name: "mcp-zenvia", version: "0.
|
|
373
|
+
const s = new Server({ name: "mcp-zenvia", version: "0.2.0" }, { capabilities: { tools: {} } }); (server as any)._requestHandlers.forEach((v: any, k: any) => (s as any)._requestHandlers.set(k, v)); (server as any)._notificationHandlers?.forEach((v: any, k: any) => (s as any)._notificationHandlers.set(k, v)); await s.connect(t);
|
|
202
374
|
await t.handleRequest(req, res, req.body); return;
|
|
203
375
|
}
|
|
204
376
|
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|