@bettercms-ai/mcp 0.56.3 → 0.57.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
@@ -2449,6 +2449,11 @@ var GIT_TEXT = {
2449
2449
  create_github_pull_request: "Open a pull request on the connected repository. `head` is the branch you pushed; `base` defaults to the build branch, so merging it is what ships the change. Use this instead of pushing straight to the build branch whenever a human should review first, or when that branch is protected. Returns the number and the url to hand the user.",
2450
2450
  list_github_pull_requests: "Pull requests on the connected repository: number, title, state, whether it merged, draft, head, base and url. `state` is 'open' (the default), 'closed' or 'all'. Poll it to find out whether the pull request you opened has landed."
2451
2451
  };
2452
+ var HOSTING_TEXT = {
2453
+ list_hosting_connections: "The Vercel, Netlify and Cloudflare accounts connected to this workspace (id, provider, accountLabel, status; never a token), plus `connectUrl`, the dashboard page where a person connects one. Call it before deploy_to_host. If the provider the user wants has no connection with status `ok`, give the user `connectUrl` to connect it in their browser and stop until they have: you never handle OAuth or a host token yourself.",
2454
+ deploy_to_host: "Deploy this project to the user's own Vercel or Netlify account through their connection. BetterCMS builds the site and the host serves that build, so there is no host CLI to run, no token to paste and no vercel.json rewrite to write; forms keep working because they post to the BetterCMS API. The first call creates the site on the host and makes it the project's host; a later call for the same provider redeploys that site (`redeployed: true`) instead of creating another. `connectionId` (from list_hosting_connections) defaults to the newest working one, `siteName` to the project's handle. A refusal carries a `code` and a `fixUrl` (not_connected, reconnect, pick_existing_site, grant_required, ADMIN_REQUIRED): each is fixed by the user at `fixUrl`, so relay it and stop. `deploymentQueued: false` means the project has no live build yet: deploy_project first, and that release reaches the host on its own. Never run the Vercel or Netlify CLI while a connection exists. Then poll get_host_deploy_status. Needs artifact:write, like deploy_project.",
2455
+ get_host_deploy_status: "The connected project's recent deployments to Vercel, Netlify or Cloudflare, newest first: state (queued|deploying|live|failed), providerUrl, error, sha and targetName. Poll it after deploy_to_host until the newest one is `live`, whose `providerUrl` is the live URL to hand the user, or `failed`, whose `error` you read back to them."
2456
+ };
2452
2457
  function ok(summary, data) {
2453
2458
  return {
2454
2459
  content: [
@@ -2511,7 +2516,10 @@ function refusalDetail(err) {
2511
2516
  const lines = [
2512
2517
  code ? `code: ${code}` : "",
2513
2518
  body.readiness !== void 0 ? `readiness: ${JSON.stringify(body.readiness)}` : "",
2514
- body.issues !== void 0 ? `issues: ${JSON.stringify(body.issues)}` : ""
2519
+ body.issues !== void 0 ? `issues: ${JSON.stringify(body.issues)}` : "",
2520
+ // The link a person follows to fix it (connect a host, create a Vercel project): an agent
2521
+ // that only sees the sentence has nothing to hand the user.
2522
+ typeof body.fixUrl === "string" ? `fixUrl: ${body.fixUrl}` : ""
2515
2523
  ].filter(Boolean);
2516
2524
  return lines.length ? `
2517
2525
  ${lines.join("\n")}` : "";
@@ -3550,7 +3558,7 @@ ${d.outline}` : "Proposed Content structure.", d);
3550
3558
  def(
3551
3559
  "submit_conversion_receipt",
3552
3560
  "Record what the conversion codemod could and could not do",
3553
- "Hand BetterCMS the codemod's own account of a run, so the meter can say WHY a path is undeclared. Submit the receipt `npx @bettercms-ai/convert` wrote (`--receipt out.json`) for the SAME `briefDigest` get_conversion_brief { complete: true } returned: `{ briefDigest, receipt }`, where the receipt carries `paths: { declared, rewritten, alreadyDeclared, pending[{ route, scope, path, kind, file, reason, message, fix }] }`. `paths.declared` must equal rewritten + alreadyDeclared + pending.length, and each pending `reason` is one of the converter's own (IN_EXPRESSION, AMBIGUOUS_LITERAL, REPEATER_FIXED_LENGTH, PARSE_ERROR, \u2026) \u2014 a path with no receipt row simply reads `not-declared`. \u{1F534} A RECEIPT WITH PENDING PATHS IS A PROGRESS REPORT, NOT A FINISH LINE: The response answers `complete` and `pendingTotal`, and echoes the first 40 pending rows with their `fix` \u2014 `{ action, file, line, col?, snippet, why?, kind? }`. `action` is `wrap-span`, `declare-attr`, `bind-expression`, `declare-richtext`, `bind-data` or `manual`; each row's `snippet` and `why` say what to change there. On a `declare-richtext` with `kind: document`, bind the ONE element wrapping every block of the Body, never the paragraph holding its first. Apply every fix, rerun, resubmit. `complete: true` is the only receipt that ends a conversion; do not report a site converted on less. It is a RECORD, not a release: it changes nothing about the site, and the meter picks it up on the next get_binding_report after the next deploy. A 404 `unknown-brief` means that digest was never issued here. \u{1F534} SUBMIT THE `--forms` RECEIPT TOO, AS A SECOND CALL: A `npx @bettercms-ai/convert --forms` run writes a receipt whose `paths` are all zero and whose account is in a `forms` block. Write it to its own file (`--receipt forms-receipt.json`) and submit it under the same `briefDigest`: stored beside the binding receipt, never counted in coverage. Then read `forms.pending` and publish every form it names. Requires artifact:write, the same authority as set_binding_mode.",
3561
+ "Hand BetterCMS the codemod's own account of a run, so the meter can say WHY a path is undeclared. Submit the receipt `npx @bettercms-ai/convert` wrote (`--receipt out.json`) for the SAME `briefDigest` get_conversion_brief { complete: true } returned: `{ briefDigest, receipt }`, where the receipt carries `paths: { declared, rewritten, alreadyDeclared, pending[{ route, scope, path, kind, file, reason, message, fix }] }`. `paths.declared` must equal rewritten + alreadyDeclared + pending.length, and each pending `reason` is one of the converter's own (IN_EXPRESSION, AMBIGUOUS_LITERAL, REPEATER_FIXED_LENGTH, PARSE_ERROR, \u2026) \u2014 a path with no receipt row simply reads `not-declared`. \u{1F534} A RECEIPT WITH PENDING PATHS IS A PROGRESS REPORT, NOT A FINISH LINE: The response answers `complete` and `pendingTotal`, and echoes the first 40 pending rows with their `fix` \u2014 `{ action, file, line, col?, snippet, why?, kind? }`. `action` is `wrap-span`, `declare-attr`, `bind-expression`, `declare-richtext`, `bind-data` or `manual`; each row's `snippet` and `why` say what to change there. On a `declare-richtext` with `kind: document`, bind the ONE element wrapping every block of the Body, never the paragraph holding its first. Apply every fix, rerun, resubmit. `complete: true` is the only receipt that ends a conversion; do not report a site converted on less. It is a RECORD, not a release: it changes nothing about the site, and the meter picks it up on the next get_binding_report after the next deploy. A 404 `unknown-brief` means that digest was never issued here. \u{1F534} SUBMIT THE `--forms` RECEIPT TOO, AS A SECOND CALL: A `npx @bettercms-ai/convert --forms` run writes a receipt whose `paths` are all zero and whose account is in a `forms` block. Write it to its own file (`--receipt forms-receipt.json`) and submit it under the same `briefDigest`: stored beside the binding receipt, never counted in coverage. Then read `forms.pending` and publish every form it names. \u{1F534} THE SITE'S DESIGN IS NOT YOURS TO CHANGE: run `npx @bettercms-ai/convert --verify-styles --base <commit before the conversion> --root .` and include its `styleEdits` in the receipt. A receipt listing any changed class, className or style is refused with 409 `style-edits` and every token \u2014 restore them; the CMS keeps inline markup exactly as written. Requires artifact:write, the same authority as set_binding_mode.",
3554
3562
  z.object({
3555
3563
  briefDigest: z.string().min(1).describe("The `briefDigest` get_conversion_brief { complete: true } returned. Must match the receipt's own."),
3556
3564
  receipt: z.record(z.string(), z.unknown()).describe("The receipt `npx @bettercms-ai/convert --receipt out.json` wrote, verbatim.")
@@ -4149,6 +4157,34 @@ ${res.warnings.join("\n")}` : summary, res.data);
4149
4157
  "Get the connected project's deploy/build status: state (idle|queued|building|categorizing|failed), whether it's publishing, the live commit sha, when it went live, and any build error. `categorizing` means the bytes are live but the import's setup lanes (content, pages, bindings) are still running, so `publishing` is still true and the site is not ready to edit yet. Poll this after deploy_project until state is idle with your sha live. Also returns `canvas.lane` (the editor's live-preview lane for this site: `bridge` / `draft-route` / `none`) and `editorUrl`, the visual editor to hand the user.",
4150
4158
  z.object({}).shape,
4151
4159
  async (c) => ok("Deploy status.", await data(c, "GET", `/management/projects/deploy-status`))
4160
+ ),
4161
+ // ── Deploy to the user's own host (parity with remote /mcp) ──
4162
+ // A person connects Vercel or Netlify in the dashboard; nothing here touches OAuth or a host
4163
+ // token. deploy_to_host takes artifact:write like deploy_project. @see management/deploy-targets.ts
4164
+ def(
4165
+ "list_hosting_connections",
4166
+ "List hosting connections",
4167
+ HOSTING_TEXT.list_hosting_connections,
4168
+ z.object({}).shape,
4169
+ async (c) => ok("Hosting connections.", await data(c, "GET", `/management/hosting/connections`))
4170
+ ),
4171
+ def(
4172
+ "deploy_to_host",
4173
+ "Deploy to Vercel or Netlify",
4174
+ HOSTING_TEXT.deploy_to_host,
4175
+ z.object({
4176
+ provider: z.enum(["vercel", "netlify"]),
4177
+ connectionId: z.string().min(1).optional().describe("a connection id from list_hosting_connections; defaults to the newest working one"),
4178
+ siteName: z.string().min(1).max(63).optional().describe("lowercase letters, numbers and hyphens; defaults to the project's handle")
4179
+ }).shape,
4180
+ async (c, a) => ok("Host deploy.", await data(c, "POST", `/management/deploy-targets/provision`, { provider: a.provider, connectionId: a.connectionId, siteName: a.siteName }))
4181
+ ),
4182
+ def(
4183
+ "get_host_deploy_status",
4184
+ "Get host deploy status",
4185
+ HOSTING_TEXT.get_host_deploy_status,
4186
+ z.object({}).shape,
4187
+ async (c) => ok("Host deployments.", await data(c, "GET", `/management/hosting/external-deployments`))
4152
4188
  )
4153
4189
  ];
4154
4190
  }
