ข้าม​ไป​ยัง​เนื้อหา

Ubiquitous Language ของ MCP server

บท​ที่ 4 ปิด​ด้วย​เส้น​แบ่ง “ใคร​ควบคุม​การ​เรียก” ระหว่าง tool กับ resource บท​นี้​ถอย​ออก​มา​มอง​พื้น​ผิว​ทั้ง​ก้อน แล้ว​ถาม​คำถาม​ที่​ลึก​กว่า: เมื่อ server เปิด primitive ออก​มา มัน​ไม่​ได้​เปิด​แค่ function — มัน​เปิด ภาษา ออก​มา​ด้วย ชื่อ tool, ชื่อ resource, ข้อความ​อธิบาย​แต่ละ​บรรทัด คือ​ถ้อยคำ​ที่ model อ่าน​แล้ว​ใช้​ตัดสิน​ใจ​ว่า​จะ​ทำ​อะไร​ต่อ นี่​คือ​จุด​ที่​วินัย​ของ DDD ที่​คอร์ส​นี้​ยืม​มา​ตลอด​กลับ​มา​เต็ม​ตัว: พื้น​ผิว​ของ server คือ ubiquitous language ของ bounded context นั้น

📦 code ตัวอย่าง

คอร์ส​นี้​เป็น คอร์ส​ออกแบบ ไม่ใช่​ทัวร์ SDK — code ใน​บท​นี้​คือ ภาพร่าง ของ schema และ​การ​ตั้ง​ชื่อ (tool-schema sketch + Thai Mermaid) ไม่ใช่ server ที่​รัน​ได้​จริง ตัวอย่าง​ต่อยอด​จาก domain เดิม​ที่ repo kaen-food-ordering (กำลัง​จัด​ทำ) โดย​มอง​ระบบ​ภายใน​ของ platform ฟู้ด​เดลิ​เวอรี (ออเดอร์ ครัว การเงิน) ที่​ถูก​ห่อ​เป็น MCP server ให้ ops-copilot เรียก​ใช้

นี่​คือ​เลนส์​ของ​คอร์ส ไม่ใช่​ศัพท์​ใน spec MCP

spec MCP ไม่มี​คำ​ว่า “ubiquitous language” อยู่​ใน​นั้น — การ​มอง​พื้น​ผิว server เป็น UL เป็น มุม​เชิง​การ​สอน​ของ​คอร์ส​นี้​เอง (ต่อยอด​จาก​สายเลือด DDD ของ​มัน) สิ่ง​ที่มา​จาก​แหล่ง​ปฐม​ภู​มิจริงๆ คือ​หลักการ​ออกแบบ tool (การ​ตั้ง namespace และ​ชื่อ​ที่​มี​ความหมาย) และ​หลัก​ความ​ปลอดภัย (คำ​อธิบาย​ไม่​น่า​เชื่อถือ​โดย​ปริยาย) ที่​เรา​จะ​เอา​มา​ร้อย​เข้า​ด้วย​กัน​ด้วย​เลนส์​นี้

ToolToolfunction ที่ Server ประกาศ​ให้ model เรียก​เอง​ได้​ระหว่าง​ให้​เหตุผล เช่น ค้น​หา​ออเดอร์ หรือ​ยิง refund มัก​มี​ผล​ข้าง​เคียง​จริง (side effect) — เป็น Primitive แบบ Model-controlled ต้อง​ระวัง parameter ผิด สิทธิ์​เกิน​จำเป็น และ​คำ​อธิบาย tool ที่​ถูก​ฝัง​คำ​สั่ง​แฝงTactical Design แต่ละ​ตัว​ที่ server ประกาศ​ออก​มา model ไม่​ได้ “เห็น code” ของ​มัน — มัน​เห็น​แค่ ชื่อ กับ คำ​อธิบาย กับ รูปร่าง parameter แล้ว​ต้อง​ตัดสิน​ใจ​จาก​เท่านั้น​ว่า​จะ​เรียกตัว​ไหน ด้วย​ค่า​อะไร ใน​จังหวะ​ไหน กล่าว​อีก​แบบ ชื่อ​และ​คำ​อธิบาย​คือ ส่วน​ต่อ​ประสาน​เดียว ระหว่าง domain ของ​คุณ​กับ​การ​ให้​เหตุผล​ของ model มัน​จึง​ไม่ใช่​เรื่อง “ความ​สวยงาม​ของ code” แต่​เป็น​เรื่อง​ว่า model จะ​เข้าใจ domain ถูก​หรือ​ผิด

