@bettercms-ai/mcp 0.81.0 → 0.83.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.
@@ -72,9 +72,10 @@ These hold on every task. The reference in brackets has the detail and the codes
72
72
  5. **Name the folder system every time you say "folder"**: Content sidebar folders never touch a
73
73
  URL, URL page folders are part of the path, media folders organise assets.
74
74
  [content-modeling.md]
75
- 6. **Headless projects are built by their host.** Never `deploy_project` or
76
- `create_deploy_upload` there, never add `bcms-rebuild.yml`; deploy = push to the repo the
77
- provider builds. [frameworks.md]
75
+ 6. **Headless projects are built by their host.** Never `deploy_project`, `deploy_to_host` or
76
+ `create_deploy_upload` there, never add `bcms-rebuild.yml`; deploy with
77
+ `npx @bettercms-ai/cli@latest deploy --to <host>`, or push to the repo the host builds.
78
+ Done = `receipt.frontEnd` closed. [getting-started.md]
78
79
  7. **Every refusal has a code. Report it; do not retry blind.** The same call returns the same
79
80
  code. Change the input or stop.
80
81
  8. **Media is served by the backend that owns the asset ids.** `mediaUrl` is `mediaBaseUrl` from
@@ -123,7 +124,7 @@ resources: `bettercms://skill/bettercms` for this file, and each reference under
123
124
 
124
125
  | Task | Reference | Read when | Keywords |
125
126
  |---|---|---|---|
126
- | Set up or resume a project | [getting-started.md](references/getting-started.md) | first contact, or "Continue BetterCMS setup" | setup, resume, onboarding, connect, import my site |
127
+ | Set up or resume a project | [getting-started.md](references/getting-started.md) | first contact, or "Continue BetterCMS setup" | setup, resume, onboarding, connect, import my site, headless, own front end, SDK, CLI |
127
128
  | Finish an imported or converted site | [finish-site.md](references/finish-site.md) | a convert or build job; "make it fully editable"; the gap ledger | make it editable, finish, gap ledger, convert |
128
129
  | Move content out of another CMS (Cosmic, Contentful, Sanity, Strapi, Prismic, WordPress…) | [external-content.md](references/external-content.md) | a CMS SDK in package.json; `envWarning` on import; pages that render empty without a third-party key | Contentful, Sanity, Strapi, envWarning, missing key, empty pages |
129
130
  | Read a source site before modelling it | [detection.md](references/detection.md) | converting from a URL or built HTML; deciding what a band, a repeat or a value is | detect, classify, section, repeat, global, carousel clone, hidden state |
@@ -19,13 +19,13 @@ A project's `hostingMode` is `managed`, `hybrid` or `headless`.
19
19
  |---|---|---|
20
20
  | managed | BetterCMS | `deploy_project` / `create_deploy_upload` are the deploy path |
21
21
  | hybrid | BetterCMS, and the client also reads content headlessly | Treat exactly like managed for builds and hosting |
22
- | headless | The client's own host (Vercel, Netlify, Cloudflare Pages) | Never call `deploy_project` or `create_deploy_upload`; never add `bcms-rebuild.yml`. Deploy = push to the repo the host builds |
22
+ | headless | The client's own host (Vercel, Netlify, Cloudflare Pages) | Never call `deploy_project`, `deploy_to_host` or `create_deploy_upload`; never add `bcms-rebuild.yml`. Deploy = `npx @bettercms-ai/cli@latest deploy --to <host>`, or push to the repo the host builds. Done = `receipt.frontEnd` closed ([getting-started.md](getting-started.md)) |
23
23
 
24
24
  Headless refusals, by code:
25
25
 
26
26
  | Code | Cause | Fix |
27
27
  |---|---|---|
28
- | `HEADLESS_NO_BUILD` | A build, release or deploy was sent for a headless project | Stop; push to the provider instead |
28
+ | `HEADLESS_NO_BUILD` | A build, release or deploy was sent for a headless project | Stop; `bettercms deploy --to <host>` or push to the provider instead |
29
29
  | `HEADLESS_NEEDS_CUSTOM_HOST` | GitHub connect without a provider host | Add Vercel, Netlify or Cloudflare Pages under Custom hosting in the dashboard, then connect |
30
30
  | `HEADLESS_ARTIFACT_TARGET` | The custom host expects builds from BetterCMS | Switch the host to build from the repo |
31
31
 
@@ -45,6 +45,9 @@ found itself. So: switch Hosting first (ask the owner), then ship the build, the
45
45
  | `@bettercms-ai/astro` | `PUBLIC_BCMS_API_URL`, `PUBLIC_BCMS_WORKSPACE`, `PUBLIC_BCMS_PROJECT_ID` | Where reads go |
