@charisol/plexo-mcp 1.0.1 → 1.0.3
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 +3 -2
- package/dist/client/plexoClient.js +14 -0
- package/dist/config.js +1 -1
- package/dist/index.js +40 -1
- package/package.json +1 -1
- package/src/client/plexoClient.ts +22 -0
- package/src/config.ts +1 -1
- package/src/index.ts +41 -1
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ Connect **Claude Desktop**, **Claude Web**, **ChatGPT**, **Cursor**, **Windsurf*
|
|
|
10
10
|
|
|
11
11
|
- 🚀 **`publish_landing_page`**: Creates, compiles, and publishes a landing page (JSON schema + HTML) to a subdomain or custom domain in 1 step.
|
|
12
12
|
- ✉️ **`create_email_template`**: Creates and saves a responsive email template (same JSON schema as landing pages, no domain).
|
|
13
|
+
- ✏️ **`update_template`**: Edits an existing landing page or email template in place, under the same template ID.
|
|
13
14
|
- 📄 **`list_landing_pages`**: Retrieves saved landing page designs and active domain URLs.
|
|
14
15
|
- ✉️ **`list_email_templates`**: Retrieves saved email templates.
|
|
15
16
|
- 📊 **`get_analytics`**: Fetches total page views, unique visitor counts, and daily timelines.
|
|
@@ -32,7 +33,7 @@ Add to your `claude_desktop_config.json`:
|
|
|
32
33
|
"args": ["-y", "@charisol/plexo-mcp"],
|
|
33
34
|
"env": {
|
|
34
35
|
"PLEXO_API_KEY": "plexo_sk_your_key_here",
|
|
35
|
-
"PLEXO_BASE_URL": "https://
|
|
36
|
+
"PLEXO_BASE_URL": "https://plexo.charisol.io"
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
}
|
|
@@ -59,7 +60,7 @@ Add to `.cursor/mcp.json`:
|
|
|
59
60
|
|
|
60
61
|
### Option 3: Browser Authentication Flow
|
|
61
62
|
If `PLEXO_API_KEY` is omitted, the tool cleanly prompts:
|
|
62
|
-
`Please log into your Plexo account to authenticate: https://
|
|
63
|
+
`Please log into your Plexo account to authenticate: https://plexo.charisol.io/mcp/login`
|
|
63
64
|
|
|
64
65
|
---
|
|
65
66
|
|
|
@@ -57,6 +57,20 @@ class PlexoClient {
|
|
|
57
57
|
}
|
|
58
58
|
return data;
|
|
59
59
|
}
|
|
60
|
+
async updateTemplate(params) {
|
|
61
|
+
this.checkAuth();
|
|
62
|
+
const { templateId, ...body } = params;
|
|
63
|
+
const res = await fetch(`${this.baseUrl}/api/v1/templates/${encodeURIComponent(templateId)}`, {
|
|
64
|
+
method: "PUT",
|
|
65
|
+
headers: this.getHeaders(),
|
|
66
|
+
body: JSON.stringify(body),
|
|
67
|
+
});
|
|
68
|
+
const data = (await res.json());
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
throw new Error(data?.error || `Template update failed with status ${res.status}`);
|
|
71
|
+
}
|
|
72
|
+
return data;
|
|
73
|
+
}
|
|
60
74
|
async getUserProfile() {
|
|
61
75
|
this.checkAuth();
|
|
62
76
|
const res = await fetch(`${this.baseUrl}/api/v1/profile`, {
|
package/dist/config.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getConfig = getConfig;
|
|
4
4
|
function getConfig() {
|
|
5
|
-
const baseUrl = process.env.PLEXO_BASE_URL || "https://
|
|
5
|
+
const baseUrl = process.env.PLEXO_BASE_URL || "https://plexo.charisol.io";
|
|
6
6
|
const apiKey = process.env.PLEXO_API_KEY;
|
|
7
7
|
return {
|
|
8
8
|
baseUrl,
|
package/dist/index.js
CHANGED
|
@@ -187,6 +187,34 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
|
|
|
187
187
|
required: ["designJson"],
|
|
188
188
|
},
|
|
189
189
|
},
|
|
190
|
+
{
|
|
191
|
+
name: "update_template",
|
|
192
|
+
description: `Edits an existing landing page or email template in place, under the same template ID. Recompiles and re-saves the HTML from the new designJson — the editable URL stays the same, and for a published landing page the live URL and domain are unchanged too (this does not publish/unpublish or change the domain; call publish_landing_page again with a domain for that).
|
|
193
|
+
|
|
194
|
+
Uses the SAME fully-hydrated layout schema as publish_landing_page/create_email_template — designJson MUST be { "body": { "style": {...}, "rows": [{ "id", "style", "columns": [{ "id", "width", "elements": [{ "id", "type", "style", "attributes" }] }] }] } }. There is no shorthand row format; rows without a 'columns' array are rejected, and this replaces the ENTIRE designJson — to edit one section, fetch the current template's designJson first (via list_landing_pages/list_email_templates, or the Plexo dashboard) and send back the full modified tree, not just the changed part. See publish_landing_page's description for a full worked example.`,
|
|
195
|
+
inputSchema: {
|
|
196
|
+
type: "object",
|
|
197
|
+
properties: {
|
|
198
|
+
templateId: {
|
|
199
|
+
type: "string",
|
|
200
|
+
description: "ID of the template to update — the templateId returned by publish_landing_page, create_email_template, list_landing_pages, or list_email_templates.",
|
|
201
|
+
},
|
|
202
|
+
name: {
|
|
203
|
+
type: "string",
|
|
204
|
+
description: "Optional new title for the template. Omit to keep the existing name.",
|
|
205
|
+
},
|
|
206
|
+
designJson: {
|
|
207
|
+
type: "object",
|
|
208
|
+
description: "The FULL replacement Plexo layout schema (same shape as publish_landing_page's designJson) — not a partial patch.",
|
|
209
|
+
},
|
|
210
|
+
compiledHtml: {
|
|
211
|
+
type: "string",
|
|
212
|
+
description: "Ignored. HTML is always compiled server-side from the validated designJson (and sanitized).",
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
required: ["templateId", "designJson"],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
190
218
|
{
|
|
191
219
|
name: "list_landing_pages",
|
|
192
220
|
description: "Lists all saved landing page templates and published domain URLs in the user's Plexo account.",
|
|
@@ -232,7 +260,7 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
|
|
|
232
260
|
properties: {
|
|
233
261
|
domain: {
|
|
234
262
|
type: "string",
|
|
235
|
-
description: "Domain name to delete (e.g. 'acme.
|
|
263
|
+
description: "Domain name to delete (e.g. 'acme.plexo.charisol.io' or 'myacme.com')",
|
|
236
264
|
},
|
|
237
265
|
domainId: {
|
|
238
266
|
type: "string",
|
|
@@ -272,6 +300,17 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
272
300
|
],
|
|
273
301
|
};
|
|
274
302
|
}
|
|
303
|
+
case "update_template": {
|
|
304
|
+
const result = await client.updateTemplate(args);
|
|
305
|
+
return {
|
|
306
|
+
content: [
|
|
307
|
+
{
|
|
308
|
+
type: "text",
|
|
309
|
+
text: JSON.stringify(result, null, 2),
|
|
310
|
+
},
|
|
311
|
+
],
|
|
312
|
+
};
|
|
313
|
+
}
|
|
275
314
|
case "list_landing_pages": {
|
|
276
315
|
const data = await client.listTemplates();
|
|
277
316
|
const landingPages = (data.templates || []).filter((t) => t.kind === "LANDING_PAGE");
|
package/package.json
CHANGED
|
@@ -79,6 +79,28 @@ export class PlexoClient {
|
|
|
79
79
|
return data;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
async updateTemplate(params: {
|
|
83
|
+
templateId: string;
|
|
84
|
+
name?: string;
|
|
85
|
+
designJson: Record<string, any>;
|
|
86
|
+
compiledHtml?: string;
|
|
87
|
+
}): Promise<any> {
|
|
88
|
+
this.checkAuth();
|
|
89
|
+
const { templateId, ...body } = params;
|
|
90
|
+
const res = await fetch(`${this.baseUrl}/api/v1/templates/${encodeURIComponent(templateId)}`, {
|
|
91
|
+
method: "PUT",
|
|
92
|
+
headers: this.getHeaders(),
|
|
93
|
+
body: JSON.stringify(body),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const data = (await res.json()) as any;
|
|
97
|
+
if (!res.ok) {
|
|
98
|
+
throw new Error(data?.error || `Template update failed with status ${res.status}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return data;
|
|
102
|
+
}
|
|
103
|
+
|
|
82
104
|
async getUserProfile(): Promise<any> {
|
|
83
105
|
this.checkAuth();
|
|
84
106
|
const res = await fetch(`${this.baseUrl}/api/v1/profile`, {
|
package/src/config.ts
CHANGED
|
@@ -4,7 +4,7 @@ export interface PlexoConfig {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
export function getConfig(): PlexoConfig {
|
|
7
|
-
const baseUrl = process.env.PLEXO_BASE_URL || "https://
|
|
7
|
+
const baseUrl = process.env.PLEXO_BASE_URL || "https://plexo.charisol.io";
|
|
8
8
|
const apiKey = process.env.PLEXO_API_KEY;
|
|
9
9
|
|
|
10
10
|
return {
|
package/src/index.ts
CHANGED
|
@@ -194,6 +194,34 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
|
|
|
194
194
|
required: ["designJson"],
|
|
195
195
|
},
|
|
196
196
|
},
|
|
197
|
+
{
|
|
198
|
+
name: "update_template",
|
|
199
|
+
description: `Edits an existing landing page or email template in place, under the same template ID. Recompiles and re-saves the HTML from the new designJson — the editable URL stays the same, and for a published landing page the live URL and domain are unchanged too (this does not publish/unpublish or change the domain; call publish_landing_page again with a domain for that).
|
|
200
|
+
|
|
201
|
+
Uses the SAME fully-hydrated layout schema as publish_landing_page/create_email_template — designJson MUST be { "body": { "style": {...}, "rows": [{ "id", "style", "columns": [{ "id", "width", "elements": [{ "id", "type", "style", "attributes" }] }] }] } }. There is no shorthand row format; rows without a 'columns' array are rejected, and this replaces the ENTIRE designJson — to edit one section, fetch the current template's designJson first (via list_landing_pages/list_email_templates, or the Plexo dashboard) and send back the full modified tree, not just the changed part. See publish_landing_page's description for a full worked example.`,
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {
|
|
205
|
+
templateId: {
|
|
206
|
+
type: "string",
|
|
207
|
+
description: "ID of the template to update — the templateId returned by publish_landing_page, create_email_template, list_landing_pages, or list_email_templates.",
|
|
208
|
+
},
|
|
209
|
+
name: {
|
|
210
|
+
type: "string",
|
|
211
|
+
description: "Optional new title for the template. Omit to keep the existing name.",
|
|
212
|
+
},
|
|
213
|
+
designJson: {
|
|
214
|
+
type: "object",
|
|
215
|
+
description: "The FULL replacement Plexo layout schema (same shape as publish_landing_page's designJson) — not a partial patch.",
|
|
216
|
+
},
|
|
217
|
+
compiledHtml: {
|
|
218
|
+
type: "string",
|
|
219
|
+
description: "Ignored. HTML is always compiled server-side from the validated designJson (and sanitized).",
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
required: ["templateId", "designJson"],
|
|
223
|
+
},
|
|
224
|
+
},
|
|
197
225
|
{
|
|
198
226
|
name: "list_landing_pages",
|
|
199
227
|
description: "Lists all saved landing page templates and published domain URLs in the user's Plexo account.",
|
|
@@ -239,7 +267,7 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — designJso
|
|
|
239
267
|
properties: {
|
|
240
268
|
domain: {
|
|
241
269
|
type: "string",
|
|
242
|
-
description: "Domain name to delete (e.g. 'acme.
|
|
270
|
+
description: "Domain name to delete (e.g. 'acme.plexo.charisol.io' or 'myacme.com')",
|
|
243
271
|
},
|
|
244
272
|
domainId: {
|
|
245
273
|
type: "string",
|
|
@@ -283,6 +311,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
283
311
|
};
|
|
284
312
|
}
|
|
285
313
|
|
|
314
|
+
case "update_template": {
|
|
315
|
+
const result = await client.updateTemplate(args as any);
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
{
|
|
319
|
+
type: "text",
|
|
320
|
+
text: JSON.stringify(result, null, 2),
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
286
326
|
case "list_landing_pages": {
|
|
287
327
|
const data = await client.listTemplates();
|
|
288
328
|
const landingPages = (data.templates || []).filter(
|