คู่มือ​ออกแบบ tool ของ Anthropic วาง​กฎ​ที่​ตรง​กับ​หลัก ubiquitous language อย่าง​น่า​ประหลาด: ให้​ตั้ง namespace ตาม​บริการ/ทรัพยากร (เช่น orders_find_late ไม่ใช่​กอง function แบนๆ ที่​ชน​กันเอง) และ​ให้​เลือก field ที่ มี​ความหมาย​ต่อ​มนุษย์ (name, file_type) แทน id ทึบๆ (uuid, mime_type) เพราะ​การ​ทำ​แบบ​นี้ “ช่วย​เพิ่ม​ความ​แม่นยำ​ของ Claude อย่าง​มี​นัย​สำคัญ… ด้วย​การ​ลด​การ hallucinate” (Anthropic, “Writing effective tools for agents”) — พูด​ใน​ภาษา DDD ก็​คือ การ​ตั้ง​ชื่อ namespace และ​ชื่อ​เชิง​ความหมาย คือ​การ​ตัดสิน​ใจ​เรื่อง ubiquitous language ตรงๆ

และ​เหมือน​กับ UL ใน​ทีม​มนุษย์​เป๊ะ: ชื่อ​รั่ว → เข้าใจ​ผิด ถ้า​ชื่อ tool กำกวม​หรือ​รูป​มา​จาก backend (เช่น run_query, upd_stat, parameter ชื่อ oid, s) model ก็​จะ​เดา​ความหมาย​เอง แล้ว​เลือก tool ผิด​หรือ​ใส่​ค่า​ผิด — เหมือน​ทีม​ที่​ใช้​คำ​คลุมเครือ​แล้ว​เข้าใจ bounded context กัน​คนละ​อย่าง ความ​กำกวม​ของ​ภาษา​แปล​เป็นการ​กระทำ​ที่​ผิดพลาด​เสมอ ไม่​ว่า​ผู้​ฟัง​จะ​เป็น​คน​หรือ model

❌ version ดิบ: ปล่อย​ชื่อ​ของ backend รั่ว​เข้า​มา​เต็ม ๆ
tools:
- name: run_query # ยิง SQL อะไรก็ได้ — model เดาไม่ออกว่าปลอดภัยแค่ไหน
description: "exec sql on orders db"
- name: upd_stat # ย่อจนอ่านไม่รู้เรื่อง กริยาไม่ชัด
params: { oid: uuid, s: int } # id ไร้ความหมาย + สถานะเป็นตัวเลขดิบ
✅ พื้น​ผิว​เชิง domain: ชื่อ+คำ​อธิบาย​เป็น​ภาษา​ที่ model ใช้​คิด​ได้
tools:
- name: orders_find_late # namespace ตามบริการ + กริยา domain ชัด
description: "ค้นออเดอร์ที่เกินกำหนดส่งในโซนที่ระบุ ตั้งแต่เวลาหนึ่ง"
params: { zone: string, since: datetime }
- name: orders_issue_refund # ขอบเขตแคบ ทำสิ่งเดียวที่ domain เข้าใจ
description: "คืนเงินให้ออเดอร์ตามเลขที่ออเดอร์ ต้องแนบเหตุผลกำกับ"
params: { orderId: string, reason: string }
หมายเหตุ: นี่​คือ​แนว​ปฏิบัติ ไม่ใช่​ข้อ​บังคับ​ของ spec

