@bettercms-ai/mcp 0.74.0 → 0.76.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 +39 -8
- package/dist/index.js.map +1 -1
- package/dist/kit-catalog.json +53 -53
- package/dist/skills/bettercms/SKILL.md +7 -2
- package/dist/skills/bettercms/references/componentize.md +5 -0
- package/dist/skills/bettercms/references/finish-site.md +5 -1
- package/dist/skills/bettercms/references/getting-started.md +2 -0
- package/dist/skills/bettercms/references/kit-catalog.md +53 -53
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2936,6 +2936,19 @@ function listsTool(name, sets) {
|
|
|
2936
2936
|
if (ABM_TOOLS.has(name)) return sets.has("abm");
|
|
2937
2937
|
return !sets.has("core") || !LISTING_TAIL.has(name);
|
|
2938
2938
|
}
|
|
2939
|
+
var ALWAYS_LOADED = /* @__PURE__ */ new Set([
|
|
2940
|
+
"get_next_steps",
|
|
2941
|
+
"editor_setup",
|
|
2942
|
+
"get_deploy_status",
|
|
2943
|
+
"get_conversion_brief",
|
|
2944
|
+
"get_binding_report",
|
|
2945
|
+
"list_projects",
|
|
2946
|
+
"list_pages",
|
|
2947
|
+
"get_page",
|
|
2948
|
+
"list_content_models"
|
|
2949
|
+
]);
|
|
2950
|
+
var toolMeta = (name) => ALWAYS_LOADED.has(name) ? { "anthropic/alwaysLoad": true } : void 0;
|
|
2951
|
+
var TOOL_LOADING_INSTRUCTION = `TOOL SCHEMAS: a client with tool search lists most BetterCMS tools by name only. Before the first call to a tool whose schema you have not been shown, load it with the client's tool search, and load every tool the task needs in ONE search (for a renaming pass: update_field, update_content_model, update_component, list_components). A call made without the schema is refused by the client before it reaches BetterCMS ("Invalid tool parameters"): that is this, never a BetterCMS error, and never a reason to change the arguments.`;
|
|
2939
2952
|
|
|
2940
2953
|
// src/tool-text.ts
|
|
2941
2954
|
var ADD_FIELD_DESCRIPTION = "Append ONE field to a content model (`modelId`) or to a page's schema (`pageId`) \u2014 additive, never overwrites; a key already in use is refused. For a section/zone add ONE 'group' (fixed block) or 'repeater' (repeating list) carrying its child `fields`, not the zone's inner fields as separate top-level fields. Pass `after` to insert it after an existing top-level field instead of at the end; `ui`, `helpText`, `showIf`, `placement` and `fieldsetId` declare how it presents in the editor \u2014 read them back with get_content_model or get_page. On a page a 'group' is stored as type 'array' with `config.zones.nonRepeatable`: entry and delivery data carry `<key>.nonRepeatable.<sub>`, while data-bcms-field paths are `<key>.<sub>`. Schema only \u2014 never places a section; use componentize_sections to turn a group into a section. `multiselect` stores a string[] of `config.options`; `icon` is a BetterCMS icon name or an uploaded icon. To change a field that exists, use update_field. V2 keys (object, openMode, advanced, helpMore, recommendedMax, config.scale): \xA714 of bettercms://playbook/schema.";
|
|
@@ -2991,6 +3004,8 @@ var PARAM_TEXT = {
|
|
|
2991
3004
|
fromBuiltin: "a builtin:* id from list_components: copies it into the project; fields you send win",
|
|
2992
3005
|
componentPreview: "a slot row's look: {title?, subtitle?, media?} TOP-LEVEL prop keys, fallback?, icon?; null clears. \xA73 of bettercms://playbook/schema",
|
|
2993
3006
|
componentIcon: "the block icon, a dock icon name; null clears. \xA73 of bettercms://playbook/schema",
|
|
3007
|
+
componentTabs: "/kit tabs no top-level prop sits in: [{id,label,icon?,order?}]. \xA73 of bettercms://playbook/schema",
|
|
3008
|
+
componentKitEntry: "the bettercms://kit entry id its V2 dock UI follows (422 kit_entry_unknown otherwise); null clears. The setup gate's dock row needs one",
|
|
2994
3009
|
describes: "for editors: what it is and holds; omitted: written for you",
|
|
2995
3010
|
runtimeBundle: "instead of pageId: the server-app bundle cleanup",
|
|
2996
3011
|
pageRef: "page id or path ('/de/about'); a bare slug only when one page has it",
|
|
@@ -3565,6 +3580,7 @@ function publishedLines(outcome, summary) {
|
|
|
3565
3580
|
if (outcome.status !== "live") return [`${summary} Publish status: ${outcome.status}.`];
|
|
3566
3581
|
return [`${summary} Published: ${outcome.live > 0 ? `${outcome.live} ${outcome.live === 1 ? "value" : "values"} live on the served page` : "live"}.`];
|
|
3567
3582
|
}
|
|
3583
|
+
var DOCK_UI_NEXT = "Give every prop of this component its V2 ui (control, tab, group, collapsed, layout, preview, itemLabel, icon) by playbook \xA714 and the closest bettercms://kit entry: get_component, then ONE update_component with propUi, propTab and propHelpText (the field-ui prompt). The setup gate's dock row stays open until it is done.";
|
|
3568
3584
|
function ok(summary, data) {
|
|
3569
3585
|
return {
|
|
3570
3586
|
content: [
|
|
@@ -4098,6 +4114,8 @@ function buildToolDefs(deps) {
|
|
|
4098
4114
|
description: z.string().optional().describe(PARAM_TEXT.describes),
|
|
4099
4115
|
preview: componentPreviewArg.optional(),
|
|
4100
4116
|
icon: dockIconArg.nullable().optional().describe(PARAM_TEXT.componentIcon),
|
|
4117
|
+
tabs: z.array(z.object({ id: z.string(), label: z.string(), icon: z.string().optional(), order: z.number().int().optional() })).nullable().optional().describe(PARAM_TEXT.componentTabs),
|
|
4118
|
+
kitEntry: z.string().min(1).max(100).nullable().optional().describe(PARAM_TEXT.componentKitEntry),
|
|
4101
4119
|
blockJson: z.array(blockObject).default([]).describe("the component's block tree"),
|
|
4102
4120
|
props: z.array(componentPropObject).default([]).describe(
|
|
4103
4121
|
"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. 'button' is ONE unit {label, href, variant?, size?, arrow?, icon?, newTab?} on a button block's 'props', never a text prop plus a url prop. 'link' an href, 'date' YYYY-MM-DD, 'color' a hex, 'file' {url, name?}, 'icon' an icon name or an image, 'multiselect' a string[] from config.options (required). " + SLOT_MULTIPLE_NOTE + " " + PROP_V2_NOTE
|
|
@@ -4116,6 +4134,7 @@ function buildToolDefs(deps) {
|
|
|
4116
4134
|
description: z.string().optional(),
|
|
4117
4135
|
preview: componentPreviewArg.optional(),
|
|
4118
4136
|
icon: dockIconArg.nullable().optional().describe(PARAM_TEXT.componentIcon),
|
|
4137
|
+
kitEntry: z.string().min(1).max(100).nullable().optional().describe(PARAM_TEXT.componentKitEntry),
|
|
4119
4138
|
blockJson: z.array(blockObject).optional().describe("REPLACES the block tree"),
|
|
4120
4139
|
props: z.array(componentPropObject).optional().describe(
|
|
4121
4140
|
"REPLACES the prop array \u2014 include every prop you want to keep, WITH its stored `ui`, or that prop's authoring chrome is dropped along with the prop. Read get_component first."
|
|
@@ -6236,10 +6255,12 @@ ${lines.join("\n")}`, found);
|
|
|
6236
6255
|
},
|
|
6237
6256
|
handler: guard(
|
|
6238
6257
|
async (args) => withClient(async (client) => {
|
|
6239
|
-
const
|
|
6258
|
+
const res = await client.extractComponent(args);
|
|
6259
|
+
const { component, replaced } = res;
|
|
6260
|
+
const kit = res.kit;
|
|
6240
6261
|
return ok(
|
|
6241
6262
|
`Created component '${component.name}' (id ${component.id}) and repointed ${replaced} sections to it. Page drafts changed; publish those pages to make it live.`,
|
|
6242
|
-
component
|
|
6263
|
+
{ ...component, ...kit ? { kit } : {}, nextAction: DOCK_UI_NEXT }
|
|
6243
6264
|
);
|
|
6244
6265
|
})
|
|
6245
6266
|
)
|
|
@@ -6466,7 +6487,8 @@ function registerTools(server, deps, tools = process.env.BETTERCMS_MCP_TOOLS) {
|
|
|
6466
6487
|
};
|
|
6467
6488
|
const sets = toolsetsOf(tools);
|
|
6468
6489
|
for (const def of buildToolDefs(withElicit).filter((d) => listsTool(d.name, sets))) {
|
|
6469
|
-
|
|
6490
|
+
const _meta = toolMeta(def.name);
|
|
6491
|
+
server.registerTool(def.name, _meta ? { ...def.config, _meta } : def.config, def.handler);
|
|
6470
6492
|
}
|
|
6471
6493
|
}
|
|
6472
6494
|
|
|
@@ -6499,6 +6521,10 @@ DERIVED by \`editor_setup\`, where the componentize lane in \xA710 reuses every
|
|
|
6499
6521
|
mcp notes and avoid its pitfalls (tag rules below).
|
|
6500
6522
|
Its props carry NO target: give each one a target
|
|
6501
6523
|
into YOUR blockJson (\xA73) before create_component
|
|
6524
|
+
Every component names its kitEntry
|
|
6525
|
+
(bettercms://kit/entry/{id}); its ui follows that
|
|
6526
|
+
entry; override the match when it's wrong; exempt
|
|
6527
|
+
with no-kit-match only with a note
|
|
6502
6528
|
Repeated visual region on a page? -> a COMPONENT with sectionType (a Section)
|
|
6503
6529
|
...and it comes in more than one look? -> siblings sharing that sectionType = VARIANTS
|
|
6504
6530
|
A stack of mixed, reorderable content? -> kind:'block' models + a \`modular\` field
|
|
@@ -6579,8 +6605,9 @@ dashboard's prop editor renders. \`helpText\` is three-state: a string is yours,
|
|
|
6579
6605
|
purpose (send it for a prop whose /kit schema has no description; nothing refills it), and omitted
|
|
6580
6606
|
lets the platform write one. They are HINTS: nothing on the write path refuses a placement
|
|
6581
6607
|
that leaves a required prop empty, so \`get_site_composition\` is what reports one.
|
|
6582
|
-
A /kit schema's document-level \`preview\` \u2192 the component's \`preview\`: how ONE instance reads as a ROW in a parent's slot list. {title?, subtitle?, media?} each name a TOP-LEVEL prop key (422 \`preview_key_unknown\` otherwise), \`fallback?\` is a literal row label (\u2264 120) and \`icon?\` a dock icon name; null clears it.
|
|
6608
|
+
A /kit schema's document-level \`preview\` \u2192 the component's \`preview\`: how ONE instance reads as a ROW in a parent's slot list. {title?, subtitle?, media?} each name a TOP-LEVEL prop key or a dotted path into one (\`slides.0.heading\`; 422 \`preview_key_unknown\` otherwise), \`fallback?\` is a literal row label (\u2264 120) and \`icon?\` a dock icon name; null clears it.
|
|
6583
6609
|
A /kit schema's document-level \`icon\` \u2192 the component's \`icon\` (a dock icon name from bettercms://icons/dock, null clears): the block icon on the root crumb, slot rows and slot child frames.
|
|
6610
|
+
A /kit schema's \`groups\` \u2192 each prop's \`ui.tab\` (defined on the first top-level prop in it; a nested field's \`group\` \u2192 its \`ui.tab: {id}\`); a group NO top-level prop sits in \u2192 the component's \`tabs\` ([{id, label, icon?, order?}], 422 \`component_tab_invalid\` for a prop's own tab). A slot's \`of.allow\` \u2192 \`config.allow\` (block names); a scalar list's item settings (\`of[0]\` validation, description, localized) \u2192 \`config.item\`.
|
|
6584
6611
|
|
|
6585
6612
|
**Prop types, one rule each.** 'select' REQUIRES \`config.options\` (a default must be one of them). 'group'/'table'/'object' REQUIRE \`config.fields\` (\`[{key,label,type,options?,fields?}]\`); a group writes an object, a table an array of them (\`config.mode: 'data'\`: ONE V2 table value, no fields). 'number' takes optional \`config.min/max/step\`. 'slot' holds ONE nested component instance: \`target.path: 'props'\` on an EMPTY component block (\`props.componentId: ""\`), \`config.componentIds\` restricts fills, defaultValue \`{componentId, overrides?}\`. 'form' holds ONE Form document: \`target.path: 'props'\` on an EMPTY form block (\`props.formId: ""\`), defaultValue \`{formId}\`, never the hydrated \`{formId, form}\` (400); do NOT re-declare the form's FIELDS as props, the Form document owns them. 'button' is ONE unit \`{label, href, variant?, size?, arrow?, icon?, newTab?}\` on a button block's 'props', never a text prop plus a url prop. 'link' an href, 'date' YYYY-MM-DD, 'color' a hex, 'file' \`{url, name?}\`, 'icon' an icon name or an image, 'multiselect' a string[] from \`config.options\` (required).
|
|
6586
6613
|
|
|
@@ -7652,6 +7679,7 @@ disagrees with the array the moment anyone drags a row in the builder.
|
|
|
7652
7679
|
// src/prompts.ts
|
|
7653
7680
|
var STRUCTURE_RULE = `### Page structure (non-negotiable)
|
|
7654
7681
|
${SECTION_DOCTRINE}`;
|
|
7682
|
+
var DOCK_UI_STEP = "EDITOR CHROME: for every component, run the field-ui lane (the `field-ui` prompt, playbook \xA714): `get_component`, then one `update_component` with `propUi`, `propTab` and `propHelpText` giving each prop its V2 ui (control, tab, group, collapsed, layout, preview, itemLabel, icon) after the closest `bettercms://kit` entry. The gate's `dock` row stays open until each has it, or a true `default-controls` exception.";
|
|
7655
7683
|
var CONVERT_SITE_TEXT = (projectId) => `Convert my site so every visible element is editable in BetterCMS${projectId ? `, project ${projectId}` : ""}.
|
|
7656
7684
|
Do not stop at the plan or at a partial receipt \u2014 finish the whole site, never editing a class or style.
|
|
7657
7685
|
|
|
@@ -7682,8 +7710,8 @@ Do not stop at the plan or at a partial receipt \u2014 finish the whole site, ne
|
|
|
7682
7710
|
do the same with \`npx @bettercms-ai/convert@latest --upgrade-element-styles --root . --dry-run\`. Run \`--verify-styles\` again
|
|
7683
7711
|
and restore what it lists (the marker, and the inline style from \`block.style\` or \`fieldAttrs\`, are allowed; changing a class
|
|
7684
7712
|
or an existing style is not). Deploy, poll, call \`get_next_steps\` again and run \`receipt.gate.nextAction\` until \`receipt.complete\` is true; what else it lists, tell me by name. Not listed: say so; invent no work. Its \`receipt.gate\` fails a route on \`sections\` or \`style\` while I could not add a section to that page, reorder it or space it: run each such row's \`fix\` too. A write that answers \`held\`, or a publish whose \`outcome\` is \`partial\`, is not done until its action is taken.
|
|
7685
|
-
11.
|
|
7686
|
-
|
|
7713
|
+
11. ${DOCK_UI_STEP}
|
|
7714
|
+
12. Ask me whether to commit and push to my repository, commit only, or leave it uncommitted (unpushed, the next rebuild from my repo drops it). Touch git only as I answer.`;
|
|
7687
7715
|
var FIELD_UI_PROMPT = {
|
|
7688
7716
|
title: "Declare how fields LOOK in the editor (guided)",
|
|
7689
7717
|
description: "Set the editor chrome of an existing schema and its component props: groups, tabs, rows, icons, item names, what titles and thumbnails a list's rows, help text, conditional visibility. Derives it from your source, then from the field shapes, and asks only what it cannot know.",
|
|
@@ -8115,6 +8143,8 @@ ${COMPONENTIZE_SITE_FLOW}
|
|
|
8115
8143
|
|
|
8116
8144
|
${STRUCTURE_RULE}
|
|
8117
8145
|
|
|
8146
|
+
Last, before the summary: ${DOCK_UI_STEP}
|
|
8147
|
+
|
|
8118
8148
|
Confirm the component library with the user before creating it. End with the summary \xA712 asks
|
|
8119
8149
|
for: the group hierarchy, each family and its variants, what you left in the site's code because
|
|
8120
8150
|
the platform does not model it, and which components are still waiting for the owner's approval
|
|
@@ -8253,7 +8283,8 @@ Read bettercms://playbook/schema section 11 first, then work this order:
|
|
|
8253
8283
|
4. PRESENTATION MANIFEST \u2014 add bcms-presentation.json to public/ (container width, type
|
|
8254
8284
|
scale, nav position/background, footer surface as DTCG {"$type","$value"} entries) and
|
|
8255
8285
|
redeploy, or structural draft previews render in a generic theme, not this site's design.
|
|
8256
|
-
5.
|
|
8286
|
+
5. ${DOCK_UI_STEP}
|
|
8287
|
+
6. VERIFY \u2014 call get_next_steps and fix what it lists (it knows about missing manifests,
|
|
8257
8288
|
a setup that has not run, and placed-but-unpublished components). You are finished only
|
|
8258
8289
|
when its receipt.complete is true: run receipt.gate.nextAction, read it again after each
|
|
8259
8290
|
batch, and record an item you cannot close with editor_setup { exempt } only under an allowed
|
|
@@ -8451,7 +8482,7 @@ function buildServer(deps) {
|
|
|
8451
8482
|
// 🔴 The plain-language ask, routed — the stdio twin of the hosted connector's line
|
|
8452
8483
|
// (src/routes/mcp/index.ts MCP_INSTRUCTIONS). "Make my site editable" has one recipe and
|
|
8453
8484
|
// one definition of done; without this an agent converts the page it landed on and stops.
|
|
8454
|
-
instructions: RECEIPT_FIRST_INSTRUCTION + " When the user asks to make a site or all of its pages editable, to convert it, or to bind its fields: this is playbook \xA713. Read `bettercms://playbook/schema` \xA713, call get_binding_report and get_conversion_brief { complete: true }, convert EVERY route the brief lists, and finish only when get_binding_report shows coverage.pending empty on every route \u2014 not when the first page works. On a workspace-wide connection pass projectId on every call; never ask the user to re-scope the connection. " + LAYOUT_CHROME_INSTRUCTION + " " + CTA_BUTTON_INSTRUCTION + " When the user asks to componentize the whole site, to turn every section into a component, or to build a component library from the site: this is playbook \xA712. Read `bettercms://playbook/schema` \xA712, start with get_site_composition, and use the batch tools \u2014 componentize_sections, compose_pages, update_layout with `commands`, publish_components \u2014 rather than one call per component. Finish with get_site_composition and tell the user what the platform does not model (cookie banners, modals, breadcrumbs, pagination) and which components still need the owner's approval in the dashboard before they can be published. " + // The default after authoring (the structure standard): organise what you made. Same
|
|
8485
|
+
instructions: TOOL_LOADING_INSTRUCTION + " " + RECEIPT_FIRST_INSTRUCTION + " When the user asks to make a site or all of its pages editable, to convert it, or to bind its fields: this is playbook \xA713. Read `bettercms://playbook/schema` \xA713, call get_binding_report and get_conversion_brief { complete: true }, convert EVERY route the brief lists, and finish only when get_binding_report shows coverage.pending empty on every route \u2014 not when the first page works. On a workspace-wide connection pass projectId on every call; never ask the user to re-scope the connection. " + LAYOUT_CHROME_INSTRUCTION + " " + CTA_BUTTON_INSTRUCTION + " When the user asks to componentize the whole site, to turn every section into a component, or to build a component library from the site: this is playbook \xA712. Read `bettercms://playbook/schema` \xA712, start with get_site_composition, and use the batch tools \u2014 componentize_sections, compose_pages, update_layout with `commands`, publish_components \u2014 rather than one call per component. Finish with get_site_composition and tell the user what the platform does not model (cookie banners, modals, breadcrumbs, pagination) and which components still need the owner's approval in the dashboard before they can be published. " + // The default after authoring (the structure standard): organise what you made. Same
|
|
8455
8486
|
// sentence as the hosted connector's MCP_INSTRUCTIONS.
|
|
8456
8487
|
STRUCTURE_DEFAULT_INSTRUCTION + " " + SKILLS_ROUTING_INSTRUCTION + " " + DOCK_UI_ROUTING_INSTRUCTION + " " + PUBLISH_HOLD_INSTRUCTION
|
|
8457
8488
|
}
|