@codespar/mcp-whatsapp-cloud 0.1.0 → 0.2.1
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 +24 -13
- package/dist/index.js +291 -4
- package/package.json +2 -2
- package/server.json +2 -2
- package/src/index.ts +282 -4
package/README.md
CHANGED
|
@@ -14,21 +14,32 @@ MCP server for the [WhatsApp Cloud API](https://developers.facebook.com/docs/wha
|
|
|
14
14
|
| take-blip | Brazilian BSP wrapper | Enterprise Brazil, CCaaS features |
|
|
15
15
|
| zenvia | Brazilian BSP wrapper | Brazil omnichannel (SMS + WhatsApp) |
|
|
16
16
|
|
|
17
|
-
## Tools
|
|
17
|
+
## Tools (22)
|
|
18
18
|
|
|
19
19
|
| Tool | Purpose |
|
|
20
|
-
|
|
21
|
-
| `send_text_message` | Send a plain text message |
|
|
22
|
-
| `send_template_message` | Send an approved template
|
|
23
|
-
| `send_media_message` | Send image, video, document, or audio
|
|
24
|
-
| `send_interactive_message` | Send reply buttons or list |
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
20
|
+
|---|---|
|
|
21
|
+
| `send_text_message` | Send a plain text message. |
|
|
22
|
+
| `send_template_message` | Send an approved message template. |
|
|
23
|
+
| `send_media_message` | Send an image, video, document, or audio. |
|
|
24
|
+
| `send_interactive_message` | Send an interactive message (reply buttons or list). |
|
|
25
|
+
| `send_interactive_cta_url` | Send an interactive message with a single CTA URL button. |
|
|
26
|
+
| `send_interactive_flow` | Send a WhatsApp Flow message. |
|
|
27
|
+
| `send_location_message` | Send a location pin with latitude/longitude and optional name/address. |
|
|
28
|
+
| `send_contacts_message` | Send one or more contact cards (vCard-like). |
|
|
29
|
+
| `send_reaction_message` | Send an emoji reaction on a previously received/sent message. |
|
|
30
|
+
| `send_typing_indicator` | Show a typing indicator on a received message. |
|
|
31
|
+
| `mark_message_as_read` | Mark an incoming message as read so the sender sees the blue double-check. |
|
|
32
|
+
| `upload_media` | Upload a media file and get back a media_id reusable in send_media_message. |
|
|
33
|
+
| `retrieve_media_url` | Resolve a media_id to a short-lived downloadable URL. |
|
|
34
|
+
| `delete_media` | Delete an uploaded media asset by id. |
|
|
35
|
+
| `list_templates` | List message templates on the WhatsApp Business Account. |
|
|
36
|
+
| `create_template` | Submit a new template for Meta review. |
|
|
37
|
+
| `delete_template` | Delete a message template from the WABA by name. |
|
|
38
|
+
| `get_business_profile` | Read the WhatsApp business profile (about, description, email, websites, vertical, address) for the configu... |
|
|
39
|
+
| `update_business_profile` | Update the business profile on the configured phone number. |
|
|
40
|
+
| `list_phone_numbers` | List all phone numbers registered under the WhatsApp Business Account, including display name, quality rati... |
|
|
41
|
+
| `request_verification_code` | Request Meta to send a verification code to the configured phone number via SMS or voice. |
|
|
42
|
+
| `verify_code` | Submit the verification code received via SMS/voice after request_verification_code. |
|
|
32
43
|
|
|
33
44
|
## Install
|
|
34
45
|
|
package/dist/index.js
CHANGED
|
@@ -7,18 +7,29 @@
|
|
|
7
7
|
* all sit on top of this. Target: merchants with an approved WhatsApp Business
|
|
8
8
|
* Account (WABA) who want Meta-direct pricing and full control.
|
|
9
9
|
*
|
|
10
|
-
* Tools (
|
|
10
|
+
* Tools (22):
|
|
11
11
|
* send_text_message — simple text message
|
|
12
12
|
* send_template_message — approved template (required for 24h+ business-initiated)
|
|
13
13
|
* send_media_message — image, video, document, or audio
|
|
14
|
-
* send_interactive_message — buttons or list
|
|
14
|
+
* send_interactive_message — buttons or list (generic)
|
|
15
|
+
* send_interactive_cta_url — interactive CTA URL button
|
|
16
|
+
* send_interactive_flow — WhatsApp Flows message
|
|
15
17
|
* send_location_message — latitude/longitude pin
|
|
18
|
+
* send_contacts_message — vCard-style contact cards
|
|
19
|
+
* send_reaction_message — emoji reaction to an inbound message
|
|
20
|
+
* send_typing_indicator — show typing indicator on a received message
|
|
16
21
|
* mark_message_as_read — mark an incoming message as read
|
|
17
22
|
* upload_media — upload a file and get a media_id (multipart)
|
|
18
23
|
* retrieve_media_url — resolve a media_id to a downloadable URL
|
|
19
24
|
* delete_media — delete an uploaded media asset
|
|
20
25
|
* list_templates — list templates on the WABA
|
|
21
26
|
* create_template — submit a new template for Meta review
|
|
27
|
+
* delete_template — delete a template by name
|
|
28
|
+
* get_business_profile — read business profile on the phone number
|
|
29
|
+
* update_business_profile — edit business profile fields
|
|
30
|
+
* list_phone_numbers — list phone numbers on the WABA
|
|
31
|
+
* request_verification_code — request SMS/voice code to verify a phone number
|
|
32
|
+
* verify_code — submit the received verification code
|
|
22
33
|
*
|
|
23
34
|
* Authentication
|
|
24
35
|
* Bearer token (permanent system-user access token).
|
|
@@ -86,7 +97,7 @@ async function whatsappRequest(method, path, body, opts = {}) {
|
|
|
86
97
|
const text = await res.text();
|
|
87
98
|
return text ? JSON.parse(text) : {};
|
|
88
99
|
}
|
|
89
|
-
const server = new Server({ name: "mcp-whatsapp-cloud", version: "0.1
|
|
100
|
+
const server = new Server({ name: "mcp-whatsapp-cloud", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
90
101
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
91
102
|
tools: [
|
|
92
103
|
{
|
|
@@ -151,6 +162,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
151
162
|
required: ["to", "interactive"],
|
|
152
163
|
},
|
|
153
164
|
},
|
|
165
|
+
{
|
|
166
|
+
name: "send_interactive_cta_url",
|
|
167
|
+
description: "Send an interactive message with a single CTA URL button. Opens the URL when the recipient taps it. Available without template approval inside the 24h window.",
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
172
|
+
body_text: { type: "string", description: "Main body text" },
|
|
173
|
+
button_text: { type: "string", description: "Label shown on the CTA button (max 20 chars)" },
|
|
174
|
+
url: { type: "string", description: "HTTPS URL the button opens" },
|
|
175
|
+
header_text: { type: "string", description: "Optional header text" },
|
|
176
|
+
footer_text: { type: "string", description: "Optional footer text" },
|
|
177
|
+
},
|
|
178
|
+
required: ["to", "body_text", "button_text", "url"],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: "send_interactive_flow",
|
|
183
|
+
description: "Send a WhatsApp Flow message. Flows are Meta's structured UI experiences (forms, appointment booking, etc.) rendered inside WhatsApp.",
|
|
184
|
+
inputSchema: {
|
|
185
|
+
type: "object",
|
|
186
|
+
properties: {
|
|
187
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
188
|
+
flow_id: { type: "string", description: "ID of the approved Flow" },
|
|
189
|
+
flow_token: { type: "string", description: "Opaque token your server correlates with this send" },
|
|
190
|
+
flow_cta: { type: "string", description: "Label on the button that opens the flow (max 20 chars)" },
|
|
191
|
+
body_text: { type: "string", description: "Body text above the CTA" },
|
|
192
|
+
header_text: { type: "string", description: "Optional header text" },
|
|
193
|
+
footer_text: { type: "string", description: "Optional footer text" },
|
|
194
|
+
flow_action: { type: "string", enum: ["navigate", "data_exchange"], description: "Flow action type (default navigate)" },
|
|
195
|
+
flow_action_payload: { type: "object", description: "Optional action payload: { screen, data }. Required for navigate." },
|
|
196
|
+
mode: { type: "string", enum: ["draft", "published"], description: "Flow mode. Use draft while testing." },
|
|
197
|
+
},
|
|
198
|
+
required: ["to", "flow_id", "flow_token", "flow_cta", "body_text"],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
154
201
|
{
|
|
155
202
|
name: "send_location_message",
|
|
156
203
|
description: "Send a location pin with latitude/longitude and optional name/address.",
|
|
@@ -166,6 +213,46 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
166
213
|
required: ["to", "latitude", "longitude"],
|
|
167
214
|
},
|
|
168
215
|
},
|
|
216
|
+
{
|
|
217
|
+
name: "send_contacts_message",
|
|
218
|
+
description: "Send one or more contact cards (vCard-like). Each contact includes name and at least one of phones, emails, addresses, urls, or org.",
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: "object",
|
|
221
|
+
properties: {
|
|
222
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
223
|
+
contacts: {
|
|
224
|
+
type: "array",
|
|
225
|
+
description: "Array of contact objects per Cloud API spec (name, phones, emails, addresses, org, urls, birthday).",
|
|
226
|
+
items: { type: "object" },
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
required: ["to", "contacts"],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "send_reaction_message",
|
|
234
|
+
description: "Send an emoji reaction on a previously received/sent message. Pass empty string for `emoji` to clear a reaction.",
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: "object",
|
|
237
|
+
properties: {
|
|
238
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
239
|
+
message_id: { type: "string", description: "wamid of the message being reacted to" },
|
|
240
|
+
emoji: { type: "string", description: "Single unicode emoji. Empty string clears the reaction." },
|
|
241
|
+
},
|
|
242
|
+
required: ["to", "message_id", "emoji"],
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
name: "send_typing_indicator",
|
|
247
|
+
description: "Show a typing indicator on a received message. Also marks the message as read. Indicator auto-clears after ~25s or when you reply.",
|
|
248
|
+
inputSchema: {
|
|
249
|
+
type: "object",
|
|
250
|
+
properties: {
|
|
251
|
+
message_id: { type: "string", description: "wamid of the inbound message to show the indicator on" },
|
|
252
|
+
},
|
|
253
|
+
required: ["message_id"],
|
|
254
|
+
},
|
|
255
|
+
},
|
|
169
256
|
{
|
|
170
257
|
name: "mark_message_as_read",
|
|
171
258
|
description: "Mark an incoming message as read so the sender sees the blue double-check. Uses the wamid from the inbound webhook.",
|
|
@@ -243,6 +330,76 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
243
330
|
required: ["name", "language", "category", "components"],
|
|
244
331
|
},
|
|
245
332
|
},
|
|
333
|
+
{
|
|
334
|
+
name: "delete_template",
|
|
335
|
+
description: "Delete a message template from the WABA by name. Optionally scope by hsm_id when two templates share a name across languages.",
|
|
336
|
+
inputSchema: {
|
|
337
|
+
type: "object",
|
|
338
|
+
properties: {
|
|
339
|
+
name: { type: "string", description: "Template name to delete" },
|
|
340
|
+
hsm_id: { type: "string", description: "Optional template id (when multiple languages share a name and only one should be deleted)" },
|
|
341
|
+
},
|
|
342
|
+
required: ["name"],
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: "get_business_profile",
|
|
347
|
+
description: "Read the WhatsApp business profile (about, description, email, websites, vertical, address) for the configured phone number.",
|
|
348
|
+
inputSchema: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {
|
|
351
|
+
fields: { type: "string", description: "Comma-separated field list. Default: about,address,description,email,profile_picture_url,websites,vertical" },
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
name: "update_business_profile",
|
|
357
|
+
description: "Update the business profile on the configured phone number. Supply only the fields you want to change.",
|
|
358
|
+
inputSchema: {
|
|
359
|
+
type: "object",
|
|
360
|
+
properties: {
|
|
361
|
+
about: { type: "string", description: "About text (max 139 chars)" },
|
|
362
|
+
address: { type: "string", description: "Physical address" },
|
|
363
|
+
description: { type: "string", description: "Business description" },
|
|
364
|
+
email: { type: "string", description: "Contact email" },
|
|
365
|
+
vertical: { type: "string", description: "Business vertical (e.g. RETAIL, EDU, HEALTH, FINANCE, OTHER)" },
|
|
366
|
+
websites: { type: "array", description: "Up to two URLs", items: { type: "string" } },
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
name: "list_phone_numbers",
|
|
372
|
+
description: "List all phone numbers registered under the WhatsApp Business Account, including display name, quality rating, and verification status.",
|
|
373
|
+
inputSchema: {
|
|
374
|
+
type: "object",
|
|
375
|
+
properties: {
|
|
376
|
+
limit: { type: "number", description: "Max results per page (default 25)" },
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
name: "request_verification_code",
|
|
382
|
+
description: "Request Meta to send a verification code to the configured phone number via SMS or voice. Use before verify_code.",
|
|
383
|
+
inputSchema: {
|
|
384
|
+
type: "object",
|
|
385
|
+
properties: {
|
|
386
|
+
code_method: { type: "string", enum: ["SMS", "VOICE"], description: "Delivery method for the verification code" },
|
|
387
|
+
language: { type: "string", description: "BCP-47 language tag for the voice/SMS copy (e.g. en_US, pt_BR)" },
|
|
388
|
+
},
|
|
389
|
+
required: ["code_method", "language"],
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
name: "verify_code",
|
|
394
|
+
description: "Submit the verification code received via SMS/voice after request_verification_code. Completes registration.",
|
|
395
|
+
inputSchema: {
|
|
396
|
+
type: "object",
|
|
397
|
+
properties: {
|
|
398
|
+
code: { type: "string", description: "Verification code received (digits only, strip dashes)" },
|
|
399
|
+
},
|
|
400
|
+
required: ["code"],
|
|
401
|
+
},
|
|
402
|
+
},
|
|
246
403
|
],
|
|
247
404
|
}));
|
|
248
405
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
@@ -308,6 +465,64 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
308
465
|
};
|
|
309
466
|
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
310
467
|
}
|
|
468
|
+
case "send_interactive_cta_url": {
|
|
469
|
+
const interactive = {
|
|
470
|
+
type: "cta_url",
|
|
471
|
+
body: { text: a.body_text },
|
|
472
|
+
action: {
|
|
473
|
+
name: "cta_url",
|
|
474
|
+
parameters: {
|
|
475
|
+
display_text: a.button_text,
|
|
476
|
+
url: a.url,
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
};
|
|
480
|
+
if (a.header_text)
|
|
481
|
+
interactive.header = { type: "text", text: a.header_text };
|
|
482
|
+
if (a.footer_text)
|
|
483
|
+
interactive.footer = { text: a.footer_text };
|
|
484
|
+
const body = {
|
|
485
|
+
messaging_product: "whatsapp",
|
|
486
|
+
recipient_type: "individual",
|
|
487
|
+
to: a.to,
|
|
488
|
+
type: "interactive",
|
|
489
|
+
interactive,
|
|
490
|
+
};
|
|
491
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
492
|
+
}
|
|
493
|
+
case "send_interactive_flow": {
|
|
494
|
+
const parameters = {
|
|
495
|
+
flow_message_version: "3",
|
|
496
|
+
flow_token: a.flow_token,
|
|
497
|
+
flow_id: a.flow_id,
|
|
498
|
+
flow_cta: a.flow_cta,
|
|
499
|
+
flow_action: a.flow_action ?? "navigate",
|
|
500
|
+
};
|
|
501
|
+
if (a.mode)
|
|
502
|
+
parameters.mode = a.mode;
|
|
503
|
+
if (a.flow_action_payload)
|
|
504
|
+
parameters.flow_action_payload = a.flow_action_payload;
|
|
505
|
+
const interactive = {
|
|
506
|
+
type: "flow",
|
|
507
|
+
body: { text: a.body_text },
|
|
508
|
+
action: {
|
|
509
|
+
name: "flow",
|
|
510
|
+
parameters,
|
|
511
|
+
},
|
|
512
|
+
};
|
|
513
|
+
if (a.header_text)
|
|
514
|
+
interactive.header = { type: "text", text: a.header_text };
|
|
515
|
+
if (a.footer_text)
|
|
516
|
+
interactive.footer = { text: a.footer_text };
|
|
517
|
+
const body = {
|
|
518
|
+
messaging_product: "whatsapp",
|
|
519
|
+
recipient_type: "individual",
|
|
520
|
+
to: a.to,
|
|
521
|
+
type: "interactive",
|
|
522
|
+
interactive,
|
|
523
|
+
};
|
|
524
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
525
|
+
}
|
|
311
526
|
case "send_location_message": {
|
|
312
527
|
const location = {
|
|
313
528
|
latitude: a.latitude,
|
|
@@ -326,6 +541,38 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
326
541
|
};
|
|
327
542
|
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
328
543
|
}
|
|
544
|
+
case "send_contacts_message": {
|
|
545
|
+
const body = {
|
|
546
|
+
messaging_product: "whatsapp",
|
|
547
|
+
recipient_type: "individual",
|
|
548
|
+
to: a.to,
|
|
549
|
+
type: "contacts",
|
|
550
|
+
contacts: a.contacts,
|
|
551
|
+
};
|
|
552
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
553
|
+
}
|
|
554
|
+
case "send_reaction_message": {
|
|
555
|
+
const body = {
|
|
556
|
+
messaging_product: "whatsapp",
|
|
557
|
+
recipient_type: "individual",
|
|
558
|
+
to: a.to,
|
|
559
|
+
type: "reaction",
|
|
560
|
+
reaction: {
|
|
561
|
+
message_id: a.message_id,
|
|
562
|
+
emoji: a.emoji,
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
566
|
+
}
|
|
567
|
+
case "send_typing_indicator": {
|
|
568
|
+
const body = {
|
|
569
|
+
messaging_product: "whatsapp",
|
|
570
|
+
status: "read",
|
|
571
|
+
message_id: a.message_id,
|
|
572
|
+
typing_indicator: { type: "text" },
|
|
573
|
+
};
|
|
574
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
575
|
+
}
|
|
329
576
|
case "mark_message_as_read": {
|
|
330
577
|
const body = {
|
|
331
578
|
messaging_product: "whatsapp",
|
|
@@ -373,6 +620,46 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
373
620
|
body.allow_category_change = a.allow_category_change;
|
|
374
621
|
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${BUSINESS_ACCOUNT_ID}/message_templates`, body), null, 2) }] };
|
|
375
622
|
}
|
|
623
|
+
case "delete_template": {
|
|
624
|
+
const q = new URLSearchParams();
|
|
625
|
+
q.set("name", String(a.name));
|
|
626
|
+
if (a.hsm_id)
|
|
627
|
+
q.set("hsm_id", String(a.hsm_id));
|
|
628
|
+
const path = `/${BUSINESS_ACCOUNT_ID}/message_templates?${q.toString()}`;
|
|
629
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("DELETE", path), null, 2) }] };
|
|
630
|
+
}
|
|
631
|
+
case "get_business_profile": {
|
|
632
|
+
const fields = a.fields ? String(a.fields) : "about,address,description,email,profile_picture_url,websites,vertical";
|
|
633
|
+
const path = `/${PHONE_NUMBER_ID}/whatsapp_business_profile?fields=${encodeURIComponent(fields)}`;
|
|
634
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("GET", path), null, 2) }] };
|
|
635
|
+
}
|
|
636
|
+
case "update_business_profile": {
|
|
637
|
+
const body = { messaging_product: "whatsapp" };
|
|
638
|
+
for (const k of ["about", "address", "description", "email", "vertical", "websites"]) {
|
|
639
|
+
if (a[k] !== undefined)
|
|
640
|
+
body[k] = a[k];
|
|
641
|
+
}
|
|
642
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/whatsapp_business_profile`, body), null, 2) }] };
|
|
643
|
+
}
|
|
644
|
+
case "list_phone_numbers": {
|
|
645
|
+
const q = new URLSearchParams();
|
|
646
|
+
if (a.limit !== undefined)
|
|
647
|
+
q.set("limit", String(a.limit));
|
|
648
|
+
const qs = q.toString();
|
|
649
|
+
const path = `/${BUSINESS_ACCOUNT_ID}/phone_numbers${qs ? `?${qs}` : ""}`;
|
|
650
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("GET", path), null, 2) }] };
|
|
651
|
+
}
|
|
652
|
+
case "request_verification_code": {
|
|
653
|
+
const body = {
|
|
654
|
+
code_method: a.code_method,
|
|
655
|
+
language: a.language,
|
|
656
|
+
};
|
|
657
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/request_code`, body), null, 2) }] };
|
|
658
|
+
}
|
|
659
|
+
case "verify_code": {
|
|
660
|
+
const body = { code: a.code };
|
|
661
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/verify_code`, body), null, 2) }] };
|
|
662
|
+
}
|
|
376
663
|
default:
|
|
377
664
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
378
665
|
}
|
|
@@ -399,7 +686,7 @@ async function main() {
|
|
|
399
686
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
400
687
|
t.onclose = () => { if (t.sessionId)
|
|
401
688
|
transports.delete(t.sessionId); };
|
|
402
|
-
const s = new Server({ name: "mcp-whatsapp-cloud", version: "0.1
|
|
689
|
+
const s = new Server({ name: "mcp-whatsapp-cloud", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
403
690
|
server._requestHandlers.forEach((v, k) => s._requestHandlers.set(k, v));
|
|
404
691
|
server._notificationHandlers?.forEach((v, k) => s._notificationHandlers.set(k, v));
|
|
405
692
|
await s.connect(t);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codespar/mcp-whatsapp-cloud",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "MCP server for WhatsApp Cloud API (Meta direct)
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "MCP server for WhatsApp Cloud API (Meta direct) \u2014 official Graph API integration for messages, media, and templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"bin": {
|
package/server.json
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"source": "github",
|
|
8
8
|
"subfolder": "packages/communication/whatsapp-cloud"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.1
|
|
10
|
+
"version": "0.2.1",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"identifier": "@codespar/mcp-whatsapp-cloud",
|
|
15
|
-
"version": "0.1
|
|
15
|
+
"version": "0.2.1",
|
|
16
16
|
"transport": {
|
|
17
17
|
"type": "stdio"
|
|
18
18
|
},
|
package/src/index.ts
CHANGED
|
@@ -8,18 +8,29 @@
|
|
|
8
8
|
* all sit on top of this. Target: merchants with an approved WhatsApp Business
|
|
9
9
|
* Account (WABA) who want Meta-direct pricing and full control.
|
|
10
10
|
*
|
|
11
|
-
* Tools (
|
|
11
|
+
* Tools (22):
|
|
12
12
|
* send_text_message — simple text message
|
|
13
13
|
* send_template_message — approved template (required for 24h+ business-initiated)
|
|
14
14
|
* send_media_message — image, video, document, or audio
|
|
15
|
-
* send_interactive_message — buttons or list
|
|
15
|
+
* send_interactive_message — buttons or list (generic)
|
|
16
|
+
* send_interactive_cta_url — interactive CTA URL button
|
|
17
|
+
* send_interactive_flow — WhatsApp Flows message
|
|
16
18
|
* send_location_message — latitude/longitude pin
|
|
19
|
+
* send_contacts_message — vCard-style contact cards
|
|
20
|
+
* send_reaction_message — emoji reaction to an inbound message
|
|
21
|
+
* send_typing_indicator — show typing indicator on a received message
|
|
17
22
|
* mark_message_as_read — mark an incoming message as read
|
|
18
23
|
* upload_media — upload a file and get a media_id (multipart)
|
|
19
24
|
* retrieve_media_url — resolve a media_id to a downloadable URL
|
|
20
25
|
* delete_media — delete an uploaded media asset
|
|
21
26
|
* list_templates — list templates on the WABA
|
|
22
27
|
* create_template — submit a new template for Meta review
|
|
28
|
+
* delete_template — delete a template by name
|
|
29
|
+
* get_business_profile — read business profile on the phone number
|
|
30
|
+
* update_business_profile — edit business profile fields
|
|
31
|
+
* list_phone_numbers — list phone numbers on the WABA
|
|
32
|
+
* request_verification_code — request SMS/voice code to verify a phone number
|
|
33
|
+
* verify_code — submit the received verification code
|
|
23
34
|
*
|
|
24
35
|
* Authentication
|
|
25
36
|
* Bearer token (permanent system-user access token).
|
|
@@ -103,7 +114,7 @@ async function whatsappRequest(
|
|
|
103
114
|
}
|
|
104
115
|
|
|
105
116
|
const server = new Server(
|
|
106
|
-
{ name: "mcp-whatsapp-cloud", version: "0.1
|
|
117
|
+
{ name: "mcp-whatsapp-cloud", version: "0.2.1" },
|
|
107
118
|
{ capabilities: { tools: {} } }
|
|
108
119
|
);
|
|
109
120
|
|
|
@@ -171,6 +182,42 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
171
182
|
required: ["to", "interactive"],
|
|
172
183
|
},
|
|
173
184
|
},
|
|
185
|
+
{
|
|
186
|
+
name: "send_interactive_cta_url",
|
|
187
|
+
description: "Send an interactive message with a single CTA URL button. Opens the URL when the recipient taps it. Available without template approval inside the 24h window.",
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: "object",
|
|
190
|
+
properties: {
|
|
191
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
192
|
+
body_text: { type: "string", description: "Main body text" },
|
|
193
|
+
button_text: { type: "string", description: "Label shown on the CTA button (max 20 chars)" },
|
|
194
|
+
url: { type: "string", description: "HTTPS URL the button opens" },
|
|
195
|
+
header_text: { type: "string", description: "Optional header text" },
|
|
196
|
+
footer_text: { type: "string", description: "Optional footer text" },
|
|
197
|
+
},
|
|
198
|
+
required: ["to", "body_text", "button_text", "url"],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "send_interactive_flow",
|
|
203
|
+
description: "Send a WhatsApp Flow message. Flows are Meta's structured UI experiences (forms, appointment booking, etc.) rendered inside WhatsApp.",
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: "object",
|
|
206
|
+
properties: {
|
|
207
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
208
|
+
flow_id: { type: "string", description: "ID of the approved Flow" },
|
|
209
|
+
flow_token: { type: "string", description: "Opaque token your server correlates with this send" },
|
|
210
|
+
flow_cta: { type: "string", description: "Label on the button that opens the flow (max 20 chars)" },
|
|
211
|
+
body_text: { type: "string", description: "Body text above the CTA" },
|
|
212
|
+
header_text: { type: "string", description: "Optional header text" },
|
|
213
|
+
footer_text: { type: "string", description: "Optional footer text" },
|
|
214
|
+
flow_action: { type: "string", enum: ["navigate", "data_exchange"], description: "Flow action type (default navigate)" },
|
|
215
|
+
flow_action_payload: { type: "object", description: "Optional action payload: { screen, data }. Required for navigate." },
|
|
216
|
+
mode: { type: "string", enum: ["draft", "published"], description: "Flow mode. Use draft while testing." },
|
|
217
|
+
},
|
|
218
|
+
required: ["to", "flow_id", "flow_token", "flow_cta", "body_text"],
|
|
219
|
+
},
|
|
220
|
+
},
|
|
174
221
|
{
|
|
175
222
|
name: "send_location_message",
|
|
176
223
|
description: "Send a location pin with latitude/longitude and optional name/address.",
|
|
@@ -186,6 +233,46 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
186
233
|
required: ["to", "latitude", "longitude"],
|
|
187
234
|
},
|
|
188
235
|
},
|
|
236
|
+
{
|
|
237
|
+
name: "send_contacts_message",
|
|
238
|
+
description: "Send one or more contact cards (vCard-like). Each contact includes name and at least one of phones, emails, addresses, urls, or org.",
|
|
239
|
+
inputSchema: {
|
|
240
|
+
type: "object",
|
|
241
|
+
properties: {
|
|
242
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
243
|
+
contacts: {
|
|
244
|
+
type: "array",
|
|
245
|
+
description: "Array of contact objects per Cloud API spec (name, phones, emails, addresses, org, urls, birthday).",
|
|
246
|
+
items: { type: "object" },
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
required: ["to", "contacts"],
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: "send_reaction_message",
|
|
254
|
+
description: "Send an emoji reaction on a previously received/sent message. Pass empty string for `emoji` to clear a reaction.",
|
|
255
|
+
inputSchema: {
|
|
256
|
+
type: "object",
|
|
257
|
+
properties: {
|
|
258
|
+
to: { type: "string", description: "Recipient phone number in E.164 without +" },
|
|
259
|
+
message_id: { type: "string", description: "wamid of the message being reacted to" },
|
|
260
|
+
emoji: { type: "string", description: "Single unicode emoji. Empty string clears the reaction." },
|
|
261
|
+
},
|
|
262
|
+
required: ["to", "message_id", "emoji"],
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: "send_typing_indicator",
|
|
267
|
+
description: "Show a typing indicator on a received message. Also marks the message as read. Indicator auto-clears after ~25s or when you reply.",
|
|
268
|
+
inputSchema: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: {
|
|
271
|
+
message_id: { type: "string", description: "wamid of the inbound message to show the indicator on" },
|
|
272
|
+
},
|
|
273
|
+
required: ["message_id"],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
189
276
|
{
|
|
190
277
|
name: "mark_message_as_read",
|
|
191
278
|
description: "Mark an incoming message as read so the sender sees the blue double-check. Uses the wamid from the inbound webhook.",
|
|
@@ -263,6 +350,76 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
263
350
|
required: ["name", "language", "category", "components"],
|
|
264
351
|
},
|
|
265
352
|
},
|
|
353
|
+
{
|
|
354
|
+
name: "delete_template",
|
|
355
|
+
description: "Delete a message template from the WABA by name. Optionally scope by hsm_id when two templates share a name across languages.",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: "object",
|
|
358
|
+
properties: {
|
|
359
|
+
name: { type: "string", description: "Template name to delete" },
|
|
360
|
+
hsm_id: { type: "string", description: "Optional template id (when multiple languages share a name and only one should be deleted)" },
|
|
361
|
+
},
|
|
362
|
+
required: ["name"],
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: "get_business_profile",
|
|
367
|
+
description: "Read the WhatsApp business profile (about, description, email, websites, vertical, address) for the configured phone number.",
|
|
368
|
+
inputSchema: {
|
|
369
|
+
type: "object",
|
|
370
|
+
properties: {
|
|
371
|
+
fields: { type: "string", description: "Comma-separated field list. Default: about,address,description,email,profile_picture_url,websites,vertical" },
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
name: "update_business_profile",
|
|
377
|
+
description: "Update the business profile on the configured phone number. Supply only the fields you want to change.",
|
|
378
|
+
inputSchema: {
|
|
379
|
+
type: "object",
|
|
380
|
+
properties: {
|
|
381
|
+
about: { type: "string", description: "About text (max 139 chars)" },
|
|
382
|
+
address: { type: "string", description: "Physical address" },
|
|
383
|
+
description: { type: "string", description: "Business description" },
|
|
384
|
+
email: { type: "string", description: "Contact email" },
|
|
385
|
+
vertical: { type: "string", description: "Business vertical (e.g. RETAIL, EDU, HEALTH, FINANCE, OTHER)" },
|
|
386
|
+
websites: { type: "array", description: "Up to two URLs", items: { type: "string" } },
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
name: "list_phone_numbers",
|
|
392
|
+
description: "List all phone numbers registered under the WhatsApp Business Account, including display name, quality rating, and verification status.",
|
|
393
|
+
inputSchema: {
|
|
394
|
+
type: "object",
|
|
395
|
+
properties: {
|
|
396
|
+
limit: { type: "number", description: "Max results per page (default 25)" },
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
name: "request_verification_code",
|
|
402
|
+
description: "Request Meta to send a verification code to the configured phone number via SMS or voice. Use before verify_code.",
|
|
403
|
+
inputSchema: {
|
|
404
|
+
type: "object",
|
|
405
|
+
properties: {
|
|
406
|
+
code_method: { type: "string", enum: ["SMS", "VOICE"], description: "Delivery method for the verification code" },
|
|
407
|
+
language: { type: "string", description: "BCP-47 language tag for the voice/SMS copy (e.g. en_US, pt_BR)" },
|
|
408
|
+
},
|
|
409
|
+
required: ["code_method", "language"],
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
name: "verify_code",
|
|
414
|
+
description: "Submit the verification code received via SMS/voice after request_verification_code. Completes registration.",
|
|
415
|
+
inputSchema: {
|
|
416
|
+
type: "object",
|
|
417
|
+
properties: {
|
|
418
|
+
code: { type: "string", description: "Verification code received (digits only, strip dashes)" },
|
|
419
|
+
},
|
|
420
|
+
required: ["code"],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
266
423
|
],
|
|
267
424
|
}));
|
|
268
425
|
|
|
@@ -325,6 +482,58 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
325
482
|
};
|
|
326
483
|
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
327
484
|
}
|
|
485
|
+
case "send_interactive_cta_url": {
|
|
486
|
+
const interactive: Record<string, unknown> = {
|
|
487
|
+
type: "cta_url",
|
|
488
|
+
body: { text: a.body_text },
|
|
489
|
+
action: {
|
|
490
|
+
name: "cta_url",
|
|
491
|
+
parameters: {
|
|
492
|
+
display_text: a.button_text,
|
|
493
|
+
url: a.url,
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
if (a.header_text) interactive.header = { type: "text", text: a.header_text };
|
|
498
|
+
if (a.footer_text) interactive.footer = { text: a.footer_text };
|
|
499
|
+
const body = {
|
|
500
|
+
messaging_product: "whatsapp",
|
|
501
|
+
recipient_type: "individual",
|
|
502
|
+
to: a.to,
|
|
503
|
+
type: "interactive",
|
|
504
|
+
interactive,
|
|
505
|
+
};
|
|
506
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
507
|
+
}
|
|
508
|
+
case "send_interactive_flow": {
|
|
509
|
+
const parameters: Record<string, unknown> = {
|
|
510
|
+
flow_message_version: "3",
|
|
511
|
+
flow_token: a.flow_token,
|
|
512
|
+
flow_id: a.flow_id,
|
|
513
|
+
flow_cta: a.flow_cta,
|
|
514
|
+
flow_action: a.flow_action ?? "navigate",
|
|
515
|
+
};
|
|
516
|
+
if (a.mode) parameters.mode = a.mode;
|
|
517
|
+
if (a.flow_action_payload) parameters.flow_action_payload = a.flow_action_payload;
|
|
518
|
+
const interactive: Record<string, unknown> = {
|
|
519
|
+
type: "flow",
|
|
520
|
+
body: { text: a.body_text },
|
|
521
|
+
action: {
|
|
522
|
+
name: "flow",
|
|
523
|
+
parameters,
|
|
524
|
+
},
|
|
525
|
+
};
|
|
526
|
+
if (a.header_text) interactive.header = { type: "text", text: a.header_text };
|
|
527
|
+
if (a.footer_text) interactive.footer = { text: a.footer_text };
|
|
528
|
+
const body = {
|
|
529
|
+
messaging_product: "whatsapp",
|
|
530
|
+
recipient_type: "individual",
|
|
531
|
+
to: a.to,
|
|
532
|
+
type: "interactive",
|
|
533
|
+
interactive,
|
|
534
|
+
};
|
|
535
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
536
|
+
}
|
|
328
537
|
case "send_location_message": {
|
|
329
538
|
const location: Record<string, unknown> = {
|
|
330
539
|
latitude: a.latitude,
|
|
@@ -341,6 +550,38 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
341
550
|
};
|
|
342
551
|
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
343
552
|
}
|
|
553
|
+
case "send_contacts_message": {
|
|
554
|
+
const body = {
|
|
555
|
+
messaging_product: "whatsapp",
|
|
556
|
+
recipient_type: "individual",
|
|
557
|
+
to: a.to,
|
|
558
|
+
type: "contacts",
|
|
559
|
+
contacts: a.contacts,
|
|
560
|
+
};
|
|
561
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
562
|
+
}
|
|
563
|
+
case "send_reaction_message": {
|
|
564
|
+
const body = {
|
|
565
|
+
messaging_product: "whatsapp",
|
|
566
|
+
recipient_type: "individual",
|
|
567
|
+
to: a.to,
|
|
568
|
+
type: "reaction",
|
|
569
|
+
reaction: {
|
|
570
|
+
message_id: a.message_id,
|
|
571
|
+
emoji: a.emoji,
|
|
572
|
+
},
|
|
573
|
+
};
|
|
574
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
575
|
+
}
|
|
576
|
+
case "send_typing_indicator": {
|
|
577
|
+
const body = {
|
|
578
|
+
messaging_product: "whatsapp",
|
|
579
|
+
status: "read",
|
|
580
|
+
message_id: a.message_id,
|
|
581
|
+
typing_indicator: { type: "text" },
|
|
582
|
+
};
|
|
583
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/messages`, body), null, 2) }] };
|
|
584
|
+
}
|
|
344
585
|
case "mark_message_as_read": {
|
|
345
586
|
const body = {
|
|
346
587
|
messaging_product: "whatsapp",
|
|
@@ -384,6 +625,43 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
384
625
|
if (a.allow_category_change !== undefined) body.allow_category_change = a.allow_category_change;
|
|
385
626
|
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${BUSINESS_ACCOUNT_ID}/message_templates`, body), null, 2) }] };
|
|
386
627
|
}
|
|
628
|
+
case "delete_template": {
|
|
629
|
+
const q = new URLSearchParams();
|
|
630
|
+
q.set("name", String(a.name));
|
|
631
|
+
if (a.hsm_id) q.set("hsm_id", String(a.hsm_id));
|
|
632
|
+
const path = `/${BUSINESS_ACCOUNT_ID}/message_templates?${q.toString()}`;
|
|
633
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("DELETE", path), null, 2) }] };
|
|
634
|
+
}
|
|
635
|
+
case "get_business_profile": {
|
|
636
|
+
const fields = a.fields ? String(a.fields) : "about,address,description,email,profile_picture_url,websites,vertical";
|
|
637
|
+
const path = `/${PHONE_NUMBER_ID}/whatsapp_business_profile?fields=${encodeURIComponent(fields)}`;
|
|
638
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("GET", path), null, 2) }] };
|
|
639
|
+
}
|
|
640
|
+
case "update_business_profile": {
|
|
641
|
+
const body: Record<string, unknown> = { messaging_product: "whatsapp" };
|
|
642
|
+
for (const k of ["about", "address", "description", "email", "vertical", "websites"]) {
|
|
643
|
+
if (a[k] !== undefined) body[k] = a[k];
|
|
644
|
+
}
|
|
645
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/whatsapp_business_profile`, body), null, 2) }] };
|
|
646
|
+
}
|
|
647
|
+
case "list_phone_numbers": {
|
|
648
|
+
const q = new URLSearchParams();
|
|
649
|
+
if (a.limit !== undefined) q.set("limit", String(a.limit));
|
|
650
|
+
const qs = q.toString();
|
|
651
|
+
const path = `/${BUSINESS_ACCOUNT_ID}/phone_numbers${qs ? `?${qs}` : ""}`;
|
|
652
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("GET", path), null, 2) }] };
|
|
653
|
+
}
|
|
654
|
+
case "request_verification_code": {
|
|
655
|
+
const body = {
|
|
656
|
+
code_method: a.code_method,
|
|
657
|
+
language: a.language,
|
|
658
|
+
};
|
|
659
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/request_code`, body), null, 2) }] };
|
|
660
|
+
}
|
|
661
|
+
case "verify_code": {
|
|
662
|
+
const body = { code: a.code };
|
|
663
|
+
return { content: [{ type: "text", text: JSON.stringify(await whatsappRequest("POST", `/${PHONE_NUMBER_ID}/verify_code`, body), null, 2) }] };
|
|
664
|
+
}
|
|
387
665
|
default:
|
|
388
666
|
return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
|
|
389
667
|
}
|
|
@@ -406,7 +684,7 @@ async function main() {
|
|
|
406
684
|
if (!sid && isInitializeRequest(req.body)) {
|
|
407
685
|
const t = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID(), onsessioninitialized: (id) => { transports.set(id, t); } });
|
|
408
686
|
t.onclose = () => { if (t.sessionId) transports.delete(t.sessionId); };
|
|
409
|
-
const s = new Server({ name: "mcp-whatsapp-cloud", version: "0.1
|
|
687
|
+
const s = new Server({ name: "mcp-whatsapp-cloud", version: "0.2.1" }, { capabilities: { tools: {} } });
|
|
410
688
|
(server as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.forEach((v, k) => (s as unknown as { _requestHandlers: Map<unknown, unknown> })._requestHandlers.set(k, v));
|
|
411
689
|
(server as unknown as { _notificationHandlers?: Map<unknown, unknown> })._notificationHandlers?.forEach((v, k) => (s as unknown as { _notificationHandlers: Map<unknown, unknown> })._notificationHandlers.set(k, v));
|
|
412
690
|
await s.connect(t);
|