46
46
  | `@bettercms-ai/astro` | `PUBLIC_BCMS_MEDIA_URL`, `PUBLIC_BCMS_STUDIO_URL` | Media host, editor origin |
47
47
  | `@bettercms-ai/astro` | `BCMS_API_KEY`, `BCMS_DRAFT_SECRET` | Server-only key; draft cookie signing |
48
+ | `@bettercms-ai/react` | `VITE_BCMS_API_URL`, `VITE_BCMS_WORKSPACE`, `VITE_BCMS_PROJECT_ID` | Where reads go (no key, no secret: a browser app) |
49
+ | `@bettercms-ai/sveltekit` | `PUBLIC_BCMS_API_URL`, `PUBLIC_BCMS_WORKSPACE`, `PUBLIC_BCMS_PROJECT_ID`; `BCMS_DRAFT_SECRET` | Where reads go; draft cookie signing |
50
+ | `@bettercms-ai/nuxt` | `NUXT_PUBLIC_BCMS_API_URL`, `NUXT_PUBLIC_BCMS_WORKSPACE`, `NUXT_PUBLIC_BCMS_PROJECT_ID`; `BCMS_DRAFT_SECRET` | Where reads go; draft cookie signing |
48
51
  | `@bettercms-ai/next` (snapshot bin) | `BETTERCMS_API_URL`, `BETTERCMS_API_KEY`, `BETTERCMS_WORKSPACE` | Pull `bcms-content.json` |
49
52
  | `@bettercms-ai/next` | `BETTERCMS_PREVIEW=1` | Read live instead of the snapshot in a preview build |
50
53
  | `@bettercms-ai/codegen` | `BETTERCMS_API_URL`, `BETTERCMS_API_KEY` | Fetch models for typegen |
@@ -38,7 +38,7 @@ Check: the MCP answers for this project id, and this folder is its repo.
38
38
  failed, run the command the warning prints, inside the clone.
39
39
  - Either way, stop and ask the user to restart you **inside** the repo folder: an MCP entry
40
40
  added while you run loads only when the agent starts, and every step below works on that
41
- folder's source.
41
+ folder's source. Not when `init` launched you: you already run there, connected.
42
42
 
43
43
  ### Phase 2 — No questions: the defaults are recorded
44
44
 
@@ -117,3 +117,40 @@ Check: the deployed site passes the numbers below on every route.
117
117
  ([site-blueprints.md](site-blueprints.md)), then build sections ([components.md](components.md)).
118
118
  3. When a repo exists, clone it with the `init` command above, restart inside it, and continue
119
119
  from Phase 3.
120
+
121
+ ## A headless project (its own front end)
122
+
123
+ `get_project` says `hostingMode: "headless"`: BetterCMS never builds this site, its own host does.
124
+ Done is `receipt.frontEnd` closed AND `receipt.gate` complete in get_next_steps: BetterCMS derived
125
+ the owner's pages, their code reads the content, the front end has its site URL and rebuilds on
126
+ publish. Work the rows in order; never `deploy_project` or `deploy_to_host`.
127
+
128
+ 1. **Launched by `bettercms init`** (`--agent claude|codex|opencode|cursor|gemini`): you run in the
129
+ owner's own front end, already connected. Do not ask to be restarted. **Their code, design and
130
+ copy ARE the site:** keep every component, route, class, style and asset; never redesign a page,
131
+ rebuild one from scratch or swap their components for new ones. A starter is only ever the
132
+ starting point when the owner chose one (`init --framework`).
133
+ 2. **Derive** (`derive` row). `npx @bettercms-ai/cli@latest derive` in the front end's folder builds
134
+ the site, renders every page as a visitor sees it and sends the pages to BetterCMS, which derives
135
+ their pages, fields and today's copy exactly as from a build it made. init runs it first; run it
136
+ yourself while the row is open. Never model those pages again.
137
+ 3. **Convert in place** (`receipt.gate`, the same gate as any built site). `get_conversion_brief
138
+ { complete: true }`, the convert codemod over this folder (its command is in
139
+ `bettercms://playbook/schema` §13), then `submit_conversion_receipt` with the receipt it wrote:
140
+ each literal becomes a read of its field with the markup byte-identical. Bind what it leaves pending by hand the same way. Then
141
+ start the build script with it, `"build": "bettercms pull-content && next build"`
142
+ (`@bettercms-ai/cli` a devDependency; not a `prebuild` hook, which Yarn and pnpm skip): the
143
+ converted pages read `bcms-content/*.json`, which only that step writes on the host's build.
144
+ 4. **Wire and read.** `.env` gets `BCMS_API_URL`, `BCMS_WORKSPACE` and `BCMS_PROJECT_ID` (from
145
+ `get_project`); published content needs no key. A starter whose config reads a key gets one
146
+ from `create_delivery_key` (`scope: "published"`), written straight into `.env`, never into
147
+ chat or a commit. Run the app and load a page: its first read closes `read`.
148
+ 5. **Deploy.** `npx @bettercms-ai/cli@latest deploy --to vercel|netlify|cloudflare` from the
149
+ folder: the host's own sign-in, the `BCMS_*` variables set there, and the rebuild hook
150
+ registered (`publish-hook`). A front end that revalidates instead: `set_front_end
151
+ { revalidation }`. Then `derive` again, so the gate measures what ships: on headless, every
152
+ gate fix that says rebuild means deploy, then derive.
153
+ 6. **Open it from BetterCMS.** `set_front_end { siteUrl, framework }` with the deployed URL
154
+ (`preview`); editors' "Preview on site" opens drafts there.
155
+ 7. **Publish**, then get_next_steps until `receipt.complete` is true. Schema and content work is
156
+ the same as on any project ([content-modeling.md](content-modeling.md)).
@@ -28,10 +28,10 @@ Nothing is mounted automatically. Add the routes:
28
28
 
