@codespar/mcp-evolution-api 0.1.0 → 0.1.2

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 ADDED
@@ -0,0 +1,120 @@
1
+ # @codespar/mcp-evolution-api
2
+
3
+ > MCP server for **Evolution API** — self-hosted WhatsApp messaging API
4
+
5
+ [![npm](https://img.shields.io/npm/v/@codespar/mcp-evolution-api)](https://www.npmjs.com/package/@codespar/mcp-evolution-api)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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,11 @@
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
16
21
  *
17
22
  * Environment:
18
23
  * EVOLUTION_API_URL — Base URL of self-hosted Evolution API
@@ -20,6 +25,8 @@
20
25
  */
21
26
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
22
27
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
28
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
29
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
23
30
  import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
24
31
  const API_URL = process.env.EVOLUTION_API_URL || "";
25
32
  const API_KEY = process.env.EVOLUTION_API_KEY || "";
@@ -170,6 +177,78 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
170
177
  required: ["instance", "numbers"],
171
178
  },
172
179
  },
180
+ {
181
+ name: "create_group",
182
+ description: "Create a WhatsApp group",
183
+ inputSchema: {
184
+ type: "object",
185
+ properties: {
186
+ instance: { type: "string", description: "Instance name" },
187
+ subject: { type: "string", description: "Group name/subject" },
188
+ participants: {
189
+ type: "array",
190
+ items: { type: "string" },
191
+ description: "Array of phone numbers to add (with country code)",
192
+ },
193
+ description: { type: "string", description: "Group description" },
194
+ },
195
+ required: ["instance", "subject", "participants"],
196
+ },
197
+ },
198
+ {
199
+ name: "get_group_info",
200
+ description: "Get group metadata, participants, and settings",
201
+ inputSchema: {
202
+ type: "object",
203
+ properties: {
204
+ instance: { type: "string", description: "Instance name" },
205
+ groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
206
+ },
207
+ required: ["instance", "groupJid"],
208
+ },
209
+ },
210
+ {
211
+ name: "update_profile",
212
+ description: "Update instance profile (name, status text, or picture)",
213
+ inputSchema: {
214
+ type: "object",
215
+ properties: {
216
+ instance: { type: "string", description: "Instance name" },
217
+ name: { type: "string", description: "New profile name" },
218
+ status: { type: "string", description: "New status text" },
219
+ picture: { type: "string", description: "URL of profile picture" },
220
+ },
221
+ required: ["instance"],
222
+ },
223
+ },
224
+ {
225
+ name: "set_presence",
226
+ description: "Set online/offline presence for an instance",
227
+ inputSchema: {
228
+ type: "object",
229
+ properties: {
230
+ instance: { type: "string", description: "Instance name" },
231
+ presence: { type: "string", enum: ["available", "unavailable", "composing", "recording", "paused"], description: "Presence state" },
232
+ number: { type: "string", description: "Target number (required for composing/recording)" },
233
+ },
234
+ required: ["instance", "presence"],
235
+ },
236
+ },
237
+ {
238
+ name: "get_chat_history",
239
+ description: "Get full chat history with pagination support",
240
+ inputSchema: {
241
+ type: "object",
242
+ properties: {
243
+ instance: { type: "string", description: "Instance name" },
244
+ remoteJid: { type: "string", description: "Chat JID (e.g. 5511999999999@s.whatsapp.net)" },
245
+ limit: { type: "number", description: "Number of messages (default 50)" },
246
+ offset: { type: "number", description: "Pagination offset (message index)" },
247
+ fromMe: { type: "boolean", description: "Filter only sent messages" },
248
+ },
249
+ required: ["instance", "remoteJid"],
250
+ },
251
+ },
173
252
  ],
174
253
  }));
175
254
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -200,6 +279,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
200
279
  }
201
280
  case "check_number":
