@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 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
- return res.json();
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.1.0" }, { capabilities: { tools: {} } });
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.1.0" }, { capabilities: { tools: {} } });
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.1.2",
4
- "description": "MCP server for Zenvia — SMS, WhatsApp, RCS messaging and templates",
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 templates",
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.1.2",
10
+ "version": "0.2.0",
11
11
  "packages": [
12
12
  {
13
13
  "registryType": "npm",
14
14
  "identifier": "@codespar/mcp-zenvia",
15
- "version": "0.1.2",
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
- return res.json();
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.1.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.1.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);
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 });