Tools Reference
Neleto exposes 40 MCP tools across 8 categories. Each tool call requires an Authorization: Bearer <token> header.
id fields for files and file-related tools are UUIDs (strings), while ids for pages, layouts, components, posts, and events are integers.{ "items": [...], "total": n } — never a bare top-level array.page_update, layout_update, and component_update overwrite every field; any field you omit is reset to its empty/default value, not left unchanged. Load the record first with the matching *_get, apply your edits, then send the complete object back.data vs form defaultValue. In a component template {{ properties.x }} reads the element's data[x] and renders empty when the key is absent — defaultValue is only an editor seed and is not a render-time fallback. New elements are auto-seeded from each property's defaultValue, but populate data explicitly for anything you rely on. Render child elements with {{ render_elements(children) }} — the same helper as {{ render_elements(page.elements) }} and {{ render_elements(page.layout.elements) }}. Translatable fields (textarea / richtext / markdown) are only persisted when page_create / page_update is called with a language code, and element order is positional (a returned order of null is not meaningful).tempId — including existing ones. In page_update / layout_update, each entry of the elements array must include a tempId string, even elements that already have a real numeric id. Omitting it rejects the whole request with missing field tempId. page_get / layout_get return every element with a tempId already set (existing elements come back as "old-<id>") — keep and resend that value unchanged. The elements array is replace-semantics: any existing element you leave out is silently deleted (no warning). To keep an element, resend it; to delete one, drop it from elementsand add its numeric id to deletedElements.Pages
Tools for managing CMS pages.
page_list
List pages available in the CMS.
page_get
Load a single page with its layout, elements, and translated content.
page_create
Create a new page including tags, meta tags, and elements.
/ (e.g. /about).[] for new pages).page_update
Update an existing page including its nested elements. Same fields as page_create plus:
page_delete
Soft-delete a page.
page_duplicate
Duplicate a page to a new route and title.
/ (e.g. /about-copy).Layouts
Layouts are reusable templates that can be assigned to multiple pages.
layout_list
List page layouts.
layout_get
Load a single layout with its element tree.
layout_create
Create a reusable layout with template, style, script, and fixed elements.
[] for new layouts).tabler:<icon-name> — an icon from the Tabler set (Iconify collection prefix tabler), exactly what the admin's icon picker stores. The filled variant adds a -filled suffix. Examples: tabler:layout-grid, tabler:layout-navbar, tabler:columns. A bare word without the tabler: prefix does not resolve.layout_update
Update an existing layout including its element tree. Same fields as layout_create plus:
layout_delete
Soft-delete a layout.
Components
Components are reusable building blocks that editors can insert on pages.
component_list
List all reusable CMS components.
No parameters required.
component_get
Load a single component by ID.
component_search
Search components by form label.
component_used_by_elements
List all element instances that use a given component.
component_create
Create a new component.
tabler:<icon-name> — an icon from the Tabler set (Iconify collection prefix tabler), exactly what the admin's icon picker stores. The filled variant of an icon adds a -filled suffix. Examples: tabler:award-filled, tabler:message-circle-2, tabler:layout-grid. A bare word without the tabler: prefix does not resolve and renders no icon. (The i-tabler-<icon-name> class form also renders, but tabler:<icon-name> is canonical.)"" for a component with no script.{{ properties.<key> }} and child elements with {{ render_elements(children) }}.Images (file relations). A relation value is not a file until you bind it. Bind the file out of the relation, then pass the bound file to image_path together with a required options string. Calling image_path(file) with no options, or passing relations.<id> unbound, is a hard render error — not an empty src:
{{#if relations.image as file}}
<img src="{{ image_path(file, "w_1600,f_webp") }}" alt="{{ file.title }}">
{{/if}}
This serves /image/user-upload/<file-id>?options=w_1600,f_webp. Options are comma-separated and applied in order — s_<N> (square), w_<N>, h_<N>, f_webp|jpeg|png|avif|gif, q_<N> (jpeg quality), e_<t>_<l>_<w>_<h> (crop), fit_contain|fit_cover|fit_fill|fit_inside|fit_outside — see image scaling. Other relation helpers follow the same bind-first rule: post_path(post), event_path(event), and file_path(file) for downloads. For a multiple relation, iterate with {{#each relations.<id> as file}} … {{/each}}.
f_ is format only — fit uses fit_.f_ sets the output format (f_webp, f_jpeg, …). Fit/crop is a separatefit_ prefix (fit_cover, fit_contain, …). There is no f_cover/f_contain — writing one is parsed as a format, fails validation, and returns a hard HTTP 400 (no graceful fallback to the original). fit_ also only works alongside a size and must appear after the w_/h_/s_ in the string. For simple cropping/aspect control, prefer CSS object-fit on the <img> — it is more predictable than the fit_ option.Builder (repeater) form property. A builder property is a repeater: the editor manages an array of rows that all share the same fields. Its JSON shape is easy to get wrong. The property definition's items array holds exactly one entry — a single row template — and the per-row fields live inside that entry's props array (not as separate items entries):
{
"id": "items",
"type": "builder",
"label": { "en": "Questions", "de": "Fragen" },
"description": null,
"hint": null,
"required": null,
"defaultValue": [
{ "id": "row1", "data": { "question": "...", "answer": "..." } },
{ "id": "row2", "data": { "question": "...", "answer": "..." } }
],
"items": [
{
"id": "question",
"type": "text",
"name": "question",
"label": { "en": "Item", "de": "Item" },
"icon": null,
"items": [],
"data": "",
"defaultValue": "",
"property": { "type": "text", "defaultValue": "" },
"props": [
{
"id": "question",
"type": "textarea",
"label": { "en": "Question", "de": "Frage" },
"defaultValue": null,
"description": null,
"hint": null,
"required": null
},
{
"id": "answer",
"type": "textarea",
"label": { "en": "Answer", "de": "Antwort" },
"defaultValue": null,
"description": null,
"hint": null,
"required": null
}
]
}
]
}
items= exactly one row-template object. The real per-row fields (question,answer, …) are plain flat property definitions inside itspropsarray.- The row-template object still needs top-level
id,name,data,defaultValue,items([]), and apropertyobject ({ type, defaultValue }). Strictly required by the deserializer arelabel,name,data,props; the rest mirror what the admin Builder Configuration editor emits — include them so the definition round-trips through the UI. - Set the row-template's
dataanddefaultValueto""(empty string). Do not use a real placeholder string: on save the admin builder editor spreads a non-empty string's characters into every row'sdataas stray numeric keys ({"0":"H","1":"o",…}). It doesn't break rendering but corrupts the stored data (known admin-UI bug);""avoids it. - Row values — both the property's top-level
defaultValueand each element'sdata[<propId>]— are an array of{ id, data }objects, not a flat{ fieldId: value }per row:[ { "id": "row1", "data": { "question": "…", "answer": "…" } } ]. - In the template, read row values via
item.data.<fieldId>(notitem.<fieldId>):
{{#each properties.items as item}}
{{item.data.question}}
{{item.data.answer}}
{{/each}}
style block is hoisted into <head>, but the order in which different components' styles are injected does not necessarily follow page/element order. When two components set the same property on a shared utility class used together on one element (e.g. a shared .wrap plus a component-specific class), the cascade can resolve unpredictably. Scope component-specific overrides with a more specific selector (e.g. .myComponentClass .wrap { … } rather than bare .wrap { … }) so they win regardless of injection order.component_update
Update an existing component. Same fields as component_create plus:
component_delete
Delete a component and all element instances linked to it.
Blog Posts
post_list
List blog posts.
post_get
Load a blog post by ID.
post_search
Search posts by title, description, or content.
post_create
Create a blog post.
"en" or "de").{}).heroImageId).post_update
Update a blog post. Same fields as post_create plus:
post_delete
Delete a blog post.
Events
event_list
List events.
event_get
Load an event by ID.
event_search
Search events by title, description, or content.
event_today
List all events happening today.
No parameters required.
event_upcoming
List upcoming events.
No parameters required.
event_create
Create an event.
{}).heroImageId).event_update
Update an event. Same fields as event_create plus:
event_delete
Delete an event.
Files
File IDs are UUIDs (strings), not integers.
file_list
List files, folders, or load a single file with optional content. File records include a serveUrl — an absolute …/image/user-upload/<id> URL that serves the stored bytes.
"/" for root), or load a specific file by path.["image/jpeg", "image/png"]).file_get
Load a file with its tags by ID. The record includes a serveUrl — an absolute …/image/user-upload/<id> URL that serves the stored bytes.
file_search
Search files by title, description, path, status, or MIME type.
true, exclude folders from results.file_create
Create a file, folder, or remote file entry. Local files are created empty on disk.
"/images/logo.png").file_upload
Upload a binary file or image using base64-encoded content. base64Data accepts both raw base64 and a data-URI (data:image/jpeg;base64,…). On success the response includes the stored byte size and a ready-to-use serveUrl (absolute …/image/user-upload/<id> URL); that URL also accepts ?options=w_200,f_webp-style resizing.
Invalid base64Data: Invalid padding or Invalid symbol at offset N (the reported offset is not reliable for locating the problem). Roughly under ~10KB of raw bytes is safe; payloads in the ~15–48KB+ range corrupt intermittently. For anything larger, upload the original binary through the admin media manager (or a direct HTTP upload) and reference the resulting file by id/path instead of inlining it. Retrying an identical corrupted payload usually fails the same way — re-encode from source if you must retry."image/png"). Auto-detected if omitted.file_update
Update file metadata or content for local files.
file_delete
Delete one or more files or folders.
file_move
Move or copy files into another folder path.
"/images/archive").true, copy the files instead of moving them.file_rename
Rename a file or folder by changing the last segment of its path.
Web Files
Web files are static text files served at the root of your site (e.g. /sitemap.xml, /llms.txt, /llms-full.txt). Each file has a unique path, raw text content, and a MIME content type derived from the path's extension.
web_file_list
List all web files.
No parameters required.
web_file_get
Get a single web file by path.
"sitemap.xml").web_file_create
Create a new web file served at /<path>.
"sitemap.xml", "blog/llms.txt"). Slashes are allowed to nest files. Must be unique.web_file_update
Update the content of an existing web file by path.
web_file_delete
Delete a web file by path.
Settings
settings_get
Load CMS settings with resolved page references and meta tags. The response also exposes the live instance's public URLs, so an agent can screenshot or verify the site it builds:
publicBaseUrl— the host the site is served from (e.g.https://yannik.free.neleto.io).imageBaseUrl— the prefix served uploads live under (e.g.…/image/user-upload).adminUrl— the editor URL.
settings_update
Update CMS settings. All fields are optional - only provided fields are changed.
robots.txt content.value (e.g. "en") and label (e.g. "English").