29
29
  ## Revalidation route
30
30
 
31
- `createRevalidateRoute({ secret })` → export `POST`. With `secret` set it verifies
32
- `X-BetterCMS-Signature` against the raw body, then calls `revalidateTag` for each tag in the
33
- webhook. Use the project's webhook secret from the dashboard (the package docs name the env
34
- `BCMS_REVALIDATE_SECRET`; any server env works). Tag your reads with the backend's tag strings
31
+ `createRevalidateRoute()` → export `POST`. It verifies `X-BetterCMS-Signature` against the raw
32
+ body with `secret`, else `BCMS_REVALIDATE_SECRET` (the project's revalidation secret from the
33
+ dashboard), then revalidates every tag (`revalidateTag(tag, "max")`) and every page path
34
+ (`revalidatePath`) the publish changed. Tag your reads with the backend's tag strings
35
35
  ([frameworks.md](frameworks.md) Revalidation).
36
36
 
37
37
  ## Rendering
@@ -164,8 +164,11 @@ Pages Router the page passes on the prop it got: `<HomeView bcmsSections={bcmsSe
164
164
 
165
165
  ## Static export (`output: "export"`)
166
166
 
167
- - Run `bettercms-snapshot` before `next build`, every build (make it the prebuild step). It writes
168
- `bcms-content.json` from `BETTERCMS_API_URL`, `BETTERCMS_API_KEY`, `BETTERCMS_WORKSPACE`.
167
+ - Pull the content before `next build`, every build: `"build": "bettercms pull-content && next build"`
168
+ (what `bettercms init` writes; keyless, it reads the project's published content).
169
+ `bettercms-snapshot` (from `BETTERCMS_API_URL`, `BETTERCMS_API_KEY`, `BETTERCMS_WORKSPACE`) does the
170
+ same with a key. Either writes `bcms-content.json`, and `createBetterCMS()` with no arguments reads
171
+ its workspace and project from it (after `NEXT_PUBLIC_BCMS_*`).
169
172
  - Without the snapshot the reads throw `SNAPSHOT_MISSING`. Why it throws instead of fetching:
170
173
  a live fetch would turn pages into dynamic routes, and a static export silently leaves them out
171
174
  of `out/`.
@@ -180,7 +183,7 @@ Pages Router the page passes on the prop it got: `<HomeView bcmsSections={bcmsSe
180
183
 
181
184
  | Symptom | Cause | Rule |
182
185
  |---|---|---|
183
- | Empty `out/` after `next build` | No snapshot | Run `bettercms-snapshot` first |
186
+ | Empty `out/` after `next build` | No snapshot | Run `bettercms pull-content` first |
184
187
  | Page never updates after publish | Read tags do not match backend tags | Use `bcms:{workspace}:…` tags |
185
188
  | Component instance renders nothing | `components` not passed | `readComponents()` into `BcmsBlocks` |
186
189
  | Canonical missing or wrong only on the self-hosted site | `buildMetadata` without `location`, and no `BETTERCMS_SITE_URL` or a prefixed route | Pass `{ siteUrl, path }` |
@@ -5,10 +5,27 @@ description: "Reading BetterCMS content from Nuxt, SvelteKit, React Router or Vi
5
5
 
6
6
  # Other frameworks
7
7
 
8
- Read this when the repo has neither `astro.config.*` nor `next.config.*`. Only Astro and Next have
9
- adapters; everything else reads through the framework-agnostic core in `@bettercms-ai/sdk`, the
10
- same one both adapters wrap. Shared rules (hosting modes, value shapes, media host, revalidation
11
- tags) are in [frameworks.md](frameworks.md).
8
+ Read this when the repo has neither `astro.config.*` nor `next.config.*`. React (Vite), SvelteKit
9
+ and Nuxt have adapters (§0); anything else reads through the framework-agnostic core in
10
+ `@bettercms-ai/sdk`, the same one every adapter wraps. Shared rules (hosting modes, value shapes,
11
+ media host, revalidation tags) are in [frameworks.md](frameworks.md).
12
+
13
+ ## 0. The adapters
14
+
15
+ | Repo has | Install | Settings | Sections | Drafts |
16
+ |---|---|---|---|---|
17
+ | `vite.config.*` + React | `@bettercms-ai/react` | `<BcmsProvider env={import.meta.env}>`, `VITE_BCMS_*` | `<BcmsBlocks blocks={page.blocks}>` (Next's markup) | the editor's `/api/draft` link, no server; the host rewrites it to the app |
18
+ | `svelte.config.*` (SvelteKit) | `@bettercms-ai/sveltekit` | `createBcms({ env: { ...publicEnv, ...privateEnv } })`, `PUBLIC_BCMS_*` | `bcms.sections(slug, fetch)` in `load`, then `<div use:bcmsSections={data.sections}>{@html sectionsMarkup(data.sections)}</div>` | `src/routes/api/draft/+server.ts`: `GET = ({ request }) => bcms.draftEnable(request)`; `await bcms.client(request)` reads drafts |
19
+ | `nuxt.config.*` | `@bettercms-ai/nuxt` | `createBcms({ env: process.env })` in `server/utils`, `NUXT_PUBLIC_BCMS_*` | a server route returning `bcms.sections(slug)`, then `<div v-bcms-sections v-html="sectionsMarkup(data)">` | `server/routes/api/draft.get.ts`: `bcms.draftEnable(toWebRequest(e))` |
20
+
21
+ - SvelteKit and Nuxt sections are server-rendered by BetterCMS (the hosted renderer's markup, a
22
+ stylesheet scoped to `[data-bcms-rendered="platform"]`, and the form, slider and tab scripts):
23
+ never rebuild them as components, and never paste their HTML into the repo. Field copy outside
24
+ the sections is still read with the client and bound with `data-bcms-field`
25
+ ([bindings.md](bindings.md)).
26
+ - The sections are the PUBLISHED page; draft mode switches the client, not the fragment.
27
+ - Set `BCMS_PROJECT_ID` under the framework's prefix: sections and keyless reads are addressed to it.
28
+ - `BCMS_DRAFT_SECRET` (server-only) signs the draft cookie on SvelteKit and Nuxt; a React SPA has none.
12
29
 
13
30
  ## 1. The client
14
31
 
@@ -25,7 +42,9 @@ export const bcms = createClient({
25
42
  Reading published content needs no key. The platform sets `BCMS_API_URL`, `BCMS_WORKSPACE` and
26
43
  `BCMS_PROJECT_ID` on every build and running server app, and again under each framework's public
27
44
  prefix (`NUXT_PUBLIC_`, `PUBLIC_`, `VITE_`…); the table is in
28
- [external-content.md](external-content.md). Never add them to `.env`.
45
+ [external-content.md](external-content.md). On a site BetterCMS builds, never add them to `.env`.
46
+ A headless front end on its own host sets them itself: in `.env` locally, and on the host by
47
+ `npx @bettercms-ai/cli@latest deploy --to <host>`.
29
48
 
30
49
  | Option | Use |
31
50
  |---|---|
@@ -49,7 +49,7 @@ that reads them.
49
49
  | Hosting mode | Release path | Public proof |
50
50
  |---|---|---|
51
51
  | `managed` or `hybrid` | `deploy_project` or `create_deploy_upload`, then `get_deploy_status` | Cache-busted BetterCMS URL answers 200 with the intended revision |
52
- | `headless` | Publish content in BetterCMS; the frontend deploys through its own host and rebuild or revalidation | The external host's URL answers 200 with the intended content and metadata |
52
+ | `headless` | Publish content in BetterCMS; the frontend deploys through its own host (`npx @bettercms-ai/cli@latest deploy --to <host>` the first time, which registers the rebuild hook) and rebuild or revalidation | The external host's URL answers 200 with the intended content and metadata |
53
53
 
54
54
  A managed site renders static unless the owner switches Hosting to **Run as a server app**, which
55
55
  must happen before an SSR build ships ([frameworks.md](frameworks.md)).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bettercms-ai/mcp",
3
- "version": "0.81.0",
3
+ "version": "0.83.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.32.0",
35
+ "@bettercms-ai/sdk": "^1.35.0",
36
36
  "@modelcontextprotocol/sdk": "^1.30.0",
37
37
  "zod": "^4.3.6"
38
38
  },