Een MCP-server maakt van een API een paar benoemde tools die een agent midden in een gesprek aanroept, en een lange taak past erachter wanneer de tool een handle teruggeeft waarop de agent in begrensde beurten wacht. Plak een podcastlink, vraag om de kern in het Frans, en er wordt geen code geschreven.
Een coding-agent kan de VocalStack API gebruiken zoals elk ander programma: de API-referentie lezen, een paar regels schrijven met de SDK en die uitvoeren. Dat is het juiste pad voor iets dat zonder de agent draait.
MCP (het Model Context Protocol) is bedoeld voor het andere geval: je praat met een assistent en wilt dat hij nu iets transcribeert, zonder code. Een MCP-server publiceert een korte lijst benoemde tools, elk met een beschrijving en een JSON-schema voor de argumenten. De agent leest die lijst één keer en roept een tool bij naam aan zodra een verzoek erbij past. "Transcribeer deze podcast en vertel me wat ze zeiden over prijzen" wordt drie toolaanroepen, en de agent ziet nooit een HTTP-route.
Servers komen in twee soorten. Een lokale server draait als subproces van de client en communiceert via stdio, zoals de meeste bestandssysteem- en databaseservers werken. Een externe server zit achter een URL en spreekt Streamable HTTP, het transport dat in de revisie van maart 2025 van de specificatie het oudere HTTP-plus-SSE-transport (server-sent events) verving. De client stuurt elk verzoek, en de server antwoordt op dezelfde verbinding; alles wat hij nog moet melden voor het definitieve resultaat wordt er vooraf gestreamd. Elke client die tegen de huidige specificatie is geschreven, kan beide soorten gebruiken.
De server van VocalStack is van het externe type, op {{api}}/v1/mcp, en het gebruik ervan kost wat de API kost. Je hebt alleen de URL nodig.
Open het Developers-gedeelte van het Dashboard op {{app}}/developers. Het eerste tabblad, Connect an AI agent, toont de server-URL en een installatiesnippet voor elke client. De drie snippets hieronder komen overeen met die op de pagina.
Voer dit uit in je terminal:
claude mcp add --transport http vocalstack {{api}}/v1/mcp
Voeg dit toe aan .cursor/mcp.json in je project, of aan ~/.cursor/mcp.json voor elk project:
{"mcpServers":{"vocalstack":{"url":"{{api}}/v1/mcp"}}}
Elke client die Streamable HTTP ondersteunt, kan de server met deze URL toevoegen. Dit is de generieke vorm die het Dashboard toont:
{
"vocalstack": {
"type": "streamable-http",
"url": "{{api}}/v1/mcp"
}
}
Je hebt geen API-sleutel nodig om te beginnen. De eerste keer dat de client verbinding maakt, log je in met je VocalStack-account in de browser, en de client bewaart het token. Het Dashboard zegt het in één regel: "Je logt de eerste keer in met je VocalStack-account. Geen sleutel nodig."
Dit is de autorisatiespecificatie van MCP in actie, geen VocalStack-truc. Een externe server beantwoordt een niet-geauthenticeerd verzoek met een 401 die verwijst naar zijn protected-resource-metadata: een klein JSON-document dat de autorisatieserver en de scopes noemt die de server vereist. De client leest dat, doorloopt in je browser een standaard OAuth 2.1-flow en komt terug met een token dat alleen voor die server is uitgegeven. Elke externe MCP-server die de specificatie volgt, logt je op dezelfde manier in, en daarom kunnen de clients hierboven dat doen zonder iets van VocalStack te weten.
Een API-sleutel werkt ook, voor een client die op een server of in een script draait. Stuur hem als Authorization: Bearer vs_key_..., dezelfde header die de REST API gebruikt. Sleutels maak je aan onder Developers → API keys. De twee transcribe-tools hebben de rechten Write en Stream audio nodig. Het tweede klinkt vreemd voor een bestand, maar de tools lopen via hetzelfde creatiepunt als elke transcriptie, ook live, en dat punt controleert beide. De leestools hebben Read nodig; list_voices heeft alleen een geldige inloggegevens nodig.
Zodra je bent ingelogd, ziet de agent negen tools. Je noemt ze zelf zelden bij naam, maar als je ze kent, begrijp je wat de agent daarna doet.
Elke tool neemt camelCase-JSON aan en geeft die terug, met dezelfde veldnamen als de wire-types van /v1; de handle van een transcriptie is haar transcriptionId.
transcribe_url start het transcriberen van audio of video op een publieke URL: een bestandslink, een podcastaflevering, een YouTube-video. Live-streams worden niet geaccepteerd. Optionele argumenten: name, spokenLanguage (weglaten om de taal te laten detecteren), speakerLabels (auto, on of off) en startS/endS om alleen een deel van de audio te transcriberen.transcribe_file doet hetzelfde voor een bestand dat de agent bij zich heeft, verstuurd als fileBase64 met de filename. De gedecodeerde grootte is beperkt tot 10 MiB, ongeveer tien minuten MP3 van 128 kbps; voor alles groters geef je de agent een link.Beide geven meteen de transcriptionId, state: "queued" en een next-hint terug met de naam van de wacht-tool. Door de agent gemaakte transcripties beginnen privé, zonder deellink.
Een agent kan niet geduldiger naar een live-uitzending luisteren dan jij; hij zou de wacht-tool blijven aanroepen tot iemand de laptop wegnam. Een afgerond opname heeft een einde, en dat is waar het volgende tweetal van wacht.
Een toolaanroep is één HTTP-verzoek, en HTTP-verzoeken zijn niet bedoeld om een uur te duren. Clients geven er op, proxies sluiten ze, en een model dat op één tool wacht, kan niets anders doen. Het patroon voor een lange taak achter MCP is daarom hetzelfde als bij de REST API: start de taak en krijg een handle, wacht er een begrensde tijd op, en roep opnieuw aan als hij nog niet klaar is. De handle draagt de status, niet de verbinding.
wait_for_transcription blokkeert tot de taak done of failed is, of tot timeoutS verstreken is (standaard 50 seconden, maximaal 60, wat de aanroep onder de time-outs houdt die de meeste clients en proxies hanteren). Als het resultaat timedOut: true zegt, draait de taak nog en roept de agent de tool opnieuw aan. Terwijl de tool wacht, stuurt de server MCP-voortgangsmeldingen als de client daarom heeft gevraagd: een percentage en een fase, zoals "ophalen 3,1 / 8,0 MB" of "transcriberen 01:25 van 02:00".get_transcription geeft de status en, zodra die klaar is, de tekst. format: "text" geeft één regel per segment als [mm:ss] Speaker 1: ...; "segments" geeft getimede regels met start, end, speaker en speakerName; includeWords: true voegt woordtijden toe. Resultaten worden in pagina's aangeboden (offset, limit, standaard 500 segmenten) met een nextOffset als er meer zijn.De paginering is er voor het model, niet voor het netwerk. Alles wat een tool teruggeeft, wordt in het contextvenster van de agent geplakt, waar het concurreert met je gesprek en elke latere beurt tokens kost. Een vergadering van twee uur is tienduizenden woorden; alles tegelijk teruggeven zou de vraag die je stelde uit de context duwen. Goede MCP-tools geven één pagina en een cursor terug en laten de agent beslissen hoeveel hij nodig heeft.
Een verbreking of time-out annuleert de taak nooit, omdat de taak op de server leeft, niet in het MCP-verzoek. Zodra hij klaar is, is de taak één item extra in je bibliotheek, en de volgende tools behandelen hem als elke andere.
list_transcriptions toont de transcripties van het account, nieuwste eerst, met status en duur; lifecycle is active, archived of all.search_transcriptions doorzoekt wat er is gezegd in alle transcripties, in elke taal, en geeft de passende regels terug, elk met de transcriptie en de seconde waarop het werd gezegd. transcriptionId beperkt de zoekopdracht tot één transcriptie; lang voegt een vertaling van elke treffer toe.Een regel vinden is het goedkope deel. Twee andere tools bepalen wat het geheel zei.
get_summary geeft de AI-titel en -samenvatting van een afgeronde transcriptie, optioneel vertaald met lang. state: "pending" betekent dat de samenvatting nog wordt geschreven (meestal is ze er binnen enkele seconden nadat de taak klaar is), en de agent vraagt opnieuw.translate_transcription geeft een afgerond transcript in een andere taal terug, als tekst of als getimede segmenten, gepagineerd zoals get_transcription. Vertalingen worden opgeslagen, dus opnieuw vragen herhaalt het werk niet.list_voices toont de stemmen die een transcript in een taal kunnen voorlezen: id, naam, geslacht en accent als die bekend zijn, een kwaliteitsbeoordeling, en of het de standaard is of een van je gekloonde stemmen. Het beschrijft stemmen; het laat er geen spreken (meer daarover hieronder bij wat de server niet doet).Zo werken de tools in de praktijk samen.
Zodra de server is toegevoegd, verloopt een gesprek in Claude Code zo. Je plakt een podcastlink en vraagt om de kern in het Frans.
De agent roept transcribe_url aan:
{ "url": "https://example.org/episodes/42.mp3", "name": "Episode 42", "speakerLabels": "auto" }
Hij krijgt een transcriptionId en state: "queued" terug, en volgt de next-hint:
{ "transcriptionId": "4f1c...", "timeoutS": 50 }
Een aflevering van vijftig minuten wordt vele keren sneller getranscribeerd dan ze duurt, maar niet binnen één wachttijd. De eerste wachttijd geeft timedOut: true terug met voortgang van, laten we zeggen, 61 procent, dus de agent roept de tool nog eens aan en krijgt state: "done".
Nu roept de agent get_summary aan met lang: "fr":
{ "transcriptionId": "4f1c...", "lang": "fr" }
Als de samenvatting nog pending is, wacht de agent een paar seconden; anders geeft hij je een titel en samenvatting in het Frans. Vraag je "waar hadden ze het over prijzen?", dan roept hij search_transcriptions aan met query: "pricing" en dezelfde transcriptionId, en citeert hij de passende regels met hun tijdstempels. Vraag je om het hele geheel in het Frans, dan roept hij translate_transcription aan.
Een handvol toolaanroepen, geen code, en een transcriptie in je bibliotheek. Elk van die aanroepen is door dezelfde controles gegaan als een REST-verzoek, en die controles bepalen de rekening en de limieten.
De MCP-server is de API via een andere voordeur: dezelfde limieten, dezelfde rekening.
POST /v1/transcriptions: de creatiesnelheid van het abonnement (10, 100 of een eigen aantal transcripties per uur), het maximum aantal gelijktijdige transcripties (1, 5 of onbeperkt) en het tegoed gelden allemaal. Het signup-tegoed van $5 dekt een eerste sessie, zonder creditcard.isError: true terug met de foutcode van de REST API, een zin waarop het model kan handelen, en de velden retryable en retryAfterS. insufficient_credit bevat een topUpUrl; rate_limited zegt hoe lang je moet wachten. Dit telt zwaarder dan het lijkt: een model leest de fouttekst, en een zin waarop het kan handelen, maakt van een doodlopende weg de volgende toolaanroep./v1/mcp-verzoek zelf: een agent in een loop krijgt een HTTP 429 met Retry-After voordat er een tool draait.idempotencyKey: opnieuw de sleutel van een voltooide aanroep gebruiken geeft het eerste resultaat terug, en hem met andere argumenten gebruiken wordt geweigerd, zodat een agent die zelfstandig opnieuw probeert nooit een tweede taak start. Elke tool die geld uitgeeft, zou dit moeten bieden; agenten proberen veel vrijer opnieuw dan mensen.Dat zijn de grenzen van wat de server doet. Twee dingen vallen er met opzet buiten.
De server heeft geen tool die willekeurige tekst in spraak omzet, en er is er ook geen gepland. VocalStack laat transcripten en hun vertalingen spreken, in een standaard- of gekloonde stem, en niets anders. list_voices vertelt een agent welke stemmen een transcript in een taal kunnen voorlezen, niet hoe je een stem iets nieuws laat zeggen.
De transcribe-tools weigeren ook live-streams. Een YouTube-livestream of een radiostream heeft een livetranscriptie nodig, gestart vanuit het Dashboard of via POST /v1/transcriptions/live in de API-referentie. Een afgelopen YouTube-video is een bestand, en transcribe_url neemt die aan.
Als je de code liever zelf schrijft, loopt een bestand transcriberen met de API dezelfde stappen door als HTTP-aanroepen, en de realtime-tutorial behandelt de WebSocket.