- Brand
- Laioutr
- Category
- Commerce
- Primary Subcategory
- AI Website Builders
Integration details
Description
Laioutr is the agentic frontend management platform that combines Studio (visual editor), Frontend (performance layer), Orchestr (backend integrations), and Laioutr Cloud (hosting) into a single platform. Developers no longer need to manually maintain integrations, while marketing and content teams can independently design pages, launch campaigns, and manage content via a unified interface - without requiring an engineering ticket for every adjustment.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- AI Website Builders
- Secondary Subcategories
- None listed
- Brand
- Laioutr
- Access
- Account required
- First tracked
- 2026-07-22
- Tool count
- 47
- Market
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Your score is coming
ChatGPT now suggests Plugins on its own when they match a user's request.Your Plugin Discovery Score measures how often yours appears, and it will show here as soon as it’s ready.
What discovery looks like

Get alerts for Laioutr
Get updates when Laioutr’s Discoverability Score or category rank changes.
Competing in ChatGPT AI Website Builders
View Category47 tools agents can invoke
Append a new item to an array-typed prop on a section or block, addressed by the same `at` + `keyPath` shape as a `studio_set_static_props` write item (here `keyPath` points at the ARRAY field). The new item is seeded with the component schema defaults and its generated id is returned — fill the item afterward with `studio_set_static_props`. **When to use:** Use to add a slide, card, row, or any repeated entry to an array prop. Discover the entity id with `studio_get_page_tree`, the array field name with `studio_get_component_schemas`, and existing item ids via `studio_get_section`. After adding, set the new item's fields with `studio_set_static_props`, using a write item's `keyPath: [["<arrayField>", "<arrayItemId>"], "<field>"]`. **When NOT to use:** Don't use to set a value on an item that already exists — use `studio_set_static_props`. Don't use to add a block to a slot — use `studio_add_block`. Global sections are read-only via this server. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","at":{"sectionId":"b8h2k4m6p0r3t5v7c9d1"},"keyPath":["features"]}` **Response:** `{ arrayItemId, catalogVersion, warnings }` — `arrayItemId` is the new item's stored key; pass it back as `keyPath: [["<arrayField>", arrayItemId], "<field>"]` in a `studio_set_static_props` write item. `catalogVersion` is the component-catalog fingerprint at write time. `INVALID_INPUT` means the `keyPath` did not resolve to an array field. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_add_array_item
Add a block to a slot of an existing section (local or global). The slot must exist on the section's component and the block component must be allowed in that slot — verify both with `studio_get_component_schemas` for the owner section. **When to use:** Use to fill out a section — drop product cards into a hero, add feature tiles into a grid, append a button into a CTA slot. Discover the owner `sectionId` (or `globalSectionId`) and slot names via `studio_get_page_tree` / `studio_get_section`. **When NOT to use:** Don't use to add a whole new section — use `studio_add_section`. Don't use to change props on a block that already exists — use `studio_set_static_props`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","owner":{"sectionId":"b8h2k4m6p0r3t5v7c9d1"},"slot":"items","componentName":"BlockFeatureTile"}` **Response:** `{ blockId, catalogVersion, warnings }` — `blockId` is what subsequent prop tools target; `catalogVersion` is the component-catalog fingerprint at write time. Catalog-related errors carry `detail.catalogVersion` + `detail.refresh.tool` — when either signal changes, re-list with `studio_list_components`. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_add_block
Place an EXISTING global section onto a page-variant container (header/body/footer) as a reference — not a copy. Discover available `globalSectionId` values with `studio_list_global_sections`. This does NOT create a global section; it only references one that already exists. **When to use:** Use to reuse a site-wide header, footer, or any shared section on a page variant. Discovery chain: `studio_list_global_sections` (list) → `studio_add_global_section_reference` (place) → `studio_get_section` with the `globalSectionId` (read its blocks) → `studio_add_block` / `studio_set_static_props` (edit them). **When NOT to use:** Don't use to add a local (page-specific) section — use `studio_add_section`. Don't use to create or edit the global section container itself (creating, detaching, or setting its own props is not exposed) — ask the human to do that in Studio. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","globalSectionId":"d4f6h8k0m2p4r6t8v0x2","at":{"pageVariantId":"a1b2c3d4e5f6g7h8j9k0","location":"header"}}` **Response:** `{ sectionReferenceId, catalogVersion, warnings }` — `sectionReferenceId` is the placement node on the variant (distinct from `globalSectionId`, the global it points at); `catalogVersion` is the component-catalog fingerprint at write time. `CAPABILITY_UNSUPPORTED` means the connected frontend-core lacks `globalSectionReferences` (check `studio_get_capabilities`). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_add_global_section_reference
Add a new section to a page variant container (header/body/footer) at a chosen position. Look up the `componentName` with `studio_list_components` and its prop shape with `studio_get_component_schemas` before passing `props`. **When to use:** Use to compose or extend a page — add a hero, add a feature grid, drop a CTA. Pre-configure props when the values are known; otherwise add empty and follow up with `studio_set_static_props`. **When NOT to use:** Don't use to add a block inside an existing section — use `studio_add_block`. Don't use to change props on an existing section — use `studio_set_static_props`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","pageVariantId":"a1b2c3d4e5f6g7h8j9k0","location":"body","componentName":"SectionHero"}` **Response:** `{ sectionId, catalogVersion, warnings }` — `sectionId` is what subsequent block/prop tools target; `catalogVersion` is the component-catalog fingerprint at write time. Catalog-related errors carry `detail.catalogVersion` + `detail.refresh.tool` — when either signal changes, re-list with `studio_list_components`. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_add_section
Create an empty draft entry of a collection, seeded with the type's defaults when its schema can be read, and return its id. A new entry is never open, so this writes the draft row. **When to use:** Use to add an entry, then fill it with `collections_set_fields`. Pass an `idempotencyKey` when you may retry. **When NOT to use:** Don't use to copy an entry or to set values — create, then write with `collections_set_fields`. To publish it, use `collections_publish_entries`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entityType":"BlogPost"}` **Response:** `{ entryId, revision }`. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); `COLLECTION_NOT_FOUND` (the storefront does not declare the type; `detail.refresh.tool` names `collections_list_collections`); `PROJECT_LOCKED` (a restore or migration holds the project). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_create_entry
Permanently remove a block from its section slot. Destructive — confirm with the human before issuing on a non-trivial block. **When to use:** Use when explicitly asked to remove a block (cleaning up a section, retiring a feature tile). Discover the `blockId` with `studio_get_page_tree`. **When NOT to use:** Don't use to clear props — use `studio_set_static_props` with a `null` write value. Don't use to hide a block — use `studio_update_block` with a visibility decorator. There is no host-side undo; recovery is via manual snapshots taken before significant edits. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","blockId":"c3e5g7j9l1n3p5r7t9v1"}` **Response:** `{ blockId, catalogVersion, warnings }` — `catalogVersion` is the component-catalog fingerprint at write time. Catalog-related errors carry `detail.catalogVersion` + `detail.refresh.tool`; re-list with `studio_list_components` when either changes. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_delete_block
Permanently remove a section (and every block inside it) from a page variant. Destructive — confirm with the human before issuing on a non-trivial section. **When to use:** Use when explicitly asked to remove a section (rebuilding a page, retiring a feature). Discover the `sectionId` with `studio_get_page_tree` first. **When NOT to use:** Don't use to clear props — use `studio_set_static_props` with a `null` write value. Don't use to hide a section — use `studio_update_section` with a visibility decorator. There is no host-side undo; recovery is via manual snapshots taken before significant edits. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","sectionId":"b8h2k4m6p0r3t5v7c9d1"}` **Response:** `{ sectionId, catalogVersion, warnings }` — `catalogVersion` is the component-catalog fingerprint at write time. Catalog-related errors carry `detail.catalogVersion` + `detail.refresh.tool`; re-list with `studio_list_components` when either changes. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_delete_section
Report whether the project's frontend is reachable, which version-gated frontend-core capabilities it supports, and which media libraries it has. **When to use:** Read before a version-sensitive edit (e.g. global-section references) and before a media task: `mediaLibraries` gives each library id and what it can do, for `media_list_assets` and `media_import_from_url`. `frontendReachable: false` is also the pre-flight signal that the component catalog, schemas, style tokens, and all mutations are unavailable right now. **When NOT to use:** Don't use to discover components or props — use `studio_list_components` / `studio_get_component_schemas`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g"}` **Response:** `{ projectId, frontendCoreVersion, frontendReachable, capabilities, thresholds, mediaLibraries, note? }` — under 1 KB plus about 1.5 KB per library. `mediaLibraries` is `Array<{ id, label, iconSrc?, capabilities }>` as the frontend reports it. DEGRADES instead of erroring: when the frontend is down it returns `frontendReachable: false` with an all-false capability set and `mediaLibraries: []` (no `FRONTEND_UNREACHABLE`). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_get_capabilities
Return the field model of one collection: per component its fields (kind, `localized`, `required`, constraints, options, union variants and their discriminator, list items), plus the link tokens that start at the type. **When to use:** Use before writing, to learn the field names for a `keyPath`, which fields are localized, and the `token` for `collections_set_links`. Pass `components` to read only some. **When NOT to use:** Don't use to read an entry's values — use `collections_get_entry` or `collections_get_field`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entityType":"BlogPost"}` **Response:** `{ entityType, fingerprint, slug?, requiredComponents, components: Array<{ name, title?, fields }>, links: Array<{ token, label, target, type }>, stale? }` — 2–15 KB. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); `COLLECTION_NOT_FOUND` (the storefront does not declare the type; `detail.refresh.tool` names `collections_list_collections`); `SCHEMA_UNAVAILABLE` (the storefront could not be read; retry); `INVALID_INPUT` for an unknown component (`detail.available`). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_get_schema
Return the full Studio-shaped usage detail for one or more components in a single call — per component: its prop fields (each tagged `translatable: boolean`), its `ai` usage guidance (purpose, constraints, hints, relationships, supports) when set, and, for a section, its slots with the block names each slot accepts; for a block, the sections it's designed to live in (`belongsIn`). **When to use:** Use to construct valid `props` for `studio_add_section` / `studio_add_block`, pick a `keyPath` for `studio_set_static_props`, check which blocks a slot allows, or read a component's usage guidance. Pass every component you are about to work with in one call (e.g. a section and the blocks that go in its slots) rather than one call per name. Discover the `componentNames` via `studio_list_components` first. **When NOT to use:** Don't use to enumerate components — use `studio_list_components`; pass only the specific names you need, never the whole catalog. The value shapes for complex fields (link/color/media/json) live in the `studio_get_data_model` tool, not here. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","componentNames":["SectionHero","BlockFeatureTile"]}` **Response:** `{ projectId, catalogVersion, schemas: Array<{ componentName, kind, studioLabel, ai?, schema, slots?, belongsIn? }>, notFound: string[] }` — one `schemas` entry per resolved component (`slots` is section-only, `belongsIn` is block-only); `projectId`/`catalogVersion` are hoisted to the root. `notFound` lists requested names absent from the catalog at `catalogVersion` — a typo or catalog drift; re-list with `studio_list_components` and retry them. Duplicate names resolve once. Roughly (name count × 5-25 KB). Requires a reachable frontend; returns `FRONTEND_UNREACHABLE` when the project frontend is down. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_get_component_schemas
Return the Studio data-model primer (markdown): page-types vs pages vs page-variants, sections vs blocks, slots, global sections, static vs translatable props, the complex-prop value shapes (link/color/media/json), content collections, and a worked example. Static and project-independent. Delivered as a tool so it loads reliably on every host, including those that can't read MCP resources autonomously. **When to use:** Call this FIRST, once per session, before listing or editing anything — it is the domain context every other tool assumes. Re-read its "Complex prop subtypes" section before writing a link / color / media / json prop with `studio_set_static_props`. **When NOT to use:** Don't use to discover a project's installed components or their schemas — use `studio_list_components` / `studio_get_component_schemas`. This is generic Studio documentation, not project data. **Example call:** `{}` **Response:** `{ markdown }` — the primer as a single markdown string (~24 KB plus a "Collections" section of about 5.0 KB). Static; cache it for the session and do not refetch.
studio_get_data_model
Return one entry: each component in the authoring view (the shape the write tools take), its links, its validation issues and its state. Reads the live form when a person has the entry open. Reading a live entry also shows you among the entry's participants. **When to use:** Use to read an entry before editing it, and to find the list item ids that `collections_edit_array_items` and `collections_remove_array_items` address. Pass `components` for the ones a large entry listed in `omitted`. **When NOT to use:** Don't use to read one value — use `collections_get_field`. Don't use to find entries — use `collections_list_entries`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entryId":"a1b2c3d4e5f6g7h8j9k0"}` **Response:** `{ entryId, entityType, revision, status, live, data: { <component>: value }, links: { <token>: entryId[] }, issues, omitted? }` — about 100 KB at most. `issues` belong to the data returned: for a live entry, its live form. `live: true` means a person has the entry open; publication is `status`. A component without a stored value is left out of `data`, also when `components` names it. Components are added in order; once one does not fit, it and every later one go to `omitted: Array<{ component, bytes }>`: read them with `components`, and a single component above the cap field by field with `collections_get_field`. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); `ENTRY_NOT_FOUND`; `COLLECTION_NOT_FOUND` (the storefront no longer declares the entry's type; `detail.refresh.tool` names `collections_list_collections`); `SCHEMA_UNAVAILABLE`; `INVALID_INPUT` for an unknown component (`detail.available`). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_get_entry
Return one value of an entry, addressed by `at` + `keyPath` exactly as a `collections_set_fields` write item addresses it. A localized field comes back as its `{ locale: value }` map, which `values` accepts as it is. **When to use:** Use to read the current value before a `collections_set_fields` write, or to read a component too large for `collections_get_entry`. **When NOT to use:** Don't use to read a whole entry — use `collections_get_entry`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","at":{"entryId":"a1b2c3d4e5f6g7h8j9k0","component":"content"},"keyPath":["title"]}` **Response:** `{ value, live }` — usually under 1 KB. `value` is `null` when nothing is stored there. `live: true`: a person has the entry open, and the value is read from their live form. Reading a live entry also shows you among the entry's participants. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); `ENTRY_NOT_FOUND`; `COLLECTION_NOT_FOUND` (the storefront no longer declares the entry's type; `detail.refresh.tool` names `collections_list_collections`); `SCHEMA_UNAVAILABLE`; `ARRAY_ITEM_NOT_FOUND`; `INVALID_INPUT` for an unknown component or field (`detail.reason`). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_get_field
Return one page variant's section/block tree — IDs, component names, slot membership. Excludes props by default; pass `includeProps: true` to inline each LOCAL section's full payload in the same call (see below). **When to use:** Use to find a `sectionId` or `blockId` to mutate. Call after `studio_list_pages` to look up `pageVariantId`. To read the props of MANY sections on a page, prefer `includeProps: true` here over a `studio_get_section` per section — one call instead of N. Use a bare `studio_get_section` only for a SINGLE section or a global-section reference. **When NOT to use:** Don't use to enumerate pages — use `studio_list_pages`. Don't set `includeProps` when you only need structure (it can add hundreds of KB). Global-section references are never inlined — read one via `studio_get_section` with its `globalSectionId`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","pageVariantId":"a1b2c3d4e5f6g7h8j9k0","includeProps":true}` **Response:** `{ pageVariantId, pageId, pageType, path, variantLabel, seo, sections, queryCount }`. Structure-only is usually 5-10 KB. `seo` is the variant's `{ title, description, robots? }` where `title`/`description` are the raw `{ <locale>: value }` maps (empty `{}` when unset) — edit them via `studio_update_page_variant`. `queryCount` is the number of queries defined on this page variant (no per-query payload via this server yet). With `includeProps: true`, every LOCAL `sections[<loc>][]` entry additionally carries `payload` — the exact object `studio_get_section` returns for that id (props + its blocks) — growing the response to roughly (section count × 5-25 KB); global-reference entries are unchanged. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_get_page_tree
Read one prop value off a section or block, addressed by the same `at` + `keyPath` shape as a `studio_set_static_props` write item. Returns just the addressed value — the targeted read-before-write counterpart of `studio_get_section` (which returns the whole 5–25 KB section payload). **When to use:** Use to read the current value of a prop before overwriting it with `studio_set_static_props`. Discover the entity id via `studio_get_page_tree`. For a translatable field `value` is the `{ <lang>: value }` map — edit the entries you want and write them back through a `studio_set_static_props` write item's `values` field (a write item's single `value` field writes one value, optionally fanned across `locales`, and does NOT accept the map). For a complex primitive (link/color/media/json) `value` is the subtype-tagged object, which a `studio_set_static_props` write item's `value` accepts directly — see the "Complex prop subtypes" section of the `studio_get_data_model` tool for each shape. `value: null` means there is no stored value at `keyPath` — covering BOTH a never-set prop AND an unknown field/path (this read is storage-direct and schema-unaware, so it cannot tell the two apart; validate field names with `studio_get_component_schemas`). A field that exists but is empty returns its stored value (e.g. `""` for a freshly-created text field), NOT `null`. And `null` means "not set in storage", not "renders nothing" — an unset field still renders its field type fallback, so do not infer the rendered value from `null`. **When NOT to use:** Don't use to read a section's whole prop set — use `studio_get_section`. Don't use to enumerate structure — use `studio_get_page_tree`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","at":{"blockId":"c3e5g7j9l1n3p5r7t9v1"},"keyPath":["cta","label"]}` **Response:** `{ value }` — the prop value at `keyPath` (primitive, subtype-tagged complex object, or `{ <lang>: value }` for a translatable field), or `null` when nothing is stored at `keyPath` — which covers a never-set prop AND an unknown field/path (this read is schema-unaware); a field that exists but is empty returns its stored value (e.g. `""`), not `null`. Typically well under 1 KB. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_get_prop
Return one section's full payload — component name, studio metadata, visibility, props, slots. Resolves either a local section (on a page variant) or a global section by id. **When to use:** Use to read the current value of a prop before deciding how to mutate it with `studio_set_static_props`, or to inspect a complex-primitive prop shape before overwriting it. Discover the `sectionId` via `studio_get_page_tree` first. **When NOT to use:** Don't use to enumerate sections — use `studio_get_page_tree`. Don't use to fetch a component's schema — use `studio_get_component_schemas`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","sectionId":"b8h2k4m6p0r3t5v7c9d1"}` **Response:** `{ sectionId, componentName, studioLabel?, scope: "local"|"global", payload, …plus pageVariantId+location when scope==="local" }` — 5-25 KB depending on props. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_get_section
Return the project's color palette (flattened to a single list) and icon catalog — `{ themeId, colors, icons }`. **When to use:** Use before setting a `color` prop (use a palette entry's `id` as `token` and its `value` as the hex) or an `icon` prop (use an icon `name`). Read the field's type from `studio_get_component_schemas` to know which applies. **When NOT to use:** Don't use to discover components — use `studio_list_components`. For a `media` prop, use `media_list_assets` or `media_import_from_url`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g"}` **Response:** `{ themeId, colors: Array<{ id, label?, value, hint?, group? }>, icons: Array<{ name, label? }> }` — a few KB. Requires a reachable frontend; returns `FRONTEND_UNREACHABLE` when the project frontend is down. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_get_style_tokens
Fetch one file from a public https URL on port 443 and store a copy in one of the project's media libraries. Returns the new library item. The library must take staged uploads (`capabilities.upload.transfer` is `staged` in `studio_get_capabilities` → `mediaLibraries`); it also sets the accepted types (`capabilities.upload.accept`) and the size limit in bytes (`capabilities.upload.maxFileSize`). **When to use:** Use when content needs an image, video or document that is not in the library yet — for example a URL from a CSV. Import each distinct URL once and reuse the returned item: every call without a repeated `idempotencyKey` makes a new asset. Write the item into a field as the `studio_get_data_model` primer describes; never store the source URL as a plain-URL source (`provider: 'raw'` or `'none'`) when a library can import. **When NOT to use:** Don't use for a file that is already in the library — find it with `media_list_assets`. After reason `timeout` or `processing_failed`, or an `upload_failed` whose message starts with `At finalize:`, the file can still be in the library: check with `media_list_assets` before you try again. The call stops after 45 s. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","library":"@laioutr-app/cms","url":"https://images.example.com/summer/banner.jpg"}` **Response:** The `item`: a media item `{ kind?: "media", media, previewUrl?, fileName?, externalId?, … }`, or a file item `{ kind: "file", file: { url, mimeType, size? }, … }` for a document such as a PDF. About 1 KB. A failed import returns `MEDIA_IMPORT_FAILED` with `detail.reason`: `source_unavailable` (the URL failed, or is not a public https URL on port 443), `unsupported_type` (also when the library takes the type in general but not in this project, for example video without the video CDN), `too_large`, `upload_failed`, `processing_failed` or `timeout`. `CAPABILITY_UNSUPPORTED` means the frontend is too old or the library takes no staged uploads. `INVALID_INPUT` for `folderId` on a library without folders. Also `FRONTEND_UNREACHABLE` (the storefront did not answer; after an upload started, check with `media_list_assets` before retrying) and `LIBRARY_NOT_FOUND`. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
media_import_from_url
List the project's content collections — each an entity type with its components — and the project locales with their fallbacks. **When to use:** Use first, to find an `entityType` for the other `collections_*` tools and the locale codes a localized field takes. **When NOT to use:** Don't use to read a collection's fields or link tokens — use `collections_get_schema`. Don't use to list entries — use `collections_list_entries`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g"}` **Response:** `{ collections: Array<{ entityType, components, slug?, app? }>, locales: Array<{ code, name, fallbacks }>, reachable, staleSince? }` — under 2 KB. `reachable: false` means the storefront could not be read: the list is the last known one (`staleSince`) or empty. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_list_collections
List the section and block components installed in this project — a thin `{ name, kind, label, description?, purpose?, tags? }` per entry (`purpose` is the agent-facing one-liner). Pass `kind` to list only sections or only blocks. **When to use:** Use to discover valid `componentName` values before `studio_add_section` / `studio_add_block`. Component names are arbitrary per-project strings (customers install different apps) — never guess them, read them here — then fetch one component's full usage detail (prop fields, slots, `ai` usage fields, `belongsIn`) with `studio_get_component_schemas`. **When NOT to use:** Don't use to read a component's prop fields, slots, or usage guidance — use `studio_get_component_schemas`. Don't use to list reusable global sections — use `studio_list_global_sections`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g"}` **Response:** `{ projectId, catalogVersion, components: Array<{ name, kind: "section"|"block", label, description?, purpose?, tags? }> }` — a few KB. `catalogVersion` is the catalog fingerprint (also surfaced in mutation error `detail.catalogVersion`) — re-list when it changes. Requires a reachable frontend; returns `FRONTEND_UNREACHABLE` when the project frontend is down. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_list_components
List the entries of one collection, a page at a time. Search them by label with `query`, or filter them by exact field values with `where` (for example a SKU): each condition names a field by `component` + `keyPath` and gives its value in `equals`, with `locale` for a localized field. `where` reads the saved draft, so an entry a person has open is matched on its last save. **When to use:** Use to find an `entryId`, and before creating an entry, to find one that already has the same key value. Page with `limit` (1–50, default 20) and `offset`. **When NOT to use:** Don't use to read an entry's values — use `collections_get_entry`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entityType":"ProductVariant","where":[{"component":"base","keyPath":["sku"],"equals":"SKU-1001"}]}` **Response:** `{ entries: Array<{ entryId, label, status, valid, issueCount, live, updatedAt }>, total }` — about 150 B per row. `live: true`: a person has the entry open. `total` counts the matching entries; with `query` it is the rows returned, since a query returns the best `limit` matches. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); for a `where` condition that does not fit the field model, the codes of `collections_set_fields` with `detail.itemIndex` (`INVALID_INPUT` for an unknown component or field or a value of the wrong type, `MISSING_LOCALES` / `INVALID_LOCALES`); with `where`, also `COLLECTION_NOT_FOUND` and `SCHEMA_UNAVAILABLE`; `INVALID_INPUT` with `detail.reason: "WHERE_WITH_QUERY"` for `where` together with `query`. Only text, number, boolean and enum fields can be filtered. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_list_entries
List the project's reusable global sections — `{ globalSectionId, componentName, studioLabel?, usageCount }` per entry, ids only, no block contents. **When to use:** Use to discover which global sections exist before placing one with `studio_add_global_section_reference`, then read a global's blocks via `studio_get_section` with its `globalSectionId`. Served straight from Storage — works even when the frontend is unreachable. **When NOT to use:** Don't use to read a global section's blocks — use `studio_get_section`. Don't use to list a project's installed component types — use `studio_list_components`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g"}` **Response:** `{ projectId, globalSections: Array<{ globalSectionId, componentName, studioLabel?, usageCount }> }` — ~1–2 KB. `usageCount` is how many page-variant references point at the global. An empty project returns `globalSections: []`, never an error. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_list_global_sections
List or search the assets in one of the project's media libraries, one page at a time, with the subfolders of the folder you list. Library ids and what each library can do (`capabilities.search`, `capabilities.folders`) come from `studio_get_capabilities` → `mediaLibraries`. **When to use:** Use before you import a file, to reuse an asset that is already in the library. Use after a `media_import_from_url` that failed with reason `timeout`: the file can still have arrived. Write an item into a field as the `studio_get_data_model` primer describes. **When NOT to use:** Don't use to add a file — use `media_import_from_url`. Don't pass `term` to a library whose `capabilities.search` is false, or `folderId` to one whose `capabilities.folders` is false. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","library":"@laioutr-app/cms","term":"banner","type":["image"]}` **Response:** `{ items, folders?, nextCursor?, total? }` as the library returns it. Each item is a media item `{ kind?: "media", media, previewUrl?, fileName?, externalId?, file?, tags? }` or a file item `{ kind: "file", file: { url, mimeType, size? }, … }`; only a file item always has `kind`. `folders` comes on the first page only; `nextCursor` is absent on the last page. About 1 KB per item. An unknown `folderId` can give an empty page. `LIBRARY_NOT_FOUND` names the libraries the project has. Requires a reachable frontend: `FRONTEND_UNREACHABLE` only when the storefront does not answer. `INVALID_INPUT` when the library refuses the request (for example an unknown `cursor`, `term` on a library that cannot search, or `folderId` on a library without folders). `MEDIA_LIBRARY_FAILED` when the library fails: retry later. Also `CAPABILITY_UNSUPPORTED` (the frontend is too old). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
media_list_assets
List every page in the project with id, type, path, and each variant's `{ id, label }` — no section tree, no props. **When to use:** Use first to inventory pages and grab the `pageVariantId` you need to pass to `studio_get_page_tree` and any page-targeted mutation. **When NOT to use:** Don't use to read a page's structure — use `studio_get_page_tree`. Don't use to read a single section's props — use `studio_get_section`. Page and variant creation are not supported via this server — ask the human to create the page in Studio, then add sections/blocks to it with `studio_add_section`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g"}` **Response:** `{ projectId, pageCount, pages: PageSummary[] }` where `PageSummary = { id, type, path, variantCount, variants: { id, label }[] }` — usually under 2 KB. Each `PageSummary.type` is a stable token defined by the customer's frontend (typical values: `home`, `category`, `product`, `static`). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_list_pages
List every Laioutr project the signed-in user has access to, across all organizations. Each row carries `mcpEnabled` — when `false`, project-scoped tools will reject calls with `MCP_NOT_ENABLED`. **When to use:** Use first, to find a `projectId` for any other tool. Cheap; no pagination. Re-call only if the human mentions a project that is not in the previous response. **When NOT to use:** Don't use to read a specific project's structure — once you have a `projectId`, switch to `studio_list_pages` / `studio_get_page_tree`. Don't use to enumerate organizations — this tool returns the org context per project already. **Example call:** `{}` **Response:** `{ projectCount, projects: Array<{ id, name, slug, organizationId, organizationName, organizationSlug, mcpEnabled, locales: Array<{ code, name, fallbacks }> }> }` — `locales` are the project's configured languages (the allowed values for `studio_set_static_props.writes[].locales`). Usually well under 5 KB.
project_list
Relocate a block to a LOCAL section's slot, or reorder it within its current slot — preserving the block id and all its props (unlike delete + re-add, which loses both). The destination block component must be allowed in the target slot — verify with `studio_get_component_schemas` for the target section. **When to use:** Use to reposition an existing block: move a CTA into a different section, or change a block's order within a slot (same `to.sectionId` + `to.slot`, different `to.atIndex`). Discover the `blockId` and target `sectionId`/slot with `studio_get_page_tree` / `studio_get_section`. **When NOT to use:** Don't use to add a new block — use `studio_add_block`. Don't use to move a SECTION — use `studio_move_section`. Targets are LOCAL sections only: global sections are read-only via this server, so neither the target nor a block currently INSIDE a global section is accepted (moving one out is rejected with `INVALID_INPUT`). NOTE: a block nested below a global-section *reference* on a page is detected as global only when it resides in the global's own storage; the source guard is best-effort. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","blockId":"c3e5g7j9l1n3p5r7t9v1","to":{"sectionId":"b8h2k4m6p0r3t5v7c9d1","slot":"items"}}` **Response:** `{ blockId, catalogVersion, warnings }` — `blockId` is the moved block (id unchanged). `catalogVersion` is the component-catalog fingerprint at write time. `SLOT_NOT_FOUND` (carries `detail.availableSlots`) means the target slot name does not exist on the section component; `BLOCK_NOT_ALLOWED_IN_SLOT` (carries `detail.catalogVersion` + `detail.refresh.tool`) means the slot exists but the block is not permitted in it. `QUERY_SCOPE_VIOLATION` means the move would orphan or import a query across the global/page boundary. `BLOCK_NOT_FOUND` / `SECTION_NOT_FOUND` mean the source block or target section id did not resolve. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_move_block
Reorder a section within its container, or relocate it between containers (header/body/footer) and between page variants — preserving the section (and, for a global-section reference, its link to the global). Works for a LOCAL section OR a global-section reference placed on a page. **When to use:** Use to reposition an existing section: reorder it in its container (same variant + `location`, new `to.atIndex`), move it from body to header/footer, or move it onto another page variant (`to.pageVariantId`). Moving a global-section *reference* around a page is allowed — it edits the PAGE, not the global. Discover the `sectionId` and target variant/container with `studio_get_page_tree` / `studio_list_pages`. **When NOT to use:** Don't use to add a new section — use `studio_add_section`. Don't use to move a BLOCK — use `studio_move_block`. A query-bound (dynamic) LOCAL section cannot be moved to a different page variant (`QUERY_SCOPE_VIOLATION`): its page-scoped query would be orphaned. Reorders and same-page relocations are always fine; references move freely across pages. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","sectionId":"b8h2k4m6p0r3t5v7c9d1","to":{"location":"header","atIndex":0}}` **Response:** `{ sectionId, catalogVersion, warnings }` — `sectionId` is the moved section (id unchanged). `catalogVersion` is the component-catalog fingerprint at write time. `QUERY_SCOPE_VIOLATION` means a cross-page move would orphan the section's page query. `SECTION_NOT_FOUND` / `PAGE_VARIANT_NOT_FOUND` mean the source section or target variant id did not resolve. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_move_section
Publish 1–50 entries to the live storefront, one after another in the given order. Each entry is checked against the deployed storefront's schema and publishes or fails on its own: the call is not atomic. An entry a person has open is published with the changes in their form. **When to use:** Use when the person asks to publish, after the entries are complete. Publish link targets (variants, categories) before the entries that link to them, and check `issues` with `collections_get_entry` first. **When NOT to use:** Don't use to save a draft — every write already saves it. Unpublishing and deleting entries are not available here. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entryIds":["a1b2c3d4e5f6g7h8j9k0"]}` **Response:** `{ results: Array<{ entryId, published: { revision, publishedAt } } | { entryId, error: { code, message, detail? } }> }` — one element per entry, in order. A publish replaces what the storefront shows for the entry. Per-entry codes: `PUBLISH_REJECTED` (the draft has blocking issues, listed in `detail.issues`); `ENTRY_NOT_FOUND`; `ENTRY_BUSY` (retry after `detail.retryAfterMs`); `ENTRY_UNSAVED` (the open form's latest changes are not saved yet: retry when `detail.retryable` is true, otherwise ask the person to reopen the entry); `COLLECTION_NOT_FOUND` (the storefront no longer declares the entry's type; `detail.refresh.tool` names `collections_list_collections`); `SCHEMA_UNAVAILABLE`; `PROJECT_LOCKED`. Errors of the whole call: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_publish_entries
Remove 1–50 items of list fields on ONE entry in one atomic call. The items and their content are removed from the entry. **When to use:** Use to delete list items. Item ids are in `collections_get_entry` (`[{ id, value }]`). **When NOT to use:** Don't use to reorder — use `collections_edit_array_items`. Deleting a whole entry is not available here. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entryId":"a1b2c3d4e5f6g7h8j9k0","removals":[{"component":"content","keyPath":["faq"],"arrayItemId":"k3m9p2"}]}` **Response:** `{ results, revision, issues, live, pulled }` — each result is `{}`. The write changes only this project's draft, never the storefront. `results` has one element per batch item, in order; `revision` is the draft revision; `issues` are the entry's validation issues after the write. `live: true`: the entry is open in this project's Cockpit editor, and the write also went into that open editor. `pulled: false`: the write is in the open editor, but the draft row is not updated yet; the next save of the entry writes it, so do not repeat the write. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); `ENTRY_NOT_FOUND`; `COLLECTION_NOT_FOUND` (the storefront no longer declares the entry's type; `detail.refresh.tool` names `collections_list_collections`); `ENTRY_BUSY` (the entry is being opened or closed: retry after `detail.retryAfterMs`. It also comes back when an open entry's live copy is lost, before or after the write reached it: nothing of the call is kept, so retry; the retry succeeds once the person leaves the entry, and then writes the draft row); `ENTRY_TOO_LARGE`; `PROJECT_LOCKED` (a restore or migration holds the project); `SCHEMA_UNAVAILABLE`. The batch is written whole or not at all: an invalid item fails the call with `detail.itemIndex` (0-based), and `INVALID_INPUT` carries the cause in `detail.reason`. Also `ARRAY_ITEM_NOT_FOUND`. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_remove_array_items
Write 1–50 values on ONE entry in one atomic call. Each write item addresses a value with `component` + `keyPath` and carries `value` (with `locales` for a localized field), or `values` (a `{ locale: value }` map; only the listed locales change, a `null` entry clears that locale); `variant` switches a union. `null` clears. When the entry is open in the project's Cockpit editor, the values appear there at once. **When to use:** Use to fill in or change field values. Read the field model with `collections_get_schema` and the current value with `collections_get_field`. Batch the writes for one entry into one call. For a media field, write `item.media` from `media_list_assets` or `media_import_from_url`. **When NOT to use:** Don't use to change a list that holds items — use `collections_edit_array_items` and `collections_remove_array_items`; a list with no items takes `[]` or `[{ value }, …]`, and a `null` is refused on a list that holds items and on an object that contains one. Don't use for links — use `collections_set_links`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entryId":"a1b2c3d4e5f6g7h8j9k0","writes":[{"component":"content","keyPath":["title"],"value":"Winter sale","locales":["en"]},{"component":"content","keyPath":["teaser"],"values":{"en":"Up to 30% off","de":"Bis zu 30% Rabatt"}}]}` **Response:** `{ results: Array<{ writtenLocales }>, revision, issues, live, pulled }` — The write changes only this project's draft, never the storefront. `results` has one element per batch item, in order; `revision` is the draft revision; `issues` are the entry's validation issues after the write. `live: true`: the entry is open in this project's Cockpit editor, and the write also went into that open editor. `pulled: false`: the write is in the open editor, but the draft row is not updated yet; the next save of the entry writes it, so do not repeat the write. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); `ENTRY_NOT_FOUND`; `COLLECTION_NOT_FOUND` (the storefront no longer declares the entry's type; `detail.refresh.tool` names `collections_list_collections`); `ENTRY_BUSY` (the entry is being opened or closed: retry after `detail.retryAfterMs`. It also comes back when an open entry's live copy is lost, before or after the write reached it: nothing of the call is kept, so retry; the retry succeeds once the person leaves the entry, and then writes the draft row); `ENTRY_TOO_LARGE`; `PROJECT_LOCKED` (a restore or migration holds the project); `SCHEMA_UNAVAILABLE`. The batch is written whole or not at all: an invalid item fails the call with `detail.itemIndex` (0-based), and `INVALID_INPUT` carries the cause in `detail.reason`. Also `MISSING_LOCALES` / `INVALID_LOCALES` (`detail.allowed`), `ARRAY_ITEM_NOT_FOUND`. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_set_fields
Replace one link list of an entry with `targetEntryIds`, in the given order. `[]` clears it. Each target must exist and be of the link's `target` type. **When to use:** Use to set the entries an entry links to. Link tokens and their target types are in `collections_get_schema` (`links`); the current lists are in `collections_get_entry` (`links`). **When NOT to use:** Don't use to change field values — use `collections_set_fields`. To add one link, pass the whole list including it. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","entryId":"a1b2c3d4e5f6g7h8j9k0","linkToken":"relatedPosts","targetEntryIds":["b8h2k4m6p0r3t5v7c9d1"]}` **Response:** `{ results, revision, issues, live, pulled }` — `results` is `[{}]`. The write changes only this project's draft, never the storefront. `results` has one element per batch item, in order; `revision` is the draft revision; `issues` are the entry's validation issues after the write. `live: true`: the entry is open in this project's Cockpit editor, and the write also went into that open editor. `pulled: false`: the write is in the open editor, but the draft row is not updated yet; the next save of the entry writes it, so do not repeat the write. Errors: `MCP_NOT_ENABLED`, `FORBIDDEN`, `PROJECT_NOT_FOUND`, `CMS_NOT_ENABLED` (the project has no content editing); `ENTRY_NOT_FOUND`; `COLLECTION_NOT_FOUND` (the storefront no longer declares the entry's type; `detail.refresh.tool` names `collections_list_collections`); `ENTRY_BUSY` (the entry is being opened or closed: retry after `detail.retryAfterMs`. It also comes back when an open entry's live copy is lost, before or after the write reached it: nothing of the call is kept, so retry; the retry succeeds once the person leaves the entry, and then writes the draft row); `ENTRY_TOO_LARGE`; `PROJECT_LOCKED` (a restore or migration holds the project); `SCHEMA_UNAVAILABLE`. The batch is written whole or not at all: an invalid item fails the call with `detail.itemIndex` (0-based), and `INVALID_INPUT` carries the cause in `detail.reason`. Also `LINK_TOKEN_NOT_FOUND`; `LINK_TARGET_INVALID` (`detail.invalid` names each target that is missing or of the wrong type). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
collections_set_links
Write 1–50 static prop values on sections or blocks (local or inside a global section) in ONE atomic mutation. Each item in `writes` is a self-contained write: `at` + `keyPath` address the prop, and exactly one of `value` (one value, optionally fanned to several `locales`) or `values` (a per-locale map writing DIFFERENT text per locale) carries the payload. Mixed targets are fine — different sections/blocks, across page variants, in one call. The global section container itself cannot be targeted — only blocks within it, via their `blockId`. For complex primitives (link/color/media/json) build the subtype-tagged object documented in the "Complex prop subtypes" section of the `studio_get_data_model` tool, and read the current shape via `studio_get_prop` / `studio_get_section` before overwriting. **When to use:** Use to fill in copy, images, links, colors — any static values on existing entities. BATCH related edits: conceptually "set the props of this page" is ONE call, not one call per prop. Discover entity ids with `studio_get_page_tree`, inspect current props via `studio_get_section`, and read each field's `translatable` flag with `studio_get_component_schemas`: when `true`, pass the SAME value to several locales via `value` + `locales`, or DIFFERENT text per locale via the `values` map (which `studio_get_prop` returns ready to edit); when `false` `locales`/`values` are optional. `values` merges (only listed locales change; a `null` entry clears just that locale). Duplicate targets in one batch apply in order, last one wins. For a `media` prop, take the value from `media_list_assets` or `media_import_from_url` (shape in the data-model primer). **When NOT to use:** Don't use to write a value bound to a query result — not yet available via this server (ask the human to wire it up in Studio). Don't use to rename or hide an entity — use `studio_update_section` / `studio_update_block`. Cap is 50 writes per call; split larger jobs across calls. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","writes":[{"at":{"blockId":"c3e5g7j9l1n3p5r7t9v1"},"keyPath":["cta","label"],"value":"Shop now","locales":["en","de"]},{"at":{"sectionId":"b8h2k4m6p0r3t5v7c9d1"},"keyPath":["heading"],"values":{"en":"Sale","de":"Angebot"}}]}` **Response:** `{ results, catalogVersion, warnings }` — `results` is index-aligned with `writes`, each `{ writtenLocales, warnings }`; `catalogVersion` is the component-catalog fingerprint at write time (once per batch); `warnings` is snapshot-level. On failure the whole batch is rejected atomically: the message is prefixed `writes[<i>]:` and `detail.itemIndex` (0-based) names the bad item. Catalog-drift errors (`COMPONENT_NOT_FOUND` / `BLOCK_NOT_ALLOWED_IN_SLOT`) carry `detail.catalogVersion` + `detail.refresh.tool`; locale errors (`MISSING_LOCALES` / `INVALID_LOCALES`) carry `detail.allowed` (the project locales). Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_set_static_props
Update a block's studio metadata (label), visibility decorator, or rules. Pass only the top-level keys (`studio`, `visibility`, `rules`) you want to touch — omitted keys are preserved. `visibility` and `rules` are absolute-state: each provided object replaces the stored one in full. `studio` is shallow-merged: the studio keys you provide overwrite their stored values, other studio keys are preserved. **When to use:** Use to rename a block in the layer tree, change its visibility condition, or update its rules. Discover the `blockId` with `studio_get_page_tree`. **When NOT to use:** Don't use to change component props — use `studio_set_static_props`. Don't use to remove the block — use `studio_delete_block`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","blockId":"c3e5g7j9l1n3p5r7t9v1","data":{"studio":{"label":"Primary CTA"}}}` **Response:** `{ blockId, catalogVersion, warnings }` — `catalogVersion` is the component-catalog fingerprint at write time. Catalog-related errors carry `detail.catalogVersion` + `detail.refresh.tool`; re-list with `studio_list_components` when either changes. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_update_block
Update a page variant's SEO metadata (`title`, `description`) and/or its Studio layer-tree `label`. SEO fields are localized via the same `value` / `values` / `locales` contract as a `studio_set_static_props` write item (always translatable), and merge per-locale — writing `en` never clobbers an existing `de`. Pass the fields under `data`; omitted keys are preserved. This edits PAGE-level metadata; it does not touch section/block props. **When to use:** Use to set or rewrite a page's SEO title/description, or to rename a variant in the layer tree. Read the current values first from `studio_get_page_tree` (its `seo` block returns the `{ <locale>: value }` maps ready to edit). SEO is stored PER VARIANT — on a localized site, update each locale's variant (or pass every locale via `values`). **When NOT to use:** Don't use to edit a section or block's component props — use `studio_set_static_props`. Page and variant creation are not supported — ask the human to create them in Studio. Setting `robots` is not yet exposed. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","pageVariantId":"a1b2c3d4e5f6g7h8j9k0","data":{"seo":{"title":{"value":"Best Running Shoes","locales":["en"]},"description":{"values":{"en":"Shop our range.","de":"Entdecke unser Sortiment."}}}}}` **Response:** `{ pageVariantId, writtenLocales, catalogVersion, warnings }` — `writtenLocales` is `{ title?: string[]; description?: string[] }`, the locales written per SEO field touched (absent for a label-only change). `PAGE_VARIANT_NOT_FOUND` means the id did not resolve; `INVALID_LOCALES` / `MISSING_LOCALES` carry `detail.allowed` (the project locales); `INVALID_INPUT` means `data` had no actionable key or a field set both `value` and `values`. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_update_page_variant
Update a section's studio metadata (label) or visibility decorator. Pass the fields under `data` — `data.studio` (label) and/or `data.visibility` — that you want to touch; omitted keys are preserved. `visibility` is absolute-state: the provided object replaces the stored one in full. `studio` is shallow-merged: the studio keys you provide overwrite their stored values, other studio keys are preserved. **When to use:** Use to rename a section in the layer tree or change its visibility conditions. Read the current value with `studio_get_section` first so the overwrite is informed. **When NOT to use:** Don't use to change the section's component props — use `studio_set_static_props`. Don't use to remove a section — use `studio_delete_section`. **Example call:** `{"projectId":"9m4e2mr0ui3e8a215n4g","sectionId":"b8h2k4m6p0r3t5v7c9d1","data":{"studio":{"label":"Above-the-fold hero"}}}` **Response:** `{ sectionId, catalogVersion, warnings }` — `catalogVersion` is the component-catalog fingerprint at write time. Catalog-related errors carry `detail.catalogVersion` + `detail.refresh.tool`; re-list with `studio_list_components` when either changes. Scoped to `projectId`. IDs (sectionId, blockId, pageVariantId, …) from one project are not valid in another.
studio_update_section
Creates a product feed for one channel and market of an environment. marketId must be a market id and channel a channel id, both exactly as distributr_feed_options returns them — never a name or a label. Give it a short name and a file name without extension. It starts with the channel's standard mapping and is not run: the customer runs it from the feed's page. Its result carries `readiness`: a new feed usually cannot run yet (it has no storefront address, or its product links cannot be built), so tell the customer each problem with its fix instead of telling them to run it. Pass the projectId from project_list; the call acts only inside that project.
How each feed of one environment has been running lately: its newest run and what succeeded or failed over the last days. Pass the projectId from project_list; the call acts only inside that project.
Lists what a new product feed can be made for in one environment: its markets (id, name) and the channels feeds are written for (id, label — Google Merchant Center, Meta, idealo, …). Call it before distributr_create_feed to pick the market and the channel id. Pass the projectId from project_list; the call acts only inside that project.
Says whether one feed can run and publish a usable file, and if not, each problem with how to fix it: no storefront address, a storefront that cannot be read, product links that cannot be built, or required columns without a source. Call it before telling a customer to run a feed, whenever they ask whether a feed is ready or why it does not work, and after changing a feed. Never tell them to run a feed it reports problems for; say the problems and their fixes instead, or offer to set the storefront address or the product link pattern for them. Pass the projectId from project_list; the call acts only inside that project.
Reads one feed: its channel, market, schedule, selection rules, field mapping (the columns of the file and where each value comes from) and public address. Pass the projectId from project_list; the call acts only inside that project.
Lists the product feeds of one environment, each with its channel, market, file name, public address, when it was last published and whether a run is going on now. Pass the projectId from project_list; the call acts only inside that project.
Lists a feed's latest runs: when each started and ended, whether it succeeded, how many products it wrote, and the error a failed run stopped on. Read this when a customer reports that a feed failed. Pass the projectId from project_list; the call acts only inside that project.
Lists the product fields the feed's storefront actually returns, to choose sources from when mapping a column. Asks the storefront, so it can take a few seconds or say why it could not. Pass the projectId from project_list; the call acts only inside that project.
Changes a feed: its name, schedule, whether it is on, its storefront address (sourceEndpoint, the storefront's public URL), its product link pattern (rules.productUrlPattern, such as https://shop.example.com/p/{slug}, sent with the feed's other rules), selection rules, or its mapping — the list of columns, each with a header, a source field and a transform. To add a custom column such as a price, read the feed first, then send the whole mapping with the new column added; columns left out are removed. Pass the projectId from project_list; the call acts only inside that project.
Files a report to Laioutr support as the customer, from what they told you — once they asked or agreed to send it. The card they confirm is their agreement: propose it straight away and never ask for agreement in words first, and never say it is filed before it ran. kind: bug for something broken (then also stepsToReproduce, expectedResult, ordersBlocked, ongoing, workaround), help for a question, idea for a wish. severity: s1 when a live shop cannot sell, s2 when an important feature is broken, s3 for a smaller problem, s4 for a question or an idea. Write a short title and, in body, what happens in their words. pagePath, screenshot and clientContext may be null. Support triages it and answers in the report. Pass the projectId from project_list; the call acts only inside that project.
Reads one of the customer's reports: its status, and the replies support or Ask Larry wrote in it. Pass the projectId from project_list; the call acts only inside that project.
Lists the customer's reports for this project, as their Reports page shows them: how many are open and closed, and each with its title, kind, severity, status, whether support answered, and when it was sent and last changed — not its text. Closed reports older than a few weeks come only with includeOldClosed: true. Use it to answer how many reports are open, what happened to one, or which one they mean; then read one with feedback_get_report. It does not say who closed a report. Pass the projectId from project_list; the call acts only inside that project.
Laioutr ChatGPT Plugin FAQ
How the directory, categories and Discoverability Score work.
Read the methodologyHow do I improve Laioutr's ChatGPT Plugin discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Laioutr alternatives on ChatGPT?
As of 2026-11-02, Laioutr competes with AgentBuild, Allo Christian, AVSBL Website Builder, B12 Website Generator, Beste, CR Servers AI Connect, Croisa, Dazzly and 30 more in ChatGPT AI Website Builders, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.