เซิร์ฟเวอร์ MCP เปลี่ยน API ให้เป็นเครื่องมือที่มีชื่อไม่กี่ตัวให้เอเจนต์เรียกใช้ระหว่างการสนทนา งานยาวก็ทำงานผ่านเครื่องมือเหล่านี้ได้ เมื่อเครื่องมือส่ง handle กลับมาให้เอเจนต์รอดูผลเป็นช่วงๆ วางลิงก์พอดแคสต์ ขอใจความสำคัญเป็นภาษาฝรั่งเศส โดยไม่ต้องเขียนโค้ดสักบรรทัด
เอเจนต์เขียนโค้ดสามารถใช้ VocalStack API ได้เหมือนโปรแกรมทั่วไป คืออ่าน เอกสาร API อ้างอิง เขียนโค้ดไม่กี่บรรทัดด้วย SDK แล้วรัน นั่นคือแนวทางที่ถูกต้องสำหรับงานที่จะทำงานโดยไม่มีเอเจนต์
MCP (Model Context Protocol) ใช้กับอีกกรณีหนึ่ง คือเมื่อคุณกำลังคุยกับผู้ช่วยและอยากให้มันถอดเสียงบางอย่างทันที โดยไม่ต้องเขียนโค้ด เซิร์ฟเวอร์ MCP จะเผยแพร่รายการเครื่องมือที่มีชื่อสั้นๆ โดยแต่ละตัวมีคำอธิบายและ JSON schema สำหรับอาร์กิวเมนต์ เอเจนต์อ่านรายการนี้ครั้งเดียว แล้วเรียกเครื่องมือด้วยชื่อเมื่อคำขอตรงกับเครื่องมือนั้น "ถอดเสียงพอดแคสต์นี้แล้วบอกหน่อยว่าพวกเขาพูดถึงราคาว่าอย่างไร" จะกลายเป็นการเรียกเครื่องมือสามครั้ง และเอเจนต์ไม่เคยเห็นเส้นทาง HTTP เลย
เซิร์ฟเวอร์มีสองประเภท เซิร์ฟเวอร์แบบโลคัลทำงานเป็น subprocess ของ client และสื่อสารผ่าน stdio ซึ่งเป็นวิธีที่เซิร์ฟเวอร์ระบบไฟล์และฐานข้อมูลส่วนใหญ่ทำงาน ส่วนเซิร์ฟเวอร์แบบรีโมตอยู่หลัง URL และใช้ Streamable HTTP ซึ่งเป็นทรานสปอร์ตที่มาแทน HTTP-plus-SSE (server-sent events) รุ่นเก่าในสเปกฉบับเดือนมีนาคม 2025 client ส่ง request แต่ละรายการ และเซิร์ฟเวอร์ตอบกลับบนการเชื่อมต่อเดียวกัน ข้อมูลใดที่เซิร์ฟเวอร์ต้องส่งก่อนผลลัพธ์สุดท้ายจะถูกสตรีมตามหน้าไป client ที่เขียนตามสเปกปัจจุบันใช้ได้ทั้งสองประเภท
เซิร์ฟเวอร์ของ VocalStack เป็นแบบรีโมต อยู่ที่ {{api}}/v1/mcp และการใช้งานมีค่าใช้จ่ายเท่ากับ API สิ่งที่ต้องใช้มีเพียง URL นี้
เปิดพื้นที่ Developers ของแดชบอร์ดที่ {{app}}/developers แท็บแรก Connect an AI agent จะแสดง URL ของเซิร์ฟเวอร์และ snippet การตั้งค่าสำหรับแต่ละ client snippet ทั้งสามด้านล่างตรงกับที่แสดงอยู่ในหน้านั้น
รันคำสั่งนี้ในเทอร์มินัลของคุณ:
claude mcp add --transport http vocalstack {{api}}/v1/mcp
เพิ่มข้อความนี้ลงในไฟล์ .cursor/mcp.json ในโปรเจกต์ของคุณ หรือใส่ใน ~/.cursor/mcp.json เพื่อใช้กับทุกโปรเจกต์:
{"mcpServers":{"vocalstack":{"url":"{{api}}/v1/mcp"}}}
client ใดก็ตามที่รองรับ Streamable HTTP สามารถเพิ่มเซิร์ฟเวอร์ด้วย URL นี้ได้ นี่คือรูปแบบทั่วไปที่แดชบอร์ดแสดง:
{
"vocalstack": {
"type": "streamable-http",
"url": "{{api}}/v1/mcp"
}
}
คุณไม่จำเป็นต้องมี API key เพื่อเริ่มต้น เมื่อ client เชื่อมต่อครั้งแรก คุณจะเข้าสู่ระบบด้วยบัญชี VocalStack ในเบราว์เซอร์ และ client จะเก็บโทเค็นไว้ แดชบอร์ดสรุปไว้ในประโยคเดียวว่า "You'll sign in with your VocalStack account the first time. No key needed."
นี่คือสเปกการอนุญาต (authorization) ของ MCP ที่ทำงานอยู่ ไม่ใช่เทคนิคเฉพาะของ VocalStack เซิร์ฟเวอร์รีโมตจะตอบคำขอที่ไม่ได้ยืนยันตัวตนด้วยสถานะ 401 ที่ชี้ไปยัง protected-resource metadata ซึ่งเป็นเอกสาร JSON เล็กๆ ที่ระบุเซิร์ฟเวอร์การอนุญาตและ scope ที่เซิร์ฟเวอร์ต้องการ client อ่านเอกสารนี้ แล้วทำ OAuth 2.1 flow มาตรฐานในเบราว์เซอร์ของคุณ และได้รับโทเค็นที่ออกให้เฉพาะเซิร์ฟเวอร์นั้น เซิร์ฟเวอร์ MCP รีโมตใดก็ตามที่ทำตามสเปกจะให้คุณเข้าสู่ระบบในลักษณะเดียวกัน นั่นคือเหตุผลที่ client ข้างต้นทำได้โดยไม่ต้องรู้อะไรเกี่ยวกับ VocalStack
API key ก็ใช้ได้เช่นกัน สำหรับ client ที่ทำงานบนเซิร์ฟเวอร์หรือในสคริปต์ ส่งคีย์ในรูปแบบ Authorization: Bearer vs_key_... ซึ่งเป็นเฮดเดอร์เดียวกับที่ REST API ใช้ คีย์สร้างได้ที่ Developers → API keys เครื่องมือถอดเสียงทั้งสองตัวต้องมีสิทธิ์ Write และ Stream audio สิทธิ์ตัวหลังอาจดูแปลกสำหรับไฟล์ แต่เครื่องมือเหล่านี้ใช้จุดสร้างเดียวกับการถอดเสียงทุกครั้ง รวมถึงการถอดเสียงสด และจุดนั้นตรวจสอบทั้งสองสิทธิ์ เครื่องมืออ่านข้อมูลต้องใช้สิทธิ์ Read ส่วน list_voices ต้องการเพียงข้อมูลยืนยันตัวตนที่ถูกต้องเท่านั้น
เมื่อเข้าสู่ระบบแล้ว เอเจนต์จะเห็นเครื่องมือเก้าตัว คุณแทบไม่ต้องเรียกชื่อเครื่องมือเองบ่อยนัก แต่การรู้จักเครื่องมือเหล่านี้จะช่วยให้เข้าใจว่าเอเจนต์จะทำอะไรต่อไป
เครื่องมือทุกตัวรับและส่งกลับ JSON แบบ camelCase โดยใช้ชื่อฟิลด์เดียวกับ wire type ของ /v1 และ handle ของการถอดเสียงคือ transcriptionId
transcribe_url เริ่มถอดเสียงจากเสียงหรือวิดีโอที่ URL สาธารณะ เช่น ลิงก์ไฟล์ ตอนพอดแคสต์ หรือวิดีโอ YouTube ไม่รับสตรีมสด อาร์กิวเมนต์เสริมได้แก่ name, spokenLanguage (ละไว้เพื่อให้ตรวจจับภาษาอัตโนมัติ), speakerLabels (auto, on หรือ off) และ startS/endS เพื่อถอดเสียงเฉพาะบางส่วนของเสียงtranscribe_file ทำงานเหมือนกันสำหรับไฟล์ที่เอเจนต์มีอยู่ ส่งเป็น fileBase64 พร้อม filename ขนาดหลังถอดรหัสจำกัดไว้ที่ 10 MiB ซึ่งประมาณสิบนาทีของ MP3 128 kbps หากไฟล์ใหญ่กว่านั้น ให้ส่งลิงก์ให้เอเจนต์แทนทั้งสองตัวจะส่งกลับทันทีพร้อม transcriptionId, state: "queued" และคำใบ้ next ที่ระบุเครื่องมือรอ การถอดเสียงที่เอเจนต์สร้างจะเริ่มในโหมดส่วนตัว โดยไม่มีลิงก์แชร์
เอเจนต์ไม่สามารถนั่งดูการถ่ายทอดสดได้อดทนไปกว่าคุณ มันจะเรียกเครื่องมือรอซ้ำไปเรื่อยๆ จนกว่าจะมีคนเอาแล็ปท็อปไปเสียก่อน การบันทึกที่เสร็จแล้วมีจุดสิ้นสุด และนั่นคือสิ่งที่เครื่องมือคู่ถัดไปรอคอย
การเรียกเครื่องมือหนึ่งครั้งคือ HTTP request หนึ่งรายการ และ HTTP request ไม่ได้ออกแบบมาให้ทำงานนานหนึ่งชั่วโมง client จะยอมแพ้ proxy จะปิดการเชื่อมต่อ และโมเดลที่ติดอยู่กับเครื่องมือหนึ่งจะทำอย่างอื่นไม่ได้เลย ดังนั้นรูปแบบสำหรับงานยาวใดๆ ผ่าน MCP จึงเป็นแบบเดียวกับที่ REST API ใช้ คือเริ่มงานแล้วรับ handle รอผลเป็นเวลาจำกัด และเรียกซ้ำหากยังไม่เสร็จ handle เป็นตัวเก็บสถานะ ไม่ใช่การเชื่อมต่อ
wait_for_transcription จะรอจนงานเป็น done หรือ failed หรือจนกว่า timeoutS จะหมด (ค่าเริ่มต้น 50 วินาที สูงสุด 60 วินาที ซึ่งทำให้การเรียกอยู่ภายใต้ timeout ที่ client และ proxy ส่วนใหญ่กำหนด) หากผลลัพธ์ระบุ timedOut: true แปลว่างานยังทำงานอยู่ และเอเจนต์จะเรียกเครื่องมือซ้ำ ขณะที่เครื่องมือกำลังรอ เซิร์ฟเวอร์จะส่งการแจ้งความคืบหน้าของ MCP หาก client ร้องขอไว้ ได้แก่ เปอร์เซ็นต์และขั้นตอน เช่น "fetching 3.1 / 8.0 MB" หรือ "transcribing 01:25 of 02:00"get_transcription ส่งสถานะกลับมา และเมื่อเสร็จแล้วจะส่งข้อความด้วย format: "text" จะได้หนึ่งบรรทัดต่อหนึ่งช่วงเป็นรูปแบบ [mm:ss] Speaker 1: ... ส่วน "segments" จะได้บรรทัดที่มีเวลากำกับพร้อม start, end, speaker และ speakerName ส่วน includeWords: true จะเพิ่มเวลาของแต่ละคำ ผลลัพธ์แบ่งเป็นหน้า (offset, limit ค่าเริ่มต้น 500 ช่วง) และมี nextOffset เมื่อยังมีข้อมูลเหลืออยู่การแบ่งหน้ามีไว้เพื่อโมเดล ไม่ใช่เพื่อเครือข่าย ทุกอย่างที่เครื่องมือส่งกลับจะถูกวางลงในหน้าต่างบริบทของเอเจนต์ ซึ่งต้องแย่งพื้นที่กับการสนทนาของคุณและเสียโทเค็นในทุกเทิร์นถัดไป การประชุมสองชั่วโมงมีคำนับหมื่นคำ หากส่งกลับทั้งหมดในครั้งเดียว คำถามที่คุณถามจะถูกดันออกจากบริบท เครื่องมือ MCP ที่ดีจะส่งกลับเป็นหนึ่งหน้าพร้อมตัวชี้ตำแหน่ง (cursor) และให้เอเจนต์ตัดสินใจเองว่าต้องการข้อมูลมากเพียงใด
การหลุดการเชื่อมต่อหรือ timeout ไม่เคยยกเลิกงาน เพราะงานอยู่บนเซิร์ฟเวอร์ ไม่ได้อยู่ในคำขอ MCP เมื่อเสร็จแล้ว งานจะกลายเป็นรายการหนึ่งในคลังของคุณ และเครื่องมือถัดไปจะจัดการกับมันเหมือนรายการอื่นๆ
list_transcriptions แสดงรายการการถอดเสียงของบัญชี เรียงจากใหม่ไปเก่า พร้อมสถานะและความยาว lifecycle คือ active, archived หรือ allsearch_transcriptions ค้นหาสิ่งที่ถูกพูดในทุกการถอดเสียง ไม่ว่าจะเป็นภาษาใด แล้วส่งบรรทัดที่ตรงกลับมา พร้อมการถอดเสียงที่บรรทัดนั้นอยู่และวินาทีที่ถูกพูด transcriptionId จำกัดการค้นหาให้อยู่ในการถอดเสียงเดียว ส่วน lang จะเพิ่มคำแปลของผลลัพธ์แต่ละรายการการหาบรรทัดเป็นครึ่งที่ง่ายกว่า อีกสองเครื่องมือจะช่วยสรุปว่าทั้งหมดพูดอะไร
get_summary ส่งชื่อเรื่องและบทสรุปที่ AI สร้างของการถอดเสียงที่เสร็จแล้วกลับมา และแปลได้ด้วย lang หาก state: "pending" หมายความว่ากำลังเขียนบทสรุปอยู่ (โดยปกติจะพร้อมภายในไม่กี่วินาทีหลังงานเสร็จ) และเอเจนต์จะถามอีกครั้งtranslate_transcription ส่งข้อความถอดเสียงที่เสร็จแล้วในอีกภาษาหนึ่งกลับมา ทั้งแบบข้อความหรือเป็นช่วงที่มีเวลากำกับ และแบ่งเป็นหน้าเหมือน get_transcription คำแปลจะถูกบันทึกไว้ จึงไม่ต้องทำงานซ้ำเมื่อถามอีกครั้งlist_voices แสดงรายการเสียงที่สามารถอ่านข้อความถอดเสียงในภาษาหนึ่งได้ ได้แก่ id ชื่อ เพศและสำเนียงเมื่อทราบ คะแนนคุณภาพ และว่าเป็นเสียงเริ่มต้นหรือหนึ่งในเสียงโคลนของคุณ เครื่องมือนี้บรรยายเสียง แต่ไม่ได้ทำให้เสียงใดพูด (ดูด้านล่างว่าเซิร์ฟเวอร์ทำอะไรไม่ได้บ้าง)นี่คือลำดับการทำงานของเครื่องมือเหล่านี้ในการใช้งานจริง
เมื่อเพิ่มเซิร์ฟเวอร์แล้ว การสนทนาใน Claude Code จะเป็นดังนี้ คุณวางลิงก์พอดแคสต์และขอใจความสำคัญเป็นภาษาฝรั่งเศส
เอเจนต์เรียก transcribe_url:
{ "url": "https://example.org/episodes/42.mp3", "name": "Episode 42", "speakerLabels": "auto" }
มันได้รับ transcriptionId และ state: "queued" กลับมา และทำตามคำใบ้ next:
{ "transcriptionId": "4f1c...", "timeoutS": 50 }
ตอนความยาวห้าสิบนาทีถูกถอดเสียงได้เร็วกว่าเวลาเล่นหลายเท่า แต่ไม่ได้เสร็จในการรอครั้งเดียว การรอครั้งแรกจะส่ง timedOut: true กลับมาพร้อมความคืบหน้า เช่น 61 เปอร์เซ็นต์ เอเจนต์จึงเรียกเครื่องมือเพิ่มอีกครั้งและได้ state: "done"
จากนั้นเอเจนต์เรียก get_summary พร้อม lang: "fr":
{ "transcriptionId": "4f1c...", "lang": "fr" }
หากบทสรุปยังเป็น pending เอเจนต์จะรอสักครู่ มิฉะนั้นมันจะส่งชื่อเรื่องและบทสรุปเป็นภาษาฝรั่งเศสให้คุณ ถ้าถามว่า "พวกเขาพูดถึงเรื่องราคาตรงไหน" มันจะเรียก search_transcriptions พร้อม query: "pricing" และ transcriptionId เดิม แล้วอ้างอิงบรรทัดที่ตรงกันพร้อมเวลากำกับ หากขอให้แปลทั้งหมดเป็นภาษาฝรั่งเศส มันจะเรียก translate_transcription
การเรียกเครื่องมือเพียงไม่กี่ครั้ง ไม่ต้องใช้โค้ด และได้การถอดเสียงหนึ่งรายการในคลังของคุณ การเรียกแต่ละครั้งผ่านด่านเดียวกับคำขอ REST และด่านเหล่านั้นแหละที่กำหนดค่าใช้จ่ายและขีดจำกัด
เซิร์ฟเวอร์ MCP คือ API ที่เข้าผ่านประตูอีกบานหนึ่ง มีขีดจำกัดเดียวกันและเรียกเก็บเงินเหมือนกัน
POST /v1/transcriptions จึงใช้อัตราการสร้างของแพ็กเกจ (จำนวนการถอดเสียงที่เริ่มได้ต่อชั่วโมง 10, 100 หรือจำนวนที่กำหนดเอง) ขีดจำกัดการถอดเสียงพร้อมกัน (1, 5 หรือไม่จำกัด) และเครดิตของแพ็กเกจทั้งหมด เครดิตเริ่มต้นตอนสมัคร $5 ครอบคลุมการใช้งานครั้งแรก โดยไม่ต้องใช้บัตรisError: true พร้อมรหัสข้อผิดพลาดของ REST API ประโยคที่โมเดลนำไปใช้ต่อได้ และฟิลด์ retryable กับ retryAfterS insufficient_credit มี topUpUrl ส่วน rate_limited บอกว่าต้องรอนานเท่าใด เรื่องนี้สำคัญกว่าที่ฟังดู เพราะโมเดลอ่านข้อความผิดพลาด ประโยคที่มันนำไปใช้ได้จึงเปลี่ยนทางตันให้เป็นการเรียกเครื่องมือครั้งถัดไปได้/v1/mcp เองด้วย เอเจนต์ที่วนลูปจะได้ HTTP 429 พร้อม Retry-After ก่อนที่เครื่องมือใดจะทำงานidempotencyKey การใช้คีย์ของการเรียกที่เสร็จแล้วซ้ำจะส่งผลลัพธ์แรกกลับมาอีกครั้ง และการใช้คีย์เดิมกับอาร์กิวเมนต์ที่ต่างออกไปจะถูกปฏิเสธ ดังนั้นเอเจนต์ที่ลองใหม่เองจะไม่เริ่มงานที่สองเลย เครื่องมือใดก็ตามที่เสียเงินควรมีตัวเลือกนี้ เพราะเอเจนต์ลองใหม่อย่างอิสระกว่าคนมากนั่นคือขอบเขตของสิ่งที่เซิร์ฟเวอร์ทำ และมีสองเรื่องที่อยู่นอกขอบเขตนี้โดยตั้งใจ
เซิร์ฟเวอร์ไม่มีเครื่องมือที่แปลงข้อความใดก็ได้ให้เป็นเสียงพูด และไม่มีแผนจะทำ VocalStack พูดได้เฉพาะข้อความถอดเสียงและคำแปลของข้อความเหล่านั้น ด้วยเสียงสำเร็จรูปหรือเสียงโคลน และไม่มีอย่างอื่น list_voices บอกเอเจนต์ว่าเสียงใดอ่านข้อความถอดเสียงในภาษาหนึ่งได้ ไม่ได้บอกวิธีให้เสียงพูดสิ่งใหม่
เครื่องมือถอดเสียงยังปฏิเสธสตรีมสดด้วย สตรีมสดของ YouTube หรือสตรีมวิทยุต้องใช้การถอดเสียงสด ซึ่งเริ่มจากแดชบอร์ดหรือผ่าน POST /v1/transcriptions/live ในเอกสาร API อ้างอิง วิดีโอ YouTube ที่จบแล้วเป็นไฟล์ และ transcribe_url รับไฟล์นั้นได้
หากคุณอยากเขียนโค้ดเอง การถอดเสียงไฟล์ด้วย API จะอธิบายขั้นตอนเดียวกันในรูปแบบการเรียก HTTP และบทแนะนำการถอดเสียงแบบเรียลไทม์ครอบคลุม WebSocket