MCP หรือ CLI คือคำถามที่ผิด ให้ถามว่าสถานะอยู่ที่ไหน

Tobi Lütke บอกว่าการเถียงเรื่อง MCP กับ CLI เกิดขึ้นผิดชั้น เพราะทั้งสองแบบใช้ได้ดีเมื่อสถานะคงอยู่ระหว่างขั้นตอน เราเห็นด้วย สำหรับการซิงค์ สถานะนั้นคือเรคคอร์ดของการเชื่อมต่อเอง ไม่ใช่ฮีปของ REPL และนั่นคือเหตุผลที่เครื่องมือ MCP ของ Tajo ไม่เก็บอะไรไว้ในเซสชัน

MCP กับ CLI ต่างกันอย่างไร

เอเจนต์ AI ควรใช้ MCP หรือ CLI?

Lütke พูดถูกว่า MCP กับ CLI เป็นการเถียงผิดชั้น ทั้งสองแบบใช้ได้เมื่อสถานะคงอยู่ระหว่างขั้นตอน และ bash ชนะเพียงเพราะระบบไฟล์คือสถานะนั้น แต่สำหรับการซิงค์ สถานะไม่ใช่ฮีปของ REPL มันคือเรคคอร์ดของการเชื่อมต่อเอง ได้แก่ เคอร์เซอร์ ข้อเสนอที่รอดำเนินการ สัญญาเช่า และแผน นั่นคือเหตุผลที่เครื่องมือ MCP ของ Tajo ไม่เก็บอะไรไว้ในเซสชัน เอเจนต์ตัวใดจากไคลเอนต์ใดก็ทำงานต่อจากเรคคอร์ดได้ ปัจจุบัน Tajo ไม่รันโค้ดของลูกค้าและไม่มี REPL ซึ่งเราจะบอกไว้อย่างตรงไปตรงมาด้านล่าง

เรียนรู้เพิ่มเติม
ในหน้านี้

เมื่อวันที่ 21 กันยายน 2026 เวลา 00:33 UTC Tobi Lütke โพสต์ข้อความนี้ “การถกเถียงเรื่อง MCP กับ CLI สำหรับ LLM เป็นการถกเถียงที่ผิด ผู้คนกำลังถกเถียงกันผิดชั้น” จากนั้นในวันที่ 23 ตุลาคม เขาชี้ไปยังรีโพสิทอรีใหม่ชื่อ dquickts ซึ่งเป็นฟอร์กของ QuickJS ที่หยุดโปรแกรมกลางลูป for ได้ เขียนฮีปทั้งหมดออกมาเป็นไบต์ แล้วไปทำงานต่อในอีกโปรเซสหนึ่ง เขาเขียนว่า “สภาพแวดล้อมสำหรับโหมดโค้ดที่ยอดเยี่ยม”

เขาพูดถูกเรื่องชั้น ผมอยากเห็นด้วยกับเขาก่อนด้วยคำพูดของเขาเอง แล้วค่อยจำกัดข้อกล่าวอ้างให้แคบลงจนมันบอกสิ่งที่การซิงค์นำไปใช้ได้

ชั้นที่เขาหมายถึง

นี่คือข้อโต้แย้งตามที่เขาเขียนไว้ ซึ่งเป็นวิธีเดียวที่ยุติธรรมในการอ้างอิงข้อโต้แย้งที่คุณตั้งใจจะดัดแปลง

ทั้งสองแบบทำงานได้ดีอย่างเหลือเชื่อ ตราบใดที่รันผ่านสภาพแวดล้อมคล้าย REPL ที่เก็บสถานะไว้ได้ เรื่องตลกของ CLI คือ เครื่องมือ CLI มักถูกเรียกผ่าน BASH ซึ่งบังเอิญเป็น REPL ที่มีสถานะถาวร (ระบบไฟล์) ดังนั้น CLI จึงทำงานได้ดีกว่า MCP มาก แต่นี่ไม่ได้ใกล้เคียงกับการเป็นคุณสมบัติโดยกำเนิดของ MCP เลย มันแค่หมายความว่าเราต้องการฮาร์เนสที่ดีกว่านี้

