Model Context Protocol (MCP)
Connect AI assistants directly to TTS generation via the MCP standard.
Overview
MCP (Model Context Protocol) lets AI agents call TTS tools without custom HTTP integration. The remote HTTP server exposes 57 tools covering generation, voices, jobs, shares, library management, storage, account pricing, and personalization.
Two connection methods are available: a local stdio CLI for desktop AI apps, and a remote HTTP endpoint for server-side agents. The stdio proxy exposes 56 REST-backed tools, while remote MCP additionally exposes apply_mode as a composite convenience tool.
Connection Methods
Local stdio (recommended for desktop)
Add this to your MCP client config (Claude Desktop, Cursor, etc.):
{
"mcpServers": {
"aittsm": {
"command": "npx",
"args": ["@theproductivepixel/aittsm"],
"env": { "AITTSM_API_KEY": "tts_YOUR_KEY" }
}
}
}Set AITTSM_API_KEY to your API key. Optionally set AITTSM_BASE_URL to override the default API base URL.
Remote HTTP (server-to-server)
Send JSON-RPC messages directly to POST /api/v1/mcp. The endpoint is stateless and accepts either an API key or an OAuth 2.1 access token.
# Remote HTTP request. Use an API key (tts_...) or OAuth token (oauth_at_...).
curl -X POST https://aitts.theproductivepixel.com/api/v1/mcp \
-H "Authorization: Bearer API_KEY_OR_OAUTH_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Use an API key or OAuth token as the bearer credential in the curl example. All URLs below derive from BASE_URL. The fallback value is the production origin.
For OAuth 2.1, discover the protected resource at https://aitts.theproductivepixel.com/.well-known/oauth-protected-resource and the authorization server at https://aitts.theproductivepixel.com/.well-known/oauth-authorization-server.
Authentication failures include a WWW-Authenticate header. The same challenge is mirrored in MCP response metadata as _meta["mcp/www_authenticate"] so Apps clients can link the user to OAuth.
A valid OAuth token without the required scope returns 403 insufficient_scope. A missing or invalid bearer credential, including a wrong-audience OAuth token, returns 401.
OAuth client lifecycle
The authorization server is provider-neutral. Clients consume its discovered metadata and their resolved client record instead of inferring endpoints, redirect URIs, or policy from a provider name.
- Discover the protected resource and authorization server metadata from the two
.well-knownendpoints. - Use a predefined client, an HTTPS CIMD
client_idthat points to its metadata document, or RFC 7591 registration atPOST /api/v1/oauth/register. - Start the authorization-code flow at
/api/v1/oauth/authorize. The record controls redirect URIs, scopes, grants, client authentication, PKCE, and resource requirements. - Exchange the code or rotate a refresh token at
POST /api/v1/oauth/token. Revoke an access or refresh token family atPOST /api/v1/oauth/revoke.
S256 is the only supported PKCE method. Public and CIMD clients, plus any client record with pkceRequired, must send it. Required-resource clients send the exact canonical resource. The legacy predefined GPT client is the transition exception and may omit PKCE and resource while its record allows it.
For CIMD, the client_idis the exact HTTPS metadata-document URL and the document's own client identifier must match it. Dynamic registration is never anonymous: generic or unverified lanes require an initial access token, while provider lanes are available only when their server-side trust configuration is enabled.
The registered authentication method controls both token and revoke requests. A private_key_jwt client publishes JWKS and sends an assertion with the exact endpoint audience, bounded iat/exp, matching iss/sub, and a one-time jti.
Available Tools (57)
Each tool requires a specific permission. API-key callers need the API-key permission, while OAuth callers need the matching OAuth scope. The names are the same. Tools are grouped by function:
Voice & Generation
API-key permission / OAuth scope: tts:generate, tts:status, voices:list
search_voicesget_voice_detailsget_voice_sample_urlgenerate_speechget_job_statusget_audio_linkJobs
API-key permission / OAuth scope: jobs:read, jobs:write
list_jobsget_job_textupdate_job_metadatadelete_job_audioShares
API-key permission / OAuth scope: shares:read, shares:write
create_sharecreate_voice_sharelist_sharesget_shareupdate_sharerevoke_sharebulk_revoke_sharestoggle_share_permanentupdate_track_orderAccess Codes & QR
API-key permission / OAuth scope: shares:read, shares:write
create_access_codeslist_access_codesupdate_access_codedelete_access_codeexport_access_codesget_qr_codeLibrary
API-key permission / OAuth scope: library:read, library:write
list_collectionsmanage_collectionlist_tagscreate_bookmarklist_bookmarksdelete_bookmarkmanage_bookmark_collectionStorage & Usage
API-key permission / OAuth scope: storage:read, storage:write, usage:read, pricing:estimate
get_storagelist_storage_itemsbulk_delete_storageget_usageestimate_costget_pricingPersonalization: Preferences & Modes
API-key permission / OAuth scope: preferences:read, preferences:write, modes:read, modes:write
get_preferencesset_preferenceslist_modesget_modepreview_modeapply_modecreate_modeupdate_modedelete_modePersonalization: Projects
API-key permission / OAuth scope: projects:read, projects:write
list_projectsget_projectcreate_projectupdate_projectdelete_projectlist_project_itemsget_project_itemadd_project_itemupdate_project_itemremove_project_itemtools/list returns every descriptor with a title and explicit readOnlyHint, destructiveHint, and openWorldHint annotations. Each descriptor also includes securitySchemes at the top level and the identical _meta.securitySchemes mirror.
Patterns
Pagination
Paginated tools accept page_size (1–100, default 20) and page_token (opaque cursor). Personalization lists preserve the complete legacy envelope up to 100 raw records when both are omitted, omit next_page_token, and return PAGINATION_REQUIRED above that cap.
Delivery Mode
generate_speech supports two modes:
- async (default): Returns job_id. Poll
get_job_statusuntil completed, thenget_audio_link. - stream: Returns one-time
stream_urlfor real-time audio. URL expires and is single-use. Durable artifact saved after stream completes.
Stream supports all 7 formats (wav, mp3, ogg_opus, pcm, mulaw, alaw, ogg_vorbis). Async supports wav, mp3, ogg_opus only.
Idempotency
Pass idempotency_key to generate_speech for safe retries. Same key + same body returns cached result. Same key + different body returns IDEMPOTENCY_KEY_REUSE (409). Key still processing returns REQUEST_IN_PROGRESS (409).
Rate Limiting
Tools use two buckets: read (higher limits, queries) and generate (lower limits, mutations). Limits are per-account. HTTP 429 with Retry-After header when exceeded.
Permissions
| API-key permission / OAuth scope | Tools |
|---|---|
| voices:list | search_voices, get_voice_details |
| tts:generate | generate_speech, get_voice_sample_url |
| tts:status | get_job_status, get_audio_link |
| jobs:read | list_jobs, get_job_text |
| jobs:write | delete_job_audio, update_job_metadata |
| shares:read | list_shares, get_share, list_access_codes, export_access_codes |
| shares:write | create_share, create_voice_share, update_share, revoke_share, bulk_revoke_shares, toggle_share_permanent, update_track_order, create_access_codes, update_access_code, delete_access_code, get_qr_code |
| library:read | list_collections, list_tags, list_bookmarks |
| library:write | manage_collection, create_bookmark, delete_bookmark, manage_bookmark_collection |
| storage:read | get_storage, list_storage_items |
| storage:write | bulk_delete_storage |
| pricing:estimate | estimate_cost, get_pricing |
| usage:read | get_usage |
| preferences:read | get_preferences, preview_mode |
| preferences:write | set_preferences, apply_mode |
| modes:read | list_modes, get_mode |
| modes:write | create_mode, update_mode, delete_mode |
| projects:read | list_projects, get_project, list_project_items, get_project_item |
| projects:write | create_project, update_project, delete_project, add_project_item, update_project_item, remove_project_item |
Examples
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "my-app", "version": "1.0.0" }
}
}{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "generate_speech",
"arguments": {
"text": "Hello from MCP!",
"voice_id": "google:en-US-Chirp3HD-Charon"
}
}
}{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "generate_speech",
"arguments": {
"text": "Stream this audio in real time.",
"voice_id": "google:en-US-Chirp3HD-Charon",
"delivery_mode": "stream",
"idempotency_key": "my-unique-key-123"
}
}
}{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "search_voices",
"arguments": { "q": "Leda", "language": "en-US", "gender": "female" }
}
}{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "get_audio_link",
"arguments": { "job_id": "YOUR_JOB_ID" }
}
}
// Result carries a fresh audio_url (signed_url is a deprecated alias) plus
// expires_at, expires_in, content_type, audio_bytes, and audio_endpoint.# Discover the protected resource and authorization server.
curl https://aitts.theproductivepixel.com/.well-known/oauth-protected-resource
curl https://aitts.theproductivepixel.com/.well-known/oauth-authorization-server# A CIMD client_id is the exact HTTPS metadata-document URL.
client_id=https://client.example/.well-known/oauth-client# RFC 7591 Dynamic Client Registration. Use the initial access
# token issued for the registration lane. Anonymous registration is not supported.
curl -X POST https://aitts.theproductivepixel.com/api/v1/oauth/register \
-H "Authorization: Bearer INITIAL_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"redirect_uris":["https://client.example/callback"],"token_endpoint_auth_method":"none","grant_types":["authorization_code","refresh_token"],"response_types":["code"],"scope":"voices:list tts:generate"}'# Authorization-code grant. Apply the client record's registered
# authentication method and send S256 code_verifier for PKCE-required clients.
# Required-resource clients also send resource=https://aitts.theproductivepixel.com.
curl -X POST https://aitts.theproductivepixel.com/api/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=authorization_code&client_id=CLIENT_ID&code=AUTH_CODE&redirect_uri=https%3A%2F%2Fclient.example%2Fcallback&code_verifier=CODE_VERIFIER&resource=https://aitts.theproductivepixel.com'
# Refresh-token grant.
curl -X POST https://aitts.theproductivepixel.com/api/v1/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=refresh_token&client_id=CLIENT_ID&refresh_token=REFRESH_TOKEN&resource=https://aitts.theproductivepixel.com'# RFC 7009 family revocation. Authenticate exactly as registered:
# public clients send client_id; confidential clients use HTTP Basic or
# client_secret_post; private_key_jwt clients send client_assertion_type and
# client_assertion. A live token revokes its family. Unknown, expired, or
# already-revoked tokens return the same empty 200 response after valid auth.
curl -X POST https://aitts.theproductivepixel.com/api/v1/oauth/revoke \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'client_id=CLIENT_ID&token=ACCESS_OR_REFRESH_TOKEN'get_audio_link returns a fresh audio_url (the legacy signed_url is a deprecated alias) plus expires_at, expires_in, content_type, and audio_bytes.
Tool Reference
Complete parameter tables, response shapes, and error codes for every tool.
Voice & Generation
search_voicesvoices:list · readSearch available TTS voices with optional filters and free-text query.
| Param | Type | Req | Description |
|---|---|---|---|
| q | string | — | Free-text fuzzy search across voice_id, family, name, language, and provider. Partial and typo-tolerant (e.g. "Leda"). Combines with the filters below. |
| language | string | — | Language code (e.g. en-US) |
| provider | string | — | google, polly, or kokoro |
| model_type | enum: premium | ultra | — | Filter by model type |
| gender | enum: male | female | neutral | unknown | — | Filter by gender |
| voice_id | string | — | Exact voice_id filter (case-insensitive) |
{
voices: ApiV1Voice[],
count: number
}get_voice_detailsvoices:list · readGet full capability details for a voice (streaming, formats, limits, speed, prompt).
| Param | Type | Req | Description |
|---|---|---|---|
| voice_id | string | âś“ | Voice ID (provider:language-Family-Name) |
| model | string | — | Model ID for ultra voices with model selection |
{
voice_id: string,
provider: string,
language: string,
family: string,
name: string,
model_type: string,
gender: string,
characteristics: object,
sample_url: string | null,
capabilities: VoiceCapabilities,
available_models?: string[],
default_model?: string,
model?: string,
model_overrides?: Record<string, ModelOverride>
}INVALID_VOICE_IDVOICE_NOT_FOUNDMODEL_SELECTION_NOT_AVAILABLEINVALID_MODELgenerate_speechtts:generate · generateGenerate TTS audio. Returns job_id for async or stream_url for real-time.
| Param | Type | Req | Description |
|---|---|---|---|
| text | string (1–500000) | ✓ | Text to synthesize |
| voice_id | string | single only | Voice ID (provider:lang-Family-Name). Required for single-speaker, forbidden for multi-speaker. |
| delivery_mode | enum: async | stream | — | Default: async. stream returns one-time URL. |
| model_type | enum: premium | ultra | — | Model type |
| model | string | — | Specific model ID |
| speed | number (0.25–4) | — | Speaking rate multiplier |
| format | enum: text | ssml | markup | — | Input format |
| speaker_type | enum: single | multi | — | Speaker type |
| voice_id_speaker_1 | string | multi only | Speaker 1 voice ID. Required for multi-speaker only. |
| voice_id_speaker_2 | string | multi only | Speaker 2 voice ID. Required for multi-speaker only. |
| output_format | enum: wav | mp3 | ogg_opus | pcm | mulaw | alaw | ogg_vorbis | — | Async: wav/mp3/ogg_opus. Stream: all 7. |
| prompt | string | — | Ultra model guidance prompt |
| webhook_url | string (URL) | — | Completion webhook (enterprise) |
| metadata | object | — | Custom metadata |
| sample_rate_hertz | integer | — | Output sample rate |
| output_bitrate_kbps | integer | — | Bitrate (async only) |
| language | string | — | Language override |
| tags | string[] | — | Library tags |
| collection_id | string | — | Collection assignment |
| idempotency_key | string (max 256) | — | Safe retry key (no line breaks) |
Async:
{
job_id,
status,
poll_url,
audio_endpoint,
chars_charged
}
Stream:
{
job_id,
status,
poll_url,
audio_endpoint,
stream_url?,
transport_format,
transport_mime_type,
transport_sample_rate_hertz,
chars_charged,
cache?
}VALIDATION_ERRORINVALID_VOICEPROVIDER_DISABLEDINSUFFICIENT_CREDITSSTORAGE_CAP_EXCEEDEDFORBIDDENINVALID_WEBHOOK_URLENTERPRISE_TIER_REQUIREDSTREAM_NOT_SUPPORTEDSPEED_OUT_OF_RANGESTREAM_BITRATE_NOT_SUPPORTEDSTREAM_FORMAT_MISMATCHMAINTENANCEIDEMPOTENCY_KEY_REUSEREQUEST_IN_PROGRESSget_job_statustts:status · readGet status and metadata of a TTS job.
| Param | Type | Req | Description |
|---|---|---|---|
| job_id | string | âś“ | Job ID |
{
job_id,
status,
created_at?,
progress_message?,
elapsed_seconds?,
provider?,
model_requested?,
model_effective?,
output_format?,
sample_rate_hertz?,
output_bitrate_kbps?,
duration_seconds?,
estimated_duration_seconds?,
prompt_bytes,
chars_charged,
audio_endpoint,
audio_available,
retention_tier?,
retained_until?,
voice_id?,
model_type?,
audio_bytes?,
tags,
collection_id?,
source?,
is_expired,
audio_url?,
audio_url_expires_at?,
error?,
metadata?
}JOB_NOT_FOUNDget_audio_linktts:status · readGet a fresh signed download URL plus audio metadata for completed audio. audio_url is canonical; signed_url is a deprecated alias.
| Param | Type | Req | Description |
|---|---|---|---|
| job_id | string | âś“ | Job ID |
{
job_id,
audio_url,
signed_url, // deprecated alias of audio_url
expires_at,
expires_in, // seconds until expiry
content_type,
audio_bytes,
audio_endpoint
}JOB_NOT_FOUNDget_voice_sample_urltts:generate · generateGet a short-lived sample-audio URL for a voice. Generation-class: when no sample exists yet, this triggers sample synthesis and returns a pending status until the audio is ready.
| Param | Type | Req | Description |
|---|---|---|---|
| voice_id | string | âś“ | Voice ID (provider:language-Family-Name). The language is derived from the voice_id. |
| model | string | — | Optional Gemini ultra sub-model id (honored only for Google ultra voices). |
Ready:
{
voice_id,
sample_url,
voices_url, // /voices deep-link to this voice
expires_at, // <= 7 days (GCS signed-URL cap)
expires_in, // seconds until expiry
content_type
}
Pending:
{
status: 'pending',
voices_url, // /voices deep-link to this voice
retry_after // seconds to wait before retrying
}VALIDATION_ERRORINVALID_VOICEJobs
get_job_textjobs:read · readRetrieve the input text of a completed TTS job.
| Param | Type | Req | Description |
|---|---|---|---|
| job_id | string | âś“ | Job ID |
{
text
}JOB_NOT_FOUNDTEXT_UNAVAILABLElist_jobsjobs:read · readList TTS jobs with pagination and filters.
| Param | Type | Req | Description |
|---|---|---|---|
| page_size | integer (1–100) | — | Default 20 |
| page_token | string | — | Pagination cursor |
| source | enum: api | ui | — | Filter by source |
| status | enum: completed | failed | pending | processing | — | Filter by status |
{
jobs: JobSummary[],
next_page_token
}delete_job_audiojobs:write · generateDelete stored audio for a completed job.
| Param | Type | Req | Description |
|---|---|---|---|
| job_id | string | âś“ | Job ID |
{
shares_revoked,
storage?
}JOB_NOT_FOUNDJOB_IN_PROGRESSupdate_job_metadatajobs:write · generateUpdate tags and/or collection for a job.
| Param | Type | Req | Description |
|---|---|---|---|
| job_id | string | âś“ | Job ID |
| tags | string[] | — | Replace tags |
| collection_id | string | null | — | Set or clear collection |
{
job_id,
tags,
collection_id
}JOB_NOT_FOUNDVALIDATION_ERRORShares
create_shareshares:write · generateCreate a shareable link. Provide job_ids (snapshot) or source_type+source_id (source-based).
| Param | Type | Req | Description |
|---|---|---|---|
| job_ids | string[] | — | Job IDs for snapshot |
| source_type | enum: collection | tag | — | Source type |
| source_id | string | — | Source ID |
| share_mode | enum: snapshot | live | — | Default: snapshot |
| auth_mode | enum: none | password | access_code | — | Auth mode |
| password | string | — | Required if auth_mode=password |
| title | string | — | Share title |
| allow_download | boolean | — | Allow download |
| include_text | boolean | — | Include text excerpts |
| show_voice | boolean | — | Show voice info |
| show_model | boolean | — | Show model info |
| show_provider | boolean | — | Show provider |
| show_language | boolean | — | Show language |
| show_expiry | boolean | — | Show expiry |
| show_track_meta | boolean | — | Show track metadata |
| track_titles | Record<string, string> | — | { jobId: title } |
| track_order | string[] | — | Custom track order |
{
code,
url,
item_count
}AMBIGUOUS_SHARE_INPUTVALIDATION_ERRORINVALID_SHARE_SOURCEPASSWORD_REQUIREDINVALID_AUTH_MODE_PASSWORD_COMBOcreate_voice_shareshares:write · generateCreate a shareable voice collection from caller-supplied voice references. The returned URL is built from the configured app origin, never the caller Origin header. Mirrors POST /api/v1/voice-shares.
| Param | Type | Req | Description |
|---|---|---|---|
| voiceRefs | VoiceRef[] (1–50) | ✓ | Voice references to share. Each ref is { id, language?, ultraModel? }: id is a public voice_id (provider:language-Family-Name) as returned by search_voices / GET /api/v1/voices, language is an optional BCP-47 code the voice supports, ultraModel is an optional Gemini sub-model id. Every id is canonicalized + language-captured internally and validated against the catalog. |
{
code,
url
}VALIDATION_ERRORINVALID_VOICE_REFINVALID_VOICE_LANGUAGEINVALID_ULTRA_MODELlist_sharesshares:read · readList active shares with pagination.
| Param | Type | Req | Description |
|---|---|---|---|
| page_size | integer (1–100) | — | Default 20 |
| page_token | string | — | Pagination cursor |
{
shares: ShareSummary[],
next_page_token
}get_shareshares:read · readGet full share details including job metadata.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
{
code,
title?,
share_mode,
auth_mode,
source_type?,
source_id?,
created_at,
expires_at?,
revoked,
views,
permanent,
allow_download,
include_text,
show_*,
track_titles?,
track_order?,
item_count,
jobs: [{ job_id, voice_id, model_type, created_at, audio_bytes?, output_format?, duration_seconds?, estimated_duration_seconds?, duration_source?, duration_confidence?, audio_sample_rate_hertz?, sample_rate_hertz?, output_bitrate_kbps? }]
}SHARE_NOT_FOUNDupdate_shareshares:write · generateUpdate share settings.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
| title | string | — | New title |
| auth_mode | enum: none | password | access_code | — | Auth mode |
| password | string | — | Password |
| allow_download | boolean | — | Allow download |
| share_mode | enum: snapshot | live | — | Share mode |
| include_text | boolean | — | Include text |
| show_voice | boolean | — | Show voice |
| show_model | boolean | — | Show model |
| show_provider | boolean | — | Show provider |
| show_language | boolean | — | Show language |
| show_expiry | boolean | — | Show expiry |
| show_track_meta | boolean | — | Show track meta |
| track_titles | Record<string, string> | — | Track titles |
| track_order | string[] | null | — | Track order or null |
| source_type | enum: collection | tag | — | Source type |
| source_id | string | — | Source ID |
(Same shape as get_share)SHARE_NOT_FOUNDINVALID_SHARE_SOURCESOURCE_IMMUTABLEPASSWORD_REQUIREDINVALID_AUTH_MODE_PASSWORD_COMBOrevoke_shareshares:write · generateRevoke a share, disabling access.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
{
code,
revoked: true
}SHARE_NOT_FOUNDbulk_revoke_sharesshares:write · generateRevoke multiple shares (max 100).
| Param | Type | Req | Description |
|---|---|---|---|
| codes | string[] (1–100) | ✓ | Share codes |
{
revoked_count,
skipped: string[]
}VALIDATION_ERRORtoggle_share_permanentshares:write · generateToggle permanent status on a share.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
{
code,
permanent,
expires_at?
}SHARE_NOT_FOUNDupdate_track_ordershares:write · generateReorder tracks. Pass null to reset.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
| track_order | string[] | null | âś“ | Ordered job IDs or null |
{
code,
track_order
}SHARE_NOT_FOUNDVALIDATION_ERRORAccess Codes & QR
create_access_codesshares:write · generateCreate access codes for a share. Raw codes returned once only.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Parent share code |
| count | integer (1–100) | — | Default 1 |
| label | string | — | Label or prefix |
| expires_at | string (ISO date) | — | Expiration |
| max_uses | integer (≥1) | — | Max uses per code |
count=1:
{
id,
code,
code_prefix,
label?,
active,
created_at,
expires_at?,
max_uses?
}
count>1:
[
{
id,
code
},
...
]SHARE_NOT_FOUNDVALIDATION_ERRORlist_access_codesshares:read · readList access codes for a share (no raw codes).
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
[
{
id,
code_prefix,
label?,
active,
created_at,
expires_at?,
max_uses?,
uses,
last_used_at?
},
...
]SHARE_NOT_FOUNDupdate_access_codeshares:write · generateUpdate an access code.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
| access_code_id | string | âś“ | Access code ID |
| label | string | — | New label |
| active | boolean | — | Active status |
| expires_at | string | null | — | New expiry or null |
| max_uses | integer | null | — | New max uses or null |
{
id,
code_prefix,
label?,
active,
created_at,
expires_at?,
max_uses?,
uses,
last_used_at?
}SHARE_NOT_FOUNDACCESS_CODE_NOT_FOUNDdelete_access_codeshares:write · generatePermanently delete an access code.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
| access_code_id | string | âś“ | Access code ID |
{
deleted: true
}SHARE_NOT_FOUNDACCESS_CODE_NOT_FOUNDexport_access_codesshares:read · readExport access codes in CSV-compatible format.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
[
{
id,
code_prefix,
label?,
active,
created_at,
expires_at?,
max_uses?,
uses
},
...
]SHARE_NOT_FOUNDget_qr_codeshares:write · generateGenerate a QR code image for a share link.
| Param | Type | Req | Description |
|---|---|---|---|
| code | string | âś“ | Share code |
| format | enum: svg | png | âś“ | Image format |
| preset | enum: clean | branded | — | Visual preset |
| include_access_code | boolean | — | Embed code in URL |
| access_code | string | — | Code to embed |
{
data_uri,
format
}SHARE_NOT_FOUNDLibrary & Storage
list_collectionslibrary:read · readList all audio collections.
{
collections: [
{
id,
name,
created_at,
updated_at
}
]
}manage_collectionlibrary:write · generateCreate, rename, or delete a collection.
| Param | Type | Req | Description |
|---|---|---|---|
| action | enum: create | rename | delete | âś“ | Operation |
| name | string | — | Required for create/rename |
| collection_id | string | — | Required for rename/delete |
create/rename:
{
id,
name,
created_at,
updated_at
}
delete:
{
deleted: true
}VALIDATION_ERRORCOLLECTION_NOT_FOUNDlist_tagslibrary:read · readList all tags with usage counts.
{
tags: [
{
tag,
count
}
]
}create_bookmarklibrary:write · generateBookmark a shared audio link.
| Param | Type | Req | Description |
|---|---|---|---|
| share_code | string | âś“ | Share code |
{
created: true
}VALIDATION_ERRORALREADY_EXISTSlist_bookmarkslibrary:read · readList bookmarks with pagination.
| Param | Type | Req | Description |
|---|---|---|---|
| page_size | integer (1–100) | — | Default 20 |
| page_token | string | — | Pagination cursor |
{
bookmarks: [
{
id,
share_code,
title,
personal_title?,
collection_id?,
created_at,
last_opened_at?,
effective_status
}
],
next_page_token
}delete_bookmarklibrary:write · generateDelete a bookmark.
| Param | Type | Req | Description |
|---|---|---|---|
| bookmark_id | string | âś“ | Bookmark ID (share code) |
{
deleted: true
}BOOKMARK_NOT_FOUNDmanage_bookmark_collectionlibrary:write · generateCreate, rename, or delete a bookmark collection.
| Param | Type | Req | Description |
|---|---|---|---|
| action | enum: create | rename | delete | âś“ | Operation |
| name | string | — | Required for create/rename |
| collection_id | string | — | Required for rename/delete |
create/rename:
{
id,
name,
color?,
created_at,
updated_at
}
delete:
{
deleted: true
}VALIDATION_ERRORBOOKMARK_NOT_FOUNDget_storagestorage:read · readGet storage usage summary.
{
used_bytes,
cap_bytes,
remaining_bytes,
pending_reclaim_bytes,
sync_status
}list_storage_itemsstorage:read · readList stored audio items with pagination.
| Param | Type | Req | Description |
|---|---|---|---|
| page_size | integer (1–100) | — | Default 20 |
| page_token | string | — | Pagination cursor |
{
items: [
{
job_id,
status,
audio_bytes?,
output_format?,
created_at?,
storage_tier?
}
],
next_page_token
}bulk_delete_storagestorage:write · generateBulk delete stored audio (max 100 jobs).
| Param | Type | Req | Description |
|---|---|---|---|
| job_ids | string[] (1–100) | ✓ | Job IDs to delete |
{
deleted_count,
skipped_count,
skipped: [
{
job_id,
reason
}
]
}VALIDATION_ERRORestimate_costpricing:estimate · readEstimate TTS cost without generating.
| Param | Type | Req | Description |
|---|---|---|---|
| text | string (1–500000) | ✓ | Text to estimate |
| model_type | enum: premium | ultra | — | Model type |
| voice_id | string | — | Voice ID |
| output_format | enum: wav | mp3 | ogg_opus | — | Format |
{
estimated_cost,
currency,
chars_charged,
model_type,
provider,
output_format
}VALIDATION_ERRORENTERPRISE_TIER_REQUIREDget_usageusage:read · readGet API usage and credit balance.
{
account_type,
credits_balance?,
balance: {
amount,
currency,
formatted
}
}get_pricingpricing:estimate · readGet the account's current subscription plans and usage rates, in one currency.
resolved:
{
pricing_available: true,
currency,
plans: [
{
id,
title,
price,
highlights
}
],
usage_rates: [
{
provider,
voice_class,
rate_per_1k_bytes
}
],
pricing_url,
guidance
}
setup required:
{
pricing_available: false,
requires_account_pricing_setup: true,
message,
setup_url
}Personalization: Preferences & Modes
get_preferencespreferences:read · readRead the user's saved TTS personalization preferences: the raw sparse overrides, the effective resolved config (system defaults, then the active mode if set, then overrides — later wins), a version for optimistic concurrency, and the active mode id/revision. Read-only.
{
overrides, // sparse user overrides
effective: {
ttsDefaults,
playlist,
interaction
},
version,
updated_at,
active_mode_id, // null when no/stale active mode
mode_revision // null when no active mode
}set_preferencespreferences:write · generatePersist a change to the user's saved preferences via an RFC 7396 merge-patch (set a field to null to reset it to the inherited default). Requires expected_version from a prior get_preferences call (optimistic concurrency).
| Param | Type | Req | Description |
|---|---|---|---|
| updates | object (RFC 7396 merge-patch) | ✓ | Merge-patch over the override doc. Each namespace is itself nullable (null resets the whole namespace): ttsDefaults { voiceId, ttsProvider (google|gemini|polly|kokoro), tier (premium|ultra), language, speed (0.25–4), outputFormat }, playlist { summaryFirst, maxTracks (1–100) }, interaction { responseStyle (concise|detailed|neutral) }, and activeModeId. Any leaf set to null resets it to inherited. |
| expected_version | integer (≥0) | ✓ | Version from a prior get_preferences call (optimistic concurrency; a stale value returns PRECONDITION_FAILED). |
{
overrides,
version,
changed: string[] // top-level keys in the patch
}PRECONDITION_FAILEDlist_modesmodes:read · readList personalization modes. Omitting pagination returns the complete legacy envelope up to 100 raw records and omits next_page_token; larger lists return PAGINATION_REQUIRED. Supplying either field enables stable pages. Read-only.
| Param | Type | Req | Description |
|---|---|---|---|
| page_size | integer (1–100) | — | Explicit page size; defaults to 20 once pagination is selected. |
| page_token | string (≤2048 chars) | — | Opaque owner-bound token from the previous page; it cannot be reused by another account. |
{
modes: [
{
id,
title,
kind,
source, // system_default | user_created | user_fork
version,
enabled,
category?, // system modes only
description? // system modes only
}
],
next_page_token?: string | null
}PAGINATION_REQUIREDget_modemodes:read · readRead full detail for one personalization mode by id (system or user-owned), including its config (the sparse preference overrides it applies) and, for system modes, agent guidance. Read-only.
| Param | Type | Req | Description |
|---|---|---|---|
| mode_id | string | âś“ | Mode id (system or user-owned) |
System mode:
{
source: 'system_default',
id,
title,
kind,
version,
enabled: true,
category,
description,
config, // sparse overrides
agentGuidance
}
User mode:
{
source: 'user_created' | 'user_fork',
id,
title,
kind,
version,
enabled,
sortKey,
config,
baseModeId?,
baseModeVersion?
}NOT_FOUNDpreview_modepreferences:read · readPreview the effective preferences that WOULD apply if this mode were active, WITHOUT changing anything. Resolves system defaults, then this mode's config, then the user's own overrides (later wins). Read-only.
| Param | Type | Req | Description |
|---|---|---|---|
| mode_id | string | âś“ | Mode id to preview (must resolve to a mode visible to this user, else NOT_FOUND) |
{
effective: {
ttsDefaults,
playlist,
interaction
},
mode_id,
mode_revision,
version
}NOT_FOUNDcreate_modemodes:write · generateCreate a new personalization mode owned by the user, either from scratch or forked from an existing system/user mode via baseModeId (deep-merging config over the base, input wins).
| Param | Type | Req | Description |
|---|---|---|---|
| title | string | âś“ | Human-readable display title |
| kind | enum: photo_to_playlist | bedtime_stories | language_learning | news_curation | longread_to_audio | podcast_two_voice | accessibility_reader | custom | — | Mode kind; defaults to custom |
| config | object | — | Sparse preference overrides (ttsDefaults/playlist/interaction; activeModeId not allowed). Defaults to empty. |
| baseModeId | string | — | System/user mode id to fork from, if any |
| enabled | boolean | — | Whether the new mode is enabled |
{
mode: UserModeDoc,
version
}NOT_FOUNDupdate_modemodes:write · generateApply an RFC 7396 merge-patch to a user-owned mode (set a field to null to reset it to inherited). System modes are immutable and always rejected. Requires expected_version from a prior list_modes/get_mode call.
| Param | Type | Req | Description |
|---|---|---|---|
| mode_id | string | âś“ | User-owned mode id |
| updates | object (RFC 7396 merge-patch) | ✓ | Patch over the mode: title, enabled, sortKey (plain replacement, at most 192 UTF-8 bytes); config (nullable merge-patch over the mode config — leaves/namespaces set to null reset to inherited; activeModeId not allowed). |
| expected_version | integer (≥1) | ✓ | Version from a prior list_modes/get_mode call (optimistic concurrency). |
{
mode: UserModeDoc,
version
}IMMUTABLE_MODENOT_FOUNDPRECONDITION_FAILEDdelete_modemodes:write · generatePermanently delete a user-owned personalization mode. System modes are immutable and always rejected. If it was the active mode, the stored selector is gracefully ignored on future reads (no automatic cleanup).
| Param | Type | Req | Description |
|---|---|---|---|
| mode_id | string | âś“ | User-owned mode id |
{
id,
deleted: true
}IMMUTABLE_MODENOT_FOUNDapply_modepreferences:write · generateSet this mode as the user's active personalization mode (persists activeModeId) and return the resulting effective preferences. Unlike preview_mode, this PERSISTS a change to the saved preferences document.
| Param | Type | Req | Description |
|---|---|---|---|
| mode_id | string | âś“ | Mode id to activate (must resolve to a mode visible to this user, else NOT_FOUND) |
{
active_mode_id,
version,
effective: {
ttsDefaults,
playlist,
interaction
},
mode_revision
}NOT_FOUNDPRECONDITION_FAILEDPersonalization: Projects
list_projectsprojects:read · readList projects. Omitting pagination returns the complete legacy envelope up to 100 raw records and omits next_page_token; larger lists return PAGINATION_REQUIRED. Supplying either field enables stable pages. Read-only.
| Param | Type | Req | Description |
|---|---|---|---|
| page_size | integer (1–100) | — | Explicit page size; defaults to 20 once pagination is selected. |
| page_token | string (≤2048 chars) | — | Opaque owner-bound token from the previous page. |
{
projects: [
{
id,
title,
kind,
status,
defaultModeId?,
updatedAt,
createdAt,
version
}
],
next_page_token?: string | null
}PAGINATION_REQUIREDget_projectprojects:read · readGet the full detail of a project: the project doc plus its itemCount and the next pending item (the resume-where-we-left-off pointer — the lowest-ordinal item still pending, or null if none). Read-only.
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Project id |
{
project: ProjectDoc,
itemCount,
nextPendingItem: {
id,
ordinal,
ref,
title?
} | null
}NOT_FOUNDcreate_projectprojects:write · generateCreate a new project owned by the user (an ongoing series/playlist with progress, e.g. a book, course, or podcast). A project never stores audio itself — items added later point at existing jobs or shares.
| Param | Type | Req | Description |
|---|---|---|---|
| title | string (1–200) | ✓ | Human-readable display title |
| kind | enum: book | course | news_feed | podcast | series | custom | — | Project kind; defaults to custom |
| description | string (max 2000) | — | Optional longer-form description |
| status | enum: active | archived | completed | — | Initial status; defaults to active |
| defaultModeId | string | — | Phase-2 personalization mode id applied by default to items |
| metadata | object (≤30 keys) | — | Free-form agent scratch space |
{
project: ProjectDoc,
version
}update_projectprojects:write · generateApply an RFC 7396 merge-patch to a user-owned project (set description/defaultModeId/metadata to null to reset to unset). Requires expected_version from a prior list_projects/get_project call.
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Project id |
| updates | object (RFC 7396 merge-patch) | âś“ | Patch over the project: title/kind/status (plain replacements); description/defaultModeId/metadata accept null to reset to unset (a null metadata VALUE deletes that key). |
| expected_version | integer (≥1) | ✓ | Version from a prior list_projects/get_project call (optimistic concurrency). |
{
project: ProjectDoc,
version
}NOT_FOUNDPRECONDITION_FAILEDdelete_projectprojects:write · generateDelete a project AND all its items (cascade). This is irreversible and cannot be undone.
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Project id |
{
id,
deleted: true
}NOT_FOUNDlist_project_itemsprojects:read · readList project items. Omitting pagination returns the complete legacy envelope up to 100 raw records and omits next_page_token; larger lists return PAGINATION_REQUIRED. Supplying either field enables stable pages. Read-only.
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Project id (a missing project yields an empty list) |
| page_size | integer (1–100) | — | Explicit page size; defaults to 20 once pagination is selected. |
| page_token | string (≤2048 chars) | — | Opaque owner- and project-bound token from the previous page. |
{
items: ProjectItemDoc[], // ordered by ordinal then id
next_page_token?: string | null
}PAGINATION_REQUIREDget_project_itemprojects:read · readGet full detail for a single project item by id. Read-only.
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Parent project id |
| item_id | string | âś“ | Project item id |
{
id,
ownerUid,
projectId,
ordinal,
title?,
ref, // { type: 'job', jobId } | { type: 'share', shareCode }
status, // pending | ready | failed | skipped
metadata?,
version,
createdAt,
updatedAt
}NOT_FOUNDadd_project_itemprojects:write · generateAdd a new item to a project: a pointer to an existing job or share (ref), with an optional explicit ordinal (otherwise auto-appended as max+1), title, initial status, and metadata.
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Parent project id |
| ref | object | âś“ | Pointer to existing content: { type: 'job', jobId } or { type: 'share', shareCode } |
| ordinal | integer (≥0) | — | Sequence position; auto-appended as max+1 when omitted |
| title | string (max 200) | — | Optional display title for this item |
| status | enum: pending | ready | failed | skipped | — | Initial status; defaults to pending |
| metadata | object (≤30 keys) | — | Free-form agent scratch space |
{
item: ProjectItemDoc,
version
}NOT_FOUNDupdate_project_itemprojects:write · generateApply an RFC 7396 merge-patch to a project item (ordinal/ref/status are plain replacements; title/metadata accept null to reset — ref is always a full replace, never merged). Requires expected_version from a prior list_project_items/get_project_item call.
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Parent project id |
| item_id | string | âś“ | Project item id |
| updates | object (RFC 7396 merge-patch) | âś“ | Patch over the item: ordinal/ref/status (plain replacements; ref is a full replace, never merged); title/metadata accept null to reset to unset. |
| expected_version | integer (≥1) | ✓ | Version from a prior list_project_items/get_project_item call (optimistic concurrency). |
{
item: ProjectItemDoc,
version
}NOT_FOUNDPRECONDITION_FAILEDremove_project_itemprojects:write · generateRemove a single item from a project. This is irreversible and cannot be undone (no cascade — items have no sub-data).
| Param | Type | Req | Description |
|---|---|---|---|
| project_id | string | âś“ | Parent project id |
| item_id | string | âś“ | Project item id |
{
id,
deleted: true
}NOT_FOUNDError Codes
Tool errors are returned as text content with isError: true and a JSON payload: { error: "message", code: "ERROR_CODE", status: 400 }. Protocol-level errors (invalid JSON-RPC, unknown method) use standard JSON-RPC 2.0 error format.
Protocol Errors
| Code | Meaning |
|---|---|
| -32700 | Parse error — malformed JSON body |
| -32600 | Invalid request — bad method or params |
| -32001 | Authentication failed: missing or invalid bearer credential |
| -32603 | Internal server error |
Tool-Level Errors
| Code | Status | Description |
|---|---|---|
| VALIDATION_ERROR | 400 | Invalid input parameters |
| INVALID_VOICE | 400 | Voice ID not found or invalid format |
| PROVIDER_DISABLED | 400 | Provider unknown or disabled |
| INSUFFICIENT_CREDITS | 402 | Not enough credits for generation |
| STORAGE_CAP_EXCEEDED | 403 | Storage quota full |
| FORBIDDEN | 403 | Enterprise-only feature or ownership check failed |
| INVALID_WEBHOOK_URL | 400 | Webhook URL validation failed |
| ENTERPRISE_TIER_REQUIRED | 400 | Enterprise pricing without tier |
| STREAM_NOT_SUPPORTED | 400 | Voice/provider doesn't support streaming |
| SPEED_OUT_OF_RANGE | 400 | Speaking rate exceeds stream limit |
| STREAM_BITRATE_NOT_SUPPORTED | 400 | Bitrate selection in stream mode |
| STREAM_FORMAT_MISMATCH | 400 | Format not supported for stream transport |
| MAINTENANCE | 503 | API in maintenance mode |
| IDEMPOTENCY_KEY_REUSE | 409 | Key reused with different request body |
| REQUEST_IN_PROGRESS | 409 | Key still processing |
| JOB_NOT_FOUND | 404 | Job doesn't exist or not owned by caller |
| JOB_IN_PROGRESS | 409 | Cannot delete pending/processing job |
| TEXT_UNAVAILABLE | 410 | Job expired or text not stored |
| SHARE_NOT_FOUND | 404 | Share doesn't exist or not owned |
| AMBIGUOUS_SHARE_INPUT | 400 | Both job_ids and source provided |
| INVALID_SHARE_SOURCE | 400 | Invalid source configuration |
| PASSWORD_REQUIRED | 400 | auth_mode=password without password |
| INVALID_AUTH_MODE_PASSWORD_COMBO | 400 | auth_mode and password combination invalid |
| SOURCE_IMMUTABLE | 400 | Cannot change source on source-backed share |
| ACCESS_CODE_NOT_FOUND | 404 | Access code doesn't exist |
| COLLECTION_NOT_FOUND | 404 | Collection doesn't exist or not owned |
| BOOKMARK_NOT_FOUND | 404 | Bookmark or collection doesn't exist |
| ALREADY_EXISTS | 409 | Bookmark already exists for this share |
| FREE_TIER_LIMIT_EXCEEDED | 403 | Free tier limit reached (>1000 characters per request) |
Next: API Reference · Examples · npm package
© 2026 AI TTS Microservice. All rights reserved.