neuron-mcp-server 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +13 -0
- package/AI_PERSONALITY_DESIGN_RESEARCH.md +1020 -0
- package/Dockerfile +18 -0
- package/README.md +146 -131
- package/dist/client.d.ts +6 -0
- package/dist/client.js +50 -12
- package/dist/client.js.map +1 -1
- package/dist/index.js +51 -7
- package/dist/index.js.map +1 -1
- package/dist/tools/ai.js +9 -3
- package/dist/tools/ai.js.map +1 -1
- package/dist/tools/approvals.d.ts +2 -0
- package/dist/tools/approvals.js +181 -0
- package/dist/tools/approvals.js.map +1 -0
- package/dist/tools/audit.js +7 -4
- package/dist/tools/audit.js.map +1 -1
- package/dist/tools/auth.js +119 -19
- package/dist/tools/auth.js.map +1 -1
- package/dist/tools/billing.js +22 -6
- package/dist/tools/billing.js.map +1 -1
- package/dist/tools/blog.js +98 -34
- package/dist/tools/blog.js.map +1 -1
- package/dist/tools/bot-api-keys.js +27 -12
- package/dist/tools/bot-api-keys.js.map +1 -1
- package/dist/tools/bot-api.js +53 -27
- package/dist/tools/bot-api.js.map +1 -1
- package/dist/tools/bots.js +116 -68
- package/dist/tools/bots.js.map +1 -1
- package/dist/tools/broadcasts.js +78 -38
- package/dist/tools/broadcasts.js.map +1 -1
- package/dist/tools/builtin-tools.js +19 -12
- package/dist/tools/builtin-tools.js.map +1 -1
- package/dist/tools/campaigns.js +149 -61
- package/dist/tools/campaigns.js.map +1 -1
- package/dist/tools/channels.js +163 -53
- package/dist/tools/channels.js.map +1 -1
- package/dist/tools/contact-lists.js +111 -51
- package/dist/tools/contact-lists.js.map +1 -1
- package/dist/tools/contacts.js +117 -44
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/conversations.js +95 -50
- package/dist/tools/conversations.js.map +1 -1
- package/dist/tools/flows.d.ts +2 -0
- package/dist/tools/flows.js +242 -0
- package/dist/tools/flows.js.map +1 -0
- package/dist/tools/group-management.d.ts +2 -0
- package/dist/tools/group-management.js +239 -0
- package/dist/tools/group-management.js.map +1 -0
- package/dist/tools/leads.d.ts +2 -0
- package/dist/tools/leads.js +100 -0
- package/dist/tools/leads.js.map +1 -0
- package/dist/tools/list-campaigns.d.ts +2 -0
- package/dist/tools/list-campaigns.js +152 -0
- package/dist/tools/list-campaigns.js.map +1 -0
- package/dist/tools/list-pool.js +250 -33
- package/dist/tools/list-pool.js.map +1 -1
- package/dist/tools/media.js +12 -7
- package/dist/tools/media.js.map +1 -1
- package/dist/tools/newsletters.js +106 -26
- package/dist/tools/newsletters.js.map +1 -1
- package/dist/tools/organizations.js +76 -35
- package/dist/tools/organizations.js.map +1 -1
- package/dist/tools/outbound-webhooks.js +32 -20
- package/dist/tools/outbound-webhooks.js.map +1 -1
- package/dist/tools/payouts.js +44 -17
- package/dist/tools/payouts.js.map +1 -1
- package/dist/tools/personas.d.ts +2 -0
- package/dist/tools/personas.js +141 -0
- package/dist/tools/personas.js.map +1 -0
- package/dist/tools/polls.d.ts +2 -0
- package/dist/tools/polls.js +30 -0
- package/dist/tools/polls.js.map +1 -0
- package/dist/tools/pool.js +101 -43
- package/dist/tools/pool.js.map +1 -1
- package/dist/tools/products.d.ts +2 -0
- package/dist/tools/products.js +277 -0
- package/dist/tools/products.js.map +1 -0
- package/dist/tools/profile-privacy.d.ts +2 -0
- package/dist/tools/profile-privacy.js +195 -0
- package/dist/tools/profile-privacy.js.map +1 -0
- package/dist/tools/reflections.js +25 -13
- package/dist/tools/reflections.js.map +1 -1
- package/dist/tools/scheduled-messages.d.ts +2 -0
- package/dist/tools/scheduled-messages.js +124 -0
- package/dist/tools/scheduled-messages.js.map +1 -0
- package/dist/tools/social-channels.d.ts +2 -0
- package/dist/tools/social-channels.js +318 -0
- package/dist/tools/social-channels.js.map +1 -0
- package/dist/tools/tasks.d.ts +2 -0
- package/dist/tools/tasks.js +155 -0
- package/dist/tools/tasks.js.map +1 -0
- package/dist/tools/tools.js +86 -40
- package/dist/tools/tools.js.map +1 -1
- package/dist/tools/wallets.js +34 -12
- package/dist/tools/wallets.js.map +1 -1
- package/dist/tools/webhooks.js +35 -20
- package/dist/tools/webhooks.js.map +1 -1
- package/dist/tools/whatsapp-actions.d.ts +2 -0
- package/dist/tools/whatsapp-actions.js +186 -0
- package/dist/tools/whatsapp-actions.js.map +1 -0
- package/package.json +4 -31
- package/scripts/setup-delivahere-bot.ts +804 -0
- package/scripts/test-delivahere-bot.ts +128 -0
- package/scripts/update-letschop-prompt.ts +194 -0
- package/server.json +26 -0
- package/smithery.yaml +19 -0
- package/src/client.ts +169 -0
- package/src/index.ts +256 -0
- package/src/tools/ai.ts +35 -0
- package/src/tools/approvals.ts +231 -0
- package/src/tools/audit.ts +49 -0
- package/src/tools/auth.ts +536 -0
- package/src/tools/billing.ts +79 -0
- package/src/tools/blog.ts +264 -0
- package/src/tools/bot-api-keys.ts +87 -0
- package/src/tools/bot-api.ts +193 -0
- package/src/tools/bots.ts +320 -0
- package/src/tools/broadcasts.ts +224 -0
- package/src/tools/builtin-tools.ts +108 -0
- package/src/tools/campaigns.ts +429 -0
- package/src/tools/channels.ts +520 -0
- package/src/tools/contact-lists.ts +320 -0
- package/src/tools/contacts.ts +395 -0
- package/src/tools/conversations.ts +349 -0
- package/src/tools/flows.ts +298 -0
- package/src/tools/group-management.ts +265 -0
- package/src/tools/knowledge-bases.ts +451 -0
- package/src/tools/leads.ts +104 -0
- package/src/tools/list-campaigns.ts +190 -0
- package/src/tools/list-pool.ts +389 -0
- package/src/tools/media.ts +50 -0
- package/src/tools/newsletters.ts +271 -0
- package/src/tools/organizations.ts +287 -0
- package/src/tools/outbound-webhooks.ts +181 -0
- package/src/tools/payouts.ts +133 -0
- package/src/tools/personas.ts +145 -0
- package/src/tools/polls.ts +32 -0
- package/src/tools/pool.ts +293 -0
- package/src/tools/products.ts +280 -0
- package/src/tools/profile-privacy.ts +215 -0
- package/src/tools/reflections.ts +124 -0
- package/src/tools/scheduled-messages.ts +144 -0
- package/src/tools/social-channels.ts +396 -0
- package/src/tools/tasks.ts +198 -0
- package/src/tools/tools.ts +255 -0
- package/src/tools/wallets.ts +107 -0
- package/src/tools/webhooks.ts +166 -0
- package/src/tools/whatsapp-actions.ts +203 -0
- package/tsconfig.json +20 -0
- package/LICENSE +0 -21
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { api, jsonResult, errorResult } from "../client.js";
|
|
4
|
+
|
|
5
|
+
const eventTypes = [
|
|
6
|
+
"message.created",
|
|
7
|
+
"message.assistant",
|
|
8
|
+
"conversation.created",
|
|
9
|
+
"conversation.escalated",
|
|
10
|
+
"conversation.closed",
|
|
11
|
+
"bot.paused",
|
|
12
|
+
"bot.resumed",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Register outbound webhook tools (event-driven webhooks that fire on bot events)
|
|
17
|
+
*/
|
|
18
|
+
export function registerOutboundWebhookTools(server: McpServer): void {
|
|
19
|
+
// 1. Create Outbound Webhook
|
|
20
|
+
server.registerTool(
|
|
21
|
+
"neuron_create_outbound_webhook",
|
|
22
|
+
{
|
|
23
|
+
title: "Create Outbound Webhook",
|
|
24
|
+
description:
|
|
25
|
+
"Register a new outbound webhook that sends HTTP POST requests to an external URL when specified bot events occur. " +
|
|
26
|
+
"Supported events: message.created, message.assistant, conversation.created, conversation.escalated, " +
|
|
27
|
+
"conversation.closed, bot.paused, bot.resumed.",
|
|
28
|
+
inputSchema: z.object({
|
|
29
|
+
botId: z.string().describe("Unique identifier (UUID) of the bot to create the outbound webhook for"),
|
|
30
|
+
name: z.string().describe("Human-readable name for the outbound webhook"),
|
|
31
|
+
url: z.string().describe("Destination URL that will receive webhook event payloads (HTTPS recommended)"),
|
|
32
|
+
eventTypes: z
|
|
33
|
+
.array(z.enum(eventTypes))
|
|
34
|
+
.describe("Array of event types that should trigger this webhook"),
|
|
35
|
+
filterPrompt: z
|
|
36
|
+
.string()
|
|
37
|
+
.optional()
|
|
38
|
+
.describe("Optional LLM prompt to selectively filter which events are forwarded (e.g., 'only forward escalations about billing')"),
|
|
39
|
+
}),
|
|
40
|
+
annotations: {
|
|
41
|
+
readOnlyHint: false,
|
|
42
|
+
destructiveHint: false,
|
|
43
|
+
idempotentHint: false,
|
|
44
|
+
openWorldHint: true,
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
async (args) => {
|
|
48
|
+
const { botId, ...body } = args;
|
|
49
|
+
const response = await api("POST", `/webhooks/outbound/bot/${botId}`, body);
|
|
50
|
+
|
|
51
|
+
if (response.success) {
|
|
52
|
+
return jsonResult(response.data);
|
|
53
|
+
} else {
|
|
54
|
+
return errorResult(response.error || "Failed to create outbound webhook");
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// 2. List Outbound Webhooks
|
|
60
|
+
server.registerTool(
|
|
61
|
+
"neuron_list_outbound_webhooks",
|
|
62
|
+
{
|
|
63
|
+
title: "List Outbound Webhooks",
|
|
64
|
+
description: "Retrieve all outbound webhooks configured for a specific bot, including their target URLs and subscribed event types.",
|
|
65
|
+
inputSchema: z.object({
|
|
66
|
+
botId: z.string().describe("Unique identifier (UUID) of the bot to list outbound webhooks for"),
|
|
67
|
+
}),
|
|
68
|
+
annotations: {
|
|
69
|
+
readOnlyHint: true,
|
|
70
|
+
destructiveHint: false,
|
|
71
|
+
idempotentHint: true,
|
|
72
|
+
openWorldHint: true,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
async (args) => {
|
|
76
|
+
const response = await api("GET", `/webhooks/outbound/bot/${args.botId}`);
|
|
77
|
+
|
|
78
|
+
if (response.success) {
|
|
79
|
+
return jsonResult(response.data);
|
|
80
|
+
} else {
|
|
81
|
+
return errorResult(response.error || "Failed to list outbound webhooks");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// 3. Update Outbound Webhook
|
|
87
|
+
server.registerTool(
|
|
88
|
+
"neuron_update_outbound_webhook",
|
|
89
|
+
{
|
|
90
|
+
title: "Update Outbound Webhook",
|
|
91
|
+
description: "Modify an existing outbound webhook's configuration, including its destination URL, subscribed event types, or filter prompt.",
|
|
92
|
+
inputSchema: z.object({
|
|
93
|
+
id: z.string().describe("Unique identifier (UUID) of the outbound webhook to update"),
|
|
94
|
+
name: z.string().optional().describe("Updated human-readable name for the webhook"),
|
|
95
|
+
url: z.string().optional().describe("Updated destination URL that will receive webhook event payloads (HTTPS recommended)"),
|
|
96
|
+
eventTypes: z
|
|
97
|
+
.array(z.enum(eventTypes))
|
|
98
|
+
.optional()
|
|
99
|
+
.describe("Updated array of event types that should trigger this webhook"),
|
|
100
|
+
filterPrompt: z.string().optional().describe("Updated LLM prompt for selectively filtering forwarded events"),
|
|
101
|
+
}),
|
|
102
|
+
annotations: {
|
|
103
|
+
readOnlyHint: false,
|
|
104
|
+
destructiveHint: false,
|
|
105
|
+
idempotentHint: true,
|
|
106
|
+
openWorldHint: true,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
async (args) => {
|
|
110
|
+
const { id, ...body } = args;
|
|
111
|
+
const response = await api("PUT", `/webhooks/outbound/${id}`, body);
|
|
112
|
+
|
|
113
|
+
if (response.success) {
|
|
114
|
+
return jsonResult(response.data);
|
|
115
|
+
} else {
|
|
116
|
+
return errorResult(response.error || "Failed to update outbound webhook");
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// 4. Delete Outbound Webhook
|
|
122
|
+
server.registerTool(
|
|
123
|
+
"neuron_delete_outbound_webhook",
|
|
124
|
+
{
|
|
125
|
+
title: "Delete Outbound Webhook",
|
|
126
|
+
description: "Permanently remove an outbound webhook. The external endpoint will no longer receive event notifications from this bot.",
|
|
127
|
+
inputSchema: z.object({
|
|
128
|
+
id: z.string().describe("Unique identifier (UUID) of the outbound webhook to delete"),
|
|
129
|
+
}),
|
|
130
|
+
annotations: {
|
|
131
|
+
readOnlyHint: false,
|
|
132
|
+
destructiveHint: true,
|
|
133
|
+
idempotentHint: true,
|
|
134
|
+
openWorldHint: true,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
async (args) => {
|
|
138
|
+
const response = await api("DELETE", `/webhooks/outbound/${args.id}`);
|
|
139
|
+
|
|
140
|
+
if (response.success) {
|
|
141
|
+
return jsonResult(response.data || { message: "Outbound webhook deleted successfully" });
|
|
142
|
+
} else {
|
|
143
|
+
return errorResult(response.error || "Failed to delete outbound webhook");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// 5. Get Outbound Webhook Logs
|
|
149
|
+
server.registerTool(
|
|
150
|
+
"neuron_get_outbound_webhook_logs",
|
|
151
|
+
{
|
|
152
|
+
title: "Get Outbound Webhook Logs",
|
|
153
|
+
description: "Retrieve delivery logs for an outbound webhook to monitor event dispatch history and debug delivery failures.",
|
|
154
|
+
inputSchema: z.object({
|
|
155
|
+
id: z.string().describe("Unique identifier (UUID) of the outbound webhook to get logs for"),
|
|
156
|
+
page: z.number().optional().describe("Page number for pagination (default: 1)"),
|
|
157
|
+
limit: z.number().optional().describe("Number of log entries per page (default: 20)"),
|
|
158
|
+
}),
|
|
159
|
+
annotations: {
|
|
160
|
+
readOnlyHint: true,
|
|
161
|
+
destructiveHint: false,
|
|
162
|
+
idempotentHint: true,
|
|
163
|
+
openWorldHint: true,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
async (args) => {
|
|
167
|
+
const { id, page, limit } = args;
|
|
168
|
+
const params: Record<string, string> = {};
|
|
169
|
+
if (page !== undefined) params.page = String(page);
|
|
170
|
+
if (limit !== undefined) params.limit = String(limit);
|
|
171
|
+
|
|
172
|
+
const response = await api("GET", `/webhooks/outbound/${id}/logs`, undefined, params);
|
|
173
|
+
|
|
174
|
+
if (response.success) {
|
|
175
|
+
return jsonResult(response.data);
|
|
176
|
+
} else {
|
|
177
|
+
return errorResult(response.error || "Failed to get outbound webhook logs");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { api, jsonResult, errorResult } from "../client.js";
|
|
4
|
+
|
|
5
|
+
export function registerPayoutTools(server: McpServer): void {
|
|
6
|
+
server.registerTool(
|
|
7
|
+
"neuron_verify_bank_account",
|
|
8
|
+
{
|
|
9
|
+
title: "Verify Bank Account",
|
|
10
|
+
description: "Verify a Nigerian bank account number via Paystack to confirm the account holder name before saving.",
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
bankCode: z.string().describe("Nigerian bank code (e.g., '058' for GTBank, '044' for Access Bank)"),
|
|
13
|
+
accountNumber: z.string().describe("10-digit Nigerian bank account number to verify"),
|
|
14
|
+
}),
|
|
15
|
+
annotations: {
|
|
16
|
+
readOnlyHint: true,
|
|
17
|
+
destructiveHint: false,
|
|
18
|
+
idempotentHint: true,
|
|
19
|
+
openWorldHint: false,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
async (params) => {
|
|
23
|
+
try {
|
|
24
|
+
const result = await api("POST", "/payouts/verify-account", params);
|
|
25
|
+
return jsonResult(result);
|
|
26
|
+
} catch (error) {
|
|
27
|
+
return errorResult(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
server.registerTool(
|
|
33
|
+
"neuron_save_bank_account",
|
|
34
|
+
{
|
|
35
|
+
title: "Save Bank Account",
|
|
36
|
+
description: "Save a verified bank account as the organization's payout destination. Verify the account first using neuron_verify_bank_account.",
|
|
37
|
+
inputSchema: z.object({
|
|
38
|
+
bankCode: z.string().describe("Nigerian bank code (e.g., '058' for GTBank, '044' for Access Bank)"),
|
|
39
|
+
bankName: z.string().describe("Full name of the bank (e.g., 'Guaranty Trust Bank')"),
|
|
40
|
+
accountNumber: z.string().describe("10-digit Nigerian bank account number"),
|
|
41
|
+
accountName: z.string().describe("Account holder name as returned from bank verification"),
|
|
42
|
+
}),
|
|
43
|
+
annotations: {
|
|
44
|
+
readOnlyHint: false,
|
|
45
|
+
destructiveHint: false,
|
|
46
|
+
idempotentHint: true,
|
|
47
|
+
openWorldHint: false,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
async (params) => {
|
|
51
|
+
try {
|
|
52
|
+
const result = await api("POST", "/payouts/bank-account", params);
|
|
53
|
+
return jsonResult(result);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
return errorResult(error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
server.registerTool(
|
|
61
|
+
"neuron_get_bank_account",
|
|
62
|
+
{
|
|
63
|
+
title: "Get Bank Account",
|
|
64
|
+
description: "Retrieve the saved bank account details currently configured as the organization's payout destination.",
|
|
65
|
+
inputSchema: z.object({}),
|
|
66
|
+
annotations: {
|
|
67
|
+
readOnlyHint: true,
|
|
68
|
+
destructiveHint: false,
|
|
69
|
+
idempotentHint: true,
|
|
70
|
+
openWorldHint: false,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
async () => {
|
|
74
|
+
try {
|
|
75
|
+
const result = await api("GET", "/payouts/bank-account");
|
|
76
|
+
return jsonResult(result);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return errorResult(error);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
server.registerTool(
|
|
84
|
+
"neuron_request_payout",
|
|
85
|
+
{
|
|
86
|
+
title: "Request Payout",
|
|
87
|
+
description: "Request a payout to transfer funds from the organization wallet to the linked bank account. A bank account must be saved first.",
|
|
88
|
+
inputSchema: z.object({
|
|
89
|
+
amount: z.number().int().positive().describe("Amount to transfer in the smallest currency unit — kobo for NGN (e.g., 100000 = NGN 1,000)"),
|
|
90
|
+
}),
|
|
91
|
+
annotations: {
|
|
92
|
+
readOnlyHint: false,
|
|
93
|
+
destructiveHint: false,
|
|
94
|
+
idempotentHint: false,
|
|
95
|
+
openWorldHint: false,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
async (params) => {
|
|
99
|
+
try {
|
|
100
|
+
const result = await api("POST", "/payouts/request", params);
|
|
101
|
+
return jsonResult(result);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return errorResult(error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
server.registerTool(
|
|
109
|
+
"neuron_list_payouts",
|
|
110
|
+
{
|
|
111
|
+
title: "List Payouts",
|
|
112
|
+
description: "Retrieve the payout history for the organization, including amounts, statuses, and timestamps.",
|
|
113
|
+
inputSchema: z.object({
|
|
114
|
+
page: z.number().int().positive().optional().describe("Page number for pagination (default: 1)"),
|
|
115
|
+
limit: z.number().int().positive().optional().describe("Number of payout records per page (default: 20)"),
|
|
116
|
+
}),
|
|
117
|
+
annotations: {
|
|
118
|
+
readOnlyHint: true,
|
|
119
|
+
destructiveHint: false,
|
|
120
|
+
idempotentHint: true,
|
|
121
|
+
openWorldHint: false,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
async (params) => {
|
|
125
|
+
try {
|
|
126
|
+
const result = await api("GET", "/payouts", undefined, params);
|
|
127
|
+
return jsonResult(result);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return errorResult(error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { api, jsonResult, errorResult } from "../client.js";
|
|
4
|
+
|
|
5
|
+
const READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
6
|
+
const WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
7
|
+
|
|
8
|
+
export function registerPersonaTools(server: McpServer): void {
|
|
9
|
+
server.registerTool(
|
|
10
|
+
"neuron_list_personas",
|
|
11
|
+
{ title: "List Personas", description: "List voice personas for the org. Each persona holds a voiceProfile (tone/style/themes/dos/donts) + exemplars distilled from a Collection of mined content.", inputSchema: z.object({}), annotations: READ },
|
|
12
|
+
async () => {
|
|
13
|
+
try { return jsonResult(await api("GET", "/personas")); } catch (e) { return errorResult(e); }
|
|
14
|
+
},
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
server.registerTool(
|
|
18
|
+
"neuron_get_persona",
|
|
19
|
+
{ title: "Get Persona", description: "Get one voice persona (full voiceProfile + exemplars).", inputSchema: z.object({ id: z.string().describe("Persona UUID") }), annotations: READ },
|
|
20
|
+
async ({ id }) => {
|
|
21
|
+
try { return jsonResult(await api("GET", `/personas/${id}`)); } catch (e) { return errorResult(e); }
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
server.registerTool(
|
|
26
|
+
"neuron_build_persona",
|
|
27
|
+
{
|
|
28
|
+
title: "Build Persona from Collection",
|
|
29
|
+
description:
|
|
30
|
+
"Distill a reusable voice from a Collection of mined content: analyzes the top-engagement items and reverse-engineers a voiceProfile (tone, diction, cadence, emoji/formatting habits, themes, dos/donts) + representative exemplars. Requires the collection to have >=3 usable samples. Runs an LLM call (a few seconds).",
|
|
31
|
+
inputSchema: z.object({
|
|
32
|
+
collectionId: z.string().describe("Source Collection UUID (built by the web agent / content mining)."),
|
|
33
|
+
name: z.string().optional().describe("Persona name. Defaults to '<collection name> voice'."),
|
|
34
|
+
sampleLimit: z.number().int().min(1).max(200).optional().describe("How many top-engagement items to analyze (default 40)."),
|
|
35
|
+
}),
|
|
36
|
+
annotations: WRITE,
|
|
37
|
+
},
|
|
38
|
+
async (body) => {
|
|
39
|
+
try { return jsonResult(await api("POST", "/personas/build", body)); } catch (e) { return errorResult(e); }
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
server.registerTool(
|
|
44
|
+
"neuron_build_persona_from_text",
|
|
45
|
+
{
|
|
46
|
+
title: "Build Persona from Text",
|
|
47
|
+
description:
|
|
48
|
+
"Distill a reusable voice from raw text — no Collection or browser extension needed. Feed it pasted posts, a chat/dialogue transcript, WhatsApp-export text, or your own well-crafted writing sample, and it reverse-engineers a voiceProfile (tone, diction, cadence, emoji/formatting habits, themes, dos/donts) + exemplars. Provide `text` (auto-split into samples) and/or `samples` (already-split). Runs an LLM call (a few seconds).",
|
|
49
|
+
inputSchema: z.object({
|
|
50
|
+
text: z.string().optional().describe("A raw blob to distill: posts, a dialogue, a chat transcript, or a writing sample. Auto-split into samples."),
|
|
51
|
+
samples: z.array(z.string()).optional().describe("Pre-split writing samples (alternative or addition to `text`)."),
|
|
52
|
+
name: z.string().optional().describe("Persona name. Defaults to '<author> voice' or 'Custom voice'."),
|
|
53
|
+
description: z.string().optional(),
|
|
54
|
+
speaker: z.string().optional().describe("If `text` is a chat transcript, keep only this speaker's lines (name match) — so a group chat yields one person's voice."),
|
|
55
|
+
platform: z.string().optional().describe("Optional metadata tag for the samples (e.g. 'whatsapp', 'x')."),
|
|
56
|
+
authorLabel: z.string().optional().describe("Who the voice belongs to, for a more grounded analysis."),
|
|
57
|
+
}),
|
|
58
|
+
annotations: WRITE,
|
|
59
|
+
},
|
|
60
|
+
async (body) => {
|
|
61
|
+
try { return jsonResult(await api("POST", "/personas/build-from-text", body)); } catch (e) { return errorResult(e); }
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
server.registerTool(
|
|
66
|
+
"neuron_build_persona_from_conversation",
|
|
67
|
+
{
|
|
68
|
+
title: "Build Persona from WhatsApp Chat",
|
|
69
|
+
description:
|
|
70
|
+
"Distill a reusable voice from an existing WhatsApp conversation's stored messages — no extension needed. `author` picks whose voice: 'me' (owner's own outgoing texts), 'contact' (the other party), 'bot', or 'any'. For a group chat, pass senderName/senderPhone to isolate one participant. Requires >=3 usable text messages. Runs an LLM call.",
|
|
71
|
+
inputSchema: z.object({
|
|
72
|
+
conversationId: z.string().describe("Conversation UUID (from neuron_list_conversations)."),
|
|
73
|
+
author: z.enum(["me", "contact", "bot", "any"]).optional().describe("Whose voice to capture. Default 'me'."),
|
|
74
|
+
senderName: z.string().optional().describe("For groups: match one participant by display name."),
|
|
75
|
+
senderPhone: z.string().optional().describe("For groups: match one participant by phone."),
|
|
76
|
+
name: z.string().optional(),
|
|
77
|
+
limit: z.number().int().min(1).max(500).optional().describe("How many recent messages to read (default 200)."),
|
|
78
|
+
}),
|
|
79
|
+
annotations: WRITE,
|
|
80
|
+
},
|
|
81
|
+
async (body) => {
|
|
82
|
+
try { return jsonResult(await api("POST", "/personas/build-from-conversation", body)); } catch (e) { return errorResult(e); }
|
|
83
|
+
},
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
server.registerTool(
|
|
87
|
+
"neuron_generate_from_persona",
|
|
88
|
+
{
|
|
89
|
+
title: "Generate Content in Voice",
|
|
90
|
+
description: "Generate content written in the persona's voice for a topic/brief. Returns N variations. Stateless — nothing is persisted.",
|
|
91
|
+
inputSchema: z.object({
|
|
92
|
+
id: z.string().describe("Persona UUID"),
|
|
93
|
+
topic: z.string().optional().describe("What to write about."),
|
|
94
|
+
platform: z.string().optional().describe("Target platform (instagram/x/tiktok/linkedin/facebook/whatsapp...)."),
|
|
95
|
+
format: z.string().optional().describe("Desired format (post, reply, caption, thread, DM...)."),
|
|
96
|
+
count: z.number().int().min(1).max(10).optional().describe("Number of variations (1-10, default 1)."),
|
|
97
|
+
extraInstructions: z.string().optional(),
|
|
98
|
+
}),
|
|
99
|
+
annotations: WRITE,
|
|
100
|
+
},
|
|
101
|
+
async ({ id, ...body }) => {
|
|
102
|
+
try { return jsonResult(await api("POST", `/personas/${id}/generate`, body)); } catch (e) { return errorResult(e); }
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
server.registerTool(
|
|
107
|
+
"neuron_apply_persona_to_bot",
|
|
108
|
+
{
|
|
109
|
+
title: "Apply Persona Voice to Bot",
|
|
110
|
+
description: "Give a bot this persona's voice: merges a delimited voice block into the bot's system prompt (idempotent — re-applying or swapping personas replaces it cleanly) and tags the bot's metadata with the persona id.",
|
|
111
|
+
inputSchema: z.object({ id: z.string().describe("Persona UUID"), botId: z.string().describe("Bot UUID to apply the voice to.") }),
|
|
112
|
+
annotations: WRITE,
|
|
113
|
+
},
|
|
114
|
+
async ({ id, botId }) => {
|
|
115
|
+
try { return jsonResult(await api("POST", `/personas/${id}/apply-to-bot`, { botId })); } catch (e) { return errorResult(e); }
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
server.registerTool(
|
|
120
|
+
"neuron_update_persona",
|
|
121
|
+
{
|
|
122
|
+
title: "Update Persona",
|
|
123
|
+
description: "Update a persona's name/description, or hand-tune its voiceProfile / exemplars.",
|
|
124
|
+
inputSchema: z.object({
|
|
125
|
+
id: z.string().describe("Persona UUID"),
|
|
126
|
+
name: z.string().optional(),
|
|
127
|
+
description: z.string().optional(),
|
|
128
|
+
voiceProfile: z.record(z.unknown()).optional(),
|
|
129
|
+
exemplars: z.array(z.record(z.unknown())).optional(),
|
|
130
|
+
}),
|
|
131
|
+
annotations: WRITE,
|
|
132
|
+
},
|
|
133
|
+
async ({ id, ...body }) => {
|
|
134
|
+
try { return jsonResult(await api("PUT", `/personas/${id}`, body)); } catch (e) { return errorResult(e); }
|
|
135
|
+
},
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
server.registerTool(
|
|
139
|
+
"neuron_delete_persona",
|
|
140
|
+
{ title: "Delete Persona", description: "Delete a voice persona.", inputSchema: z.object({ id: z.string().describe("Persona UUID") }), annotations: WRITE },
|
|
141
|
+
async ({ id }) => {
|
|
142
|
+
try { return jsonResult(await api("DELETE", `/personas/${id}`)); } catch (e) { return errorResult(e); }
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { api, jsonResult, errorResult } from "../client.js";
|
|
4
|
+
|
|
5
|
+
export function registerPollTools(server: McpServer): void {
|
|
6
|
+
server.registerTool(
|
|
7
|
+
"neuron_send_poll",
|
|
8
|
+
{
|
|
9
|
+
title: "Send Poll",
|
|
10
|
+
description: "Send a poll message via WhatsApp. Polls support 2-12 options and optional multi-select. Only supported on Baileys channels.",
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
channelId: z.string().describe("WhatsApp channel identifier (UUID)"),
|
|
13
|
+
to: z.string().describe("Recipient JID or phone number (individual or group)"),
|
|
14
|
+
name: z.string().min(1).describe("Poll question/title"),
|
|
15
|
+
options: z.array(z.string().min(1)).min(2).max(12).describe("Poll answer options (2-12 items)"),
|
|
16
|
+
selectableCount: z.number().int().min(0).optional().describe("Max selections allowed. 0 = unlimited (default). 1 = single-choice."),
|
|
17
|
+
}),
|
|
18
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
19
|
+
},
|
|
20
|
+
async (args) => {
|
|
21
|
+
try {
|
|
22
|
+
const res = await api("POST", `/whatsapp-actions/${args.channelId}/send-poll`, {
|
|
23
|
+
to: args.to,
|
|
24
|
+
name: args.name,
|
|
25
|
+
options: args.options,
|
|
26
|
+
selectableCount: args.selectableCount,
|
|
27
|
+
});
|
|
28
|
+
return jsonResult(res);
|
|
29
|
+
} catch (error) { return errorResult(error); }
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
}
|