@charisol/plexo-mcp 1.0.9 → 1.0.10
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/dist/client/plexoClient.js +193 -0
- package/dist/commerceTools.js +500 -0
- package/dist/index.js +62 -1
- package/dist/lmsTools.js +254 -0
- package/package.json +1 -1
- package/src/client/plexoClient.ts +225 -0
- package/src/commerceTools.ts +516 -0
- package/src/index.ts +62 -1
- package/src/lmsTools.ts +262 -0
package/src/lmsTools.ts
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { PlexoClient } from "./client/plexoClient.js";
|
|
2
|
+
|
|
3
|
+
export const LMS_TOOL_DEFS = [
|
|
4
|
+
{
|
|
5
|
+
name: "create_funnel",
|
|
6
|
+
description: "Attaches Funnel/LMS capability to an existing root landing page (see create_landing_page / list_landing_pages for how to get one), seeding a single Welcome page. Gated by the organization's funnel slot allowance (get_funnel_addon_slots shows current usage) — fails with the slot summary if none is available.",
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: { templateId: { type: "string", description: "An existing root landing page's template id to turn into a funnel." } },
|
|
10
|
+
required: ["templateId"],
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
name: "get_funnel",
|
|
15
|
+
description: "Fetches a funnel's settings: enabled state, signup mode (FREE/PAID), signup product, and welcome-page options.",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: "object",
|
|
18
|
+
properties: { templateId: { type: "string", description: "The funnel's home page template id." } },
|
|
19
|
+
required: ["templateId"],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "update_funnel",
|
|
24
|
+
description: "Updates a funnel's settings. Only the fields provided are changed. Switching signupMode to PAID requires signupProductId (a DIGITAL Commerce product) to already be set or provided here.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
29
|
+
enabled: { type: "boolean" },
|
|
30
|
+
signupMode: { type: "string", enum: ["FREE", "PAID"] },
|
|
31
|
+
signupProductId: { type: "string", description: "A DIGITAL Commerce product id (see create_commerce_product). Pass an empty string to clear it." },
|
|
32
|
+
requireName: { type: "boolean" },
|
|
33
|
+
collectPhone: { type: "boolean" },
|
|
34
|
+
welcomeRedirectPath: { type: "string", description: "Pass an empty string to clear it." },
|
|
35
|
+
},
|
|
36
|
+
required: ["templateId"],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: "delete_funnel",
|
|
41
|
+
description: "Soft-deletes a funnel (enabled:false — hidden from visitors). Pages, enrollments, and progress are kept, so re-enabling later restores everything as-is.",
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: { templateId: { type: "string", description: "The funnel's home page template id." } },
|
|
45
|
+
required: ["templateId"],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "list_funnel_pages",
|
|
50
|
+
description: "Lists a funnel's pages in their display order.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
type: "object",
|
|
53
|
+
properties: { templateId: { type: "string", description: "The funnel's home page template id." } },
|
|
54
|
+
required: ["templateId"],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "create_funnel_page",
|
|
59
|
+
description: "Appends a new page to a funnel. Defaults to kind VIDEO and unlocks after the current last page — use update_funnel_page to customize unlockRules afterward.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
64
|
+
title: { type: "string", description: "Defaults to \"Page N\"." },
|
|
65
|
+
kind: { type: "string", enum: ["WELCOME", "VIDEO", "DOCUMENT", "IMAGE"], description: "Defaults to VIDEO." },
|
|
66
|
+
},
|
|
67
|
+
required: ["templateId"],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "update_funnel_page",
|
|
72
|
+
description: "Updates a funnel page's content and/or unlock rules. Only the fields provided are changed. unlockRules shape: { mode: \"ALL\", conditions: [...] } where each condition is one of PREREQUISITE_PAGES ({type,pageIds}), TIME_DRIP_RELATIVE ({type,days}), TIME_DRIP_FIXED_DATE ({type,date}), MANUAL_APPROVAL ({type}) — satisfied via approve_funnel_enrollment_page.",
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
77
|
+
pageId: { type: "string" },
|
|
78
|
+
title: { type: "string" },
|
|
79
|
+
kind: { type: "string", enum: ["WELCOME", "VIDEO", "DOCUMENT", "IMAGE"] },
|
|
80
|
+
contentMode: { type: "string", enum: ["SIMPLE", "RAW_HTML", "ADVANCED"], description: "SIMPLE/RAW_HTML both write to bodyHtml; ADVANCED is a full-builder page and isn't settable from here." },
|
|
81
|
+
bodyHtml: { type: "string", description: "Sanitized server-side. For a WELCOME page this renders as plain text, not markup." },
|
|
82
|
+
youtubeVideoId: { type: "string", description: "Empty string clears it." },
|
|
83
|
+
assetUrl: { type: "string", description: "Already-uploaded file URL (this tool can't accept raw bytes). Empty string clears it." },
|
|
84
|
+
assetFileName: { type: "string" },
|
|
85
|
+
assetMimeType: { type: "string" },
|
|
86
|
+
assetSizeBytes: { type: "number" },
|
|
87
|
+
unlockRules: { type: "object", description: "See this tool's description for the shape." },
|
|
88
|
+
visible: { type: "boolean" },
|
|
89
|
+
},
|
|
90
|
+
required: ["templateId", "pageId"],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "delete_funnel_page",
|
|
95
|
+
description: "Deletes a funnel page and closes the resulting order gap in the remaining pages.",
|
|
96
|
+
inputSchema: {
|
|
97
|
+
type: "object",
|
|
98
|
+
properties: {
|
|
99
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
100
|
+
pageId: { type: "string" },
|
|
101
|
+
},
|
|
102
|
+
required: ["templateId", "pageId"],
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "reorder_funnel_pages",
|
|
107
|
+
description: "Reorders a funnel's pages. orderedPageIds must contain exactly this funnel's current page ids, in the new order.",
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: {
|
|
111
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
112
|
+
orderedPageIds: { type: "array", items: { type: "string" } },
|
|
113
|
+
},
|
|
114
|
+
required: ["templateId", "orderedPageIds"],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "list_funnel_enrollments",
|
|
119
|
+
description: "Lists a funnel's learner enrollments, most recently enrolled first.",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
type: "object",
|
|
122
|
+
properties: { templateId: { type: "string", description: "The funnel's home page template id." } },
|
|
123
|
+
required: ["templateId"],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: "get_funnel_enrollment",
|
|
128
|
+
description: "Fetches one learner's enrollment plus their per-page progress.",
|
|
129
|
+
inputSchema: {
|
|
130
|
+
type: "object",
|
|
131
|
+
properties: {
|
|
132
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
133
|
+
email: { type: "string" },
|
|
134
|
+
},
|
|
135
|
+
required: ["templateId", "email"],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: "approve_funnel_enrollment_page",
|
|
140
|
+
description: "Marks one page as manually approved for one learner — the way a MANUAL_APPROVAL unlock condition gets satisfied (see update_funnel_page's unlockRules description). Reversible: calling it again just re-stamps the approval.",
|
|
141
|
+
inputSchema: {
|
|
142
|
+
type: "object",
|
|
143
|
+
properties: {
|
|
144
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
145
|
+
email: { type: "string" },
|
|
146
|
+
pageId: { type: "string" },
|
|
147
|
+
},
|
|
148
|
+
required: ["templateId", "email", "pageId"],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "set_funnel_enrollment_status",
|
|
153
|
+
description: "Revokes or reactivates a learner's enrollment. Revoked takes effect immediately on their next page load (no session to separately invalidate).",
|
|
154
|
+
inputSchema: {
|
|
155
|
+
type: "object",
|
|
156
|
+
properties: {
|
|
157
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
158
|
+
email: { type: "string" },
|
|
159
|
+
status: { type: "string", enum: ["ACTIVE", "REVOKED"] },
|
|
160
|
+
},
|
|
161
|
+
required: ["templateId", "email", "status"],
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "resend_funnel_magic_link",
|
|
166
|
+
description: "Re-sends the passwordless login email to an ACTIVE learner. Rate-limited to once per 60 seconds per learner.",
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {
|
|
170
|
+
templateId: { type: "string", description: "The funnel's home page template id." },
|
|
171
|
+
email: { type: "string" },
|
|
172
|
+
},
|
|
173
|
+
required: ["templateId", "email"],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "get_funnel_addon_slots",
|
|
178
|
+
description: "Reports the organization's funnel slot usage (plan-included + purchased addon slots vs. funnels currently using one) and any active addon-slot subscriptions. Org-scoped, not per-site.",
|
|
179
|
+
inputSchema: { type: "object", properties: {} },
|
|
180
|
+
},
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
export const LMS_TOOL_NAMES = new Set(LMS_TOOL_DEFS.map((t) => t.name));
|
|
184
|
+
|
|
185
|
+
export async function handleLmsToolCall(name: string, args: any, client: PlexoClient): Promise<any> {
|
|
186
|
+
const templateId = typeof args?.templateId === "string" ? args.templateId : "";
|
|
187
|
+
|
|
188
|
+
switch (name) {
|
|
189
|
+
case "create_funnel": {
|
|
190
|
+
if (!templateId) throw new Error("templateId is required.");
|
|
191
|
+
const result = await client.createFunnel(templateId);
|
|
192
|
+
return { success: true, funnel: result.funnel };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
case "get_funnel":
|
|
196
|
+
return await client.getFunnel(templateId);
|
|
197
|
+
|
|
198
|
+
case "update_funnel": {
|
|
199
|
+
const { templateId: _t, ...payload } = args;
|
|
200
|
+
const result = await client.updateFunnel(templateId, payload);
|
|
201
|
+
return { success: true, funnel: result.funnel };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
case "delete_funnel":
|
|
205
|
+
await client.deleteFunnel(templateId);
|
|
206
|
+
return { success: true };
|
|
207
|
+
|
|
208
|
+
case "list_funnel_pages":
|
|
209
|
+
return await client.listFunnelPages(templateId);
|
|
210
|
+
|
|
211
|
+
case "create_funnel_page": {
|
|
212
|
+
const result = await client.createFunnelPage(templateId, { title: args.title, kind: args.kind });
|
|
213
|
+
return { success: true, page: result.page };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
case "update_funnel_page": {
|
|
217
|
+
if (!args.pageId) throw new Error("pageId is required.");
|
|
218
|
+
const { templateId: _t, pageId, ...payload } = args;
|
|
219
|
+
const result = await client.updateFunnelPage(templateId, pageId, payload);
|
|
220
|
+
return { success: true, page: result.page };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
case "delete_funnel_page":
|
|
224
|
+
if (!args.pageId) throw new Error("pageId is required.");
|
|
225
|
+
await client.deleteFunnelPage(templateId, args.pageId);
|
|
226
|
+
return { success: true, deletedPageId: args.pageId };
|
|
227
|
+
|
|
228
|
+
case "reorder_funnel_pages": {
|
|
229
|
+
if (!Array.isArray(args.orderedPageIds) || args.orderedPageIds.length === 0) throw new Error("orderedPageIds is required.");
|
|
230
|
+
return await client.reorderFunnelPages(templateId, args.orderedPageIds);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
case "list_funnel_enrollments":
|
|
234
|
+
return await client.listFunnelEnrollments(templateId);
|
|
235
|
+
|
|
236
|
+
case "get_funnel_enrollment":
|
|
237
|
+
if (!args.email) throw new Error("email is required.");
|
|
238
|
+
return await client.getFunnelEnrollment(templateId, args.email);
|
|
239
|
+
|
|
240
|
+
case "approve_funnel_enrollment_page": {
|
|
241
|
+
if (!args.email || !args.pageId) throw new Error("email and pageId are required.");
|
|
242
|
+
const result = await client.approveFunnelEnrollmentPage(templateId, args.email, args.pageId);
|
|
243
|
+
return { success: true, progress: result.progress };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
case "set_funnel_enrollment_status": {
|
|
247
|
+
if (!args.email || !args.status) throw new Error("email and status are required.");
|
|
248
|
+
const result = await client.setFunnelEnrollmentStatus(templateId, args.email, args.status);
|
|
249
|
+
return { success: true, enrollment: result.enrollment };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
case "resend_funnel_magic_link":
|
|
253
|
+
if (!args.email) throw new Error("email is required.");
|
|
254
|
+
return await client.resendFunnelMagicLink(templateId, args.email);
|
|
255
|
+
|
|
256
|
+
case "get_funnel_addon_slots":
|
|
257
|
+
return await client.getFunnelAddonSlots();
|
|
258
|
+
|
|
259
|
+
default:
|
|
260
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
261
|
+
}
|
|
262
|
+
}
|