นั่นถูกต้อง และมันอธิบายสิ่งที่หลายคนสังเกตเห็นโดยไม่ได้อธิบาย เอเจนต์ที่ได้ CLI จะเขียนไฟล์ อ่านกลับมาในอีกสามขั้นตอนต่อมา grep มัน และส่งต่อผ่านไปป์ เอเจนต์ที่ได้เซิร์ฟเวอร์ MCP เปล่า ๆ จะได้ผลลัพธ์หนึ่งชิ้นต่อการเรียกหนึ่งครั้ง และต้องแบกทุกอย่างที่เหลือไว้ในหน้าต่างบริบท CLI ไม่ได้ชนะเพราะโปรโตคอล มันชนะเพราะ bash มีที่ให้วางของมาด้วย

การเดิมพันของเขาตามมาจากตรงนั้น จะมีใครสักคนสร้างเอนจินแบบฝังได้ในลักษณะเดียวกับ SQLite ที่เปลี่ยน bash, TypeScript หรือการเรียกเครื่องมือให้เป็นแผนเดียว ตรวจสอบก่อนรัน และออกแบบมาเพื่อการทำงานแบบคงทน dquickts คือก้าวหนึ่งไปสู่สิ่งนั้น README ของมัน อธิบายถึง REPL “ที่ฟังก์ชันและตัวแปรอยู่รอดหลังการรีสตาร์ต” และยังอธิบายการเรียกโฮสต์แบบคงทนไว้ด้วย “สคริปต์เรียกฟังก์ชันของโฮสต์ที่คำตอบจะมาถึงในอีกหลายชั่วโมง โปรเซสจะบันทึกสคริปต์แล้วออกไป”

ถึงตรงนี้ผมเห็นด้วยทุกประการ ต่อไปคือการจำกัดให้แคบลง

สถานะแบบไหน สำหรับงานอะไร

คำว่า “เก็บสถานะไว้” ซ่อนคำถามหนึ่งไว้ นั่นคือสถานะของใคร และอยู่ในรูปแบบใด สำหรับเอเจนต์เขียนโค้ด สถานะคือรีโพสิทอรีและเชลล์ และ REPL ที่อยู่รอดหลังการรีสตาร์ตเป็นคำตอบที่ดี สำหรับเอเจนต์ที่สร้างการซิงค์ระหว่างระบบ SaaS สองระบบ สถานะที่มันต้องใช้ระหว่างขั้นตอนถูกกำหนดไว้แล้วโดยตัวงานเอง เรคคอร์ดสี่ชนิดเป็นตัวแบกสถานะนั้น และไม่มีชนิดใดเป็นตัวแปรในสคริปต์

เคอร์เซอร์ การซิงค์อ่านแหล่งข้อมูลเป็นช่วง ๆ และต้องรู้ว่าช่วงก่อนหน้าจบลงที่ไหน ในเอนจินการซิงค์ของ Tajo สิ่งนี้คือเคอร์เซอร์ที่จัดเก็บแยกตามการซิงค์และขอบเขต ถูกเขียนหลังแต่ละแบตช์ที่ไม่มีเรคคอร์ดล้มเหลวเลย และการรันครั้งถัดไปจะทำต่อจากตรงนั้น การรันตามกำหนดเวลาจะบันทึกจุดเริ่มต้นก่อนทำอย่างอื่น ดังนั้นหากการรันครั้งแรกล้มเหลว การรันครั้งถัดไปก็ยังอ่านจากจุดเดิม หลักการเบื้องหลังเรื่องนี้ คือส่งเฉพาะส่วนที่เปลี่ยนและไม่ส่งทั้งตาราง อยู่ในบทอธิบาย reverse ETL ผมขอฝากเรื่องเคอร์เซอร์ไว้ที่นั่น