202
281
  return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/whatsappNumbers/${args?.instance}`, { numbers: args?.numbers }), null, 2) }] };
282
+ case "create_group":
283
+ 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) }] };
284
+ case "get_group_info":
285
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/group/findGroupInfos/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
286
+ case "update_profile": {
287
+ const profileData = {};
288
+ if (args?.name)
289
+ profileData.name = args.name;
290
+ if (args?.status)
291
+ profileData.status = args.status;
292
+ if (args?.picture)
293
+ profileData.picture = args.picture;
294
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("PUT", `/instance/updateProfile/${args?.instance}`, profileData), null, 2) }] };
295
+ }
296
+ case "set_presence":
297
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/setPresence/${args?.instance}`, { presence: args?.presence, number: args?.number }), null, 2) }] };
298
+ case "get_chat_history": {
299
+ const body = {
300
+ where: { key: { remoteJid: args?.remoteJid } },
301
+ };
302
+ if (args?.limit)
303
+ body.limit = args.limit;
304
+ if (args?.offset)
305
+ body.offset = args.offset;
306
+ if (args?.fromMe !== undefined)
307
+ body.where = { ...body.where, key: { remoteJid: args?.remoteJid, fromMe: args.fromMe } };
308
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/findMessages/${args?.instance}`, body), null, 2) }] };
309
+ }
203
310
  default:
204
311
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
205
312
  }
@@ -209,11 +316,46 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
209
316
  }
210
317
  });
211
318
  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);
319
+ if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
320
+ const { default: express } = await import("express");
321
+ const { randomUUID } = await import("node:crypto");
322
+ const app = express();
323
+ app.use(express.json());
324
+ const transports = new Map();
325
+ app.get("/health", (_req, res) => res.json({ status: "ok", sessions: transports.size }));
326
+ app.post("/mcp", async (req, res) => {
327
+ const sid = req.headers["mcp-session-id"];
328
+ if (sid && transports.has(sid)) {
329
+ await transports.get(sid).handleRequest(req, res, req.body);
330
+ return;
331
+ }
332
+ if (!sid && isInitializeRequest(req.body)) {
333
+ const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
334
+ t.onclose = () => { if (t.sessionId)
335
+ transports.delete(t.sessionId); };
336
+ const s = new Server({ name: "mcp-evolution-api", version: "0.1.0" }, { capabilities: { tools: {} } });
337
+ server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
338
+ server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
339
+ await s.connect(t);
340
+ await t.handleRequest(req, res, req.body);
341
+ return;
342
+ }
343
+ res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
344
+ });
345
+ app.get("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
346
+ await transports.get(sid).handleRequest(req, res);
347
+ else
348
+ res.status(400).send("Invalid session"); });
349
+ app.delete("/mcp", async (req, res) => { const sid = req.headers["mcp-session-id"]; if (sid && transports.has(sid))
350
+ await transports.get(sid).handleRequest(req, res);
351
+ else
352
+ res.status(400).send("Invalid session"); });
353
+ const port = Number(process.env.MCP_PORT) || 3000;
354
+ app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
355
+ }
356
+ else {
357
+ const transport = new StdioServerTransport();
358
+ await server.connect(transport);
215
359
  }
216
- const transport = new StdioServerTransport();
217
- await server.connect(transport);
218
360
  }
219
361
  main().catch(console.error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codespar/mcp-evolution-api",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
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.1.2",
11
+ "packages": [
12
+ {
13
+ "registryType": "npm",
14
+ "identifier": "@codespar/mcp-evolution-api",
15
+ "version": "0.1.2",
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,11 @@
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
17
22
  *
18
23
  * Environment:
19
24
  * EVOLUTION_API_URL — Base URL of self-hosted Evolution API
@@ -22,6 +27,8 @@
22
27
 
23
28
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
24
29
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
30
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
31
+ import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
25
32
  import {
26
33
  CallToolRequestSchema,
27
34
  ListToolsRequestSchema,
@@ -182,6 +189,78 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
182
189
  required: ["instance", "numbers"],
183
190
  },
184
191
  },
192
+ {
193
+ name: "create_group",
194
+ description: "Create a WhatsApp group",
195
+ inputSchema: {
196
+ type: "object",
197
+ properties: {
198
+ instance: { type: "string", description: "Instance name" },
199
+ subject: { type: "string", description: "Group name/subject" },
200
+ participants: {
201
+ type: "array",
202
+ items: { type: "string" },
203
+ description: "Array of phone numbers to add (with country code)",
204
+ },
205
+ description: { type: "string", description: "Group description" },
206
+ },
207
+ required: ["instance", "subject", "participants"],
208
+ },
209
+ },
210
+ {
211
+ name: "get_group_info",
212
+ description: "Get group metadata, participants, and settings",
213
+ inputSchema: {
214
+ type: "object",
215
+ properties: {
216
+ instance: { type: "string", description: "Instance name" },
217
+ groupJid: { type: "string", description: "Group JID (e.g. 120363000000000000@g.us)" },
218
+ },
219
+ required: ["instance", "groupJid"],
220
+ },
221
+ },
222
+ {
223
+ name: "update_profile",
224
+ description: "Update instance profile (name, status text, or picture)",
225
+ inputSchema: {
226
+ type: "object",
227
+ properties: {
228
+ instance: { type: "string", description: "Instance name" },
229
+ name: { type: "string", description: "New profile name" },
230
+ status: { type: "string", description: "New status text" },
231
+ picture: { type: "string", description: "URL of profile picture" },
232
+ },
233
+ required: ["instance"],
234
+ },
235
+ },
236
+ {
237
+ name: "set_presence",
238
+ description: "Set online/offline presence for an instance",
239
+ inputSchema: {
240
+ type: "object",
241
+ properties: {
242
+ instance: { type: "string", description: "Instance name" },
243
+ presence: { type: "string", enum: ["available", "unavailable", "composing", "recording", "paused"], description: "Presence state" },
244
+ number: { type: "string", description: "Target number (required for composing/recording)" },
245
+ },
246
+ required: ["instance", "presence"],
247
+ },
248
+ },
249
+ {
250
+ name: "get_chat_history",
251
+ description: "Get full chat history with pagination support",
252
+ inputSchema: {
253
+ type: "object",
254
+ properties: {
255
+ instance: { type: "string", description: "Instance name" },
256
+ remoteJid: { type: "string", description: "Chat JID (e.g. 5511999999999@s.whatsapp.net)" },
257
+ limit: { type: "number", description: "Number of messages (default 50)" },
258
+ offset: { type: "number", description: "Pagination offset (message index)" },
259
+ fromMe: { type: "boolean", description: "Filter only sent messages" },
260
+ },
261
+ required: ["instance", "remoteJid"],
262
+ },
263
+ },
185
264
  ],
186
265
  }));
187
266
 
@@ -213,6 +292,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
213
292
  }
214
293
  case "check_number":
215
294
  return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/whatsappNumbers/${args?.instance}`, { numbers: args?.numbers }), null, 2) }] };
