Install
openclaw skills install @bsalaeddin/voicelabs-reading-text-aloudTurns text into spoken audio in the user's VoiceLabs voices and hands back the audio link. Use when the user asks to 'read this paragraph in my Narrator voice', 'narrate these five sections', 'make a voiceover', 'convert this text to speech', 'generate an audio version', 'say this in my voice', or wants text to speech (TTS) for a script. Generation is metered by the user's plan.
openclaw skills install @bsalaeddin/voicelabs-reading-text-aloudGenerate speech from the user's text in one of their voice profiles, wait for it to finish, and give them the audio link.
Every tool in this skill comes from the voicelabs MCP server, the VoiceLabs connector, and is written here as voicelabs:<tool>. The server lists the named tools and the Code Mode pair side by side.
voicelabs:list_voice_profiles.voicelabs:search_tools first, then call each tool inside voicelabs:execute_typescript as external_<tool> (for example external_list_voice_profiles); the program must return its result. Scopes, confirmations and gates are identical on both surfaces.voicelabs:search_tools does not return the tool either, its OAuth scope was not granted; the user reconnects VoiceLabs and approves that scope.voicelabs tools at all: VoiceLabs is not connected yet. Install the VoiceLabs power, which adds the server; it signs in with OAuth on first use.voicelabs:list_voice_profiles: the account's voices with id, name, language, voice_type (preset or cloned) and engine.voicelabs:ensure_voice_profile: make sure a named voice exists, creating it from a built-in preset if it does not. Safe to repeat.voicelabs:speak: start a generation from text with profileId or profileName and optional language. Returns generationId with status generating.voicelabs:get_generation: poll a generation; returns status (generating, completed, failed) and, once completed, audioUrl.voicelabs:search_tools and voicelabs:execute_typescript: start several generations in one round trip for multi-part scripts.profileName. Otherwise call voicelabs:list_voice_profiles and ask which voice, or suggest one that matches the text's language.voicelabs:speak returns NOT_FOUND, offer a built-in preset: call voicelabs:ensure_voice_profile with a name such as "Narrator" and use the id it returns.voicelabs:speak with the text and the voice. Keep the generationId.voicelabs:get_generation with that id. While the status is generating, call it again. When it is completed, give the user the audioUrl. If it is failed, report the error field.Call voicelabs:search_tools first to get the exact declarations, then start every part at once. Do not wait for audio inside the sandbox; return the ids and poll voicelabs:get_generation afterwards.
const parts = ["Section one text", "Section two text"];
const started = await Promise.all(
parts.map((text) =>
external_speak({ text, profileName: "Narrator" }).catch((e) => ({ error: String(e) })),
),
);
return started; // generationId per part, or "<CODE>: <details>"
voicelabs:get_generation again for a fresh link.QUOTA_EXCEEDED: the account's allowance for the period is used. Relay the message as written and stop; do not push a plan or retry.RATE_LIMITED: wait the retryAfterSeconds it names, then continue.SCOPE_MISSING: the connection lacks voice:generate; the user can reconnect and grant it.profileId, not profile_id).