> ## Documentation Index
> Fetch the complete documentation index at: https://developers.x-pilot.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Integration

> Connect X-Pilot to a compatible AI assistant with OAuth or an API key and explore the MCP tool schemas.

export const XpilotArticleMetadata = () => <script type="application/ld+json" data-xpilot-article-metadata="" dangerouslySetInnerHTML={{
  __html: "{\"@context\":\"https://schema.org\",\"@id\":\"https://developers.x-pilot.ai/mcp-integration#article\",\"author\":{\"@type\":\"Organization\",\"@id\":\"https://www.x-pilot.ai/#organization\",\"name\":\"X-Pilot\",\"url\":\"https://www.x-pilot.ai/\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https://developers.x-pilot.ai/images/integration-mcp.png\",\"width\":1200,\"height\":675,\"caption\":\"Connect an AI client to the business MCP server. Available tools depend on the scopes you grant.\"}}"
}} />;

<XpilotArticleMetadata />

export const XpilotBreadcrumb = () => <nav aria-label="Breadcrumb" className="xpilot-breadcrumb"><ol><li><a href="/">X-Pilot Developers</a></li><li aria-current="page"><span aria-hidden="true">/</span><span>MCP Integration</span></li></ol></nav>;

<XpilotBreadcrumb />

<p className="xpilot-doc-author">Documentation by <a href="https://www.x-pilot.ai/">X-Pilot</a></p>

## Connect your assistant

### 1. Open connector settings

In your AI tool, open the MCP or Connectors settings and choose to add a custom (remote) server.

### 2. Paste the MCP URL

Name it X Pilot and paste the URL below. Transport: Streamable HTTP.

### 3. Sign in with OAuth

Click Connect, sign in to X Pilot and approve the requested access. No API key is needed, and you can disconnect at any time from the developer console.

### 4. Start creating

Ask for a video in plain language. Generation uses your existing credits, exactly like the web app.

```text theme={null}
https://mcp.x-pilot.ai/mcp
```