ข้อเสนอที่รอดำเนินการ บนเครื่องมือ MCP ของคอนเนกเตอร์ของ Tajo การเขียนหนึ่งครั้งประกอบด้วยการเรียกสองครั้ง brevo_propose ตรวจสอบอินพุตเทียบกับแอ็กชันที่ประกาศไว้และบันทึกข้อเสนอ ไม่มีอะไรถูกส่งออกไป ผลลัพธ์จะมี ID ข้อเสนอ และเมื่อต้องให้คนตัดสินใจ ก็จะมีหน้าที่ใช้อนุมัติข้อเสนอนั้นด้วย brevo_apply รันการเขียนที่ได้รับอนุมัติแล้วของการเชื่อมต่อนั้นตามลำดับ และหยุดที่การเขียนแรกที่ยังรอคนอยู่ เรคคอร์ดที่อยู่ตรงกลางคือสถานะ มันเก็บอินพุตและไดเจสต์ของอินพุตไว้ ดังนั้นการเขียนที่ได้รับอนุมัติแล้วจึงแก้ไขไม่ได้ระหว่างการตัดสินใจกับการ apply มันไม่เก็บค่าที่ผู้ให้บริการตอบกลับมา เพราะเรคคอร์ดการอนุมัติแบบเพิ่มต่อท้ายอย่างเดียวต้องไม่กลายเป็นที่เก็บข้อมูลส่วนบุคคล

สัญญาเช่า (lease) การรันการซิงค์หนึ่งครั้งถือสัญญาเช่าแบบเอกสิทธิ์หนึ่งรายการตลอดอายุการรัน โดยมีโทเค็นเจ้าของ เวลาจำกัดค่าเริ่มต้นสิบห้านาที และการต่ออายุ เจ้าของที่ล้าสมัยไม่สามารถปล่อยสัญญาเช่าที่ใหม่กว่าได้ หากเข้าถึงที่เก็บสัญญาเช่าไม่ได้ การรันจะปฏิเสธที่จะเริ่มแทนที่จะเดา เวิร์กเกอร์ตัวไหนถือการรันอยู่เป็นข้อเท็จจริงในสัญญาเช่า ไม่ได้อยู่ในหน่วยความจำของโปรเซสใด

แผน syncs.dryRun อ่านช่วงถัดไปจากเคอร์เซอร์ คำนวณว่าแต่ละเรคคอร์ดจะเป็นการเพิ่ม อัปเดต ลบ หรือไม่เปลี่ยนแปลง แล้วเขียนแถวเดียวพอดี นั่นคือแผน ซึ่งเก็บไว้หนึ่งชั่วโมง มันไม่ขยับเคอร์เซอร์และไม่เขียนอะไรลงระบบใดเลย syncs.apply เขียนแผนนั้นหนึ่งครั้ง มันจะปฏิเสธหากเคอร์เซอร์ขยับไปแล้ว หรือหากเรคคอร์ดเปลี่ยนไปเกินสัดส่วนที่ยอมรับได้ในระหว่างนั้น การเรียกซ้ำจะคืนผลลัพธ์แรกแทนที่จะเขียนอีกครั้ง

นั่นคือสถานะระหว่างขั้นตอน เรคคอร์ดสี่รายการที่มี ID ซึ่งเอเจนต์ใดก็ตามที่ได้รับอนุญาตให้ดูก็อ่านได้

ทำไมฝั่ง MCP จึงไม่เก็บอะไรไว้ในเซสชัน

เมื่อสถานะอยู่ในเรคคอร์ดแล้ว เซสชันก็ไม่มีอะไรเหลือให้ถือไว้ ดังนั้นเอนด์พอยต์ MCP ของ Tajo จึงไม่ถือเซสชัน ทุกคำขอจะได้เซิร์ฟเวอร์ใหม่และทรานสปอร์ตที่ไม่มี ID เซสชัน การอ่านคือหนึ่งคำขอต่อการเรียกหนึ่งครั้ง และผลลัพธ์มีขีดจำกัดเพื่อไม่ให้คำตอบเดียวท่วมทั้งเทิร์น การเขียนคือเสนอก่อนแล้วจึง apply และสิ่งที่คุณถือติดตัวไปคือ ID ของข้อเสนอ

