# Prompt for Claude Code: improve Neleto MCP tool documentation

Paste everything below into Claude Code, run from inside the Neleto MCP server's repo (the codebase that defines the `component_create` / `component_update` / `page_update` / `layout_update` / etc. MCP tools).

---

I need you to improve the tool descriptions/schemas in this MCP server based on real gaps found while using it from an AI agent (Claude) to build a CMS site. The agent had to reverse-engineer several things through trial-and-error against API error messages, which wasted a lot of turns. Find the source files that define these MCP tools (likely tool schema + description strings for `component_create`, `component_update`, `page_update`, `layout_update`, `file_upload`, `*_list`) and fix the documentation in place. Don't just write a separate markdown file — the goal is that the tool's own `description` field (what an agent sees when it loads the tool) carries this knowledge.

## 1. The `builder` form-property type is essentially undocumented — this is the priority fix

`builder` (a repeater/array-of-structured-items form property) currently has no real documentation of its JSON shape. An agent had to discover the working shape purely from "missing field X" validation errors on `component_create`/`component_update`, got it wrong twice, and even a manual fix through the admin UI introduced a data-corruption bug. Please document the following, confirmed-working shape directly in the tool description(s):

```json
{
  "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 }
      ]
    }
  ]
}
```

Key facts an agent needs up front, none of which are currently documented:

- The property's `items` array holds **exactly one entry** — a single "row template" object. It is NOT one entry per row-field (an earlier reverse-engineered attempt put `question` and `answer` as two separate top-level `items` entries; this technically validated and rendered, but is the wrong shape and isn't what the admin UI's own Builder Configuration editor produces).
- The actual per-row fields (`question`, `answer`, or whatever the row needs) go inside that single row-template entry's **`props`** array, as plain property definitions (`id`, `type`, `label`, `defaultValue`, `description`, `hint`, `required` — flat, no nested `items`/`props` needed on these).
- The row-template entry itself still requires top-level `id`, `name`, `data`, `defaultValue`, `items` (empty array), and a `property` object (`{type, defaultValue}` mirroring its own type) to pass `component_create`/`component_update` validation, even though these are otherwise unused for actual row content.
- **Bug/gotcha to document explicitly**: if `data`/`defaultValue` on that row-template entry are set to a real non-empty string (e.g. a placeholder question), the admin UI's builder editor will, on save, spread that string's characters into every row's data as extra numeric keys (`{"0":"H","1":"o","2":"w",...}`) alongside the real field values. It doesn't break rendering (the stray keys are just unused garbage) but it's clearly a bug. **Recommend/require `data: ""` and `defaultValue: ""` (empty string) on the row-template entry** until this is fixed upstream, and document why.
- Actual row **values** — both the property's top-level `defaultValue` and what gets stored in a page/layout element's `data[<propId>]` — are an array of `{ "id": "<rowId>", "data": { "<fieldId>": "<value>", ... } }` objects. Not a flat `{ "<fieldId>": "<value>" }` per row.
- In component **templates**, row values must be read via `item.data.<fieldId>`, e.g.:
  ```
  {{#each properties.items as item}}
    {{ item.data.question }}
    {{ item.data.answer }}
  {{/each}}
  ```
  Not `item.<fieldId>` directly — this is a very natural mistake to make and should be called out with an example in the docs, since `{{ properties.x }}` elsewhere in the same template language reads flat values.

## 2. Other real gaps found in the same session — please fix these too

- **`image_path(file, "options")` — `f_` is overloaded.** The options string mixes format (`f_webp`) and fit (`f_cover`) under the same `f_` prefix. Combining a size (`w_`/`h_`) with a fit value causes a hard HTTP 400, not a graceful fallback to the original image as one might assume. Document that fit and format cannot both be expressed via `f_` in the same call, and that CSS `object-fit` should be used for cropping/fit instead of relying on this helper.

- **`component_update` / `page_update` / `layout_update` are REPLACE-semantics APIs**, but this is easy to miss in practice: every field is fully overwritten, including `elements`. Please make sure the docs are explicit that:
  - Every existing element being resent (not just new ones) must include its own `tempId` field, or the API rejects the whole request with `missing field tempId` — this is non-obvious because those elements already have real `id`s.
  - Omitting any element that should persist deletes it silently (no warning) unless it's also listed in `deletedElements`.

- **`*_list` endpoints** (`layout_list`, `page_list`, `component_list`, `web_file_list`) can throw a DB error (`column 'title' does not exist`) — worth either fixing the underlying query or, at minimum, documenting the known failure and recommending `*_create` responses / `*_get` / `*_search` as the reliable way to enumerate or resolve IDs.

- **`file_upload` with inline base64** — large inline base64 payloads (roughly in the 15–48KB range) are prone to corruption when passed through as tool-call arguments (`Invalid padding` / `Invalid symbol at offset N` errors, where the reported offset is not reliable for debugging). Document a recommended max size and/or a chunked/streaming upload alternative if one exists.

- **CSS injection order across components is not guaranteed to match page/element order.** When two components both set the same CSS property on a shared utility class used together on one element (e.g. a shared `.wrap` class plus a component-specific class), the cascade can resolve unpredictably depending on which component's `<style>` block got injected into `<head>` first. Document this and recommend scoping component-specific overrides with a more specific selector (e.g. `.componentClass .wrap` rather than relying on `.wrap` alone) to be injection-order-independent.

## 3. The `icon` field has zero documentation — it uses Tabler icon names

`component_create` and `component_update` both define `icon` as just `{"type": "string"}` — no description, no format, no examples. There is nothing telling an agent (or a human) what a valid value looks like.

In practice, icons are Tabler icons (https://tabler.io/icons), referenced as `"tabler:<icon-name>"` — for example `"tabler:award-filled"`, `"tabler:message-circle-2"`, `"tabler:layout-grid"`. Tabler ships both outline and filled variants of most icons, distinguished by a `-filled` suffix on the name.

Without this documented, an agent has no way to know the `tabler:` prefix is required and will guess plain icon-ish words instead (`"award"`, `"help-circle"`, `"star"`, `"mail"`, `"code"`, `"palette"`, `"grid"`, `"square"`, `"list-ordered"`, `"quote"`, `"type"`, `"image"` — all of these were guessed this session and are almost certainly wrong/non-rendering in the admin UI). Please add a description to the `icon` field on both `component_create` and `component_update` stating:
- the expected format is `"tabler:<icon-name>"`
- where to find valid icon names (Tabler's icon set)
- the `-filled` suffix convention for the filled variant
- ideally 2-3 example values inline in the description, since agents can't browse tabler.io themselves mid-session

## What to do

1. Locate the actual tool schema/description definitions for the tools above in this repo.
2. Update the `description` fields (and any accompanying example text) in place with the corrected, concrete information above — include the JSON example for `builder` verbatim or adapted to this repo's conventions, and the `icon` field's `tabler:` prefix convention.
3. If there's a separate human-readable docs site/markdown in this repo (not just inline tool descriptions), update that too, but the inline tool descriptions are the priority since that's what an agent actually sees.
4. Where you found an actual bug rather than just a docs gap (the row-data character-spread corruption, the `f_` collision, the `*_list` DB error), flag it clearly as a TODO/bug comment near the relevant code, separate from the documentation fix, and tell me what you found.
