@codespar/mcp-evolution-api 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,219 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Server for Evolution API — self-hosted WhatsApp API.
4
+ *
5
+ * Tools:
6
+ * - send_text: Send a text message
7
+ * - send_image: Send an image message
8
+ * - send_document: Send a document
9
+ * - get_instances: List all instances
10
+ * - create_instance: Create a new WhatsApp instance
11
+ * - get_qrcode: Get QR code for instance pairing
12
+ * - get_contacts: Get contacts from an instance
13
+ * - send_poll: Send a poll message
14
+ * - get_messages: Get messages from a chat
15
+ * - check_number: Check if a number is on WhatsApp
16
+ *
17
+ * Environment:
18
+ * EVOLUTION_API_URL — Base URL of self-hosted Evolution API
19
+ * EVOLUTION_API_KEY — API key for authentication
20
+ */
21
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
22
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
24
+ const API_URL = process.env.EVOLUTION_API_URL || "";
25
+ const API_KEY = process.env.EVOLUTION_API_KEY || "";
26
+ async function evolutionRequest(method, path, body) {
27
+ const res = await fetch(`${API_URL}${path}`, {
28
+ method,
29
+ headers: {
30
+ "Content-Type": "application/json",
31
+ "apikey": API_KEY,
32
+ },
33
+ body: body ? JSON.stringify(body) : undefined,
34
+ });
35
+ if (!res.ok) {
36
+ const err = await res.text();
37
+ throw new Error(`Evolution API ${res.status}: ${err}`);
38
+ }
39
+ return res.json();
40
+ }
41
+ const server = new Server({ name: "mcp-evolution-api", version: "0.1.0" }, { capabilities: { tools: {} } });
42
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
43
+ tools: [
44
+ {
45
+ name: "send_text",
46
+ description: "Send a text message via WhatsApp",
47
+ inputSchema: {
48
+ type: "object",
49
+ properties: {
50
+ instance: { type: "string", description: "Instance name" },
51
+ number: { type: "string", description: "Phone number with country code (e.g. 5511999999999)" },
52
+ text: { type: "string", description: "Message text" },
53
+ },
54
+ required: ["instance", "number", "text"],
55
+ },
56
+ },
57
+ {
58
+ name: "send_image",
59
+ description: "Send an image message via WhatsApp",
60
+ inputSchema: {
61
+ type: "object",
62
+ properties: {
63
+ instance: { type: "string", description: "Instance name" },
64
+ number: { type: "string", description: "Phone number with country code" },
65
+ mediaUrl: { type: "string", description: "Image URL" },
66
+ caption: { type: "string", description: "Image caption" },
67
+ },
68
+ required: ["instance", "number", "mediaUrl"],
69
+ },
70
+ },
71
+ {
72
+ name: "send_document",
73
+ description: "Send a document via WhatsApp",
74
+ inputSchema: {
75
+ type: "object",
76
+ properties: {
77
+ instance: { type: "string", description: "Instance name" },
78
+ number: { type: "string", description: "Phone number with country code" },
79
+ mediaUrl: { type: "string", description: "Document URL" },
80
+ fileName: { type: "string", description: "File name" },
81
+ caption: { type: "string", description: "Document caption" },
82
+ },
83
+ required: ["instance", "number", "mediaUrl"],
84
+ },
85
+ },
86
+ {
87
+ name: "get_instances",
88
+ description: "List all WhatsApp instances",
89
+ inputSchema: { type: "object", properties: {} },
90
+ },
91
+ {
92
+ name: "create_instance",
93
+ description: "Create a new WhatsApp instance",
94
+ inputSchema: {
95
+ type: "object",
96
+ properties: {
97
+ instanceName: { type: "string", description: "Name for the instance" },
98
+ qrcode: { type: "boolean", description: "Generate QR code on creation (default true)" },
99
+ },
100
+ required: ["instanceName"],
101
+ },
102
+ },
103
+ {
104
+ name: "get_qrcode",
105
+ description: "Get QR code for instance pairing",
106
+ inputSchema: {
107
+ type: "object",
108
+ properties: {
109
+ instance: { type: "string", description: "Instance name" },
110
+ },
111
+ required: ["instance"],
112
+ },
113
+ },
114
+ {
115
+ name: "get_contacts",
116
+ description: "Get contacts from an instance",
117
+ inputSchema: {
118
+ type: "object",
119
+ properties: {
120
+ instance: { type: "string", description: "Instance name" },
121
+ },
122
+ required: ["instance"],
123
+ },
124
+ },
125
+ {
126
+ name: "send_poll",
127
+ description: "Send a poll message via WhatsApp",
128
+ inputSchema: {
129
+ type: "object",
130
+ properties: {
131
+ instance: { type: "string", description: "Instance name" },
132
+ number: { type: "string", description: "Phone number with country code" },
133
+ name: { type: "string", description: "Poll question" },
134
+ options: {
135
+ type: "array",
136
+ items: { type: "string" },
137
+ description: "Poll options (max 12)",
138
+ },
139
+ selectableCount: { type: "number", description: "Max selectable options (0 = unlimited)" },
140
+ },
141
+ required: ["instance", "number", "name", "options"],
142
+ },
143
+ },
144
+ {
145
+ name: "get_messages",
146
+ description: "Get messages from a chat",
147
+ inputSchema: {
148
+ type: "object",
149
+ properties: {
150
+ instance: { type: "string", description: "Instance name" },
151
+ remoteJid: { type: "string", description: "Chat JID (e.g. 5511999999999@s.whatsapp.net)" },
152
+ limit: { type: "number", description: "Number of messages (default 20)" },
153
+ },
154
+ required: ["instance", "remoteJid"],
155
+ },
156
+ },
157
+ {
158
+ name: "check_number",
159
+ description: "Check if a phone number is registered on WhatsApp",
160
+ inputSchema: {
161
+ type: "object",
162
+ properties: {
163
+ instance: { type: "string", description: "Instance name" },
164
+ numbers: {
165
+ type: "array",
166
+ items: { type: "string" },
167
+ description: "Phone numbers to check",
168
+ },
169
+ },
170
+ required: ["instance", "numbers"],
171
+ },
172
+ },
173
+ ],
174
+ }));
175
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
176
+ const { name, arguments: args } = request.params;
177
+ try {
178
+ switch (name) {
179
+ case "send_text":
180
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendText/${args?.instance}`, { number: args?.number, text: args?.text }), null, 2) }] };
181
+ case "send_image":
182
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendMedia/${args?.instance}`, { number: args?.number, mediatype: "image", media: args?.mediaUrl, caption: args?.caption }), null, 2) }] };
183
+ case "send_document":
184
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendMedia/${args?.instance}`, { number: args?.number, mediatype: "document", media: args?.mediaUrl, fileName: args?.fileName, caption: args?.caption }), null, 2) }] };
185
+ case "get_instances":
186
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", "/instance/fetchInstances"), null, 2) }] };
187
+ case "create_instance":
188
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", "/instance/create", args), null, 2) }] };
189
+ case "get_qrcode":
190
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/instance/connect/${args?.instance}`), null, 2) }] };
191
+ case "get_contacts":
192
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/chat/contacts/${args?.instance}`), null, 2) }] };
193
+ case "send_poll":
194
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendPoll/${args?.instance}`, { number: args?.number, name: args?.name, values: args?.options, selectableCount: args?.selectableCount ?? 0 }), null, 2) }] };
195
+ case "get_messages": {
196
+ const body = { where: { key: { remoteJid: args?.remoteJid } } };
197
+ if (args?.limit)
198
+ body.limit = args.limit;
199
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/findMessages/${args?.instance}`, body), null, 2) }] };
200
+ }
201
+ case "check_number":
202
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/whatsappNumbers/${args?.instance}`, { numbers: args?.numbers }), null, 2) }] };
203
+ default:
204
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
205
+ }
206
+ }
207
+ catch (err) {
208
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
209
+ }
210
+ });
211
+ async function main() {
212
+ if (!API_URL || !API_KEY) {
213
+ console.error("EVOLUTION_API_URL and EVOLUTION_API_KEY environment variables are required");
214
+ process.exit(1);
215
+ }
216
+ const transport = new StdioServerTransport();
217
+ await server.connect(transport);
218
+ }
219
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@codespar/mcp-evolution-api",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Evolution API — WhatsApp messaging, instances, contacts",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "mcp-evolution-api": "./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
+ "evolution-api",
25
+ "whatsapp",
26
+ "messaging",
27
+ "brazil"
28
+ ]
29
+ }
package/src/index.ts ADDED
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server for Evolution API — self-hosted WhatsApp API.
5
+ *
6
+ * Tools:
7
+ * - send_text: Send a text message
8
+ * - send_image: Send an image message
9
+ * - send_document: Send a document
10
+ * - get_instances: List all instances
11
+ * - create_instance: Create a new WhatsApp instance
12
+ * - get_qrcode: Get QR code for instance pairing
13
+ * - get_contacts: Get contacts from an instance
14
+ * - send_poll: Send a poll message
15
+ * - get_messages: Get messages from a chat
16
+ * - check_number: Check if a number is on WhatsApp
17
+ *
18
+ * Environment:
19
+ * EVOLUTION_API_URL — Base URL of self-hosted Evolution API
20
+ * EVOLUTION_API_KEY — API key for authentication
21
+ */
22
+
23
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
24
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
25
+ import {
26
+ CallToolRequestSchema,
27
+ ListToolsRequestSchema,
28
+ } from "@modelcontextprotocol/sdk/types.js";
29
+
30
+ const API_URL = process.env.EVOLUTION_API_URL || "";
31
+ const API_KEY = process.env.EVOLUTION_API_KEY || "";
32
+
33
+ async function evolutionRequest(method: string, path: string, body?: unknown): Promise<unknown> {
34
+ const res = await fetch(`${API_URL}${path}`, {
35
+ method,
36
+ headers: {
37
+ "Content-Type": "application/json",
38
+ "apikey": API_KEY,
39
+ },
40
+ body: body ? JSON.stringify(body) : undefined,
41
+ });
42
+ if (!res.ok) {
43
+ const err = await res.text();
44
+ throw new Error(`Evolution API ${res.status}: ${err}`);
45
+ }
46
+ return res.json();
47
+ }
48
+
49
+ const server = new Server(
50
+ { name: "mcp-evolution-api", version: "0.1.0" },
51
+ { capabilities: { tools: {} } }
52
+ );
53
+
54
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
55
+ tools: [
56
+ {
57
+ name: "send_text",
58
+ description: "Send a text message via WhatsApp",
59
+ inputSchema: {
60
+ type: "object",
61
+ properties: {
62
+ instance: { type: "string", description: "Instance name" },
63
+ number: { type: "string", description: "Phone number with country code (e.g. 5511999999999)" },
64
+ text: { type: "string", description: "Message text" },
65
+ },
66
+ required: ["instance", "number", "text"],
67
+ },
68
+ },
69
+ {
70
+ name: "send_image",
71
+ description: "Send an image message via WhatsApp",
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: {
75
+ instance: { type: "string", description: "Instance name" },
76
+ number: { type: "string", description: "Phone number with country code" },
77
+ mediaUrl: { type: "string", description: "Image URL" },
78
+ caption: { type: "string", description: "Image caption" },
79
+ },
80
+ required: ["instance", "number", "mediaUrl"],
81
+ },
82
+ },
83
+ {
84
+ name: "send_document",
85
+ description: "Send a document via WhatsApp",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ instance: { type: "string", description: "Instance name" },
90
+ number: { type: "string", description: "Phone number with country code" },
91
+ mediaUrl: { type: "string", description: "Document URL" },
92
+ fileName: { type: "string", description: "File name" },
93
+ caption: { type: "string", description: "Document caption" },
94
+ },
95
+ required: ["instance", "number", "mediaUrl"],
96
+ },
97
+ },
98
+ {
99
+ name: "get_instances",
100
+ description: "List all WhatsApp instances",
101
+ inputSchema: { type: "object", properties: {} },
102
+ },
103
+ {
104
+ name: "create_instance",
105
+ description: "Create a new WhatsApp instance",
106
+ inputSchema: {
107
+ type: "object",
108
+ properties: {
109
+ instanceName: { type: "string", description: "Name for the instance" },
110
+ qrcode: { type: "boolean", description: "Generate QR code on creation (default true)" },
111
+ },
112
+ required: ["instanceName"],
113
+ },
114
+ },
115
+ {
116
+ name: "get_qrcode",
117
+ description: "Get QR code for instance pairing",
118
+ inputSchema: {
119
+ type: "object",
120
+ properties: {
121
+ instance: { type: "string", description: "Instance name" },
122
+ },
123
+ required: ["instance"],
124
+ },
125
+ },
126
+ {
127
+ name: "get_contacts",
128
+ description: "Get contacts from an instance",
129
+ inputSchema: {
130
+ type: "object",
131
+ properties: {
132
+ instance: { type: "string", description: "Instance name" },
133
+ },
134
+ required: ["instance"],
135
+ },
136
+ },
137
+ {
138
+ name: "send_poll",
139
+ description: "Send a poll message via WhatsApp",
140
+ inputSchema: {
141
+ type: "object",
142
+ properties: {
143
+ instance: { type: "string", description: "Instance name" },
144
+ number: { type: "string", description: "Phone number with country code" },
145
+ name: { type: "string", description: "Poll question" },
146
+ options: {
147
+ type: "array",
148
+ items: { type: "string" },
149
+ description: "Poll options (max 12)",
150
+ },
151
+ selectableCount: { type: "number", description: "Max selectable options (0 = unlimited)" },
152
+ },
153
+ required: ["instance", "number", "name", "options"],
154
+ },
155
+ },
156
+ {
157
+ name: "get_messages",
158
+ description: "Get messages from a chat",
159
+ inputSchema: {
160
+ type: "object",
161
+ properties: {
162
+ instance: { type: "string", description: "Instance name" },
163
+ remoteJid: { type: "string", description: "Chat JID (e.g. 5511999999999@s.whatsapp.net)" },
164
+ limit: { type: "number", description: "Number of messages (default 20)" },
165
+ },
166
+ required: ["instance", "remoteJid"],
167
+ },
168
+ },
169
+ {
170
+ name: "check_number",
171
+ description: "Check if a phone number is registered on WhatsApp",
172
+ inputSchema: {
173
+ type: "object",
174
+ properties: {
175
+ instance: { type: "string", description: "Instance name" },
176
+ numbers: {
177
+ type: "array",
178
+ items: { type: "string" },
179
+ description: "Phone numbers to check",
180
+ },
181
+ },
182
+ required: ["instance", "numbers"],
183
+ },
184
+ },
185
+ ],
186
+ }));
187
+
188
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
189
+ const { name, arguments: args } = request.params;
190
+
191
+ try {
192
+ switch (name) {
193
+ case "send_text":
194
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendText/${args?.instance}`, { number: args?.number, text: args?.text }), null, 2) }] };
195
+ case "send_image":
196
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendMedia/${args?.instance}`, { number: args?.number, mediatype: "image", media: args?.mediaUrl, caption: args?.caption }), null, 2) }] };
197
+ case "send_document":
198
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendMedia/${args?.instance}`, { number: args?.number, mediatype: "document", media: args?.mediaUrl, fileName: args?.fileName, caption: args?.caption }), null, 2) }] };
199
+ case "get_instances":
200
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", "/instance/fetchInstances"), null, 2) }] };
201
+ case "create_instance":
202
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", "/instance/create", args), null, 2) }] };
203
+ case "get_qrcode":
204
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/instance/connect/${args?.instance}`), null, 2) }] };
205
+ case "get_contacts":
206
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/chat/contacts/${args?.instance}`), null, 2) }] };
207
+ case "send_poll":
208
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/message/sendPoll/${args?.instance}`, { number: args?.number, name: args?.name, values: args?.options, selectableCount: args?.selectableCount ?? 0 }), null, 2) }] };
209
+ case "get_messages": {
210
+ const body: Record<string, unknown> = { where: { key: { remoteJid: args?.remoteJid } } };
211
+ if (args?.limit) body.limit = args.limit;
212
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/findMessages/${args?.instance}`, body), null, 2) }] };
213
+ }
214
+ case "check_number":
215
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/whatsappNumbers/${args?.instance}`, { numbers: args?.numbers }), null, 2) }] };
216
+ default:
217
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
218
+ }
219
+ } catch (err) {
220
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
221
+ }
222
+ });
223
+
224
+ async function main() {
225
+ if (!API_URL || !API_KEY) {
226
+ console.error("EVOLUTION_API_URL and EVOLUTION_API_KEY environment variables are required");
227
+ process.exit(1);
228
+ }
229
+ const transport = new StdioServerTransport();
230
+ await server.connect(transport);
231
+ }
232
+
233
+ 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
+ }