โปรโตคอลก็เปลี่ยนไปในทางเดียวกัน การปรับปรุงข้อกำหนด MCP ฉบับ 2026-07-28 ได้นำเซสชันระดับโปรโตคอลและเฮดเดอร์ Mcp-Session-Id ออกจากทรานสปอร์ต Streamable HTTP เซิร์ฟเวอร์ที่ต้องการความต่อเนื่องจะสร้างแฮนเดิลของตัวเองและรับมันกลับมาเป็นอาร์กิวเมนต์ธรรมดา ID ข้อเสนอก็คือสิ่งนั้น เซิร์ฟเวอร์ MCP คืออะไร อธิบายการปรับปรุงครั้งนั้นและความแตกต่างระหว่างการเรียกเครื่องมือกับการซิงค์แบบคงทน ส่วนบทความนี้พูดถึงเพียงว่าสถานะไปอยู่ที่ไหน

ผลที่ตามมาซึ่งสำคัญต่องานการเชื่อมต่อคือแบบนี้ สมมติว่าเอเจนต์ใน Claude Code เสนอการเขียนแล้วเซสชันของมันจบลง หนึ่งชั่วโมงต่อมามีคนอนุมัติ เอเจนต์ในไคลเอนต์อื่น หรือเอเจนต์ตัวเดิมในวันรุ่งขึ้น เรียก apply พร้อม ID และการเขียนก็เกิดขึ้นหนึ่งครั้ง เอเจนต์ที่ล่มจะเสียหน้าต่างบริบทของมันและไม่เสียอะไรอื่นเลย ไม่มีอะไรถูกพักไว้ จึงไม่มีอะไรต้องกลับมาทำต่อ

สิ่งที่เราไม่มี และราคาที่ต้องจ่าย

ข้อคัดค้านที่ตรงไปตรงมามาจากฝั่ง REPL และเป็นแบบนี้ “ก็ได้ เรคคอร์ดของคุณอยู่รอด แต่ทุกการเรียกต้องอ่านซ้ำสิ่งที่การเรียกครั้งก่อนมีอยู่แล้ว สคริปต์จะดึงรายชื่อผู้ติดต่อครั้งเดียว join กรอง แล้วคืนตัวเลขหนึ่งตัว เอเจนต์ของคุณเรียกห้าครั้งและจ่ายราคาทุกครั้ง” ข้อคัดค้านนี้ถูกต้องในเชิงกลไก และเรายังไม่ได้วัดราคานั้น การประเมินที่จะนับจำนวนการเรียกต่องานทั้งแบบที่มีและไม่มีการแบกสถานะยังไม่ได้สร้างขึ้น จนกว่าจะสร้างเสร็จ ข้อกล่าวอ้างที่ว่าโหมดการเรียกใช้โทเค็นมากกว่าก็ยังไม่มีคำตอบ

ยังมีอีกสามเรื่องที่ต้องบอกอย่างตรงไปตรงมา เพราะผู้อ่านที่อ่านบทความนี้จบไม่ควรนึกภาพว่าสิ่งเหล่านี้มีอยู่

  • ปัจจุบันไม่มีการรันโค้ดที่ลูกค้าเขียน การอ่านผ่านคอนเนกเตอร์คือหนึ่งคำขอต่อการเรียกหนึ่งครั้ง โดยไม่มีแซนด์บ็อกซ์อยู่ในเส้นทาง ตัวรันโค้ดมีอยู่ในสแตกของเรา และเป็นเครื่องมือภายใน ในโปรดักชันมันปิดอยู่เว้นแต่จะตั้งแฟล็กสองตัวที่แยกจากกัน และตอบสนองเฉพาะผู้ดูแลระบบ การเรียกแต่ละครั้งจะได้บริบทใหม่ การผูกที่อนุญาตเฉพาะการอ่าน และงบเวลาสองวินาทีสำหรับโค้ดแบบซิงโครนัส เฮดเดอร์ของมันเองระบุไว้ด้วยตัวพิมพ์ใหญ่ว่ามันเป็นขอบเขตด้านความถูกต้อง ไม่ใช่ขอบเขตด้านความปลอดภัย
  • ไม่มี REPL สิ่งใดที่เอเจนต์คำนวณในการเรียกครั้งหนึ่งจะใช้ในการเรียกครั้งถัดไปไม่ได้ เว้นแต่จะถูกเขียนเป็นเรคคอร์ดที่มี ID
  • ไม่มีเซสชันเอเจนต์แบบถาวร ไคลเอนต์เก็บบทสนทนาของมัน Tajo เก็บข้อเสนอ แผน เคอร์เซอร์ และสัญญาเช่า ไม่มีสิ่งที่สาม