295
+ case "create_group":
296
+ 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) }] };
297
+ case "get_group_info":
298
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("GET", `/group/findGroupInfos/${args?.instance}?groupJid=${args?.groupJid}`), null, 2) }] };
299
+ case "update_profile": {
300
+ const profileData: Record<string, unknown> = {};
301
+ if (args?.name) profileData.name = args.name;
302
+ if (args?.status) profileData.status = args.status;
303
+ if (args?.picture) profileData.picture = args.picture;
304
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("PUT", `/instance/updateProfile/${args?.instance}`, profileData), null, 2) }] };
305
+ }
306
+ case "set_presence":
307
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/setPresence/${args?.instance}`, { presence: args?.presence, number: args?.number }), null, 2) }] };
308
+ case "get_chat_history": {
309
+ const body: Record<string, unknown> = {
310
+ where: { key: { remoteJid: args?.remoteJid } },
311
+ };
312
+ if (args?.limit) body.limit = args.limit;
313
+ if (args?.offset) body.offset = args.offset;
314
+ if (args?.fromMe !== undefined) body.where = { ...(body.where as Record<string, unknown>), key: { remoteJid: args?.remoteJid, fromMe: args.fromMe } };
315
+ return { content: [{ type: "text", text: JSON.stringify(await evolutionRequest("POST", `/chat/findMessages/${args?.instance}`, body), null, 2) }] };
316
+ }
216
317
  default:
217
318
  return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
218
319
  }
@@ -222,12 +323,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
222
323
  });
223
324
 
224
325
  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);
326
+ if (process.argv.includes("--http") || process.env.MCP_HTTP === "true") {
327
+ const { default: express } = await import("express");
328
+ const { randomUUID } = await import("node:crypto");
329
+ const app = express();
330
+ app.use(express.json());
331
+ const transports = new Map<string, StreamableHTTPServerTransport>();
332
+ app.get("/health", (_req: any, res: any) => res.json({ status: "ok", sessions: transports.size }));
333
+ app.post("/mcp", async (req: any, res: any) => {
334
+ const sid = req.headers["mcp-session-id"] as string | undefined;
335
+ if (sid && transports.has(sid)) { await transports.get(sid)!.handleRequest(req, res, req.body); return; }
336
+ if (!sid && isInitializeRequest(req.body)) {
337
+ const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
338
+ t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
339
+ const s = new Server({ name: "mcp-evolution-api", 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);
340
+ await t.handleRequest(req, res, req.body); return;
341
+ }
342
+ res.status(400).json({ jsonrpc: "2.0", error: { code: -32000, message: "Bad Request" }, id: null });
343
+ });
344
+ 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"); });
345
+ 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"); });
346
+ const port = Number(process.env.MCP_PORT) || 3000;
347
+ app.listen(port, () => { console.error(`MCP HTTP server on http://localhost:${port}/mcp`); });
348
+ } else {
349
+ const transport = new StdioServerTransport();
350
+ await server.connect(transport);
228
351
  }
229
- const transport = new StdioServerTransport();
230
- await server.connect(transport);
231
352
  }
232
353
 
233
354
  main().catch(console.error);
package/tsconfig.json CHANGED
@@ -6,6 +6,7 @@
6
6
  "outDir": "./dist",
7
7
  "rootDir": "./src",
8
8
  "strict": true,
9
+ "skipLibCheck": true,
9
10
  "esModuleInterop": true,
10
11
  "declaration": true
11
12
  },