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 server exposes 35 tools covering generation, voices, jobs, shares, library management, and storage — the same capabilities as the REST API.
Two connection methods are available: a local stdio CLI for desktop AI apps, and a remote HTTP endpoint for server-side agents.
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. Stateless — no session tracking required.
# Remote HTTP — single request
curl -X POST https://aitts.theproductivepixel.com/api/v1/mcp \
-H "Authorization: Bearer tts_YOUR_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Available Tools (35)
Each tool requires specific API key permissions. Tools are grouped by function:
Voice & Generation
Permissions: tts:generate, tts:status, voices:list
search_voicesget_voice_detailsgenerate_speechget_job_statusget_audio_linkJobs
Permissions: jobs:read, jobs:write
list_jobsget_job_textupdate_job_metadatadelete_job_audioShares
Permissions: shares:read, shares:write
create_sharelist_sharesget_shareupdate_sharerevoke_sharebulk_revoke_sharestoggle_share_permanentupdate_track_orderAccess Codes & QR
Permissions: shares:read, shares:write
create_access_codeslist_access_codesupdate_access_codedelete_access_codeexport_access_codesget_qr_codeLibrary
Permissions: library:read, library:write
list_collectionsmanage_collectionlist_tagscreate_bookmarklist_bookmarksdelete_bookmarkmanage_bookmark_collectionStorage & Usage
Permissions: storage:read, storage:write, usage:read, pricing:estimate
get_storagelist_storage_itemsbulk_delete_storageget_usageestimate_costPatterns
Pagination
Paginated tools accept page_size (1–100, default 20) and page_token (opaque cursor). Results include next_page_token (null when done).
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
| Permission | Tools |
|---|---|
| voices:list | search_voices, get_voice_details |
| tts:generate | generate_speech |
| 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, 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 |
| usage:read | get_usage |
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": { "language": "en-US", "gender": "female" }
}
}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.
| Param | Type | Req | Description |
|---|---|---|---|
| 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?,
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 signed download URL for completed audio.
| Param | Type | Req | Description |
|---|---|---|---|
| job_id | string | ✓ | Job ID |
{
audio_endpoint,
signed_url?,
expires_at?,
job_id
}JOB_NOT_FOUNDJobs
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_COMBOlist_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,
chars_charged,
model_type,
provider,
output_format
}VALIDATION_ERRORENTERPRISE_TIER_REQUIREDget_usageusage:read · readGet API usage and credit balance.
{
account_type,
credits_balance
}Error 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 API key |
| -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.