@codespar/mcp-evolution-api 0.1.0 → 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/README.md +120 -0
- package/dist/index.js +335 -6
- package/package.json +3 -2
- package/server.json +30 -0
- package/src/__tests__/index.test.ts +53 -0
- package/src/index.ts +312 -6
- package/tsconfig.json +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# @codespar/mcp-evolution-api
|
|
2
|
+
|
|
3
|
+
> MCP server for **Evolution API** — self-hosted WhatsApp messaging API
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@codespar/mcp-evolution-api)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
### Claude Desktop
|
|
11
|
+
|
|
12
|
+
Add to `~/.config/claude/claude_desktop_config.json`:
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"mcpServers": {
|
|
17
|
+
"evolution-api": {
|
|
18
|
+
"command": "npx",
|
|
19
|
+
"args": ["-y", "@codespar/mcp-evolution-api"],
|
|
20
|
+
"env": {
|
|
21
|
+
"EVOLUTION_API_URL": "https://your-instance.example.com",
|
|
22
|
+
"EVOLUTION_API_KEY": "your-key"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Claude Code
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
claude mcp add evolution-api -- npx @codespar/mcp-evolution-api
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Cursor / VS Code
|
|
36
|
+
|
|
37
|
+
Add to `.cursor/mcp.json` or `.vscode/mcp.json`:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"servers": {
|
|
42
|
+
"evolution-api": {
|
|
43
|
+
"command": "npx",
|
|
44
|
+
"args": ["-y", "@codespar/mcp-evolution-api"],
|
|
45
|
+
"env": {
|
|
46
|
+
"EVOLUTION_API_URL": "https://your-instance.example.com",
|
|
47
|
+
"EVOLUTION_API_KEY": "your-key"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Tools
|
|
55
|
+
|
|
56
|
+
| Tool | Description |
|
|
57
|
+
|------|-------------|
|
|
58
|
+
| `send_text` | Send a text message via WhatsApp |
|
|
59
|
+
| `send_image` | Send an image message via WhatsApp |
|
|
60
|
+
| `send_document` | Send a document via WhatsApp |
|
|
61
|
+
| `get_instances` | List all WhatsApp instances |
|
|
62
|
+
| `create_instance` | Create a new WhatsApp instance |
|
|
63
|
+
| `get_qrcode` | Get QR code for instance pairing |
|
|
64
|
+
| `get_contacts` | Get contacts from an instance |
|
|
65
|
+
| `send_poll` | Send a poll message via WhatsApp |
|
|
66
|
+
| `get_messages` | Get messages from a chat |
|
|
67
|
+
| `check_number` | Check if a phone number is registered on WhatsApp |
|
|
68
|
+
|
|
69
|
+
## Authentication
|
|
70
|
+
|
|
71
|
+
Evolution API uses an API key passed via the `apikey` header.
|
|
72
|
+
|
|
73
|
+
## Sandbox / Testing
|
|
74
|
+
|
|
75
|
+
Evolution API is self-hosted. Deploy your own instance using Docker for testing.
|
|
76
|
+
|
|
77
|
+
### Get your credentials
|
|
78
|
+
|
|
79
|
+
1. Go to [Evolution API Documentation](https://doc.evolution-api.com)
|
|
80
|
+
2. Deploy your own instance (Docker recommended)
|
|
81
|
+
3. Get the API key from your instance configuration
|
|
82
|
+
4. Set the environment variables
|
|
83
|
+
|
|
84
|
+
## Environment Variables
|
|
85
|
+
|
|
86
|
+
| Variable | Required | Description |
|
|
87
|
+
|----------|----------|-------------|
|
|
88
|
+
| `EVOLUTION_API_URL` | Yes | Base URL of your Evolution API instance |
|
|
89
|
+
| `EVOLUTION_API_KEY` | Yes | API key for authentication |
|
|
90
|
+
|
|
91
|
+
## Roadmap
|
|
92
|
+
|
|
93
|
+
### v0.2 (planned)
|
|
94
|
+
- `create_group` — Create a WhatsApp group
|
|
95
|
+
- `get_group_info` — Get group details and participants
|
|
96
|
+
- `update_profile` — Update instance profile (name, photo, status)
|
|
97
|
+
- `set_presence` — Set online/offline presence status
|
|
98
|
+
- `get_chat_history` — Get full chat history with a contact
|
|
99
|
+
|
|
100
|
+
### v0.3 (planned)
|
|
101
|
+
- `bulk_send` — Send messages to multiple contacts
|
|
102
|
+
- `template_messages` — Send WhatsApp Business template messages
|
|
103
|
+
- `label_management` — Create, update, and assign labels to chats
|
|
104
|
+
|
|
105
|
+
Want to contribute? [Open a PR](https://github.com/codespar/mcp-dev-brasil) or [request a tool](https://github.com/codespar/mcp-dev-brasil/issues).
|
|
106
|
+
|
|
107
|
+
## Links
|
|
108
|
+
|
|
109
|
+
- [Evolution API Documentation](https://doc.evolution-api.com)
|
|
110
|
+
- [Evolution API GitHub](https://github.com/EvolutionAPI/evolution-api)
|
|
111
|
+
- [MCP Dev Brasil](https://github.com/codespar/mcp-dev-brasil)
|
|
112
|
+
- [Landing Page](https://codespar.dev/mcp)
|
|
113
|
+
|
|
114
|
+
## Enterprise
|
|
115
|
+
|
|
116
|
+
Need governance, budget limits, and audit trails for agent payments? [CodeSpar Enterprise](https://codespar.dev/enterprise) adds policy engine, payment routing, and compliance templates on top of these MCP servers.
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,21 @@
|
|
|
13
13
|
* - send_poll: Send a poll message
|
|
14
14
|
* - get_messages: Get messages from a chat
|
|
15
15
|
* - check_number: Check if a number is on WhatsApp
|
|
16
|
+
* - create_group: Create a WhatsApp group
|
|
17
|
+
* - get_group_info: Get group metadata and participants
|
|
18
|
+
* - update_profile: Update instance profile (name, picture, status)
|
|
19
|
+
* - set_presence: Set online/offline presence for an instance
|
|
20
|
+
* - get_chat_history: Get full chat history with pagination
|
|
21
|
+
* - logout_instance: Logout an instance (disconnects WhatsApp session)
|
|
22
|
+
* - restart_instance: Restart an instance
|
|
23
|
+
* - delete_instance: Delete an instance permanently
|
|
24
|
+
* - connection_state: Get connection state of an instance
|
|
25
|
+
* - leave_group: Leave a WhatsApp group
|
|
26
|
+
* - update_group_participants: Add/remove/promote/demote participants in a group
|
|
27
|
+
* - fetch_group_invite_code: Fetch invite code/link for a group
|
|
28
|
+
* - mark_message_as_read: Mark messages in a chat as read
|
|
29
|
+
* - archive_chat: Archive or unarchive a chat
|
|
30
|
+
* - delete_message: Delete a message (for me or for everyone)
|
|
16
31
|
*
|
|
17
32
|
* Environment:
|
|
18
33
|
* EVOLUTION_API_URL — Base URL of self-hosted Evolution API
|
|
@@ -20,6 +35,8 @@
|
|
|
20
35
|
*/
|
|
21
36
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
22
37
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
38
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
39
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
23
40
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
24
41
|
const API_URL = process.env.EVOLUTION_API_URL || "";
|
|
25
42
|
const API_KEY = process.env.EVOLUTION_API_KEY || "";
|
|
@@ -38,7 +55,7 @@ async function evolutionRequest(method, path, body) {
|
|
|
38
55
|
}
|
|
39
56
|
return res.json();
|
|
40
57
|
}
|
|
41
|
-
const server = new Server({ name: "mcp-evolution-api", version: "0.
|
|
58
|
+
const server = new Server({ name: "mcp-evolution-api", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
42
59
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
43
60
|
tools: [
|
|
44
61
|
{
|
|
@@ -170,6 +187,220 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
170
187
|
required: ["instance", "numbers"],
|
|
171
188
|
},
|
|
172
189
|
},
|
|
190
|
+
{
|
|
191
|
+
name: "create_group",
|
|
192
|
+
description: "Create a WhatsApp group",
|
|
193
|
+
inputSchema: {
|
|
194
|
+
type: "object",
|
|
195
|
+
properties: {
|
|
196
|
+
instance: { type: "string", description: "Instance name" },
|
|
197
|
+
subject: { type: "string", description: "Group name/subject" },
|
|
198
|
+
participants: {
|
|
199
|
+
type: "array",
|
|
200
|
+
items: { type: "string" },
|
|
201
|
+
description: "Array of phone numbers to add (with country code)",
|
|
202
|
+
},
|
|
203
|
+
description: { type: "string", description: "Group description" },
|
|
204
|
+
},
|
|
205
|
+
required: ["instance", "subject", "participants"],
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "get_group_info",
|
|
210
|
+
description: "Get group metadata, participants, and settings",
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
instance: { type: "string", description: "Instance name" },
|
|
215
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
216
|
+
},
|
|
217
|
+
required: ["instance", "groupJid"],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "update_profile",
|
|
222
|
+
description: "Update instance profile (name, status text, or picture)",
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: "object",
|
|
225
|
+
properties: {
|
|
226
|
+
instance: { type: "string", description: "Instance name" },
|
|
227
|
+
name: { type: "string", description: "New profile name" },
|
|
228
|
+
status: { type: "string", description: "New status text" },
|
|
229
|
+
picture: { type: "string", description: "URL of profile picture" },
|
|
230
|
+
},
|
|
231
|
+
required: ["instance"],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
name: "set_presence",
|
|
236
|
+
description: "Set online/offline presence for an instance",
|
|
237
|
+
inputSchema: {
|
|
238
|
+
type: "object",
|
|
239
|
+
properties: {
|
|
240
|
+
instance: { type: "string", description: "Instance name" },
|
|
241
|
+
presence: { type: "string", enum: ["available", "unavailable", "composing", "recording", "paused"], description: "Presence state" },
|
|
242
|
+
number: { type: "string", description: "Target number (required for composing/recording)" },
|
|
243
|
+
},
|
|
244
|
+
required: ["instance", "presence"],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: "get_chat_history",
|
|
249
|
+
description: "Get full chat history with pagination support",
|
|
250
|
+
inputSchema: {
|
|
251
|
+
type: "object",
|
|
252
|
+
properties: {
|
|
253
|
+
instance: { type: "string", description: "Instance name" },
|
|
254
|
+
remoteJid: { type: "string", description: "Chat JID (e.g. 5511999999999@s.whatsapp.net)" },
|
|
255
|
+
limit: { type: "number", description: "Number of messages (default 50)" },
|
|
256
|
+
offset: { type: "number", description: "Pagination offset (message index)" },
|
|
257
|
+
fromMe: { type: "boolean", description: "Filter only sent messages" },
|
|
258
|
+
},
|
|
259
|
+
required: ["instance", "remoteJid"],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: "logout_instance",
|
|
264
|
+
description: "Logout an instance (disconnects the WhatsApp session without deleting the instance)",
|
|
265
|
+
inputSchema: {
|
|
266
|
+
type: "object",
|
|
267
|
+
properties: {
|
|
268
|
+
instance: { type: "string", description: "Instance name" },
|
|
269
|
+
},
|
|
270
|
+
required: ["instance"],
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: "restart_instance",
|
|
275
|
+
description: "Restart an instance",
|
|
276
|
+
inputSchema: {
|
|
277
|
+
type: "object",
|
|
278
|
+
properties: {
|
|
279
|
+
instance: { type: "string", description: "Instance name" },
|
|
280
|
+
},
|
|
281
|
+
required: ["instance"],
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: "delete_instance",
|
|
286
|
+
description: "Delete an instance permanently",
|
|
287
|
+
inputSchema: {
|
|
288
|
+
type: "object",
|
|
289
|
+
properties: {
|
|
290
|
+
instance: { type: "string", description: "Instance name" },
|
|
291
|
+
},
|
|
292
|
+
required: ["instance"],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: "connection_state",
|
|
297
|
+
description: "Get the connection state of an instance (open, connecting, close)",
|
|
298
|
+
inputSchema: {
|
|
299
|
+
type: "object",
|
|
300
|
+
properties: {
|
|
301
|
+
instance: { type: "string", description: "Instance name" },
|
|
302
|
+
},
|
|
303
|
+
required: ["instance"],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: "leave_group",
|
|
308
|
+
description: "Leave a WhatsApp group",
|
|
309
|
+
inputSchema: {
|
|
310
|
+
type: "object",
|
|
311
|
+
properties: {
|
|
312
|
+
instance: { type: "string", description: "Instance name" },
|
|
313
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
314
|
+
},
|
|
315
|
+
required: ["instance", "groupJid"],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: "update_group_participants",
|
|
320
|
+
description: "Add, remove, promote, or demote participants in a WhatsApp group",
|
|
321
|
+
inputSchema: {
|
|
322
|
+
type: "object",
|
|
323
|
+
properties: {
|
|
324
|
+
instance: { type: "string", description: "Instance name" },
|
|
325
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
326
|
+
action: { type: "string", enum: ["add", "remove", "promote", "demote"], description: "Action to take on participants" },
|
|
327
|
+
participants: {
|
|
328
|
+
type: "array",
|
|
329
|
+
items: { type: "string" },
|
|
330
|
+
description: "Array of phone numbers (with country code)",
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
required: ["instance", "groupJid", "action", "participants"],
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
name: "fetch_group_invite_code",
|
|
338
|
+
description: "Fetch the invite code/link for a WhatsApp group",
|
|
339
|
+
inputSchema: {
|
|
340
|
+
type: "object",
|
|
341
|
+
properties: {
|
|
342
|
+
instance: { type: "string", description: "Instance name" },
|
|
343
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
344
|
+
},
|
|
345
|
+
required: ["instance", "groupJid"],
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "mark_message_as_read",
|
|
350
|
+
description: "Mark one or more messages in a chat as read",
|
|
351
|
+
inputSchema: {
|
|
352
|
+
type: "object",
|
|
353
|
+
properties: {
|
|
354
|
+
instance: { type: "string", description: "Instance name" },
|
|
355
|
+
readMessages: {
|
|
356
|
+
type: "array",
|
|
357
|
+
items: {
|
|
358
|
+
type: "object",
|
|
359
|
+
properties: {
|
|
360
|
+
remoteJid: { type: "string", description: "Chat JID" },
|
|
361
|
+
fromMe: { type: "boolean", description: "Whether the message was sent by the instance" },
|
|
362
|
+
id: { type: "string", description: "Message ID" },
|
|
363
|
+
},
|
|
364
|
+
required: ["remoteJid", "fromMe", "id"],
|
|
365
|
+
},
|
|
366
|
+
description: "List of messages to mark as read",
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
required: ["instance", "readMessages"],
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
name: "archive_chat",
|
|
374
|
+
description: "Archive or unarchive a chat",
|
|
375
|
+
inputSchema: {
|
|
376
|
+
type: "object",
|
|
377
|
+
properties: {
|
|
378
|
+
instance: { type: "string", description: "Instance name" },
|
|
379
|
+
remoteJid: { type: "string", description: "Chat JID" },
|
|
380
|
+
archive: { type: "boolean", description: "true to archive, false to unarchive" },
|
|
381
|
+
lastMessage: {
|
|
382
|
+
type: "object",
|
|
383
|
+
description: "Last message key reference (optional)",
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
required: ["instance", "remoteJid", "archive"],
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
name: "delete_message",
|
|
391
|
+
description: "Delete a message for me or for everyone in a chat",
|
|
392
|
+
inputSchema: {
|
|
393
|
+
type: "object",
|
|
394
|
+
properties: {
|
|
395
|
+
instance: { type: "string", description: "Instance name" },
|
|
396
|
+
remoteJid: { type: "string", description: "Chat JID" },
|
|
397
|
+
id: { type: "string", description: "Message ID" },
|
|
398
|
+
fromMe: { type: "boolean", description: "Whether the message was sent by the instance" },
|
|
399
|
+
participant: { type: "string", description: "Participant JID (required for group messages)" },
|
|
400
|
+
},
|
|
401
|
+
required: ["instance", "remoteJid", "id", "fromMe"],
|
|
402
|
+
},
|
|
403
|
+
},
|
|
173
404
|
],
|
|
174
405
|
}));
|
|
175
406
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -200,6 +431,69 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
200
431
|
}
|
|
201
432
|
case "check_number":
|
|
202
433
|
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/whatsappNumbers/${args?.instance}`, { numbers: args?.numbers }), null, 2) }] };
|
|
434
|
+
case "create_group":
|
|
435
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/group/create/${args?.instance}`, { subject: args?.subject, participants: args?.participants, description: args?.description }), null, 2) }] };
|
|
436
|
+
case "get_group_info":
|
|
437
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/group/findGroupInfos/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
|
|
438
|
+
case "update_profile": {
|
|
439
|
+
const profileData = {};
|
|
440
|
+
if (args?.name)
|
|
441
|
+
profileData.name = args.name;
|
|
442
|
+
if (args?.status)
|
|
443
|
+
profileData.status = args.status;
|
|
444
|
+
if (args?.picture)
|
|
445
|
+
profileData.picture = args.picture;
|
|
446
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("PUT", `/instance/updateProfile/${args?.instance}`, profileData), null, 2) }] };
|
|
447
|
+
}
|
|
448
|
+
case "set_presence":
|
|
449
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/setPresence/${args?.instance}`, { presence: args?.presence, number: args?.number }), null, 2) }] };
|
|
450
|
+
case "get_chat_history": {
|
|
451
|
+
const body = {
|
|
452
|
+
where: { key: { remoteJid: args?.remoteJid } },
|
|
453
|
+
};
|
|
454
|
+
if (args?.limit)
|
|
455
|
+
body.limit = args.limit;
|
|
456
|
+
if (args?.offset)
|
|
457
|
+
body.offset = args.offset;
|
|
458
|
+
if (args?.fromMe !== undefined)
|
|
459
|
+
body.where = { ...body.where, key: { remoteJid: args?.remoteJid, fromMe: args.fromMe } };
|
|
460
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/findMessages/${args?.instance}`, body), null, 2) }] };
|
|
461
|
+
}
|
|
462
|
+
case "logout_instance":
|
|
463
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/instance/logout/${args?.instance}`), null, 2) }] };
|
|
464
|
+
case "restart_instance":
|
|
465
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/instance/restart/${args?.instance}`), null, 2) }] };
|
|
466
|
+
case "delete_instance":
|
|
467
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/instance/delete/${args?.instance}`), null, 2) }] };
|
|
468
|
+
case "connection_state":
|
|
469
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/instance/connectionState/${args?.instance}`), null, 2) }] };
|
|
470
|
+
case "leave_group":
|
|
471
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/group/leaveGroup/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
|
|
472
|
+
case "update_group_participants":
|
|
473
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/group/updateParticipant/${args?.instance}?groupJid=${args?.groupJid}`, { action: args?.action, participants: args?.participants }), null, 2) }] };
|
|
474
|
+
case "fetch_group_invite_code":
|
|
475
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/group/inviteCode/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
|
|
476
|
+
case "mark_message_as_read":
|
|
477
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/markMessageAsRead/${args?.instance}`, { readMessages: args?.readMessages }), null, 2) }] };
|
|
478
|
+
case "archive_chat": {
|
|
479
|
+
const body = {
|
|
480
|
+
chat: args?.remoteJid,
|
|
481
|
+
archive: args?.archive,
|
|
482
|
+
};
|
|
483
|
+
if (args?.lastMessage)
|
|
484
|
+
body.lastMessage = args.lastMessage;
|
|
485
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/archiveChat/${args?.instance}`, body), null, 2) }] };
|
|
486
|
+
}
|
|
487
|
+
case "delete_message": {
|
|
488
|
+
const body = {
|
|
489
|
+
id: args?.id,
|
|
490
|
+
remoteJid: args?.remoteJid,
|
|
491
|
+
fromMe: args?.fromMe,
|
|
492
|
+
};
|
|
493
|
+
if (args?.participant)
|
|
494
|
+
body.participant = args.participant;
|
|
495
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/chat/deleteMessageForEveryone/${args?.instance}`, body), null, 2) }] };
|
|
496
|
+
}
|
|
203
497
|
default:
|
|
204
498
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
205
499
|
}
|
|
@@ -209,11 +503,46 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
209
503
|
}
|
|
210
504
|
});
|
|
211
505
|
async function main() {
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
506
|
+
if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
|
|
507
|
+
const { default: express } = await import("express");
|
|
508
|
+
const { randomUUID } = await import("node:crypto");
|
|
509
|
+
const app = express();
|
|
510
|
+
app.use(express.json());
|
|
511
|
+
const transports = new Map();
|
|
512
|
+
app.get("/health", (_req, res) => res.json({ status: "ok", sessions: transports.size }));
|
|
513
|
+
app.post("/mcp", async (req, res) => {
|
|
514
|
+
const sid = req.headers["mcp-session-id"];
|
|
515
|
+
if (sid && transports.has(sid)) {
|
|
516
|
+
await transports.get(sid).handleRequest(req, res, req.body);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (!sid && isInitializeRequest(req.body)) {
|
|
520
|
+
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
521
|
+
t.onclose = () => { if (t.sessionId)
|
|
522
|
+
transports.delete(t.sessionId); };
|
|
523
|
+
const s = new Server({ name: "mcp-evolution-api", version: "0.2.0" }, { capabilities: { tools: {} } });
|
|
524
|
+
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
525
|
+
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
526
|
+
await s.connect(t);
|
|
527
|
+
await t.handleRequest(req, res, req.body);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|
|
531
|
+
});
|
|
532
|
+
app.get("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
533
|
+
await transports.get(sid).handleRequest(req, res);
|
|
534
|
+
else
|
|
535
|
+
res.status(400).send("Invalid session"); });
|
|
536
|
+
app.delete("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
|
|
537
|
+
await transports.get(sid).handleRequest(req, res);
|
|
538
|
+
else
|
|
539
|
+
res.status(400).send("Invalid session"); });
|
|
540
|
+
const port = Number(process.env.MCP_PORT) || 3000;
|
|
541
|
+
app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
const transport = new StdioServerTransport();
|
|
545
|
+
await server.connect(transport);
|
|
215
546
|
}
|
|
216
|
-
const transport = new StdioServerTransport();
|
|
217
|
-
await server.connect(transport);
|
|
218
547
|
}
|
|
219
548
|
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-evolution-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server for Evolution API — WhatsApp messaging, instances, contacts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,5 +25,6 @@
|
|
|
25
25
|
"whatsapp",
|
|
26
26
|
"messaging",
|
|
27
27
|
"brazil"
|
|
28
|
-
]
|
|
28
|
+
],
|
|
29
|
+
"mcpName": "io.github.codespar/mcp-evolution-api"
|
|
29
30
|
}
|
package/server.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.codespar/mcp-evolution-api",
|
|
4
|
+
"description": "MCP server for Evolution API — WhatsApp messaging, instances, contacts",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/codespar/mcp-dev-brasil",
|
|
7
|
+
"source": "github",
|
|
8
|
+
"subfolder": "packages/communication/evolution-api"
|
|
9
|
+
},
|
|
10
|
+
"version": "0.2.0",
|
|
11
|
+
"packages": [
|
|
12
|
+
{
|
|
13
|
+
"registryType": "npm",
|
|
14
|
+
"identifier": "@codespar/mcp-evolution-api",
|
|
15
|
+
"version": "0.2.0",
|
|
16
|
+
"transport": {
|
|
17
|
+
"type": "stdio"
|
|
18
|
+
},
|
|
19
|
+
"environmentVariables": [
|
|
20
|
+
{
|
|
21
|
+
"name": "EVOLUTION_API_KEY",
|
|
22
|
+
"description": "API key for evolution-api",
|
|
23
|
+
"isRequired": true,
|
|
24
|
+
"format": "string",
|
|
25
|
+
"isSecret": true
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
|
|
3
|
+
let listToolsHandler: Function;
|
|
4
|
+
let callToolHandler: Function;
|
|
5
|
+
|
|
6
|
+
vi.mock("@modelcontextprotocol/sdk/server/index.js", () => {
|
|
7
|
+
class FakeServer {
|
|
8
|
+
constructor() {}
|
|
9
|
+
setRequestHandler(schema: any, handler: Function) {
|
|
10
|
+
if (JSON.stringify(schema).includes("tools/list")) listToolsHandler = handler;
|
|
11
|
+
if (JSON.stringify(schema).includes("tools/call")) callToolHandler = handler;
|
|
12
|
+
}
|
|
13
|
+
connect() { return Promise.resolve(); }
|
|
14
|
+
}
|
|
15
|
+
return { Server: FakeServer };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
vi.mock("@modelcontextprotocol/sdk/server/stdio.js", () => ({ StdioServerTransport: class {} }));
|
|
19
|
+
|
|
20
|
+
process.env.EVOLUTION_API_URL = "https://evo.example.com";
|
|
21
|
+
process.env.EVOLUTION_API_KEY = "test-key";
|
|
22
|
+
|
|
23
|
+
const mockFetch = vi.fn();
|
|
24
|
+
global.fetch = mockFetch as any;
|
|
25
|
+
|
|
26
|
+
beforeEach(async () => {
|
|
27
|
+
vi.resetModules();
|
|
28
|
+
listToolsHandler = undefined as any;
|
|
29
|
+
callToolHandler = undefined as any;
|
|
30
|
+
mockFetch.mockReset();
|
|
31
|
+
global.fetch = mockFetch as any;
|
|
32
|
+
await import("../index.js");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("mcp-evolution-api", () => {
|
|
36
|
+
it("should register 15 tools", async () => {
|
|
37
|
+
const result = await listToolsHandler();
|
|
38
|
+
expect(result.tools).toHaveLength(15);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should call correct API endpoint for send_text", async () => {
|
|
42
|
+
mockFetch.mockResolvedValueOnce({ ok: true, json: () => Promise.resolve({ key: { id: "msg1" } }) });
|
|
43
|
+
|
|
44
|
+
await callToolHandler({
|
|
45
|
+
params: { name: "send_text", arguments: { instance: "mybot", number: "5511999999999", text: "Hello" } },
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const [url, opts] = mockFetch.mock.calls[0];
|
|
49
|
+
expect(url).toContain("/message/sendText/mybot");
|
|
50
|
+
expect(opts.method).toBe("POST");
|
|
51
|
+
expect(opts.headers.apikey).toBe("test-key");
|
|
52
|
+
});
|
|
53
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,21 @@
|
|
|
14
14
|
* - send_poll: Send a poll message
|
|
15
15
|
* - get_messages: Get messages from a chat
|
|
16
16
|
* - check_number: Check if a number is on WhatsApp
|
|
17
|
+
* - create_group: Create a WhatsApp group
|
|
18
|
+
* - get_group_info: Get group metadata and participants
|
|
19
|
+
* - update_profile: Update instance profile (name, picture, status)
|
|
20
|
+
* - set_presence: Set online/offline presence for an instance
|
|
21
|
+
* - get_chat_history: Get full chat history with pagination
|
|
22
|
+
* - logout_instance: Logout an instance (disconnects WhatsApp session)
|
|
23
|
+
* - restart_instance: Restart an instance
|
|
24
|
+
* - delete_instance: Delete an instance permanently
|
|
25
|
+
* - connection_state: Get connection state of an instance
|
|
26
|
+
* - leave_group: Leave a WhatsApp group
|
|
27
|
+
* - update_group_participants: Add/remove/promote/demote participants in a group
|
|
28
|
+
* - fetch_group_invite_code: Fetch invite code/link for a group
|
|
29
|
+
* - mark_message_as_read: Mark messages in a chat as read
|
|
30
|
+
* - archive_chat: Archive or unarchive a chat
|
|
31
|
+
* - delete_message: Delete a message (for me or for everyone)
|
|
17
32
|
*
|
|
18
33
|
* Environment:
|
|
19
34
|
* EVOLUTION_API_URL — Base URL of self-hosted Evolution API
|
|
@@ -22,6 +37,8 @@
|
|
|
22
37
|
|
|
23
38
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
24
39
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
40
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
41
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
25
42
|
import {
|
|
26
43
|
CallToolRequestSchema,
|
|
27
44
|
ListToolsRequestSchema,
|
|
@@ -47,7 +64,7 @@ async function evolutionRequest(method: string, path: string, body?: unknown): P
|
|
|
47
64
|
}
|
|
48
65
|
|
|
49
66
|
const server = new Server(
|
|
50
|
-
{ name: "mcp-evolution-api", version: "0.
|
|
67
|
+
{ name: "mcp-evolution-api", version: "0.2.0" },
|
|
51
68
|
{ capabilities: { tools: {} } }
|
|
52
69
|
);
|
|
53
70
|
|
|
@@ -182,6 +199,220 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
182
199
|
required: ["instance", "numbers"],
|
|
183
200
|
},
|
|
184
201
|
},
|
|
202
|
+
{
|
|
203
|
+
name: "create_group",
|
|
204
|
+
description: "Create a WhatsApp group",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
type: "object",
|
|
207
|
+
properties: {
|
|
208
|
+
instance: { type: "string", description: "Instance name" },
|
|
209
|
+
subject: { type: "string", description: "Group name/subject" },
|
|
210
|
+
participants: {
|
|
211
|
+
type: "array",
|
|
212
|
+
items: { type: "string" },
|
|
213
|
+
description: "Array of phone numbers to add (with country code)",
|
|
214
|
+
},
|
|
215
|
+
description: { type: "string", description: "Group description" },
|
|
216
|
+
},
|
|
217
|
+
required: ["instance", "subject", "participants"],
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
name: "get_group_info",
|
|
222
|
+
description: "Get group metadata, participants, and settings",
|
|
223
|
+
inputSchema: {
|
|
224
|
+
type: "object",
|
|
225
|
+
properties: {
|
|
226
|
+
instance: { type: "string", description: "Instance name" },
|
|
227
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
228
|
+
},
|
|
229
|
+
required: ["instance", "groupJid"],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "update_profile",
|
|
234
|
+
description: "Update instance profile (name, status text, or picture)",
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: "object",
|
|
237
|
+
properties: {
|
|
238
|
+
instance: { type: "string", description: "Instance name" },
|
|
239
|
+
name: { type: "string", description: "New profile name" },
|
|
240
|
+
status: { type: "string", description: "New status text" },
|
|
241
|
+
picture: { type: "string", description: "URL of profile picture" },
|
|
242
|
+
},
|
|
243
|
+
required: ["instance"],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "set_presence",
|
|
248
|
+
description: "Set online/offline presence for an instance",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
instance: { type: "string", description: "Instance name" },
|
|
253
|
+
presence: { type: "string", enum: ["available", "unavailable", "composing", "recording", "paused"], description: "Presence state" },
|
|
254
|
+
number: { type: "string", description: "Target number (required for composing/recording)" },
|
|
255
|
+
},
|
|
256
|
+
required: ["instance", "presence"],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: "get_chat_history",
|
|
261
|
+
description: "Get full chat history with pagination support",
|
|
262
|
+
inputSchema: {
|
|
263
|
+
type: "object",
|
|
264
|
+
properties: {
|
|
265
|
+
instance: { type: "string", description: "Instance name" },
|
|
266
|
+
remoteJid: { type: "string", description: "Chat JID (e.g. 5511999999999@s.whatsapp.net)" },
|
|
267
|
+
limit: { type: "number", description: "Number of messages (default 50)" },
|
|
268
|
+
offset: { type: "number", description: "Pagination offset (message index)" },
|
|
269
|
+
fromMe: { type: "boolean", description: "Filter only sent messages" },
|
|
270
|
+
},
|
|
271
|
+
required: ["instance", "remoteJid"],
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: "logout_instance",
|
|
276
|
+
description: "Logout an instance (disconnects the WhatsApp session without deleting the instance)",
|
|
277
|
+
inputSchema: {
|
|
278
|
+
type: "object",
|
|
279
|
+
properties: {
|
|
280
|
+
instance: { type: "string", description: "Instance name" },
|
|
281
|
+
},
|
|
282
|
+
required: ["instance"],
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: "restart_instance",
|
|
287
|
+
description: "Restart an instance",
|
|
288
|
+
inputSchema: {
|
|
289
|
+
type: "object",
|
|
290
|
+
properties: {
|
|
291
|
+
instance: { type: "string", description: "Instance name" },
|
|
292
|
+
},
|
|
293
|
+
required: ["instance"],
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: "delete_instance",
|
|
298
|
+
description: "Delete an instance permanently",
|
|
299
|
+
inputSchema: {
|
|
300
|
+
type: "object",
|
|
301
|
+
properties: {
|
|
302
|
+
instance: { type: "string", description: "Instance name" },
|
|
303
|
+
},
|
|
304
|
+
required: ["instance"],
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: "connection_state",
|
|
309
|
+
description: "Get the connection state of an instance (open, connecting, close)",
|
|
310
|
+
inputSchema: {
|
|
311
|
+
type: "object",
|
|
312
|
+
properties: {
|
|
313
|
+
instance: { type: "string", description: "Instance name" },
|
|
314
|
+
},
|
|
315
|
+
required: ["instance"],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
name: "leave_group",
|
|
320
|
+
description: "Leave a WhatsApp group",
|
|
321
|
+
inputSchema: {
|
|
322
|
+
type: "object",
|
|
323
|
+
properties: {
|
|
324
|
+
instance: { type: "string", description: "Instance name" },
|
|
325
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
326
|
+
},
|
|
327
|
+
required: ["instance", "groupJid"],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: "update_group_participants",
|
|
332
|
+
description: "Add, remove, promote, or demote participants in a WhatsApp group",
|
|
333
|
+
inputSchema: {
|
|
334
|
+
type: "object",
|
|
335
|
+
properties: {
|
|
336
|
+
instance: { type: "string", description: "Instance name" },
|
|
337
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
338
|
+
action: { type: "string", enum: ["add", "remove", "promote", "demote"], description: "Action to take on participants" },
|
|
339
|
+
participants: {
|
|
340
|
+
type: "array",
|
|
341
|
+
items: { type: "string" },
|
|
342
|
+
description: "Array of phone numbers (with country code)",
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
required: ["instance", "groupJid", "action", "participants"],
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "fetch_group_invite_code",
|
|
350
|
+
description: "Fetch the invite code/link for a WhatsApp group",
|
|
351
|
+
inputSchema: {
|
|
352
|
+
type: "object",
|
|
353
|
+
properties: {
|
|
354
|
+
instance: { type: "string", description: "Instance name" },
|
|
355
|
+
groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
|
|
356
|
+
},
|
|
357
|
+
required: ["instance", "groupJid"],
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
name: "mark_message_as_read",
|
|
362
|
+
description: "Mark one or more messages in a chat as read",
|
|
363
|
+
inputSchema: {
|
|
364
|
+
type: "object",
|
|
365
|
+
properties: {
|
|
366
|
+
instance: { type: "string", description: "Instance name" },
|
|
367
|
+
readMessages: {
|
|
368
|
+
type: "array",
|
|
369
|
+
items: {
|
|
370
|
+
type: "object",
|
|
371
|
+
properties: {
|
|
372
|
+
remoteJid: { type: "string", description: "Chat JID" },
|
|
373
|
+
fromMe: { type: "boolean", description: "Whether the message was sent by the instance" },
|
|
374
|
+
id: { type: "string", description: "Message ID" },
|
|
375
|
+
},
|
|
376
|
+
required: ["remoteJid", "fromMe", "id"],
|
|
377
|
+
},
|
|
378
|
+
description: "List of messages to mark as read",
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
required: ["instance", "readMessages"],
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: "archive_chat",
|
|
386
|
+
description: "Archive or unarchive a chat",
|
|
387
|
+
inputSchema: {
|
|
388
|
+
type: "object",
|
|
389
|
+
properties: {
|
|
390
|
+
instance: { type: "string", description: "Instance name" },
|
|
391
|
+
remoteJid: { type: "string", description: "Chat JID" },
|
|
392
|
+
archive: { type: "boolean", description: "true to archive, false to unarchive" },
|
|
393
|
+
lastMessage: {
|
|
394
|
+
type: "object",
|
|
395
|
+
description: "Last message key reference (optional)",
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
required: ["instance", "remoteJid", "archive"],
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
name: "delete_message",
|
|
403
|
+
description: "Delete a message for me or for everyone in a chat",
|
|
404
|
+
inputSchema: {
|
|
405
|
+
type: "object",
|
|
406
|
+
properties: {
|
|
407
|
+
instance: { type: "string", description: "Instance name" },
|
|
408
|
+
remoteJid: { type: "string", description: "Chat JID" },
|
|
409
|
+
id: { type: "string", description: "Message ID" },
|
|
410
|
+
fromMe: { type: "boolean", description: "Whether the message was sent by the instance" },
|
|
411
|
+
participant: { type: "string", description: "Participant JID (required for group messages)" },
|
|
412
|
+
},
|
|
413
|
+
required: ["instance", "remoteJid", "id", "fromMe"],
|
|
414
|
+
},
|
|
415
|
+
},
|
|
185
416
|
],
|
|
186
417
|
}));
|
|
187
418
|
|
|
@@ -213,6 +444,61 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
213
444
|
}
|
|
214
445
|
case "check_number":
|
|
215
446
|
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/whatsappNumbers/${args?.instance}`, { numbers: args?.numbers }), null, 2) }] };
|
|
447
|
+
case "create_group":
|
|
448
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/group/create/${args?.instance}`, { subject: args?.subject, participants: args?.participants, description: args?.description }), null, 2) }] };
|
|
449
|
+
case "get_group_info":
|
|
450
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/group/findGroupInfos/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
|
|
451
|
+
case "update_profile": {
|
|
452
|
+
const profileData: Record<string, unknown> = {};
|
|
453
|
+
if (args?.name) profileData.name = args.name;
|
|
454
|
+
if (args?.status) profileData.status = args.status;
|
|
455
|
+
if (args?.picture) profileData.picture = args.picture;
|
|
456
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("PUT", `/instance/updateProfile/${args?.instance}`, profileData), null, 2) }] };
|
|
457
|
+
}
|
|
458
|
+
case "set_presence":
|
|
459
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/setPresence/${args?.instance}`, { presence: args?.presence, number: args?.number }), null, 2) }] };
|
|
460
|
+
case "get_chat_history": {
|
|
461
|
+
const body: Record<string, unknown> = {
|
|
462
|
+
where: { key: { remoteJid: args?.remoteJid } },
|
|
463
|
+
};
|
|
464
|
+
if (args?.limit) body.limit = args.limit;
|
|
465
|
+
if (args?.offset) body.offset = args.offset;
|
|
466
|
+
if (args?.fromMe !== undefined) body.where = { ...(body.where as Record<string, unknown>), key: { remoteJid: args?.remoteJid, fromMe: args.fromMe } };
|
|
467
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/findMessages/${args?.instance}`, body), null, 2) }] };
|
|
468
|
+
}
|
|
469
|
+
case "logout_instance":
|
|
470
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/instance/logout/${args?.instance}`), null, 2) }] };
|
|
471
|
+
case "restart_instance":
|
|
472
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/instance/restart/${args?.instance}`), null, 2) }] };
|
|
473
|
+
case "delete_instance":
|
|
474
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/instance/delete/${args?.instance}`), null, 2) }] };
|
|
475
|
+
case "connection_state":
|
|
476
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/instance/connectionState/${args?.instance}`), null, 2) }] };
|
|
477
|
+
case "leave_group":
|
|
478
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/group/leaveGroup/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
|
|
479
|
+
case "update_group_participants":
|
|
480
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/group/updateParticipant/${args?.instance}?groupJid=${args?.groupJid}`, { action: args?.action, participants: args?.participants }), null, 2) }] };
|
|
481
|
+
case "fetch_group_invite_code":
|
|
482
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/group/inviteCode/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
|
|
483
|
+
case "mark_message_as_read":
|
|
484
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/markMessageAsRead/${args?.instance}`, { readMessages: args?.readMessages }), null, 2) }] };
|
|
485
|
+
case "archive_chat": {
|
|
486
|
+
const body: Record<string, unknown> = {
|
|
487
|
+
chat: args?.remoteJid,
|
|
488
|
+
archive: args?.archive,
|
|
489
|
+
};
|
|
490
|
+
if (args?.lastMessage) body.lastMessage = args.lastMessage;
|
|
491
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/archiveChat/${args?.instance}`, body), null, 2) }] };
|
|
492
|
+
}
|
|
493
|
+
case "delete_message": {
|
|
494
|
+
const body: Record<string, unknown> = {
|
|
495
|
+
id: args?.id,
|
|
496
|
+
remoteJid: args?.remoteJid,
|
|
497
|
+
fromMe: args?.fromMe,
|
|
498
|
+
};
|
|
499
|
+
if (args?.participant) body.participant = args.participant;
|
|
500
|
+
return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("DELETE", `/chat/deleteMessageForEveryone/${args?.instance}`, body), null, 2) }] };
|
|
501
|
+
}
|
|
216
502
|
default:
|
|
217
503
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
218
504
|
}
|
|
@@ -222,12 +508,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
222
508
|
});
|
|
223
509
|
|
|
224
510
|
async function main() {
|
|
225
|
-
if (
|
|
226
|
-
|
|
227
|
-
|
|
511
|
+
if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
|
|
512
|
+
const { default: express } = await import("express");
|
|
513
|
+
const { randomUUID } = await import("node:crypto");
|
|
514
|
+
const app = express();
|
|
515
|
+
app.use(express.json());
|
|
516
|
+
const transports = new Map<string, StreamableHTTPServerTransport>();
|
|
517
|
+
app.get("/health", (_req: any, res: any) => res.json({ status: "ok", sessions: transports.size }));
|
|
518
|
+
app.post("/mcp", async (req: any, res: any) => {
|
|
519
|
+
const sid = req.headers["mcp-session-id"] as string | undefined;
|
|
520
|
+
if (sid && transports.has(sid)) { await transports.get(sid)!.handleRequest(req, res, req.body); return; }
|
|
521
|
+
if (!sid && isInitializeRequest(req.body)) {
|
|
522
|
+
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
523
|
+
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
524
|
+
const s = new Server({ name: "mcp-evolution-api", 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);
|
|
525
|
+
await t.handleRequest(req, res, req.body); return;
|
|
526
|
+
}
|
|
527
|
+
res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
|
|
528
|
+
});
|
|
529
|
+
app.get("/mcp", async (req: any, res: any) => { const sid = req.headers["mcp-session-id"] as string; if (sid && transports.has(sid)) await transports.get(sid)!.handleRequest(req, res); else res.status(400).send("Invalid session"); });
|
|
530
|
+
app.delete("/mcp", async (req: any, res: any) => { const sid = req.headers["mcp-session-id"] as string; if (sid && transports.has(sid)) await transports.get(sid)!.handleRequest(req, res); else res.status(400).send("Invalid session"); });
|
|
531
|
+
const port = Number(process.env.MCP_PORT) || 3000;
|
|
532
|
+
app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
|
|
533
|
+
} else {
|
|
534
|
+
const transport = new StdioServerTransport();
|
|
535
|
+
await server.connect(transport);
|
|
228
536
|
}
|
|
229
|
-
const transport = new StdioServerTransport();
|
|
230
|
-
await server.connect(transport);
|
|
231
537
|
}
|
|
232
538
|
|
|
233
539
|
main().catch(console.error);
|