กฎ​การ​ตั้ง​ชื่อ​เหล่า​นี้​มา​จาก​คู่มือ วิศวกรรม ของ Anthropic ไม่ใช่​ถ้อยคำ​บังคับ (normative) ใน spec MCP — อ่าน​ว่า “ออกแบบ​ให้​ดี​อย่างไร” ไม่ใช่ “spec สั่ง​ให้​ทำ​อะไร”

เหตุผล​ที่​การ​ตั้ง​ชื่อ​กลาย​เป็น​งาน​หนัก​ก็​เพราะ server อยู่​ตรง​รอย​ต่อ​ระหว่าง​สอง​โลก​ที่​พูด​คนละ​ภาษา ฝั่ง​หนึ่ง​คือ backend จริง​ที่​เต็ม​ไป​ด้วย status_code=3, uuid, ตาราง และ error code อีก​ฝั่ง​คือ model ที่​ต้องการ​ภาษา domain ที่​อ่าน​แล้ว​เข้าใจ งาน​ของ server คือ แปล — และ​นี่​คือ​นิยาม​ของ Anti-Corruption LayerAnti-Corruption Layer (ACL)แนวคิด​จาก DDD ที่​ยืม​มา​ใช้​มอง​งาน​ออกแบบ MCP Server: มอง Tool แต่ละ​ตัว​เป็น​ชั้น​แปล​ระหว่าง model กับ​ระบบ​ภายนอก​จริง ไม่​ปล่อย​ให้ model ของ​ระบบ​เดิม (schema, error code, id ที่​ไม่มี​ความหมาย) รั่ว​ตรง​เข้า​สู่​บริบท​ของ agent — Tool ที่​ดี​คือ ACL ที่​รักษา model ของ agent ไม่​ให้​ปน​เปื้อนStrategic Design ใน​ภาษา DDD เป๊ะๆ: ชั้น​ที่​กัน​ไม่​ให้ model ของ​ระบบ​เดิม (schema ดิบ, id ไร้​ความหมาย, สถานะ​เป็น​ตัวเลข) รั่ว​ตรง​เข้า​สู่​บริบท​ของ agent

มอง​แบบ​นี้​แล้ว การ​ตั้ง​ชื่อ tool ที่​ดี​ไม่ใช่​แค่ “เรื่อง​สไตล์” แต่​คือ การ​ทำ​หน้าที่ ACL ให้​ครบ — ทุก​ชื่อ​ที่​คุณ​เปลี่ยน​จาก upd_stat เป็น orders_mark_delivered คือ​การ​แปล​หนึ่ง​ครั้ง​ที่​ทำให้​ภาษา​ของ backend หยุด​ตรง​พื้น​ผิว ไม่​ไหล​เข้าไป​ใน​หัว​ของ model บท​ที่ 3 เรียก​สิ่ง​นี้​ว่า “tool เป็น ACL” ใน​แง่ ขอบเขต​สิทธิ์ บท​นี้​เติม​อีก​ครึ่ง: มัน​เป็น ACL ใน​แง่ ภาษา ด้วย

คำ​อธิบาย​เป็น artifact ที่ model อ่าน และ​ไม่​น่า​เชื่อถือ​โดย​ปริยาย

หัวข้อ​ที่​มีชื่อ​ว่า “คำ​อธิบาย​เป็น artifact ที่ model อ่าน และ​ไม่​น่า​เชื่อถือ​โดย​ปริยาย”

นี่​คือ​หัวใจ​ที่​ทำให้​บท​นี้​ไม่ใช่​แค่​เรื่อง “ตั้ง​ชื่อ​ให้​สวย” คำ​อธิบาย​ของ tool ไม่ใช่​คอมเมนต์​ที่​มนุษย์​อ่าน — มัน​เป็น artifact ชั้น​หนึ่ง​ที่​ไหล​ตรง​เข้า context ของ model ทุก​ครั้ง​ที่ server ประกาศ tool model อ่าน​มัน​เต็มๆ และ​ใช้​มัน​ตัดสิน​ใจ ซึ่ง​แปล​ว่า​ใคร​ก็ตาม​ที่​เขียน​คำ​อธิบาย​นั้น​ได้ ก็​เขียน​คำ​สั่ง​ลง​ใน​หัว​ของ model ได้​ทาง​อ้อม

