Skip to main content
Glama

ElevenLabs MCP Server

+page.svelte10.5 kB
<script lang="ts"> import AudioPlayer from '$lib/components/AudioPlayer.svelte'; import DebugInfo from '$lib/components/DebugInfo.svelte'; import LoadingSpinner from '$lib/components/LoadingSpinner.svelte'; import type { AudioGenerationResponse, Voice } from '$lib/elevenlabs-client'; import { onMount } from 'svelte'; let text = ''; let voiceDetailsExpanded = false; let voiceId = 'dQn9HIMKSXWzKBGkbhfP'; // Default to Atom-Pro let loading = false; let result: AudioGenerationResponse | null = null; let voices: Voice[] = []; onMount(async () => { try { const response = await fetch('/api/voices'); voices = await response.json(); } catch (error) { console.error('Error loading voices:', error); } }); async function generateAudio() { if (!text) return; loading = true; result = null; try { const response = await fetch('/api/tts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text, voice_id: voiceId || undefined, type: 'simple' }) }); const data = await response.json() as AudioGenerationResponse; if (!data.success) { throw new Error(data.message); } result = data; } catch (error) { result = { success: false, message: error instanceof Error ? error.message : String(error), debugInfo: [] }; } finally { loading = false; } } const formatLabels = (voice: Voice) =>{ if (!voice.labels) return 'No labels'; return Object.entries(voice.labels).map(([key, value]) => `${key}: ${value}`).join(', '); } function getSelectedVoice() { return voices.find(v => v.voice_id === voiceId); } </script> <main> <h2>Basic Text-to-Speech Conversion</h2> <p class="page-description">Convert single text input to speech using optional voice ID.</p> <form on:submit|preventDefault={generateAudio} class="tts-form"> <div class="form-group"> <label for="text">Text</label> <textarea id="text" bind:value={text} placeholder="Enter text to convert to speech..." rows="4" required ></textarea> </div> <div class="form-group"> <label for="voice">Voice</label> <select id="voice" bind:value={voiceId} required > {#each voices as voice} <option value={voice.voice_id}> {voice.name} ({voice.category}) </option> {/each} </select> </div> <button type="submit" disabled={loading || !text}> {#if loading} <LoadingSpinner size={16} /> Generating... {:else} Generate Audio {/if} </button> </form> {#if getSelectedVoice()} {@const voice = getSelectedVoice()} <div class="voice-details"> <button type="button" class="toggle-details" on:click={() => voiceDetailsExpanded = !voiceDetailsExpanded} aria-expanded={voiceDetailsExpanded} > <h3>Selected Voice Details</h3> <span class="toggle-icon">{voiceDetailsExpanded ? '▼' : '▶'}</span> </button> {#if voiceDetailsExpanded} <div class="voice-info"> <div class="info-group"> <span class="label">Name:</span> <span>{voice ? voice.name : 'Unknown'}</span> </div> {#if voice && voice.description} <div class="info-group"> <span class="label">Description:</span> <span>{voice.description}</span> </div> {/if} {#if voice && voice.labels} <div class="info-group"> <span class="label">Labels:</span> <span>{formatLabels(voice)}</span> </div> {/if} {#if voice && voice.preview_url} <div class="preview-audio"> <span class="label">Preview:</span> <audio controls src={voice.preview_url}> <track kind="captions"> Your browser does not support the audio element. </audio> </div> {/if} </div> {/if} </div> {/if} {#if result} <div class="result"> {#if result.success && result.audioData} <AudioPlayer audioData={result.audioData.data} name={result.audioData.name} /> {:else} <p class="error">{result.message}</p> {/if} <DebugInfo info={result.debugInfo} /> </div> {/if} </main> <style> main { max-width: 800px; margin: 0 auto; padding: var(--spacing-8); } h2 { margin-bottom: var(--spacing-2); color: var(--color-text); font-size: var(--font-size-2xl); text-align: center; } .page-description { text-align: center; color: var(--color-text-light); margin-bottom: var(--spacing-8); } .tts-form { display: flex; flex-direction: column; gap: var(--spacing-6); margin-bottom: var(--spacing-8); background: var(--color-surface); padding: var(--spacing-6); border-radius: var(--border-radius-lg); box-shadow: var(--shadow-base); } .form-group { display: flex; flex-direction: column; gap: var(--spacing-2); } label { font-weight: 500; color: var(--color-text); font-size: var(--font-size-sm); } textarea, input, select { padding: var(--spacing-3); border: 1px solid var(--border-color); border-radius: var(--border-radius-base); font-size: var(--font-size-base); background: var(--color-background); transition: all var(--transition-base); } textarea:focus, input:focus, select:focus { outline: none; border-color: var(--color-primary); box-shadow: var(--shadow-sm); } button { padding: var(--spacing-3) var(--spacing-6); background: var(--color-primary); color: var(--color-surface); border: none; border-radius: var(--border-radius-base); font-size: var(--font-size-base); font-weight: 500; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: var(--spacing-2); transition: all var(--transition-base); box-shadow: var(--shadow-sm); } button:disabled { opacity: 0.7; cursor: not-allowed; transform: none; } button:not(:disabled):not(.toggle-details):hover { background: var(--color-primary-dark); transform: translateY(-1px); box-shadow: var(--shadow-base); } .result { margin-top: var(--spacing-8); background: var(--color-surface); padding: var(--spacing-6); border-radius: var(--border-radius-lg); box-shadow: var(--shadow-base); } .error { color: var(--color-error); padding: var(--spacing-4); background: #fef2f2; border: 1px solid #fee2e2; border-radius: var(--border-radius-base); margin-bottom: var(--spacing-4); } .voice-details { margin-top: var(--spacing-8); background: var(--color-surface); padding: var(--spacing-6); border-radius: var(--border-radius-lg); box-shadow: var(--shadow-base); } .toggle-details { width: 100%; display: flex; justify-content: space-between; align-items: center; background: none; border: none; padding: 0; margin-bottom: var(--spacing-3); cursor: pointer; color: var(--color-text); } .toggle-details h3 { font-size: var(--font-size-lg); margin: 0; color: inherit; } .toggle-icon { font-size: var(--font-size-lg); font-weight: bold; color: var(--color-text-light); } .voice-info { display: flex; flex-direction: column; gap: var(--spacing-3); } .info-group { display: flex; flex-direction: column; gap: var(--spacing-1); } .preview-audio { display: flex; flex-direction: column; gap: var(--spacing-2); margin-top: var(--spacing-2); } .preview-audio audio { width: 100%; margin-top: var(--spacing-1); } .label { font-weight: 500; color: var(--color-text-light); font-size: var(--font-size-sm); } @media (max-width: 640px) { main { padding: var(--spacing-4); } h2 { font-size: var(--font-size-xl); margin-bottom: var(--spacing-2); } .page-description { font-size: var(--font-size-sm); margin-bottom: var(--spacing-6); } .tts-form { padding: var(--spacing-4); gap: var(--spacing-4); } .result { padding: var(--spacing-4); } .voice-details { padding: var(--spacing-4); margin-top: var(--spacing-6); } .voice-details h3 { font-size: var(--font-size-base); margin-bottom: var(--spacing-3); } } </style>

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mamertofabian/elevenlabs-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server