@bettercms-ai/mcp 0.73.1 → 0.74.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.
@@ -15,6 +15,7 @@ that only show up in code. Read doctrine from the MCP, never from memory:
15
15
 
16
16
  - Schema, sections, variants, publish order: resource `bettercms://playbook/schema` (§1–§14).
17
17
  - Content sidebar organisation: resource `bettercms://playbook/structure`.
18
+ - Section patterns (149 worked kit entries by category and edge tag): resource `bettercms://kit`.
18
19
  - Guided flows: the `studio` prompt routes to the rest (`convert_site` prompt,
19
20
  `build_site` prompt, `componentize_site` prompt, `propose_schema` prompt, `new_page` prompt,
20
21
  `new_component` prompt, `new_form` prompt, `seo_optimize` prompt, `import-site` prompt,
@@ -127,11 +128,13 @@ resources: `bettercms://skill/bettercms` for this file, and each reference under
127
128
  | What the server does and does not check on a write | [write-limits.md](references/write-limits.md) | before relying on a limit; reading a refused write; a bulk load or re-run | validation, maxLength, unique, 400, refused write, dry run, externalKey, upsert, re-run |
128
129
  | Plan a site by business type | [site-blueprints.md](references/site-blueprints.md) | the IA of a known site type, with its collections, forms and structured data | new site, IA, portfolio, agency, corporate, SaaS, ecommerce, directory, blog, docs, restaurant, events, course, jobs, real estate, nonprofit, multi-brand |
129
130
  | Components, props, variants, component publish codes | [components.md](references/components.md) | creating, swapping, publishing or validating a component | component, prop, variant, sectionType, publish component |
130
- | One section, source HTML to bound template: hero, feature grid, pricing table, testimonials, FAQ | [worked-examples.md](references/worked-examples.md) | you want the whole path for one section in production shapes | example, hero, pricing, FAQ, testimonial, source to schema |
131
+ | Which kit pattern fits a section, and its shape and pitfalls, by category and edge tag | [kit-scenarios.md](references/kit-scenarios.md) | before designing any section's fields or props; a band detection.md classified | kit, pattern, scenario, edge tag, events, locations, calculator, map, chart, docs, team, bento, slots, personalization, localized |
132
+ | One section, source HTML to bound template: hero, feature grid, pricing table, testimonials, FAQ, bento | [worked-examples.md](references/worked-examples.md) | you want the whole path for one section in production shapes | example, hero, pricing, FAQ, testimonial, source to schema |
131
133
  | The same for an article entry, navigation in the Global Layout, tabs holding columns | [worked-examples-advanced.md](references/worked-examples-advanced.md) | a collection entry with a body, site chrome, or nested blocks | example, blog post, document body, mega menu, tabs, columns |
134
+ | The modelled section closest to the one you are building: schema, HTML signals, mcp steps, pitfalls | [kit-catalog.md](references/kit-catalog.md) | before designing a section or its props; matching source HTML to a known pattern | kit, catalogue, edge tag, section pattern, bento, localized, personalization, a11y, responsive |
132
135
  | Componentize a derived site | [componentize.md](references/componentize.md) | `get_site_composition` shows pages made of fields only | componentize, sections, fields-only, get_componentize_plan, BcmsSections, --sections |
133
136
  | Click-to-edit attributes, addresses, declared mode | [bindings.md](references/bindings.md) | `grep -rl "data-bcms-" src` finds files; a value the editor cannot select | data-bcms-field, click to edit, binding report, stega, declared |
134
- | Animated sites: GSAP, ScrollTrigger, split text, Framer Motion, Lenis, marquees, carousels, Lottie, hydrated islands | [animation.md](references/animation.md) | `package.json` lists `gsap`, `lenis`, `framer-motion`, `swiper` or a text splitter; a publish refused with `SPLIT_PATTERN_UNKNOWN` or `ISLAND_PROPS_UNMATCHED`; a `client-only-island` gate exception | animation, GSAP, ScrollTrigger, SplitText, Lenis, Framer Motion, marquee, Swiper, Lottie, island, client:only, data-bcms-split |
137
+ | Animated sites: GSAP, ScrollTrigger, split text, Motion, Lenis, Locomotive, three.js and WebGL, marquees, carousels, Lottie, hydrated islands, and what must stand down in the editor's canvas (`inEditor()`) | [animation.md](references/animation.md) | `package.json` lists `gsap`, `lenis`, `locomotive-scroll`, `framer-motion` or `motion`, `three`, `swiper` or a text splitter; a publish refused with `SPLIT_PATTERN_UNKNOWN` or `ISLAND_PROPS_UNMATCHED`; a `client-only-island` gate exception | animation, GSAP, ScrollTrigger, SplitText, Lenis, Framer Motion, marquee, Swiper, Lottie, island, client:only, data-bcms-split |
135
138
  | Wiring, env, hosting modes, media host, snapshots | [frameworks.md](references/frameworks.md) | `package.json` lists an adapter; `bcms-content.json` exists | hosting, headless, env, codegen, types, revalidate, snapshot |
136
139
  | Astro adapter | [astro.md](references/astro.md) | the repo has `astro.config.*` | astro.config, Astro, BcmsBlocks, getStaticPaths |
137
140
  | Next.js adapter | [next.md](references/next.md) | the repo has `next.config.*` | next.config, Next.js, App Router, ISR, notFound, static export |
@@ -5,8 +5,9 @@ description: "Keeping GSAP, ScrollTrigger, SplitText, Framer Motion, Lenis, marq
5
5
 
6
6
  # Animated sites: editable copy that keeps moving
7
7
 
8
- Read this when `package.json` lists `gsap`, `lenis`, `framer-motion` or `motion`, `swiper`,
9
- `split-type`, `splitting`, `lottie-web` or `three`, or when the built HTML carries
8
+ Read this when `package.json` lists `gsap`, `lenis`, `locomotive-scroll`, `framer-motion` or
9
+ `motion`, `swiper`, `split-type`, `splitting`, `lottie-web`, `three` or `@react-three/fiber`, or
10
+ when the built HTML carries
10
11
  `astro-island`, split-text spans or a duplicated marquee track. Attribute grammar:
11
12
  [bindings.md](bindings.md). Adapter wiring: [frameworks.md](frameworks.md).
12
13
 
@@ -150,6 +151,13 @@ window.addEventListener("message", (event) => {
150
151
  and any panel of yours that must scroll by itself inside the page takes `data-lenis-prevent`.
151
152
  Never bind the element Lenis is given as `wrapper` or `content`.
152
153
 
154
+ In the Visual Editor the canvas owns the scroll: it marks the page's `<body>` with
155
+ `data-lenis-prevent`, so the wheel there is the browser's own and has no easing. That is the
156
+ editor, not a fault in the site: Lenis still reports every scroll (ScrollTrigger and anything
157
+ else hung on `lenis.on("scroll")` runs), its own `scrollTo` still eases, and the published page
158
+ is untouched. Do not remove the attribute or "repair" the easing for the canvas. What other
159
+ scroll libraries need there is in section 12.
160
+
153
161
  Check: after an edit that adds two lines to a paragraph, the page still scrolls to the footer.
154
162
 
155
163
  ## 7. Marquees and duplicated tracks
@@ -248,4 +256,41 @@ editable is what you pass in:
248
256
  Give the canvas `role="img"` and an `aria-label` bound to a field, or an adjacent caption. A
249
257
  scene with no text a visitor reads needs no field; record it under the `decorative` exception.
250
258
 
259
+ A canvas drawn over the page (a fixed full-screen scene behind or above the copy) does not block
260
+ the editor: a click looks through an element that is no field and no control to the field under
261
+ it. Keep the copy in the HTML above or below the canvas, never only inside the scene.
262
+
251
263
  Check: change the bound string, publish, reload: the drawn text reads the new value.
264
+
265
+ ## 12. In the editor's canvas
266
+
267
+ The Visual Editor shows the real page with its scripts running, and owns three things there: the
268
+ scroll (its canvas scrolls and the page's window follows), the pointer (a click selects a field)
269
+ and the place on the page (picking a field in the panel scrolls to it). Motion that reads the
270
+ window's scroll or the clock runs there as it does live. Only what takes one of those three away
271
+ has to stand down, and the site decides that with one question:
272
+
273
+ ```js
274
+ import { inEditor } from "@bettercms-ai/sdk"; // 1.31+. Without the SDK: window.name === "bcms-editor"
275
+
276
+ const smooth = inEditor() ? null : new LocomotiveScroll({ el, smooth: true });
277
+ ```
278
+
279
+ `inEditor()` is true from the page's first script and through every navigation inside the
280
+ canvas, and false on the published page. What each kind of page does there:
281
+
282
+ | The page uses | In the canvas | You write |
283
+ |---|---|---|
284
+ | Native scroll, CSS scroll-driven animations (`animation-timeline`), Motion `useScroll` / `whileInView`, ScrollTrigger pin and scrub | runs as live | nothing |
285
+ | Lenis, Locomotive Scroll 5 | runs; the wheel has no easing (section 6) | nothing |
286
+ | GSAP ScrollSmoother | runs; the window still scrolls, the content eases behind it | nothing |
287
+ | Locomotive Scroll 4, any script that locks the window and moves the content by a transform | the page works under its own wheel, but the canvas cannot scroll it and a field picked in the panel is not brought into view | `smooth: !inEditor()`, so the window scrolls |
288
+ | A wheel-driven slide deck (GSAP `Observer`, fullPage) | the same | do not create the observer when `inEditor()`; let the sections stack and scroll |
289
+ | A scene with its own scroll layer (react-three-fiber `ScrollControls`, a fixed `overflow: auto` layer) | runs; picking a field scrolls that layer | nothing |
290
+ | An intro or preloader that holds the page (`overflow: hidden` until it ends, a full-screen cover) | plays on every reload of the canvas, and covers the page while it does | skip it when `inEditor()` |
291
+
292
+ Never use `inEditor()` to change what a field renders, to hide a section or to stop a timeline:
293
+ the canvas must show what a visitor gets. It is for scroll takeover, intros and nothing else.
294
+
295
+ Check: open the page in the Visual Editor, scroll with the wheel, drag the canvas scrollbar to the
296
+ footer, then pick a field of the first section in the panel: the canvas returns to it.
@@ -108,6 +108,11 @@ sitemap), RSS (dated by `publishedAt`), `llms.txt` (one link per published item)
108
108
 
109
109
  ## 4. Field decisions
110
110
 
111
+ Before designing a section's fields, find the closest component-kit entry: the chooser by
112
+ category and edge tag is [kit-scenarios.md](kit-scenarios.md), the full list is the
113
+ `bettercms://kit` resource and [kit-catalog.md](kit-catalog.md). The entry's notes are the
114
+ field mapping and its pitfalls the review list; the rules below still decide the storage.
115
+
111
116
  - **Model each visual band as a `group` field (or a repeater), or a component instance.** The
112
117
  schema's shape decides whether the site can ever be componentized. The componentize lane finds
113
118
  a section in a `group`, a repeater, or two or more top-level keys sharing a prefix before `-` or
@@ -177,6 +177,11 @@ For each band, find the row whose strong signals all match. One match: classify.
177
177
  None: §9. The class picks the component family (`sectionType`) and the fields; it is never a
178
178
  field itself.
179
179
 
180
+ Every group below is a category of the component kit. Once a band is classified, open the
181
+ closest kit entry (the chooser in [kit-scenarios.md](kit-scenarios.md), the full list in the
182
+ `bettercms://kit` resource or [kit-catalog.md](kit-catalog.md)), map it by that entry's notes and
183
+ check its pitfalls before you write.
184
+
180
185
  | Confidence | Evidence | Action |
181
186
  |---|---|---|
182
187
  | High | A unique structural or vendor signal: `<form action="/cart/add">`, `role="tablist"`, JSON-LD of the right type | Model it |
@@ -229,7 +234,17 @@ field itself.
229
234
  | Long-form | Blog post | `<article>` with one `h1`, a byline, a hero image, a rich body |
230
235
  | Long-form | Docs page | Left nav tree, article, right "On this page" anchors, prev and next |
231
236
  | Long-form | Recipe, job, event detail | JSON-LD of that type, or the matching plugin classes |
237
+ | Long-form | Help article | "Was this article helpful?" at the end, a Help Center breadcrumb, numbered steps with screenshots |
238
+ | Long-form | Glossary or curriculum | An A–Z letter bar over `<dl>` terms; or "Module 1" accordions of lesson rows with durations |
232
239
  | Company | Team or leadership | Headshots with name and role; leadership adds C-level titles, bio modals, Board headings |
240
+ | Company | Careers or job | Job rows under department headings linking to a hiring board; or "What you'll do", a salary range, JobPosting JSON-LD |
241
+ | Company | Values or press kit | Three to eight headed value blocks and a perks grid; or logo tiles with SVG and PNG downloads |
242
+ | Events | Events list | Rows led by a big day and short month (12 NOV), Upcoming and Past tabs, "Get tickets" per row |
243
+ | Events | Webinar | A date with a time zone, a registration form beside speakers, "Watch on demand" |
244
+ | Places | Locations or location page | A map beside an address list, hours rows per day, `data-lat`; or one address with `tel:` and Restaurant JSON-LD |
245
+ | Places | Menu | Section headings with dish and price per line, dietary letter codes and a key, "MP" |
246
+ | Data | Calculator or configurator | Range and number inputs with listeners and an `<output>`, no submit; or "Step 1 of 3" option cards with "+$60" |
247
+ | Data | Countdown or poll | Digits as dd:hh:mm:ss with `data-end`; or one question with radio inputs and a Vote button |
233
248
  | Data | Table | `<table>` with `<th scope="col">`, more than two rows, a `<caption>` |
234
249
  | Data | Chart or map | An `<svg>` with axis ticks or a chart library global; a Leaflet, Mapbox or Google container |
235
250
  | Data | Embed or code | A third-party `<iframe>` or script widget; `<pre><code class="language-*">` |
@@ -62,7 +62,7 @@ line), which is what a script waiting on the setup reads.
62
62
  | `svg` | every inline SVG a visitor sees (a logo, an icon, an illustration) belongs to a field; a hidden symbol sheet is not counted | `routes.rows[].svg` |
63
63
  | `number` | every element whose whole text is a number (a stat, a price, a rating) belongs to a field | `routes.rows[].number` |
64
64
  | `attribute` | every `alt`, `aria-label`, `title` and `placeholder` with words in it belongs to a field | `routes.rows[].attribute` |
65
- | `names` | no field of the page, and no component, has a name a person cannot read: a slug, a hash, a structural word (`Section 2`, `wrap`), or a number beside the same name (`Icon 2`) | `blocking.rows` |
65
+ | `names` | no field of the page, and no component, has a name a person cannot read: a slug, a hash, a structural word (`Section 2`, `wrap`), a number beside the same name (`Icon 2`), a numbered run the import left (`Pricing text 1` … `Pricing text 12`), a section the import called by a sentence of its copy (`The Numbers After One Season items`), or a component name past four words | `blocking.rows` |
66
66
  | `seo` | the page has a title of at most 70 characters and a description of at most 200, and a canonical where the build ships one for this site's origin; site-wide, every page has a share image, its own or the site's default | `blocking.rows` |
67
67
  | `structuredData` | JSON-LD the build ships on the route is on the page or the entry | `blocking.rows` |
68
68
  | `collections` | sibling routes that are one family (three under one parent, or two under `tags`, `category`, `author`) are a collection; reported on the family's listing route, built or not | `blocking.rows` |
@@ -99,7 +99,7 @@ The other rows name their fix the same way:
99
99
  | `fixCode` | The fix |
100
100
  |---|---|
101
101
  | `bind-elements` | `get_conversion_brief { complete: true }` lists what no field owns on the route; bind each (an SVG as an image or icon field, a number as its own field, attribute copy with `data-bcms-props`) |
102
- | `rename-fields` | `get_page`, then `update_field` with a name a person would say; `update_component` for a component |
102
+ | `rename-fields` | `get_page`, read the section's source, then name every listed field of the page in ONE `update_content_model` call (`pageId`, `fieldLabel` and `fieldHelpText` keyed by field path, `ifMatch` from `get_page`'s `backingModel`), or `update_field` for a single one; `update_component` for a component (`name`, and `propLabel` / `propHelpText` for its props in one call). See "Names" below |
103
103
  | `fill-seo` | `update_page` with `metaTitle` and `metaDescription` (`update_content_entry` with `meta` for an entry; `generate_seo_meta` drafts one from text you give it); a missing canonical is setup's to copy from the built head, so that row carries `editor_setup { force: true }`; the site-wide row wants the site's default share image, `update_seo` with `seoDefaults.ogImage` |
104
104
  | `import-structured-data` | `editor_setup { force: true }` imports the build's JSON-LD; `update_seo` when it has to be written by hand |
105
105
  | `model-collection` | `create_content_model` with the family's `urlPattern` (`/tags/:slug`), then its entries: one item template for the family ([content-modeling.md](content-modeling.md)) |
@@ -119,8 +119,9 @@ the map), `import-structured-data`, `fill-seo` from the built head and the page'
119
119
  `rename-fields` for a field still carrying a derived name, `componentize` once the authoring
