@bettercms-ai/mcp 0.68.2 → 0.70.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 +165 -70
- package/dist/index.js.map +1 -1
- package/dist/skills/bettercms/SKILL.md +8 -4
- package/dist/skills/bettercms/references/accessibility.md +171 -0
- package/dist/skills/bettercms/references/astro.md +5 -1
- package/dist/skills/bettercms/references/bindings.md +47 -0
- package/dist/skills/bettercms/references/componentize.md +6 -4
- package/dist/skills/bettercms/references/components.md +56 -5
- package/dist/skills/bettercms/references/content-modeling.md +156 -24
- package/dist/skills/bettercms/references/detection.md +450 -0
- package/dist/skills/bettercms/references/editor-chrome.md +4 -0
- package/dist/skills/bettercms/references/forms-and-interactions.md +16 -4
- package/dist/skills/bettercms/references/frameworks.md +2 -2
- package/dist/skills/bettercms/references/media-and-brand.md +2 -1
- package/dist/skills/bettercms/references/next.md +2 -2
- package/dist/skills/bettercms/references/publishing.md +22 -4
- package/dist/skills/bettercms/references/seo.md +226 -26
- package/dist/skills/bettercms/references/site-blueprints.md +647 -2
- package/dist/skills/bettercms/references/site-review.md +2 -1
- package/dist/skills/bettercms/references/worked-examples-advanced.md +509 -0
- package/dist/skills/bettercms/references/worked-examples.md +738 -0
- package/dist/skills/bettercms/references/write-limits.md +138 -4
- package/package.json +2 -2
|
@@ -14,6 +14,16 @@ Doctrine: §1–§5 and §14 of `bettercms://playbook/schema` (schema shape and
|
|
|
14
14
|
[write-limits.md](write-limits.md). Planning a whole site by business type:
|
|
15
15
|
[site-blueprints.md](site-blueprints.md).
|
|
16
16
|
|
|
17
|
+
## Contents
|
|
18
|
+
|
|
19
|
+
1. Read the site before modeling
|
|
20
|
+
2. Pages, collections and entries (thresholds, promoting items to a collection)
|
|
21
|
+
3. Four structure layers, four meanings
|
|
22
|
+
4. Field decisions (references, lists, listings, the `document` body, buttons)
|
|
23
|
+
5. Editor labels and order (when to write help text)
|
|
24
|
+
6. Changing a schema safely
|
|
25
|
+
7. Reading a schema written in the V2 language (field type map, glossary)
|
|
26
|
+
|
|
17
27
|
## 1. Read the site before modeling
|
|
18
28
|
|
|
19
29
|
Inventory route files and rendered routes, source data schemas, repeated records, page templates,
|
|
@@ -33,6 +43,12 @@ collection: the model and its entries both point at the page (`content_models.pa
|
|
|
33
43
|
`create_page` takes its route pattern and its collection through `update_page { dynamic:
|
|
34
44
|
{ previewPath, modelId } }`: `/blog/{slug}`, and an existing collection with no page yet.
|
|
35
45
|
|
|
46
|
+
A listing elsewhere (latest posts on home, related posts on an article) is a `collection` block
|
|
47
|
+
with `source: { modelId }`, never copied entries. It also takes `filters` (max 5, `{ field, op,
|
|
48
|
+
value }` with the content API operators), `sort` (`-publishedAt,title`), `pinnedIds` (max 10,
|
|
49
|
+
shown first), `excludeCurrent` and `limit`. BetterCMS-rendered pages read it on every request;
|
|
50
|
+
a route the repo builds does not draw this block, so list there with the content API.
|
|
51
|
+
|
|
36
52
|
| Source pattern | BetterCMS destination |
|
|
37
53
|
|---|---|
|
|
38
54
|
| Home, about, contact, legal, editorial landing page | Singleton page with its own content |
|
|
@@ -43,6 +59,21 @@ collection: the model and its entries both point at the page (`content_models.pa
|
|
|
43
59
|
| Data from an external runtime API | Keep the adapter and its owner; never fabricate entries |
|
|
44
60
|
| Sitewide nav, footer, promo bar | Global Layout, never copied page fields |
|
|
45
61
|
|
|
62
|
+
**Thresholds.** Three or more pages sharing a URL pattern and a card or detail layout are a
|
|
63
|
+
collection. Two are a collection when the set will grow (jobs, case studies). Otherwise they are
|
|
64
|
+
singletons.
|
|
65
|
+
|
|
66
|
+
**Promote repeater items to a collection** when any one holds; keep them inline when none does
|
|
67
|
+
(pricing bullets, a hero proof line, quotes on a one-off campaign page):
|
|
68
|
+
|
|
69
|
+
| Signal | Example |
|
|
70
|
+
|---|---|
|
|
71
|
+
| The item has its own URL | A case-study card linking to its detail page |
|
|
72
|
+
| The same item shows on two or more pages | A testimonial on home and on a product page |
|
|
73
|
+
| It carries facts that must not drift between copies | Price, stock, rating, job status, a person's photo |
|
|
74
|
+
| Another entry points at it | A post's author, an event's speaker |
|
|
75
|
+
| Editors will filter, sort or count the items | Jobs by team, events by date |
|
|
76
|
+
|
|
46
77
|
## 3. Four structure layers, four meanings
|
|
47
78
|
|
|
48
79
|
Name the layer every time you say "folder". The answer to "where does X live" differs per layer.
|
|
@@ -84,8 +115,29 @@ sitemap), RSS (dated by `publishedAt`), `llms.txt` (one link per published item)
|
|
|
84
115
|
one page or section owns.
|
|
85
116
|
- A reference for reusable entities and internal links where the model supports it; an external
|
|
86
117
|
URL for links that leave the site.
|
|
118
|
+
- **Resolve references before creating anything.** Build the target collection and its entries
|
|
119
|
+
first, then the model that points at them. Match each source item to an existing entry by
|
|
120
|
+
stable facts (a slug, a product handle, a name plus the start of the quote) and create only what
|
|
121
|
+
is missing. Store the entry id, never an href or a title. A write naming a target that does not
|
|
122
|
+
exist is refused with 400 (ref_missing). Flag an item with no identity (a quote with no name);
|
|
123
|
+
never import it.
|
|
87
124
|
- A repeater for small, same-shaped items (cards, FAQ rows, links, social accounts); a nested
|
|
88
125
|
component or slot when an item has its own layout or variants.
|
|
126
|
+
- **Lists are arrays; tags are taxonomy.** Features, steps, benefits and "what's included" are an
|
|
127
|
+
`array` (`itemType: "text"`), or a repeater when each item carries its own state (included or
|
|
128
|
+
not, an icon). Never a group holding one field. A label set that filters or classifies entries
|
|
129
|
+
is a shared collection referenced with `multi-reference` (§2), not free text.
|
|
130
|
+
- **Never freeze a feed.** A list of entries on the source stays a listing: copying the nine
|
|
131
|
+
visible cards into a repeater stops it updating. On a page BetterCMS renders, that is the
|
|
132
|
+
`collection` block: on the collection's own dynamic page it lists that collection, and with
|
|
133
|
+
`source: { modelId }` it lists any collection on any hosted page (§2). It takes `limit` (1 to
|
|
134
|
+
200), `order` (`newest` or `oldest`) or `sort`, `filters`, `pinnedIds`, `excludeCurrent` and
|
|
135
|
+
`emptyText`; in an article template a filter value may be `{{field}}` of the entry shown, which
|
|
136
|
+
is a "related" list. Pagination and grouping stay a query in the site's code. Always set the
|
|
137
|
+
empty state, never write card titles or images into the listing, and put "read next" on the
|
|
138
|
+
template once, not on each entry.
|
|
139
|
+
- **`document` is the one long body.** A page schema or a collection may hold it, one at
|
|
140
|
+
most, and only as a top-level field: a group or a repeater cannot contain it. Label it Body. Short formatted copy stays `richtext`.
|
|
89
141
|
- **Model a call to action as a single `button` field.** It stores `{ label, href }` plus the
|
|
90
142
|
optional look keys `variant`, `size`, `arrow`, `icon` and `newTab`, and the site draws it with
|
|
91
143
|
`BcmsButton` ([astro.md](astro.md), [next.md](next.md)). Never a text
|
|
@@ -97,11 +149,17 @@ sitemap), RSS (dated by `publishedAt`), `llms.txt` (one link per published item)
|
|
|
97
149
|
so code still reading them keeps rendering; the live copy changes on publish. On a route the
|
|
98
150
|
site's app renders at request time the merge is refused (409 `CODE_RENDERED_ROUTE`): change the
|
|
99
151
|
code to draw the CTA with `BcmsButton`, and the next build adds the button field. Inside a component
|
|
100
|
-
a CTA is a `button` block whose `props
|
|
152
|
+
a CTA is a `button` block whose `props` one `button` prop targets ([components.md](components.md) §1).
|
|
153
|
+
- A sponsored, untrusted or user-generated link takes `rel` (`nofollow`, `sponsored` or `ugc`) on the
|
|
154
|
+
button, or a `link` field value written as `{ href, rel?, newTab? }`; code that opts into the object reads `.href`.
|
|
155
|
+
- A `date` field with a time takes `timezone` (`site` or an IANA name such as `Europe/Paris`) in its config;
|
|
156
|
+
values must then end in `Z` or an offset, and a bracketed zone suffix is dropped on save.
|
|
101
157
|
- Never model SEO columns ([seo.md](seo.md)), Form questions (they live on the Form document) or
|
|
102
158
|
Global Layout values as content fields.
|
|
103
159
|
- Keep source ids, slugs, locale, status and previous URLs as migration metadata, stable across
|
|
104
160
|
re-imports.
|
|
161
|
+
- Lock a field whose value another system owns (a store, job board or feed id): `readOnly: "developer"` with a `lockReason`.
|
|
162
|
+
Only schema access may then change the value or the lock; other writes are refused (403 `FIELD_LOCKED`) unless they send the stored value back.
|
|
105
163
|
- **Prefer a `select` over a `boolean` for a state that may grow** (`draft | review | live`, not
|
|
106
164
|
`isLive`). Why: no tool retypes a field, so a third state later costs a new field and a value
|
|
107
165
|
migration (§6).
|
|
@@ -117,38 +175,48 @@ fail, because the next redesign makes them lie.
|
|
|
117
175
|
|
|
118
176
|
Short labels named for what the editor sees: Heading, Body, Primary button, Label, Link, Image,
|
|
119
177
|
Alt text — or the name the site's code already uses (`cents` → Cents), never the copy itself
|
|
120
|
-
(`.00`).
|
|
178
|
+
(`.00`). Most fields need no help line from you (below). Group by what renders
|
|
121
179
|
together, not by the DOM or the source file: core content first, optional appearance second, technical fields hidden. Repeatable items need a meaningful preview label (the FAQ question, the
|
|
122
180
|
button label), reorder, and stable item ids. An empty optional field removes its element without
|
|
123
181
|
leaving a gap; a long value wraps, and clamps only where the design intends it.
|
|
124
182
|
|
|
125
183
|
### Writing help text
|
|
126
184
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
185
|
+
The platform writes a role line on every element, so leaving `helpText` out is the default, not
|
|
186
|
+
a gap. It says so: `helpText: { source: "auto" }` in the response, `helpSource: "auto"` on the
|
|
187
|
+
stored field. Text you send is stored as yours (`helpSource: "agent"`) and is never rewritten;
|
|
188
|
+
`editor_setup` rewrites only its own lines and never a person's.
|
|
189
|
+
|
|
190
|
+
Ask one question per field: does the editor need this line to type the right value? Write your
|
|
191
|
+
own `helpText` (on a field or a prop) only for one of four reasons:
|
|
130
192
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
193
|
+
| Reason | Good |
|
|
194
|
+
|---|---|
|
|
195
|
+
| A format or unit the label cannot show | Price in cents. · Length in minutes. · A .vtt caption file. |
|
|
196
|
+
| Where the value shows, when the canvas cannot show it | Shown in search results and link previews. · Read by screen readers, not shown. |
|
|
197
|
+
| A real constraint or consequence | Changing this breaks existing links. · Must be later than the start date. |
|
|
198
|
+
| A fallback | Leave empty to use the page title. |
|
|
136
199
|
|
|
137
|
-
|
|
200
|
+
Everything else is left out:
|
|
201
|
+
|
|
202
|
+
| Never | Bad | Do instead |
|
|
138
203
|
|---|---|---|
|
|
139
|
-
|
|
|
140
|
-
|
|
|
141
|
-
|
|
|
142
|
-
|
|
|
143
|
-
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
204
|
+
| The label or the value again | Heading: "The section's main heading." · `e.g. "Starter Kit"` | Nothing. The API refuses help that echoes the value |
|
|
205
|
+
| Coaching the writing | "Keep it short." · "Two to four words works best." · "Start with a verb." | A length is a counter: a number in `config` (`"maxLength": 60`, §6) |
|
|
206
|
+
| An example of the value | Badge: "e.g. Most popular" | A `placeholder` where the control takes one (component props do) |
|
|
207
|
+
| Strategy | Secondary button: "A lower-commitment option that builds trust." | Nothing. The label says which button it is |
|
|
208
|
+
| Internal notes | "Renders as h2." · "Label and link are one unit." · a ticket or decision id | Keep them in the code or the report, never in the form |
|
|
209
|
+
| Generic good practice | "Add alt text for screen readers." on every image | Nothing |
|
|
210
|
+
|
|
211
|
+
One sentence, plain words, sentence case. If more than about one field in five carries your own
|
|
212
|
+
line, you are over-writing. A component, a collection and a Layout Section (`update-section`)
|
|
213
|
+
still take a `description` that says what the thing is ("Hero with a heading, supporting text,
|
|
214
|
+
two buttons and an image"), never its name again.
|
|
215
|
+
|
|
216
|
+
`get_next_steps` lists `help-text-missing` for a field with no help line at all, or one that
|
|
217
|
+
only echoes its label, and `get_binding_report` and `get_site_composition` carry the coverage as
|
|
218
|
+
`help`. Clear the finding by letting the platform's line stand or by writing one of the four
|
|
219
|
+
kinds above, never with filler.
|
|
152
220
|
|
|
153
221
|
## 6. Changing a schema safely
|
|
154
222
|
|
|
@@ -217,6 +285,70 @@ authorization and the component readiness and Visual Approval gates.
|
|
|
217
285
|
count, codegen and build result, one editor check and one rendered-page check, warnings, and any
|
|
218
286
|
change the platform could not make (an open dependency, never a success).
|
|
219
287
|
|
|
288
|
+
## 7. Reading a schema written in the V2 language
|
|
289
|
+
|
|
290
|
+
A plan or Kit sample may arrive in the V2 schema language. It is an intermediate model only:
|
|
291
|
+
production cannot store it. Translate before the first write.
|
|
292
|
+
|
|
293
|
+
- **Keys.** V2 names are camelCase and scoped per object (`hero.heading`). Production keys are
|
|
294
|
+
flat, one namespace per model, prefixed by section: `"hero_heading"`.
|
|
295
|
+
- **Labels and help.** V2 `title` is `label`; V2 `description` is `helpText` (§5 decides whether
|
|
296
|
+
it is written at all).
|
|
297
|
+
- **Limits.** V2 `validation` becomes numbers in `config` (§6 step 4).
|
|
298
|
+
|
|
299
|
+
| V2 type | Model field (`create_content_model`, `add_field`) | Component prop (`create_component`) |
|
|
300
|
+
|---|---|---|
|
|
301
|
+
| `string` | `text` with `richText: false` | `text` |
|
|
302
|
+
| `text` | `longtext` | `text` |
|
|
303
|
+
| `richtext` | `richtext` for section copy; `document` for the one body | `richtext` |
|
|
304
|
+
| `number` | `number` | `number` |
|
|
305
|
+
| `rating` | `number` with `config.min`, `max`, `step` and `ui.control: "rating"` (stars) | `number` |
|
|
306
|
+
| `boolean` | `boolean` | `boolean` |
|
|
307
|
+
| `slug` | `slug` (an entry's own slug stays on the entry) | `text` |
|
|
308
|
+
| `url`, `link` | `link` | `link`; `url` for a media or embed address |
|
|
309
|
+
| `email`, `phone` | `email`, `phone` | `text` |
|
|
310
|
+
| `date`, `datetime` | `date`, `datetime` | `date` (`YYYY-MM-DD`); a datetime stays `text` |
|
|
311
|
+
| `color` | `color` | `color` |
|
|
312
|
+
| `select` | `select` | `select` with `config.options` |
|
|
313
|
+
| `multiselect`, `tags` | `multiselect` with `config.options` (a `string[]`; `config.allowCustom` for free tags, `min`/`max` count the choices); a taxonomy with its own pages is a collection plus `multi-reference` | `multiselect` with `config.options` |
|
|
314
|
+
| `image` | `image` | `image` |
|
|
315
|
+
| `file`, `video` | `file`; a hosted video URL is a `link` | `file`; `url` for a hosted video |
|
|
316
|
+
| `icon` | `icon` (an icon name or an uploaded image) | `icon` (an icon name or an image) |
|
|
317
|
+
| `button` | `button` | `button`: one prop targeting the `props` of a `button` block (§4) |
|
|
318
|
+
| `reference` | `reference`; `multi-reference` for many | none: keep it on the model |
|
|
319
|
+
| `code` | `longtext` | `text` |
|
|
320
|
+
| `json` | `json` | `text` |
|
|
321
|
+
| `geopoint` | `location` (`{ lat, lng, label? }`) | `group` of two `number` props |
|
|
322
|
+
| `table` | a Portable Text table inside `richtext` or `document`, or a `repeater` of rows | `table` |
|
|
323
|
+
| `object` | `group` | `group` |
|
|
324
|
+
| `array` of objects | `repeater` | `table` |
|
|
325
|
+
| `array` of strings | `array` (`itemType: "text"`) | `table` |
|
|
326
|
+
| `array` of several item shapes | `modular` with `config.blockSlugs` (create the block models first) | separate components |
|
|
327
|
+
| `slot` | `sections` or `component-ref` | `slot` (one instance; a list with `config.multiple`) |
|
|
328
|
+
| a form `reference` | a `form` block inside the section | `form` |
|
|
329
|
+
|
|
330
|
+
V2 features with no production shape are dropped and reported, never faked: a per-cell table
|
|
331
|
+
type, per-breakpoint values (only `style.hideOn` differs by breakpoint) and per-field
|
|
332
|
+
localization. Two have a shape now: typed table columns are a `repeater` with one field per column
|
|
333
|
+
and `ui.layout: "table"`, and a slot with several children is a `slot` prop with `config.multiple`
|
|
334
|
+
([components.md](components.md) §1).
|
|
335
|
+
|
|
336
|
+
| The plan says | Production word |
|
|
337
|
+
|---|---|
|
|
338
|
+
| Block | Component (the type); section or component instance (the placed band) |
|
|
339
|
+
| Site-wide block, global | Global Layout (`update_layout`) |
|
|
340
|
+
| CMS item | Entry |
|
|
341
|
+
| Collection page | An entry shown through its collection's dynamic page |
|
|
342
|
+
| Static page | `singleton` page |
|
|
343
|
+
| Collection | Content model plus its `dynamic` page |
|
|
344
|
+
| Variant, layout | Components sharing one `sectionType` ([components.md](components.md)) |
|
|
345
|
+
| Listing | `collection` block (§4) |
|
|
346
|
+
| Picks | `multi-reference` |
|
|
347
|
+
| Polymorphic array, modules | `modular` field |
|
|
348
|
+
|
|
349
|
+
Tools and reports use the production word. Labels and help an editor reads never say
|
|
350
|
+
"document": the `document` field is labelled Body.
|
|
351
|
+
|
|
220
352
|
## Acceptance
|
|
221
353
|
|
|
222
354
|
- Every imported route and collection has one clear home; one dynamic template per collection.
|