เอนจินแบบคงทนมีไว้เพื่ออะไร

ทั้งหมดนี้ไม่ใช่ข้อโต้แย้งคัดค้าน dquickts README ของมันระบุว่าสแนปช็อตมีไว้เพื่ออะไร และรายการนั้นดี ทำให้สคริปต์ที่ว่างอยู่จำศีลและคืนหน่วยความจำ รันการคำนวณที่ยาวเป็นช่วง ๆ บนเวิร์กเกอร์ตัวไหนก็ได้ที่ว่าง เริ่มทุกอินสแตนซ์แบบอุ่นเครื่องจากสแนปช็อตแทนการตั้งค่าซ้ำ พักสคริปต์ไว้กับการเรียกโฮสต์ที่คำตอบจะมาถึงภายหลัง สำหรับพื้นที่ทำงานของเอเจนต์เขียนโค้ด หรือโน้ตบุ๊กที่ควรอยู่รอดหลังการรีสตาร์ต นี่คือเครื่องมือที่เหมาะสม

แต่การซิงค์ก็ยังไม่ควรพึ่งพามัน ด้วยเหตุผลสามข้อที่ README เองบอกผม ข้อแรก “ไฟล์เซสชันโหลดได้เฉพาะในบิลด์ qjs ที่เขียนมันขึ้นมา” ฮีปที่เขียนโดยเอนจินเวอร์ชันหนึ่งกลายเป็นไฟล์ที่ใช้ไม่ได้หลังการอัปเกรด ส่วนเคอร์เซอร์คือสตริง ข้อสอง “สแนปช็อตเป็นอินพุตที่เชื่อถือได้ เช่นเดียวกับไบต์โค้ด” และ “สแนปช็อตที่เป็นอันตรายจะไม่ถูกตรวจพบ” ฮีปที่จัดเก็บไว้เป็นสิ่งที่คุณต้องปกป้อง ไม่ใช่สิ่งที่คุณตรวจทานได้ ส่วนข้อเสนอคือแถวที่คนอ่านก่อนอนุมัติ ข้อสาม สแนปช็อตเก็บทุกอย่างที่สคริปต์อ่านไว้ รวมถึงเรคคอร์ดของลูกค้า เรคคอร์ดที่กล่าวถึงข้างต้นถูกสร้างในทางตรงข้าม แผนเก็บไดเจสต์ ไม่ใช่ค่า และเรคคอร์ดข้อเสนอไม่เคยเก็บการตอบกลับของผู้ให้บริการ

หนึ่งในกฎที่เรายึดถือในการสร้างคือ ความแน่นอนชนะความฉลาด ฮีปที่กลับมาทำงานต่อกลางลูปนั้นฉลาด เคอร์เซอร์ ข้อเสนอ สัญญาเช่า และแผนนั้นน่าเบื่อ ตรวจสอบได้ และเหมือนเดิมในทุกเอนจิน ในทุกไคลเอนต์ หลังการล่มทุกครั้ง

Lütke ตั้งคำถามที่ถูกต้อง สถานะอยู่ที่ไหน? สำหรับการซิงค์ คำตอบไม่มีทางเป็นในโปรเซสอยู่แล้ว มันต้องอยู่ในเรคคอร์ดมาตลอด เพราะนั่นคือที่ที่คนผู้อนุมัติการเขียนต้องอ่านมันได้

คำถามที่พบบ่อย

MCP หรือ CLI ดีกว่ากันสำหรับเอเจนต์ AI?

