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,293 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { api, jsonResult, textResult, errorResult } from "../client.js";
|
|
4
|
+
|
|
5
|
+
export function registerPoolTools(server: McpServer): void {
|
|
6
|
+
// 1. Browse pool
|
|
7
|
+
server.registerTool(
|
|
8
|
+
"neuron_browse_pool",
|
|
9
|
+
{
|
|
10
|
+
title: "Browse Pool",
|
|
11
|
+
description: "Browse the shared marketplace pool of published bots, tools, knowledge bases, and reflections with optional filtering by type, category, tags, or search query.",
|
|
12
|
+
inputSchema: z.object({
|
|
13
|
+
query: z.string().optional().describe("Free-text search query to filter pool items by name or description"),
|
|
14
|
+
type: z.enum(["knowledge_base", "bot_template", "tool_template", "reflection_pack"]).optional().describe("Filter by resource type"),
|
|
15
|
+
category: z.string().optional().describe("Filter by category name"),
|
|
16
|
+
tags: z.string().optional().describe("Filter by tags (comma-separated list, e.g., 'ai,chatbot')"),
|
|
17
|
+
featured: z.boolean().optional().describe("When true, return only featured items"),
|
|
18
|
+
page: z.number().optional().describe("Page number for pagination (default: 1)"),
|
|
19
|
+
limit: z.number().optional().describe("Number of items per page (default: 20)"),
|
|
20
|
+
}),
|
|
21
|
+
annotations: {
|
|
22
|
+
readOnlyHint: true,
|
|
23
|
+
destructiveHint: false,
|
|
24
|
+
idempotentHint: true,
|
|
25
|
+
openWorldHint: false,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
async (args) => {
|
|
29
|
+
try {
|
|
30
|
+
const queryParams: Record<string, string | number> = {};
|
|
31
|
+
if (args.query) queryParams.query = args.query;
|
|
32
|
+
if (args.type) queryParams.type = args.type;
|
|
33
|
+
if (args.category) queryParams.category = args.category;
|
|
34
|
+
if (args.tags) queryParams.tags = args.tags;
|
|
35
|
+
if (args.featured !== undefined) queryParams.featured = args.featured ? "true" : "false";
|
|
36
|
+
if (args.page !== undefined) queryParams.page = args.page;
|
|
37
|
+
if (args.limit !== undefined) queryParams.limit = args.limit;
|
|
38
|
+
const response = await api("GET", "/pool", undefined, queryParams);
|
|
39
|
+
return jsonResult(response);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return errorResult(error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// 2. Get pool item
|
|
47
|
+
server.registerTool(
|
|
48
|
+
"neuron_get_pool_item",
|
|
49
|
+
{
|
|
50
|
+
title: "Get Pool Item",
|
|
51
|
+
description: "Retrieve detailed information about a specific item in the marketplace pool, including its description, category, tags, and install count.",
|
|
52
|
+
inputSchema: z.object({
|
|
53
|
+
id: z.string().describe("Unique identifier (UUID) of the pool item to retrieve"),
|
|
54
|
+
}),
|
|
55
|
+
annotations: {
|
|
56
|
+
readOnlyHint: true,
|
|
57
|
+
destructiveHint: false,
|
|
58
|
+
idempotentHint: true,
|
|
59
|
+
openWorldHint: false,
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
async (args) => {
|
|
63
|
+
try {
|
|
64
|
+
const response = await api("GET", `/pool/${args.id}`);
|
|
65
|
+
return jsonResult(response);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
return errorResult(error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// 3. List published items
|
|
73
|
+
server.registerTool(
|
|
74
|
+
"neuron_list_published",
|
|
75
|
+
{
|
|
76
|
+
title: "List My Published Items",
|
|
77
|
+
description: "List all items the current organization has published to the marketplace pool.",
|
|
78
|
+
inputSchema: z.object({}),
|
|
79
|
+
annotations: {
|
|
80
|
+
readOnlyHint: true,
|
|
81
|
+
destructiveHint: false,
|
|
82
|
+
idempotentHint: true,
|
|
83
|
+
openWorldHint: false,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
async () => {
|
|
87
|
+
try {
|
|
88
|
+
const response = await api("GET", "/pool/my");
|
|
89
|
+
return jsonResult(response);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return errorResult(error);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// 4. List installed items
|
|
97
|
+
server.registerTool(
|
|
98
|
+
"neuron_list_installed",
|
|
99
|
+
{
|
|
100
|
+
title: "List Installed Items",
|
|
101
|
+
description: "List all items the current organization has installed from the marketplace pool.",
|
|
102
|
+
inputSchema: z.object({}),
|
|
103
|
+
annotations: {
|
|
104
|
+
readOnlyHint: true,
|
|
105
|
+
destructiveHint: false,
|
|
106
|
+
idempotentHint: true,
|
|
107
|
+
openWorldHint: false,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
async () => {
|
|
111
|
+
try {
|
|
112
|
+
const response = await api("GET", "/pool/installed");
|
|
113
|
+
return jsonResult(response);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return errorResult(error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// 5. Publish to pool
|
|
121
|
+
server.registerTool(
|
|
122
|
+
"neuron_publish_to_pool",
|
|
123
|
+
{
|
|
124
|
+
title: "Publish to Pool",
|
|
125
|
+
description: "Publish a bot, tool, knowledge base, or reflection to the shared marketplace pool for other organizations to discover and install.",
|
|
126
|
+
inputSchema: z.object({
|
|
127
|
+
type: z.enum(["knowledge_base", "bot_template", "tool_template", "reflection_pack"]).describe("Type of resource to publish to the pool"),
|
|
128
|
+
sourceId: z.string().describe("Unique identifier (UUID) of the source resource to publish"),
|
|
129
|
+
name: z.string().describe("Display name for the pool listing"),
|
|
130
|
+
description: z.string().optional().describe("Detailed description of the resource for the pool listing"),
|
|
131
|
+
category: z.string().optional().describe("Category name for organizing the resource in the pool"),
|
|
132
|
+
tags: z.array(z.string()).optional().describe("List of tags for improved discoverability (e.g., ['ai', 'customer-support'])"),
|
|
133
|
+
setupInstructions: z.string().optional().describe("Step-by-step setup guide shown after installation (tool_template only, max 5000 chars)"),
|
|
134
|
+
defaultSecrets: z.record(z.string()).optional().describe("Pre-filled secrets auto-populated on install, e.g. { OAUTH2_TOKEN_URL: 'https://...' }"),
|
|
135
|
+
}),
|
|
136
|
+
annotations: {
|
|
137
|
+
readOnlyHint: false,
|
|
138
|
+
destructiveHint: false,
|
|
139
|
+
idempotentHint: false,
|
|
140
|
+
openWorldHint: false,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
async (args) => {
|
|
144
|
+
try {
|
|
145
|
+
const response = await api("POST", "/pool", args);
|
|
146
|
+
return jsonResult(response);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
return errorResult(error);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// 6. Update pool item
|
|
154
|
+
server.registerTool(
|
|
155
|
+
"neuron_update_pool_item",
|
|
156
|
+
{
|
|
157
|
+
title: "Update Pool Item",
|
|
158
|
+
description: "Update a pool item you have published — metadata (name, description, category, tags) and content fields (setupInstructions, defaultSecrets for tool_template items). The underlying resource is re-snapshotted and version is bumped on every update.",
|
|
159
|
+
inputSchema: z.object({
|
|
160
|
+
id: z.string().describe("Unique identifier (UUID) of the pool item to update"),
|
|
161
|
+
name: z.string().optional().describe("New display name for the pool listing"),
|
|
162
|
+
description: z.string().optional().describe("New detailed description for the pool listing"),
|
|
163
|
+
category: z.string().optional().describe("New category name for the pool listing"),
|
|
164
|
+
tags: z.array(z.string()).optional().describe("New list of tags for the pool listing"),
|
|
165
|
+
setupInstructions: z.string().optional().describe("Updated setup guide shown after installation (tool_template only, max 5000 chars)"),
|
|
166
|
+
defaultSecrets: z.record(z.string()).optional().describe("Updated pre-filled secrets auto-populated on install, e.g. { OAUTH2_TOKEN_URL: 'https://...' }"),
|
|
167
|
+
}),
|
|
168
|
+
annotations: {
|
|
169
|
+
readOnlyHint: false,
|
|
170
|
+
destructiveHint: false,
|
|
171
|
+
idempotentHint: true,
|
|
172
|
+
openWorldHint: false,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
async (args) => {
|
|
176
|
+
try {
|
|
177
|
+
const { id, ...updateData } = args;
|
|
178
|
+
const response = await api("PUT", `/pool/${id}`, updateData);
|
|
179
|
+
return jsonResult(response);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
return errorResult(error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
// 7. Unpublish from pool
|
|
187
|
+
server.registerTool(
|
|
188
|
+
"neuron_unpublish_from_pool",
|
|
189
|
+
{
|
|
190
|
+
title: "Unpublish from Pool",
|
|
191
|
+
description: "Permanently remove a published item from the marketplace pool. Organizations that installed this item will retain their copies.",
|
|
192
|
+
inputSchema: z.object({
|
|
193
|
+
id: z.string().describe("Unique identifier (UUID) of the pool item to unpublish"),
|
|
194
|
+
}),
|
|
195
|
+
annotations: {
|
|
196
|
+
readOnlyHint: false,
|
|
197
|
+
destructiveHint: true,
|
|
198
|
+
idempotentHint: true,
|
|
199
|
+
openWorldHint: false,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
async (args) => {
|
|
203
|
+
try {
|
|
204
|
+
const response = await api("DELETE", `/pool/${args.id}`);
|
|
205
|
+
return jsonResult(response);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return errorResult(error);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// 8. Install from pool
|
|
213
|
+
server.registerTool(
|
|
214
|
+
"neuron_install_from_pool",
|
|
215
|
+
{
|
|
216
|
+
title: "Install from Pool",
|
|
217
|
+
description: "Install a resource from the marketplace pool into the current organization. Choose 'fork' for an independent copy or 'subscribe' to receive future updates from the publisher.",
|
|
218
|
+
inputSchema: z.object({
|
|
219
|
+
id: z.string().describe("Unique identifier (UUID) of the pool item to install"),
|
|
220
|
+
mode: z.enum(["fork", "subscribe"]).describe("Installation mode: 'fork' creates an independent copy, 'subscribe' links to publisher updates"),
|
|
221
|
+
targetBotId: z.string().optional().describe("UUID of the bot to attach the installed resource to (for tool_template or knowledge_base types)"),
|
|
222
|
+
targetKbId: z.string().optional().describe("UUID of the knowledge base to install into (for knowledge_base types)"),
|
|
223
|
+
}),
|
|
224
|
+
annotations: {
|
|
225
|
+
readOnlyHint: false,
|
|
226
|
+
destructiveHint: false,
|
|
227
|
+
idempotentHint: false,
|
|
228
|
+
openWorldHint: false,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
async (args) => {
|
|
232
|
+
try {
|
|
233
|
+
const { id, ...body } = args;
|
|
234
|
+
const response = await api("POST", `/pool/${id}/install`, body);
|
|
235
|
+
return jsonResult(response);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
return errorResult(error);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// 9. Pull pool update
|
|
243
|
+
server.registerTool(
|
|
244
|
+
"neuron_pull_pool_update",
|
|
245
|
+
{
|
|
246
|
+
title: "Pull Pool Update",
|
|
247
|
+
description: "Pull the latest updates for a subscribed pool resource to sync with the publisher's changes. Only applies to items installed with 'subscribe' mode.",
|
|
248
|
+
inputSchema: z.object({
|
|
249
|
+
installId: z.string().describe("Unique identifier (UUID) of the installed resource to pull updates for"),
|
|
250
|
+
}),
|
|
251
|
+
annotations: {
|
|
252
|
+
readOnlyHint: false,
|
|
253
|
+
destructiveHint: false,
|
|
254
|
+
idempotentHint: true,
|
|
255
|
+
openWorldHint: false,
|
|
256
|
+
},
|
|
257
|
+
},
|
|
258
|
+
async (args) => {
|
|
259
|
+
try {
|
|
260
|
+
const response = await api("POST", `/pool/installed/${args.installId}/pull`);
|
|
261
|
+
return jsonResult(response);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
return errorResult(error);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
// 10. Toggle featured status
|
|
269
|
+
server.registerTool(
|
|
270
|
+
"neuron_toggle_featured",
|
|
271
|
+
{
|
|
272
|
+
title: "Toggle Featured Status",
|
|
273
|
+
description: "Toggle the featured status of a pool item. Featured items appear prominently in the marketplace. Requires admin privileges.",
|
|
274
|
+
inputSchema: z.object({
|
|
275
|
+
id: z.string().describe("Unique identifier (UUID) of the pool item to feature or unfeature"),
|
|
276
|
+
}),
|
|
277
|
+
annotations: {
|
|
278
|
+
readOnlyHint: false,
|
|
279
|
+
destructiveHint: false,
|
|
280
|
+
idempotentHint: true,
|
|
281
|
+
openWorldHint: false,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
async (args) => {
|
|
285
|
+
try {
|
|
286
|
+
const response = await api("POST", `/pool/${args.id}/feature`);
|
|
287
|
+
return jsonResult(response);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
return errorResult(error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
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 PRICE_NOTE = "Amount in kobo (minor units): ₦1 = 100. Minimum ₦500 = 50000.";
|
|
6
|
+
|
|
7
|
+
const deliveryEnum = z.enum(["file", "link", "code"]);
|
|
8
|
+
const feePayerEnum = z.enum(["buyer", "seller"]);
|
|
9
|
+
|
|
10
|
+
const productFields = {
|
|
11
|
+
title: z.string().describe("Product title shown to buyers"),
|
|
12
|
+
description: z.string().optional().describe("Rich description / what the buyer gets"),
|
|
13
|
+
priceMinor: z.number().int().describe(`Current price (charged). ${PRICE_NOTE} Set 0 for a free product (no payment).`),
|
|
14
|
+
originalPriceMinor: z.number().int().nullable().optional().describe("Optional compare-at / 'was' price (kobo), shown struck through. Must be ≥ current price."),
|
|
15
|
+
currency: z.string().optional().describe("ISO currency (default NGN)"),
|
|
16
|
+
channelId: z.string().uuid().nullable().optional().describe("WhatsApp session UUID used to deliver the product (defaults to the org default channel)"),
|
|
17
|
+
deliveryType: deliveryEnum.describe("How the product is delivered: 'file' (uploaded file), 'link' (URL), or 'code' (text/code)"),
|
|
18
|
+
deliveryFileUrl: z.string().url().nullable().optional().describe("For deliveryType 'file': the uploaded file URL (upload first with neuron_upload_media)"),
|
|
19
|
+
deliveryFileName: z.string().nullable().optional().describe("Filename shown in the WhatsApp document"),
|
|
20
|
+
deliveryText: z.string().nullable().optional().describe("For 'link'/'code': the link or code the buyer receives. Optional note for 'file'."),
|
|
21
|
+
coverImageUrl: z.string().url().nullable().optional().describe("Cover image URL"),
|
|
22
|
+
coverEmoji: z.string().nullable().optional().describe("Emoji used as a cover fallback (e.g. 📓)"),
|
|
23
|
+
coverType: z.enum(["none", "emoji", "image", "color", "gradient"]).optional().describe("How the cover renders"),
|
|
24
|
+
coverColor: z.string().nullable().optional().describe("Cover colour (hex) for 'color'/'gradient' cover types"),
|
|
25
|
+
coverColor2: z.string().nullable().optional().describe("Second colour (hex) for 'gradient' cover"),
|
|
26
|
+
content: z.array(z.any()).optional().describe("Rich product-page content blocks. Ordered array of {type, ...}: heading {text,level}, text {markdown}, image {url,caption}, video {url}, callout {emoji,text}, collapsible {title,blocks[]}, divider {}, button {label,url}, embed {url}, spacer {}"),
|
|
27
|
+
theme: z.record(z.string()).optional().describe("Per-product theme: { bg, accent, text (hex), mode: 'light'|'dark', layout: 'hero'|'side', coverAspect: '16/10'|'3/2'|'1/1'|'4/5', cta: buy-button label e.g. 'Reserve your hour' }"),
|
|
28
|
+
checkoutFields: z.array(z.object({ key: z.string(), label: z.string(), type: z.enum(["text", "textarea", "number", "email", "phone", "select"]), required: z.boolean().optional(), placeholder: z.string().optional(), options: z.array(z.string()).optional() })).optional().describe("Custom checkout questions the buyer answers (name, address, specs/customisations…). Their answers are attached to the order and sent to the seller."),
|
|
29
|
+
kind: z.string().nullable().optional().describe("Short label e.g. 'E-book', 'Presets', 'Service'"),
|
|
30
|
+
category: z.string().nullable().optional().describe("Category label e.g. 'Productivity'"),
|
|
31
|
+
codeMode: z.enum(["shared", "pool", "generated"]).optional().describe("For deliveryType 'code': 'shared' (same text to every buyer), 'pool' (a unique code per sale from codes you upload — sells out when empty), or 'generated' (auto-mint a unique license key per sale)"),
|
|
32
|
+
codes: z.array(z.string()).optional().describe("For codeMode 'pool': the unique codes/keys to add (one gets sent per buyer). Appended to any existing pool."),
|
|
33
|
+
feePayer: feePayerEnum.optional().describe("'buyer' (fee added on top, seller gets full price — default) or 'seller' (fee deducted)"),
|
|
34
|
+
enabled: z.boolean().optional().describe("Whether the product is live"),
|
|
35
|
+
membership: z.boolean().optional().describe("Make this a recurring subscription/membership: it tracks a paid period per buyer, renews from their Neuron wallet when possible (else emails a reminder — no card auto-charge), and marks them lapsed when the period + grace passes. React to membership.started/renewed/lapsed in Flows to deliver whatever the membership grants."),
|
|
36
|
+
membershipInterval: z.enum(["daily", "weekly", "monthly"]).nullable().optional().describe("Billing period. Required when membership is true."),
|
|
37
|
+
membershipGraceDays: z.number().int().min(0).optional().describe("Days after the period ends before an unpaid member lapses (and, if a group is attached, is removed from it). Default 1."),
|
|
38
|
+
membershipGroupSlug: z.string().nullable().optional().describe("OPTIONAL: attach a WhatsApp group as a membership perk. Org-unique handle (lowercase letters, numbers, hyphens); the group is created on the first sale, buyers are added on renewal and removed on lapse. Your connected number must be a group admin. Omit for a membership that isn't group-based."),
|
|
39
|
+
membershipGroupName: z.string().nullable().optional().describe("Group subject used when the attached group is auto-created (defaults to the product title). Only relevant with membershipGroupSlug."),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export function registerProductsTools(server: McpServer): void {
|
|
43
|
+
const ro = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
44
|
+
const write = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
45
|
+
|
|
46
|
+
server.registerTool(
|
|
47
|
+
"neuron_seller_status",
|
|
48
|
+
{
|
|
49
|
+
title: "Seller Status",
|
|
50
|
+
description: "Check whether this account can sell (Pro plan + admin-granted access) and any pending application.",
|
|
51
|
+
inputSchema: z.object({}),
|
|
52
|
+
annotations: ro,
|
|
53
|
+
},
|
|
54
|
+
async () => {
|
|
55
|
+
try { return jsonResult(await api("GET", "/sell/status")); } catch (e) { return errorResult(e); }
|
|
56
|
+
},
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
server.registerTool(
|
|
60
|
+
"neuron_apply_to_sell",
|
|
61
|
+
{
|
|
62
|
+
title: "Apply to Sell",
|
|
63
|
+
description: "Apply for seller access (digital products). Requires a Pro plan or higher; a platform admin then grants access.",
|
|
64
|
+
inputSchema: z.object({ pitch: z.string().optional().describe("Short note on what you plan to sell") }),
|
|
65
|
+
annotations: write,
|
|
66
|
+
},
|
|
67
|
+
async (p) => {
|
|
68
|
+
try { return jsonResult(await api("POST", "/sell/apply", p)); } catch (e) { return errorResult(e); }
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
server.registerTool(
|
|
73
|
+
"neuron_get_storefront",
|
|
74
|
+
{
|
|
75
|
+
title: "Get Storefront",
|
|
76
|
+
description: "Get this seller's storefront profile (handle, display name, bio, branding) and its public URL.",
|
|
77
|
+
inputSchema: z.object({}),
|
|
78
|
+
annotations: ro,
|
|
79
|
+
},
|
|
80
|
+
async () => {
|
|
81
|
+
try { return jsonResult(await api("GET", "/sell/store")); } catch (e) { return errorResult(e); }
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
server.registerTool(
|
|
86
|
+
"neuron_update_storefront",
|
|
87
|
+
{
|
|
88
|
+
title: "Update Storefront",
|
|
89
|
+
description: "Create or update the seller storefront (nestuge-style page). Handle is auto-generated from the display name if omitted.",
|
|
90
|
+
inputSchema: z.object({
|
|
91
|
+
handle: z.string().optional().describe("URL handle (a-z, 0-9, hyphens). Becomes /s/<handle>."),
|
|
92
|
+
displayName: z.string().optional().describe("Store display name"),
|
|
93
|
+
bio: z.string().nullable().optional().describe("Short store bio"),
|
|
94
|
+
avatarUrl: z.string().url().nullable().optional(),
|
|
95
|
+
brandColor: z.string().nullable().optional().describe("Hex brand colour e.g. #188A56"),
|
|
96
|
+
socials: z.record(z.string()).optional().describe("Map of social links e.g. { instagram: 'https://...' }"),
|
|
97
|
+
enabled: z.boolean().optional().describe("Whether the storefront is public"),
|
|
98
|
+
}),
|
|
99
|
+
annotations: write,
|
|
100
|
+
},
|
|
101
|
+
async (p) => {
|
|
102
|
+
try { return jsonResult(await api("PUT", "/sell/store", p)); } catch (e) { return errorResult(e); }
|
|
103
|
+
},
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
server.registerTool(
|
|
107
|
+
"neuron_list_products",
|
|
108
|
+
{
|
|
109
|
+
title: "List Products",
|
|
110
|
+
description: "List all products for this seller, each with its public checkout URL and price breakdown.",
|
|
111
|
+
inputSchema: z.object({}),
|
|
112
|
+
annotations: ro,
|
|
113
|
+
},
|
|
114
|
+
async () => {
|
|
115
|
+
try { return jsonResult(await api("GET", "/sell/products")); } catch (e) { return errorResult(e); }
|
|
116
|
+
},
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
server.registerTool(
|
|
120
|
+
"neuron_get_product",
|
|
121
|
+
{
|
|
122
|
+
title: "Get Product",
|
|
123
|
+
description: "Get a single product by id, with its public URL and fee breakdown.",
|
|
124
|
+
inputSchema: z.object({ id: z.string().uuid().describe("Product UUID") }),
|
|
125
|
+
annotations: ro,
|
|
126
|
+
},
|
|
127
|
+
async (p) => {
|
|
128
|
+
try { return jsonResult(await api("GET", `/sell/products/${p.id}`)); } catch (e) { return errorResult(e); }
|
|
129
|
+
},
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
server.registerTool(
|
|
133
|
+
"neuron_create_product",
|
|
134
|
+
{
|
|
135
|
+
title: "Create Product",
|
|
136
|
+
description: `Create a digital product/service with a public checkout link. ${PRICE_NOTE} For 'file' delivery, upload the file first (neuron_upload_media) and pass its URL as deliveryFileUrl. On payment, Neuron confirms it and delivers the product to the buyer's WhatsApp automatically.`,
|
|
137
|
+
inputSchema: z.object(productFields),
|
|
138
|
+
annotations: write,
|
|
139
|
+
},
|
|
140
|
+
async (p) => {
|
|
141
|
+
try { return jsonResult(await api("POST", "/sell/products", p)); } catch (e) { return errorResult(e); }
|
|
142
|
+
},
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
server.registerTool(
|
|
146
|
+
"neuron_update_product",
|
|
147
|
+
{
|
|
148
|
+
title: "Update Product",
|
|
149
|
+
description: "Update any field of a product (price, delivery, cover, enabled, fee payer, …).",
|
|
150
|
+
inputSchema: z.object({ id: z.string().uuid().describe("Product UUID"), ...Object.fromEntries(Object.entries(productFields).map(([k, v]) => [k, (v as z.ZodTypeAny).optional()])) }),
|
|
151
|
+
annotations: write,
|
|
152
|
+
},
|
|
153
|
+
async (p) => {
|
|
154
|
+
try { const { id, ...body } = p as { id: string } & Record<string, unknown>; return jsonResult(await api("PUT", `/sell/products/${id}`, body)); } catch (e) { return errorResult(e); }
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
server.registerTool(
|
|
159
|
+
"neuron_product_members",
|
|
160
|
+
{
|
|
161
|
+
title: "Product Members",
|
|
162
|
+
description: "List the members of a membership product: current (paid-up) or all. Returns each buyer's status (active / lapsed / cancelled), when their period renews, whether they're in the attached WhatsApp group (if one is attached), plus totals.",
|
|
163
|
+
inputSchema: z.object({
|
|
164
|
+
id: z.string().uuid().describe("Product UUID (must be a membership product)"),
|
|
165
|
+
status: z.enum(["active", "all"]).optional().describe("'active' (current members, default) or 'all' (every member ever)"),
|
|
166
|
+
}),
|
|
167
|
+
annotations: ro,
|
|
168
|
+
},
|
|
169
|
+
async (p) => {
|
|
170
|
+
try { return jsonResult(await api("GET", `/sell/products/${p.id}/members`, undefined, { status: p.status ?? "active" })); } catch (e) { return errorResult(e); }
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
server.registerTool(
|
|
175
|
+
"neuron_add_product_codes",
|
|
176
|
+
{
|
|
177
|
+
title: "Add Product Codes",
|
|
178
|
+
description: "Add more unique codes/keys to a 'pool' code product's stock. Each buyer receives one. Returns the product with the new remaining count.",
|
|
179
|
+
inputSchema: z.object({
|
|
180
|
+
id: z.string().uuid().describe("Product UUID"),
|
|
181
|
+
codes: z.array(z.string()).min(1).describe("Unique codes/keys to append to the pool"),
|
|
182
|
+
}),
|
|
183
|
+
annotations: write,
|
|
184
|
+
},
|
|
185
|
+
async (p) => {
|
|
186
|
+
try { return jsonResult(await api("PUT", `/sell/products/${p.id}`, { codes: p.codes })); } catch (e) { return errorResult(e); }
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
server.registerTool(
|
|
191
|
+
"neuron_duplicate_product",
|
|
192
|
+
{
|
|
193
|
+
title: "Duplicate Product",
|
|
194
|
+
description: "Clone an existing product into a hidden draft copy (new checkout link, '(copy)' title, reset counters). A 'pool' code product is cloned with an empty pool — restock codes before enabling. Edit the copy with neuron_update_product, then set enabled: true to publish.",
|
|
195
|
+
inputSchema: z.object({ id: z.string().uuid().describe("Product UUID to clone") }),
|
|
196
|
+
annotations: write,
|
|
197
|
+
},
|
|
198
|
+
async (p) => {
|
|
199
|
+
try { return jsonResult(await api("POST", `/sell/products/${p.id}/duplicate`, {})); } catch (e) { return errorResult(e); }
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
server.registerTool(
|
|
204
|
+
"neuron_delete_product",
|
|
205
|
+
{
|
|
206
|
+
title: "Delete Product",
|
|
207
|
+
description: "Permanently delete a product. Existing orders are retained.",
|
|
208
|
+
inputSchema: z.object({ id: z.string().uuid().describe("Product UUID") }),
|
|
209
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
210
|
+
},
|
|
211
|
+
async (p) => {
|
|
212
|
+
try { return jsonResult(await api("DELETE", `/sell/products/${p.id}`)); } catch (e) { return errorResult(e); }
|
|
213
|
+
},
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
server.registerTool(
|
|
217
|
+
"neuron_list_orders",
|
|
218
|
+
{
|
|
219
|
+
title: "List Orders",
|
|
220
|
+
description: "List recent product orders (sales) for this seller, with buyer, amount, fee breakdown, and status.",
|
|
221
|
+
inputSchema: z.object({}),
|
|
222
|
+
annotations: ro,
|
|
223
|
+
},
|
|
224
|
+
async () => {
|
|
225
|
+
try { return jsonResult(await api("GET", "/sell/orders")); } catch (e) { return errorResult(e); }
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
server.registerTool(
|
|
230
|
+
"neuron_refund_order",
|
|
231
|
+
{
|
|
232
|
+
title: "Refund Order",
|
|
233
|
+
description: "Refund a paid order via Paystack. The buyer is refunded and the sale amount is reversed from your wallet.",
|
|
234
|
+
inputSchema: z.object({ id: z.string().uuid().describe("Order UUID") }),
|
|
235
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false },
|
|
236
|
+
},
|
|
237
|
+
async (p) => {
|
|
238
|
+
try { return jsonResult(await api("POST", `/sell/orders/${p.id}/refund`)); } catch (e) { return errorResult(e); }
|
|
239
|
+
},
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
server.registerTool(
|
|
243
|
+
"neuron_deliver_order",
|
|
244
|
+
{
|
|
245
|
+
title: "Deliver Order",
|
|
246
|
+
description: "Re-send a paid-but-undelivered order to the buyer over WhatsApp/email. Use when a sale settled while no WhatsApp session was connected; connect one first, then deliver.",
|
|
247
|
+
inputSchema: z.object({ id: z.string().uuid().describe("Order UUID") }),
|
|
248
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
249
|
+
},
|
|
250
|
+
async (p) => {
|
|
251
|
+
try { return jsonResult(await api("POST", `/sell/orders/${p.id}/deliver`)); } catch (e) { return errorResult(e); }
|
|
252
|
+
},
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
server.registerTool(
|
|
256
|
+
"neuron_list_reviews",
|
|
257
|
+
{
|
|
258
|
+
title: "List Reviews",
|
|
259
|
+
description: "List verified-purchase reviews across this seller's products (rating, text, buyer, status).",
|
|
260
|
+
inputSchema: z.object({}),
|
|
261
|
+
annotations: ro,
|
|
262
|
+
},
|
|
263
|
+
async () => {
|
|
264
|
+
try { return jsonResult(await api("GET", "/sell/reviews")); } catch (e) { return errorResult(e); }
|
|
265
|
+
},
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
server.registerTool(
|
|
269
|
+
"neuron_sales_stats",
|
|
270
|
+
{
|
|
271
|
+
title: "Sales Stats",
|
|
272
|
+
description: "Total paid sales count and net revenue (kobo) for this seller.",
|
|
273
|
+
inputSchema: z.object({}),
|
|
274
|
+
annotations: ro,
|
|
275
|
+
},
|
|
276
|
+
async () => {
|
|
277
|
+
try { return jsonResult(await api("GET", "/sell/stats")); } catch (e) { return errorResult(e); }
|
|
278
|
+
},
|
|
279
|
+
);
|
|
280
|
+
}
|