@codespar/mcp-zenvia 0.1.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 ADDED
@@ -0,0 +1,181 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Server for Zenvia — multi-channel messaging (SMS, WhatsApp, RCS).
4
+ *
5
+ * Tools:
6
+ * - send_sms: Send an SMS message
7
+ * - send_whatsapp: Send a WhatsApp message
8
+ * - send_rcs: Send an RCS message
9
+ * - get_message_status: Get message delivery status
10
+ * - list_channels: List available messaging channels
11
+ * - create_subscription: Create a webhook subscription for events
12
+ * - list_contacts: List contacts
13
+ * - send_template: Send a WhatsApp template message
14
+ *
15
+ * Environment:
16
+ * ZENVIA_API_TOKEN — API token from https://app.zenvia.com/
17
+ */
18
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
19
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
20
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
21
+ const API_TOKEN = process.env.ZENVIA_API_TOKEN || "";
22
+ const BASE_URL = "https://api.zenvia.com/v2";
23
+ async function zenviaRequest(method, path, body) {
24
+ const res = await fetch(`${BASE_URL}${path}`, {
25
+ method,
26
+ headers: {
27
+ "Content-Type": "application/json",
28
+ "X-API-TOKEN": API_TOKEN,
29
+ },
30
+ body: body ? JSON.stringify(body) : undefined,
31
+ });
32
+ if (!res.ok) {
33
+ const err = await res.text();
34
+ throw new Error(`Zenvia API ${res.status}: ${err}`);
35
+ }
36
+ return res.json();
37
+ }
38
+ const server = new Server({ name: "mcp-zenvia", version: "0.1.0" }, { capabilities: { tools: {} } });
39
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
40
+ tools: [
41
+ {
42
+ name: "send_sms",
43
+ description: "Send an SMS message",
44
+ inputSchema: {
45
+ type: "object",
46
+ properties: {
47
+ from: { type: "string", description: "Sender ID" },
48
+ to: { type: "string", description: "Recipient phone number with country code (e.g. 5511999999999)" },
49
+ text: { type: "string", description: "Message text" },
50
+ },
51
+ required: ["from", "to", "text"],
52
+ },
53
+ },
54
+ {
55
+ name: "send_whatsapp",
56
+ description: "Send a WhatsApp message",
57
+ inputSchema: {
58
+ type: "object",
59
+ properties: {
60
+ from: { type: "string", description: "Sender ID (WhatsApp channel)" },
61
+ to: { type: "string", description: "Recipient phone number with country code" },
62
+ text: { type: "string", description: "Message text" },
63
+ },
64
+ required: ["from", "to", "text"],
65
+ },
66
+ },
67
+ {
68
+ name: "send_rcs",
69
+ description: "Send an RCS (Rich Communication Services) message",
70
+ inputSchema: {
71
+ type: "object",
72
+ properties: {
73
+ from: { type: "string", description: "Sender ID (RCS channel)" },
74
+ to: { type: "string", description: "Recipient phone number with country code" },
75
+ text: { type: "string", description: "Message text" },
76
+ },
77
+ required: ["from", "to", "text"],
78
+ },
79
+ },
80
+ {
81
+ name: "get_message_status",
82
+ description: "Get message delivery status by ID",
83
+ inputSchema: {
84
+ type: "object",
85
+ properties: {
86
+ id: { type: "string", description: "Message ID" },
87
+ },
88
+ required: ["id"],
89
+ },
90
+ },
91
+ {
92
+ name: "list_channels",
93
+ description: "List available messaging channels",
94
+ inputSchema: { type: "object", properties: {} },
95
+ },
96
+ {
97
+ name: "create_subscription",
98
+ description: "Create a webhook subscription for message events",
99
+ inputSchema: {
100
+ type: "object",
101
+ properties: {
102
+ url: { type: "string", description: "Webhook URL to receive events" },
103
+ channel: { type: "string", enum: ["sms", "whatsapp", "rcs"], description: "Channel to subscribe to" },
104
+ eventType: { type: "string", enum: ["MESSAGE", "MESSAGE_STATUS"], description: "Event type" },
105
+ },
106
+ required: ["url", "channel", "eventType"],
107
+ },
108
+ },
109
+ {
110
+ name: "list_contacts",
111
+ description: "List contacts from the contact base",
112
+ inputSchema: {
113
+ type: "object",
114
+ properties: {
115
+ page: { type: "number", description: "Page number" },
116
+ size: { type: "number", description: "Page size" },
117
+ },
118
+ },
119
+ },
120
+ {
121
+ name: "send_template",
122
+ description: "Send a WhatsApp template message (pre-approved)",
123
+ inputSchema: {
124
+ type: "object",
125
+ properties: {
126
+ from: { type: "string", description: "Sender ID (WhatsApp channel)" },
127
+ to: { type: "string", description: "Recipient phone number with country code" },
128
+ templateId: { type: "string", description: "Approved template ID" },
129
+ fields: {
130
+ type: "object",
131
+ description: "Template variable values (key-value map)",
132
+ },
133
+ },
134
+ required: ["from", "to", "templateId"],
135
+ },
136
+ },
137
+ ],
138
+ }));
139
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
140
+ const { name, arguments: args } = request.params;
141
+ try {
142
+ switch (name) {
143
+ case "send_sms":
144
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/sms/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
145
+ case "send_whatsapp":
146
+ 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) }] };
147
+ case "send_rcs":
148
+ 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) }] };
149
+ case "get_message_status":
150
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/reports/${args?.id}`), null, 2) }] };
151
+ case "list_channels":
152
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", "/channels"), null, 2) }] };
153
+ case "create_subscription":
154
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/subscriptions", { webhook: { url: args?.url }, criteria: { channel: args?.channel }, eventType: args?.eventType }), null, 2) }] };
155
+ case "list_contacts": {
156
+ const params = new URLSearchParams();
157
+ if (args?.page)
158
+ params.set("page", String(args.page));
159
+ if (args?.size)
160
+ params.set("size", String(args.size));
161
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/contacts?${params}`), null, 2) }] };
162
+ }
163
+ case "send_template":
164
+ 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) }] };
165
+ default:
166
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
167
+ }
168
+ }
169
+ catch (err) {
170
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
171
+ }
172
+ });
173
+ async function main() {
174
+ if (!API_TOKEN) {
175
+ console.error("ZENVIA_API_TOKEN environment variable is required");
176
+ process.exit(1);
177
+ }
178
+ const transport = new StdioServerTransport();
179
+ await server.connect(transport);
180
+ }
181
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@codespar/mcp-zenvia",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Zenvia — SMS, WhatsApp, RCS messaging and templates",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "mcp-zenvia": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^25.5.0",
19
+ "typescript": "^5.8.0"
20
+ },
21
+ "license": "MIT",
22
+ "keywords": [
23
+ "mcp",
24
+ "zenvia",
25
+ "sms",
26
+ "whatsapp",
27
+ "rcs",
28
+ "brazil"
29
+ ]
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server for Zenvia — multi-channel messaging (SMS, WhatsApp, RCS).
5
+ *
6
+ * Tools:
7
+ * - send_sms: Send an SMS message
8
+ * - send_whatsapp: Send a WhatsApp message
9
+ * - send_rcs: Send an RCS message
10
+ * - get_message_status: Get message delivery status
11
+ * - list_channels: List available messaging channels
12
+ * - create_subscription: Create a webhook subscription for events
13
+ * - list_contacts: List contacts
14
+ * - send_template: Send a WhatsApp template message
15
+ *
16
+ * Environment:
17
+ * ZENVIA_API_TOKEN — API token from https://app.zenvia.com/
18
+ */
19
+
20
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
21
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
22
+ import {
23
+ CallToolRequestSchema,
24
+ ListToolsRequestSchema,
25
+ } from "@modelcontextprotocol/sdk/types.js";
26
+
27
+ const API_TOKEN = process.env.ZENVIA_API_TOKEN || "";
28
+ const BASE_URL = "https://api.zenvia.com/v2";
29
+
30
+ async function zenviaRequest(method: string, path: string, body?: unknown): Promise<unknown> {
31
+ const res = await fetch(`${BASE_URL}${path}`, {
32
+ method,
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ "X-API-TOKEN": API_TOKEN,
36
+ },
37
+ body: body ? JSON.stringify(body) : undefined,
38
+ });
39
+ if (!res.ok) {
40
+ const err = await res.text();
41
+ throw new Error(`Zenvia API ${res.status}: ${err}`);
42
+ }
43
+ return res.json();
44
+ }
45
+
46
+ const server = new Server(
47
+ { name: "mcp-zenvia", version: "0.1.0" },
48
+ { capabilities: { tools: {} } }
49
+ );
50
+
51
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
52
+ tools: [
53
+ {
54
+ name: "send_sms",
55
+ description: "Send an SMS message",
56
+ inputSchema: {
57
+ type: "object",
58
+ properties: {
59
+ from: { type: "string", description: "Sender ID" },
60
+ to: { type: "string", description: "Recipient phone number with country code (e.g. 5511999999999)" },
61
+ text: { type: "string", description: "Message text" },
62
+ },
63
+ required: ["from", "to", "text"],
64
+ },
65
+ },
66
+ {
67
+ name: "send_whatsapp",
68
+ description: "Send a WhatsApp message",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ from: { type: "string", description: "Sender ID (WhatsApp channel)" },
73
+ to: { type: "string", description: "Recipient phone number with country code" },
74
+ text: { type: "string", description: "Message text" },
75
+ },
76
+ required: ["from", "to", "text"],
77
+ },
78
+ },
79
+ {
80
+ name: "send_rcs",
81
+ description: "Send an RCS (Rich Communication Services) message",
82
+ inputSchema: {
83
+ type: "object",
84
+ properties: {
85
+ from: { type: "string", description: "Sender ID (RCS channel)" },
86
+ to: { type: "string", description: "Recipient phone number with country code" },
87
+ text: { type: "string", description: "Message text" },
88
+ },
89
+ required: ["from", "to", "text"],
90
+ },
91
+ },
92
+ {
93
+ name: "get_message_status",
94
+ description: "Get message delivery status by ID",
95
+ inputSchema: {
96
+ type: "object",
97
+ properties: {
98
+ id: { type: "string", description: "Message ID" },
99
+ },
100
+ required: ["id"],
101
+ },
102
+ },
103
+ {
104
+ name: "list_channels",
105
+ description: "List available messaging channels",
106
+ inputSchema: { type: "object", properties: {} },
107
+ },
108
+ {
109
+ name: "create_subscription",
110
+ description: "Create a webhook subscription for message events",
111
+ inputSchema: {
112
+ type: "object",
113
+ properties: {
114
+ url: { type: "string", description: "Webhook URL to receive events" },
115
+ channel: { type: "string", enum: ["sms", "whatsapp", "rcs"], description: "Channel to subscribe to" },
116
+ eventType: { type: "string", enum: ["MESSAGE", "MESSAGE_STATUS"], description: "Event type" },
117
+ },
118
+ required: ["url", "channel", "eventType"],
119
+ },
120
+ },
121
+ {
122
+ name: "list_contacts",
123
+ description: "List contacts from the contact base",
124
+ inputSchema: {
125
+ type: "object",
126
+ properties: {
127
+ page: { type: "number", description: "Page number" },
128
+ size: { type: "number", description: "Page size" },
129
+ },
130
+ },
131
+ },
132
+ {
133
+ name: "send_template",
134
+ description: "Send a WhatsApp template message (pre-approved)",
135
+ inputSchema: {
136
+ type: "object",
137
+ properties: {
138
+ from: { type: "string", description: "Sender ID (WhatsApp channel)" },
139
+ to: { type: "string", description: "Recipient phone number with country code" },
140
+ templateId: { type: "string", description: "Approved template ID" },
141
+ fields: {
142
+ type: "object",
143
+ description: "Template variable values (key-value map)",
144
+ },
145
+ },
146
+ required: ["from", "to", "templateId"],
147
+ },
148
+ },
149
+ ],
150
+ }));
151
+
152
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
153
+ const { name, arguments: args } = request.params;
154
+
155
+ try {
156
+ switch (name) {
157
+ case "send_sms":
158
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/channels/sms/messages", { from: args?.from, to: args?.to, contents: [{ type: "text", text: args?.text }] }), null, 2) }] };
159
+ case "send_whatsapp":
160
+ 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) }] };
161
+ case "send_rcs":
162
+ 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) }] };
163
+ case "get_message_status":
164
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/reports/${args?.id}`), null, 2) }] };
165
+ case "list_channels":
166
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", "/channels"), null, 2) }] };
167
+ case "create_subscription":
168
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("POST", "/subscriptions", { webhook: { url: args?.url }, criteria: { channel: args?.channel }, eventType: args?.eventType }), null, 2) }] };
169
+ case "list_contacts": {
170
+ const params = new URLSearchParams();
171
+ if (args?.page) params.set("page", String(args.page));
172
+ if (args?.size) params.set("size", String(args.size));
173
+ return { content: [{ type: "text", text: JSON.stringify(await zenviaRequest("GET", `/contacts?${params}`), null, 2) }] };
174
+ }
175
+ case "send_template":
176
+ 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) }] };
177
+ default:
178
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
179
+ }
180
+ } catch (err) {
181
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
182
+ }
183
+ });
184
+
185
+ async function main() {
186
+ if (!API_TOKEN) {
187
+ console.error("ZENVIA_API_TOKEN environment variable is required");
188
+ process.exit(1);
189
+ }
190
+ const transport = new StdioServerTransport();
191
+ await server.connect(transport);
192
+ }
193
+
194
+ main().catch(console.error);
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "declaration": true
11
+ },
12
+ "include": ["src"]
13
+ }