# DBK Platform MCP Guide for LLMs This guide explains how to use the DBK Platform MCP tools safely and consistently. The tools operate on the active tenant's data and configuration; do not infer another tenant's catalog, brand, or enabled integrations from an example. > HomAroma is the reference tenant. See [`docs/PLATFORM_OVERVIEW.md`](docs/PLATFORM_OVERVIEW.md) for platform terminology. ## Core Rules - Prefer the dedicated block tools over the generic `add_page_section` / `update_page_section` tools when available. - Use `update_page_translations` when you need to set several languages for a page title in one call. - Treat localized fields and plain strings differently. - Read back the page after edits when you need confirmation. ## Finding a Page - Use `get_dynamic_page_detail` with the page slug when you already know the slug. - Use `list_dynamic_pages_metadata` when you need to search all pages quickly. - The dynamic page detail response includes the page `id`, which is the `pageId` you use for section tools. ## Create Page Response Contract Use `create_page_with_section` only when the slug does not exist. It atomically creates a new page and its first section; it does not update an existing page. If it returns `PAGE_SLUG_EXISTS`, use the returned `pageId` with `add_page_section`. To add another block to an existing page: 1. Call `get_dynamic_page_detail` with the slug. 2. Call `get_page_block_schema` for the desired block type. 3. Call `add_page_section` with the returned page `id`. 4. Call `reorder_page_sections` if the new block must move relative to existing blocks. 5. Read the page back with `get_dynamic_page_detail`. `create_dynamic_page` now returns a structured response that is easy for LLMs to parse: - `success` - `operation` - `pageId` - `slug` - `title` - `isPublished` - `diagnostic` - `page` Use `diagnostic` as the human-readable explanation, not as the only signal of success. ## Section Tool Mapping - `update_hero_article` - `update_rich_text` - `update_scent_notes_breakdown` - `update_quote_callout` - `update_product_carousel` - `update_accordion` - `update_contact_info` - `update_contact_form` These tools return a normalized response with: - `success` - `tool` - `sectionType` - `sectionId` - `updatedFields` - `appliedPayload` - `notes` - `section` ## Payload Shape Cheat Sheet ### Localized string Use this when a field can be translated: ```json { "th": "ข้อความภาษาไทย", "en": "English text" } ``` ### Plain string Use one string only for URLs, slugs, IDs, and booleans/numbers as appropriate. ## Block-Specific Rules ### `HERO_ARTICLE` - Localized: `title`, `subtitle`, `author`, `category` - Plain string: `publishDate`, `date`, `imageUrl`, `backgroundImageUrl`, `overlayColor` - The image is passed as a URL string in `imageUrl` ### `RICH_TEXT` - `title` is optional and localized - `content` is required and localized - HTML is allowed in `content` ### `ARTICLE_MEDIA` - `body` is required, localized plain text; do not send HTML - `heading`, `imageAlt`, and `imageCaption` are localized - `imageUrl` is a plain URL or website-relative media path - `imageAlt` is required whenever `imageUrl` is present - `imagePosition` accepts `left`, `right`, or `top` - `width` accepts `narrow`, `standard`, or `wide` - `background` accepts `default` or `muted` ### Article composition blocks - `ARTICLE_HEADER`: article title, subtitle, category, author, date, and optional cover image - `ARTICLE_GALLERY`: 2–8 images with required localized alt text; layouts are `grid` or `featured` - `ARTICLE_FACTS`: 1–8 concise facts or key takeaways - `ARTICLE_VIDEO`: HTTPS YouTube, Vimeo, or direct video URL; embed HTML is not accepted - `ARTICLE_REFERENCES`: 1–20 references with optional verified HTTPS URLs and notes - `ARTICLE_TABLE`: responsive technical comparison with 2–6 columns - `ARTICLE_DOWNLOAD`: 1–10 verified HTTPS documents - `ARTICLE_CTA`: one primary action and an optional secondary action - Reuse `QUOTE_CALLOUT`, `ACCORDION`, and `PRODUCT_CAROUSEL` instead of creating duplicate article-only variants ### `SCENT_NOTES_BREAKDOWN` - `title` and `description` are localized - Each note item may be a plain string or a localized string object - `heartNotes` and `middleNotes` are mirrored by the backend ### `QUOTE_CALLOUT` - `quote` is required and localized - `author` and `role` are optional localized fields ### `PRODUCT_CAROUSEL` - `title` and `description` are localized - `collectionId`, `limit`, `showPrice`, `showAddToCart`, and `products` are plain values - If `products` is omitted, the backend can auto-populate products on read-back ### `ACCORDION` - `title` is optional and localized - `categories` is required - Each category needs: - localized `name` - optional `icon` - at least one question - Each question needs localized `q` and `a` ### `CONTACT_INFO` - `title` is optional and localized - `address` and `hours` are localized - `email` and `line` are plain strings ### `CONTACT_FORM` - `title` and `subtitle` are localized - There are no extra fields ## Recommended Flow 1. Determine whether the page already exists. 2. For a new page, use `create_page_with_section` for the page and first block. 3. For an existing page, get its id and use `add_page_section`. 4. Create or update the page title translations. 5. Update existing sections using the dedicated block tool when possible. 6. Read back the page with `get_dynamic_page_detail`. 7. If a section has auto-populated fields, trust the read-back result over the raw update payload. ## Translating Page Builder Blocks Translate one language and one block at a time. This keeps the context compact and prevents a full payload replacement from overwriting existing languages. 1. Call `get_page_translation_status` with `pageId` and the target `lang`. 2. Pick one incomplete `sectionId`. 3. Call `get_page_section_translation` with that `sectionId` and target `lang`. The default fallback supplies source text for untranslated fields and lists those fields in `fallbackPaths`. 4. Translate the fields listed in `fallbackPaths`; preserve the payload's object and array shape. 5. Call `update_section_translation` with `translations` and the `updatedAt` value as `expectedUpdatedAt`. 6. Read the block again with `get_page_section_translation` and confirm `fallbackPaths` is empty. `update_section_translation` changes only the requested language in registered localized fields. It preserves other languages, URLs, IDs, layout configuration, and other non-translatable values. A `SECTION_VERSION_CONFLICT` response means the block changed after it was read; fetch it again before retrying. Use `get_page_section` with `lang=all` only when every stored language map is genuinely needed. A specific language returns resolved strings and uses fallback by default. ## Editing Footer Menus `footer-explore` and `footer-support` are flat menus shared by Admin, Backend, MCP, and Website. They are not Page Builder sections and do not use the Mega Menu tree tools. 1. Call `get_footer_menu` with `menuName` and the target `lang`. 2. Keep the returned `version`. 3. Preview one item-level mutation with `dryRun=true` and `expectedVersion`. 4. Apply only after the preview is correct. 5. Read the menu again and verify the result. Use `update_footer_menu_item_translation` for one language at a time. Use `reorder_footer_menu_items` only with the complete list of current item IDs. If the backend returns `MENU_VERSION_CONFLICT`, discard the old version and read the menu again. An allowed Footer menu may not exist before its first item is saved. In that case, `get_footer_menu` returns `exists=false` and `version=null`. Run `add_footer_menu_item` with `dryRun=true`; a `willCreateMenu=true` preview confirms that the actual add will create the active menu and first item atomically. Do not use legacy `update_footer_content` for menu arrays. It remains available only for the Footer brand description and social links. Website Collections come from Categories, not a Footer menu. ## What to Expect - `PRODUCT_CAROUSEL` may return products even when you did not send them. - `SCENT_NOTES_BREAKDOWN` may show mirrored note groups in the response. - `create_dynamic_page` should now return a clear success/error contract. ## Safe Usage Tips - Do not guess the field shape from the UI alone. - If a field is a URL, send a URL string. - If a field is translated, send a localized object. - If you are unsure, inspect an existing page with `get_dynamic_page_detail` first.