spec จึงระบุไว้ตรงๆ ใน​หมวด Security & Trust ว่า “คำ​อธิบาย​พฤติกรรม​ของ tool เช่น annotation ควร​ถือว่า​ไม่​น่า​เชื่อถือ เว้น​แต่​ได้​มา​จาก server ที่​เชื่อถือ​ได้” (descriptions of tool behavior such as annotations should be considered untrusted, unless obtained from a trusted server — MCP spec, Security section, 2025-11-25) คือ ณ spec 2025-11-25 คำ​อธิบาย​ที่มา​กับ tool ไม่มี​สถานะ​น่า​เชื่อถือ​ใน​ตัวเอง — มัน​น่า​เชื่อ​ได้​ก็​ต่อ​เมื่อ server ที่​ยื่น​มัน​มา​เป็น server ที่​เรา​เชื่อถือ​อยู่​แล้ว​เท่านั้น

จุด​นี้​เอง​ที่​ทำให้ การ​ออกแบบ ubiquitous language กับ​การ​ออกแบบ​ความ​ปลอดภัย​กลาย​เป็นการ​กระทำ​เดียวกัน: ถ้อยคำ​ชุด​เดียว​ที่​คุณ​เปิด​ออก​มา​เป็น​ทั้ง API (ภาษา​ที่ model ใช้​ทำงาน) และ พื้น​ผิว​การ​โจมตี (ช่อง​ที่​คำ​สั่ง​แฝง​เข้า​มา​ได้) ใน​เวลา​เดียวกัน คุณ​ออกแบบ​ภาษา​ที่​เผยแพร่​ของ context ก็​คือ​คุณ​กำลัง​ตัดสิน​ใจ​เรื่อง​ความ​ปลอดภัย​ไป​พร้อม​กัน — นี่​คือ​สะพาน​ตรง​ไป​บท​ที่ 6 ที่​จะ​แยก​ให้​เห็น​ว่า​ทำไม annotation อย่าง readOnlyHint จึง​ไม่ใช่ trust boundary และ server ที่​ไม่​น่า​เชื่อถือ โกหก​ได้

รวม​ทุก​อย่าง​เข้า​ด้วย​กัน​ด้วย​เลนส์​หลัก​ของ​คอร์ส​พี่ Agent as Bounded Contextagent as bounded context: ถ้า​เรา​มอง server หนึ่ง​ตัว (เช่น ระบบ​ออเดอร์) เป็น bounded context หนึ่ง พื้น​ผิว​ที่​มัน​เปิด​ออก​มา — ชื่อ tool, ชื่อ resource, คำ​อธิบาย​ทั้ง​ชุด — ก็​คือ published language ของ context นั้น คือ​ภาษา​ทางการ​ที่ context นี้​ตกลง​จะ​พูด​กับ​โลก​ภายนอก ทุก​คำ​ใน​พื้น​ผิว​นั้น​ถูก​ทั้ง model อ่าน​เพื่อ​ทำงาน และ​ถูก​ผู้​ไม่​หวังดี​อ่าน​เพื่อ​หา​ช่อง มัน​จึง​ต้อง​ถูก​ออกแบบ​ด้วย​ความ​ตั้งใจ​ระดับ​เดียว​กับ​ที่​ทีม DDD ออกแบบ context map ระหว่าง bounded context

