webcake-landing-mcp 1.0.46 → 1.0.47
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
CHANGED
|
@@ -89,7 +89,7 @@ persists it (source-only — the page opens in the editor where re-saving render
|
|
|
89
89
|
| **npx (local)** — runs on your machine | Personal daily use, full control | browser `login`, a JWT, or none (reference tools) |
|
|
90
90
|
| **Hosted URL** — use our live server, nothing to install | No Node.js, teams, the claude.ai dialog | your personal `?jwt=` link / `x-webcake-jwt` header |
|
|
91
91
|
|
|
92
|
-
The **reference + generation tools** (`get_generation_guide`, `list_elements`, `validate_page`, …) and the **ingest tools** (`ingest_html`, `ingest_url` — turn an existing HTML or URL into a layout anchor so the AI can recreate or adapt it) work with **zero config**; only the **persistence tools** (`create_page`, `update_page`, `add_section`, `list_pages`, `find_pages`, `get_page`, `list_organizations`) need a token. Credentials resolve in order: **per-request header → env var → saved `auth.json`** (`login`).
|
|
92
|
+
The **reference + generation tools** (`get_generation_guide`, `list_elements`, `validate_page`, …) and the **ingest tools** (`ingest_html`, `ingest_url` — turn an existing HTML or URL into a layout anchor so the AI can recreate or adapt it) work with **zero config**; only the **persistence tools** (`create_page`, `update_page`, `add_section`, `patch_page`, `list_pages`, `find_pages`, `get_page`, `list_organizations`) need a token. Credentials resolve in order: **per-request header → env var → saved `auth.json`** (`login`).
|
|
93
93
|
|
|
94
94
|
> 🛠️ Prefer a shell-script installer (`install.sh`/`install.ps1`), a cloned local build, or hand-written per-IDE config? See **[docs/manual-install.md](docs/manual-install.md)**.
|
|
95
95
|
|
package/dist/changelog.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"v": "1.0.47",
|
|
4
|
+
"d": "10/06/2026",
|
|
5
|
+
"type": "Added",
|
|
6
|
+
"en": "New patch_page tool edits an existing page by element id without re-sending the whole source: the agent sends per-element ops (update, replace,…",
|
|
7
|
+
"vi": "Công cụ patch_page mới cho phép chỉnh sửa trang hiện có theo element id mà không cần gửi lại toàn bộ source: agent gửi các op theo element (update,…"
|
|
8
|
+
},
|
|
2
9
|
{
|
|
3
10
|
"v": "1.0.46",
|
|
4
11
|
"d": "09/06/2026",
|
|
@@ -33,12 +40,5 @@
|
|
|
33
40
|
"type": "Fixed",
|
|
34
41
|
"en": "validate_page now raises an error when a countdown element's specials.language is set to a value outside the eight supported word-values (vietnam,…",
|
|
35
42
|
"vi": "validate_page nay phát lỗi khi element countdown có specials.language là giá trị nằm ngoài tám word-value được hỗ trợ (vietnam, english, filipino,…"
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"v": "1.0.41",
|
|
39
|
-
"d": "09/06/2026",
|
|
40
|
-
"type": "Changed",
|
|
41
|
-
"en": "get_generation_guide and server instructions now require the agent to write all page copy in the same language the user is chatting in, with full,…",
|
|
42
|
-
"vi": "get_generation_guide và hướng dẫn server nay yêu cầu agent viết toàn bộ nội dung trang bằng cùng ngôn ngữ người dùng đang nhắn tin, với đầy đủ dấu…"
|
|
43
43
|
}
|
|
44
44
|
]
|
|
@@ -134,7 +134,9 @@ WORKFLOW (recommended)
|
|
|
134
134
|
6. To save: call list_organizations, show the orgs to the user and ask which to use (default to is_default). Then create_page (dry_run first, then dry_run:false with the chosen organization_id).
|
|
135
135
|
|
|
136
136
|
EDITING an existing page
|
|
137
|
-
- list_pages → let the user pick (or take a page_id from a URL).
|
|
137
|
+
- find_pages / list_pages → let the user pick (or take a page_id from a URL).
|
|
138
138
|
- get_page(page_id) → you get the live { page, popup, settings, ... }. Edit it surgically: change only the elements the user asked for (text/styles/specials/events); keep every other element, its id, and coordinates intact. Never regenerate the whole tree for a small change.
|
|
139
|
-
-
|
|
140
|
-
-
|
|
139
|
+
- SMALL edit → PREFER patch_page(page_id, patches): send ONLY the changed elements by id, not the whole source. Ops — {op:'update',id,specials?,styles?:{desktop?,mobile?},config?:{desktop?,mobile?},events?,properties?} (shallow-merge; op defaults to 'update'), {op:'replace',id,element}, {op:'remove',id}, {op:'add',parent_id,element}. The MCP fetches the live source, applies the ops, validates the whole tree, and saves. Reserve update_page(page_id, full source) for when you're rewriting most of the page.
|
|
140
|
+
- To add an element: give it a unique id + top/left/width/height, then patch_page({op:'add', parent_id:<section id>, element:<node>}).
|
|
141
|
+
- FIX-AFTER-ERROR: when create_page/update_page/add_section reports validation errors, fix ONLY the offending element ids with patch_page — do not rebuild and re-send the whole source. (If a create failed before any save, create_page a small valid skeleton first to get a page_id, then patch_page/add_section.)
|
|
142
|
+
- patch_page/update_page default to dry_run=true (preview); pass dry_run:false to save.`;
|
|
@@ -13,7 +13,8 @@ RULES (follow for every request):
|
|
|
13
13
|
- BUILD THE SOURCE IN ONE PASS — gather everything you need BEFORE assembling the source, then build the FULL tree once. BATCH the reads: when a section needs several element types (section + text-block + image-block + button + form + input), call get_element({types:[…]}) ONCE instead of one call per type — same for images, call search_images({queries:[…]}) ONCE with one query per image slot (it dedups + parallelizes and returns one best photo per query). Do NOT interleave get_element calls between create_page previews and rebuild. create_page/update_page take the entire source as input, so each call re-ships the whole page — re-previewing repeatedly wastes the request.
|
|
14
14
|
- create_page and update_page DEFAULT to dry_run=true (a safety net for ambiguous requests). When the user's intent is clear AND validate_page already passed (no errors), SKIP the dry-run and call with dry_run=false directly — saves one round-trip. Use dry_run=true only when (a) the request is ambiguous about target/content, (b) the user explicitly asks to "preview" or "xem trước", (c) this is an update_page that overwrites significant existing content, or (d) you genuinely need to inspect the redacted payload. Never loop dry-runs to "check" the source — validate_page is the validator. Do not run dry-run then dry-run again before the real write.
|
|
15
15
|
- LARGE PAGES (4+ sections) — build INCREMENTALLY to avoid the giant single create_page payload that can drop the connection: create_page with a SMALL skeleton (empty/near-empty page) to get a page_id, then call add_section once per section (each call ships ONLY that section; the backend appends it server-side and rejects duplicate ids — no whole-source get+put). Small pages can still go in one create_page pass.
|
|
16
|
-
- EDIT existing pages surgically: find_pages (locate the page by name/domain/id when you don't already have a page_id) → get_page → change ONLY what was asked → keep every other element, its id, and coordinates
|
|
16
|
+
- EDIT existing pages surgically: find_pages (locate the page by name/domain/id when you don't already have a page_id) → get_page → change ONLY what was asked → keep every other element, its id, and coordinates. For a SMALL edit, PREFER patch_page over update_page: send only the changed elements by id (ops: update/replace/remove/add) instead of re-shipping the whole tree — the MCP fetches the live source, merges, validates and saves server-side. Use update_page only when you're rewriting most of the page. Never regenerate the whole tree for a small change.
|
|
17
|
+
- FIX-AFTER-ERROR (don't rebuild): when create_page / update_page / add_section returns validation errors, the page already saved by then has a page_id — correct ONLY the offending element ids with patch_page (send just those ops), do NOT re-emit the whole source. That avoids re-shipping the large payload that drops the connection. (For a create that failed before any save: create_page a SMALL valid skeleton first to get a page_id, then patch_page / add_section to fill it in.)
|
|
17
18
|
- Organizations: call list_organizations and ask which to use; default to the is_default org. Endpoints are owner-scoped (only the account's own pages).
|
|
18
19
|
- REFERENCE INPUT — if the user provides a layout reference, USE it as the layout anchor (don't ignore it, don't re-invent from scratch). Three input modes: (1) IMAGE/screenshot attached in chat → analyze it natively (no tool call): identify section flow (hero/features/form/cta/footer), heading hierarchy, dominant colors, font feel, then map sections to Webcake elements. (2) HTML string → call ingest_html(html) to get a compact AST. (3) URL → call ingest_url(url) for the same AST. The AST classifies sections by role and lists headings/subheadings/ctas/images/form_fields plus brand hints (colors/fonts) — use it for LAYOUT + HIERARCHY, then generate FRESH content tailored to the user's brand (don't 1:1 copy text). intent='clone' only when the user explicitly asks to mirror the original; default intent='adapt'. The reference workflow PRESERVES craft rules above (centering, page margin, premium spacing, real images) — apply them on top of the reference layout, don't bypass them.
|
|
19
20
|
|
|
@@ -28,4 +29,4 @@ MODEL (essentials):
|
|
|
28
29
|
- Visible content lives in specials (text, src, field_name…), never in styles. Colors as rgba(). Animation in config.animation={name,delay,duration,repeat}. Form inputs need a unique specials.field_name (use canonical keys: full_name, phone_number, email, address, quantity).
|
|
29
30
|
- IMAGES: include them (hero/product, feature icons, about photo). PREFER REAL PHOTOS — call search_images with a short English subject (e.g. 'fresh coffee cup') and put a returned URL (src.large for a hero/banner, src.medium for a card/thumb) into the image-block specials.src; it works out of the box (a shared proxy supplies images). Only if search_images returns ok:false, FALL BACK to a PLACEHOLDER sized to the box: https://placehold.co/<width>x<height>. (gallery.media = array of OBJECTS {type:'image',link:'<url>',linkVideo:'',typeVideo:'youtube',imageCompression:true} — NOT plain strings, the gallery reads item.link; video.specials.img = poster). NEVER leave src empty (renders blank). Ensure text contrasts with its section background.
|
|
30
31
|
|
|
31
|
-
Start by calling get_generation_guide. Tools: get_generation_guide, list_elements, get_element, new_element, new_page_skeleton, get_page_schema, validate_page, search_images, ingest_html, ingest_url, list_organizations, create_page, list_pages, find_pages, get_page, update_page, add_section.`;
|
|
32
|
+
Start by calling get_generation_guide. Tools: get_generation_guide, list_elements, get_element, new_element, new_page_skeleton, get_page_schema, validate_page, search_images, ingest_html, ingest_url, list_organizations, create_page, list_pages, find_pages, get_page, update_page, add_section, patch_page.`;
|
|
@@ -366,4 +366,235 @@ export function registerPersistenceTools(server, domain) {
|
|
|
366
366
|
warnings: mergedResult.warnings,
|
|
367
367
|
});
|
|
368
368
|
});
|
|
369
|
+
// 14) Patch page (surgical element edit / fix-after-error) -------------------
|
|
370
|
+
// Why this exists: update_page takes the ENTIRE source as one tool argument, so
|
|
371
|
+
// fixing one bad element — or making a small edit — forces the model to re-emit
|
|
372
|
+
// the whole (often huge) page JSON, the same large payload that can drop the
|
|
373
|
+
// client↔Claude connection. patch_page lets the model send ONLY the diff: a list
|
|
374
|
+
// of per-element ops keyed by element id. The MCP fetches the live source, applies
|
|
375
|
+
// the ops, validates the WHOLE merged tree, and PUTs — the big merge lives on the
|
|
376
|
+
// robust MCP↔backend link, never in a tool argument the model has to stream.
|
|
377
|
+
//
|
|
378
|
+
// This is the fix-after-error path: when create_page/add_section/update_page
|
|
379
|
+
// returns validation errors, the model corrects ONLY the offending element ids via
|
|
380
|
+
// patch_page instead of rebuilding the source. It is also the everyday surgical-edit
|
|
381
|
+
// path (change one element's text/color/position without resending the tree).
|
|
382
|
+
//
|
|
383
|
+
// Ops (each keyed by element id, found anywhere in page or popup):
|
|
384
|
+
// { op:"update", id, specials?, styles?:{desktop?,mobile?}, config?:{desktop?,mobile?}, events?, properties? }
|
|
385
|
+
// — shallow-merge the given fields into the existing element (op defaults to "update").
|
|
386
|
+
// { op:"replace", id, element } — swap the whole node in place (compact authoring ok; keeps the id).
|
|
387
|
+
// { op:"remove", id } — delete the element and its subtree.
|
|
388
|
+
// { op:"add", parent_id, element } — append a new child element to the parent container.
|
|
389
|
+
// Unlike create_page's env-less preview, patch_page MUST read the live page, so it
|
|
390
|
+
// needs creds even on dry_run; dry_run only gates the final write.
|
|
391
|
+
const asArray = (input) => {
|
|
392
|
+
let v = input;
|
|
393
|
+
if (typeof v === "string") {
|
|
394
|
+
try {
|
|
395
|
+
v = JSON.parse(v);
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
/* not JSON — wrap as single */
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return Array.isArray(v) ? v : [v];
|
|
402
|
+
};
|
|
403
|
+
// Find a node by id within the real array refs (so remove/add mutate the live tree),
|
|
404
|
+
// recursing into children; returns the node, its parent array, and index.
|
|
405
|
+
const findById = (arr, id) => {
|
|
406
|
+
for (let i = 0; i < arr.length; i++) {
|
|
407
|
+
const n = arr[i];
|
|
408
|
+
if (!n || typeof n !== "object")
|
|
409
|
+
continue;
|
|
410
|
+
if (n.id === id)
|
|
411
|
+
return { node: n, parentArr: arr, index: i };
|
|
412
|
+
if (Array.isArray(n.children)) {
|
|
413
|
+
const found = findById(n.children, id);
|
|
414
|
+
if (found)
|
|
415
|
+
return found;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
};
|
|
420
|
+
const mergeStyleMap = (node, kind, byBp) => {
|
|
421
|
+
const touched = [];
|
|
422
|
+
for (const bp of ["desktop", "mobile"]) {
|
|
423
|
+
const patch = byBp?.[bp];
|
|
424
|
+
if (!patch || typeof patch !== "object")
|
|
425
|
+
continue;
|
|
426
|
+
node.responsive = node.responsive ?? {};
|
|
427
|
+
node.responsive[bp] = node.responsive[bp] ?? { config: {}, styles: {} };
|
|
428
|
+
node.responsive[bp][kind] = { ...(node.responsive[bp][kind] ?? {}), ...patch };
|
|
429
|
+
touched.push(`${bp}.${kind}`);
|
|
430
|
+
}
|
|
431
|
+
return touched;
|
|
432
|
+
};
|
|
433
|
+
server.tool("patch_page", "Edits an EXISTING page by element id WITHOUT re-sending the whole source — the surgical-edit and fix-after-error path. Send only a list of per-element ops; the MCP fetches the live source, applies them, validates the WHOLE merged tree (blocks on errors), and saves. Ops: {op:'update',id,specials?,styles?:{desktop?,mobile?},config?:{desktop?,mobile?},events?,properties?} (shallow-merges; op defaults to 'update'), {op:'replace',id,element}, {op:'remove',id}, {op:'add',parent_id,element}. Use this to fix the elements a failed create_page/update_page/add_section reported, or to change one element's text/color/position — instead of rebuilding the page. DEFAULTS to dry_run=true (reads + merges + validates + previews, no write); dry_run=false to save. Needs WEBCAKE_API_BASE + WEBCAKE_JWT (it reads the live page, so creds are required even on dry_run).", {
|
|
434
|
+
page_id: z.string().describe("The page id to edit (from create_page, list_pages, or find_pages; must be owned by the account)."),
|
|
435
|
+
patches: z
|
|
436
|
+
.any()
|
|
437
|
+
.describe("One op object or an array of them (object/array or JSON string). Each targets an element by id: {op:'update',id,specials?,styles?:{desktop?,mobile?},config?:{desktop?,mobile?},events?,properties?} merges fields into the element (op may be omitted); {op:'replace',id,element} swaps the node; {op:'remove',id} deletes it; {op:'add',parent_id,element} appends a child to a container."),
|
|
438
|
+
dry_run: z
|
|
439
|
+
.boolean()
|
|
440
|
+
.optional()
|
|
441
|
+
.describe("Default TRUE — read, merge, validate and preview the resulting save WITHOUT writing. Set false to actually save."),
|
|
442
|
+
}, { title: "Patch Webcake Page (by element id)", readOnlyHint: false, destructiveHint: true, openWorldHint: true }, async ({ page_id, patches, dry_run }, extra) => {
|
|
443
|
+
const isDry = dry_run !== false; // default true (safe)
|
|
444
|
+
const ops = asArray(patches).filter((p) => p != null && typeof p === "object");
|
|
445
|
+
if (ops.length === 0) {
|
|
446
|
+
return text({ patched: false, reason: "no_patches", hint: "Pass an op object or a non-empty array of { op, id, … } ops." });
|
|
447
|
+
}
|
|
448
|
+
// patch_page always reads the live tree (even on dry_run) — needs creds.
|
|
449
|
+
const { config, missing } = cfgFor(extra);
|
|
450
|
+
if (!config) {
|
|
451
|
+
return text({
|
|
452
|
+
patched: false,
|
|
453
|
+
reason: "missing_env",
|
|
454
|
+
missing_env: missing,
|
|
455
|
+
hint: "Configure WEBCAKE_API_BASE and WEBCAKE_JWT (env), or send the x-webcake-jwt header (remote), then retry.",
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
const current = await getPageSource(config, page_id);
|
|
459
|
+
if (!current.ok || current.source == null) {
|
|
460
|
+
return text({
|
|
461
|
+
patched: false,
|
|
462
|
+
reason: "fetch_failed",
|
|
463
|
+
status: current.status,
|
|
464
|
+
error: current.error ?? "Page source not found.",
|
|
465
|
+
hint: "Check the page_id (find_pages/list_pages) and that the account owns it.",
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
let base = current.source;
|
|
469
|
+
if (typeof base === "string") {
|
|
470
|
+
try {
|
|
471
|
+
base = JSON.parse(base);
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
return text({ patched: false, reason: "bad_source", hint: "The stored page source could not be parsed." });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const treeRoots = [base.page, base.popup].filter((a) => Array.isArray(a));
|
|
478
|
+
const locate = (id) => {
|
|
479
|
+
for (const r of treeRoots) {
|
|
480
|
+
const hit = findById(r, id);
|
|
481
|
+
if (hit)
|
|
482
|
+
return hit;
|
|
483
|
+
}
|
|
484
|
+
return null;
|
|
485
|
+
};
|
|
486
|
+
// Apply every op against the live tree. A missing target aborts the whole
|
|
487
|
+
// patch (we never write a partial edit).
|
|
488
|
+
const applied = [];
|
|
489
|
+
const notFound = [];
|
|
490
|
+
const badOps = [];
|
|
491
|
+
for (const p of ops) {
|
|
492
|
+
const op = p.op ?? "update";
|
|
493
|
+
if (op === "add") {
|
|
494
|
+
const pid = p.parent_id ?? p.id;
|
|
495
|
+
if (typeof pid !== "string" || p.element == null) {
|
|
496
|
+
badOps.push(`add needs parent_id + element`);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
const hit = locate(pid);
|
|
500
|
+
if (!hit) {
|
|
501
|
+
notFound.push({ op, id: pid });
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
hit.node.children = Array.isArray(hit.node.children) ? hit.node.children : [];
|
|
505
|
+
hit.node.children.push(p.element);
|
|
506
|
+
applied.push({ op, parent_id: pid, added_id: p.element?.id });
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
if (typeof p.id !== "string") {
|
|
510
|
+
badOps.push(`${op} needs a string id`);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
const hit = locate(p.id);
|
|
514
|
+
if (!hit) {
|
|
515
|
+
notFound.push({ op, id: p.id });
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (op === "remove") {
|
|
519
|
+
hit.parentArr.splice(hit.index, 1);
|
|
520
|
+
applied.push({ op, id: p.id });
|
|
521
|
+
}
|
|
522
|
+
else if (op === "replace") {
|
|
523
|
+
if (p.element == null) {
|
|
524
|
+
badOps.push(`replace ${p.id} needs element`);
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
const repl = p.element;
|
|
528
|
+
if (repl && typeof repl === "object" && repl.id == null)
|
|
529
|
+
repl.id = p.id;
|
|
530
|
+
hit.parentArr[hit.index] = repl;
|
|
531
|
+
applied.push({ op, id: p.id });
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
// update (default)
|
|
535
|
+
const changed = [];
|
|
536
|
+
if (p.specials && typeof p.specials === "object") {
|
|
537
|
+
hit.node.specials = { ...(hit.node.specials ?? {}), ...p.specials };
|
|
538
|
+
changed.push("specials");
|
|
539
|
+
}
|
|
540
|
+
changed.push(...mergeStyleMap(hit.node, "styles", p.styles));
|
|
541
|
+
changed.push(...mergeStyleMap(hit.node, "config", p.config));
|
|
542
|
+
if (Array.isArray(p.events)) {
|
|
543
|
+
hit.node.events = p.events;
|
|
544
|
+
changed.push("events");
|
|
545
|
+
}
|
|
546
|
+
if (p.properties && typeof p.properties === "object") {
|
|
547
|
+
hit.node.properties = { ...(hit.node.properties ?? {}), ...p.properties };
|
|
548
|
+
changed.push("properties");
|
|
549
|
+
}
|
|
550
|
+
applied.push({ op: "update", id: p.id, changed });
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
if (badOps.length > 0) {
|
|
554
|
+
return text({ patched: false, reason: "bad_ops", bad_ops: badOps, hint: "Each op needs id (or parent_id for add) and an element where required." });
|
|
555
|
+
}
|
|
556
|
+
if (notFound.length > 0) {
|
|
557
|
+
return text({
|
|
558
|
+
patched: false,
|
|
559
|
+
reason: "target_not_found",
|
|
560
|
+
not_found: notFound,
|
|
561
|
+
hint: "No element with that id exists on the live page. Run get_page to see the current ids; ids are case-sensitive.",
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
// Validate the WHOLE merged tree (hydrate sparse replaced/added nodes first).
|
|
565
|
+
const expanded = domain.expand(base);
|
|
566
|
+
const result = domain.validate(expanded);
|
|
567
|
+
if (!result.valid) {
|
|
568
|
+
return text({
|
|
569
|
+
patched: false,
|
|
570
|
+
reason: "validation_failed",
|
|
571
|
+
errors: result.errors,
|
|
572
|
+
warnings: result.warnings,
|
|
573
|
+
patches_applied: applied,
|
|
574
|
+
hint: "The edit produced an invalid tree — fix the listed errors in your ops, then retry.",
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
const parsed = domain.coerce(expanded);
|
|
578
|
+
if (isDry) {
|
|
579
|
+
return text({
|
|
580
|
+
dry_run: true,
|
|
581
|
+
page_id,
|
|
582
|
+
patches_applied: applied,
|
|
583
|
+
validation: { valid: true, warnings: result.warnings, stats: result.stats },
|
|
584
|
+
request: buildUpdateRequestRedacted(config, page_id, parsed),
|
|
585
|
+
hint: "Re-run with dry_run=false to actually save the edit.",
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
const outcome = await updatePageSource(config, page_id, parsed);
|
|
589
|
+
return text({
|
|
590
|
+
patched: outcome.ok,
|
|
591
|
+
patches_applied: applied,
|
|
592
|
+
page_id: outcome.page_id,
|
|
593
|
+
editor_url: outcome.editor_url,
|
|
594
|
+
preview_url: outcome.preview_url,
|
|
595
|
+
status: outcome.status,
|
|
596
|
+
error: outcome.error,
|
|
597
|
+
warnings: result.warnings,
|
|
598
|
+
});
|
|
599
|
+
});
|
|
369
600
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "webcake-landing-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.47",
|
|
4
4
|
"description": "MCP server exposing Webcake landing-page element schemas + AI usage hints, and persisting LLM-generated page sources to a Webcake backend.",
|
|
5
5
|
"mcpName": "io.github.vuluu2k/webcake-landing-mcp",
|
|
6
6
|
"type": "module",
|