@caelo-cms/mcp-server 0.10.58 → 0.10.60
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/package.json +1 -1
- package/power-catalogue.json +74 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caelo-cms/mcp-server",
|
|
3
3
|
"mcpName": "io.github.caelo-cms/caelo",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.60",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MPL-2.0",
|
|
7
7
|
"description": "MCP servers for Caelo CMS: `caelo_chat` (talk to Caelo's own AI) and the admin-scoped Power-MCP (the full chat-runner tool catalogue, driven by your own agent). Lets Claude Code (or any MCP-aware client) drive a Caelo install from outside the browser.",
|
package/power-catalogue.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.10.
|
|
2
|
+
"version": "0.10.60",
|
|
3
3
|
"tools": [
|
|
4
4
|
{
|
|
5
5
|
"name": "edit_module",
|
|
@@ -350,6 +350,76 @@
|
|
|
350
350
|
}
|
|
351
351
|
}
|
|
352
352
|
},
|
|
353
|
+
{
|
|
354
|
+
"name": "patch_page_module_fields",
|
|
355
|
+
"description": "Change individual values of ONE module placement — a heading, one bullet of a text-list, a link-list entry's text or target, a `url` field, an `image` field — and leave every other value of that placement untouched. Use this for targeted wording fixes ('the hero says Welcom, fix it', 'rename the second menu item to Pricing', or a reference chip naming `placement=(blockName, position), field=…[index]`): you send only the changed values instead of the placement's whole contentValues. `text` is the new value: plain text for text fields (it is HTML-escaped for you — never send markup), a path / https URL for `url` / `image` fields and link targets. `index` addresses a `text-list` item or a `link-list` entry; `part: \"href\"` sets that entry's target instead of its text. Several edits of one placement go in one call (all or none). Use `set_page_module_content` (NOT this tool) to replace a placement's values wholesale or to set nested modules; `set_page_module_content_many` for passes across several placements; `edit_module` for structure. SYNCED placements (content shared with other pages) need `scope`: `\"everywhere\"` changes it on every page that shows it, `\"here\"` detaches this page first and changes only it — ask the operator which they mean when it is not clear. Typical call: `{pageId: \"…\", blockName: \"content\", position: 0, edits: [{field: \"hero_title\", text: \"Welcome\"}, {field: \"nav_items\", index: 1, text: \"Pricing\"}]}`.",
|
|
356
|
+
"inputSchema": {
|
|
357
|
+
"type": "object",
|
|
358
|
+
"additionalProperties": false,
|
|
359
|
+
"required": [
|
|
360
|
+
"pageId",
|
|
361
|
+
"blockName",
|
|
362
|
+
"position",
|
|
363
|
+
"edits"
|
|
364
|
+
],
|
|
365
|
+
"properties": {
|
|
366
|
+
"pageId": {
|
|
367
|
+
"type": "string",
|
|
368
|
+
"format": "uuid"
|
|
369
|
+
},
|
|
370
|
+
"blockName": {
|
|
371
|
+
"type": "string",
|
|
372
|
+
"minLength": 1
|
|
373
|
+
},
|
|
374
|
+
"position": {
|
|
375
|
+
"type": "integer",
|
|
376
|
+
"minimum": 0
|
|
377
|
+
},
|
|
378
|
+
"edits": {
|
|
379
|
+
"type": "array",
|
|
380
|
+
"minItems": 1,
|
|
381
|
+
"maxItems": 20,
|
|
382
|
+
"items": {
|
|
383
|
+
"type": "object",
|
|
384
|
+
"additionalProperties": false,
|
|
385
|
+
"required": [
|
|
386
|
+
"field",
|
|
387
|
+
"text"
|
|
388
|
+
],
|
|
389
|
+
"properties": {
|
|
390
|
+
"field": {
|
|
391
|
+
"type": "string",
|
|
392
|
+
"minLength": 1,
|
|
393
|
+
"maxLength": 200
|
|
394
|
+
},
|
|
395
|
+
"index": {
|
|
396
|
+
"type": "integer",
|
|
397
|
+
"minimum": 0
|
|
398
|
+
},
|
|
399
|
+
"part": {
|
|
400
|
+
"type": "string",
|
|
401
|
+
"enum": [
|
|
402
|
+
"label",
|
|
403
|
+
"href"
|
|
404
|
+
]
|
|
405
|
+
},
|
|
406
|
+
"text": {
|
|
407
|
+
"type": "string",
|
|
408
|
+
"maxLength": 10000
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
"scope": {
|
|
414
|
+
"type": "string",
|
|
415
|
+
"enum": [
|
|
416
|
+
"everywhere",
|
|
417
|
+
"here"
|
|
418
|
+
]
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
},
|
|
353
423
|
{
|
|
354
424
|
"name": "build_page",
|
|
355
425
|
"description": "Build a WHOLE page in ONE call: the page (create new via `page: {slug, title}` or target existing via `page: {pageId}`) plus the FULL ordered module list — each entry places one module into a template block, top to bottom. ONE transaction: any invalid entry aborts the whole call (the error names `modules[i]` and the failing field) and nothing is written. This is THE page-creation tool: `modules: []` (empty) creates an intentionally empty page shell; a populated `modules` array builds the whole page in one transaction. **Prefer it over add_module / create_content_instance chains whenever a page needs more than one module** — one build_page call replaces that whole round-trip chain (§11 bulk-first). Use add_module (target='page') only for a single incremental module on an existing page. **Per module, two modes** (same contract as add_module): pass `moduleId` to place an EXISTING module from `## Modules` (reuse first — shared modules keep pages consistent), or `displayName` + `html` (+ required `description`, `kind`, explicit `fields[]`) to mint a new one. Field names are semantic snake_case (`hero_title`, `primary_cta_href`); repeats are LIST fields (`text-list`, `link-list`, `module-list`), never numbered scalars. **Per module, `content` fills the placement**: `{source:'inline', values:{hero_title:'…'}}` for page-local content (the routine case); `{source:'shared', purpose:'…', values:{…}}` to mint a REUSABLE instance other pages can bind (synced by default); `{source:'existing', contentInstanceId}` to bind a row from `## Content Library`. Omit `content` for an empty private instance you fill later. Typical call: `{page:{slug:'pricing', title:'Pricing'}, modules:[{blockName:'content', displayName:'Pricing Hero', kind:'hero', description:'…', html:'<section><h1>{{hero_title}}</h1></section>', fields:[{name:'hero_title', kind:'text', label:'Hero title'}], content:{source:'inline', values:{hero_title:'Fair pricing'}}}, …]}`. **Nested modules in the SAME call**: give the inner module `ref` and NO blockName (detached — minted + content-bound but not placed), then reference it from the outer module's `module`/`module-list` field value as `{\"$ref\": \"<ref>\"}` — the server resolves it to the real {moduleId, contentInstanceId}. Referenced entries come FIRST. Example: `modules:[{ref:'btn', displayName:'Button', html:'<button>{{label}}</button>', fields:[…], content:{source:'inline', values:{label:'Go'}}}, {blockName:'content', displayName:'CTA', html:'<section>{{>cta}}</section>', fields:[{name:'cta', kind:'module', label:'CTA', allowedModuleTypes:['button']}], content:{source:'inline', values:{cta:{'$ref':'btn'}}}}]` — the whole nested composition is ONE call, never an add_module → remove_module_from → create_content_instance chain. For site-wide chrome (header/footer) use add_module (target='layout') — chrome is layout-owned and never rides in a page body. Each entry's `blockName` must be a block on the page's template — see `# Current page` (exhaustive list for the focused page); a mismatch error names the valid set. A block name is a template slot, NOT a module `kind`. With zero templates on the site, bootstrap first (create_layout → create_template). **Internal links** (`href`s in HTML, `url`/`link` fields, link-list and nav items): write the page's path, e.g. `/about` — it must be an EXISTING page (list_pages); a link to a page that does not exist yet is refused, so create the target first, or add the link after. Exception, a site import you are rebuilding in this chat: a link to a page of that import run is kept and resolved by resolve_import_links at the end. Images/files: their media URL (find_media). Other sites: the full https:// URL.",
|
|
@@ -4829,7 +4899,7 @@
|
|
|
4829
4899
|
},
|
|
4830
4900
|
{
|
|
4831
4901
|
"name": "update_pages_many",
|
|
4832
|
-
"description": "Edit page metadata — the ONE tool for it, for 1 page or 200 (pass a single-item `updates` array for one page; there is no separate rename/retitle/reslug tool). Use for 'rename this page', 'change the browser-tab title', 'move this to /pricing', 'archive all draft posts', 'move these pages to the {tpl} template'. **A page has THREE independently-editable identifiers — never substitute one for another:** `name` = the internal editor label (page picker / breadcrumbs; not public), `title` = the HTML <title> tag (browser tab + search results), `slug` = the URL path. If the operator just says 'rename' without saying which, ASK before guessing — 'rename' usually means `name`. **Changing `slug` moves the page's public URL — and the URL of every page nested below it**: a 301 from each old URL is created automatically
|
|
4902
|
+
"description": "Edit page metadata — the ONE tool for it, for 1 page or 200 (pass a single-item `updates` array for one page; there is no separate rename/retitle/reslug tool). Use for 'rename this page', 'change the browser-tab title', 'move this to /pricing', 'archive all draft posts', 'move these pages to the {tpl} template'. **A page has THREE independently-editable identifiers — never substitute one for another:** `name` = the internal editor label (page picker / breadcrumbs; not public), `title` = the HTML <title> tag (browser tab + search results), `slug` = the URL path. If the operator just says 'rename' without saying which, ASK before guessing — 'rename' usually means `name`. **Changing `slug` moves the page's public URL — and the URL of every page nested below it**: a 301 from each old URL is created automatically in the same transaction, for links from outside the site. Links on this site (nav menus, link lists, modules, content) follow the page on their own — do not update them after a rename. A rename that moves 10+ URLs (the page plus its subtree) needs one Owner click in chat before it runs. To put a page under another page (or back at the top level) use move_pages, not this tool. Pass `redirectFromOld: 'skip'` ONLY when the operator explicitly says the old URL should 404 — the default keeps inbound links alive. Per-item failures (not-found, version conflict) are reported; the rest of the batch still applies. DO NOT use for SEO sidecar (`pages_seo.set_many`) or modules (`update_modules_many`) — those have their own bulk tools.\n\nOver MCP: when this call needs the operator's approval, it queues an approval card in their Caelo admin (Live Edit chat and the pending-approvals page) and returns the outcome once they decide — tell them to approve the card; never ask them to paste a prompt into the Caelo chat.",
|
|
4833
4903
|
"inputSchema": {
|
|
4834
4904
|
"type": "object",
|
|
4835
4905
|
"additionalProperties": false,
|
|
@@ -4872,7 +4942,7 @@
|
|
|
4872
4942
|
"type": "string",
|
|
4873
4943
|
"minLength": 1,
|
|
4874
4944
|
"maxLength": 120,
|
|
4875
|
-
"description": "URL path. Changing it auto-creates a 301 from the old URL
|
|
4945
|
+
"description": "URL path. Changing it auto-creates a 301 from the old URL; links on the site follow the page automatically."
|
|
4876
4946
|
},
|
|
4877
4947
|
"templateId": {
|
|
4878
4948
|
"type": "string",
|
|
@@ -4902,7 +4972,7 @@
|
|
|
4902
4972
|
},
|
|
4903
4973
|
{
|
|
4904
4974
|
"name": "move_pages",
|
|
4905
|
-
"description": "Put pages under other pages (nested URLs), or back at the top level — for 1 page or 200 in one call. Use for 'put Alerts under SEO Monitoring', 'move the pricing page into Products', 'make this a top-level page again', 'group these 5 guides under /guides'. A page's URL is its parent's URL + '/' + its slug, so moving `/alerts` under `/seo-monitoring` makes it `/seo-monitoring/alerts`; every page below a moved page moves with it. Each move: `page` = the page's id or current URL path, `parent` = the new parent's id or path, or null for the top level, optional `newSlug` to rename in the same step. Everything happens in one transaction
|
|
4975
|
+
"description": "Put pages under other pages (nested URLs), or back at the top level — for 1 page or 200 in one call. Use for 'put Alerts under SEO Monitoring', 'move the pricing page into Products', 'make this a top-level page again', 'group these 5 guides under /guides'. A page's URL is its parent's URL + '/' + its slug, so moving `/alerts` under `/seo-monitoring` makes it `/seo-monitoring/alerts`; every page below a moved page moves with it. Each move: `page` = the page's id or current URL path, `parent` = the new parent's id or path, or null for the top level, optional `newSlug` to rename in the same step. Everything happens in one transaction, with a 301 from every old URL for links from outside the site. Links on this site (nav menus, link lists, module and content links) follow the pages automatically — there is no link-update step after a move. If the target URL is already taken, the call changes nothing and names the clash — retry with `newSlug`. The homepage can neither move under a page nor have child pages; nesting is limited to 6 levels. Moving 10+ URLs in one call (pages + their subtrees) needs one Owner click in chat before it runs. To change only a page's name/title/slug use update_pages_many; to create a page under a parent use build_page with page.parentPageId.\n\nOver MCP: when this call needs the operator's approval, it queues an approval card in their Caelo admin (Live Edit chat and the pending-approvals page) and returns the outcome once they decide — tell them to approve the card; never ask them to paste a prompt into the Caelo chat.",
|
|
4906
4976
|
"inputSchema": {
|
|
4907
4977
|
"type": "object",
|
|
4908
4978
|
"additionalProperties": false,
|