Un server MCP trasforma un'API in alcuni strumenti con nome che un agente richiama durante la conversazione, e un lavoro lungo ci sta dietro quando lo strumento restituisce un handle che l'agente attende per turni limitati. Incolla il link di un podcast, chiedi il senso in francese, e non verrà scritto nessun codice.
Un agente di programmazione può usare l'API di VocalStack come farebbe qualsiasi programma: leggere il riferimento dell'API, scrivere qualche riga con l'SDK ed eseguirle. Quella è la strada giusta per qualcosa che girerà senza l'agente.
MCP (il Model Context Protocol) serve per l'altro caso: stai parlando con un assistente e vuoi che trascriva qualcosa subito, senza codice. Un server MCP pubblica un breve elenco di strumenti con nome, ciascuno con una descrizione e uno schema JSON per i propri argomenti. L'agente legge l'elenco una volta sola e richiama uno strumento per nome quando una richiesta corrisponde. "Trascrivi questo podcast e dimmi che cosa hanno detto sui prezzi" diventa tre chiamate a strumenti, e l'agente non vede mai una route HTTP.
I server sono di due tipi. Un server locale gira come sottoprocesso del client e comunica via stdio, che è il modo in cui funzionano la maggior parte dei server per file system e database. Un server remoto sta dietro un URL e parla Streamable HTTP, il trasporto che nella revisione di marzo 2025 delle specifiche ha sostituito il vecchio trasporto HTTP-plus-SSE (server-sent events). Il client invia ogni richiesta e il server risponde sulla stessa connessione; tutto ciò che deve dire prima del risultato finale viene trasmesso in anticipo. Qualsiasi client scritto secondo le specifiche attuali può usare entrambi i tipi.
Il server di VocalStack è del tipo remoto, si trova su {{api}}/v1/mcp, e usarlo costa quanto costa l'API. Ti basta l'URL.
Apri l'area Developers della Dashboard all'indirizzo {{app}}/developers. La prima scheda, Connect an AI agent, mostra l'URL del server e uno snippet di configurazione per ciascun client. I tre snippet qui sotto corrispondono a quelli mostrati lì.
Esegui questo comando nel terminale:
claude mcp add --transport http vocalstack {{api}}/v1/mcp
Aggiungi questo in .cursor/mcp.json nel tuo progetto, oppure in ~/.cursor/mcp.json per tutti i progetti:
{"mcpServers":{"vocalstack":{"url":"{{api}}/v1/mcp"}}}
Qualsiasi client che supporta Streamable HTTP può aggiungere il server con questo URL. Questa è la forma generica che mostra la Dashboard:
{
"vocalstack": {
"type": "streamable-http",
"url": "{{api}}/v1/mcp"
}
}
Non ti serve una chiave API per iniziare. La prima volta che il client si connette, accedi con il tuo account VocalStack nel browser, e il client conserva il token. La Dashboard lo dice in una riga: "La prima volta accederai con il tuo account VocalStack. Non serve nessuna chiave."
Questo è il funzionamento delle specifiche di autorizzazione di MCP, non un trucco di VocalStack. Un server remoto risponde a una richiesta non autenticata con un 401 che rimanda ai suoi metadati della risorsa protetta: un piccolo documento JSON che indica il server di autorizzazione e gli scope richiesti dal server. Il client lo legge, esegue nel browser un normale flusso OAuth 2.1 e torna con un token emesso solo per quel server. Qualsiasi server MCP remoto che segue le specifiche ti fa accedere allo stesso modo, ed è per questo che i client qui sopra possono farlo senza sapere nulla di VocalStack.
Funziona anche una chiave API, per un client che gira su un server o in uno script. Inviala come Authorization: Bearer vs_key_..., la stessa intestazione che accetta l'API REST. Le chiavi si creano in Developers → API keys. I due strumenti di trascrizione richiedono i permessi Write e Stream audio. Il secondo sembra strano per un file, ma gli strumenti passano dallo stesso punto di creazione di ogni trascrizione, incluse quelle in diretta, e quel punto controlla entrambi. Gli strumenti di lettura richiedono Read; list_voices richiede solo una credenziale valida.
Una volta effettuato l'accesso, l'agente vede nove strumenti. Raramente li nominerai tu stesso, ma conoscerli aiuta a capire che cosa farà l'agente dopo.
Ogni strumento riceve e restituisce JSON in camelCase, con gli stessi nomi di campo dei tipi di /v1; l'handle di una trascrizione è il suo transcriptionId.
transcribe_url avvia la trascrizione di audio o video a un URL pubblico: un link a un file, un episodio di podcast, un video di YouTube. Le dirette non vengono accettate. Argomenti facoltativi: name, spokenLanguage (omettilo per rilevare la lingua), speakerLabels (auto, on o off) e startS/endS per trascrivere solo una parte dell'audio.transcribe_file fa lo stesso per un file che l'agente possiede, inviato come fileBase64 con il suo filename. La dimensione decodificata è limitata a 10 MiB, circa dieci minuti di MP3 a 128 kbps; per qualcosa di più grande, dai all'agente un link.Entrambi restituiscono subito il transcriptionId, state: "queued" e un suggerimento next che indica lo strumento di attesa. Le trascrizioni create dall'agente iniziano private, senza link di condivisione.
Un agente non può seguire una trasmissione in diretta con più pazienza di te; continuerebbe a richiamare lo strumento di attesa finché qualcuno non gli togliesse il portatile. Una registrazione conclusa ha una fine, ed è ciò che aspetta la coppia di strumenti successiva.
Una chiamata a uno strumento è una singola richiesta HTTP, e le richieste HTTP non sono pensate per durare un'ora. I client vi rinunciano, i proxy le chiudono, e un modello bloccato su uno strumento non può fare altro. Quindi lo schema per ogni lavoro lungo dietro MCP è lo stesso che usa anche l'API REST: avvia il lavoro e ottieni un handle, attendilo per un tempo limitato, e richiama se non è ancora finito. Lo stato lo porta l'handle, non la connessione.
wait_for_transcription si blocca finché il lavoro non è done o failed, oppure finché non scade timeoutS (predefinito 50 secondi, massimo 60, che tiene la chiamata sotto i timeout applicati dalla maggior parte dei client e dei proxy). Se il risultato dice timedOut: true, il lavoro è ancora in corso e l'agente richiama lo strumento. Mentre lo strumento attende, il server invia notifiche di avanzamento MCP se il client le ha richieste: una percentuale e una fase, come "fetching 3.1 / 8.0 MB" o "transcribing 01:25 of 02:00".get_transcription restituisce lo stato e, a lavoro concluso, il testo. format: "text" dà una riga per segmento nella forma [mm:ss] Speaker 1: ...; "segments" dà righe con i tempi, con start, end, speaker e speakerName; includeWords: true aggiunge i tempi delle singole parole. I risultati sono impaginati (offset, limit, predefinito 500 segmenti) con un nextOffset quando ne restano altri.L'impaginazione serve al modello, non alla rete. Tutto ciò che uno strumento restituisce viene incollato nella finestra di contesto dell'agente, dove compete con la tua conversazione e costa token a ogni turno successivo. Una riunione di due ore sono decine di migliaia di parole; restituirle tutte in una volta spingerebbe fuori dal contesto la domanda che hai fatto. I buoni strumenti MCP restituiscono una pagina e un cursore, e lasciano che sia l'agente a decidere quanto gli serve.
Una disconnessione o un timeout non annulla mai il lavoro, perché il lavoro vive sul server, non nella richiesta MCP. Una volta concluso, il lavoro diventa una voce in più nella tua libreria, e gli strumenti successivi lo trattano come qualsiasi altro.
list_transcriptions elenca le trascrizioni dell'account, dalla più recente, con stato e durata; lifecycle è active, archived o all.search_transcriptions cerca ciò che è stato detto in tutte le trascrizioni, in qualsiasi lingua, e restituisce le righe corrispondenti, ciascuna con la sua trascrizione e il secondo in cui è stata pronunciata. transcriptionId restringe la ricerca a una sola trascrizione; lang aggiunge una traduzione di ogni risultato.Trovare una riga è la metà facile. Altri due strumenti ricostruiscono che cosa abbia detto tutto il testo.
get_summary restituisce il titolo e il riepilogo generati dall'IA di una trascrizione conclusa, facoltativamente tradotti con lang. state: "pending" significa che il riepilogo è ancora in scrittura (di solito è pronto pochi secondi dopo la fine del lavoro), e l'agente chiede di nuovo.translate_transcription restituisce una trascrizione conclusa in un'altra lingua, come testo o segmenti con i tempi, impaginata come get_transcription. Le traduzioni vengono salvate, quindi chiedere di nuovo non ripete il lavoro.list_voices elenca le voci che possono leggere una trascrizione in una lingua: id, nome, genere e accento quando noti, una valutazione di qualità, e se è la predefinita o una delle tue voci clonate. Descrive le voci; non fa parlare nessuna di esse (più avanti, su che cosa il server non fa).Ecco come si concatenano gli strumenti nella pratica.
Una volta aggiunto il server, una conversazione in Claude Code va così. Incolli il link di un podcast e chiedi il senso in francese.
L'agente chiama transcribe_url:
{ "url": "https://example.org/episodes/42.mp3", "name": "Episode 42", "speakerLabels": "auto" }
Riceve un transcriptionId e state: "queued", e segue il suggerimento next:
{ "transcriptionId": "4f1c...", "timeoutS": 50 }
Un episodio di cinquanta minuti viene trascritto molte volte più velocemente di quanto duri, ma non in una sola attesa. La prima attesa restituisce timedOut: true con l'avanzamento al, diciamo, 61 percento, quindi l'agente richiama lo strumento una volta ancora e ottiene state: "done".
Ora l'agente chiama get_summary con lang: "fr":
{ "transcriptionId": "4f1c...", "lang": "fr" }
Se il riepilogo è ancora pending, l'agente attende qualche secondo; altrimenti ti consegna un titolo e un riepilogo in francese. Chiedi "dove hanno parlato dei prezzi?" e l'agente chiama search_transcriptions con query: "pricing" e lo stesso transcriptionId, e cita le righe corrispondenti con i relativi timestamp. Chiedi l'intero testo in francese e chiamerà translate_transcription.
Qualche chiamata a strumenti, nessun codice, e una trascrizione nella tua libreria. Ciascuna di quelle chiamate è passata dagli stessi controlli di una richiesta REST, e sono quei controlli a fissare il conto e i limiti.
Il server MCP è l'API attraverso un'altra porta: stessi limiti, stesso conto.
POST /v1/transcriptions: si applicano la frequenza di creazione del piano (10, 100 o un numero personalizzato di trascrizioni avviate all'ora), il suo limite di trascrizioni contemporanee (1, 5 o illimitate) e il suo credito. Il credito di benvenuto da $5 copre una prima sessione, senza carta di credito.isError: true con il codice di errore dell'API REST, una frase su cui il modello può agire, e i campi retryable e retryAfterS. insufficient_credit porta un topUpUrl; rate_limited indica quanto aspettare. Conta più di quanto sembri: un modello legge il testo dell'errore, quindi una frase su cui può agire trasforma un vicolo cieco nella chiamata successiva./v1/mcp stessa: un agente in loop riceve un HTTP 429 con Retry-After prima che parta qualunque strumento.idempotencyKey: riusare la chiave di una chiamata completata ripropone il primo risultato, e riusarla con argomenti diversi viene rifiutato, così un agente che riprova da solo non avvia mai un secondo lavoro. Ogni strumento che spende denaro dovrebbe offrirla; gli agenti riprovano molto più liberamente delle persone.Questi sono i confini di ciò che fa il server. Due cose ne restano fuori di proposito.
Il server non ha uno strumento che trasformi un testo qualsiasi in voce, e non è previsto. VocalStack legge trascrizioni e le loro traduzioni, con una voce predefinita o clonata, e nient'altro. list_voices dice a un agente quali voci possono leggere una trascrizione in una lingua, non come far dire a una voce qualcosa di nuovo.
Gli strumenti di trascrizione rifiutano anche le dirette. Una diretta su YouTube o una radio in streaming richiede una trascrizione in diretta, avviata dalla Dashboard o tramite POST /v1/transcriptions/live nel riferimento dell'API. Un video di YouTube già concluso è un file, e transcribe_url lo accetta.
Se preferisci scrivere tu il codice, trascrivere un file con l'API illustra gli stessi passaggi come chiamate HTTP, e il tutorial sul tempo reale copre il WebSocket.