@charisol/plexo-mcp 1.0.8 → 1.0.9
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 +73 -3
- package/dist/index.js +126 -2
- package/package.json +1 -1
- package/src/client/plexoClient.ts +88 -3
- package/src/index.ts +129 -2
|
@@ -147,15 +147,23 @@ class PlexoClient {
|
|
|
147
147
|
/**
|
|
148
148
|
* Creates a new page nested under an existing landing page (its home page
|
|
149
149
|
* for a top-level page, or another sub-page's id to nest deeper) — how a
|
|
150
|
-
* single landing page becomes a multi-page site. Starts blank
|
|
151
|
-
*
|
|
150
|
+
* single landing page becomes a multi-page site. Starts blank unless
|
|
151
|
+
* htmlContent is given, in which case it's created as a RAW_UPLOAD page
|
|
152
|
+
* instead — see updateTemplate() (BUILDER) / updateRawLandingPage()
|
|
153
|
+
* (RAW_UPLOAD) to add/edit content afterward, depending on which mode.
|
|
152
154
|
*/
|
|
153
155
|
async createLandingPageSubpage(params) {
|
|
154
156
|
this.checkAuth();
|
|
155
157
|
const res = await fetch(`${this.baseUrl}/api/templates`, {
|
|
156
158
|
method: "POST",
|
|
157
159
|
headers: this.getHeaders(),
|
|
158
|
-
body: JSON.stringify({
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
name: params.name,
|
|
162
|
+
parentId: params.parentTemplateId,
|
|
163
|
+
slug: params.slug,
|
|
164
|
+
htmlContent: params.htmlContent,
|
|
165
|
+
acceptAup: params.acceptAup,
|
|
166
|
+
}),
|
|
159
167
|
});
|
|
160
168
|
const data = (await res.json());
|
|
161
169
|
if (!res.ok) {
|
|
@@ -163,6 +171,68 @@ class PlexoClient {
|
|
|
163
171
|
}
|
|
164
172
|
return data;
|
|
165
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Creates a landing page from raw HTML supplied directly (no designJson),
|
|
176
|
+
* served byte-for-byte as given — a different storage mode from
|
|
177
|
+
* createLandingPage/publishLandingPage, which are for the drag-and-drop
|
|
178
|
+
* BUILDER pages built from designJson. Same endpoint upload-raw's REST
|
|
179
|
+
* multipart path uses, but with a JSON body — this client has no real
|
|
180
|
+
* file to upload.
|
|
181
|
+
*/
|
|
182
|
+
async createRawLandingPage(params) {
|
|
183
|
+
this.checkAuth();
|
|
184
|
+
const res = await fetch(`${this.baseUrl}/api/v1/templates/upload-raw`, {
|
|
185
|
+
method: "POST",
|
|
186
|
+
headers: this.getHeaders(),
|
|
187
|
+
body: JSON.stringify(params),
|
|
188
|
+
});
|
|
189
|
+
const data = (await res.json());
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
throw new Error(data?.error || `Raw landing page creation failed with status ${res.status}`);
|
|
192
|
+
}
|
|
193
|
+
return data;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Creates AND publishes a raw-HTML landing page in one call — the combined
|
|
197
|
+
* equivalent of createRawLandingPage() + publishExistingLandingPage().
|
|
198
|
+
*/
|
|
199
|
+
async publishRawLandingPage(params) {
|
|
200
|
+
const created = await this.createRawLandingPage({
|
|
201
|
+
name: params.name,
|
|
202
|
+
htmlContent: params.htmlContent,
|
|
203
|
+
acceptAup: params.acceptAup,
|
|
204
|
+
});
|
|
205
|
+
const templateId = created?.templateId;
|
|
206
|
+
if (!templateId) {
|
|
207
|
+
throw new Error(created?.error || "Raw landing page creation did not return a templateId.");
|
|
208
|
+
}
|
|
209
|
+
const published = await this.publishExistingLandingPage({
|
|
210
|
+
templateId,
|
|
211
|
+
domain: params.domain,
|
|
212
|
+
type: params.type,
|
|
213
|
+
});
|
|
214
|
+
return { ...created, ...published };
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Replaces an existing RAW_UPLOAD page's HTML in place, under the same
|
|
218
|
+
* template id — the editable URL, live URL, and domain (if published) are
|
|
219
|
+
* all unchanged. Same endpoint replace-upload's REST multipart path uses,
|
|
220
|
+
* but with a JSON body.
|
|
221
|
+
*/
|
|
222
|
+
async updateRawLandingPage(params) {
|
|
223
|
+
this.checkAuth();
|
|
224
|
+
const { templateId, ...body } = params;
|
|
225
|
+
const res = await fetch(`${this.baseUrl}/api/v1/templates/${encodeURIComponent(templateId)}/replace-upload`, {
|
|
226
|
+
method: "POST",
|
|
227
|
+
headers: this.getHeaders(),
|
|
228
|
+
body: JSON.stringify(body),
|
|
229
|
+
});
|
|
230
|
+
const data = (await res.json());
|
|
231
|
+
if (!res.ok) {
|
|
232
|
+
throw new Error(data?.error || `Raw landing page update failed with status ${res.status}`);
|
|
233
|
+
}
|
|
234
|
+
return data;
|
|
235
|
+
}
|
|
166
236
|
/**
|
|
167
237
|
* Returns every page belonging to the same multi-page site as templateId
|
|
168
238
|
* — its home page plus all nested sub-pages. templateId can be the home
|
package/dist/index.js
CHANGED
|
@@ -278,6 +278,8 @@ Map the returned tokens into designJson style fields: color-type tokens into bac
|
|
|
278
278
|
name: "update_template",
|
|
279
279
|
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).
|
|
280
280
|
|
|
281
|
+
This is for drag-and-drop BUILDER pages only — it REJECTS a page created via create_raw_landing_page/publish_raw_landing_page/create_landing_page_subpage's htmlContent option. Use update_raw_landing_page for those instead. If unsure which kind an existing page is, check its sourceType via list_landing_pages/get_landing_page_pages first.
|
|
282
|
+
|
|
281
283
|
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.`,
|
|
282
284
|
inputSchema: {
|
|
283
285
|
type: "object",
|
|
@@ -302,6 +304,30 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page/create_email_
|
|
|
302
304
|
required: ["templateId", "designJson"],
|
|
303
305
|
},
|
|
304
306
|
},
|
|
307
|
+
{
|
|
308
|
+
name: "update_raw_landing_page",
|
|
309
|
+
description: `Replaces an existing RAW_UPLOAD page's HTML in place, under the same template ID — the editable URL, live URL, and domain (if published) are all unchanged. This is for pages created via create_raw_landing_page/publish_raw_landing_page/create_landing_page_subpage's htmlContent option ONLY — it REJECTS a drag-and-drop BUILDER page (use update_template for those). If unsure which kind an existing page is, check its sourceType via list_landing_pages/get_landing_page_pages first.
|
|
310
|
+
|
|
311
|
+
This replaces the ENTIRE HTML document, not a patch — send back the full modified document. Same 1MB single-document scope as create_raw_landing_page.`,
|
|
312
|
+
inputSchema: {
|
|
313
|
+
type: "object",
|
|
314
|
+
properties: {
|
|
315
|
+
templateId: {
|
|
316
|
+
type: "string",
|
|
317
|
+
description: "ID of the raw-upload page to update — the templateId returned by create_raw_landing_page, publish_raw_landing_page, list_landing_pages, or get_landing_page_pages.",
|
|
318
|
+
},
|
|
319
|
+
htmlContent: {
|
|
320
|
+
type: "string",
|
|
321
|
+
description: "The FULL replacement raw HTML document (up to 1MB) — not a partial patch.",
|
|
322
|
+
},
|
|
323
|
+
name: {
|
|
324
|
+
type: "string",
|
|
325
|
+
description: "Optional new title for the page. Omit to keep the existing name.",
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
required: ["templateId", "htmlContent"],
|
|
329
|
+
},
|
|
330
|
+
},
|
|
305
331
|
{
|
|
306
332
|
name: "create_landing_page",
|
|
307
333
|
description: `Creates and saves a landing page template WITHOUT publishing it to a domain — use this when the user wants to build a page (or start a multi-page site) first and go live later. Call publish_existing_landing_page afterward with the returned templateId to link a subdomain or custom domain whenever they're ready. To create a page AND publish it in one step, use publish_landing_page instead.
|
|
@@ -322,6 +348,63 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — see its d
|
|
|
322
348
|
required: ["designJson"],
|
|
323
349
|
},
|
|
324
350
|
},
|
|
351
|
+
{
|
|
352
|
+
name: "create_raw_landing_page",
|
|
353
|
+
description: `Creates a landing page from a RAW HTML document you supply directly, WITHOUT publishing it to a domain — for when the user wants a page built from hand-written or externally-sourced HTML/CSS/JS instead of Plexo's drag-and-drop builder. The page is served byte-for-byte as given, completely unsanitized (no designJson, no compilation) — this is a DIFFERENT storage mode from create_landing_page/publish_landing_page/update_template, which are for the drag-and-drop BUILDER pages built from designJson. Do NOT use this tool for a page you want to build with designJson — use create_landing_page instead.
|
|
354
|
+
|
|
355
|
+
Scope: a single self-contained HTML document only (inline or CDN-linked CSS/JS), up to 1MB — no zip/multi-file sites with separate local asset files (that's a dashboard-only "import a website" feature with no MCP equivalent yet).
|
|
356
|
+
|
|
357
|
+
Because this serves unsanitized content, the account must accept the Acceptable Use Policy once — see the acceptAup argument. Call publish_existing_landing_page afterward with the returned templateId to link a subdomain or custom domain, or use publish_raw_landing_page to create and publish in one step. To edit this page's HTML later, use update_raw_landing_page (NOT update_template — that tool rejects raw-upload pages).`,
|
|
358
|
+
inputSchema: {
|
|
359
|
+
type: "object",
|
|
360
|
+
properties: {
|
|
361
|
+
name: {
|
|
362
|
+
type: "string",
|
|
363
|
+
description: "Title of the landing page (e.g. 'Kicks Store').",
|
|
364
|
+
},
|
|
365
|
+
htmlContent: {
|
|
366
|
+
type: "string",
|
|
367
|
+
description: "The full, self-contained raw HTML document to serve as-is (up to 1MB). Inline or CDN-linked CSS/JS only — no separate local asset files.",
|
|
368
|
+
},
|
|
369
|
+
acceptAup: {
|
|
370
|
+
type: "boolean",
|
|
371
|
+
description: "Pass true ONLY after explicitly telling the user this publishes unsanitized HTML/CSS/JS as-is and getting their confirmation of the Acceptable Use Policy (/legal/acceptable-use). Not needed once the account has accepted it once before.",
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
required: ["htmlContent"],
|
|
375
|
+
},
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
name: "publish_raw_landing_page",
|
|
379
|
+
description: `Creates, and publishes to a domain, a landing page from a RAW HTML document you supply directly — the combined create+publish equivalent of create_raw_landing_page. See create_raw_landing_page's description for the unsanitized-content, AUP, and scope caveats (single self-contained HTML document, up to 1MB, no zip/multi-file sites). For drag-and-drop BUILDER pages built from designJson, use publish_landing_page instead — this tool is a different storage mode.`,
|
|
380
|
+
inputSchema: {
|
|
381
|
+
type: "object",
|
|
382
|
+
properties: {
|
|
383
|
+
name: {
|
|
384
|
+
type: "string",
|
|
385
|
+
description: "Title of the landing page (e.g. 'Kicks Store').",
|
|
386
|
+
},
|
|
387
|
+
htmlContent: {
|
|
388
|
+
type: "string",
|
|
389
|
+
description: "The full, self-contained raw HTML document to serve as-is (up to 1MB). Inline or CDN-linked CSS/JS only — no separate local asset files.",
|
|
390
|
+
},
|
|
391
|
+
acceptAup: {
|
|
392
|
+
type: "boolean",
|
|
393
|
+
description: "Pass true ONLY after explicitly telling the user this publishes unsanitized HTML/CSS/JS as-is and getting their confirmation of the Acceptable Use Policy (/legal/acceptable-use). Not needed once the account has accepted it once before.",
|
|
394
|
+
},
|
|
395
|
+
domain: {
|
|
396
|
+
type: "string",
|
|
397
|
+
description: "Subdomain slug (e.g. 'kicks') or custom domain ('kicks.com').",
|
|
398
|
+
},
|
|
399
|
+
type: {
|
|
400
|
+
type: "string",
|
|
401
|
+
enum: ["SUBDOMAIN", "CUSTOM"],
|
|
402
|
+
description: "Domain routing type. Inferred from the domain string if omitted.",
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
required: ["htmlContent", "domain"],
|
|
406
|
+
},
|
|
407
|
+
},
|
|
325
408
|
{
|
|
326
409
|
name: "publish_existing_landing_page",
|
|
327
410
|
description: `Links a subdomain or custom domain to an already-existing landing page template — for a template created via create_landing_page, or the home page of a multi-page site (see create_landing_page_subpage) once its sub-pages are ready. Unlike publish_landing_page, this does NOT create a new template — it publishes the one you already have. Every sub-page nested under templateId automatically becomes reachable at this domain too (e.g. domain.com/about) — no separate publish call is needed per sub-page.`,
|
|
@@ -347,9 +430,9 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — see its d
|
|
|
347
430
|
},
|
|
348
431
|
{
|
|
349
432
|
name: "create_landing_page_subpage",
|
|
350
|
-
description: `Adds a new page nested under an existing landing page, reachable at <site domain>/<slug> — or nested deeper (e.g. /blog/post-1) if parentTemplateId is itself a sub-page rather than the home page. This is how a single landing page becomes a multi-page site. No separate publish step is needed for sub-pages: they automatically become reachable once the site's home page has a domain linked (see publish_existing_landing_page). Requires an Ultra subscription plan — sub-pages are otherwise unlimited per site once enabled.
|
|
433
|
+
description: `Adds a new page nested under an existing landing page, reachable at <site domain>/<slug> — or nested deeper (e.g. /blog/post-1) if parentTemplateId is itself a sub-page rather than the home page. This is how a single landing page becomes a multi-page site (BUILDER and RAW_UPLOAD pages can be freely mixed within the same site — each page's own sourceType is independent of its siblings). No separate publish step is needed for sub-pages: they automatically become reachable once the site's home page has a domain linked (see publish_existing_landing_page). Requires an Ultra subscription plan — sub-pages are otherwise unlimited per site once enabled.
|
|
351
434
|
|
|
352
|
-
|
|
435
|
+
Provide htmlContent for a raw-HTML sub-page (same unsanitized/AUP/1MB caveats as create_raw_landing_page). Omit it to create a blank BUILDER page — fill it in afterward with update_template using the returned templateId. Edit an existing sub-page's content afterward with update_template (BUILDER) or update_raw_landing_page (RAW_UPLOAD) depending on which mode it was created with.`,
|
|
353
436
|
inputSchema: {
|
|
354
437
|
type: "object",
|
|
355
438
|
properties: {
|
|
@@ -365,6 +448,14 @@ The new page starts blank — use update_template with the returned templateId t
|
|
|
365
448
|
type: "string",
|
|
366
449
|
description: "Optional URL segment (e.g. 'about-us'). Auto-generated from name if omitted; auto-suffixed (about-us-2, ...) if it collides with a sibling page under the same parent.",
|
|
367
450
|
},
|
|
451
|
+
htmlContent: {
|
|
452
|
+
type: "string",
|
|
453
|
+
description: "Optional. A full, self-contained raw HTML document (up to 1MB) to serve as-is. Creates a RAW_UPLOAD sub-page instead of a blank BUILDER one.",
|
|
454
|
+
},
|
|
455
|
+
acceptAup: {
|
|
456
|
+
type: "boolean",
|
|
457
|
+
description: "Only relevant when htmlContent is given. Pass true ONLY after explicitly telling the user this publishes unsanitized HTML/CSS/JS as-is and getting their confirmation of the Acceptable Use Policy (/legal/acceptable-use). Not needed once the account has accepted it once before.",
|
|
458
|
+
},
|
|
368
459
|
},
|
|
369
460
|
required: ["parentTemplateId", "name"],
|
|
370
461
|
},
|
|
@@ -562,6 +653,39 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
562
653
|
],
|
|
563
654
|
};
|
|
564
655
|
}
|
|
656
|
+
case "create_raw_landing_page": {
|
|
657
|
+
const result = await client.createRawLandingPage(args);
|
|
658
|
+
return {
|
|
659
|
+
content: [
|
|
660
|
+
{
|
|
661
|
+
type: "text",
|
|
662
|
+
text: JSON.stringify(result, null, 2),
|
|
663
|
+
},
|
|
664
|
+
],
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
case "publish_raw_landing_page": {
|
|
668
|
+
const result = await client.publishRawLandingPage(args);
|
|
669
|
+
return {
|
|
670
|
+
content: [
|
|
671
|
+
{
|
|
672
|
+
type: "text",
|
|
673
|
+
text: JSON.stringify(result, null, 2),
|
|
674
|
+
},
|
|
675
|
+
],
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
case "update_raw_landing_page": {
|
|
679
|
+
const result = await client.updateRawLandingPage(args);
|
|
680
|
+
return {
|
|
681
|
+
content: [
|
|
682
|
+
{
|
|
683
|
+
type: "text",
|
|
684
|
+
text: JSON.stringify(result, null, 2),
|
|
685
|
+
},
|
|
686
|
+
],
|
|
687
|
+
};
|
|
688
|
+
}
|
|
565
689
|
case "publish_existing_landing_page": {
|
|
566
690
|
const result = await client.publishExistingLandingPage(args);
|
|
567
691
|
return {
|
package/package.json
CHANGED
|
@@ -196,19 +196,29 @@ export class PlexoClient {
|
|
|
196
196
|
/**
|
|
197
197
|
* Creates a new page nested under an existing landing page (its home page
|
|
198
198
|
* for a top-level page, or another sub-page's id to nest deeper) — how a
|
|
199
|
-
* single landing page becomes a multi-page site. Starts blank
|
|
200
|
-
*
|
|
199
|
+
* single landing page becomes a multi-page site. Starts blank unless
|
|
200
|
+
* htmlContent is given, in which case it's created as a RAW_UPLOAD page
|
|
201
|
+
* instead — see updateTemplate() (BUILDER) / updateRawLandingPage()
|
|
202
|
+
* (RAW_UPLOAD) to add/edit content afterward, depending on which mode.
|
|
201
203
|
*/
|
|
202
204
|
async createLandingPageSubpage(params: {
|
|
203
205
|
parentTemplateId: string;
|
|
204
206
|
name: string;
|
|
205
207
|
slug?: string;
|
|
208
|
+
htmlContent?: string;
|
|
209
|
+
acceptAup?: boolean;
|
|
206
210
|
}): Promise<any> {
|
|
207
211
|
this.checkAuth();
|
|
208
212
|
const res = await fetch(`${this.baseUrl}/api/templates`, {
|
|
209
213
|
method: "POST",
|
|
210
214
|
headers: this.getHeaders(),
|
|
211
|
-
body: JSON.stringify({
|
|
215
|
+
body: JSON.stringify({
|
|
216
|
+
name: params.name,
|
|
217
|
+
parentId: params.parentTemplateId,
|
|
218
|
+
slug: params.slug,
|
|
219
|
+
htmlContent: params.htmlContent,
|
|
220
|
+
acceptAup: params.acceptAup,
|
|
221
|
+
}),
|
|
212
222
|
});
|
|
213
223
|
|
|
214
224
|
const data = (await res.json()) as any;
|
|
@@ -219,6 +229,81 @@ export class PlexoClient {
|
|
|
219
229
|
return data;
|
|
220
230
|
}
|
|
221
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Creates a landing page from raw HTML supplied directly (no designJson),
|
|
234
|
+
* served byte-for-byte as given — a different storage mode from
|
|
235
|
+
* createLandingPage/publishLandingPage, which are for the drag-and-drop
|
|
236
|
+
* BUILDER pages built from designJson. Same endpoint upload-raw's REST
|
|
237
|
+
* multipart path uses, but with a JSON body — this client has no real
|
|
238
|
+
* file to upload.
|
|
239
|
+
*/
|
|
240
|
+
async createRawLandingPage(params: { name?: string; htmlContent: string; acceptAup?: boolean }): Promise<any> {
|
|
241
|
+
this.checkAuth();
|
|
242
|
+
const res = await fetch(`${this.baseUrl}/api/v1/templates/upload-raw`, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
headers: this.getHeaders(),
|
|
245
|
+
body: JSON.stringify(params),
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const data = (await res.json()) as any;
|
|
249
|
+
if (!res.ok) {
|
|
250
|
+
throw new Error(data?.error || `Raw landing page creation failed with status ${res.status}`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return data;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Creates AND publishes a raw-HTML landing page in one call — the combined
|
|
258
|
+
* equivalent of createRawLandingPage() + publishExistingLandingPage().
|
|
259
|
+
*/
|
|
260
|
+
async publishRawLandingPage(params: {
|
|
261
|
+
name?: string;
|
|
262
|
+
htmlContent: string;
|
|
263
|
+
acceptAup?: boolean;
|
|
264
|
+
domain: string;
|
|
265
|
+
type?: "SUBDOMAIN" | "CUSTOM";
|
|
266
|
+
}): Promise<any> {
|
|
267
|
+
const created = await this.createRawLandingPage({
|
|
268
|
+
name: params.name,
|
|
269
|
+
htmlContent: params.htmlContent,
|
|
270
|
+
acceptAup: params.acceptAup,
|
|
271
|
+
});
|
|
272
|
+
const templateId = created?.templateId;
|
|
273
|
+
if (!templateId) {
|
|
274
|
+
throw new Error(created?.error || "Raw landing page creation did not return a templateId.");
|
|
275
|
+
}
|
|
276
|
+
const published = await this.publishExistingLandingPage({
|
|
277
|
+
templateId,
|
|
278
|
+
domain: params.domain,
|
|
279
|
+
type: params.type,
|
|
280
|
+
});
|
|
281
|
+
return { ...created, ...published };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Replaces an existing RAW_UPLOAD page's HTML in place, under the same
|
|
286
|
+
* template id — the editable URL, live URL, and domain (if published) are
|
|
287
|
+
* all unchanged. Same endpoint replace-upload's REST multipart path uses,
|
|
288
|
+
* but with a JSON body.
|
|
289
|
+
*/
|
|
290
|
+
async updateRawLandingPage(params: { templateId: string; htmlContent: string; name?: string }): Promise<any> {
|
|
291
|
+
this.checkAuth();
|
|
292
|
+
const { templateId, ...body } = params;
|
|
293
|
+
const res = await fetch(`${this.baseUrl}/api/v1/templates/${encodeURIComponent(templateId)}/replace-upload`, {
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers: this.getHeaders(),
|
|
296
|
+
body: JSON.stringify(body),
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const data = (await res.json()) as any;
|
|
300
|
+
if (!res.ok) {
|
|
301
|
+
throw new Error(data?.error || `Raw landing page update failed with status ${res.status}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return data;
|
|
305
|
+
}
|
|
306
|
+
|
|
222
307
|
/**
|
|
223
308
|
* Returns every page belonging to the same multi-page site as templateId
|
|
224
309
|
* — its home page plus all nested sub-pages. templateId can be the home
|
package/src/index.ts
CHANGED
|
@@ -285,6 +285,8 @@ Map the returned tokens into designJson style fields: color-type tokens into bac
|
|
|
285
285
|
name: "update_template",
|
|
286
286
|
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).
|
|
287
287
|
|
|
288
|
+
This is for drag-and-drop BUILDER pages only — it REJECTS a page created via create_raw_landing_page/publish_raw_landing_page/create_landing_page_subpage's htmlContent option. Use update_raw_landing_page for those instead. If unsure which kind an existing page is, check its sourceType via list_landing_pages/get_landing_page_pages first.
|
|
289
|
+
|
|
288
290
|
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.`,
|
|
289
291
|
inputSchema: {
|
|
290
292
|
type: "object",
|
|
@@ -309,6 +311,30 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page/create_email_
|
|
|
309
311
|
required: ["templateId", "designJson"],
|
|
310
312
|
},
|
|
311
313
|
},
|
|
314
|
+
{
|
|
315
|
+
name: "update_raw_landing_page",
|
|
316
|
+
description: `Replaces an existing RAW_UPLOAD page's HTML in place, under the same template ID — the editable URL, live URL, and domain (if published) are all unchanged. This is for pages created via create_raw_landing_page/publish_raw_landing_page/create_landing_page_subpage's htmlContent option ONLY — it REJECTS a drag-and-drop BUILDER page (use update_template for those). If unsure which kind an existing page is, check its sourceType via list_landing_pages/get_landing_page_pages first.
|
|
317
|
+
|
|
318
|
+
This replaces the ENTIRE HTML document, not a patch — send back the full modified document. Same 1MB single-document scope as create_raw_landing_page.`,
|
|
319
|
+
inputSchema: {
|
|
320
|
+
type: "object",
|
|
321
|
+
properties: {
|
|
322
|
+
templateId: {
|
|
323
|
+
type: "string",
|
|
324
|
+
description: "ID of the raw-upload page to update — the templateId returned by create_raw_landing_page, publish_raw_landing_page, list_landing_pages, or get_landing_page_pages.",
|
|
325
|
+
},
|
|
326
|
+
htmlContent: {
|
|
327
|
+
type: "string",
|
|
328
|
+
description: "The FULL replacement raw HTML document (up to 1MB) — not a partial patch.",
|
|
329
|
+
},
|
|
330
|
+
name: {
|
|
331
|
+
type: "string",
|
|
332
|
+
description: "Optional new title for the page. Omit to keep the existing name.",
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
required: ["templateId", "htmlContent"],
|
|
336
|
+
},
|
|
337
|
+
},
|
|
312
338
|
{
|
|
313
339
|
name: "create_landing_page",
|
|
314
340
|
description: `Creates and saves a landing page template WITHOUT publishing it to a domain — use this when the user wants to build a page (or start a multi-page site) first and go live later. Call publish_existing_landing_page afterward with the returned templateId to link a subdomain or custom domain whenever they're ready. To create a page AND publish it in one step, use publish_landing_page instead.
|
|
@@ -329,6 +355,63 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — see its d
|
|
|
329
355
|
required: ["designJson"],
|
|
330
356
|
},
|
|
331
357
|
},
|
|
358
|
+
{
|
|
359
|
+
name: "create_raw_landing_page",
|
|
360
|
+
description: `Creates a landing page from a RAW HTML document you supply directly, WITHOUT publishing it to a domain — for when the user wants a page built from hand-written or externally-sourced HTML/CSS/JS instead of Plexo's drag-and-drop builder. The page is served byte-for-byte as given, completely unsanitized (no designJson, no compilation) — this is a DIFFERENT storage mode from create_landing_page/publish_landing_page/update_template, which are for the drag-and-drop BUILDER pages built from designJson. Do NOT use this tool for a page you want to build with designJson — use create_landing_page instead.
|
|
361
|
+
|
|
362
|
+
Scope: a single self-contained HTML document only (inline or CDN-linked CSS/JS), up to 1MB — no zip/multi-file sites with separate local asset files (that's a dashboard-only "import a website" feature with no MCP equivalent yet).
|
|
363
|
+
|
|
364
|
+
Because this serves unsanitized content, the account must accept the Acceptable Use Policy once — see the acceptAup argument. Call publish_existing_landing_page afterward with the returned templateId to link a subdomain or custom domain, or use publish_raw_landing_page to create and publish in one step. To edit this page's HTML later, use update_raw_landing_page (NOT update_template — that tool rejects raw-upload pages).`,
|
|
365
|
+
inputSchema: {
|
|
366
|
+
type: "object",
|
|
367
|
+
properties: {
|
|
368
|
+
name: {
|
|
369
|
+
type: "string",
|
|
370
|
+
description: "Title of the landing page (e.g. 'Kicks Store').",
|
|
371
|
+
},
|
|
372
|
+
htmlContent: {
|
|
373
|
+
type: "string",
|
|
374
|
+
description: "The full, self-contained raw HTML document to serve as-is (up to 1MB). Inline or CDN-linked CSS/JS only — no separate local asset files.",
|
|
375
|
+
},
|
|
376
|
+
acceptAup: {
|
|
377
|
+
type: "boolean",
|
|
378
|
+
description: "Pass true ONLY after explicitly telling the user this publishes unsanitized HTML/CSS/JS as-is and getting their confirmation of the Acceptable Use Policy (/legal/acceptable-use). Not needed once the account has accepted it once before.",
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
required: ["htmlContent"],
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
name: "publish_raw_landing_page",
|
|
386
|
+
description: `Creates, and publishes to a domain, a landing page from a RAW HTML document you supply directly — the combined create+publish equivalent of create_raw_landing_page. See create_raw_landing_page's description for the unsanitized-content, AUP, and scope caveats (single self-contained HTML document, up to 1MB, no zip/multi-file sites). For drag-and-drop BUILDER pages built from designJson, use publish_landing_page instead — this tool is a different storage mode.`,
|
|
387
|
+
inputSchema: {
|
|
388
|
+
type: "object",
|
|
389
|
+
properties: {
|
|
390
|
+
name: {
|
|
391
|
+
type: "string",
|
|
392
|
+
description: "Title of the landing page (e.g. 'Kicks Store').",
|
|
393
|
+
},
|
|
394
|
+
htmlContent: {
|
|
395
|
+
type: "string",
|
|
396
|
+
description: "The full, self-contained raw HTML document to serve as-is (up to 1MB). Inline or CDN-linked CSS/JS only — no separate local asset files.",
|
|
397
|
+
},
|
|
398
|
+
acceptAup: {
|
|
399
|
+
type: "boolean",
|
|
400
|
+
description: "Pass true ONLY after explicitly telling the user this publishes unsanitized HTML/CSS/JS as-is and getting their confirmation of the Acceptable Use Policy (/legal/acceptable-use). Not needed once the account has accepted it once before.",
|
|
401
|
+
},
|
|
402
|
+
domain: {
|
|
403
|
+
type: "string",
|
|
404
|
+
description: "Subdomain slug (e.g. 'kicks') or custom domain ('kicks.com').",
|
|
405
|
+
},
|
|
406
|
+
type: {
|
|
407
|
+
type: "string",
|
|
408
|
+
enum: ["SUBDOMAIN", "CUSTOM"],
|
|
409
|
+
description: "Domain routing type. Inferred from the domain string if omitted.",
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
required: ["htmlContent", "domain"],
|
|
413
|
+
},
|
|
414
|
+
},
|
|
332
415
|
{
|
|
333
416
|
name: "publish_existing_landing_page",
|
|
334
417
|
description: `Links a subdomain or custom domain to an already-existing landing page template — for a template created via create_landing_page, or the home page of a multi-page site (see create_landing_page_subpage) once its sub-pages are ready. Unlike publish_landing_page, this does NOT create a new template — it publishes the one you already have. Every sub-page nested under templateId automatically becomes reachable at this domain too (e.g. domain.com/about) — no separate publish call is needed per sub-page.`,
|
|
@@ -354,9 +437,9 @@ Uses the SAME fully-hydrated layout schema as publish_landing_page — see its d
|
|
|
354
437
|
},
|
|
355
438
|
{
|
|
356
439
|
name: "create_landing_page_subpage",
|
|
357
|
-
description: `Adds a new page nested under an existing landing page, reachable at <site domain>/<slug> — or nested deeper (e.g. /blog/post-1) if parentTemplateId is itself a sub-page rather than the home page. This is how a single landing page becomes a multi-page site. No separate publish step is needed for sub-pages: they automatically become reachable once the site's home page has a domain linked (see publish_existing_landing_page). Requires an Ultra subscription plan — sub-pages are otherwise unlimited per site once enabled.
|
|
440
|
+
description: `Adds a new page nested under an existing landing page, reachable at <site domain>/<slug> — or nested deeper (e.g. /blog/post-1) if parentTemplateId is itself a sub-page rather than the home page. This is how a single landing page becomes a multi-page site (BUILDER and RAW_UPLOAD pages can be freely mixed within the same site — each page's own sourceType is independent of its siblings). No separate publish step is needed for sub-pages: they automatically become reachable once the site's home page has a domain linked (see publish_existing_landing_page). Requires an Ultra subscription plan — sub-pages are otherwise unlimited per site once enabled.
|
|
358
441
|
|
|
359
|
-
|
|
442
|
+
Provide htmlContent for a raw-HTML sub-page (same unsanitized/AUP/1MB caveats as create_raw_landing_page). Omit it to create a blank BUILDER page — fill it in afterward with update_template using the returned templateId. Edit an existing sub-page's content afterward with update_template (BUILDER) or update_raw_landing_page (RAW_UPLOAD) depending on which mode it was created with.`,
|
|
360
443
|
inputSchema: {
|
|
361
444
|
type: "object",
|
|
362
445
|
properties: {
|
|
@@ -372,6 +455,14 @@ The new page starts blank — use update_template with the returned templateId t
|
|
|
372
455
|
type: "string",
|
|
373
456
|
description: "Optional URL segment (e.g. 'about-us'). Auto-generated from name if omitted; auto-suffixed (about-us-2, ...) if it collides with a sibling page under the same parent.",
|
|
374
457
|
},
|
|
458
|
+
htmlContent: {
|
|
459
|
+
type: "string",
|
|
460
|
+
description: "Optional. A full, self-contained raw HTML document (up to 1MB) to serve as-is. Creates a RAW_UPLOAD sub-page instead of a blank BUILDER one.",
|
|
461
|
+
},
|
|
462
|
+
acceptAup: {
|
|
463
|
+
type: "boolean",
|
|
464
|
+
description: "Only relevant when htmlContent is given. Pass true ONLY after explicitly telling the user this publishes unsanitized HTML/CSS/JS as-is and getting their confirmation of the Acceptable Use Policy (/legal/acceptable-use). Not needed once the account has accepted it once before.",
|
|
465
|
+
},
|
|
375
466
|
},
|
|
376
467
|
required: ["parentTemplateId", "name"],
|
|
377
468
|
},
|
|
@@ -576,6 +667,42 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
576
667
|
};
|
|
577
668
|
}
|
|
578
669
|
|
|
670
|
+
case "create_raw_landing_page": {
|
|
671
|
+
const result = await client.createRawLandingPage(args as any);
|
|
672
|
+
return {
|
|
673
|
+
content: [
|
|
674
|
+
{
|
|
675
|
+
type: "text",
|
|
676
|
+
text: JSON.stringify(result, null, 2),
|
|
677
|
+
},
|
|
678
|
+
],
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
case "publish_raw_landing_page": {
|
|
683
|
+
const result = await client.publishRawLandingPage(args as any);
|
|
684
|
+
return {
|
|
685
|
+
content: [
|
|
686
|
+
{
|
|
687
|
+
type: "text",
|
|
688
|
+
text: JSON.stringify(result, null, 2),
|
|
689
|
+
},
|
|
690
|
+
],
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
case "update_raw_landing_page": {
|
|
695
|
+
const result = await client.updateRawLandingPage(args as any);
|
|
696
|
+
return {
|
|
697
|
+
content: [
|
|
698
|
+
{
|
|
699
|
+
type: "text",
|
|
700
|
+
text: JSON.stringify(result, null, 2),
|
|
701
|
+
},
|
|
702
|
+
],
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
|
|
579
706
|
case "publish_existing_landing_page": {
|
|
580
707
|
const result = await client.publishExistingLandingPage(args as any);
|
|
581
708
|
return {
|