@@ -5915,7 +5951,7 @@ disagrees with the array the moment anyone drags a row in the builder.
5915
5951
  var STRUCTURE_RULE = `### Page structure (non-negotiable)
5916
5952
  ${SECTION_DOCTRINE}`;
5917
5953
  var CONVERT_SITE_TEXT = (projectId) => `Convert my site so every visible element is editable in BetterCMS${projectId ? `, project ${projectId}` : ""}.
5918
- Do not stop at the plan or at a partial receipt \u2014 finish the whole site.
5954
+ Do not stop at the plan or at a partial receipt \u2014 finish the whole site, never editing a class or style.
5919
5955
 
5920
5956
  1. \`list_projects\`; pass \`projectId\`${projectId ? ` (${projectId})` : ""} on every project tool. Not listed = the grant is on
5921
5957
  another workspace: have the user re-authenticate there. Never convert a different project.
@@ -5931,14 +5967,14 @@ Do not stop at the plan or at a partial receipt \u2014 finish the whole site.
5931
5967
  framework's bcmsField helper; \`declare-richtext\` bind the container with the richtext helper;
5932
5968
  \`bind-data\` bind the field in the data file it names; \`manual\` do what its \`why\` says. A row
5933
5969
  with no \`fix\` is still yours. Then rerun step 4 until \`paths.pending\` is EMPTY.
5934
- 6. \`submit_conversion_receipt { briefDigest, receipt }\`. It must answer \`complete: true\`; if it
5935
- does not, go back to step 5 \u2014 that is the only receipt that ends a conversion.
5936
- 7. Deploy (push, or \`deploy_project\`) and poll \`get_deploy_status\` until the release is live.
5937
- 8. \`get_binding_report\` must read \`unmatched\` 0 and \`unaddressable\` under 2% of each route's
5938
- visible text. Fix what it names in the SOURCE, deploy, re-read (playbook section 11).
5939
- 9. \`set_binding_mode { declaredBindings: true }\`, deploy AGAIN and poll \u2014 the mode applies to the
5940
- NEXT release \u2014 then confirm \`get_binding_report\` reads mode "declared", unmatched 0, bound
5941
- above 0, \`coverage.pending\` empty. That report is your final answer to me.`;
5970
+ 6. \`npx @bettercms-ai/convert --verify-styles --base <commit before step 4>\`, restore what it lists, then
5971
+ \`submit_conversion_receipt { briefDigest, receipt }\` with its \`styleEdits\`: only \`complete: true\` ends it (else step 5).
5972
+ 7. Deploy the working tree (\`create_deploy_upload\` + \`deploy_from_upload\`); poll \`get_deploy_status\` until live. No git yet.
5973
+ 8. \`get_binding_report\`: \`unmatched\` 0, \`unaddressable\` under 2% per route; fix the SOURCE, deploy, re-read (playbook \xA711).
5974
+ 9. \`set_binding_mode { declaredBindings: true }\`, deploy AGAIN and poll, then confirm \`get_binding_report\`
5975
+ reads mode "declared", unmatched 0, bound above 0, \`coverage.pending\` empty. Send me that report.
5976
+ 10. Ask me whether to commit and push to my repository, commit only, or leave it uncommitted (unpushed, the
5977
+ next rebuild from my repo drops it). Touch git only as I answer.`;
5942
5978
  var SCHEMA_PROPOSAL_FLOW = `### Whole-project design (confirm-first) \u2192 \`create_component\` / \`create_page\` / \`create_content_model\`
5943
5979
  Design the WHOLE project from its brief or its code, and **confirm the shape with the user
5944
5980
  BEFORE creating anything**. Never silently guess.