ไม่ใช่ทั้งคู่ หากดูแค่ตัวมันเอง ประเด็นของ Tobi Lütke เมื่อวันที่ 21 กันยายน 2026 ยังเป็นจริง คือทั้งสองแบบใช้ได้ดีเมื่อการเรียกทำงานผ่านสิ่งที่เก็บสถานะไว้ระหว่างขั้นตอน ทุกวันนี้ CLI ให้ความรู้สึกที่ดีกว่าเพราะ bash เก็บสถานะไว้ในระบบไฟล์ ความแตกต่างไม่ได้อยู่ที่โปรโตคอล แต่อยู่ที่ฮาร์เนส

เอเจนต์ต้องการสถานะอะไรระหว่างขั้นตอนต่าง ๆ ของการซิงค์?

เรคคอร์ดของการเชื่อมต่อเอง ได้แก่ เคอร์เซอร์ที่บอกว่าการรันครั้งก่อนหยุดอยู่ที่ไหน ข้อเสนอที่รอการอนุมัติจากคน สัญญาเช่า (lease) ที่บอกว่าเวิร์กเกอร์ตัวไหนถือการรันอยู่ และแผนแบบ dry-run ที่ apply ต้องตรงกัน ไม่มีสิ่งใดในนี้เป็นตัวแปรในสคริปต์ ทั้งหมดคือแถวที่มี ID

ทำไมเซิร์ฟเวอร์ MCP ของ Tajo จึงไม่เก็บอะไรไว้ในเซสชัน?

ทุกคำขอจะได้เซิร์ฟเวอร์ใหม่และทรานสปอร์ตที่ไม่มี ID เซสชัน สิ่งที่การเรียกครั้งถัดไปต้องใช้ล้วนเป็นเรคคอร์ด ได้แก่ ID ข้อเสนอ ID แผน และเคอร์เซอร์ ดังนั้นเอเจนต์ตัวที่สองซึ่งอยู่ในไคลเอนต์อื่นจึงทำงานต่อจากจุดที่ตัวแรกหยุดไว้ได้ และเอเจนต์ที่ล่มจะไม่เสียอะไรเลยนอกจากหน้าต่างบริบทของตัวเอง

Tajo รันโค้ดที่เอเจนต์เขียนหรือไม่?

ไม่รันให้ลูกค้า การอ่านผ่านเครื่องมือคอนเนกเตอร์คือหนึ่งคำขอต่อการเรียกหนึ่งครั้ง โดยไม่มีแซนด์บ็อกซ์ ตัวรันโค้ดที่มีอยู่เป็นเครื่องมือภายในที่อยู่หลังแฟล็กสองตัว ผู้ดูแลระบบเท่านั้นที่เข้าถึงได้ ใช้การผูกแบบอ่านอย่างเดียว และใช้บริบทใหม่ทุกครั้งที่เรียก ตัวมันเองระบุว่าไม่ใช่ขอบเขตด้านความปลอดภัย ไม่มี REPL และไม่มีเซสชันถาวร

เอนจิน JavaScript แบบคงทนอย่าง dquickts เหมาะกับงานอะไร?

README ของมันระบุการใช้งานไว้ ได้แก่ ทำให้สคริปต์ที่ว่างอยู่จำศีล แบ่งการคำนวณที่ยาวไปรันบนหลายเวิร์กเกอร์ เริ่มต้นแบบอุ่นเครื่องจากสแนปช็อต และพักสคริปต์ไว้กับการเรียกโฮสต์ที่คำตอบจะมาถึงในอีกหลายชั่วโมง ทั้งหมดเป็นการใช้งานจริง แต่การซิงค์ก็ยังไม่ควรพึ่งพามัน เพราะสแนปช็อตโหลดได้เฉพาะในบิลด์ของเอนจินที่เขียนมันขึ้นมา ผู้ตรวจทานมองไม่เห็นข้างใน และมันเก็บข้อมูลลูกค้าทุกอย่างที่สคริปต์อ่านไว้

ขอสิทธิ์ใช้งานล่วงหน้า

กรอกชื่อและที่อยู่อีเมลของคุณ เราจะส่งลิงก์เพื่อยืนยันคำขอเข้าใช้งาน Tajo ทางอีเมล

รับ Brevo

หน้านี้ในรูปแบบ Markdown: mcp-or-cli-is-the-wrong-question.md

กำลังโหลด Markdown…
เปิดใน Claude เปิดใน ChatGPT