flowchart LR
  subgraph BC["Bounded Context — ระบบออเดอร์ (backend จริง)"]
    RAW["model ภายใน<br/>ตาราง orders, status_code=3<br/>uuid, mime_type"]
  end
  subgraph SRV["MCP Server = Anti-Corruption Layer"]
    ACL["ชั้นแปล: ตั้งชื่อ + คำอธิบายเชิง domain"]
  end
  subgraph PUB["พื้นผิว server = published language ของ context"]
    T1["orders_find_late — ค้นออเดอร์ที่ช้า"]
    T2["orders_issue_refund — คืนเงินออเดอร์"]
    RES["orders_status — สถานะออเดอร์ (resource)"]
  end
  MODEL["model ของ ops-copilot<br/>อ่านชื่อ+คำอธิบายเพื่อเลือกและเรียก"]
  RAW -->|"แปลหยุดตรงนี้"| ACL
  ACL --> T1
  ACL --> T2
  ACL --> RES
  T1 -->|"ชื่อชัด เลือกถูก"| MODEL
  T2 --> MODEL
  RES --> MODEL
  classDef bc fill:#fde68a,stroke:#92400e,color:#1a1a1f;
  classDef acl fill:#c7d2fe,stroke:#3730a3,color:#1a1a1f;
  classDef pub fill:#bbf7d0,stroke:#065f46,color:#1a1a1f;
  class RAW bc;
  class ACL acl;
  class T1,T2,RES pub;

คำ​บรรยาย​ภาพ: model ภายใน​ของ bounded context (เหลือง) ที่​เต็ม​ไป​ด้วย​ศัพท์ backend ถูก MCP server ใน​ฐานะ Anti-Corruption Layer (น้ำเงิน) แปล​ให้​กลาย​เป็น​พื้น​ผิว​เชิง domain (เขียว) — ชื่อ tool/resource กับ​คำ​อธิบาย​ของ​มัน​คือ published language ที่ model ops-copilot อ่าน​เพื่อ​เลือก​และ​เรียก การ​แปล​ต้อง​หยุด​ที่​ชั้น ACL ให้​ครบ ไม่​ปล่อย​ให้ status_code=3 หรือ uuid รั่ว​ข้าม​ไป​ถึง model เพราะ​ถ้อยคำ​ชุด​นี้​คือ​ทั้ง API และ​พื้น​ผิว​การ​โจมตี​ใน​เวลา​เดียวกัน

  • ชื่อ​และ​คำ​อธิบาย​ของ primitive คือ ภาษา domain ที่ model ใช้​ให้​เหตุผล ไม่ใช่​คอมเมนต์​ประดับ
  • การ​ตั้ง namespace และ​ชื่อ​เชิง​ความหมาย​คือ การ​ตัดสิน​ใจ​เรื่อง ubiquitous language — ชื่อ​รั่ว​ทำให้ model เลือก tool ผิด เหมือน UL รั่ว​ทำให้​คน​เข้าใจ context ผิด
  • server เป็น Anti-Corruption Layer เชิง​ภาษา: แปล backend ให้​หยุด​ตรง​พื้น​ผิว
  • คำ​อธิบาย​เป็น artifact ที่ model อ่าน และ ไม่​น่า​เชื่อถือ​โดย​ปริยาย (ณ spec 2025-11-25) — จึง​ทำให้การ​ออกแบบ UL กับ​ความ​ปลอดภัย​เป็น​เรื่อง​เดียวกัน และ​เป็น​สะพาน​เข้า​บท​ที่ 6

🔗 อ้างอิง​เพิ่มเติม​ใน DevIQ

เจาะ​ลึก​แนวคิด​ใน​บท​นี้​ต่อ​ได้ที่​คลัง​อ้างอิง DevIQ:

  • Ubiquitous Language — ภาษา​กลาง​ที่ model ของ domain ต้อง​สะท้อน​ตรง​กัน บท​นี้​ยืม​มัน​มา​ใช้​กับ​พื้น​ผิว MCP server ใน​ฐานะ​ภาษา​ที่ model อ่าน​และ​ใช้​ให้​เหตุผล
  • Anti-Corruption Layer — ชั้น​แปล​ที่​กัน model ของ​ระบบ​เดิม​ไม่​ให้​รั่ว​ข้าม​ขอบเขต — บท​นี้​ชี้​ว่าการ​ตั้ง​ชื่อ tool/resource คือ​การ​ทำ​หน้าที่ ACL เชิง​ภาษา

เช็กความเข้าใจ — บทที่ 5

ข้อ 1 / 3

ชื่อของ tool ทำหน้าที่เป็น ubiquitous language อย่างไรในมุมของบทนี้?