@bettercms-ai/mcp 0.70.0 → 0.72.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 +504 -76
- package/dist/index.js.map +1 -1
- package/dist/skills/bettercms/SKILL.md +16 -7
- package/dist/skills/bettercms/references/animation.md +251 -0
- package/dist/skills/bettercms/references/astro.md +47 -2
- package/dist/skills/bettercms/references/bindings.md +47 -4
- package/dist/skills/bettercms/references/componentize.md +65 -6
- package/dist/skills/bettercms/references/components.md +50 -11
- package/dist/skills/bettercms/references/content-modeling.md +14 -4
- package/dist/skills/bettercms/references/detection.md +2 -2
- package/dist/skills/bettercms/references/finish-site.md +188 -3
- package/dist/skills/bettercms/references/forms-and-interactions.md +7 -0
- package/dist/skills/bettercms/references/getting-started.md +2 -0
- package/dist/skills/bettercms/references/media-and-brand.md +39 -1
- package/dist/skills/bettercms/references/next.md +63 -3
- package/dist/skills/bettercms/references/publishing.md +100 -1
- package/dist/skills/bettercms/references/seo.md +2 -1
- package/dist/skills/bettercms/references/worked-examples.md +4 -2
- package/package.json +2 -2
|
@@ -35,7 +35,8 @@ that reads them.
|
|
|
35
35
|
- Publishing copies the stored draft to live, so what you checked in the draft is what ships.
|
|
36
36
|
- **Many at once**: `publish_entries` { action: 'publish' | 'unpublish', entryIds, pageIds } takes
|
|
37
37
|
up to 100 of each in one call (pages publish only). Each item still passes its own gates, and
|
|
38
|
-
nothing stops the run: read `failed` for every refusal and its reason
|
|
38
|
+
nothing stops the run: read `failed` for every refusal and its reason, then `outcome` for what
|
|
39
|
+
the served pages show (the same shape `update_page` returns on a publish, below).
|
|
39
40
|
- **Throw away a draft**: `discard_draft` with `entryId` or `pageId` returns it to exactly what is
|
|
40
41
|
live; a page also drops its unpublished Layout overrides (`no_published_version` if it never
|
|
41
42
|
went live: delete it instead).
|
|
@@ -64,6 +65,19 @@ renders rows no address names, or was built empty). That publish waits for its b
|
|
|
64
65
|
the list shows N rows") and fails with the reason if the build still shows the page's own rows: the
|
|
65
66
|
site's code renders the list itself. `get_next_steps` names what is waiting.
|
|
66
67
|
|
|
68
|
+
A static page whose own code fixes its sections takes all of it in place too, when the project's
|
|
69
|
+
in-place structure switch is on: the publish writes the page's block list into the built HTML.
|
|
70
|
+
The code's bands stand in the published order, a removed one is cut, a section added in
|
|
71
|
+
BetterCMS is drawn between them, and every root takes its block's style. No repository change
|
|
72
|
+
and no build; each rebuild is put right the same way. The gate's `sections` row says which
|
|
73
|
+
routes take it (`accepts: "in-place"`).
|
|
74
|
+
|
|
75
|
+
A collection's item pages take it per TEMPLATE: the collection page's sections are the
|
|
76
|
+
template's, held once, so a section added to that page (`update_page` on the collection page)
|
|
77
|
+
stands on every entry's route, and an entry has no sections of its own. Its publish reads one
|
|
78
|
+
row for all of them (`route` `blog/*`). The entry's body is a section too: add sections around
|
|
79
|
+
it or move it, and space it through its field's style, since its element is the field's.
|
|
80
|
+
|
|
67
81
|
The SEO a page or entry stores is what its `<head>` says, whenever it was set: the title, the
|
|
68
82
|
description, the canonical, the card fields, a `noindex` that is on. A field left empty keeps what
|
|
69
83
|
the build rendered, and a site default only fills a tag the build has none of. Only a real choice
|
|
@@ -79,6 +93,57 @@ Builds run `build:bcms` instead of `build` when `package.json` declares it. Keep
|
|
|
79
93
|
of it and every step the build needs in: `"build": "astro check && astro build"` beside
|
|
80
94
|
`"build:bcms": "astro build"`. The convert codemod adds one to a repo whose build chains a check.
|
|
81
95
|
|
|
96
|
+
### Add a section, or set its spacing
|
|
97
|
+
|
|
98
|
+
One call each, on any page (a page BetterCMS draws, a code-built one, a collection template):
|
|
99
|
+
|
|
100
|
+
- **Add a section here**: `update_page { pageId, blockJson: { insertSection: { componentId, after: "<block id>" } } }`.
|
|
101
|
+
`componentId` is a component from `list_components`; a `builtin:*` id is copied into the project
|
|
102
|
+
once and the copy is placed. Place it with `at` (an index into the page's sections), `before`
|
|
103
|
+
or `after` (a section's block id from `get_page`); none of the three adds it last. `style` and
|
|
104
|
+
`overrides` set its block style and its copy in the same call.
|
|
105
|
+
- **Set spacing on this section**: `update_page { pageId, blockJson: { sectionStyle: { blockId, style } } }`.
|
|
106
|
+
Each key of `style` replaces the stored one and `null` unsets it: `paddingTop`, `paddingBottom`,
|
|
107
|
+
`paddingSides` (px, 0 to 240), `spaceTop`, `spaceBottom`, `spaceSides` (the spacing tokens),
|
|
108
|
+
`bg`, `bgCustom`, `contentWidth`, `contentAlign`, `theme`, `corner`, `shadow`, `border`,
|
|
109
|
+
`opacity`, `hideOn`. A key the block style does not have is refused (`INVALID_STYLE`).
|
|
110
|
+
|
|
111
|
+
The server edits the page as stored, so nothing else on it is resent or overwritten; send
|
|
112
|
+
`ifMatch` to refuse a page that changed since you read it. Both save a DRAFT: add
|
|
113
|
+
`status: "published"` to the same call, or publish after. Then read the answer, top down:
|
|
114
|
+
|
|
115
|
+
1. `held`: the draft is saved and the live page will NOT show it. It names the reason and the one
|
|
116
|
+
action (below). Do the action; do not report the change as made.
|
|
117
|
+
2. `outcome` (on a publish): `status` `live` is every value found on the served page. `partial`
|
|
118
|
+
lists what is not in `notLive`, each with its `reason` and `action` (the table under Reading
|
|
119
|
+
`get_deploy_status`). `next: "get_deploy_status"` means it is not judged whole: nothing has
|
|
120
|
+
looked at it yet, or a section change is still being written (`NOT_CHECKED`). It resolves by
|
|
121
|
+
itself to live or held: poll `publishes` there before you say anything is live.
|
|
122
|
+
3. `section`: the block's `id` and `index`, to place the next one beside it.
|
|
123
|
+
4. `warnings`: `COMPONENT_DRAFT` is a section that renders nothing until `publish_component`.
|
|
124
|
+
|
|
125
|
+
Refusals: `SECTION_NOT_FOUND` (the answer lists the page's section ids), `POSITION_OUT_OF_RANGE`,
|
|
126
|
+
`COMPONENT_NOT_FOUND`, `BUILTIN_NOT_FOUND`. A whole `blockJson` array still replaces the page's
|
|
127
|
+
sections; use it to move or remove one, with `ifMatch`.
|
|
128
|
+
|
|
129
|
+
### A section change that is held at the write
|
|
130
|
+
|
|
131
|
+
`update_page` and `compose_pages` save the draft and answer `held` first (with `holds`) when a
|
|
132
|
+
write adds, moves or removes a section the live page cannot show yet. Each hold has a `reason`,
|
|
133
|
+
a `message`, the `sections` it is about and the one `action`. Never report such a write as done:
|
|
134
|
+
|
|
135
|
+
- `CODE_FIXED_SECTIONS`: the site's code fixes this page's sections, so no publish ships the
|
|
136
|
+
change. Read the route's `sections` row in `get_next_steps` (`receipt.gate`): its `fixCode`
|
|
137
|
+
names the lane ([finish-site.md](finish-site.md)). `enable-in-place-structure` is the user's
|
|
138
|
+
switch and needs no code. `run-sections-conversion` is the wrap: `componentize_sections` with
|
|
139
|
+
`codemod: true` and the `digest` of `get_componentize_plan`, or, when that answers `skipped`
|
|
140
|
+
(no repository the platform may commit to), `npx @bettercms-ai/convert@latest --sections --root .`
|
|
141
|
+
in a checkout ([componentize.md](componentize.md) §5). After its build, publish.
|
|
142
|
+
- `SECTION_NEEDS_BUILD`: the build draws the page's sections from BetterCMS. Publish: the build
|
|
143
|
+
starts by itself, and a move or a removal needs none.
|
|
144
|
+
- `STRUCTURE_UNMEASURED`: nothing has measured the page. Publish, then read the publish's outcome
|
|
145
|
+
in `get_deploy_status`.
|
|
146
|
+
|
|
82
147
|
### Reading `get_deploy_status`
|
|
83
148
|
|
|
84
149
|
- `release` is the newest release: `id`, `sha`, `status`, `phaseMarks`, `error`, `createdAt`.
|
|
@@ -92,6 +157,40 @@ of it and every step the build needs in: `"build": "astro check && astro build"`
|
|
|
92
157
|
and ready on staging while production keeps the older sha, until `promote_project` runs.
|
|
93
158
|
- The poll is finished only when `slot` is `current`, or when `awaitingPromote` is false and
|
|
94
159
|
`productionSha` equals the sha you deployed.
|
|
160
|
+
- `publishes` is the newest publishes, value by value. `status` `live` means every published value
|
|
161
|
+
was found in the file the site serves that page from. `partial` means some were not: `summary`
|
|
162
|
+
counts them ("3 of 4 live · 1 held") and `notLive` lists each with its `route`, `address`,
|
|
163
|
+
`state`, `reason` and the one `action` that fixes it. Never tell the user a publish is live
|
|
164
|
+
while it reads `partial`: relay the reason and do the action, then poll again.
|
|
165
|
+
|
|
166
|
+
| `reason` | `state` | The one action |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| `NOT_STAMPED` | held | Rebuild: the built page has no element for the field and does not show it |
|
|
169
|
+
| `VALUE_REFUSED` | held | Bind the field to an element that can hold its value |
|
|
170
|
+
| `APP_DOES_NOT_READ` | held | Convert in code: make the page's code read the published field |
|
|
171
|
+
| `ROWS_NEED_BUILD`, `SECTION_NEEDS_BUILD`, `NEEDS_BUILD` | held | Wait for the build the publish started |
|
|
172
|
+
| `STRUCTURE_UNBOUND` | refused | The fix the route's `sections` gate row names: the in-place switch, or the sections conversion |
|
|
173
|
+
| `SECTION_MISSING` | held | The same: the served page does not render the section |
|
|
174
|
+
| `SECTION_NOT_ON_PAGE` | held | In place: a section of the site's own code has no band on this page. The sections conversion, or remove the placement |
|
|
175
|
+
| `SECTION_NOT_RENDERABLE` | held | In place: the added section's component is not published, or it needs a form. `publish_component`, then publish the page again |
|
|
176
|
+
| `STYLE_NOT_PAINTABLE` | held | Rebuild once the section root carries the style marker (`sectionRootAttrs`, or the codemod's `--upgrade-sections`: [components.md](components.md)) |
|
|
177
|
+
| `WINDOW_NOT_OPEN` | held | Nothing: it goes live when its window opens |
|
|
178
|
+
| `ROUTE_NOT_OWNED` | held | Change one of the two page paths that share the URL |
|
|
179
|
+
| `APP_UNREACHABLE`, `NOT_CHECKED`, `EDGE_PENDING`, `IMAGE_UNAVAILABLE`, `STRUCTURE_UNMEASURED` | unverified | Poll again; publish again if it stays |
|
|
180
|
+
|
|
181
|
+
Each row's `action.code` is one of these, and `get_next_steps` lists every held or refused value
|
|
182
|
+
as `publish-held` until it is gone:
|
|
183
|
+
|
|
184
|
+
| `action.code` | What you do |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `rebuild` | No tool rebuilds: change the site's code if the reason names it, then ask the user to Rebuild from Publishing (or `deploy_project` from a checkout) |
|
|
187
|
+
| `wait_for_build` | Poll `get_deploy_status` until the build the publish started is live |
|
|
188
|
+
| `convert_in_code` | Make the page's code read the field ([bindings.md](bindings.md)), then release |
|
|
189
|
+
| `control_section_order` | The route's `sections` gate row names it: the in-place switch (the user's), or `componentize_sections` with `codemod: true` (else `convert --sections` in a checkout), then release |
|
|
190
|
+
| `editor_setup` | `editor_setup` |
|
|
191
|
+
| `rebind` | Bind the field to an element that can hold it, in the site's code |
|
|
192
|
+
| `resolve_route` | Change one of the two page paths |
|
|
193
|
+
| `wait`, `retry` | Poll again; publish again if it stays |
|
|
95
194
|
|
|
96
195
|
### Deploy to Vercel or Netlify
|
|
97
196
|
|
|
@@ -53,7 +53,8 @@ values as they are; rewriting copy is a separate, reported recommendation.
|
|
|
53
53
|
| `<meta name="description">` | `metaDescription` | Copy verbatim. Aim for 120–160 characters; the audit flags only a missing one and one over 160. When the source has none, take a `generate_seo_meta` suggestion and list it for review |
|
|
54
54
|
| `og:title`, `og:description`, `og:image`, `og:type` | `og.title`, `og.description`, `og.image`, `og.type` | Write a key only when it differs from the meta title, the description or the site default. Upload the image first and store its media URL |
|
|
55
55
|
| `twitter:card`, `twitter:title`, `twitter:description`, `twitter:image` | `twitter.card`, `twitter.title`, `twitter.description`, `twitter.image` | Same rule as OG |
|
|
56
|
-
| `twitter:site` | `update_seo` `seoDefaults.twitterHandle` | Once per site |
|
|
56
|
+
| `twitter:site` | `update_seo` `seoDefaults.twitterHandle` | Once per site. A BetterCMS-built import fills it, the card and `og:type` from the home page's head where the project holds none |
|
|
57
|
+
| `keywords`, `author`, `theme-color`, a site verification tag | nothing | No native key stores them. The binding report's `elementGaps` lists each by name (`HEAD_META_UNMAPPED`); report them |
|
|
57
58
|
| A site-wide share image | `update_seo` `seoDefaults.ogImage` | Then pages inherit it and `og.image` stays empty |
|
|
58
59
|
| Canonical pointing at the page itself | nothing | A hosted page gets its own canonical |
|
|
59
60
|
| Canonical pointing at another URL | `canonical` | Keep it: someone consolidated those URLs on purpose |
|
|
@@ -109,8 +109,8 @@ Value (`set_page_content`):
|
|
|
109
109
|
"hero_eyebrow": "Freight visibility for mid-size shippers",
|
|
110
110
|
"hero_title": "Know where every container is before your customer asks.",
|
|
111
111
|
"hero_lede": "Live ETAs from 140 carriers, delays flagged before they cost you, and one shared view for ops and sales.",
|
|
112
|
-
"hero_primary": { "label": "Book a demo", "href": "/demo", "
|
|
113
|
-
"hero_secondary": { "label": "Start free trial", "href": "https://app.kestrelfreight.com/signup"
|
|
112
|
+
"hero_primary": { "label": "Book a demo", "href": "/demo", "arrow": true },
|
|
113
|
+
"hero_secondary": { "label": "Start free trial", "href": "https://app.kestrelfreight.com/signup" },
|
|
114
114
|
"hero_image": {
|
|
115
115
|
"url": "/img/hero-harbor-2560.jpg",
|
|
116
116
|
"altText": "Container cranes over a quiet harbor at dusk",
|
|
@@ -149,6 +149,8 @@ Bound template (classes unchanged, shortened here):
|
|
|
149
149
|
- The image binding sits on the `<img>`, not the `<picture>`. The alt text travels in the image
|
|
150
150
|
value (`altText`): a publish writes it to the bound element's `alt`. When `get_conversion_brief`
|
|
151
151
|
lists a separate alt path, declare that path in `data-bcms-props` with kind `text`.
|
|
152
|
+
- Neither button value names a `variant`: the anchors keep the site's own `btn-primary` and
|
|
153
|
+
`btn-ghost`, and the release reads those two looks into the project's Button family.
|
|
152
154
|
- An `href` is kind `url`, never `text`. The label gets its own `<span>` so a save does not erase
|
|
153
155
|
the arrow; the arrow is drawn by `BcmsButton` ([components.md](components.md)), so remove the
|
|
154
156
|
hand-written `<svg>` once the button renders through it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bettercms-ai/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "BetterCMS MCP server — schema, content, forms + components authoring tools over the Management API, authed via OAuth device-code.",
|
|
6
6
|
"bin": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@bettercms-ai/device-auth": "^0.2.0",
|
|
35
|
-
"@bettercms-ai/sdk": "^1.
|
|
35
|
+
"@bettercms-ai/sdk": "^1.29.0",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
37
37
|
"zod": "^4.3.6"
|
|
38
38
|
},
|