@bettercms-ai/mcp 0.30.0 → 0.31.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/dist/index.js CHANGED
@@ -2485,6 +2485,10 @@ function buildToolDefs(deps) {
2485
2485
  command: layoutCommand.describe("one canonical discriminated Layout command; authority is derived from type"),
2486
2486
  ifMatch: z.number().int().nonnegative().describe("revision returned by get_layout")
2487
2487
  });
2488
+ const publishLayoutInput = z.object({
2489
+ projectId: z.string().min(1).optional().describe("required only for a workspace-scoped grant"),
2490
+ ifMatch: z.number().int().nonnegative().describe("revision returned by get_layout (scope global)")
2491
+ });
2488
2492
  const componentCategory = z.enum([
2489
2493
  "navbar",
2490
2494
  "footer",
@@ -2833,8 +2837,8 @@ function buildToolDefs(deps) {
2833
2837
  def(
2834
2838
  "update_project",
2835
2839
  "Update the connected project",
2836
- "Update the connected project's settings \u2014 rename it, change its slug/description, SEO defaults, or visibility. Only the provided fields change.",
2837
- z.object({ name: z.string().optional(), slug: z.string().optional(), description: z.string().optional(), visibility: z.string().optional(), seoDefaults: z.record(z.string(), z.unknown()).optional() }).shape,
2840
+ "Update the connected project's settings \u2014 rename it, change its slug/description, SEO defaults, visibility, or `presentation`. Only the provided fields change. `presentation` themes the platform's OWN renderer (the one serving a site built in the CMS with no deployed build, and previewing structural drafts everywhere) with the same closed knobs as bcms-presentation.json \u2014 DTCG { $type, $value } tokens keyed like 'page.maxWidth', 'nav.background'. Without it that renderer is a generic 768px document theme. An invalid manifest is 422 PRESENTATION_INVALID.",
2841
+ z.object({ name: z.string().optional(), slug: z.string().optional(), description: z.string().optional(), visibility: z.string().optional(), seoDefaults: z.record(z.string(), z.unknown()).optional(), presentation: z.record(z.string(), z.unknown()).optional().describe("theme knobs for the platform renderer, keyed like bcms-presentation.json") }).shape,
2838
2842
  async (c, a) => ok("Updated project.", await data(c, "PATCH", `/management/projects/current`, a))
2839
2843
  ),
2840
2844
  def(
@@ -3565,7 +3569,7 @@ function buildToolDefs(deps) {
3565
3569
  })
3566
3570
  )
3567
3571
  },
3568
- // ── Project/page Layout (draft-only; publish remains dashboard-gated) ──
3572
+ // ── Project/page Layout (draft commands + Global Layout publish; page overrides publish with the page) ──
3569
3573
  {
3570
3574
  name: "get_layout",
3571
3575
  config: {
@@ -3590,6 +3594,18 @@ function buildToolDefs(deps) {
3590
3594
  return ok(`Updated ${layout.scope === "global" ? "Global" : `Page ${layout.pageSlug}`} Layout draft to revision ${layout.revision}.`, layout);
3591
3595
  }))
3592
3596
  },
3597
+ {
3598
+ name: "publish_layout",
3599
+ config: {
3600
+ title: "Publish the Global Layout draft",
3601
+ description: "Publish the connected project's GLOBAL Layout draft (navigation, footer, every reserved section) so the live site builds from it. Until this is called the layout stays draft and get_layout copy:'published' answers PUBLISHED_LAYOUT_UNAVAILABLE \u2014 site chrome authored with update_layout is NOT live. Read get_layout first and pass its revision as ifMatch; a stale revision returns 409 \u2014 re-read, never retry blindly. A 422 lists validation issues to fix with update_layout first. Verify with get_layout copy:'published' and check the copy echo \u2014 this tool's own response is the write's echo, not a receipt. Page overrides go live with the page (update_page status:'published'). A 403 PUBLISH_NOT_GRANTED means this connection can author drafts but cannot publish \u2014 say so and let the user allow publishing or publish from the dashboard.",
3602
+ inputSchema: publishLayoutInput.shape
3603
+ },
3604
+ handler: guard(async (args) => withClient(async (client) => {
3605
+ const layout = await client.publishManagedLayout(args);
3606
+ return ok(`Published the Global Layout at revision ${layout.revision} (copy: published). Verify with get_layout copy:'published'.`, layout);
3607
+ }))
3608
+ },
3593
3609
  // ── Components (discover + author reusable symbols) ──
