DBK Platform MCP Server
HomAroma is the reference tenant. This server exposes shared MCP capabilities; a tool call must use the active tenant's data and configuration.
MCP server สำหรับเชื่อม AI tools เข้ากับ backend ของ DBK Platform ผ่าน stdio transport โดยใช้ข้อมูลและการตั้งค่าของ tenant ที่กำลังเชื่อมต่อ
Run
bun install
bun run --cwd apps/mcp-server dev
หรือรันตรงจาก package:
cd apps/mcp-server
bun run dev
Environment
Multi-customer target profiles (recommended)
MCP process เดียวสลับ backend ลูกค้าหลายรายได้ด้วย allowlist ใน private JSON registry ตัวอย่างโครงสร้างอยู่ที่ target-profiles.example.json ให้คัดลอกไปยัง absolute path นอก Git แล้วกำหนด:
MCP_TARGET_PROFILES_FILE= absolute path ของ registryMCP_DEFAULT_TARGET_PROFILE= profile ID ที่เริ่มใช้งานทุกครั้งหลัง restart- ตัวแปร username/password ที่แต่ละ profile ระบุใน
usernameEnvและpasswordEnvผ่าน secret store หรือ environment ของ MCP client
Registry เก็บเพียง id, displayName, kind (local/staging/production), baseUrl, และ ชื่อตัวแปร credential; ห้ามใส่รหัสผ่านจริงใน JSON หรือ Git แต่ละ profile ต้องใช้ชื่อตัวแปร credential แยกกัน ไม่รับ URL ซ้ำกัน และตรวจว่าทุก secret มีค่าก่อนเริ่ม server URL ระยะไกลต้องเป็น HTTPS; HTTP อนุญาตเฉพาะ loopback หาก config ผิด server จะไม่เริ่ม
ใน profile mode ใช้ list_target_profiles → select_target_profile ด้วย ID → get_target_environment เพื่อยืนยัน URL ก่อนเขียน ทุก backend-facing tool ต้องส่ง expectedProfileId ให้ตรงกับ active profile; หากแชตอื่นสลับเป้าหมายระหว่างนั้น คำขอจะถูกปฏิเสธก่อนแตะ backend คำขอ MCP ถูกประมวลผลแบบเรียงลำดับเพื่อไม่ให้การสลับ target ตัดกลางคำขอ; auth token เก็บแยกตาม profile การเรียก set_target_environment รวมทั้ง customUrl จะถูกปฏิเสธ และไม่ fallback ไป URL หรือ admin credential ชุด legacy การเปลี่ยน active profile มีผลเฉพาะ process ปัจจุบัน; restart กลับไป MCP_DEFAULT_TARGET_PROFILE
Registry เป็น allowlist ของปลายทาง ไม่ใช่ระบบ authorization แยกตามแชต/ผู้ใช้ ทุก client ที่เข้าถึง MCP process เดียวกันสามารถเลือกทุก profile ใน registry ได้ และ expectedProfileId ป้องกันการส่งคำขอไปผิดเป้าหมายโดยไม่ตั้งใจเท่านั้น หากลูกค้าอยู่คนละ trust group ให้ใช้ MCP process และ registry แยกกัน
upload_product_image_from_url ถูกซ่อนและปฏิเสธใน profile mode เพราะตัวดาวน์โหลดเดิมรับ URL ภายนอกโดยไม่มี outbound allowlist หรือ resource limits; legacy mode ยังคง behavior เดิม ระหว่างนี้ให้ใช้ workflow อัปโหลดที่ผ่านการตรวจความปลอดภัยจากระบบอื่น
Migration: ตั้ง registry และ secret environment ทุก profile ให้ครบก่อนเปิด MCP_TARGET_PROFILES_FILE แล้ว restart MCP client ตรวจ get_target_environment ว่า mode เป็น profiles และ ID/URL ถูกต้องก่อนใช้ tool เขียนข้อมูล อย่าคัดลอก URL หรือ credential ของลูกค้าจริงลงตัวอย่างใน repository
Legacy mode (no registry)
รองรับตัวแปรต่อไปนี้เพื่อความเข้ากันได้:
MCP_BACKEND_URLสำหรับกำหนด backend target ตรง ๆMCP_BACKEND_URL_LOCALค่าเริ่มต้นสำหรับ localMCP_BACKEND_URL_PRODUCTIONค่าเริ่มต้นสำหรับ productionMCP_ADMIN_USERNAMEMCP_ADMIN_PASSWORD
ถ้าไม่ตั้งค่า MCP_BACKEND_URL server จะเริ่มที่ local backend และสลับเป็น production ได้ด้วย tool set_target_environment
ชื่อ homaroma ที่ยังอยู่ใน package, server identity, config key หรือ URL เป็น legacy runtime identifier เพื่อความเข้ากันได้ ไม่ใช่ชื่อแพลตฟอร์มหรือค่าแบรนด์สำหรับทุก tenant การเปลี่ยน identifier เหล่านี้ต้องทำเป็น migration แยกต่างหาก
ข้อควรระวังใน legacy mode: set_target_environment ยังใช้ admin credential ชุดเดียวกับทุก backend; production ยังมี HomAroma URL เป็น fallback หากไม่ได้กำหนด MCP_BACKEND_URL_PRODUCTION และ customUrl รับ URL อิสระ จึงไม่ควรใช้ legacy mode เพื่อสลับหลายลูกค้า
legacy mode ยังมีค่า admin สำรองในโค้ดตามการตัดสินใจชั่วคราวของเจ้าของโปรเจกต์ ควรกำหนด MCP_ADMIN_USERNAME และ MCP_ADMIN_PASSWORD ในสภาพแวดล้อมของ MCP client และย้ายออกจากค่าสำรองในรอบ migration ถัดไป อย่าใส่รหัสผ่านในแชตหรือ commit ลง repository ค่า fallback นี้ ไม่ถูกใช้ใน profile mode
Available tools
upload_cms_assetอัปโหลด JPEG/PNG/WebP/GIF จากไฟล์ในเครื่องเข้า Media Library ของ backend ปัจจุบัน แล้วคืนurlและpathสำหรับARTICLE_MEDIA/ARTICLE_GALLERYโดยไม่ผูกกับสินค้า ต้องตั้งMCP_ASSET_ROOTเป็น absolute directory ของไฟล์ที่อนุญาตก่อน restart; ส่งfilePathแบบ absolute ภายใน directory นั้น ขนาดไม่เกิน 10 MiB และใน profile mode ต้องส่งexpectedProfileIdด้วย Tool ตรวจ real path, ชนิดไฟล์จากนามสกุลและ magic bytes; ไม่รับ URL ภายนอก, SVG หรือ base64 การอัปโหลดสร้าง Media record แม้ยังไม่เพิ่มภาพลงหน้า CMS ให้ตรวจ target ด้วยget_target_environmentก่อนเสมอupdate_brand_nameเปลี่ยนชื่อแบรนด์กลางที่ใช้ใน header/footer ของapps/spaและapps/websiteupdate_store_settings_translationอัปเดตข้อความแปลของ store settingsupdate_footer_contentอัปเดตคำอธิบายแบรนด์และ social links เท่านั้นget_footer_brand/update_footer_brandอ่านและแก้ Footer Brand block โดยตรง (brand name, logo, คำอธิบายแยกภาษา, social links) พร้อมdryRunและexpectedVersionget_footer_layout/patch_footer_layoutอ่านและแก้โครง Footer พร้อม preview และการตรวจ version ก่อนเขียนget_category_sections/seed_category_sections/add_category_section/update_category_section/reorder_category_sections/delete_category_sectionจัดการ section ของหน้า Category
Tool definitions มี annotation บอกการอ่านอย่างเดียวหรือการลบ และผลลัพธ์ JSON มี structuredContent เพิ่มจากข้อความเดิมเพื่อให้ LLM ใช้ง่ายขึ้น โดยยังเข้ากันได้กับ client ที่อ่าน content อยู่
ตัวอย่างจาก get_page_block_schema เป็นโครง payload สำหรับเริ่มต้น ไม่ใช่เนื้อหาที่ควรเผยแพร่ทันที ให้แทน URL ตัวอย่าง ข้อความ ผู้เขียน และช่องทางติดต่อด้วยข้อมูลที่ตรวจสอบแล้วของ tenant ปัจจุบันก่อนเขียนจริง โดยเฉพาะ CONTACT_INFO ที่มี email และ LINE ID เป็น placeholder
Safe editing workflow
- เรียก
get_target_environmentก่อนแก้ข้อมูล โดยเฉพาะหลัง restart; ใน profile mode ยืนยันทั้ง profile ID และ URL อย่าเขียน production ถ้าผู้ใช้ไม่ได้ขอชัดเจน - อ่านข้อมูลเป้าหมายก่อน แล้วเลือก tool ที่เฉพาะงาน
- ใช้
dryRun: trueเมื่อมี, ตรวจผล preview แล้วค่อยเขียนจริง - ส่ง
expectedVersionสำหรับ Footer layout และงานอื่นที่รองรับ version - อ่านข้อมูลซ้ำหลังเขียนเพื่อตรวจผล
Category section tools ทำ preview ฝั่ง MCP เท่านั้น เพราะ backend ปัจจุบันยังไม่มี dryRun หรือ version check แบบ atomic สำหรับ endpoint กลุ่มนี้ ข้อมูลอาจเปลี่ยนระหว่าง preview กับการเขียนจริง; ให้ตรวจรายการล่าสุดก่อนเขียนเสมอ
Footer Menu LLM Workflow
Footer Explore and Footer Support are flat item menus, not Mega Menu payload trees. Supported menu names are:
footer-explore
footer-support
Use this workflow for every Footer menu edit:
- Call
get_footer_menuwith the target language. - Keep the returned
version. - Call one item-level write tool with
dryRun: trueandexpectedVersion. - Review the compact preview.
- Apply the same operation with
dryRun: falseand the same version. - Call
get_footer_menuagain to verify and obtain the next version.
If an allowed Footer menu does not exist yet, get_footer_menu returns exists: false, version: null, and an empty item list. Previewing the first add returns willCreateMenu: true; applying that add creates the menu as active and inserts the first item atomically.
Available item-level tools:
add_footer_menu_itemupdate_footer_menu_itemdelete_footer_menu_itemreorder_footer_menu_itemsupdate_footer_menu_item_translation
Every reorder request must include every current item ID exactly once. A MENU_VERSION_CONFLICT response means Admin or another agent changed the menu after it was read; fetch it again before retrying.
Do not pass exploreLinks, collectionLinks, or supportLinks to legacy update_footer_content. Full menu-array replacement is rejected because it can delete item IDs and translations. Website Collections are populated from Categories rather than a footer-collections menu.
Mega Menu LLM Workflow
Header Mega Menu uses the main menu payload tree. The website renders this structure:
Root -> Column -> Link -> Child Link
Supported localized text keys are:
th, en, ja, zh, vi, ko, lo, it
LLM agents should use this workflow for every menu edit:
capture_menu_snapshotformainget_mega_menu_treevalidate_mega_menu_tree- Plan operations from diagnostics and current
nodeIdvalues bulk_menu_operationswithdryRun: true- Review
preview.diff,preview.before,preview.after,issues, andwarnings - Apply only after approval
- Run
validate_mega_menu_treeagain - Use
restore_menu_snapshotif the applied result is wrong
Do not add a Link directly under root. Add a Column under Root first, then add Links under that Column.
Dry-run responses are expected to include path-based diffs such as:
{
"path": "payload[0].columns[0].links[0].label.it",
"before": null,
"after": "Candele"
}
validate_mega_menu_tree reports:
- missing locale keys, especially
loandit - unsupported translation keys
- duplicate or missing
nodeId - invalid URL shape
- placeholder text
- empty columns
- featured card issues
- desktop render limits such as hidden columns or links
Category Management LLM Workflow
Product categories are managed by backend /categories APIs through MCP. Supported localized text keys are:
th, en, ja, zh, vi, ko, lo, it
LLM agents should use this workflow for category edits:
list_categorieswithlang: "all"to discover category IDs and current translationscreate_category,update_category,update_category_media,update_category_translations, ordelete_categorywithdryRun: true- Review the preview and product counts
- Apply only after the intended change is clear
- Run
list_categoriesagain to verify
Category media uses semantic roles: thumbnail, hero_desktop, hero_mobile,
card_portrait, and social_share. update_category_media replaces the complete
media set, so omitted roles are removed. update_category_image remains available
only as a legacy thumbnail compatibility tool.
Backend category support currently covers:
- base
name imageUrl- localized names
- product-count guarded soft delete (
deletedAt)
Backend category support does not currently cover:
- slug
- nested parent/child hierarchy
- sort order
- active/inactive state
- restore/undelete workflow
Scripts
bun run devbun run startbun run check