Use the actual deployment URL shown in your [Developer Console](https://app.x-pilot.ai/developers/console).

## Client setup

Pick your AI tool for the exact place to add X Pilot and how to sign in.

<Tabs>
  <Tab title="Claude">
    ```bash theme={null}
    Name  X Pilot
    URL   https://mcp.x-pilot.ai/mcp
    ```

    Works in Claude on the web and desktop. Click Connect, then sign in and approve on the X Pilot page. On Team and Enterprise plans an owner adds the connector first.
  </Tab>

  <Tab title="Cursor">
    ```bash theme={null}
    {
      "mcpServers": {
        "x-pilot": {
          "url": "https://mcp.x-pilot.ai/mcp"
        }
      }
    }
    ```

    Click Add to Cursor or save this to mcp.json. Then open Settings → MCP and click Connect next to x-pilot to sign in.
  </Tab>

  <Tab title="Claude Code">
    ```bash theme={null}
    claude mcp add --transport http x-pilot https://mcp.x-pilot.ai/mcp
    ```

    Run this once, then type /mcp inside Claude Code, choose x-pilot and Authenticate.
  </Tab>

  <Tab title="Codex">
    ```bash theme={null}
    codex mcp add x-pilot --url https://mcp.x-pilot.ai/mcp
    codex mcp login x-pilot
    ```

    The second command opens the browser to sign in. Restart Codex if the tools do not appear.
  </Tab>

  <Tab title="VS Code">
    ```bash theme={null}
    code --add-mcp '{"name":"x-pilot","type":"http","url":"https://mcp.x-pilot.ai/mcp"}'
    ```

    Or add the same server to .vscode/mcp.json. VS Code asks you to sign in the first time the agent uses it.
  </Tab>

  <Tab title="Gemini CLI">
    ```bash theme={null}
    gemini mcp add --transport http x-pilot https://mcp.x-pilot.ai/mcp
    ```

    Then run /mcp auth x-pilot inside Gemini CLI to sign in.
  </Tab>
</Tabs>

## API key fallback

Use an API Key when a client does not support OAuth, or for CI and servers. Create the key in the developer console and keep it in the XPILOT\_API\_KEY environment variable — never paste it into a config file or repository. The key's scopes and monthly budget still apply.

### Cursor · \~/.cursor/mcp.json

```json theme={null}
{
  "mcpServers": {
    "x-pilot": {
      "url": "https://mcp.x-pilot.ai/mcp",
      "headers": {
        "Authorization": "Bearer ${env:XPILOT_API_KEY}"
      }
    }
  }
}
```

### Codex · Terminal

```json theme={null}
codex mcp add x-pilot --url https://mcp.x-pilot.ai/mcp --bearer-token-env-var XPILOT_API_KEY
```

## Tool reference

These 31 definitions come from the same tool registry used by the MCP server. Available tools depend on your granted scopes. Upload file bytes through [REST assets](/api-reference/upload-assets).

<Accordion title="check_credits">
  Read the shared wallet balance in credits. Call before paid operations.

  Scope: `account:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="check_capacity">
  Read available concurrency slots and the remaining monthly budget of this credential. Call before create\_conversation or send\_message.

  Scope: `account:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="list_conversations">
  List owned video conversations, newest first. status is one of queued, running, stopping, paused, completed, failed, stopped; next\_action tells what a paused conversation needs. Times are Unix seconds.

  Scope: `video_agents:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "limit": {
        "type": "integer",
        "minimum": 1,
        "maximum": 100,
        "description": "Items per page. Default 20."
      },
      "offset": {
        "type": "integer",
        "minimum": 0,
        "description": "Items to skip. Default 0."
      }
    },
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="get_conversation">
  Read one conversation summary with status, next\_action, cost\_credits and preview\_video\_url. Use list\_messages for per-message detail.

  Scope: `video_agents:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      }
    },
    "required": [
      "conversation_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="create_conversation">
  Create a video conversation and start the first paid message immediately. Check credits and capacity first. Outline modes: by default (require\_outline\_confirmation omitted or false) the video is generated straight through without pausing. With require\_outline\_confirmation=true the message pauses with status=paused and next\_action=confirm\_outline; show the returned outline to the user, then call send\_message with outline\_action=confirm (no prompt needed) or outline\_action=revise plus a prompt describing the change. Poll list\_messages every 5 seconds or more; there is no streaming. Billing matches the app: the platform reserve is held, released at the end, then actual usage is charged (clamped to the balance). cost\_credits is the held reserve until the message settles, then the actual charge.

  Scope: `video_agents:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "prompt": {
        "type": "string",
        "minLength": 1,
        "maxLength": 20000,
        "description": "The lesson or video brief: audience, goal, then # title and ## sections with concrete facts, numbers, formulas, dates or ordered steps. Do not name visual components."
      },
      "idempotency_key": {
        "type": "string",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$",
        "description": "Retry key for this conversation. Generate a UUID once and reuse it only when retrying the same input; a different input with the same key is rejected. Never ask the user for it."
      },
      "require_outline_confirmation": {
        "type": "boolean",
        "description": "Default false (generate straight through). Set true only when the user wants to review the outline first."
      },
      "video_config": {
        "type": "object",
        "description": "Video settings applied when the conversation is created. They cannot be changed by send_message.",
        "properties": {
          "target_language": {
            "type": "string",
            "pattern": "^[A-Za-z]{2,3}([-_][A-Za-z0-9]{2,8})*$",
            "description": "Narration and subtitle language as an ISO 639-1 code or BCP-47 tag, for example en, zh or zh-CN. Default en. Decide before creating; changing it later means a new video."
          },
          "video_duration": {
            "type": "string",
            "enum": [
              "short",
              "deep",
              "long"
            ],
            "description": "short: 1–3 minutes (default). deep: 3–10 minutes. long: 10–20 minutes."
          },
          "video_style": {
            "type": "string",
            "enum": [
              "auto",
              "clean_lecture",
              "science_explainer",
              "professional_training",
              "product_showcase",
              "cinematic_story"
            ],
            "description": "auto (default) lets the agent choose. clean_lecture: courses. science_explainer: science processes. professional_training: workplace training. product_showcase: product demos. cinematic_story: history and narrative."
          },
          "voice_id": {
            "type": "string",
            "description": "Narration voice id from list_voices. Default picks a voice for target_language."
          },
          "voice_version": {
            "type": "integer",
            "enum": [
              1,
              2
            ],
            "description": "Version of voice_id: 1 (Voice I, default) or 2. Requires voice_id."
          },
          "locale": {
            "type": "string",
            "description": "Optional native locale of voice_id, for example en-US. Requires voice_id."
          },
          "avatar_id": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Presenter avatar id from list_avatars. The avatar must have selectable=true; otherwise create fails with avatar_not_ready."
          },
          "brand_kit_id": {
            "type": [
              "string",
              "integer"
            ],
            "description": "Brand kit id from list_brand_kits for colors, logo and tone."
          },
          "enable_initial_ai_video": {
            "type": "boolean",
            "description": "Generate an AI opening clip when the plan allows it. Default false."
          }
        },
        "additionalProperties": false
      },
      "files": {
        "type": "array",
        "maxItems": 5,
        "description": "Up to 5 attachments. Each item uses an owned asset_id or a caller-supplied public http(s) URL. Third-party URLs are archived to the caller’s storage before use.",
        "items": {
          "type": "object",
          "description": "An attachment with asset_id or a public url and its own role.",
          "properties": {
            "asset_id": {
              "type": [
                "string",
                "integer"
              ],
              "description": "id returned by POST /v1/assets or list_assets."
            },
            "url": {
              "type": "string",
              "pattern": "^https?://",
              "description": "Caller-supplied public http(s) URL. Third-party content is archived before use."
            },
            "name": {
              "type": "string",
              "maxLength": 255,
              "description": "Optional display name; inferred from the source when omitted."
            },
            "type": {
              "type": "string",
              "enum": [
                "document",
                "image",
                "video"
              ],
              "description": "Optional; inferred from the file when omitted."
            },
            "files_role": {
              "type": "string",
              "enum": [
                "primary",
                "reference"
              ],
              "description": "How this file shapes the video: primary or reference. Omit to let the agent decide."
            }
          },
          "additionalProperties": false
        }
      },
      "callback_url": {
        "type": "string",
        "maxLength": 2048,
        "description": "Optional public HTTPS URL notified once when this message reaches completed, paused, failed or stopped. Signed with the workspace callback secret (X-XPilot-Signature). Only set it when the user runs a receiving service."
      },
      "callback_id": {
        "type": "string",
        "pattern": "^[\\w.:-]{1,128}$",
        "description": "Optional reference echoed back as data.callback_id. Requires callback_url."
      }
    },
    "required": [
      "prompt",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="send_message">
  Send a follow-up turn to a conversation that is not running. Paid. Use it to edit the video, answer the assistant (next\_action=reply), or confirm/revise the outline (next\_action=confirm\_outline). Outline modes: by default (require\_outline\_confirmation omitted or false) the video is generated straight through without pausing. With require\_outline\_confirmation=true the message pauses with status=paused and next\_action=confirm\_outline; show the returned outline to the user, then call send\_message with outline\_action=confirm (no prompt needed) or outline\_action=revise plus a prompt describing the change. video\_config cannot be changed here.

  Scope: `video_agents:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      },
      "prompt": {
        "type": "string",
        "maxLength": 20000,
        "description": "The instruction or answer. Required unless outline_action=confirm."
      },
      "outline_action": {
        "type": "string",
        "enum": [
          "confirm",
          "revise"
        ],
        "description": "Only when next_action=confirm_outline. confirm continues with the outline; revise needs a prompt describing the change."
      },
      "idempotency_key": {
        "type": "string",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$",
        "description": "Retry key for this message. Generate a UUID once and reuse it only when retrying the same input; a different input with the same key is rejected. Never ask the user for it."
      },
      "files": {
        "type": "array",
        "maxItems": 5,
        "description": "Up to 5 attachments. Each item uses an owned asset_id or a caller-supplied public http(s) URL. Third-party URLs are archived to the caller’s storage before use.",
        "items": {
          "type": "object",
          "description": "An attachment with asset_id or a public url and its own role.",
          "properties": {
            "asset_id": {
              "type": [
                "string",
                "integer"
              ],
              "description": "id returned by POST /v1/assets or list_assets."
            },
            "url": {
              "type": "string",
              "pattern": "^https?://",
              "description": "Caller-supplied public http(s) URL. Third-party content is archived before use."
            },
            "name": {
              "type": "string",
              "maxLength": 255,
              "description": "Optional display name; inferred from the source when omitted."
            },
            "type": {
              "type": "string",
              "enum": [
                "document",
                "image",
                "video"
              ],
              "description": "Optional; inferred from the file when omitted."
            },
            "files_role": {
              "type": "string",
              "enum": [
                "primary",
                "reference"
              ],
              "description": "How this file shapes the video: primary or reference. Omit to let the agent decide."
            }
          },
          "additionalProperties": false
        }
      },
      "callback_url": {
        "type": "string",
        "maxLength": 2048,
        "description": "Optional public HTTPS URL notified once when this message reaches completed, paused, failed or stopped. Signed with the workspace callback secret (X-XPilot-Signature). Only set it when the user runs a receiving service."
      },
      "callback_id": {
        "type": "string",
        "pattern": "^[\\w.:-]{1,128}$",
        "description": "Optional reference echoed back as data.callback_id. Requires callback_url."
      }
    },
    "required": [
      "conversation_id",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="list_messages">
  List messages of a conversation in order. Poll this for status, next\_action, outline (when an outline exists), cost\_credits and preview\_video\_url. completed, paused, failed and stopped are terminal for that message.

  Scope: `video_agents:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      }
    },
    "required": [
      "conversation_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="stop_conversation">
  Stop the running message. Poll list\_messages until it is stopped; cost already incurred may still be charged.

  Scope: `video_agents:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      }
    },
    "required": [
      "conversation_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="delete_conversation">
  Delete an owned conversation and stop any running message. Only when the user asks.

  Scope: `video_agents:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      }
    },
    "required": [
      "conversation_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="list_renders">
  List MP4 exports of a conversation. status is queued, running, completed, failed or superseded. progress is a 0–100 percentage when known, otherwise null.

  Scope: `video_agents:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      }
    },
    "required": [
      "conversation_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="create_render">
  Export an MP4 after preview\_video\_url is available. A preview is not an export. Poll get\_render and report its 0–100 progress until completed, then return video\_url.

  Scope: `video_agents:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      },
      "message_id": {
        "type": "string",
        "minLength": 1,
        "description": "Message to export. Default: the latest message with a preview."
      },
      "force": {
        "type": "boolean",
        "description": "Re-export even when a completed export exists. Default false."
      },
      "idempotency_key": {
        "type": "string",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$",
        "description": "Retry key for this export. Generate a UUID once and reuse it only when retrying the same input; a different input with the same key is rejected. Never ask the user for it."
      }
    },
    "required": [
      "conversation_id",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="get_render">
  Read one export: status, progress (0–100 or null when unavailable), and video\_url when completed.

  Scope: `video_agents:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      },
      "render_id": {
        "type": "string",
        "minLength": 1,
        "description": "render_id returned by create_render or list_renders."
      }
    },
    "required": [
      "conversation_id",
      "render_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="delete_render">
  Delete an export record. Only when the user asks.

  Scope: `video_agents:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "conversation_id": {
        "type": "string",
        "minLength": 1,
        "description": "conversation_id returned by create_conversation or list_conversations."
      },
      "render_id": {
        "type": "string",
        "minLength": 1,
        "description": "render_id returned by create_render or list_renders."
      }
    },
    "required": [
      "conversation_id",
      "render_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="list_brand_kits">
  List owned brand kits. Pass an id as video\_config.brand\_kit\_id.

  Scope: `brand_kits:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "limit": {
        "type": "integer",
        "minimum": 1,
        "maximum": 100,
        "description": "Items per page. Default 20."
      },
      "offset": {
        "type": "integer",
        "minimum": 0,
        "description": "Items to skip. Default 0."
      }
    },
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="get_brand_kit">
  Read one owned brand kit.

  Scope: `brand_kits:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "brand_kit_id": {
        "type": [
          "string",
          "integer"
        ],
        "description": "Brand kit id returned by list_brand_kits or create_brand_kit."
      }
    },
    "required": [
      "brand_kit_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="create_brand_kit">
  Create a brand kit for logos, theme colors and tone. Only when the user asks.

  Scope: `brand_kits:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "brand_name": {
        "type": "string",
        "minLength": 1,
        "maxLength": 120,
        "description": "Brand name shown in videos."
      },
      "brand_logo": {
        "type": "string",
        "maxLength": 2048,
        "description": "Logo URL, preferably one returned by POST /v1/assets."
      },
      "content_style_description": {
        "type": "string",
        "maxLength": 20000,
        "description": "Tone and style guidance for scripts, for example \"friendly, concise, avoid jargon\"."
      },
      "theme_color": {
        "type": "object",
        "description": "Theme colors. Each value is #rgb, #rrggbb, #rrggbbaa or rgb()/rgba(); null clears a color.",
        "properties": {
          "primary": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "primary color."
          },
          "secondary": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "secondary color."
          },
          "accent": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "accent color."
          },
          "background": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "background color."
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "text color."
          },
          "textSecondary": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "textSecondary color."
          },
          "success": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "success color."
          },
          "warning": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "warning color."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "error color."
          },
          "surface": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "surface color."
          },
          "surfaceLight": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "surfaceLight color."
          },
          "shadow": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "shadow color."
          }
        },
        "additionalProperties": false
      }
    },
    "required": [
      "brand_name"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="update_brand_kit">
  Update fields of an owned brand kit. Omitted fields stay unchanged.

  Scope: `brand_kits:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "brand_kit_id": {
        "type": [
          "string",
          "integer"
        ],
        "description": "Brand kit id returned by list_brand_kits or create_brand_kit."
      },
      "brand_name": {
        "type": "string",
        "minLength": 1,
        "maxLength": 120,
        "description": "Brand name shown in videos."
      },
      "brand_logo": {
        "type": "string",
        "maxLength": 2048,
        "description": "Logo URL, preferably one returned by POST /v1/assets."
      },
      "content_style_description": {
        "type": "string",
        "maxLength": 20000,
        "description": "Tone and style guidance for scripts, for example \"friendly, concise, avoid jargon\"."
      },
      "theme_color": {
        "type": "object",
        "description": "Theme colors. Each value is #rgb, #rrggbb, #rrggbbaa or rgb()/rgba(); null clears a color.",
        "properties": {
          "primary": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "primary color."
          },
          "secondary": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "secondary color."
          },
          "accent": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "accent color."
          },
          "background": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "background color."
          },
          "text": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "text color."
          },
          "textSecondary": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "textSecondary color."
          },
          "success": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "success color."
          },
          "warning": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "warning color."
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "error color."
          },
          "surface": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "surface color."
          },
          "surfaceLight": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "surfaceLight color."
          },
          "shadow": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50,
            "description": "shadow color."
          }
        },
        "additionalProperties": false
      }
    },
    "required": [
      "brand_kit_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="delete_brand_kit">
  Delete an owned brand kit. Only when the user asks.

  Scope: `brand_kits:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "brand_kit_id": {
        "type": [
          "string",
          "integer"
        ],
        "description": "Brand kit id returned by list_brand_kits or create_brand_kit."
      }
    },
    "required": [
      "brand_kit_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="list_assets">
  List files uploaded to this account, newest first. Upload new files with REST multipart POST /v1/assets (field files, up to 5 files); MCP cannot carry file bytes.

  Scope: `assets:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "limit": {
        "type": "integer",
        "minimum": 1,
        "maximum": 100,
        "description": "Items per page. Default 20."
      },
      "offset": {
        "type": "integer",
        "minimum": 0,
        "description": "Items to skip. Default 0."
      }
    },
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="list_avatars">
  List owned presenter avatars and quota. Only avatars with selectable=true can be used as video\_config.avatar\_id.

  Scope: `avatars:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="list_voices">
  Find public catalog voices or your own clones. Choose voice\_id from the returned items; never guess provider identifiers. Use samples on list/detail items for fixed audition clips.

  Scope: `voices:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "voice_version": {
        "type": "integer",
        "description": "Voice I (1, default) or Voice II (2) for the public catalog. Private clone listings include both versions.",
        "enum": [
          1,
          2
        ]
      },
      "type": {
        "type": "string",
        "description": "public shared catalog or private owned clones.",
        "enum": [
          "public",
          "private"
        ]
      },
      "language": {
        "type": "string",
        "description": "Optional language filter aligned with the platform catalog."
      },
      "page": {
        "type": "integer",
        "description": "Page, starting at 1.",
        "minimum": 1
      },
      "limit": {
        "type": "integer",
        "description": "Items per page, up to 100.",
        "minimum": 1,
        "maximum": 100
      }
    },
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="get_voice">
  Read a public or owned voice, including readiness and samples. Private voices from another account are unavailable. Poll this after clone\_voice: a Voice II clone moves from training to trained (selectable=true) or failed.

  Scope: `voices:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "voice_id": {
        "type": "string",
        "description": "Catalog or owned clone voice ID."
      },
      "voice_version": {
        "type": "integer",
        "description": "Voice version of the voice: Voice I (1, default) or Voice II (2).",
        "enum": [
          1,
          2
        ]
      }
    },
    "required": [
      "voice_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="generate_audio">
  Synthesize up to 1,000 characters with a catalog voice or one of your ready clones, in Voice I or Voice II. voice\_version must match the voice. Returns MP3 audio\_url (which may be a data URL). Uses platform TTS pricing; reuse the same idempotency\_key for retries.

  Scope: `audio:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "text": {
        "type": "string",
        "description": "Spoken text.",
        "minLength": 1,
        "maxLength": 1000
      },
      "voice_id": {
        "type": "string",
        "description": "Catalog voice ID or owned ready clone ID."
      },
      "voice_version": {
        "type": "integer",
        "description": "Voice version of voice_id: Voice I (1, default) or Voice II (2). Never substitute one version for the other.",
        "enum": [
          1,
          2
        ]
      },
      "locale": {
        "type": "string",
        "description": "Speech locale. Voice II catalog voices accept their native or secondary locales; Voice II clones accept the locales they support."
      },
      "speed": {
        "type": "number",
        "description": "Playback speed.",
        "minimum": 0.5,
        "maximum": 2
      },
      "idempotency_key": {
        "type": "string",
        "description": "Retry key for this operation. Generate a UUID once and reuse it only when retrying the same input. Never ask the user for it.",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$"
      }
    },
    "required": [
      "text",
      "voice_id",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="get_voice_clone_config">
  Read Voice II clone requirements before clone\_voice with voice\_version 2: availability, the authorized company\_name, supported locales with the consent\_statement the speaker must read aloud, and sample duration/size limits. When voice\_ii\_available is false (for example voice\_ii\_unavailable\_reason clone\_disabled), do not attempt a Voice II clone; offer Voice I instead.

  Scope: `voices:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {},
    "required": [],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="clone_voice">
  Clone a private voice from audio already uploaded to your account. Voice I (default) is an instant clone from one sample. Voice II needs the speaker's consent: call get\_voice\_clone\_config, have the speaker record the consent\_statement for the chosen locale with their name and company\_name filled in, upload it separately, then pass voice\_talent\_name, locale, consent\_audio\_url and consent\_accepted=true. Never fabricate consent or set consent\_accepted without the speaker's explicit agreement. Voice II returns 403 voice\_ii\_unavailable while cloning is not open for the account. Poll get\_voice until state is trained or failed. Reuse idempotency\_key on retry.

  Scope: `audio:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Voice title.",
        "minLength": 1,
        "maxLength": 80
      },
      "audio_url": {
        "type": "string",
        "description": "Owned uploaded training audio URL. Voice II: 5–90 seconds of the same speaker."
      },
      "cover_image_url": {
        "type": "string",
        "description": "Optional owned uploaded cover image URL. Voice I only."
      },
      "voice_version": {
        "type": "integer",
        "description": "Voice I (1, default) or Voice II (2, consent-based personal voice).",
        "enum": [
          1,
          2
        ]
      },
      "voice_talent_name": {
        "type": "string",
        "description": "Voice II only: the speaker's name exactly as spoken in the consent recording.",
        "minLength": 1,
        "maxLength": 128
      },
      "locale": {
        "type": "string",
        "description": "Voice II only: recording locale from get_voice_clone_config, for example en-US or zh-CN.",
        "maxLength": 16
      },
      "consent_audio_url": {
        "type": "string",
        "description": "Voice II only: owned uploaded recording of the speaker reading the consent_statement for locale. Must be a separate file from audio_url, at most 90 seconds."
      },
      "consent_accepted": {
        "type": "boolean",
        "description": "Voice II only: true when the speaker has explicitly agreed that their voice may be cloned. Required for Voice II."
      },
      "idempotency_key": {
        "type": "string",
        "description": "Retry key for this operation. Generate a UUID once and reuse it only when retrying the same input. Never ask the user for it.",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$"
      }
    },
    "required": [
      "name",
      "audio_url",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="delete_voice">
  Delete an owned cloned voice. Public catalog voices cannot be deleted. Reuse idempotency\_key on retry.

  Scope: `audio:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "voice_id": {
        "type": "string",
        "description": "Owned clone voice ID."
      },
      "idempotency_key": {
        "type": "string",
        "description": "Retry key for this operation. Generate a UUID once and reuse it only when retrying the same input. Never ask the user for it.",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$"
      }
    },
    "required": [
      "voice_id",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="get_avatar">
  Read an owned avatar. status is the overall state (draft, generating, ready, failed, deleting, migration\_required); training\_status and preview\_status track training and the preview sample; selectable must be true before video generation can use it; stage names the step in progress (queued, generating\_training\_video, provider\_processing, generating\_preview, deleting\_provider\_asset) or null when idle; error is \{code, message} on failure. Training ready alone does not mean it can be used in a video.

  Scope: `avatars:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "avatar_id": {
        "type": "string",
        "description": "Owned avatar ID."
      }
    },
    "required": [
      "avatar_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="create_avatar">
  Create an avatar from an owned asset\_id or a public http(s) image/video URL. Third-party URLs are downloaded and archived to the caller’s owned storage before training. Requires platform entitlement. Poll get\_avatar for preview\_image\_url and preview\_video\_url, or receive avatar.completed / avatar.failed through callback\_url or a webhook subscription. Reuse idempotency\_key on retry; never retrain automatically.

  Scope: `avatars:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "name": {
        "type": "string",
        "description": "Avatar name.",
        "minLength": 1,
        "maxLength": 80
      },
      "avatar_type": {
        "type": "string",
        "description": "Training source.",
        "enum": [
          "human_video",
          "virtual_character"
        ]
      },
      "asset_id": {
        "type": "string",
        "description": "Owned asset ID returned by POST /v1/assets. human_video uses a video asset; virtual_character uses an image asset."
      },
      "image_url": {
        "type": "string",
        "description": "Public http(s) image URL. The backend archives third-party content to your owned storage before training. Use this or asset_id for virtual_character."
      },
      "video_url": {
        "type": "string",
        "description": "Public http(s) training video URL. The backend archives third-party content to your owned storage before training. Use this or asset_id for human_video."
      },
      "gender": {
        "type": "string",
        "description": "Required for image avatars.",
        "enum": [
          "Male",
          "Female"
        ]
      },
      "callback_url": {
        "type": "string",
        "description": "Optional public HTTPS URL notified once with avatar.completed or avatar.failed. Signed with the workspace callback secret (X-XPilot-Signature). Only set it when you run a receiving service.",
        "maxLength": 2048
      },
      "callback_id": {
        "type": "string",
        "description": "Optional reference echoed back as data.callback_id. Requires callback_url.",
        "maxLength": 128,
        "pattern": "^[\\w.:-]{1,128}$"
      },
      "idempotency_key": {
        "type": "string",
        "description": "Retry key for this operation. Generate a UUID once and reuse it only when retrying the same input. Never ask the user for it.",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$"
      }
    },
    "required": [
      "name",
      "avatar_type",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="delete_avatar">
  Delete an owned avatar. Poll get\_avatar or list\_avatars until it disappears. Reuse idempotency\_key on retry.

  Scope: `avatars:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "avatar_id": {
        "type": "string",
        "description": "Owned avatar ID."
      },
      "idempotency_key": {
        "type": "string",
        "description": "Retry key for this operation. Generate a UUID once and reuse it only when retrying the same input. Never ask the user for it.",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$"
      }
    },
    "required": [
      "avatar_id",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="create_avatar_video">
  Synthesize a talking-presenter video with an owned avatar whose training\_status is ready. Drive it with text + voice\_id (platform TTS, Voice I or Voice II) or with your own speech audio (asset\_id or a public http(s) audio\_url) — exactly one source, up to 5 minutes of speech. Output is a transparent-background WebM (VP9 with alpha) for compositing. Billed per rendered second at the digital-human video rate; the balance must cover the estimate before dispatch. Poll get\_avatar\_video, or receive avatar-video.completed / avatar-video.failed through callback\_url or a webhook subscription. Reuse idempotency\_key on retry; never resubmit automatically.

  Scope: `avatars:write`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "avatar_id": {
        "type": "string",
        "description": "Owned avatar ID with training_status ready."
      },
      "text": {
        "type": "string",
        "description": "Spoken text for platform TTS. Use with voice_id; omit when sending audio.",
        "minLength": 1,
        "maxLength": 5000
      },
      "voice_id": {
        "type": "string",
        "description": "Catalog voice ID or owned ready clone ID from list_voices. Required with text."
      },
      "voice_version": {
        "type": "integer",
        "description": "Voice version of voice_id: Voice I (1, default) or Voice II (2).",
        "enum": [
          1,
          2
        ]
      },
      "locale": {
        "type": "string",
        "description": "Optional native locale for the voice."
      },
      "speed": {
        "type": "number",
        "description": "TTS playback speed.",
        "minimum": 0.5,
        "maximum": 2
      },
      "audio_url": {
        "type": "string",
        "description": "Public http(s) speech audio URL (≤20 MB). The backend archives third-party audio to your owned storage first. Use this or asset_id instead of text."
      },
      "asset_id": {
        "type": "string",
        "description": "Owned audio asset ID returned by POST /v1/assets. Use this or audio_url instead of text."
      },
      "callback_url": {
        "type": "string",
        "description": "Optional public HTTPS URL notified once with avatar-video.completed or avatar-video.failed. Signed with the workspace callback secret (X-XPilot-Signature). Only set it when you run a receiving service.",
        "maxLength": 2048
      },
      "callback_id": {
        "type": "string",
        "description": "Optional reference echoed back as data.callback_id. Requires callback_url.",
        "maxLength": 128,
        "pattern": "^[\\w.:-]{1,128}$"
      },
      "idempotency_key": {
        "type": "string",
        "description": "Retry key for this operation. Generate a UUID once and reuse it only when retrying the same input. Never ask the user for it.",
        "minLength": 8,
        "maxLength": 128,
        "pattern": "^[\\w.:-]{8,128}$"
      }
    },
    "required": [
      "avatar_id",
      "idempotency_key"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

<Accordion title="get_avatar_video">
  Read an avatar video job: status (queued, processing, completed, failed), video\_url (transparent WebM) once completed, duration\_seconds, width, height and the settled cost\_credits. Poll at least 5 seconds apart.

  Scope: `avatars:read`

  ```json theme={null}
  {
    "type": "object",
    "properties": {
      "avatar_id": {
        "type": "string",
        "description": "Owned avatar ID."
      },
      "video_id": {
        "type": "string",
        "description": "Video ID returned by create_avatar_video."
      }
    },
    "required": [
      "avatar_id",
      "video_id"
    ],
    "additionalProperties": false
  }
  ```
</Accordion>

## Access and billing

* The sign-in page shows the account and the access being granted. Tokens only work for the X Pilot MCP server; access tokens last one hour and refresh automatically.
* Disconnect a client any time from Developer console → Connected AI. Tasks already running continue.
* MCP uses the same wallet as the web app: credits are reserved when a task starts, released when it ends, and the actual usage is charged. A task never takes your balance below zero.
* The AI sees only the tools allowed by the access you approved. Upload files with REST POST /v1/assets.

[Download MCP schemas](/downloads/mcp-tools.json).

export const XpilotDocFigure = () => <figure className="xpilot-doc-figure"><img src="/images/integration-mcp.png" alt="An AI client connects to the X-Pilot MCP server using OAuth or a scoped API key, then calls tools allowed by its granted scopes." width="1200" height="675" loading="lazy" /><figcaption>Connect an AI client to the business MCP server. Available tools depend on the scopes you grant.</figcaption></figure>;

<XpilotDocFigure />


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.