3594
3610
  {
3595
3611
  name: "list_components",
@@ -3788,9 +3804,11 @@ Decide before your first call; converting later means rewriting content.
3788
3804
 
3789
3805
  ## 3. Anatomy of a real section component
3790
3806
 
3791
- A component that is only headings and text renders as an unstyled stack. Every section the
3792
- product ships looks like this \u2014 a \`section\` block carrying \`style\`, with content nested in
3793
- \`props.children\`:
3807
+ A component that is only headings and text renders as an unstyled stack \u2014 plain text on white
3808
+ in a 768px column, on the live site AND in the visual editor. Every section the product ships
3809
+ looks like this \u2014 a \`section\` block carrying \`style\`, with content nested in
3810
+ \`props.children\`. \`create_component\` and \`update_component\` return a \`warnings\` entry when a
3811
+ Section's root section has no \`style\`; treat it as a defect, not a note:
3794
3812
 
3795
3813
  {
3796
3814
  type: "section", id: "root",
@@ -3845,8 +3863,8 @@ words (the page-builder model): each placement takes that group's current values
3845
3863
  kept and marked \`origin: "componentized"\` so the editor stops offering a second place to type
3846
3864
  them. Either way, running it again converts nothing and duplicates nothing. Then run \`npx @bettercms-ai/convert --componentize\` in the repo
3847
3865
  so its templates render those sections from \`pages[].blocks\`, and finish with the publishes:
3848
- \`publish_component\` every new component and publish the pages, or the site renders the
3849
- sections as empty strings.
3866
+ \`publish_component\` every new component, \`publish_layout\` if you touched site chrome (nav,
3867
+ footer), and publish the pages, or the site renders the sections as empty strings.
3850
3868
 
3851
3869
  ## 5. Blocks and modular fields
3852
3870
 
@@ -3911,6 +3929,7 @@ is interpolated per entry with \`{{fieldKey}}\` placeholders \u2014 \`{{title}}\
3911
3929
  5. pages create_page with blockJson placing those components
3912
3930
  6. content create_content_entry / set_page_content
3913
3931
  7. publish update_page status:'published'
3932
+ 7b. layout publish_layout <- if you authored the Global Layout (nav/footer); page overrides publish with the page
3914
3933
  8. check get_next_steps, and fix what it lists
3915
3934
 
3916
3935
  ## 8. Two ways to ship a blank site
@@ -3930,8 +3949,11 @@ in preview and is blank in production. Always pass the project's id.
3930
3949
  accepts it and ignores it**, silently. No error, no warning, wrong project.
3931
3950
 
3932
3951
  So call \`get_project\` (no arguments) first \u2014 it reports the project you are actually
3933
- writing to. If that is not where the work belongs, stop and tell the user: only they can
3934
- approve a grant for the other project, no tool can switch it.
3952
+ writing to. If that is not where the work belongs \u2014 or it answers that the grant covers the
3953
+ whole workspace \u2014 stop and tell the user the one-step fix: in the BetterCMS dashboard open
3954
+ **Settings \u2192 Connected AI clients** and use **Scope to project** on this connection. No tool
3955
+ can switch it, but the user does NOT need to re-authorize or re-add the server: the next call
3956
+ after that runs with the new scope. Then retry.
3935
3957
 
3936
3958
  If you must probe, probe with a \`create_content_model\` \u2014 models are deletable
3937
3959
  (\`delete_content_model\`, soft-delete) and **there is no \`delete_component\`**. A component
@@ -3957,6 +3979,7 @@ in this order:
3957
3979
 
3958
3980
  1. create_component per section (they land as DRAFTS)
3959
3981
  2. publish_component each one \u2014 unpublished renders as NOTHING, on a page that 200s
3982
+ 2b. publish_layout if you authored site chrome \u2014 the layout is a separate publish
3960
3983
  3. set_page_content place them as \`component\` blocks on the page
3961
3984
  4. list_extraction_candidates / extract_component
3962
3985
  now that blocks exist, fold any section repeated 3+ times
@@ -3983,6 +4006,13 @@ type scale, nav position and background, footer surface \u2014 as DTCG \`{"$type
3983
4006
  entries. Redeclare it on every deploy; absent means "declared nothing" and previews fall back
3984
4007
  to platform defaults that will not look like this site.
3985
4008
 
4009
+ **A site built in the CMS with NO deployed build has only that renderer** \u2014 live and in the
4010
+ editor \u2014 so the generic theme IS the site until you theme it. Theme it without a deploy:
4011
+ \`update_project { presentation: { "page.maxWidth": { "$type": "dimension", "$value": 1200 },
4012
+ "nav.background": { "$type": "color", "$value": "#0b0b0b" }, ... } }\` takes the same knobs as
4013
+ bcms-presentation.json, and set \`style\` on every Section's root (\xA73). A later deploy that
4014
+ ships bcms-presentation.json replaces it wholesale.
4015
+
3986
4016
  **Editing binds by VALUE.** The editor matches CMS field values against the text the site
3987
4017
  renders. Three consequences, each load-bearing:
3988
4018
 
@@ -3995,6 +4025,7 @@ renders. Three consequences, each load-bearing:
3995
4025
  c. set_page_content values EXACTLY equal to the text the site renders \u2014
3996
4026
  binding matches by value, so a paraphrase binds nothing
3997
4027
  d. update_page status 'published' \u2014 the canvas binds the PUBLISHED copy
4028
+ e. publish_layout if site chrome was authored \u2014 the canvas binds the PUBLISHED layout too
3998
4029
  2. A value that renders in more than one place is still ONE field: bind every element that
3999
4030
  renders it, and the editor keeps them in sync \u2014 an edit patches every copy at once.
4000
4031
  Binding only one copy leaves the others showing the old text until the next rebuild.
@@ -4050,7 +4081,8 @@ green on the wrong document. Three rules, none optional:
4050
4081
  the draft; a PUBLISH claim verifies ONLY via \`get_layout copy:'published'\` (or the
4051
4082
  entry/page's published copy) \u2014 and check the response's \`copy\` echo says
4052
4083
  'published'. A reader that ignores your copy selector hands you the draft and a
4053
- false green; the echo is how you catch it.
4084
+ false green; the echo is how you catch it. \`publish_layout\`'s own response echoes
4085
+ copy:'published' too \u2014 that is the WRITE's echo, not a receipt; still re-read.
4054
4086
  2. Publish and deploy are SEPARATE claims. "Published" means the published copy changed;
4055
4087
  the LIVE SITE changes only after its next deploy/rebuild. Never report "it's live"
4056
4088
  from a publish receipt \u2014 fetch the live URL (cache-busted) for that claim.
@@ -4287,9 +4319,10 @@ error-prone, so go slow and confirm. Never guess the layout.
4287
4319
  6. **\`publish_component\`.** It lands as a DRAFT, and a draft component renders as NOTHING on
4288
4320
  the live site \u2014 no error, no placeholder. This step is not optional.
4289
4321
  `;
4290
- var LAYOUT_FLOW = `### Project/page Layout authoring \u2192 \`get_layout\` / \`update_layout\`
4291
- Edit the project's draft Global Layout or one page's draft override. Layout publishing stays
4292
- in the dashboard; these tools never change the live site.
4322
+ var LAYOUT_FLOW = `### Project/page Layout authoring \u2192 \`get_layout\` / \`update_layout\` / \`publish_layout\`
4323
+ Edit the project's draft Global Layout or one page's draft override. \`update_layout\` never
4324
+ changes the live site; \`publish_layout\` takes the Global Layout draft live (page overrides go
4325
+ live with their page). Until it is called, site chrome you authored is NOT on the live site.
4293
4326
  1. **Read first** \u2014 call \`get_layout\` with scope \`global\`, or scope \`page\` + pageId.
4294
4327
  Keep the returned \`revision\`; every write must pass it as \`ifMatch\`.
4295
4328
  2. **Choose one discriminated command type** \u2014 the server derives its authority family;