Model Context Protocol — Streamable HTTP (stateless) — https://api.cabina.ai
Cabina exposes all its AI models — text, image, video, and audio — as MCP tools, alongside tools for retrieval (datastores / RAG) and reusable skills. Any MCP host (Claude Desktop, Claude Code, VS Code Copilot, Cursor, etc.) can call them directly.
The tools are grouped into these areas:
| Area | Tools |
|---|---|
| Generation | generate, get_message, list_models |
| Attachments | upload_attachment, list_attachments, delete_attachment |
| Conversations | list_conversations, get_conversation, delete_conversation |
| Datastores (RAG) | search_datastores, list_datastores, create_datastore, upload_document, add_datastore_document, list_datastore_files, delete_datastore_file |
| Skills | list_skills, get_skill, read_skill_file, write_skill_file |
Every tool also returns a human-readable text summary, so hosts that only render text content still show usable results.
Every request must carry a Cabina API token as a Bearer token:
Authorization: Bearer <your-cabina-token>
Tokens are issued by the admin API (POST /admin/user). Missing or invalid tokens return 401 Unauthorized.
| Property | Value |
|---|---|
| Transport | Streamable HTTP (stateless — no session affinity required) |
| Endpoint | https://api.cabina.ai/mcp |
| Protocol | JSON-RPC 2.0 over HTTP POST |
generateGenerate content with any Cabina model. Returns text for LLMs, CDN URLs for image/video/audio, or transcribed text for transcription models.
Async video: video generation can take several minutes, so generate returns immediately with status: "processing" and a message_id instead of blocking. Poll get_message with that message_id until status becomes success (or error). Text, image, and audio models still return their result synchronously.
| Field | Type | Required | Description |
|---|---|---|---|
model | string | yes | Model ID in provider/model-name format. Use list_models to discover options. |
prompt | string | no* | Text prompt. Required for generation, optional for transcription. |
system | string | no | System prompt (text models only). |
conversation_id | string (UUID) | no | Continue an existing conversation. A new one is created if omitted. |
input_attachments | array | no | Input media: [{"data": "<value>", "type": "url"|"b64_data"|"id"}]. Required for image-to-image, image-to-video, and transcription. For local files, prefer upload_attachment and reference the returned attachment_id with "type": "id" instead of inlining base64. |
config | object | no | Model-specific parameters (e.g. size, aspect_ratio, duration, voice). |
| Field | Type | Description |
|---|---|---|
message_id | string (UUID) | ID of the created message. Pass to get_message to poll async results. |
conversation_id | string (UUID) | ID of the conversation. |
status | string | Generation status: created, processing, success, or error. Returns processing for in-flight video. |
text | string | Generated text (LLMs and transcription). |
attachments | array | Generated media: [{"content_type": "image/png", "url": "https://..."}] |
usage | object | {"prompt_tokens", "completion_tokens", "total_tokens"} |
Text GPT-4o chat:
{
"model": "gpt/gpt-4o",
"prompt": "Explain quantum entanglement in one paragraph.",
"system": "You are a concise science writer."
}
Image Flux image generation:
{
"model": "flux/flux-pro",
"prompt": "A photorealistic red fox in a snowy forest at golden hour",
"config": { "aspect_ratio": "16:9" }
}
Video Kling video generation:
{
"model": "kling/kling-2.5-turbo",
"prompt": "Camera slowly pans across a misty mountain range at sunrise",
"input_attachments": [{ "data": "https://example.com/frame.jpg", "type": "url" }]
}
Transcription Audio to text:
{
"model": "whisper/whisper-large-v3",
"input_attachments": [{ "data": "https://example.com/audio.mp3", "type": "url" }]
}
get_messageFetch the current status and result of a message by its ID. Use this to poll for completion after generate returns status: "processing" (e.g. for video).
| Field | Type | Required | Description |
|---|---|---|---|
message_id | string (UUID) | yes | The message_id returned by a previous generate call. |
Same shape as generate: message_id, conversation_id, status, text, attachments, usage. When status is success, attachments holds the finished media URLs.
{ "message_id": "5f3c…" }
list_modelsList all available Cabina models across every modality.
Takes no input. Returns:
{
"models": [
{ "id": "claude/claude-opus-4-8", "type": "text" },
{ "id": "flux/flux-pro", "type": "image" },
{ "id": "klingimage/kling-v2-master","type": "image" },
...
]
}
Modality values: text image video audio
The list below is generated from the same model configuration as list_models, so it always
reflects the models currently enabled. Pass any of these as the model parameter in
provider/model-name format.
text Text
gpt: gpt/gpt-4.1-mini, gpt/gpt-5.6-terra, gpt/gpt-5.1, gpt/gpt-5, gpt/o3-pro, gpt/gpt-4o, gpt/gpt-5.6-luna, gpt/gpt-5.4, gpt/gpt-4.1, gpt/o3-mini, gpt/o1-mini, gpt/o1-preview, gpt/gpt-4o-mini, gpt/gpt-4-turbo, gpt/gpt-5.4-pro, gpt/gpt-5.2-pro-2025-12-11, gpt/gpt-5-pro-2025-10-06, gpt/gpt-5-mini, gpt/gpt-4.1-nano, gpt/o1, gpt/gpt-5.6-sol, gpt/gpt-5.5, gpt/gpt-5.3-chat-latest, gpt/gpt-5.2-2025-12-11, gpt/gpt-5-nano, gpt/o3, gpt/o4-miniclaude: claude/claude-opus-4-8, claude/claude-opus-4-6, claude/claude-opus-4-5, claude/claude-haiku-4-5, claude/claude-sonnet-4-6, claude/claude-fable-5, claude/claude-sonnet-5, claude/claude-opus-4-7, claude/claude-sonnet-4-5, claude/claude-opus-4-1gemini: gemini/gemini-3-flash-preview, gemini/gemini-2.5-flash-lite, gemini/gemini-2.5-flash, gemini/gemini-2.5-pro, gemini/gemini-3.5-flash, gemini/gemini-3.1-flash-lite, gemini/gemini-3.1-pro-preview-customtoolsmistral: mistral/pixtral-12b-2409, mistral/pixtral-large-latest, mistral/ministral-8b-latest, mistral/mistral-small-latest, mistral/mistral-large-latest, mistral/open-mixtral-8x22b, mistral/magistral-medium-latest, mistral/mistral-medium-latestllama: llama/meta-llama/llama-4-scout, llama/meta-llama/llama-4-maverick, llama/meta-llama/llama-3-70b-instructgrok: grok/grok-4.3, grok/grok-4.20-experimental-beta-0304-reasoning, grok/grok-4.20-experimental-beta-0304-non-reasoning, grok/grok-4.20-multi-agent-experimental-beta-0304deepseek: deepseek/deepseek-v4-flash, deepseek/deepseek-v4-prokimi: kimi/moonshotai/kimi-k3, kimi/moonshotai/kimi-k2.7-code, kimi/moonshotai/kimi-k2.6, kimi/moonshotai/kimi-k2.5, kimi/moonshotai/kimi-k2-thinking, kimi/moonshotai/kimi-k2image Image
dalle: dalle/dall-e-3ideogram: ideogram/V_3_TURBO, ideogram/V_3, ideogram/V_2_TURBO, ideogram/V_1_TURBO, ideogram/V_4_QUALITY, ideogram/V_4, ideogram/V_2A, ideogram/V_2A_TURBO, ideogram/V_2, ideogram/V_1, ideogram/V_4_TURBO, ideogram/V_3_QUALITYflux: flux/flux-tool-canny, flux/flux-dev, flux/flux-2, flux/flux-2-flex, flux/flux-kontext-max, flux/flux-kontext-pro, flux/flux-tool-redux, flux/flux-pro-v1.1-ultra, flux/flux-pro-v1.1, flux/flux-2-max, flux/flux-2-pro, flux/flux-tool-depthleonardo: leonardo/Phoenix 0.9, leonardo/Leonardo Lightning XL, leonardo/Leonardo Kino XL, leonardo/Leonardo Vision XL, leonardo/AlbedoBase XL, leonardo/Lucid Origin, leonardo/Lucid Realism, leonardo/Leonardo Anime XL, leonardo/SDXL 1.0, leonardo/Leonardo Diffusion XL, leonardo/Absolute Reality v1.6, leonardo/DreamShaper V7, leonardo/RPG V5, leonardo/Phoenix 1.0stablediffusion: stablediffusion/edit-outpaint, stablediffusion/edit-search-and-replace, stablediffusion/edit-search-and-recolor, stablediffusion/control-style, stablediffusion/sd3.5-medium, stablediffusion/core, stablediffusion/edit-erase, stablediffusion/edit-remove-background, stablediffusion/control-sketch, stablediffusion/control-structure, stablediffusion/sd3.5-large, stablediffusion/sd3.5-large-turbo, stablediffusion/ultrananobanana: nanobanana/nano-banana-flash-lite, nanobanana/nano-banana-v1, nanobanana/nano-banana-2, nanobanana/nano-banana-pro-tool-inpaint, nanobanana/nano-banana-progptimage: gptimage/gpt-image-2, gptimage/gpt-image-1.5, gptimage/gpt-image-1-mini, gptimage/gpt-image-1klingimage: klingimage/kling-3, klingimage/kling-o3, klingimage/kling-o1video Video
kling: kling/kling-2.1-pro, kling/kling-o3-std, kling/kling-3-pro, kling/kling-2.6-pro-motion, kling/kling-2.1-std, kling/kling-2.1-master, kling/kling-2-master, kling/kling-o3-pro, kling/kling-3-std, kling/kling-o1-std, kling/kling-2.6-motion, kling/kling-2.6-pro, kling/kling-o1, kling/kling-2.5-turboseedance: seedance/seedance-2-fast, seedance/seedance-1.5-pro, seedance/seedance-1-pro, seedance/seedance-1-lite, seedance/seedance-2-mini, seedance/seedance-2-mini-reference, seedance/seedance-2pixverse: pixverse/pixverse-v5, pixverse/pixverse-lipsync, pixverse/pixverse-c1-transition, pixverse/pixverse-v5-transition, pixverse/pixverse-v5-fast, pixverse/pixverse-v5-extend, pixverse/pixverse-sound-effects, pixverse/pixverse-c1, pixverse/pixverse-v6-transition, pixverse/pixverse-v6-extend, pixverse/pixverse-v5.5, pixverse/pixverse-v5.5-effects, pixverse/pixverse-v6, pixverse/pixverse-v5-extend-fast, pixverse/pixverse-v5.5-transition, pixverse/pixverse-v5.6sora: sora/sora-2-pro, sora/sora-2
Input attachments let you send a file (image, audio, or video) to a model — for image-to-image,
image-to-video, or transcription. Upload the file once with upload_attachment,
then reference the returned attachment_id from one or more generate calls via
input_attachments: [{"type": "id", "data": "<attachment_id>"}] — this avoids
re-sending large base64 payloads on every request. Attachments are capped at 100 MB.
upload_attachmentUpload an input media file once and get back an attachment_id to reference in generate.
| Parameter | Type | Required | Description |
|---|---|---|---|
data | string | yes | Base64-encoded file bytes. Accepts a raw base64 string or a data URI (data:<content-type>;base64,<payload>). |
filename | string | no | Original filename; helps detect the content type and sets the CDN extension. |
content_type | string | no | Content-type hint (e.g. image/png) used when data is raw base64 without a data URI. |
size_bytes | integer | no | Declared file size in bytes. When provided it is validated against the 100 MB limit up front, so oversized uploads are rejected before the data is decoded. |
Returns: { "attachment_id": "att_123", "url": "https://...", "content_type": "image/png" }
Then reference it in generate:
{
"model": "flux/flux-pro",
"prompt": "Restyle this photo as an oil painting",
"input_attachments": [{ "type": "id", "data": "att_123" }]
}
list_attachmentsList the input attachments you have uploaded. Takes no input. Returns:
{
"attachments": [
{ "attachment_id": "att_123", "filename": "photo.png", "content_type": "image/png", "url": "https://...", "created_at": "2026-06-26T..." },
...
]
}
delete_attachmentDelete a previously uploaded input attachment by its attachment_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
attachment_id | string | yes | The attachment_id returned by upload_attachment. |
Returns: { "deleted": true }
Every generate call belongs to a conversation. Omit conversation_id on the first
call to start a new one (the returned conversation_id can be passed back to continue it).
These tools let you browse history, read a whole thread, and delete threads you no longer need.
list_conversationsList your conversations, most recently updated first. Takes no input. Returns:
{
"conversations": [
{ "conversation_id": "5f3c…", "title": "Quantum physics chat", "last_message_text": "Thanks!", "updated_at": "2026-06-26T..." },
...
]
}
get_conversationFetch a whole conversation by its ID: its title and every message in order, including roles, models, statuses, and any attachment URLs.
| Parameter | Type | Required | Description |
|---|---|---|---|
conversation_id | string (UUID) | yes | The conversation ID returned by generate or list_conversations. |
Returns:
{
"conversation_id": "5f3c…",
"title": "Quantum physics chat",
"created_at": "2026-06-26T...",
"messages": [
{ "message_id": "a1…", "role": "user", "text": "Explain entanglement", "status": "user", "created_at": "..." },
{ "message_id": "b2…", "role": "assistant", "text": "Entanglement is...", "model": "gpt-4o", "status": "success",
"attachments": [], "created_at": "..." }
]
}
delete_conversationDelete a conversation and all of its messages by conversation_id. This cannot be undone.
| Parameter | Type | Required | Description |
|---|---|---|---|
conversation_id | string (UUID) | yes | The conversation ID to delete. |
Returns: { "deleted": true }
A datastore is a searchable knowledge base. The typical workflow is:
create_datastore → upload_document (returns a file_id, indexed in the background)
→ add_datastore_document (attach the file) → search_datastores.
Use list_datastore_files to check indexing status before searching.
search_datastoresPerform semantic search across one or more Cabina datastores. Returns matching document excerpts with confidence scores.
| Parameter | Type | Required | Description |
|---|---|---|---|
datastore_ids | string[] | yes | IDs of the datastores to search across. |
query | string | yes | The search query text. |
top_k | integer | no | Maximum number of results to return. Uses the datastore default when omitted. |
Returns:
{
"raw": "Formatted context string ready for injection into a prompt",
"results": [
{ "content": "Matched document excerpt...", "confidence_score": 0.92 },
...
]
}
list_datastoresList all datastores you have access to. Takes no input. Returns:
{
"datastores": [
{ "id": "ds_123", "name": "Support docs", "created_at": "2026-01-15T..." },
...
]
}
create_datastoreCreate a new datastore you can add documents to and search.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | Name for the new datastore. |
Returns: { "id": "ds_123", "name": "Support docs" }
upload_documentIngest a text or PDF document into your library from inline text or a URL. Returns a file_id; indexing runs in the background. Then call add_datastore_document to attach it to a datastore.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | yes | Document name. |
text | string | no* | Inline text content. Provide either text or url, not both. |
url | string | no* | URL of a text or PDF document to fetch and ingest. |
Returns: { "file_id": "file_789", "url": "https://..." }
add_datastore_documentAttach a previously uploaded document (by file_id) to a datastore so it can be searched.
| Parameter | Type | Required | Description |
|---|---|---|---|
datastore_id | string | yes | ID of the datastore to attach the file to. |
file_id | string | yes | File ID returned by upload_document. |
Returns: { "success": true }
list_datastore_filesList the documents in a datastore, including each document's processing status.
| Parameter | Type | Required | Description |
|---|---|---|---|
datastore_id | string | yes | ID of the datastore whose files to list. |
Returns:
{
"files": [
{ "file_id": "file_789", "name": "handbook.pdf", "status": "indexed" },
...
]
}
delete_datastore_fileRemove a document from a datastore by file_id.
| Parameter | Type | Required | Description |
|---|---|---|---|
datastore_id | string | yes | ID of the datastore to remove the file from. |
file_id | string | yes | ID of the file to remove. |
Returns: { "success": true }
Skills are reusable, file-based bundles (a SKILL.md plus optional scripts/assets) that you can
discover, download, and run locally — or author yourself. Global skills are shared and read-only;
user skills are private and writable.
These tools are only present when the server is configured with a skills backend.
list_skillsList all skills available to you (global skills plus your own). Call this first to discover skills. Takes no input. Returns:
{
"global_skills": [ { "name": "pdf-fill", "description": "Fill PDF forms" } ],
"user_skills": [ { "name": "my-report", "description": "..." } ]
}
get_skillFetch a full skill: its file tree and the base64-encoded contents of every file, so you can save it into your local skills/ directory and run it yourself.
| Parameter | Type | Required | Description |
|---|---|---|---|
skill_name | string | yes | Name of the skill to fetch. |
Returns the skill name, description, and a files array of { path, content_b64, content_type, size, is_dir }.
read_skill_fileRead a single file from a skill (base64-encoded). Use when you don't need the whole skill bundle.
| Parameter | Type | Required | Description |
|---|---|---|---|
skill_name | string | yes | Name of the skill. |
path | string | yes | File path relative to the skill root, e.g. SKILL.md. |
Returns: { "content_b64": "...", "content_type": "text/markdown" }
write_skill_fileWrite a single file into one of your skills (creates the skill if it doesn't exist). Build a skill file-by-file, or patch individual files. Global skills are read-only.
| Parameter | Type | Required | Description |
|---|---|---|---|
skill_name | string | yes | Name of your skill to write to (user-private skills only). |
path | string | yes | File path relative to the skill root, e.g. SKILL.md or run.py. |
content | string | yes | Plain text file content. Pass raw text — do not base64-encode it. |
Returns: { "path": "SKILL.md" }
npx @modelcontextprotocol/inspector
https://api.cabina.ai/mcpAuthorization: Bearer <your-token>Add to claude_desktop_config.json:
{
"mcpServers": {
"cabina": {
"url": "https://api.cabina.ai/mcp",
"headers": {
"Authorization": "Bearer <your-cabina-token>"
}
}
}
}
Register via the CLI (one-time):
claude mcp add --transport http cabina https://api.cabina.ai/mcp \ --header "Authorization: Bearer <your-cabina-token>"
Or add to .claude/settings.json (project) / ~/.claude/settings.json (global):
{
"mcpServers": {
"cabina": {
"type": "http",
"url": "https://api.cabina.ai/mcp",
"headers": {
"Authorization": "Bearer <your-cabina-token>"
}
}
}
}
Add to opencode.json (project root) or ~/.config/opencode/opencode.json (global):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"cabina": {
"type": "remote",
"url": "https://api.cabina.ai/mcp",
"headers": {
"Authorization": "Bearer <your-cabina-token>"
}
}
}
}
To avoid storing the token in plaintext use the {env:VAR} syntax: "Bearer {env:CABINA_TOKEN}".
Add to ~/.openclaw/openclaw.json:
{
"mcp": {
"cabina": {
"transport": "streamable-http",
"url": "https://api.cabina.ai/mcp",
"headers": {
"Authorization": "Bearer <your-cabina-token>"
}
}
}
}
Then run openclaw mcp reload to pick up the change without restarting.
Add to ~/.hermes/config.yaml:
mcp_servers:
cabina:
url: "https://api.cabina.ai/mcp"
headers:
Authorization: "Bearer <your-cabina-token>"
Use environment variable interpolation to avoid hardcoding the token: "Bearer ${CABINA_TOKEN}".
| HTTP status | Meaning |
|---|---|
401 | Missing or invalid bearer token. |
500 (in tool result) | Model generation failed. The error message is returned in the tool result's text content with isError: true. |