Một máy chủ MCP biến API thành vài công cụ có tên mà agent gọi giữa cuộc trò chuyện, và một tác vụ dài vẫn chạy được theo cách này khi công cụ trả về một mã tham chiếu mà agent chờ theo từng lượt có giới hạn. Dán một liên kết podcast, yêu cầu nội dung chính bằng tiếng Pháp, và không cần viết dòng code nào.
Một coding agent có thể dùng API của VocalStack như mọi chương trình khác: đọc tài liệu tham chiếu API, viết vài dòng với SDK rồi chạy chúng. Đó là cách đúng cho những việc sẽ chạy mà không cần agent.
MCP (Model Context Protocol) dành cho trường hợp còn lại: bạn đang trò chuyện với một trợ lý và muốn nó phiên âm thứ gì đó ngay bây giờ, không cần viết code. Một máy chủ MCP công bố một danh sách ngắn các công cụ có tên, mỗi công cụ kèm mô tả và một JSON schema cho các tham số của nó. Agent đọc danh sách này một lần và gọi công cụ theo tên khi một yêu cầu khớp với công cụ đó. "Hãy phiên âm podcast này và cho tôi biết họ đã nói gì về giá" sẽ thành ba lần gọi công cụ, và agent không bao giờ thấy một đường dẫn HTTP nào.
Máy chủ có hai loại. Máy chủ cục bộ chạy như một tiến trình con của client và giao tiếp qua stdio, cách hoạt động của hầu hết các máy chủ hệ thống tệp và cơ sở dữ liệu. Máy chủ từ xa nằm sau một URL và dùng Streamable HTTP, giao thức thay thế cho cơ chế HTTP-plus-SSE (server-sent events) cũ trong bản sửa đổi tháng 3 năm 2025 của đặc tả. Client gửi từng yêu cầu, và máy chủ trả lời trên cùng kết nối; mọi nội dung máy chủ cần nói trước kết quả cuối cùng sẽ được truyền theo luồng trước đó. Bất kỳ client nào viết theo đặc tả hiện hành đều dùng được cả hai loại.
Máy chủ của VocalStack thuộc loại từ xa, tại {{api}}/v1/mcp, và dùng nó tốn đúng chi phí như API. Bạn chỉ cần có URL.
Mở khu vực Developers trong Bảng điều khiển tại {{app}}/developers. Tab đầu tiên, Connect an AI agent, hiển thị URL của máy chủ và đoạn cấu hình cho từng client. Ba đoạn cấu hình dưới đây khớp với những đoạn được hiển thị ở đó.
Chạy lệnh này trong terminal của bạn:
claude mcp add --transport http vocalstack {{api}}/v1/mcp
Thêm nội dung sau vào .cursor/mcp.json trong dự án của bạn, hoặc vào ~/.cursor/mcp.json để dùng cho mọi dự án:
{"mcpServers":{"vocalstack":{"url":"{{api}}/v1/mcp"}}}
Bất kỳ client nào hỗ trợ Streamable HTTP đều có thể thêm máy chủ bằng URL này. Đây là dạng chung mà Bảng điều khiển hiển thị:
{
"vocalstack": {
"type": "streamable-http",
"url": "{{api}}/v1/mcp"
}
}
Bạn không cần khóa API để bắt đầu. Lần đầu client kết nối, bạn đăng nhập bằng tài khoản VocalStack trong trình duyệt, và client sẽ giữ lại token. Bảng điều khiển ghi điều này trong một dòng: “Lần đầu, bạn sẽ đăng nhập bằng tài khoản VocalStack. Không cần khóa.”
Đây là đặc tả ủy quyền của MCP đang hoạt động, không phải mẹo riêng của VocalStack. Máy chủ từ xa trả lời yêu cầu chưa xác thực bằng mã 401 kèm liên kết đến metadata tài nguyên được bảo vệ: một tài liệu JSON nhỏ, nêu rõ máy chủ ủy quyền và các phạm vi (scope) mà máy chủ yêu cầu. Client đọc tài liệu này, chạy luồng OAuth 2.1 chuẩn trong trình duyệt của bạn, và quay về với một token chỉ được cấp cho riêng máy chủ đó. Bất kỳ máy chủ MCP từ xa nào tuân theo đặc tả đều đăng nhập bạn theo cách này, đó là lý do các client trên có thể làm điều đó mà không cần biết gì về VocalStack.
Khóa API cũng dùng được, cho client chạy trên máy chủ hoặc trong script. Gửi khóa dưới dạng Authorization: Bearer vs_key_..., cùng header mà REST API dùng. Khóa được tạo trong Developers → API keys. Hai công cụ phiên âm cần quyền Write và Stream audio. Quyền thứ hai nghe có vẻ lạ đối với một tệp, nhưng các công cụ này đi qua cùng một điểm tạo như mọi bản phiên âm, kể cả bản trực tiếp, và điểm đó kiểm tra cả hai quyền. Các công cụ đọc cần quyền Read; list_voices chỉ cần một thông tin xác thực hợp lệ.
Khi đã đăng nhập, agent sẽ thấy chín công cụ. Bạn hiếm khi tự gọi tên chúng, nhưng biết chúng sẽ giúp hiểu agent làm gì tiếp theo.
Mọi công cụ đều nhận và trả về JSON camelCase, với cùng tên trường như các kiểu dữ liệu truyền tải của /v1; mã tham chiếu của một bản phiên âm là transcriptionId.
transcribe_url bắt đầu phiên âm âm thanh hoặc video tại một URL công khai: liên kết tệp, một tập podcast, một video YouTube. Không chấp nhận luồng trực tiếp. Các tham số tùy chọn: name, spokenLanguage (bỏ qua để tự nhận diện ngôn ngữ), speakerLabels (auto, on hoặc off) và startS/endS để chỉ phiên âm một phần âm thanh.transcribe_file làm việc tương tự với một tệp mà agent đang giữ, được gửi dưới dạng fileBase64 kèm filename. Kích thước sau khi giải mã giới hạn ở 10 MiB, tương đương khoảng mười phút MP3 128 kbps; với tệp lớn hơn, hãy đưa cho agent một liên kết.Cả hai đều trả về ngay lập tức với transcriptionId, state: "queued" và gợi ý next nêu tên công cụ chờ. Các bản phiên âm do agent tạo ra bắt đầu ở chế độ riêng tư, không có liên kết chia sẻ.
Agent không thể kiên nhẫn theo dõi một buổi phát sóng trực tiếp hơn bạn; nó sẽ liên tục gọi công cụ chờ cho đến khi ai đó lấy mất chiếc laptop. Một bản ghi đã hoàn tất thì có điểm kết thúc, và đó là thứ cặp công cụ tiếp theo chờ đợi.
Một lần gọi công cụ là một yêu cầu HTTP, và yêu cầu HTTP không được phép kéo dài cả giờ. Client sẽ bỏ cuộc, proxy sẽ đóng kết nối, và một mô hình đang bị chặn bởi một công cụ thì không làm được việc gì khác. Vì vậy, mẫu cho bất kỳ tác vụ dài nào phía sau MCP cũng giống với REST API: khởi động tác vụ và nhận một mã tham chiếu, chờ trong một khoảng thời gian giới hạn, rồi gọi lại nếu chưa xong. Mã tham chiếu, chứ không phải kết nối, mang trạng thái.
wait_for_transcription chặn cho đến khi tác vụ chuyển sang done hoặc failed, hoặc đến khi hết timeoutS (mặc định 50 giây, tối đa 60 giây, để lời gọi nằm dưới mức timeout mà hầu hết client và proxy áp dụng). Nếu kết quả ghi timedOut: true, tác vụ vẫn đang chạy và agent gọi lại công cụ. Trong lúc công cụ chờ, nếu client yêu cầu, máy chủ gửi thông báo tiến trình MCP: một tỷ lệ phần trăm và một giai đoạn, chẳng hạn "đang tải 3.1 / 8.0 MB" hoặc "đang phiên âm 01:25 trên 02:00".get_transcription trả về trạng thái và, khi đã xong, nội dung văn bản. format: "text" cho mỗi đoạn một dòng dạng [mm:ss] Speaker 1: ...; "segments" cho các dòng có mốc thời gian với start, end, speaker và speakerName; includeWords: true thêm mốc thời gian của từng từ. Kết quả được chia trang (offset, limit, mặc định 500 đoạn) và có nextOffset khi còn dữ liệu.Việc chia trang có mặt vì mô hình, không phải vì mạng. Mọi thứ một công cụ trả về đều được dán vào cửa sổ ngữ cảnh của agent, nơi nó cạnh tranh với cuộc trò chuyện của bạn và tốn token ở mỗi lượt sau. Một cuộc họp hai giờ có hàng chục nghìn từ; trả về toàn bộ cùng lúc sẽ đẩy câu hỏi của bạn ra khỏi ngữ cảnh. Công cụ MCP tốt trả về một trang cùng một con trỏ và để agent quyết định nó cần bao nhiêu.
Việc mất kết nối hoặc hết thời gian chờ không bao giờ hủy tác vụ, vì tác vụ nằm trên máy chủ, không nằm trong yêu cầu MCP. Khi đã xong, tác vụ trở thành một mục nữa trong thư viện của bạn, và các công cụ tiếp theo đối xử với nó như mọi mục khác.
list_transcriptions liệt kê các bản phiên âm của tài khoản, mới nhất trước, kèm trạng thái và thời lượng; lifecycle là active, archived hoặc all.search_transcriptions tìm kiếm nội dung đã được nói trong tất cả bản phiên âm, bằng mọi ngôn ngữ, và trả về các dòng khớp, mỗi dòng kèm bản phiên âm chứa nó và giây được nói. transcriptionId thu hẹp tìm kiếm vào một bản phiên âm; lang thêm bản dịch của mỗi kết quả.Tìm một dòng chỉ là nửa dễ. Hai công cụ nữa giúp hiểu toàn bộ nội dung đã được nói.
get_summary trả về tiêu đề và bản tóm tắt do AI tạo cho một bản phiên âm đã hoàn tất, có thể dịch bằng lang. state: "pending" nghĩa là bản tóm tắt vẫn đang được viết (thường sẵn sàng trong vài giây sau khi tác vụ xong), và agent hỏi lại.translate_transcription trả về bản phiên âm đã hoàn tất bằng một ngôn ngữ khác, dưới dạng văn bản hoặc các đoạn có mốc thời gian, được chia trang như get_transcription. Bản dịch được lưu lại, nên hỏi lại sẽ không làm lại công việc.list_voices liệt kê các giọng đọc có thể đọc bản phiên âm bằng một ngôn ngữ: id, tên, giới tính và giọng vùng miền khi biết, xếp hạng chất lượng, và việc đó là giọng mặc định hay là một trong các giọng nhân bản của bạn. Công cụ này chỉ mô tả giọng đọc; nó không làm giọng nào nói (xem bên dưới về những gì máy chủ không làm).Dưới đây là cách các công cụ nối tiếp nhau trong thực tế.
Sau khi thêm máy chủ, một cuộc trò chuyện trong Claude Code diễn ra như sau. Bạn dán liên kết podcast và yêu cầu nội dung chính bằng tiếng Pháp.
Agent gọi transcribe_url:
{ "url": "https://example.org/episodes/42.mp3", "name": "Episode 42", "speakerLabels": "auto" }
Nó nhận lại transcriptionId và state: "queued", rồi làm theo gợi ý next:
{ "transcriptionId": "4f1c...", "timeoutS": 50 }
Một tập dài năm mươi phút được phiên âm nhanh hơn thời lượng phát rất nhiều lần, nhưng không xong trong một lần chờ. Lần chờ đầu tiên trả về timedOut: true với tiến trình ở khoảng 61 phần trăm, nên agent gọi công cụ thêm một lần nữa và nhận được state: "done".
Bây giờ agent gọi get_summary với lang: "fr":
{ "transcriptionId": "4f1c...", "lang": "fr" }
Nếu bản tóm tắt vẫn ở trạng thái pending, agent chờ vài giây; nếu không, nó đưa cho bạn tiêu đề và bản tóm tắt bằng tiếng Pháp. Hỏi “họ đã nói về giá ở đâu?” và nó gọi search_transcriptions với query: "pricing" cùng transcriptionId đó, rồi trích dẫn các dòng khớp kèm mốc thời gian. Yêu cầu dịch toàn bộ sang tiếng Pháp và nó gọi translate_transcription.
Vài lần gọi công cụ, không cần code, và một bản phiên âm nằm trong thư viện của bạn. Mỗi lần gọi đó đều đi qua cùng những cổng kiểm soát như một yêu cầu REST, và chính những cổng này quyết định hóa đơn và giới hạn.
Máy chủ MCP chính là API qua một cửa vào khác: cùng giới hạn, cùng hóa đơn.
POST /v1/transcriptions: tốc độ tạo của gói (10, 100 hoặc một số tùy chỉnh các bản phiên âm được bắt đầu mỗi giờ), giới hạn số bản phiên âm chạy đồng thời (1, 5 hoặc không giới hạn) và tín dụng của gói đều được áp dụng. Tín dụng đăng ký $5 đủ cho phiên làm việc đầu tiên, không cần thẻ.isError: true cùng mã lỗi của REST API, một câu mà mô hình có thể hành động theo, và các trường retryable và retryAfterS. insufficient_credit đi kèm topUpUrl; rate_limited cho biết cần chờ bao lâu. Điều này quan trọng hơn vẻ ngoài: mô hình đọc nội dung lỗi, nên một câu mà nó có thể hành động theo sẽ biến một ngõ cụt thành lần gọi công cụ tiếp theo./v1/mcp: một agent đang lặp sẽ nhận HTTP 429 kèm Retry-After trước khi bất kỳ công cụ nào chạy.idempotencyKey: dùng lại khóa của một lời gọi đã hoàn tất sẽ phát lại kết quả đầu tiên, còn dùng lại khóa với tham số khác sẽ bị từ chối, nên một agent tự thử lại sẽ không bao giờ khởi động tác vụ thứ hai. Bất kỳ công cụ nào tiêu tốn tiền đều nên cung cấp tính năng này; agent thử lại thoải mái hơn con người nhiều.Đó là giới hạn của những gì máy chủ làm. Có hai việc nằm ngoài phạm vi đó, và đây là có chủ đích.
Máy chủ không có công cụ nào biến văn bản tùy ý thành giọng nói, và cũng không có kế hoạch nào như vậy. VocalStack chỉ đọc bản phiên âm và bản dịch của chúng, bằng giọng có sẵn hoặc giọng nhân bản, và không làm gì khác. list_voices cho agent biết giọng nào có thể đọc bản phiên âm bằng một ngôn ngữ, chứ không hướng dẫn cách làm cho một giọng nói điều gì mới.
Các công cụ phiên âm cũng từ chối luồng trực tiếp. Luồng trực tiếp trên YouTube hoặc luồng radio cần một bản phiên âm trực tiếp, được bắt đầu từ Bảng điều khiển hoặc qua POST /v1/transcriptions/live trong tài liệu tham chiếu API. Một video YouTube đã kết thúc là một tệp, và transcribe_url nhận nó.
Nếu bạn muốn tự viết code, phiên âm một tệp bằng API hướng dẫn các bước tương tự dưới dạng lời gọi HTTP, còn hướng dẫn thời gian thực trình bày về WebSocket.