120
120
  answer is `components`, and `enable-in-place-structure` on a project it creates. What is left
121
121
  takes judgement and is yours: `bind-elements`, `model-collection`, `set-form-recipient` (ask the
122
- user), `run-sections-conversion`, a name a person gave, a title over its limit, a look named by a
123
- number, and the authoring answer itself (the user's). `rebuild` is the user's.
122
+ user), `run-sections-conversion`, a name a person gave, a name only the site's code can give (a
123
+ numbered run, a section called by its copy), a title over its limit, a look named by a number, and
124
+ the authoring answer itself (the user's). `rebuild` is the user's.
124
125
 
125
126
  Beside the gate, `data` lists what else is unfinished, each item by its `ruleId`: content
126
127
  (`mcp-pages-draft`, `mcp-pages-missing-meta`, `mcp-model-empty`, `mcp-form-no-recipient`,
@@ -171,6 +172,27 @@ the same way. A term has a `body` only where its template has an article. Chrome
171
172
  could not take (an icon with no Media asset) is template copy on item routes: one shared field
172
173
  of the collection, as it is a page's own field on a page.
173
174
 
175
+ ### Names
176
+
177
+ The import names a field by its section and its role there (`Hero headline`, `FAQ questions`).
178
+ Where the markup says no more it numbers them (`Pricing text 4`, `Hero image 2`), and a section
179
+ whose classes name nothing is called by its heading's words. The `names` row lists both until
180
+ each is renamed, and nothing excuses it: an editor reads these names all day.
181
+
182
+ Name from the site's code, the way a developer writes a schema. Open the section's source file
183
+ and use what it calls things (`plan.cents`, `billingFrequency`, `StatsBand.astro`):
184
+
185
+ | What | The name | Like | Never |
186
+ |---|---|---|---|
187
+ | A section (a component's `name`) | what it IS, one to four words, sentence case | `Announcement banner`, `Logo cloud`, `Feature tabs`, `Testimonial + stats`, `Call to action` | its heading's copy, `Section 4`, a file path |
188
+ | A field or prop | what it HOLDS there, one to three words | `Dismiss key`, `Starter plan price`, `Dashboard screenshot`, `Billing frequency` | its own copy, a role with a number (`Text 4`), the section's name alone |
189
+ | Help text | one sentence saying what it is for or where it shows, only where the name leaves a question | `Change this to show the banner again to visitors who dismissed an earlier one.` | the label or the value said again |
190
+
191
+ A numbered run that is one thing repeated (plans, features, logos) is a list, not twelve names:
192
+ fold it into one list field or one `table` prop whose rows are the cards
193
+ ([componentize.md](componentize.md)), and name the list (`Plans`). A name you write is yours: a
194
+ later setup never renames it.
195
+
174
196
  ### Exceptions go on the record
175
197
 
176
198
  A check you are leaving on purpose is recorded, one row per route and check, with