@wabery/cli 0.12.0 → 0.14.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 +60 -9
- package/dist/api-client.d.ts +3 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +8 -0
- package/dist/api-client.js.map +1 -1
- package/dist/cli.js +26 -4
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +559 -107
- package/dist/mcp-server.js.map +1 -1
- package/package.json +2 -2
package/dist/mcp-server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
1
|
+
import { McpServer, } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { WaberyApiClient, WaberyApiError } from "./api-client.js";
|
|
4
4
|
import { createExampleConfig } from "./config-example.js";
|
|
@@ -16,6 +16,11 @@ const readinessRequirementSchema = z.enum([
|
|
|
16
16
|
]);
|
|
17
17
|
const optInSchema = z
|
|
18
18
|
.object({
|
|
19
|
+
categories: z
|
|
20
|
+
.array(z.enum(["GENERAL", "MARKETING", "UTILITY", "AUTHENTICATION"]))
|
|
21
|
+
.min(1)
|
|
22
|
+
.max(4)
|
|
23
|
+
.optional(),
|
|
19
24
|
source: z.string().min(1).max(120),
|
|
20
25
|
method: z.string().min(1).max(80).optional(),
|
|
21
26
|
occurred_at: z.string().datetime().optional(),
|
|
@@ -23,9 +28,67 @@ const optInSchema = z
|
|
|
23
28
|
evidence: z.record(z.string(), z.unknown()).optional(),
|
|
24
29
|
})
|
|
25
30
|
.optional();
|
|
26
|
-
const
|
|
31
|
+
const broadcastStatusSchema = z.enum([
|
|
32
|
+
"draft",
|
|
33
|
+
"preparing",
|
|
34
|
+
"ready",
|
|
35
|
+
"scheduled",
|
|
36
|
+
"sending",
|
|
37
|
+
"completed",
|
|
38
|
+
"canceled",
|
|
39
|
+
"failed",
|
|
40
|
+
]);
|
|
41
|
+
const broadcastRecipientStatusSchema = z.enum([
|
|
42
|
+
"pending",
|
|
43
|
+
"queued",
|
|
44
|
+
"sending",
|
|
45
|
+
"sent",
|
|
46
|
+
"delivered",
|
|
47
|
+
"read",
|
|
48
|
+
"failed",
|
|
49
|
+
"skipped",
|
|
50
|
+
"canceled",
|
|
51
|
+
]);
|
|
52
|
+
const broadcastParameterMappingSchema = z.object({
|
|
53
|
+
component: z.enum(["header", "body", "button"]),
|
|
54
|
+
index: z.number().int().min(0),
|
|
55
|
+
button_index: z.number().int().min(0).optional(),
|
|
56
|
+
parameter_type: z
|
|
57
|
+
.enum(["text", "image", "document", "video"])
|
|
58
|
+
.default("text"),
|
|
59
|
+
source: z.enum([
|
|
60
|
+
"name",
|
|
61
|
+
"phone",
|
|
62
|
+
"preferred_language",
|
|
63
|
+
"metadata",
|
|
64
|
+
"constant",
|
|
65
|
+
]),
|
|
66
|
+
metadata_key: z.string().min(1).max(120).optional(),
|
|
67
|
+
value: z.string().max(2000).optional(),
|
|
68
|
+
fallback: z.string().max(2000).optional(),
|
|
69
|
+
});
|
|
70
|
+
const broadcastVariantSchema = z.object({
|
|
71
|
+
template_id: z.string().min(1),
|
|
72
|
+
language: z.string().min(2).max(35),
|
|
73
|
+
parameter_mappings: z.array(broadcastParameterMappingSchema).max(100),
|
|
74
|
+
});
|
|
75
|
+
const broadcastAudienceFilterSchema = z.object({
|
|
76
|
+
search: z.string().max(120).optional(),
|
|
77
|
+
tags: z.array(z.string().min(1).max(60)).max(50).optional(),
|
|
78
|
+
languages: z.array(z.string().min(2).max(35)).max(50).optional(),
|
|
79
|
+
is_blocked: z.boolean().optional(),
|
|
80
|
+
created_after: z.string().datetime().optional(),
|
|
81
|
+
created_before: z.string().datetime().optional(),
|
|
82
|
+
});
|
|
83
|
+
const toolOutputSchema = {
|
|
84
|
+
result: z
|
|
85
|
+
.record(z.string(), z.unknown())
|
|
86
|
+
.describe("The structured Wabery API result object. Its operation-specific fields are described by the tool and Wabery API documentation."),
|
|
87
|
+
};
|
|
88
|
+
const SERVER_INSTRUCTIONS = `Use Wabery tools as a developer platform for WhatsApp, Instagram, and Messenger messaging: channels, routing, contacts, registration intents, conversations, message history, outbound messages, inbound media metadata, WhatsApp Flows, templates, broadcasts, automations, webhooks, and Meta Business Agent extensions.
|
|
27
89
|
Recommended order: read wabery_get_config_schema, select the project, call wabery_get_project_readiness, list channels only when more channel detail is needed, check Business Agent eligibility before adding business_agent config, create registration intents or enroll WhatsApp contacts as needed, store returned contact_id plus channel_id, validate and preview config diff, apply config only after review, publish flows only after explicit confirmation, then send messages or published flows with channel_id and contact_id/conversation_id.
|
|
28
90
|
Always distinguish sandbox testing from production readiness: a project without its own connected phone number can often still be tested in the shared Wabery sandbox, but production inbound traffic requires a dedicated project channel. For WhatsApp templates and proactive reminders, inspect readiness before creation: template submission and proactive template sends require a dedicated non-sandbox WhatsApp channel, Meta business verification/account approval, a connected phone, and a Meta payment method. After sending a message, use wabery_get_message to inspect provider status and failures.
|
|
91
|
+
For broadcasts, use only approved WhatsApp templates on a dedicated channel and only contacts with valid category-specific consent. Create a draft, prepare exactly one audience source, poll wabery_get_broadcast until it is ready, inspect its counts and excluded recipients, and send or schedule only after explicit user confirmation. Preparation freezes the recipient snapshot; create a new draft when the intended audience changes. CSV files are uploaded through the dashboard or CLI; use wabery_list_contact_imports and wabery_get_contact_import to select a completed import.
|
|
29
92
|
Use wabery_list_conversations, wabery_get_conversation, and wabery_list_conversation_messages to inspect history. Inbound WhatsApp media appears on message records/webhooks as media/assets metadata with Wabery signed URLs when retained; download files promptly because URLs are short-lived and retention is plan-based.
|
|
30
93
|
Config apply creates or updates Wabery flows, automations, webhook settings, and eligible Meta Business Agent declarations. It never publishes flows to Meta. Automations and Business Agent connector tools can trigger flows through references to flow config keys.
|
|
31
94
|
High-risk tools require confirmation_token values shown in each tool description.`;
|
|
@@ -40,10 +103,57 @@ const WRITE_NOTE = {
|
|
|
40
103
|
function serverInstructions(writePolicy) {
|
|
41
104
|
return `${SERVER_INSTRUCTIONS}\n${WRITE_NOTE[writePolicy]}`;
|
|
42
105
|
}
|
|
106
|
+
const MCP_SENSITIVE_OUTPUT_KEY_SUFFIXES = [
|
|
107
|
+
"accesstoken",
|
|
108
|
+
"apikey",
|
|
109
|
+
"authorizationheader",
|
|
110
|
+
"authtoken",
|
|
111
|
+
"bearertoken",
|
|
112
|
+
"credential",
|
|
113
|
+
"credentials",
|
|
114
|
+
"idtoken",
|
|
115
|
+
"jwt",
|
|
116
|
+
"password",
|
|
117
|
+
"passwordhash",
|
|
118
|
+
"privatekey",
|
|
119
|
+
"providertoken",
|
|
120
|
+
"refreshtoken",
|
|
121
|
+
"secret",
|
|
122
|
+
"serviceproof",
|
|
123
|
+
"sessiontoken",
|
|
124
|
+
"signingkey",
|
|
125
|
+
"stacktrace",
|
|
126
|
+
];
|
|
127
|
+
const MCP_INTERNAL_OUTPUT_KEYS = new Set([
|
|
128
|
+
"debugid",
|
|
129
|
+
"internaldebugid",
|
|
130
|
+
"requestid",
|
|
131
|
+
"traceid",
|
|
132
|
+
]);
|
|
133
|
+
function isSensitiveMcpOutputKey(key) {
|
|
134
|
+
const normalizedKey = key.replaceAll(/[^a-zA-Z0-9]/g, "").toLowerCase();
|
|
135
|
+
return (MCP_INTERNAL_OUTPUT_KEYS.has(normalizedKey) ||
|
|
136
|
+
MCP_SENSITIVE_OUTPUT_KEY_SUFFIXES.some((suffix) => normalizedKey.endsWith(suffix)));
|
|
137
|
+
}
|
|
138
|
+
function sanitizeMcpOutput(data) {
|
|
139
|
+
if (Array.isArray(data)) {
|
|
140
|
+
return data.map((item) => sanitizeMcpOutput(item));
|
|
141
|
+
}
|
|
142
|
+
if (!data || typeof data !== "object") {
|
|
143
|
+
return data;
|
|
144
|
+
}
|
|
145
|
+
return Object.fromEntries(Object.entries(data).flatMap(([key, value]) => isSensitiveMcpOutputKey(key) ? [] : [[key, sanitizeMcpOutput(value)]]));
|
|
146
|
+
}
|
|
43
147
|
function jsonResult(data) {
|
|
148
|
+
const sanitizedData = sanitizeMcpOutput(data);
|
|
44
149
|
return {
|
|
45
|
-
content: [
|
|
46
|
-
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: JSON.stringify(sanitizedData, null, 2),
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
structuredContent: { result: sanitizedData },
|
|
47
157
|
};
|
|
48
158
|
}
|
|
49
159
|
function errorResult(data) {
|
|
@@ -89,11 +199,85 @@ function checkWriteMode(action, token, expectedToken, writePolicy = "env") {
|
|
|
89
199
|
}
|
|
90
200
|
export function createWaberyMcpServer(client = new WaberyApiClient(), options = {}) {
|
|
91
201
|
const writePolicy = options.writePolicy ?? "env";
|
|
92
|
-
const requireWrite = (action, token, expectedToken) => checkWriteMode(action, token, expectedToken, writePolicy);
|
|
93
202
|
const server = new McpServer({
|
|
94
203
|
name: "wabery",
|
|
95
204
|
version: options.serverVersion ?? "0.1.0",
|
|
96
205
|
}, { instructions: serverInstructions(writePolicy) });
|
|
206
|
+
const registerTool = (name, config, callback) => server.registerTool(name, {
|
|
207
|
+
outputSchema: toolOutputSchema,
|
|
208
|
+
...config,
|
|
209
|
+
}, callback);
|
|
210
|
+
const requireWrite = async (action, token, expectedToken) => {
|
|
211
|
+
const modeBlocked = checkWriteMode(action, undefined, undefined, writePolicy);
|
|
212
|
+
if (modeBlocked)
|
|
213
|
+
return modeBlocked;
|
|
214
|
+
if (!expectedToken)
|
|
215
|
+
return null;
|
|
216
|
+
if (server.server.getClientCapabilities()?.elicitation) {
|
|
217
|
+
const response = await server.server.elicitInput({
|
|
218
|
+
message: `Approve the high-risk Wabery action: ${action}`,
|
|
219
|
+
requestedSchema: {
|
|
220
|
+
type: "object",
|
|
221
|
+
properties: {
|
|
222
|
+
confirm: {
|
|
223
|
+
type: "boolean",
|
|
224
|
+
title: "Confirm action",
|
|
225
|
+
description: "Approve this external or destructive action",
|
|
226
|
+
default: false,
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
required: ["confirm"],
|
|
230
|
+
},
|
|
231
|
+
});
|
|
232
|
+
if (response.action === "accept" && response.content?.confirm === true) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return errorResult({
|
|
236
|
+
action,
|
|
237
|
+
message: "The user declined or canceled the action.",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
if (writePolicy === "enabled") {
|
|
241
|
+
return errorResult({
|
|
242
|
+
action,
|
|
243
|
+
message: "This hosted MCP client must support elicitation so Wabery can obtain real user approval.",
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return checkWriteMode(action, token, expectedToken, writePolicy);
|
|
247
|
+
};
|
|
248
|
+
const requireBroadcastApproval = async (action, message, token, localConfirmationToken) => {
|
|
249
|
+
if (server.server.getClientCapabilities()?.elicitation) {
|
|
250
|
+
const response = await server.server.elicitInput({
|
|
251
|
+
message,
|
|
252
|
+
requestedSchema: {
|
|
253
|
+
type: "object",
|
|
254
|
+
properties: {
|
|
255
|
+
confirm: {
|
|
256
|
+
type: "boolean",
|
|
257
|
+
title: "Confirm broadcast action",
|
|
258
|
+
description: "Approve this external messaging action",
|
|
259
|
+
default: false,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
required: ["confirm"],
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
if (response.action === "accept" && response.content?.confirm === true) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
return errorResult({
|
|
269
|
+
action,
|
|
270
|
+
message: "The user declined or canceled the broadcast action.",
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (writePolicy === "enabled") {
|
|
274
|
+
return errorResult({
|
|
275
|
+
action,
|
|
276
|
+
message: "This hosted MCP client must support elicitation so Wabery can obtain real user approval.",
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return await requireWrite(action, token, localConfirmationToken);
|
|
280
|
+
};
|
|
97
281
|
const resolveProjectId = async (projectId) => {
|
|
98
282
|
if (projectId)
|
|
99
283
|
return projectId;
|
|
@@ -164,7 +348,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
164
348
|
},
|
|
165
349
|
],
|
|
166
350
|
}));
|
|
167
|
-
|
|
351
|
+
registerTool("wabery_check_connection", {
|
|
168
352
|
title: "Check Wabery connection",
|
|
169
353
|
description: "Verify the configured API key can reach Wabery projects, channels, and the config schema before making changes.",
|
|
170
354
|
inputSchema: {},
|
|
@@ -175,7 +359,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
175
359
|
openWorldHint: false,
|
|
176
360
|
},
|
|
177
361
|
}, async () => jsonResult(await checkWaberyConnection(client)));
|
|
178
|
-
|
|
362
|
+
registerTool("wabery_get_config_example", {
|
|
179
363
|
title: "Get example Wabery config",
|
|
180
364
|
description: "Return a starter wabery.config.json with one flow and one automation that triggers it with a send_flow step.",
|
|
181
365
|
inputSchema: {
|
|
@@ -192,7 +376,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
192
376
|
openWorldHint: false,
|
|
193
377
|
},
|
|
194
378
|
}, async ({ project_id }) => jsonResult(createExampleConfig(project_id)));
|
|
195
|
-
|
|
379
|
+
registerTool("wabery_get_config_schema", {
|
|
196
380
|
title: "Get Wabery config schema",
|
|
197
381
|
description: "Return the JSON Schema for wabery.config.json. Use this before generating flows, automations, webhooks, or business_agent declarations.",
|
|
198
382
|
inputSchema: {},
|
|
@@ -203,7 +387,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
203
387
|
openWorldHint: false,
|
|
204
388
|
},
|
|
205
389
|
}, async () => jsonResult(await client.get("/config/schema")));
|
|
206
|
-
|
|
390
|
+
registerTool("wabery_export_config", {
|
|
207
391
|
title: "Export Wabery config",
|
|
208
392
|
description: "Export the organization's flows, automations, webhook settings, and Business Agent declarations as a config object.",
|
|
209
393
|
inputSchema: {},
|
|
@@ -214,7 +398,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
214
398
|
openWorldHint: false,
|
|
215
399
|
},
|
|
216
400
|
}, async () => jsonResult(await client.get("/config")));
|
|
217
|
-
|
|
401
|
+
registerTool("wabery_validate_config", {
|
|
218
402
|
title: "Validate Wabery config",
|
|
219
403
|
description: "Validate a declarative config object without changing flows or automations.",
|
|
220
404
|
inputSchema: {
|
|
@@ -227,7 +411,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
227
411
|
openWorldHint: false,
|
|
228
412
|
},
|
|
229
413
|
}, async ({ config }) => apiResult(client.post("/config/validate", config)));
|
|
230
|
-
|
|
414
|
+
registerTool("wabery_preview_config_diff", {
|
|
231
415
|
title: "Preview Wabery config diff",
|
|
232
416
|
description: "Preview which flows, automations, webhook settings, Business Agents, connectors, and tools would be created, updated, deleted, or left unchanged.",
|
|
233
417
|
inputSchema: {
|
|
@@ -240,7 +424,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
240
424
|
openWorldHint: false,
|
|
241
425
|
},
|
|
242
426
|
}, async ({ config }) => apiResult(client.post("/config/diff", config)));
|
|
243
|
-
|
|
427
|
+
registerTool("wabery_apply_config", {
|
|
244
428
|
title: "Apply Wabery config",
|
|
245
429
|
description: "Create, update, or remove Wabery flows, automations, webhook settings, and eligible Business Agent declarations from config. This does not publish flows to Meta; call wabery_publish_flow explicitly after review.",
|
|
246
430
|
inputSchema: {
|
|
@@ -258,7 +442,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
258
442
|
},
|
|
259
443
|
}, async (input) => {
|
|
260
444
|
const { config, confirmation_token } = input;
|
|
261
|
-
const blocked = requireWrite("wabery_apply_config", confirmation_token, "apply_config");
|
|
445
|
+
const blocked = await requireWrite("wabery_apply_config", confirmation_token, "apply_config");
|
|
262
446
|
if (blocked)
|
|
263
447
|
return blocked;
|
|
264
448
|
try {
|
|
@@ -282,7 +466,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
282
466
|
throw error;
|
|
283
467
|
}
|
|
284
468
|
});
|
|
285
|
-
|
|
469
|
+
registerTool("wabery_list_business_agents", {
|
|
286
470
|
title: "List Meta Business Agents",
|
|
287
471
|
description: "List Meta Business Agent records for the selected Wabery project.",
|
|
288
472
|
inputSchema: {
|
|
@@ -297,7 +481,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
297
481
|
}, async ({ channel_id }) => jsonResult(await client.get("/business-agents", {
|
|
298
482
|
channel_id,
|
|
299
483
|
})));
|
|
300
|
-
|
|
484
|
+
registerTool("wabery_check_business_agent_eligibility", {
|
|
301
485
|
title: "Check Meta Business Agent eligibility",
|
|
302
486
|
description: "Check whether a WhatsApp channel is eligible for Meta Business Agent.",
|
|
303
487
|
inputSchema: {
|
|
@@ -312,7 +496,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
312
496
|
}, async ({ channel_id }) => jsonResult(await client.get("/business-agents/eligibility", {
|
|
313
497
|
channel_id,
|
|
314
498
|
})));
|
|
315
|
-
|
|
499
|
+
registerTool("wabery_apply_business_agent", {
|
|
316
500
|
title: "Apply Meta Business Agent settings",
|
|
317
501
|
description: "Provision or update a Meta Business Agent for a WhatsApp channel. Prefer wabery_apply_config when managing the whole project declaratively.",
|
|
318
502
|
inputSchema: {
|
|
@@ -335,12 +519,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
335
519
|
},
|
|
336
520
|
}, async (input) => {
|
|
337
521
|
const { confirmation_token, ...body } = input;
|
|
338
|
-
const blocked = requireWrite("wabery_apply_business_agent", confirmation_token, "apply_business_agent");
|
|
522
|
+
const blocked = await requireWrite("wabery_apply_business_agent", confirmation_token, "apply_business_agent");
|
|
339
523
|
if (blocked)
|
|
340
524
|
return blocked;
|
|
341
525
|
return jsonResult(await client.put("/business-agents", body));
|
|
342
526
|
});
|
|
343
|
-
|
|
527
|
+
registerTool("wabery_get_business_agent_settings", {
|
|
344
528
|
title: "Get Meta Business Agent settings",
|
|
345
529
|
description: "Retrieve live Meta Business Agent settings.",
|
|
346
530
|
inputSchema: {
|
|
@@ -353,7 +537,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
353
537
|
openWorldHint: true,
|
|
354
538
|
},
|
|
355
539
|
}, async ({ business_agent_id }) => jsonResult(await client.get(`/business-agents/${encodeURIComponent(business_agent_id)}/settings`)));
|
|
356
|
-
|
|
540
|
+
registerTool("wabery_update_business_agent_settings", {
|
|
357
541
|
title: "Update Meta Business Agent settings",
|
|
358
542
|
description: "Update live Meta Business Agent settings.",
|
|
359
543
|
inputSchema: {
|
|
@@ -376,12 +560,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
376
560
|
},
|
|
377
561
|
}, async (input) => {
|
|
378
562
|
const { business_agent_id, confirmation_token, ...body } = input;
|
|
379
|
-
const blocked = requireWrite("wabery_update_business_agent_settings", confirmation_token, "update_business_agent_settings");
|
|
563
|
+
const blocked = await requireWrite("wabery_update_business_agent_settings", confirmation_token, "update_business_agent_settings");
|
|
380
564
|
if (blocked)
|
|
381
565
|
return blocked;
|
|
382
566
|
return jsonResult(await client.put(`/business-agents/${encodeURIComponent(business_agent_id)}/settings`, body));
|
|
383
567
|
});
|
|
384
|
-
|
|
568
|
+
registerTool("wabery_list_business_agent_connectors", {
|
|
385
569
|
title: "List Meta Business Agent connectors",
|
|
386
570
|
description: "List connector records for Meta Business Agents in the selected Wabery project.",
|
|
387
571
|
inputSchema: {
|
|
@@ -396,7 +580,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
396
580
|
}, async ({ business_agent_id }) => jsonResult(await client.get("/business-agents/connectors", {
|
|
397
581
|
business_agent_id,
|
|
398
582
|
})));
|
|
399
|
-
|
|
583
|
+
registerTool("wabery_create_business_agent_connector", {
|
|
400
584
|
title: "Create Meta Business Agent connector",
|
|
401
585
|
description: "Create a connector on a provisioned Meta Business Agent. Prefer wabery_apply_config for declarative project-wide changes.",
|
|
402
586
|
inputSchema: {
|
|
@@ -418,12 +602,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
418
602
|
},
|
|
419
603
|
}, async (input) => {
|
|
420
604
|
const { confirmation_token, ...body } = input;
|
|
421
|
-
const blocked = requireWrite("wabery_create_business_agent_connector", confirmation_token, "create_business_agent_connector");
|
|
605
|
+
const blocked = await requireWrite("wabery_create_business_agent_connector", confirmation_token, "create_business_agent_connector");
|
|
422
606
|
if (blocked)
|
|
423
607
|
return blocked;
|
|
424
608
|
return jsonResult(await client.post("/business-agents/connectors", body));
|
|
425
609
|
});
|
|
426
|
-
|
|
610
|
+
registerTool("wabery_create_business_agent_connector_tool", {
|
|
427
611
|
title: "Create Meta Business Agent connector tool",
|
|
428
612
|
description: "Create a connector tool backed by a Wabery Flow, hosted function, API endpoint, or no-op placeholder.",
|
|
429
613
|
inputSchema: {
|
|
@@ -453,12 +637,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
453
637
|
},
|
|
454
638
|
}, async (input) => {
|
|
455
639
|
const { connector_id, confirmation_token, ...body } = input;
|
|
456
|
-
const blocked = requireWrite("wabery_create_business_agent_connector_tool", confirmation_token, "create_business_agent_connector_tool");
|
|
640
|
+
const blocked = await requireWrite("wabery_create_business_agent_connector_tool", confirmation_token, "create_business_agent_connector_tool");
|
|
457
641
|
if (blocked)
|
|
458
642
|
return blocked;
|
|
459
643
|
return jsonResult(await client.post(`/business-agents/connectors/${encodeURIComponent(connector_id)}/tools`, body));
|
|
460
644
|
});
|
|
461
|
-
|
|
645
|
+
registerTool("wabery_list_business_agent_connector_tools", {
|
|
462
646
|
title: "List Meta Business Agent connector tools",
|
|
463
647
|
description: "List tools for a Meta Business Agent connector.",
|
|
464
648
|
inputSchema: {
|
|
@@ -471,7 +655,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
471
655
|
openWorldHint: false,
|
|
472
656
|
},
|
|
473
657
|
}, async ({ connector_id }) => jsonResult(await client.get(`/business-agents/connectors/${encodeURIComponent(connector_id)}/tools`)));
|
|
474
|
-
|
|
658
|
+
registerTool("wabery_get_business_agent_connector_tool", {
|
|
475
659
|
title: "Get Meta Business Agent connector tool",
|
|
476
660
|
description: "Fetch one Meta Business Agent connector tool.",
|
|
477
661
|
inputSchema: {
|
|
@@ -485,7 +669,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
485
669
|
openWorldHint: false,
|
|
486
670
|
},
|
|
487
671
|
}, async ({ connector_id, tool_id }) => jsonResult(await client.get(`/business-agents/connectors/${encodeURIComponent(connector_id)}/tools/${encodeURIComponent(tool_id)}`)));
|
|
488
|
-
|
|
672
|
+
registerTool("wabery_update_business_agent_connector_tool", {
|
|
489
673
|
title: "Update Meta Business Agent connector tool",
|
|
490
674
|
description: "Update one Meta Business Agent connector tool.",
|
|
491
675
|
inputSchema: {
|
|
@@ -513,12 +697,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
513
697
|
},
|
|
514
698
|
}, async (input) => {
|
|
515
699
|
const { connector_id, tool_id, confirmation_token, ...body } = input;
|
|
516
|
-
const blocked = requireWrite("wabery_update_business_agent_connector_tool", confirmation_token, "update_business_agent_connector_tool");
|
|
700
|
+
const blocked = await requireWrite("wabery_update_business_agent_connector_tool", confirmation_token, "update_business_agent_connector_tool");
|
|
517
701
|
if (blocked)
|
|
518
702
|
return blocked;
|
|
519
703
|
return jsonResult(await client.patch(`/business-agents/connectors/${encodeURIComponent(connector_id)}/tools/${encodeURIComponent(tool_id)}`, body));
|
|
520
704
|
});
|
|
521
|
-
|
|
705
|
+
registerTool("wabery_delete_business_agent_connector_tool", {
|
|
522
706
|
title: "Delete Meta Business Agent connector tool",
|
|
523
707
|
description: "Delete one Meta Business Agent connector tool.",
|
|
524
708
|
inputSchema: {
|
|
@@ -536,16 +720,35 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
536
720
|
openWorldHint: false,
|
|
537
721
|
},
|
|
538
722
|
}, async ({ connector_id, tool_id, confirmation_token }) => {
|
|
539
|
-
const blocked = requireWrite("wabery_delete_business_agent_connector_tool", confirmation_token, "delete_business_agent_connector_tool");
|
|
723
|
+
const blocked = await requireWrite("wabery_delete_business_agent_connector_tool", confirmation_token, "delete_business_agent_connector_tool");
|
|
540
724
|
if (blocked)
|
|
541
725
|
return blocked;
|
|
542
726
|
return jsonResult(await client.delete(`/business-agents/connectors/${encodeURIComponent(connector_id)}/tools/${encodeURIComponent(tool_id)}`));
|
|
543
727
|
});
|
|
544
|
-
|
|
728
|
+
registerTool("wabery_get_business_agent_thread_control", {
|
|
729
|
+
title: "Get Meta Business Agent thread control",
|
|
730
|
+
description: "Inspect WhatsApp thread control for a channel/contact pair without changing its owner.",
|
|
731
|
+
inputSchema: {
|
|
732
|
+
channel_id: z.string().min(1),
|
|
733
|
+
contact_id: z.string().min(1),
|
|
734
|
+
business_agent_id: z.string().min(1).optional(),
|
|
735
|
+
thread_id: z.string().min(1).optional(),
|
|
736
|
+
},
|
|
737
|
+
annotations: {
|
|
738
|
+
readOnlyHint: true,
|
|
739
|
+
destructiveHint: false,
|
|
740
|
+
idempotentHint: true,
|
|
741
|
+
openWorldHint: true,
|
|
742
|
+
},
|
|
743
|
+
}, async (input) => jsonResult(await client.post("/business-agents/thread-control", {
|
|
744
|
+
...input,
|
|
745
|
+
action: "get",
|
|
746
|
+
})));
|
|
747
|
+
registerTool("wabery_set_business_agent_thread_control", {
|
|
545
748
|
title: "Set Meta Business Agent thread control",
|
|
546
|
-
description: "Take
|
|
749
|
+
description: "Take or pass WhatsApp thread control for a channel/contact pair. Use the separate get tool to inspect current ownership.",
|
|
547
750
|
inputSchema: {
|
|
548
|
-
action: z.enum(["take", "pass"
|
|
751
|
+
action: z.enum(["take", "pass"]),
|
|
549
752
|
channel_id: z.string().min(1),
|
|
550
753
|
contact_id: z.string().min(1),
|
|
551
754
|
business_agent_id: z.string().min(1).optional(),
|
|
@@ -561,16 +764,16 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
561
764
|
readOnlyHint: false,
|
|
562
765
|
destructiveHint: false,
|
|
563
766
|
idempotentHint: true,
|
|
564
|
-
openWorldHint:
|
|
767
|
+
openWorldHint: true,
|
|
565
768
|
},
|
|
566
769
|
}, async (input) => {
|
|
567
770
|
const { confirmation_token, ...body } = input;
|
|
568
|
-
const blocked = requireWrite("
|
|
771
|
+
const blocked = await requireWrite("wabery_set_business_agent_thread_control", confirmation_token, "thread_control");
|
|
569
772
|
if (blocked)
|
|
570
773
|
return blocked;
|
|
571
774
|
return jsonResult(await client.post("/business-agents/thread-control", body));
|
|
572
775
|
});
|
|
573
|
-
|
|
776
|
+
registerTool("wabery_test_business_agent", {
|
|
574
777
|
title: "Test Meta Business Agent",
|
|
575
778
|
description: "Run a Meta Business Agent test scenario.",
|
|
576
779
|
inputSchema: {
|
|
@@ -585,7 +788,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
585
788
|
openWorldHint: true,
|
|
586
789
|
},
|
|
587
790
|
}, async (body) => jsonResult(await client.post("/business-agents/test", body)));
|
|
588
|
-
|
|
791
|
+
registerTool("wabery_get_business_agent_eval", {
|
|
589
792
|
title: "Get Meta Business Agent eval summary",
|
|
590
793
|
description: "Retrieve the Meta Business Agent eval summary.",
|
|
591
794
|
inputSchema: {
|
|
@@ -600,7 +803,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
600
803
|
}, async ({ business_agent_id }) => jsonResult(await client.get("/business-agents/eval", {
|
|
601
804
|
business_agent_id,
|
|
602
805
|
})));
|
|
603
|
-
|
|
806
|
+
registerTool("wabery_list_business_agent_skills", {
|
|
604
807
|
title: "List Meta Business Agent skills",
|
|
605
808
|
description: "List configured Meta Business Agent skills.",
|
|
606
809
|
inputSchema: {
|
|
@@ -615,7 +818,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
615
818
|
}, async ({ business_agent_id }) => jsonResult(await client.get("/business-agents/skills", {
|
|
616
819
|
business_agent_id,
|
|
617
820
|
})));
|
|
618
|
-
|
|
821
|
+
registerTool("wabery_set_business_agent_skills", {
|
|
619
822
|
title: "Set Meta Business Agent skills",
|
|
620
823
|
description: "Replace configured Meta Business Agent skills.",
|
|
621
824
|
inputSchema: {
|
|
@@ -628,17 +831,17 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
628
831
|
},
|
|
629
832
|
annotations: {
|
|
630
833
|
readOnlyHint: false,
|
|
631
|
-
destructiveHint:
|
|
834
|
+
destructiveHint: true,
|
|
632
835
|
idempotentHint: true,
|
|
633
836
|
openWorldHint: true,
|
|
634
837
|
},
|
|
635
838
|
}, async ({ confirmation_token, ...body }) => {
|
|
636
|
-
const blocked = requireWrite("wabery_set_business_agent_skills", confirmation_token, "set_business_agent_skills");
|
|
839
|
+
const blocked = await requireWrite("wabery_set_business_agent_skills", confirmation_token, "set_business_agent_skills");
|
|
637
840
|
if (blocked)
|
|
638
841
|
return blocked;
|
|
639
842
|
return jsonResult(await client.put("/business-agents/skills", body));
|
|
640
843
|
});
|
|
641
|
-
|
|
844
|
+
registerTool("wabery_list_projects", {
|
|
642
845
|
title: "List Wabery projects",
|
|
643
846
|
description: "List projects and their project_id values. Use project_id when creating flows or enrolling WhatsApp contacts.",
|
|
644
847
|
inputSchema: {},
|
|
@@ -650,7 +853,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
650
853
|
},
|
|
651
854
|
}, async () => jsonResult(await client.get("/projects")));
|
|
652
855
|
if (options.projectSelection) {
|
|
653
|
-
|
|
856
|
+
registerTool("wabery_get_selected_project", {
|
|
654
857
|
title: "Get selected Wabery project",
|
|
655
858
|
description: "Return the project currently selected for this MCP session. If no project has been selected, Wabery uses the credential's default project.",
|
|
656
859
|
inputSchema: {},
|
|
@@ -665,14 +868,14 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
665
868
|
selected_project_id: options.projectSelection?.get() ?? null,
|
|
666
869
|
default_project_id: client.projectId ?? null,
|
|
667
870
|
}));
|
|
668
|
-
|
|
871
|
+
registerTool("wabery_select_project", {
|
|
669
872
|
title: "Select Wabery project",
|
|
670
873
|
description: "Select the project that subsequent Wabery MCP tools should use in this session. Call wabery_list_projects first, then pass the project_id.",
|
|
671
874
|
inputSchema: {
|
|
672
875
|
project_id: z.string().min(1),
|
|
673
876
|
},
|
|
674
877
|
annotations: {
|
|
675
|
-
readOnlyHint:
|
|
878
|
+
readOnlyHint: false,
|
|
676
879
|
destructiveHint: false,
|
|
677
880
|
idempotentHint: true,
|
|
678
881
|
openWorldHint: false,
|
|
@@ -687,7 +890,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
687
890
|
});
|
|
688
891
|
});
|
|
689
892
|
}
|
|
690
|
-
|
|
893
|
+
registerTool("wabery_get_project_readiness", {
|
|
691
894
|
title: "Get Wabery project readiness",
|
|
692
895
|
description: "Explain what the selected project can do now: sandbox testing, production inbound, flow publish/send, templates, proactive messages, routing, opt-in, and submission handling. Call before building, after wabery_apply_config, before template/reminder work, and before the final creator summary.",
|
|
693
896
|
inputSchema: {
|
|
@@ -701,9 +904,9 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
701
904
|
openWorldHint: false,
|
|
702
905
|
},
|
|
703
906
|
}, async ({ project_id, requirements }) => jsonResult(await getReadiness(project_id, requirements)));
|
|
704
|
-
|
|
907
|
+
registerTool("wabery_get_project", {
|
|
705
908
|
title: "Get a Wabery project",
|
|
706
|
-
description: "Fetch a single project
|
|
909
|
+
description: "Fetch a single project's user-safe configuration. Secrets and provider credentials are omitted from MCP results.",
|
|
707
910
|
inputSchema: { project_id: z.string().min(1) },
|
|
708
911
|
annotations: {
|
|
709
912
|
readOnlyHint: true,
|
|
@@ -712,7 +915,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
712
915
|
openWorldHint: false,
|
|
713
916
|
},
|
|
714
917
|
}, async ({ project_id }) => jsonResult(await client.get(`/projects/${encodeURIComponent(project_id)}`)));
|
|
715
|
-
|
|
918
|
+
registerTool("wabery_update_project", {
|
|
716
919
|
title: "Update a Wabery project",
|
|
717
920
|
description: "Update name, description, routing_mode, require_opt_in, or webhook config (url, signing on/off, or a bring-your-own secret) of an existing project. Set require_opt_in=false on a dedicated production number so inbound senders can be replied to and messaged without the invite/opt-in step.",
|
|
718
921
|
inputSchema: {
|
|
@@ -732,14 +935,14 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
732
935
|
openWorldHint: false,
|
|
733
936
|
},
|
|
734
937
|
}, async ({ project_id, ...body }) => {
|
|
735
|
-
const blocked = requireWrite("wabery_update_project");
|
|
938
|
+
const blocked = await requireWrite("wabery_update_project");
|
|
736
939
|
if (blocked)
|
|
737
940
|
return blocked;
|
|
738
941
|
return jsonResult(await client.patch(`/projects/${encodeURIComponent(project_id)}`, body));
|
|
739
942
|
});
|
|
740
|
-
|
|
943
|
+
registerTool("wabery_rotate_webhook_secret", {
|
|
741
944
|
title: "Rotate a project's webhook secret",
|
|
742
|
-
description: "Set a new signing secret (server-generated, or a provided one) and
|
|
945
|
+
description: "Set a new signing secret (server-generated, or a provided one) and invalidate the previous secret. MCP confirms the rotation but omits the secret value; retrieve or manage sensitive credentials in the Wabery dashboard.",
|
|
743
946
|
inputSchema: {
|
|
744
947
|
project_id: z.string().min(1),
|
|
745
948
|
webhook_secret: z.string().min(16).max(256).optional(),
|
|
@@ -751,12 +954,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
751
954
|
openWorldHint: false,
|
|
752
955
|
},
|
|
753
956
|
}, async ({ project_id, webhook_secret }) => {
|
|
754
|
-
const blocked = requireWrite("wabery_rotate_webhook_secret");
|
|
957
|
+
const blocked = await requireWrite("wabery_rotate_webhook_secret");
|
|
755
958
|
if (blocked)
|
|
756
959
|
return blocked;
|
|
757
960
|
return jsonResult(await client.post(`/projects/${encodeURIComponent(project_id)}/rotate-webhook-secret`, webhook_secret ? { webhook_secret } : undefined));
|
|
758
961
|
});
|
|
759
|
-
|
|
962
|
+
registerTool("wabery_list_channels", {
|
|
760
963
|
title: "List Wabery channels",
|
|
761
964
|
description: "List connected messaging channels. Use a WhatsApp channel_id when sending a published flow. For WhatsApp templates or proactive template sends, inspect publish_readiness and tell the creator when a dedicated phone number, verified business, approved account, connected phone, or Meta payment method is missing.",
|
|
762
965
|
inputSchema: {},
|
|
@@ -767,7 +970,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
767
970
|
openWorldHint: false,
|
|
768
971
|
},
|
|
769
972
|
}, async () => jsonResult(await client.get("/channels")));
|
|
770
|
-
|
|
973
|
+
registerTool("wabery_get_channel", {
|
|
771
974
|
title: "Get a Wabery channel",
|
|
772
975
|
description: "Fetch a single channel, including routing_warning and WhatsApp publish_readiness. If can_submit_templates is false, explain the blockers before attempting template creation or proactive template sends.",
|
|
773
976
|
inputSchema: { channel_id: z.string().min(1) },
|
|
@@ -778,7 +981,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
778
981
|
openWorldHint: false,
|
|
779
982
|
},
|
|
780
983
|
}, async ({ channel_id }) => jsonResult(await client.get(`/channels/${encodeURIComponent(channel_id)}`)));
|
|
781
|
-
|
|
984
|
+
registerTool("wabery_update_channel", {
|
|
782
985
|
title: "Update a Wabery channel's routing",
|
|
783
986
|
description: "Set a dedicated channel's routing_mode (NONE | FLOWS | EXTERNAL). Use EXTERNAL so inbound to this channel is forwarded to the project webhook. The shared sandbox channel routes per-participant and cannot be updated this way.",
|
|
784
987
|
inputSchema: {
|
|
@@ -792,14 +995,14 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
792
995
|
openWorldHint: false,
|
|
793
996
|
},
|
|
794
997
|
}, async ({ channel_id, routing_mode }) => {
|
|
795
|
-
const blocked = requireWrite("wabery_update_channel");
|
|
998
|
+
const blocked = await requireWrite("wabery_update_channel");
|
|
796
999
|
if (blocked)
|
|
797
1000
|
return blocked;
|
|
798
1001
|
return jsonResult(await client.patch(`/channels/${encodeURIComponent(channel_id)}`, {
|
|
799
1002
|
routing_mode,
|
|
800
1003
|
}));
|
|
801
1004
|
});
|
|
802
|
-
|
|
1005
|
+
registerTool("wabery_create_registration_intent", {
|
|
803
1006
|
title: "Create registration intent",
|
|
804
1007
|
description: "Mint a single-use wa.me registration link so an end-customer can connect over WhatsApp.",
|
|
805
1008
|
inputSchema: {
|
|
@@ -821,7 +1024,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
821
1024
|
openWorldHint: false,
|
|
822
1025
|
},
|
|
823
1026
|
}, async ({ project_id, customer_reference, metadata }) => {
|
|
824
|
-
const blocked = requireWrite("wabery_create_registration_intent");
|
|
1027
|
+
const blocked = await requireWrite("wabery_create_registration_intent");
|
|
825
1028
|
if (blocked)
|
|
826
1029
|
return blocked;
|
|
827
1030
|
return jsonResult(await client.post("/registration-intents", {
|
|
@@ -830,7 +1033,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
830
1033
|
metadata,
|
|
831
1034
|
}));
|
|
832
1035
|
});
|
|
833
|
-
|
|
1036
|
+
registerTool("wabery_get_registration_intent", {
|
|
834
1037
|
title: "Get registration intent",
|
|
835
1038
|
description: "Poll a registration intent's status. Works with a publishable key or a scoped secret key.",
|
|
836
1039
|
inputSchema: {
|
|
@@ -843,7 +1046,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
843
1046
|
openWorldHint: false,
|
|
844
1047
|
},
|
|
845
1048
|
}, async ({ intent_id }) => jsonResult(await client.get(`/registration-intents/${encodeURIComponent(intent_id)}`)));
|
|
846
|
-
|
|
1049
|
+
registerTool("wabery_get_limits", {
|
|
847
1050
|
title: "Get Wabery API limits",
|
|
848
1051
|
description: "Return the enforced public API rate limit and monthly request quota for the configured API key.",
|
|
849
1052
|
inputSchema: {},
|
|
@@ -854,7 +1057,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
854
1057
|
openWorldHint: false,
|
|
855
1058
|
},
|
|
856
1059
|
}, async () => jsonResult(await client.get("/limits")));
|
|
857
|
-
|
|
1060
|
+
registerTool("wabery_list_flows", {
|
|
858
1061
|
title: "List Wabery flows",
|
|
859
1062
|
description: "List flows, including ids, config keys, project ids, and Meta publish status.",
|
|
860
1063
|
inputSchema: {},
|
|
@@ -865,7 +1068,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
865
1068
|
openWorldHint: false,
|
|
866
1069
|
},
|
|
867
1070
|
}, async () => jsonResult(await client.get("/flows")));
|
|
868
|
-
|
|
1071
|
+
registerTool("wabery_get_flow", {
|
|
869
1072
|
title: "Get Wabery flow",
|
|
870
1073
|
description: "Fetch one flow by id, including status and field schema.",
|
|
871
1074
|
inputSchema: {
|
|
@@ -878,7 +1081,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
878
1081
|
openWorldHint: false,
|
|
879
1082
|
},
|
|
880
1083
|
}, async ({ flow_id }) => jsonResult(await client.get(`/flows/${flow_id}`)));
|
|
881
|
-
|
|
1084
|
+
registerTool("wabery_publish_flow", {
|
|
882
1085
|
title: "Publish Wabery flow",
|
|
883
1086
|
description: "Publish a Wabery draft flow to Meta. Only call after validate/diff/apply and explicit user confirmation.",
|
|
884
1087
|
inputSchema: {
|
|
@@ -890,17 +1093,17 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
890
1093
|
},
|
|
891
1094
|
annotations: {
|
|
892
1095
|
readOnlyHint: false,
|
|
893
|
-
destructiveHint:
|
|
1096
|
+
destructiveHint: true,
|
|
894
1097
|
idempotentHint: false,
|
|
895
1098
|
openWorldHint: true,
|
|
896
1099
|
},
|
|
897
1100
|
}, async ({ flow_id, confirmation_token }) => {
|
|
898
|
-
const blocked = requireWrite("wabery_publish_flow", confirmation_token, `publish:${flow_id}`);
|
|
1101
|
+
const blocked = await requireWrite("wabery_publish_flow", confirmation_token, `publish:${flow_id}`);
|
|
899
1102
|
if (blocked)
|
|
900
1103
|
return blocked;
|
|
901
1104
|
return apiResult(client.post(`/flows/${flow_id}/publish`));
|
|
902
1105
|
});
|
|
903
|
-
|
|
1106
|
+
registerTool("wabery_enroll_contact", {
|
|
904
1107
|
title: "Enroll WhatsApp contact",
|
|
905
1108
|
description: "Register a phone number against a Wabery project. Returns contact_id, which is the stable id used in flow submissions and future sends.",
|
|
906
1109
|
inputSchema: {
|
|
@@ -919,7 +1122,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
919
1122
|
.optional()
|
|
920
1123
|
.describe("Your own user/customer id for correlation."),
|
|
921
1124
|
metadata: metadataSchema,
|
|
922
|
-
opt_in: optInSchema.describe("WhatsApp opt-in evidence.
|
|
1125
|
+
opt_in: optInSchema.describe("WhatsApp opt-in evidence. For broadcasts include the applicable MARKETING or UTILITY category plus source, method, consent_text, and evidence."),
|
|
923
1126
|
},
|
|
924
1127
|
annotations: {
|
|
925
1128
|
readOnlyHint: false,
|
|
@@ -928,12 +1131,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
928
1131
|
openWorldHint: false,
|
|
929
1132
|
},
|
|
930
1133
|
}, async (input) => {
|
|
931
|
-
const blocked = requireWrite("wabery_enroll_contact");
|
|
1134
|
+
const blocked = await requireWrite("wabery_enroll_contact");
|
|
932
1135
|
if (blocked)
|
|
933
1136
|
return blocked;
|
|
934
1137
|
return jsonResult(await client.post("/contacts", input));
|
|
935
1138
|
});
|
|
936
|
-
|
|
1139
|
+
registerTool("wabery_list_contacts", {
|
|
937
1140
|
title: "List Wabery contacts",
|
|
938
1141
|
description: "List enrolled contacts and their stable contact_id values.",
|
|
939
1142
|
inputSchema: {
|
|
@@ -947,7 +1150,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
947
1150
|
openWorldHint: false,
|
|
948
1151
|
},
|
|
949
1152
|
}, async ({ limit, starting_after }) => jsonResult(await client.get("/contacts", { limit, starting_after })));
|
|
950
|
-
|
|
1153
|
+
registerTool("wabery_get_contact", {
|
|
951
1154
|
title: "Get Wabery contact",
|
|
952
1155
|
description: "Fetch one enrolled contact by stable contact_id before sending or unenrolling.",
|
|
953
1156
|
inputSchema: {
|
|
@@ -960,7 +1163,34 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
960
1163
|
openWorldHint: false,
|
|
961
1164
|
},
|
|
962
1165
|
}, async ({ contact_id }) => jsonResult(await client.get(`/contacts/${contact_id}`)));
|
|
963
|
-
|
|
1166
|
+
registerTool("wabery_list_contact_imports", {
|
|
1167
|
+
title: "List Wabery contact imports",
|
|
1168
|
+
description: "List CSV contact imports and their processing counts. Upload files in the dashboard or CLI, then use a completed import_id as a broadcast audience source.",
|
|
1169
|
+
inputSchema: {
|
|
1170
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
1171
|
+
starting_after: z.string().optional(),
|
|
1172
|
+
},
|
|
1173
|
+
annotations: {
|
|
1174
|
+
readOnlyHint: true,
|
|
1175
|
+
destructiveHint: false,
|
|
1176
|
+
idempotentHint: true,
|
|
1177
|
+
openWorldHint: false,
|
|
1178
|
+
},
|
|
1179
|
+
}, async (query) => apiResult(client.get("/contact-imports", query)));
|
|
1180
|
+
registerTool("wabery_get_contact_import", {
|
|
1181
|
+
title: "Get Wabery contact import",
|
|
1182
|
+
description: "Inspect one CSV contact import's status and row counts. Wait for completed before using its import_id to prepare a broadcast.",
|
|
1183
|
+
inputSchema: {
|
|
1184
|
+
import_id: z.string().min(1),
|
|
1185
|
+
},
|
|
1186
|
+
annotations: {
|
|
1187
|
+
readOnlyHint: true,
|
|
1188
|
+
destructiveHint: false,
|
|
1189
|
+
idempotentHint: true,
|
|
1190
|
+
openWorldHint: false,
|
|
1191
|
+
},
|
|
1192
|
+
}, async ({ import_id }) => apiResult(client.get(`/contact-imports/${import_id}`)));
|
|
1193
|
+
registerTool("wabery_unenroll_contact", {
|
|
964
1194
|
title: "Unenroll Wabery contact",
|
|
965
1195
|
description: "Remove a contact phone's sandbox authorization for this organization. Pass erase=true for irreversible org-scoped contact data deletion.",
|
|
966
1196
|
inputSchema: {
|
|
@@ -978,12 +1208,234 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
978
1208
|
openWorldHint: false,
|
|
979
1209
|
},
|
|
980
1210
|
}, async ({ contact_id, erase, confirmation_token }) => {
|
|
981
|
-
const blocked = requireWrite("wabery_unenroll_contact", confirmation_token, erase ? `erase:${contact_id}` : undefined);
|
|
1211
|
+
const blocked = await requireWrite("wabery_unenroll_contact", confirmation_token, erase ? `erase:${contact_id}` : undefined);
|
|
982
1212
|
if (blocked)
|
|
983
1213
|
return blocked;
|
|
984
1214
|
return jsonResult(await client.delete(`/contacts/${contact_id}${erase ? "?erase=true" : ""}`));
|
|
985
1215
|
});
|
|
986
|
-
|
|
1216
|
+
registerTool("wabery_list_broadcasts", {
|
|
1217
|
+
title: "List Wabery broadcasts",
|
|
1218
|
+
description: "List broadcast campaigns and delivery counts. Filter by lifecycle status when monitoring preparation or delivery.",
|
|
1219
|
+
inputSchema: {
|
|
1220
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
1221
|
+
starting_after: z.string().optional(),
|
|
1222
|
+
status: broadcastStatusSchema.optional(),
|
|
1223
|
+
},
|
|
1224
|
+
annotations: {
|
|
1225
|
+
readOnlyHint: true,
|
|
1226
|
+
destructiveHint: false,
|
|
1227
|
+
idempotentHint: true,
|
|
1228
|
+
openWorldHint: false,
|
|
1229
|
+
},
|
|
1230
|
+
}, async (query) => apiResult(client.get("/broadcasts", query)));
|
|
1231
|
+
registerTool("wabery_get_broadcast", {
|
|
1232
|
+
title: "Get Wabery broadcast",
|
|
1233
|
+
description: "Fetch one broadcast with its lifecycle status and recipient counts. Poll while status is preparing, and review counts before requesting send or schedule confirmation.",
|
|
1234
|
+
inputSchema: {
|
|
1235
|
+
broadcast_id: z.string().min(1),
|
|
1236
|
+
},
|
|
1237
|
+
annotations: {
|
|
1238
|
+
readOnlyHint: true,
|
|
1239
|
+
destructiveHint: false,
|
|
1240
|
+
idempotentHint: true,
|
|
1241
|
+
openWorldHint: false,
|
|
1242
|
+
},
|
|
1243
|
+
}, async ({ broadcast_id }) => apiResult(client.get(`/broadcasts/${broadcast_id}`)));
|
|
1244
|
+
registerTool("wabery_create_broadcast", {
|
|
1245
|
+
title: "Create Wabery broadcast draft",
|
|
1246
|
+
description: "Create a draft from approved WhatsApp template variants. This does not select recipients or send messages. Use a dedicated WhatsApp channel and category-specific consent.",
|
|
1247
|
+
inputSchema: {
|
|
1248
|
+
name: z.string().min(1).max(120),
|
|
1249
|
+
channel_id: z.string().min(1),
|
|
1250
|
+
category: z.enum(["MARKETING", "UTILITY"]),
|
|
1251
|
+
template_name: z.string().min(1).max(512),
|
|
1252
|
+
fallback_language: z.string().min(2).max(35),
|
|
1253
|
+
variants: z.array(broadcastVariantSchema).min(1).max(50),
|
|
1254
|
+
},
|
|
1255
|
+
annotations: {
|
|
1256
|
+
readOnlyHint: false,
|
|
1257
|
+
destructiveHint: false,
|
|
1258
|
+
idempotentHint: false,
|
|
1259
|
+
openWorldHint: false,
|
|
1260
|
+
},
|
|
1261
|
+
}, async ({ name, channel_id, category, template_name, fallback_language, variants, }) => {
|
|
1262
|
+
const blocked = await requireWrite("wabery_create_broadcast");
|
|
1263
|
+
if (blocked)
|
|
1264
|
+
return blocked;
|
|
1265
|
+
return apiResult(client.post("/broadcasts", {
|
|
1266
|
+
name,
|
|
1267
|
+
channelId: channel_id,
|
|
1268
|
+
category,
|
|
1269
|
+
templateName: template_name,
|
|
1270
|
+
fallbackLanguage: fallback_language,
|
|
1271
|
+
variants: variants.map((variant) => ({
|
|
1272
|
+
templateId: variant.template_id,
|
|
1273
|
+
language: variant.language,
|
|
1274
|
+
parameterMappings: variant.parameter_mappings.map((mapping) => ({
|
|
1275
|
+
component: mapping.component,
|
|
1276
|
+
index: mapping.index,
|
|
1277
|
+
buttonIndex: mapping.button_index,
|
|
1278
|
+
parameterType: mapping.parameter_type,
|
|
1279
|
+
source: mapping.source,
|
|
1280
|
+
metadataKey: mapping.metadata_key,
|
|
1281
|
+
value: mapping.value,
|
|
1282
|
+
fallback: mapping.fallback,
|
|
1283
|
+
})),
|
|
1284
|
+
})),
|
|
1285
|
+
}));
|
|
1286
|
+
});
|
|
1287
|
+
registerTool("wabery_prepare_broadcast_audience", {
|
|
1288
|
+
title: "Prepare Wabery broadcast audience",
|
|
1289
|
+
description: "Resolve and freeze a draft broadcast's recipients from exactly one source: contact_ids, import_id, or filter. Preparation is asynchronous and does not send messages. Poll wabery_get_broadcast until ready, then inspect counts and recipients.",
|
|
1290
|
+
inputSchema: {
|
|
1291
|
+
broadcast_id: z.string().min(1),
|
|
1292
|
+
contact_ids: z.array(z.string().min(1)).max(1000).optional(),
|
|
1293
|
+
import_id: z.string().min(1).optional(),
|
|
1294
|
+
filter: broadcastAudienceFilterSchema.optional(),
|
|
1295
|
+
exclude_contact_ids: z.array(z.string().min(1)).max(1000).optional(),
|
|
1296
|
+
},
|
|
1297
|
+
annotations: {
|
|
1298
|
+
readOnlyHint: false,
|
|
1299
|
+
destructiveHint: false,
|
|
1300
|
+
idempotentHint: true,
|
|
1301
|
+
openWorldHint: false,
|
|
1302
|
+
},
|
|
1303
|
+
}, async ({ broadcast_id, contact_ids, import_id, filter, exclude_contact_ids, }) => {
|
|
1304
|
+
const blocked = await requireWrite("wabery_prepare_broadcast_audience");
|
|
1305
|
+
if (blocked)
|
|
1306
|
+
return blocked;
|
|
1307
|
+
const sources = [contact_ids, import_id, filter].filter((value) => value !== undefined);
|
|
1308
|
+
if (sources.length !== 1) {
|
|
1309
|
+
return {
|
|
1310
|
+
...jsonResult({
|
|
1311
|
+
error: "invalid_audience_source",
|
|
1312
|
+
message: "Provide exactly one audience source: contact_ids, import_id, or filter.",
|
|
1313
|
+
}),
|
|
1314
|
+
isError: true,
|
|
1315
|
+
};
|
|
1316
|
+
}
|
|
1317
|
+
return apiResult(client.post(`/broadcasts/${broadcast_id}/audience`, {
|
|
1318
|
+
contactIds: contact_ids,
|
|
1319
|
+
importId: import_id,
|
|
1320
|
+
filter: filter
|
|
1321
|
+
? {
|
|
1322
|
+
search: filter.search,
|
|
1323
|
+
tags: filter.tags,
|
|
1324
|
+
languages: filter.languages,
|
|
1325
|
+
isBlocked: filter.is_blocked,
|
|
1326
|
+
createdAfter: filter.created_after,
|
|
1327
|
+
createdBefore: filter.created_before,
|
|
1328
|
+
}
|
|
1329
|
+
: undefined,
|
|
1330
|
+
excludeContactIds: exclude_contact_ids ?? [],
|
|
1331
|
+
}));
|
|
1332
|
+
});
|
|
1333
|
+
registerTool("wabery_list_broadcast_recipients", {
|
|
1334
|
+
title: "List Wabery broadcast recipients",
|
|
1335
|
+
description: "Inspect the prepared recipient snapshot, language selection, skips, failures, and delivery state. Use before confirmation and while monitoring delivery.",
|
|
1336
|
+
inputSchema: {
|
|
1337
|
+
broadcast_id: z.string().min(1),
|
|
1338
|
+
limit: z.number().int().min(1).max(100).optional(),
|
|
1339
|
+
starting_after: z.string().optional(),
|
|
1340
|
+
status: broadcastRecipientStatusSchema.optional(),
|
|
1341
|
+
},
|
|
1342
|
+
annotations: {
|
|
1343
|
+
readOnlyHint: true,
|
|
1344
|
+
destructiveHint: false,
|
|
1345
|
+
idempotentHint: true,
|
|
1346
|
+
openWorldHint: false,
|
|
1347
|
+
},
|
|
1348
|
+
}, async ({ broadcast_id, ...query }) => apiResult(client.get(`/broadcasts/${broadcast_id}/recipients`, query)));
|
|
1349
|
+
registerTool("wabery_send_broadcast", {
|
|
1350
|
+
title: "Send Wabery broadcast",
|
|
1351
|
+
description: "Start sending a ready broadcast to its frozen recipient snapshot. This creates real external WhatsApp messages and requires explicit user confirmation with confirmation_token send_broadcast:<broadcast_id>.",
|
|
1352
|
+
inputSchema: {
|
|
1353
|
+
broadcast_id: z.string().min(1),
|
|
1354
|
+
confirmation_token: z
|
|
1355
|
+
.string()
|
|
1356
|
+
.optional()
|
|
1357
|
+
.describe("Required: send_broadcast:<broadcast_id>. Obtain explicit user confirmation after reviewing counts."),
|
|
1358
|
+
},
|
|
1359
|
+
annotations: {
|
|
1360
|
+
readOnlyHint: false,
|
|
1361
|
+
destructiveHint: true,
|
|
1362
|
+
idempotentHint: true,
|
|
1363
|
+
openWorldHint: true,
|
|
1364
|
+
},
|
|
1365
|
+
}, async ({ broadcast_id, confirmation_token }) => {
|
|
1366
|
+
const blocked = await requireBroadcastApproval("wabery_send_broadcast", `Send broadcast ${broadcast_id} to its prepared WhatsApp recipients now?`, confirmation_token, `send_broadcast:${broadcast_id}`);
|
|
1367
|
+
if (blocked)
|
|
1368
|
+
return blocked;
|
|
1369
|
+
return apiResult(client.post(`/broadcasts/${broadcast_id}/send`));
|
|
1370
|
+
});
|
|
1371
|
+
registerTool("wabery_schedule_broadcast", {
|
|
1372
|
+
title: "Schedule Wabery broadcast",
|
|
1373
|
+
description: "Schedule a ready broadcast for future delivery to its frozen recipient snapshot. Requires explicit user confirmation with confirmation_token schedule_broadcast:<broadcast_id>.",
|
|
1374
|
+
inputSchema: {
|
|
1375
|
+
broadcast_id: z.string().min(1),
|
|
1376
|
+
scheduled_at: z.string().datetime(),
|
|
1377
|
+
timezone: z.string().min(1).max(80),
|
|
1378
|
+
confirmation_token: z
|
|
1379
|
+
.string()
|
|
1380
|
+
.optional()
|
|
1381
|
+
.describe("Required: schedule_broadcast:<broadcast_id>. Obtain explicit user confirmation after reviewing counts and time."),
|
|
1382
|
+
},
|
|
1383
|
+
annotations: {
|
|
1384
|
+
readOnlyHint: false,
|
|
1385
|
+
destructiveHint: true,
|
|
1386
|
+
idempotentHint: true,
|
|
1387
|
+
openWorldHint: true,
|
|
1388
|
+
},
|
|
1389
|
+
}, async ({ broadcast_id, scheduled_at, timezone, confirmation_token }) => {
|
|
1390
|
+
const blocked = await requireBroadcastApproval("wabery_schedule_broadcast", `Schedule broadcast ${broadcast_id} for ${scheduled_at} (${timezone})?`, confirmation_token, `schedule_broadcast:${broadcast_id}`);
|
|
1391
|
+
if (blocked)
|
|
1392
|
+
return blocked;
|
|
1393
|
+
return apiResult(client.post(`/broadcasts/${broadcast_id}/schedule`, {
|
|
1394
|
+
scheduledAt: scheduled_at,
|
|
1395
|
+
timezone,
|
|
1396
|
+
}));
|
|
1397
|
+
});
|
|
1398
|
+
registerTool("wabery_cancel_broadcast", {
|
|
1399
|
+
title: "Cancel Wabery broadcast",
|
|
1400
|
+
description: "Cancel a draft, ready, scheduled, or sending broadcast and stop recipients that have not been dispatched. Already accepted messages cannot be recalled. Requires confirmation_token cancel_broadcast:<broadcast_id>.",
|
|
1401
|
+
inputSchema: {
|
|
1402
|
+
broadcast_id: z.string().min(1),
|
|
1403
|
+
confirmation_token: z
|
|
1404
|
+
.string()
|
|
1405
|
+
.optional()
|
|
1406
|
+
.describe("Required: cancel_broadcast:<broadcast_id>."),
|
|
1407
|
+
},
|
|
1408
|
+
annotations: {
|
|
1409
|
+
readOnlyHint: false,
|
|
1410
|
+
destructiveHint: true,
|
|
1411
|
+
idempotentHint: true,
|
|
1412
|
+
openWorldHint: false,
|
|
1413
|
+
},
|
|
1414
|
+
}, async ({ broadcast_id, confirmation_token }) => {
|
|
1415
|
+
const blocked = await requireBroadcastApproval("wabery_cancel_broadcast", `Cancel broadcast ${broadcast_id}? Messages already accepted by WhatsApp cannot be recalled.`, confirmation_token, `cancel_broadcast:${broadcast_id}`);
|
|
1416
|
+
if (blocked)
|
|
1417
|
+
return blocked;
|
|
1418
|
+
return apiResult(client.post(`/broadcasts/${broadcast_id}/cancel`));
|
|
1419
|
+
});
|
|
1420
|
+
registerTool("wabery_duplicate_broadcast", {
|
|
1421
|
+
title: "Duplicate Wabery broadcast",
|
|
1422
|
+
description: "Create a new draft with the source broadcast's channel, template variants, and parameter mappings. The recipient snapshot is not copied; prepare a new audience before sending.",
|
|
1423
|
+
inputSchema: {
|
|
1424
|
+
broadcast_id: z.string().min(1),
|
|
1425
|
+
},
|
|
1426
|
+
annotations: {
|
|
1427
|
+
readOnlyHint: false,
|
|
1428
|
+
destructiveHint: false,
|
|
1429
|
+
idempotentHint: false,
|
|
1430
|
+
openWorldHint: false,
|
|
1431
|
+
},
|
|
1432
|
+
}, async ({ broadcast_id }) => {
|
|
1433
|
+
const blocked = await requireWrite("wabery_duplicate_broadcast");
|
|
1434
|
+
if (blocked)
|
|
1435
|
+
return blocked;
|
|
1436
|
+
return apiResult(client.post(`/broadcasts/${broadcast_id}/duplicate`));
|
|
1437
|
+
});
|
|
1438
|
+
registerTool("wabery_list_conversations", {
|
|
987
1439
|
title: "List Wabery conversations",
|
|
988
1440
|
description: "List organization-scoped conversations for inbox, support, or analytics tooling.",
|
|
989
1441
|
inputSchema: {
|
|
@@ -1002,7 +1454,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1002
1454
|
...query,
|
|
1003
1455
|
active: query.active === undefined ? undefined : String(query.active),
|
|
1004
1456
|
})));
|
|
1005
|
-
|
|
1457
|
+
registerTool("wabery_get_conversation", {
|
|
1006
1458
|
title: "Get Wabery conversation",
|
|
1007
1459
|
description: "Fetch one organization-scoped conversation by id.",
|
|
1008
1460
|
inputSchema: {
|
|
@@ -1015,7 +1467,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1015
1467
|
openWorldHint: false,
|
|
1016
1468
|
},
|
|
1017
1469
|
}, async ({ conversation_id }) => jsonResult(await client.get(`/conversations/${conversation_id}`)));
|
|
1018
|
-
|
|
1470
|
+
registerTool("wabery_list_conversation_messages", {
|
|
1019
1471
|
title: "List Wabery conversation messages",
|
|
1020
1472
|
description: "Fetch message history for a conversation. Cached inbound WhatsApp media includes short-lived Wabery signed URLs; expires_at is the retention deadline.",
|
|
1021
1473
|
inputSchema: {
|
|
@@ -1031,7 +1483,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1031
1483
|
openWorldHint: false,
|
|
1032
1484
|
},
|
|
1033
1485
|
}, async ({ conversation_id, ...query }) => jsonResult(await client.get(`/conversations/${conversation_id}/messages`, query)));
|
|
1034
|
-
|
|
1486
|
+
registerTool("wabery_send_message", {
|
|
1035
1487
|
title: "Send Wabery message",
|
|
1036
1488
|
description: "Send a WhatsApp text, approved template, media, or interactive message. The body must match POST /messages and should include an idempotency_key for retries.",
|
|
1037
1489
|
inputSchema: {
|
|
@@ -1039,17 +1491,17 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1039
1491
|
},
|
|
1040
1492
|
annotations: {
|
|
1041
1493
|
readOnlyHint: false,
|
|
1042
|
-
destructiveHint:
|
|
1494
|
+
destructiveHint: true,
|
|
1043
1495
|
idempotentHint: false,
|
|
1044
1496
|
openWorldHint: true,
|
|
1045
1497
|
},
|
|
1046
1498
|
}, async ({ body }) => {
|
|
1047
|
-
const blocked = requireWrite("wabery_send_message");
|
|
1499
|
+
const blocked = await requireWrite("wabery_send_message");
|
|
1048
1500
|
if (blocked)
|
|
1049
1501
|
return blocked;
|
|
1050
1502
|
return apiResult(client.post("/messages", body));
|
|
1051
1503
|
});
|
|
1052
|
-
|
|
1504
|
+
registerTool("wabery_get_message", {
|
|
1053
1505
|
title: "Get Wabery message",
|
|
1054
1506
|
description: "Fetch one message by message_id to inspect queued/provider status, delivery state, and Meta failure details after wabery_send_message.",
|
|
1055
1507
|
inputSchema: {
|
|
@@ -1062,7 +1514,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1062
1514
|
openWorldHint: false,
|
|
1063
1515
|
},
|
|
1064
1516
|
}, async ({ message_id }) => jsonResult(await client.get(`/messages/${message_id}`)));
|
|
1065
|
-
|
|
1517
|
+
registerTool("wabery_list_templates", {
|
|
1066
1518
|
title: "List WhatsApp templates",
|
|
1067
1519
|
description: "List WhatsApp message templates and approval statuses for dedicated WhatsApp channels.",
|
|
1068
1520
|
inputSchema: {
|
|
@@ -1076,7 +1528,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1076
1528
|
openWorldHint: false,
|
|
1077
1529
|
},
|
|
1078
1530
|
}, async (query) => jsonResult(await client.get("/templates", query)));
|
|
1079
|
-
|
|
1531
|
+
registerTool("wabery_get_template", {
|
|
1080
1532
|
title: "Get WhatsApp template",
|
|
1081
1533
|
description: "Fetch one WhatsApp template and its Meta approval status. Set refresh=true to force a live Meta status check.",
|
|
1082
1534
|
inputSchema: {
|
|
@@ -1092,7 +1544,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1092
1544
|
}, async ({ template_id, refresh }) => jsonResult(await client.get(`/templates/${template_id}`, {
|
|
1093
1545
|
refresh: refresh ? "true" : undefined,
|
|
1094
1546
|
})));
|
|
1095
|
-
|
|
1547
|
+
registerTool("wabery_create_template", {
|
|
1096
1548
|
title: "Create WhatsApp template",
|
|
1097
1549
|
description: "Create and submit a WhatsApp message template to Meta for approval. Only dedicated, non-sandbox WhatsApp channels with Meta-verified business accounts and a Meta payment method can submit templates. Check channel publish_readiness first.",
|
|
1098
1550
|
inputSchema: {
|
|
@@ -1105,12 +1557,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1105
1557
|
openWorldHint: true,
|
|
1106
1558
|
},
|
|
1107
1559
|
}, async ({ body }) => {
|
|
1108
|
-
const blocked = requireWrite("wabery_create_template");
|
|
1560
|
+
const blocked = await requireWrite("wabery_create_template");
|
|
1109
1561
|
if (blocked)
|
|
1110
1562
|
return blocked;
|
|
1111
1563
|
return apiResult(client.post("/templates", body));
|
|
1112
1564
|
});
|
|
1113
|
-
|
|
1565
|
+
registerTool("wabery_wait_template", {
|
|
1114
1566
|
title: "Wait for WhatsApp template approval",
|
|
1115
1567
|
description: "Poll a WhatsApp template until Meta reports APPROVED. Fails if Meta rejects/disables it or if timeout_seconds elapses.",
|
|
1116
1568
|
inputSchema: {
|
|
@@ -1128,7 +1580,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1128
1580
|
timeoutMs: (timeout_seconds ?? 86_400) * 1000,
|
|
1129
1581
|
intervalMs: (interval_seconds ?? 60) * 1000,
|
|
1130
1582
|
})));
|
|
1131
|
-
|
|
1583
|
+
registerTool("wabery_send_flow", {
|
|
1132
1584
|
title: "Send Wabery flow",
|
|
1133
1585
|
description: "Send a published WhatsApp Flow to an enrolled contact, existing conversation, or phone number. Returns flow_token and dispatch info.",
|
|
1134
1586
|
inputSchema: {
|
|
@@ -1146,17 +1598,17 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1146
1598
|
},
|
|
1147
1599
|
annotations: {
|
|
1148
1600
|
readOnlyHint: false,
|
|
1149
|
-
destructiveHint:
|
|
1601
|
+
destructiveHint: true,
|
|
1150
1602
|
idempotentHint: false,
|
|
1151
1603
|
openWorldHint: true,
|
|
1152
1604
|
},
|
|
1153
1605
|
}, async ({ flow_id, ...body }) => {
|
|
1154
|
-
const blocked = requireWrite("wabery_send_flow");
|
|
1606
|
+
const blocked = await requireWrite("wabery_send_flow");
|
|
1155
1607
|
if (blocked)
|
|
1156
1608
|
return blocked;
|
|
1157
1609
|
return apiResult(client.post(`/flows/${flow_id}/send`, body));
|
|
1158
1610
|
});
|
|
1159
|
-
|
|
1611
|
+
registerTool("wabery_list_submissions", {
|
|
1160
1612
|
title: "List Wabery submissions",
|
|
1161
1613
|
description: "List completed WhatsApp Flow submissions for the organization.",
|
|
1162
1614
|
inputSchema: {
|
|
@@ -1172,7 +1624,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1172
1624
|
openWorldHint: false,
|
|
1173
1625
|
},
|
|
1174
1626
|
}, async (query) => jsonResult(await client.get("/submissions", query)));
|
|
1175
|
-
|
|
1627
|
+
registerTool("wabery_list_dispatches", {
|
|
1176
1628
|
title: "List Wabery dispatches",
|
|
1177
1629
|
description: "List sent flow dispatches. Use this to inspect flow_token status across sends.",
|
|
1178
1630
|
inputSchema: {
|
|
@@ -1189,7 +1641,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1189
1641
|
openWorldHint: false,
|
|
1190
1642
|
},
|
|
1191
1643
|
}, async (query) => jsonResult(await client.get("/dispatches", query)));
|
|
1192
|
-
|
|
1644
|
+
registerTool("wabery_get_dispatch", {
|
|
1193
1645
|
title: "Get Wabery dispatch",
|
|
1194
1646
|
description: "Get one flow dispatch by flow_token.",
|
|
1195
1647
|
inputSchema: {
|
|
@@ -1203,7 +1655,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1203
1655
|
},
|
|
1204
1656
|
}, async ({ flow_token }) => jsonResult(await client.get(`/dispatches/${flow_token}`)));
|
|
1205
1657
|
// ─── Hosted functions (serverless logic the agent authors) ───────────
|
|
1206
|
-
|
|
1658
|
+
registerTool("wabery_list_functions", {
|
|
1207
1659
|
title: "List hosted functions",
|
|
1208
1660
|
description: "List the project's hosted functions (serverless handlers that run on send/tool triggers).",
|
|
1209
1661
|
inputSchema: {},
|
|
@@ -1214,7 +1666,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1214
1666
|
openWorldHint: false,
|
|
1215
1667
|
},
|
|
1216
1668
|
}, async () => jsonResult(await client.get("/functions")));
|
|
1217
|
-
|
|
1669
|
+
registerTool("wabery_get_function", {
|
|
1218
1670
|
title: "Get a hosted function",
|
|
1219
1671
|
description: "Fetch one hosted function including its current source.",
|
|
1220
1672
|
inputSchema: { function_id: z.string().min(1) },
|
|
@@ -1225,7 +1677,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1225
1677
|
openWorldHint: false,
|
|
1226
1678
|
},
|
|
1227
1679
|
}, async ({ function_id }) => jsonResult(await client.get(`/functions/${function_id}`)));
|
|
1228
|
-
|
|
1680
|
+
registerTool("wabery_create_function", {
|
|
1229
1681
|
title: "Create a hosted function",
|
|
1230
1682
|
description: "Create a hosted function (metadata only). Deploy source separately with wabery_deploy_function. trigger_type is an AutomationTriggerType, e.g. KEYWORD, WELCOME, ANY_MESSAGE.",
|
|
1231
1683
|
inputSchema: {
|
|
@@ -1249,12 +1701,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1249
1701
|
},
|
|
1250
1702
|
}, async (input) => {
|
|
1251
1703
|
const { confirmation_token, ...body } = input;
|
|
1252
|
-
const blocked = requireWrite("wabery_create_function", confirmation_token, "create_function");
|
|
1704
|
+
const blocked = await requireWrite("wabery_create_function", confirmation_token, "create_function");
|
|
1253
1705
|
if (blocked)
|
|
1254
1706
|
return blocked;
|
|
1255
1707
|
return jsonResult(await client.post("/functions", body));
|
|
1256
1708
|
});
|
|
1257
|
-
|
|
1709
|
+
registerTool("wabery_deploy_function", {
|
|
1258
1710
|
title: "Deploy a hosted function",
|
|
1259
1711
|
description: "Bundle and upload TypeScript source for a hosted function. Returns { status, version_id?, diagnostics? } — read diagnostics on error, fix, and redeploy.",
|
|
1260
1712
|
inputSchema: {
|
|
@@ -1272,12 +1724,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1272
1724
|
openWorldHint: false,
|
|
1273
1725
|
},
|
|
1274
1726
|
}, async ({ function_id, source, confirmation_token }) => {
|
|
1275
|
-
const blocked = requireWrite("wabery_deploy_function", confirmation_token, "deploy_function");
|
|
1727
|
+
const blocked = await requireWrite("wabery_deploy_function", confirmation_token, "deploy_function");
|
|
1276
1728
|
if (blocked)
|
|
1277
1729
|
return blocked;
|
|
1278
1730
|
return jsonResult(await client.post(`/functions/${function_id}/deploy`, { source }));
|
|
1279
1731
|
});
|
|
1280
|
-
|
|
1732
|
+
registerTool("wabery_test_function", {
|
|
1281
1733
|
title: "Test a hosted function",
|
|
1282
1734
|
description: "Invoke a hosted function with a sample message event (no real send) and return its result plus logs.",
|
|
1283
1735
|
inputSchema: {
|
|
@@ -1295,12 +1747,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1295
1747
|
openWorldHint: false,
|
|
1296
1748
|
},
|
|
1297
1749
|
}, async ({ function_id, text, confirmation_token }) => {
|
|
1298
|
-
const blocked = requireWrite("wabery_test_function", confirmation_token, "test_function");
|
|
1750
|
+
const blocked = await requireWrite("wabery_test_function", confirmation_token, "test_function");
|
|
1299
1751
|
if (blocked)
|
|
1300
1752
|
return blocked;
|
|
1301
1753
|
return jsonResult(await client.post(`/functions/${function_id}/test`, { text }));
|
|
1302
1754
|
});
|
|
1303
|
-
|
|
1755
|
+
registerTool("wabery_tail_function_logs", {
|
|
1304
1756
|
title: "Tail hosted function logs",
|
|
1305
1757
|
description: "Recent invocations of a hosted function (status, cpu, error, captured logs) for debugging.",
|
|
1306
1758
|
inputSchema: { function_id: z.string().min(1) },
|
|
@@ -1311,7 +1763,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1311
1763
|
openWorldHint: false,
|
|
1312
1764
|
},
|
|
1313
1765
|
}, async ({ function_id }) => jsonResult(await client.get(`/functions/${function_id}/logs`)));
|
|
1314
|
-
|
|
1766
|
+
registerTool("wabery_delete_function", {
|
|
1315
1767
|
title: "Delete a hosted function",
|
|
1316
1768
|
description: "Delete a hosted function and its deployed Worker.",
|
|
1317
1769
|
inputSchema: {
|
|
@@ -1328,12 +1780,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1328
1780
|
openWorldHint: false,
|
|
1329
1781
|
},
|
|
1330
1782
|
}, async ({ function_id, confirmation_token }) => {
|
|
1331
|
-
const blocked = requireWrite("wabery_delete_function", confirmation_token, "delete_function");
|
|
1783
|
+
const blocked = await requireWrite("wabery_delete_function", confirmation_token, "delete_function");
|
|
1332
1784
|
if (blocked)
|
|
1333
1785
|
return blocked;
|
|
1334
1786
|
return jsonResult(await client.delete(`/functions/${function_id}`));
|
|
1335
1787
|
});
|
|
1336
|
-
|
|
1788
|
+
registerTool("wabery_update_function", {
|
|
1337
1789
|
title: "Update a hosted function",
|
|
1338
1790
|
description: "Update a hosted function: rename, set expose_as_mcp_tool (make it callable by external agents), toggle is_active, or set input_schema (the tool schema used for agent/MCP calls).",
|
|
1339
1791
|
inputSchema: {
|
|
@@ -1355,12 +1807,12 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1355
1807
|
},
|
|
1356
1808
|
}, async (input) => {
|
|
1357
1809
|
const { function_id, confirmation_token, ...body } = input;
|
|
1358
|
-
const blocked = requireWrite("wabery_update_function", confirmation_token, "update_function");
|
|
1810
|
+
const blocked = await requireWrite("wabery_update_function", confirmation_token, "update_function");
|
|
1359
1811
|
if (blocked)
|
|
1360
1812
|
return blocked;
|
|
1361
1813
|
return jsonResult(await client.patch(`/functions/${function_id}`, body));
|
|
1362
1814
|
});
|
|
1363
|
-
|
|
1815
|
+
registerTool("wabery_invoke_function", {
|
|
1364
1816
|
title: "Invoke a hosted function as a tool",
|
|
1365
1817
|
description: "Call a hosted function as a tool (mcp_call) with structured arguments and return its result. Optionally target a specific contact.",
|
|
1366
1818
|
inputSchema: {
|
|
@@ -1379,7 +1831,7 @@ export function createWaberyMcpServer(client = new WaberyApiClient(), options =
|
|
|
1379
1831
|
openWorldHint: true,
|
|
1380
1832
|
},
|
|
1381
1833
|
}, async ({ function_id, arguments: args, contact_id, confirmation_token, }) => {
|
|
1382
|
-
const blocked = requireWrite("wabery_invoke_function", confirmation_token, "invoke_function");
|
|
1834
|
+
const blocked = await requireWrite("wabery_invoke_function", confirmation_token, "invoke_function");
|
|
1383
1835
|
if (blocked)
|
|
1384
1836
|
return blocked;
|
|
1385
1837
|
return jsonResult(await client.post(`/functions/${function_id}/invoke`, {
|