@bettercms-ai/mcp 0.67.3 → 0.68.1

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
@@ -2694,6 +2694,8 @@ var NEW_TOOL_TEXT = {
2694
2694
  };
2695
2695
  var DELETE_PAGE_RUNTIME_BUNDLE = " Or, with no pageId, `runtimeBundle`: 'report' lists the duplicate pages derived from a server app's own bundle (`app/` folders); 'apply' retires them and their empty folders.";
2696
2696
  var PARAM_TEXT = {
2697
+ helpText: "shown under the field: its purpose, where it shows, any constraint; never its label or value. Omitted: written for you.",
2698
+ describes: "for editors: what it is and holds; omitted: written for you",
2697
2699
  runtimeBundle: "instead of pageId: the server-app bundle cleanup",
2698
2700
  pageRef: "page id or path ('/de/about'); a bare slug only when one page has it",
2699
2701
  editorSetupForce: "re-run a setup already ready for the live build",
@@ -2962,9 +2964,7 @@ var fieldShape = {
2962
2964
  // builder. They were unreachable from MCP for ONE reason: the OutField/toField whitelist
2963
2965
  // below did not model them, so they were stripped after the model had gone to the trouble of
2964
2966
  // sending them. Declaring them here is what makes the existing panel agent-writable.
2965
- helpText: z.string().max(500).optional().describe(
2966
- "one line of guidance shown UNDER this field in the editor \u2014 what to write here, not what the field is. Write it for the person typing ('Roughly 60 characters; it becomes the browser tab title'), not for a developer."
2967
- ),
2967
+ helpText: z.string().max(500).optional().describe(PARAM_TEXT.helpText),
2968
2968
  showIf: z.object({
2969
2969
  match: z.enum(["all", "any"]),
2970
2970
  rules: z.array(
@@ -3281,6 +3281,7 @@ var withProjectId = (def) => ({
3281
3281
  return currentProject.run(arg("projectId"), () => currentBranch.run(arg("branchId"), () => def.handler(args)));
3282
3282
  }
3283
3283
  });
3284
+ var autoHelpNote = (args) => typeof args.helpText === "string" && args.helpText.trim() ? "" : " No helpText was sent, so the platform wrote it (helpSource: auto); send helpText with update_field to write your own.";
3284
3285
  function buildToolDefs(deps) {
3285
3286
  async function withClient(fn) {
3286
3287
  const token = await deps.auth.getAccessToken();
@@ -3382,7 +3383,7 @@ function buildToolDefs(deps) {
3382
3383
  const createModelInput = z.object({
3383
3384
  name: z.string().min(1).describe("human model name, e.g. 'Blog Post'"),
3384
3385
  slug: slug.describe("url-safe unique slug, e.g. 'blog-post'"),
3385
- description: z.string().optional(),
3386
+ description: z.string().optional().describe(PARAM_TEXT.describes),
3386
3387
  urlPattern: urlPatternArg,
3387
3388
  kind: z.enum(["model", "block"]).optional().describe(
3388
3389
  "'model' (default) = a collection with its own entries. 'block' = a type that exists only to be stacked inside another model's 'modular' field \u2014 it holds no entries, and create_content_entry against it is refused. Create blocks FIRST, then the model whose modular field lists their slugs. Cannot be changed later."
@@ -3614,7 +3615,7 @@ function buildToolDefs(deps) {
3614
3615
  z.object({ type: z.literal("set-section-state"), ...sectionTarget, state: z.enum(["inherit", "override-content", "customize-structure", "disable", "reset"]) }),
3615
3616
  z.object({ type: z.literal("add-section"), name: z.string().min(1), icon: layoutIcon, zone: z.enum(["before_page", "after_page"]) }),
3616
3617
  z.object({ type: z.literal("restore-section"), section: z.object({ id: z.string().min(1) }).passthrough(), data: z.object({ fields: z.record(z.string(), z.unknown()) }), placement: z.object({ zone: z.enum(["above-page-content", "below-page-content"]), localOrderKey: z.string().min(1), afterSectionId: z.string().optional(), beforeSectionId: z.string().optional() }).optional() }),
3617
- z.object({ type: z.literal("update-section"), ...sectionTarget, name: z.string().min(1).optional(), icon: layoutIcon.optional(), slug: z.string().min(1).optional(), zone: z.enum(["above-page-content", "below-page-content"]).optional(), orderKey: z.string().min(1).optional() }),
3618
+ z.object({ type: z.literal("update-section"), ...sectionTarget, name: z.string().min(1).optional(), icon: layoutIcon.optional(), slug: z.string().min(1).optional(), zone: z.enum(["above-page-content", "below-page-content"]).optional(), orderKey: z.string().min(1).optional(), description: z.string().optional() }),
3618
3619
  z.object({ type: z.literal("remove-section"), ...sectionTarget }),
3619
3620
  z.object({ type: z.literal("move-section"), ...sectionTarget, afterId: z.string().nullable().optional(), beforeId: z.string().nullable().optional() }),
3620
3621
  z.object({ type: z.literal("add-field"), ...sectionTarget, parentFieldId: z.string().optional(), fieldType: z.enum(["text", "longtext", "richtext", "number", "toggle", "date", "image", "file", "link", "email", "phone", "select", "color", "json", "reference", "multi-reference", "group", "repeater"]).optional(), field: z.object({ id: z.string(), slug: z.string(), label: z.string(), type: z.string() }).passthrough().optional() }),
@@ -3653,19 +3654,19 @@ function buildToolDefs(deps) {
3653
3654
  "conversion"
3654
3655
  ]);
3655
3656
  const sectionType = z.string().min(1).max(64).describe(
3656
- "section family, e.g. 'Hero' \u2014 set this with a library category to make the component selectable in the page editor's 'Add a section' picker. Components sharing a sectionType are layout variants of one section."
3657
+ "section family, e.g. 'Hero' \u2014 set this with a library category to make the component selectable in the page editor's 'Add a section' picker. Components sharing one are layout variants of one section."
3657
3658
  );
3658
3659
  const allowedOn = z.array(z.string().regex(/^(\*|slug:[a-z0-9-]+|type:(singleton|dynamic|template))$/)).max(100).describe("placement allowlist: *, slug:<page-slug>, and/or type:singleton|dynamic|template");
3659
3660
  const createComponentInput = z.object({
3660
3661
  name: z.string().min(1),
3661
3662
  slug: slug.describe("url-safe unique slug (lowercase letters/numbers/hyphens)"),
3662
3663
  category: componentCategory.optional().describe("defaults to 'custom'"),
3663
- group: z.string().min(1).max(100).optional().describe("the folder editors see in the dashboard's Components tab, addressed by NAME (e.g. 'Sections', 'Navigation', 'Forms'). Resolved against this project's Groups and CREATED when missing, so a whole-site run can file everything without looking any id up first. Separate from `category`, which is the picker tab."),
3664
+ group: z.string().min(1).max(100).optional().describe("the folder editors see in the dashboard's Components tab, by NAME (e.g. 'Sections', 'Forms'); CREATED when missing. Separate from `category`, the picker tab."),
3664
3665
  sectionType: sectionType.optional(),
3665
3666
  variantOf: z.string().min(1).max(255).optional().describe("id/slug of the component this is a VARIANT of: joins or starts its family (422 if its shared inputs are missing)"),
3666
3667
  projectId: z.string().nullable().optional().describe("owning project id; null creates a workspace-wide global component"),
3667
3668
  allowedOn: allowedOn.optional(),
3668
- description: z.string().optional(),
3669
+ description: z.string().optional().describe(PARAM_TEXT.describes),
3669
3670
  blockJson: z.array(blockObject).default([]).describe("the component's block tree"),
3670
3671
  props: z.array(componentPropObject).default([]).describe(
3671
3672
  "overridable fields. DECLARE A PROP \u2014 WITH A `target` \u2014 for every string, link and image a marketer will ever touch: only the leaves a prop targets are click-to-edit. Each one may carry `ui` to say how its control presents in the inspector \u2014 a collapsed group, what titles and thumbnails a table's rows, whether that table's rows can be dragged at all."
@@ -5179,7 +5180,7 @@ ${notes.join("\n")}` : summary, created);
5179
5180
  body: JSON.stringify(input2),
5180
5181
  headers: confirmedHeaders(true)
5181
5182
  })).data : await client.addPageFields(args.pageId, input2);
5182
- return ok(`Added field '${args.key}' to page '${page.title}'. Page now has ${page.fields.length} field(s).`, page);
5183
+ return ok(`Added field '${args.key}' to page '${page.title}'. Page now has ${page.fields.length} field(s).${autoHelpNote(args)}`, page);
5183
5184
  }
5184
5185
  const modelId = args.modelId;
5185
5186
  const model = await client.getModel(modelId);
@@ -5208,7 +5209,7 @@ ${notes.join("\n")}` : summary, created);
5208
5209
  headers: { ...opts ? { "If-Match": `W/"${opts.ifMatch}"` } : {}, ...confirmedHeaders(true) }
5209
5210
  })).data : opts ? await client.updateModel(modelId, input, opts) : await client.updateModel(modelId, input);
5210
5211
  return ok(
5211
- `Added field '${args.key}' to '${updated.name}'${args.after ? ` after '${args.after}'` : ""}. Model now has ${updated.fields.length} field(s).`,
5212
+ `Added field '${args.key}' to '${updated.name}'${args.after ? ` after '${args.after}'` : ""}. Model now has ${updated.fields.length} field(s).${autoHelpNote(args)}`,
5212
5213
  updated
5213
5214
  );
5214
5215
  })
@@ -6711,6 +6712,11 @@ conversion is yours to write.
6711
6712
  5. Codemod the templates. Read each value from the CMS (\`@bettercms-ai/astro\` /
6712
6713
  \`@bettercms-ai/next\` client, or the \`bcms-content.json\` build snapshot), KEEP the in-code
6713
6714
  copy as the fallback, and declare the binding on the element that already renders it.
6715
+ Read the snapshot ONLY through a supported reader: the helper \`npx @bettercms-ai/convert\`
6716
+ writes (\`bcms\`, \`bcmsRows\`, \`bcmsImage\`) or \`publishedEntry(slug)\` from
6717
+ \`@bettercms-ai/sdk\`. Never hand-write a parser of the file: a row holds its values under
6718
+ \`data\`, so \`row.title\` is undefined and the page renders its fallback on every publish. An
6719
+ editable image is \`bcmsImage(page, path, importedImage)\`, never the bare import with a stamp.
6714
6720
  Prefer schema-derived bindings \u2014 the TypeGen analogue:
6715
6721
  \`npx @bettercms-ai/codegen --bindings-out src/bettercms.bindings.generated.ts\`, then spread
6716
6722
  \`{...bcms.home.hero.title}\` / \`{...bcms.blog.features.$(i)}\`. The hand form is
@@ -6745,7 +6751,7 @@ conversion is yours to write.
6745
6751
  \`bcms-content.json\` into the repo root before \`build\`, with a key it mints per build: no
6746
6752
  repo secret feeds it. (The secret a connect sets, \`BCMS_API_KEY_<RUNG>\` such as
6747
6753
  \`BCMS_API_KEY_PROD\`, is the validation workflows' key; they fall back to the shared
6748
- \`BCMS_API_KEY\` only for a repo whose last connect predates it.) Read that file, with in-code
6754
+ \`BCMS_API_KEY\` only for a repo whose last connect predates it.) Read that file (step 5's readers), with in-code
6749
6755
  fallbacks so a local or CI build without it still renders.
6750
6756
  - ARCHIVE (\`deploy_project\`, inline or by \`uploadKey\`): the sandbox build runs with no env
6751
6757
  and no network content step, by design. So the archive MUST SHIP its own