> ## Documentation Index
> Fetch the complete documentation index at: https://docs.moda.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Tools Reference

> Complete reference for all Moda MCP tools — parameters, return types, and usage examples.

The Moda MCP server provides 20+ tools for working with canvas designs. These tools are called automatically by your AI coding agent when it needs design data or wants to create and manage designs.

| Tool                                                        | Description                                                      | Auth required    |
| ----------------------------------------------------------- | ---------------------------------------------------------------- | ---------------- |
| [`get_moda_bootstrap`](#get_moda_bootstrap)                 | Fetch Moda session bootstrap before any canvas work (call first) | Yes              |
| [`set_context`](#set_context)                               | Set your active organization and team                            | Yes              |
| [`get_context`](#get_context)                               | Show your current session context                                | Yes              |
| [`set_session_brand_kit`](#set_session_brand_kit)           | Pin a brand kit for this session                                 | Yes              |
| [`get_moda_canvas`](#get_moda_canvas)                       | Fetch semantic pseudo-HTML and design tokens                     | Public links: No |
| [`get_moda_canvas_tokens`](#get_moda_canvas_tokens)         | Extract design tokens only                                       | Public links: No |
| [`list_moda_canvas_pages`](#list_moda_canvas_pages)         | List pages with dimensions and node counts                       | Public links: No |
| [`export_canvas`](#export_canvas)                           | Export as PNG, JPEG, PDF, or PPTX                                | Public links: No |
| [`get_export_status`](#get_export_status)                   | Poll an in-progress export                                       | Public links: No |
| [`list_my_canvases`](#list_my_canvases)                     | Browse your canvases                                             | Yes              |
| [`search_canvases`](#search_canvases)                       | Search canvases by name                                          | Yes              |
| [`list_organizations`](#list_organizations)                 | List your orgs and teams                                         | Yes              |
| [`find_brand_kits`](#find_brand_kits)                       | JSON-only brand-kit lookup (agent-side, no iframe)               | Yes              |
| [`list_brand_kits`](#list_brand_kits)                       | List brand kits — renders visual showcase iframe                 | Yes              |
| [`create_brand_kit`](#create_brand_kit)                     | Create brand kit from website URL                                | Yes              |
| [`update_brand_kit`](#update_brand_kit)                     | Update brand kit colors, fonts, etc.                             | Yes              |
| [`set_default_brand_kit`](#set_default_brand_kit)           | Mark a brand kit as the **team's** default (destructive)         | Yes              |
| [`list_brand_kit_images`](#list_brand_kit_images)           | List logos and reference images for a kit                        | Yes              |
| [`remove_brand_kit_image`](#remove_brand_kit_image)         | Detach an image from a brand kit                                 | Yes              |
| [`upload_file`](#upload_file)                               | Upload a file from URL for attachments                           | Yes              |
| [`start_design_task`](#start_design_task)                   | Start an AI design task                                          | Yes              |
| [`get_task_status`](#get_task_status)                       | Get task status and progress                                     | Yes              |
| [`list_tasks`](#list_tasks)                                 | List recent design tasks                                         | Yes              |
| [`remix_design`](#remix_design)                             | Duplicate canvas + optional AI edits                             | Yes              |
| [`list_my_websites`](#list_my_websites)                     | Browse websites in your team                                     | Yes              |
| [`get_website`](#get_website)                               | Read HTML, title, share state, published URL                     | Yes              |
| [`create_website_from_upload`](#create_website_from_upload) | Create a website from an HTML upload                             | Yes              |
| [`update_website_from_upload`](#update_website_from_upload) | Replace HTML and/or title of an existing website                 | Yes              |

## Session context

When you first connect, Moda uses your default organization and team. If you belong to multiple organizations, you can switch with `set_context`:

1. Call `list_organizations` to see your orgs and teams.
2. Call `set_context` with the org name (and optionally team name).
3. All subsequent tool calls use that workspace automatically.

Your context persists for 24 hours across reconnections. Tools like `list_brand_kits`, `create_brand_kit`, `start_design_task`, and `remix_design` all read from your session context automatically. You can also pass `org_id` or `team_id` explicitly to override the session context for a single call.

### URL formats

All tools that accept a `url` parameter support these formats:

| Format             | Example                                                        |
| ------------------ | -------------------------------------------------------------- |
| Share link         | `https://moda.app/s/abc123-token`                              |
| Private canvas URL | `https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000` |
| Raw share token    | `abc123-token`                                                 |
| Legacy domain      | `https://app.moda.so/s/abc123-token`                           |

Private canvas URLs require [authentication](/mcp/authentication) via the remote server.

***

## set\_context

Sets your preferred organization and team for the current session. Once set, all subsequent tools that operate within a workspace (brand kits, design tasks, remixes) will use these defaults automatically.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter   | Type     | Required | Description                                                                                  |
| ----------- | -------- | -------- | -------------------------------------------------------------------------------------------- |
| `org_name`  | `string` | Yes      | Name of the organization to use (case-insensitive). Use `list_organizations` to see options. |
| `team_name` | `string` | No       | Name of the team within the org (case-insensitive). Defaults to the org's default team.      |

### Returns

A confirmation string:

```
Context set to organization 'Acme Corp' and team 'Design Team'. All subsequent operations will use this workspace.
```

### Example usage

```
# Set org (uses default team)
set_context(org_name="Acme Corp")

# Set org and specific team
set_context(org_name="Acme Corp", team_name="Marketing")
```

### Notes

* Call `list_organizations` first to see available organization and team names
* Context persists for 24 hours across reconnections
* You can call `set_context` again at any time to switch workspaces

***

## get\_context

Shows your current session context -- which organization and team are active for workspace-scoped tools.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

This tool takes no parameters.

### Returns

A string describing your current context:

```
Organization: Acme Corp | Team: Design Team
```

If no context is set:

```
No session context set. Call set_context to choose your organization and team.
```

***

## get\_moda\_bootstrap

MANDATORY FIRST CALL, every new conversation, before creating, editing, reading, remixing, or exporting any canvas — even if the agent already knows the user's identity: this returns far more than identity (the Moda skill pointer, active user, active workspace, and entitlements that affect tool-call decisions). The response also lets agents skip downstream lookups when defaults are unambiguous.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

This tool takes no parameters.

### Returns

```json theme={null}
{
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Jane Doe",
    "email": "jane@acme.com"
  },
  "session": {
    "org_id": "660e8400-e29b-41d4-a716-446655440000",
    "org_name": "Acme Corp",
    "team_id": "770e8400-e29b-41d4-a716-446655440000",
    "team_name": "Design Team",
    "brand_kit_id": null
  },
  "org_count": 1,
  "team_default_brand_kit": {
    "id": "880e8400-e29b-41d4-a716-446655440000",
    "title": "Acme Corp"
  },
  "brand_kit_count": 1,
  "plan": "paid",
  "concurrency_cap": 10
}
```

| Field                    | Type             | Description                                                                                      |
| ------------------------ | ---------------- | ------------------------------------------------------------------------------------------------ |
| `user.id`                | `string`         | The Moda user UUID.                                                                              |
| `user.name`              | `string`         | Display name.                                                                                    |
| `user.email`             | `string`         | Email address.                                                                                   |
| `session.org_id`         | `string \| null` | Active org for this session, or null if unset.                                                   |
| `session.team_id`        | `string \| null` | Active team for this session.                                                                    |
| `session.brand_kit_id`   | `string \| null` | Session-pinned brand kit, if any. Set via the showcase iframe button or `set_session_brand_kit`. |
| `org_count`              | `integer`        | Total orgs the user belongs to. If 1, no multi-org disambiguation is needed.                     |
| `team_default_brand_kit` | `object \| null` | The team default's id + title, if a default exists.                                              |
| `brand_kit_count`        | `integer`        | Brand kits in the active team. If 1, the agent can use that kit without further listing.         |
| `plan`                   | `string`         | Billing plan: `free`, `free_beta`, `paid`, `ultra`, or `enterprise`.                             |
| `concurrency_cap`        | `integer`        | Max concurrent design tasks for this plan. Use as the upper bound for bulk fan-out.              |

### Notes

* Reads from existing session context — no side effects.
* A good first call on any new conversation: `org_count`, `brand_kit_count`, and `concurrency_cap` collectively eliminate the need for several downstream tool calls.
* The `plan` value is the same enum returned by the billing service; `concurrency_cap` is pre-resolved so callers don't need to maintain their own mapping.

***

## set\_session\_brand\_kit

Pin a brand kit for the current session **without** changing the team default. Subsequent calls to `start_design_task` / `remix_design` that omit `brand_kit_id` will resolve to this kit instead of the team default.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

This is what the brand-kit showcase iframe's **Use for this session** button calls. For changing the team default (destructive, affects every team member), use [`set_default_brand_kit`](#set_default_brand_kit) instead.

### Parameters

| Parameter      | Type             | Required | Description                                                                     |
| -------------- | ---------------- | -------- | ------------------------------------------------------------------------------- |
| `brand_kit_id` | `string \| null` | No       | Brand kit UUID (or `bk_…` wire form). Pass null/empty to clear the session pin. |

### Returns

A confirmation string:

```
Session brand kit set to <uuid>. Subsequent design tasks will apply it unless overridden.
```

Or when cleared:

```
Session brand kit cleared. Subsequent design tasks will use the team default.
```

### Notes

* Switching workspaces via [`set_context`](#set_context) clears the session brand kit (kits are team-scoped).
* Resolution order for `start_design_task` / `remix_design`: explicit `brand_kit_id` → session preference → team default → none (if `skip_brand_kit=true`).
* The kit must belong to the active team; trying to pin a kit from a different team raises a tool error.

***

## get\_moda\_canvas

The primary tool for design-to-code workflows. Fetches a Moda canvas and returns semantic pseudo-HTML with CSS properties and design tokens that AI agents can translate to any frontend framework.

### Parameters

| Parameter     | Type              | Required | Description                                                                                                       |
| ------------- | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `url`         | `string`          | Yes      | Moda share URL (`https://moda.app/s/...`), private canvas URL (`https://moda.app/canvas/...`), or raw share token |
| `page_number` | `integer \| null` | No       | 1-indexed page number. Omit to get all pages.                                                                     |

### Returns

A string containing semantic pseudo-HTML with CSS properties, followed by a design tokens summary.

```html theme={null}
# Moda Design Export — Canvas Title # Source: https://moda.app/s/abc123 ## Page: Login Screen (1440x900) [page 1]

<section
  background="#f3f4f6"
  height="900px"
  width="1440px"
  display="flex"
  justify-content="center"
  align-items="center"
>
  <Card background="#ffffff" border-radius="16px" width="600px" display="flex" flex-direction="column" gap="20px">
    <Heading font-size="32px" font-weight="700" color="#111827">Welcome back</Heading>
    <Text font-size="14px" color="#6b7280"> Sign in to your account</Text>
    <TextInput border="1px solid #d1d5db" border-radius="8px" height="48px" width="520px" />
    <button background="#2563eb" border-radius="8px" height="48px" width="520px">Sign in</button>
  </Card>
</section>

## Design Tokens - Colors: #111827, #2563eb, #6b7280, #d1d5db, #f3f4f6, #ffffff - Font Inter: weights [400, 700] roles
[body, heading] - Corner radii: 8px, 16px
```

### Semantic tags

The transformer assigns semantic tag names based on visual properties and layer names:

| Tag                     | When used                                                  |
| ----------------------- | ---------------------------------------------------------- |
| `Heading`               | Text with font-size >= 24px or font-weight >= 600          |
| `Text`                  | Default text elements                                      |
| `Button`                | Rectangle with text, dark fill, and button-like dimensions |
| `TextInput`             | Rectangle with light fill, thin border, no text            |
| `Image`                 | Element with an image fill                                 |
| `Avatar`                | Small circular element with an image fill                  |
| `Card`                  | Group with a background rectangle and content              |
| `Row`                   | Container with `flex-direction: row`                       |
| `Column`                | Container with `flex-direction: column`                    |
| `Divider`               | Line element                                               |
| `Nav`, `Hero`, `Footer` | Matched from layer names                                   |

Layer names in Moda take priority over visual heuristics. See [Naming Layers](/mcp/naming-layers) for best practices.

### Example usage

```
# Fetch all pages
get_moda_canvas(url="https://moda.app/s/abc123")

# Fetch a specific page
get_moda_canvas(url="https://moda.app/s/abc123", page_number=2)

# Fetch a private canvas (requires authentication)
get_moda_canvas(url="https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000")
```

### Notes

* The output is intentionally HTML-like so agents can map it directly to React, Vue, or HTML components
* Design tokens are appended as a summary at the end of the output
* For multi-page canvases, omitting `page_number` returns all pages in a single response
* Hidden layers are excluded from the output
* Rich text extracts the first style span's properties; mixed-style paragraphs use the first style encountered

***

## get\_moda\_canvas\_tokens

Returns only the design tokens from a canvas — colors, fonts, variables, and corner radii — as structured JSON. Use this when you need to generate theme configuration files without the full semantic layout.

### Parameters

| Parameter     | Type              | Required | Description                                            |
| ------------- | ----------------- | -------- | ------------------------------------------------------ |
| `url`         | `string`          | Yes      | Moda share URL, private canvas URL, or raw share token |
| `page_number` | `integer \| null` | No       | 1-indexed page number. Omit for all pages.             |

### Returns

```json theme={null}
{
  "variables": {
    "primary": "#2563eb",
    "secondary": "#6b7280",
    "background": "#f3f4f6"
  },
  "colors": ["#111827", "#2563eb", "#6b7280", "#d1d5db", "#f3f4f6", "#ffffff"],
  "fonts": [
    {
      "family": "Inter",
      "weights": [400, 700],
      "roles": ["body", "heading"]
    }
  ],
  "radii": ["8px", "16px"],
  "dimensions": {
    "width": 1440,
    "height": 900
  }
}
```

| Field        | Type       | Description                                                             |
| ------------ | ---------- | ----------------------------------------------------------------------- |
| `variables`  | `object`   | Named design variables defined in the canvas (colors, numbers, strings) |
| `colors`     | `string[]` | All unique colors used in the canvas                                    |
| `fonts`      | `object[]` | Font families with weights and inferred roles                           |
| `radii`      | `string[]` | All unique corner radii used                                            |
| `dimensions` | `object`   | Canvas page dimensions (width and height)                               |

### Example usage

```
# Extract tokens for theme generation
get_moda_canvas_tokens(url="https://moda.app/s/abc123")

# Extract tokens from a specific page
get_moda_canvas_tokens(url="https://moda.app/s/abc123", page_number=1)
```

### Notes

* Variables defined in the Moda canvas (via the variables panel) appear in the `variables` field with their default values
* Colors are deduplicated and sorted
* Font roles (`body`, `heading`) are inferred from usage context (font size and weight)

***

## list\_moda\_canvas\_pages

Returns metadata about each page in a canvas, including page names, dimensions, and the number of design elements. Use this to understand the structure of multi-page canvases before fetching specific pages.

### Parameters

| Parameter | Type     | Required | Description                                            |
| --------- | -------- | -------- | ------------------------------------------------------ |
| `url`     | `string` | Yes      | Moda share URL, private canvas URL, or raw share token |

### Returns

```json theme={null}
{
  "canvas_name": "Marketing Website",
  "total_pages": 3,
  "pages": [
    {
      "page_number": 1,
      "name": "Hero Section",
      "width": 1440,
      "height": 900,
      "node_count": 12
    },
    {
      "page_number": 2,
      "name": "Features",
      "width": 1440,
      "height": 1200,
      "node_count": 24
    }
  ]
}
```

| Field                 | Type      | Description                           |
| --------------------- | --------- | ------------------------------------- |
| `canvas_name`         | `string`  | Name of the canvas                    |
| `total_pages`         | `integer` | Total number of pages                 |
| `pages[].page_number` | `integer` | 1-indexed page number                 |
| `pages[].name`        | `string`  | Page name as set in Moda              |
| `pages[].width`       | `number`  | Page width in pixels                  |
| `pages[].height`      | `number`  | Page height in pixels                 |
| `pages[].node_count`  | `integer` | Number of design elements on the page |

### Notes

* Page numbers are 1-indexed
* The `node_count` includes all visible elements on the page (shapes, text, images, groups)
* Hidden elements are excluded from the count

***

## export\_canvas

Export a Moda canvas as an image or document file. Pass exactly one of `canvas_id` or `url`.

### Parameters

| Parameter     | Type              | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                  |
| ------------- | ----------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `canvas_id`   | `string`          | No       | —       | Canvas UUID to export directly. Preferred after `start_design_task` returns a new canvas.                                                                                                                                                                                                                                                                                                                    |
| `url`         | `string`          | No       | —       | Moda share URL, private canvas URL, or raw share token. Use this for share-link or design-to-code workflows.                                                                                                                                                                                                                                                                                                 |
| `format`      | `string`          | No       | `"png"` | Export format: `png`, `jpeg`, `pdf`, or `pptx`                                                                                                                                                                                                                                                                                                                                                               |
| `page_number` | `integer \| null` | No       | `null`  | 1-indexed page number. Omit to export all pages — PDF/PPTX bundle every page natively, while multi-page PNG/JPEG are returned as a `.zip` of per-page files (`page-1.png`, `page-2.png`, …) since a single image container can't hold multiple pages. Single-page canvases still return a raw PNG/JPEG. The `format` field in the response reflects what was actually delivered (`zip` in the bundled case). |
| `task_id`     | `string \| null`  | No       | `null`  | Optional task identifier to scope the export's cache key. When provided, the rendered file is cached as a stable snapshot for that task — useful for design-task progress UIs that should keep showing the original task output even after the canvas is edited. When omitted (typical LLM use), the cache busts on any canvas-data change so direct re-exports always reflect the current state.            |
| `wait`        | `boolean`         | No       | `true`  | When `true` (default), block up to \~20s for the export to finish before returning an in-progress handle. When `false`, return `status='in_progress'` with a `task_id` immediately — call `get_export_status(task_id)` to retrieve the URL once the background work completes. Cache hits return synchronously regardless.                                                                                   |

### Format guide

| Format | Best for                                                              |
| ------ | --------------------------------------------------------------------- |
| `png`  | Design-to-code workflows — lossless image, ideal for visual reference |
| `jpeg` | Smaller file size when lossless quality isn't needed                  |
| `pdf`  | Multi-page documents, printing, sharing                               |
| `pptx` | PowerPoint presentations, slide decks                                 |

For multi-page PNG/JPEG without `page_number`, the response carries `format: "zip"` and the URL serves a `.zip` of per-page raster files. The requested raster format is preserved inside the archive (`page-1.png`, `page-2.png`, …).

### Returns

On success:

```json theme={null}
{
  "status": "completed",
  "url": "https://assets-cdn.moda.app/exports/abc123.png",
  "format": "png"
}
```

When the canvas has multiple pages and `page_number` was omitted, a PNG/JPEG export bundles into a zip:

```json theme={null}
{
  "status": "completed",
  "url": "https://assets-cdn.moda.app/exports/abc123.zip",
  "format": "zip",
  "total_pages": 8
}
```

When the export hasn't finished within the synchronous wait budget (large multi-page canvases, cold renders), the call returns an in-progress handle. Call `get_export_status(task_id)` to fetch the URL once it's ready — the same `task_id` keeps returning the in-progress shape until terminal:

```json theme={null}
{
  "status": "in_progress",
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "canvas_id": "660e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/660e8400-e29b-41d4-a716-446655440000",
  "format": "pdf",
  "total_pages": 23,
  "retry_after_seconds": 5
}
```

If a design task is still active for the target canvas:

```json theme={null}
{
  "status": "not_ready",
  "reason": "active_design_job",
  "retry_after_seconds": 3,
  "canvas_id": "550e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000",
  "task_id": "990e8400-e29b-41d4-a716-446655440000"
}
```

`canvas_id`, `canvas_url`, and `task_id` are only included for authenticated private-canvas exports (i.e., when using `canvas_id` or a private URL). Share-link exports omit these fields to avoid leaking internal identifiers.

| Field                 | Type      | Description                                                                                                                                         |
| --------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`              | `string`  | `"completed"`, `"in_progress"`, or `"not_ready"` (active design job blocks export)                                                                  |
| `url`                 | `string`  | Signed URL to the exported file (only when `status='completed'`)                                                                                    |
| `format`              | `string`  | The format that was used for the export                                                                                                             |
| `reason`              | `string`  | Retryable not-ready reason: `active_design_task` or `task_status_unavailable`                                                                       |
| `retry_after_seconds` | `integer` | Suggested delay before retrying when `status` is `"not_ready"` or `"in_progress"`                                                                   |
| `task_id`             | `string`  | Export task ID when `status='in_progress'`; pass to `get_export_status` to poll. Also surfaces the active design task ID when `status='not_ready'`. |
| `canvas_id`           | `string`  | Canvas UUID for the export target (private exports only)                                                                                            |
| `canvas_url`          | `string`  | Direct Moda link to the canvas (private exports only)                                                                                               |
| `total_pages`         | `integer` | Total page count for the canvas (echoed for client convenience)                                                                                     |

### Example usage

Provide exactly one of `canvas_id` or `url` per call:

```
# Export as PNG for design-to-code (default)
export_canvas(url="https://moda.app/s/abc123")

# Export directly from a canvas_id returned by start_design_task
export_canvas(canvas_id="550e8400-e29b-41d4-a716-446655440000", format="pdf")

# Export a specific page as JPEG
export_canvas(url="https://moda.app/s/abc123", format="jpeg", page_number=2)

# Export all pages as a PDF
export_canvas(url="https://moda.app/s/abc123", format="pdf")

# Export as a PowerPoint file
export_canvas(url="https://moda.app/s/abc123", format="pptx")
```

### Notes

* Pass exactly one of `canvas_id` or `url`
* For design-generation workflows, prefer `canvas_id` from `start_design_task` / `get_task_status`
* Export-before-ready and temporary readiness-check failures are normal retryable MCP states, not tool failures
* The signed URL expires after 7 days
* In production, the URL is served from the signed `assets-cdn.moda.app` CDN; raw `storage.googleapis.com` links are a fallback and not the canonical host
* Page exports are cached in Redis for 4 hours per `(canvas, page, ratio)` combo. The cache key includes the canvas `updated_at`, so any edit auto-invalidates. Re-exporting an unchanged page returns in milliseconds.
* For document formats (`pdf`, `pptx`), all pages are included by default unless `page_number` is specified
* For PNG/JPEG: pass a specific `page_number` to get a single image; omit it to get every page — single-page canvases return a raw image, multi-page canvases return a `.zip` of `page-1.png`/`page-2.png`/… (`format: "zip"` in the response)
* When a design task auto-exports via `export_on_complete`, the artifact lives at `result.export` on the task and is keyed identically to a manual `export_canvas` call. A follow-up `export_canvas` for the same canvas with no edits hits the cache instead of re-rendering.
* Screenshots are rendered server-side using a headless browser

***

## get\_export\_status

Poll the status of an asynchronous export started by `export_canvas`. Use when `export_canvas` returned `status='in_progress'` (large multi-page exports that exceed the synchronous wait budget).

### Parameters

| Parameter | Type     | Required | Description                                                                                               |
| --------- | -------- | -------- | --------------------------------------------------------------------------------------------------------- |
| `task_id` | `string` | Yes      | Export task ID returned by `export_canvas` when `status='in_progress'` — bare UUID or prefixed wire form. |

### Returns

While running:

```json theme={null}
{
  "status": "running",
  "is_terminal": false,
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "canvas_id": "660e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/660e8400-e29b-41d4-a716-446655440000",
  "format": "pdf",
  "total_pages": 23,
  "retry_after_seconds": 5
}
```

On completion:

```json theme={null}
{
  "status": "completed",
  "is_terminal": true,
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "canvas_id": "660e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/660e8400-e29b-41d4-a716-446655440000",
  "format": "pdf",
  "url": "https://assets-cdn.moda.app/exports/abc123.pdf",
  "total_pages": 23
}
```

On failure:

```json theme={null}
{
  "status": "failed",
  "is_terminal": true,
  "task_id": "550e8400-e29b-41d4-a716-446655440000",
  "error": "Failed to export document: ...",
  "format": "pdf"
}
```

| Field                 | Type      | Description                                               |
| --------------------- | --------- | --------------------------------------------------------- |
| `status`              | `string`  | `queued`, `running`, `completed`, or `failed`.            |
| `is_terminal`         | `boolean` | `true` once the task has reached `completed` or `failed`. |
| `task_id`             | `string`  | The export task ID being polled.                          |
| `url`                 | `string`  | Signed download URL once `status='completed'`.            |
| `error`               | `string`  | Failure detail when `status='failed'`.                    |
| `retry_after_seconds` | `integer` | Suggested poll cadence while `is_terminal` is `false`.    |

### Notes

* Export task records are kept for \~1 hour. After that the task ID is treated as unknown and the call returns a tool error.
* Call `get_export_status` instead of repeatedly calling `export_canvas` while a known task is running — `export_canvas` may return the same in-progress handle, but `get_export_status` is the canonical poll path.

***

## list\_my\_canvases

Returns a paginated list — or, with a `query`, a paginated keyword search — of canvases accessible to the authenticated user. Use this to browse available canvases when you don't have a specific URL, and whenever the user should see or pick one.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter | Type      | Required | Default | Description                                                                                                                       |
| --------- | --------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `query`   | `string`  | No       | `null`  | Keyword to match against canvas names (fuzzy) and content (full-text). Omit to browse by recency; combines with the other filters |
| `limit`   | `integer` | No       | `20`    | Number of canvases to return (max 100)                                                                                            |
| `offset`  | `integer` | No       | `0`     | Number of canvases to skip for pagination                                                                                         |

### Returns

```json theme={null}
{
  "canvases": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Marketing Website",
      "url": "https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000",
      "updated_at": "2025-03-15T10:30:00Z"
    }
  ],
  "total": 42,
  "limit": 20,
  "offset": 0
}
```

| Field                   | Type      | Description                      |
| ----------------------- | --------- | -------------------------------- |
| `canvases[].id`         | `string`  | Canvas UUID                      |
| `canvases[].name`       | `string`  | Canvas name                      |
| `canvases[].url`        | `string`  | Direct canvas URL                |
| `canvases[].updated_at` | `string`  | Last update timestamp (ISO 8601) |
| `total`                 | `integer` | Total number of canvases         |
| `limit`                 | `integer` | Page size                        |
| `offset`                | `integer` | Current offset                   |

### Notes

* Canvases are returned in order of most recently updated; with a `query`, results are ranked by relevance and then recency
* Pairs with the Canvas Gallery [interactive app](/mcp/interactive-apps), which renders the results as a thumbnail grid and calls this tool again from its search box and "Show more" button
* Prefer this tool over `search_canvases` whenever the user should see or pick a canvas — pass `query` to hand them a pre-filtered gallery
* The `url` field can be passed directly to other tools like `get_moda_canvas`
* The maximum `limit` is 100 per request

***

## search\_canvases

Searches your canvases by name or content and returns matching results as plain JSON. Use this when *you* need to resolve a canvas ID for a follow-up call and don't have the exact URL.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter | Type      | Required | Default | Description                                            |
| --------- | --------- | -------- | ------- | ------------------------------------------------------ |
| `query`   | `string`  | Yes      | —       | Search query to match against canvas names and content |
| `limit`   | `integer` | No       | `20`    | Maximum number of results to return (max 100)          |

### Returns

```json theme={null}
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Marketing Website v2",
    "url": "https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000"
  }
]
```

| Field     | Type     | Description       |
| --------- | -------- | ----------------- |
| `[].id`   | `string` | Canvas UUID       |
| `[].name` | `string` | Canvas name       |
| `[].url`  | `string` | Direct canvas URL |

### Notes

* The search matches against canvas names and content
* Results are ranked by relevance
* Renders **no** interactive app — it's the JSON-only counterpart to `list_my_canvases`, which takes the same `query` and renders the Canvas Gallery. Use that one when the user should see the results
* The returned `url` can be passed directly to other tools like `get_moda_canvas`
* The maximum `limit` is 100 per request

***

## list\_organizations

Returns a list of organizations and teams you belong to. Use this to discover available workspaces, then call `set_context` to choose which one to use.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

This tool takes no parameters.

### Returns

```json theme={null}
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Acme Corp",
    "role": "admin",
    "teams": [
      {
        "id": "660e8400-e29b-41d4-a716-446655440000",
        "name": "Design Team",
        "is_default": true
      },
      {
        "id": "770e8400-e29b-41d4-a716-446655440000",
        "name": "Marketing",
        "is_default": false
      }
    ]
  }
]
```

| Field                   | Type      | Description                                     |
| ----------------------- | --------- | ----------------------------------------------- |
| `[].id`                 | `string`  | Organization UUID                               |
| `[].name`               | `string`  | Organization name                               |
| `[].role`               | `string`  | Your role in the organization (admin or member) |
| `[].teams`              | `array`   | Teams you have access to within this org        |
| `[].teams[].id`         | `string`  | Team UUID                                       |
| `[].teams[].name`       | `string`  | Team name                                       |
| `[].teams[].is_default` | `boolean` | Whether this is the org's default team          |

### Notes

* Use org and team names with `set_context` to choose your active workspace
* IDs are included but typically don't need to be shown to users — use names in conversation instead
* Organizations are sorted alphabetically by name
* Only teams you have access to are included

***

## find\_brand\_kits

JSON-only sibling of [`list_brand_kits`](#list_brand_kits). Same data, same parameters — the difference is presentation. `find_brand_kits` does **not** render the visual brand-kit showcase iframe, so it's appropriate when the agent is just looking up a `brand_kit_id` to pass to another tool (e.g. `start_design_task`).

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

Per the [MCP Apps spec](https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/), iframe rendering is decided at tool-listing time from a tool's `_meta.ui.resourceUri`. There is no per-call way to suppress rendering. So Moda exposes two tools sharing the same backing data:

* `find_brand_kits` — JSON-only. Use for tool-call decisions.
* `list_brand_kits` — renders the visual showcase. Use when the user asked to see their kits.

### Parameters

Identical to [`list_brand_kits`](#list_brand_kits): `org_id`, `team_id`, `verbose`, `limit`, `offset`.

### Returns

Identical to [`list_brand_kits`](#list_brand_kits).

***

## list\_brand\_kits

Returns brand kits for a team. **Renders the visual brand-kit showcase iframe on every call** (on app-aware hosts like claude.ai and Claude Desktop). Brand kits contain colors, fonts, logos, and brand guidelines that were extracted from company websites. Uses your session context (set via `set_context`) or defaults to your primary workspace.

For agent-side lookups where the iframe would take over screen space the user didn't ask for, prefer [`find_brand_kits`](#find_brand_kits) — same data, no UI.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter | Type      | Required | Description                                                                                                                             |
| --------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `org_id`  | `string`  | No       | Organization UUID. Overrides session context for this call.                                                                             |
| `team_id` | `string`  | No       | Team UUID. Overrides session context for this call.                                                                                     |
| `verbose` | `boolean` | No       | Default `true`. Include full kit detail (colors, fonts, logos, brand values). Pass `false` for an id/title-only projection.             |
| `limit`   | `integer` | No       | Default `10`. Caps the response so teams with many kits don't overflow the host's tool-result context window. Use `offset` to paginate. |
| `offset`  | `integer` | No       | Default `0`. Pagination offset; check `has_more` in the response to know when to stop.                                                  |

If neither `org_id` nor `team_id` is provided, uses your [session context](#session-context) or defaults to your primary workspace.

### Returns

```json theme={null}
{
  "brand_kits": [
    {
      "id": "880e8400-e29b-41d4-a716-446655440000",
      "title": "Acme Corp",
      "is_default": true,
      "created_at": "2025-03-15T10:30:00",
      "updated_at": "2025-03-16T14:20:00",
      "company_name": "Acme Corp",
      "company_url": "https://acme.com",
      "company_description": "Modern design tools for teams",
      "tagline": "Design at scale",
      "brand_values": ["innovation", "simplicity"],
      "brand_aesthetic": ["modern", "minimal"],
      "brand_tone_of_voice": ["professional", "friendly"],
      "default_color_mode": "light",
      "default_theme_canvas_id": "cvs_01HT9WK8N3M2J4A5Z6P7Q8R9TV",
      "colors": [
        { "id": "clr_01HT9WK8N3M2J4A5Z6P7Q8R9TV", "color": "#2563eb", "label": "Primary", "mode": "light" },
        { "id": "clr_01HT9WK8N3M2J4A5Z6P7Q8R9TW", "color": "#111827", "label": "Text", "mode": null }
      ],
      "fonts": [{ "family": "Inter", "label": "Body", "weight": 400, "supported": true }],
      "logos": [
        {
          "group_name": "Primary Logo",
          "images": [{ "name": "logo-dark.svg", "url": "https://..." }]
        }
      ]
    }
  ],
  "team_id": "660e8400-e29b-41d4-a716-446655440000",
  "total": 12,
  "offset": 0,
  "limit": 10,
  "has_more": true
}
```

| Field                        | Type      | Description                                                                                                                                                               |
| ---------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `[].id`                      | `string`  | Brand kit UUID                                                                                                                                                            |
| `[].title`                   | `string`  | Display name for the brand kit                                                                                                                                            |
| `[].is_default`              | `boolean` | Whether this kit is the team's default, applied automatically by `start_design_task`                                                                                      |
| `[].default_color_mode`      | `string`  | Preferred color mode when both light and dark palettes are present. Matches `colors[].mode` — typically `"light"` or `"dark"`, or `null` when no mode distinction exists. |
| `[].default_theme_canvas_id` | `string`  | Prefixed `cvs_…` ID of the kit's saved default slides theme canvas, or `null`. Set/clear it via `update_brand_kit`.                                                       |
| `[].colors`                  | `array`   | Ordered brand color palette (lower index = higher primacy)                                                                                                                |
| `[].colors[].id`             | `string`  | Unique identifier for this color entry                                                                                                                                    |
| `[].colors[].color`          | `string`  | Hex color value (e.g. `"#2563eb"`)                                                                                                                                        |
| `[].colors[].label`          | `string`  | Semantic role label (e.g. `"Primary"`, `"Text"`)                                                                                                                          |
| `[].colors[].mode`           | `string`  | Color palette mode: `"light"`, `"dark"`, or `null` when the color isn't mode-specific                                                                                     |
| `[].fonts`                   | `array`   | Brand typography entries                                                                                                                                                  |
| `[].logos`                   | `array`   | Logo image groups                                                                                                                                                         |

When `verbose=false`, each kit drops the `colors`, `fonts`, `logos`, `brand_values`, `brand_aesthetic`, `brand_tone_of_voice`, `company_url`, and `company_description` fields — useful when you only need ids/titles to pick a kit by name.

`brand_aesthetic` is a deprecated keyword array describing the brand's visual style. The brand's visual aesthetic is otherwise captured internally and is not exposed through this API.

### Notes

* Uses your session context to determine the workspace. Call `set_context` to switch organizations or teams.
* Default-kit-first, then created-at descending. Stable across paginated calls when no edits intervene.
* The `is_default` brand kit is automatically used by `start_design_task` when no `brand_kit_id` is specified.
* Cache: signed export URLs and signed CDN logo URLs are valid for 24h; a chat refresh that re-renders the showcase iframe within that window hits a Redis cache instead of re-signing.

***

## create\_brand\_kit

Creates a brand kit by extracting brand information from a company website. Provide a URL or domain and Moda will extract colors, fonts, logos, and brand guidelines automatically. Uses your session context (set via `set_context`) or defaults to your primary workspace.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter | Type     | Required | Description                                                        |
| --------- | -------- | -------- | ------------------------------------------------------------------ |
| `url`     | `string` | Yes      | Website URL or domain (e.g., `stripe.com` or `https://stripe.com`) |
| `org_id`  | `string` | No       | Organization UUID. Overrides session context for this call.        |
| `team_id` | `string` | No       | Team UUID. Overrides session context for this call.                |

### Returns

Returns the created brand kit in the same format as `list_brand_kits` entries.

```json theme={null}
{
  "id": "880e8400-e29b-41d4-a716-446655440000",
  "title": "Stripe",
  "is_default": false,
  "company_name": "Stripe",
  "company_url": "https://stripe.com",
  "company_description": "Financial infrastructure for the internet",
  "default_color_mode": null,
  "colors": [
    { "id": "clr_01HT9WK8N3M2J4A5Z6P7Q8R9TV", "color": "#635bff", "label": "Primary", "mode": null },
    { "id": "clr_01HT9WK8N3M2J4A5Z6P7Q8R9TW", "color": "#0a2540", "label": "Dark", "mode": null }
  ],
  "fonts": [{ "family": "Inter", "label": "Body", "weight": 400, "supported": true }],
  "logos": []
}
```

### Notes

* Extraction typically takes 10–30 seconds depending on the website
* Uses Firecrawl to scrape the website and extract brand data
* If the brand has been extracted before, a cached result is used for faster response
* The first brand kit created for a team is automatically set as the default

***

## update\_brand\_kit

Updates an existing brand kit. Pass only the fields you want to change — all other fields remain unchanged.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter                       | Type       | Required | Description                                                                                                                                          |
| ------------------------------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `brand_kit_id`                  | `string`   | Yes      | ID of the brand kit to update                                                                                                                        |
| `title`                         | `string`   | No       | Display title for the brand kit                                                                                                                      |
| `colors`                        | `array`    | No       | Array of `{ color, label?, id?, mode? }` objects (replaces all colors). `color` is required (hex string). `id` is assigned automatically if omitted. |
| `fonts`                         | `array`    | No       | Array of `{ family, label, weight }` objects                                                                                                         |
| `company_name`                  | `string`   | No       | Company name                                                                                                                                         |
| `company_description`           | `string`   | No       | Company description                                                                                                                                  |
| `tagline`                       | `string`   | No       | Brand tagline                                                                                                                                        |
| `brand_values`                  | `string[]` | No       | List of brand values                                                                                                                                 |
| `brand_aesthetic`               | `string[]` | No       | **Deprecated** list of aesthetic descriptors. The free-text `aesthetic` is derived during brand-kit population and is not settable here.             |
| `brand_tone_of_voice`           | `string[]` | No       | List of tone descriptors                                                                                                                             |
| `default_theme_canvas_id`       | `string`   | No       | Canvas ID (`cvs_…` or bare UUID) of a `template_type='theme'` canvas on the same team to set as the kit's default theme                              |
| `clear_default_theme_canvas_id` | `boolean`  | No       | Set `true` to clear the saved default theme. Mutually exclusive with `default_theme_canvas_id`                                                       |

Setting `default_theme_canvas_id` to a non-theme canvas — or one on a different team — fails with `invalid_theme_canvas`.

### Returns

Returns the updated brand kit in the same format as `list_brand_kits` entries.

### Notes

* Pass only the fields you want to change — omitted fields are not modified
* When updating `colors` or `fonts`, the entire array is replaced (not merged)
* You must have access to the team that owns the brand kit

***

## set\_default\_brand\_kit

Marks a brand kit as the team's default and clears the default flag on the previously-default kit. The default brand kit is automatically applied by `start_design_task` when no `brand_kit_id` is provided.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

<Warning>
  **Destructive — affects every team member.** This tool clears the default flag on whichever kit was previously default.
  Only call it when the user explicitly asks to change their **team** default brand kit. For session-only changes
  (no team-wide impact), use [`set_session_brand_kit`](#set_session_brand_kit) instead — that's what the Brand Kit
  Showcase iframe's "Use for this session" button now calls.
</Warning>

### Parameters

| Parameter      | Type     | Required | Description                                                        |
| -------------- | -------- | -------- | ------------------------------------------------------------------ |
| `brand_kit_id` | `string` | Yes      | Brand kit UUID — bare UUID or prefixed `bk_` wire form is accepted |

### Returns

Returns the team's brand kits in the same shape as `list_brand_kits`, with the new default first. Use this to rerender any UI that depends on default-kit state without an additional round-trip.

### Example usage

```
# Promote a kit to default after the user explicitly asks
set_default_brand_kit(brand_kit_id="880e8400-e29b-41d4-a716-446655440000")
```

### Notes

* Idempotent: setting the already-default kit is a no-op
* Only the user's currently-active team is affected
* Pairs with the Brand Kit Showcase [interactive app](/mcp/interactive-apps), which calls this tool from its "Set as default" button

***

## list\_brand\_kit\_images

Returns every image attached to a brand kit — both logos and design references — in newest-insertion order. Useful when reconciling an existing brand kit before adding new images, since blindly calling `add_brand_kit_image` would create duplicates.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter      | Type     | Required | Description                                                        |
| -------------- | -------- | -------- | ------------------------------------------------------------------ |
| `brand_kit_id` | `string` | Yes      | Brand kit UUID — bare UUID or prefixed `bk_` wire form is accepted |

### Returns

```json theme={null}
{
  "brand_kit_id": "bk_abc123",
  "images": [
    {
      "id": "bki_550e8400e29b41d4a716446655440000",
      "role": "logo",
      "file_id": "file_660e8400e29b41d4a716446655440000",
      "url": "https://assets-cdn.moda.app/brand-kits/.../logo-dark.svg",
      "name": "logo-dark.svg",
      "group_id": "770e8400-e29b-41d4-a716-446655440000",
      "group_name": "Primary Logo"
    },
    {
      "id": "bki_aa0e8400e29b41d4a716446655440000",
      "role": "reference",
      "file_id": "file_bb0e8400e29b41d4a716446655440000",
      "url": "https://assets-cdn.moda.app/brand-kits/.../homepage-hero.png",
      "name": "homepage-hero.png",
      "group_id": "cc0e8400-e29b-41d4-a716-446655440000",
      "group_name": "Website screenshots"
    }
  ]
}
```

| Field                 | Type     | Description                                                         |
| --------------------- | -------- | ------------------------------------------------------------------- |
| `brand_kit_id`        | `string` | Echoed back for round-tripping                                      |
| `images[].id`         | `string` | Image-row ID (prefixed `bki_`) — pass to `remove_brand_kit_image`   |
| `images[].role`       | `string` | `"logo"` or `"reference"`                                           |
| `images[].file_id`    | `string` | Underlying file ID (prefixed `file_`)                               |
| `images[].url`        | `string` | Direct URL to the image (CDN-served)                                |
| `images[].name`       | `string` | Original filename, if known                                         |
| `images[].group_id`   | `string` | UUID of the containing logo group / reference group                 |
| `images[].group_name` | `string` | Display name of the group (e.g., `"Primary Logo"`, `"Photography"`) |

### Example usage

```
list_brand_kit_images(brand_kit_id="880e8400-e29b-41d4-a716-446655440000")
```

### Notes

* Pairs with the Brand Kit Image Gallery [interactive app](/mcp/interactive-apps), which renders the images grouped by role
* The image `url` is served from the signed CDN; treat it as opaque

***

## remove\_brand\_kit\_image

Detaches an image from a brand kit by its `bki_` ID. Returns the freshly re-listed images so callers can rerender from authoritative state.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

<Warning>
  **Destructive.** Only call this when the user explicitly asks to remove a specific image, or in response to a user
  action (e.g., the per-card Remove button in the Brand Kit Image Gallery iframe). The underlying file remains in
  storage; only the brand-kit reference is removed.
</Warning>

### Parameters

| Parameter      | Type     | Required | Description                                                        |
| -------------- | -------- | -------- | ------------------------------------------------------------------ |
| `brand_kit_id` | `string` | Yes      | Brand kit UUID — bare UUID or prefixed `bk_` wire form is accepted |
| `image_id`     | `string` | Yes      | Brand-kit image-row ID — bare UUID or prefixed `bki_` wire form    |

### Returns

Returns the same payload shape as `list_brand_kit_images` after the removal completes, so the caller can rerender directly from the response.

### Example usage

```
remove_brand_kit_image(
  brand_kit_id="880e8400-e29b-41d4-a716-446655440000",
  image_id="bki_550e8400e29b41d4a716446655440000",
)
```

### Notes

* The underlying file is **not** deleted from storage — only the brand-kit reference is removed
* Pairs with the Brand Kit Image Gallery [interactive app](/mcp/interactive-apps), which calls this tool from each card's Remove button
* Not idempotent: calling twice with the same `image_id` raises a not-found error on the second call

***

## upload\_file

Uploads a file from a URL to Moda's storage. Returns a stable proxy URL that can be used as an attachment in `start_design_task`. Supports images, PDFs, Office documents (PowerPoint, Word, and Excel spreadsheets), CSV, plain-text/Markdown, and web-playable video (MP4, WebM, MOV).

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter    | Type     | Required | Description                                                 |
| ------------ | -------- | -------- | ----------------------------------------------------------- |
| `source_url` | `string` | Yes      | Public URL of the file to download and store                |
| `filename`   | `string` | No       | Filename to use. Inferred from URL if omitted.              |
| `org_id`     | `string` | No       | Organization UUID. Overrides session context for this call. |
| `team_id`    | `string` | No       | Team UUID. Overrides session context for this call.         |

### Returns

```json theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "url": "https://api.moda.app/api/v2/images/ref/550e8400-e29b-41d4-a716-446655440000?h=abc123",
  "filename": "design-reference.png",
  "mime_type": "image/png",
  "size_bytes": 245760,
  "was_duplicate": false,
  "message": "File uploaded successfully. Use the 'url' field as an attachment URL in start_design_task."
}
```

| Field           | Type      | Description                               |
| --------------- | --------- | ----------------------------------------- |
| `id`            | `string`  | Unique file identifier (UUID)             |
| `url`           | `string`  | Stable proxy URL for the uploaded file    |
| `filename`      | `string`  | Filename of the uploaded file             |
| `mime_type`     | `string`  | MIME type of the file                     |
| `size_bytes`    | `integer` | File size in bytes                        |
| `was_duplicate` | `boolean` | True if an identical file already existed |
| `message`       | `string`  | Human-readable confirmation message       |

### Example usage

```
# Upload an image for use as a design reference
upload_file(source_url="https://example.com/mockup.png")

# Upload with a custom filename
upload_file(source_url="https://example.com/file.pdf", filename="brand-guidelines.pdf")
```

### Notes

* The returned `url` can be passed directly as an attachment URL in `start_design_task`
* Files are deduplicated by content hash — uploading the same file twice returns the existing record
* Supported types include images (PNG, JPEG, WebP, etc.), PDFs, Office documents (PowerPoint `.pptx`/`.ppt`, Word `.docx`/`.doc`, Excel `.xlsx`/`.xls`), CSV, plain text/Markdown, and web-playable video (MP4, WebM, MOV)

***

## start\_design\_task

Starts an AI design task using Moda's design agent. The agent creates or edits a canvas based on your prompt. Provide a `canvas_id` to edit an existing canvas, or omit it to create a new one.

By default this tool returns **immediately** in milliseconds with a task handle (`{task_id, canvas_id, canvas_url, status: 'queued'}`). On hosts that support [interactive apps](/mcp/interactive-apps) (claude.ai web, Claude Desktop), the paired Design Task Progress iframe shows live progress and rendered pages, so you don't need to block on completion. On non-interactive hosts, poll `get_task_status(task_id)` to track progress and detect completion. Pass `wait=True` to opt back into synchronous behavior — useful only for non-interactive consumers that need a single blocking call.

Uses your session context (set via `set_context`) or defaults to your primary workspace.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter              | Type       | Required | Default | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| ---------------------- | ---------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `prompt`               | `string`   | Yes      | —       | Design instructions for the AI agent                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `canvas_id`            | `string`   | No       | —       | Canvas to edit. Omit to create a new canvas. Ignored when resuming a conversation.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `canvas_name`          | `string`   | No       | —       | Name for the new canvas (only used when `canvas_id` is omitted)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `brand_kit_id`         | `string`   | No       | —       | Brand kit to apply. Defaults to the team's default brand kit.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `org_id`               | `string`   | No       | —       | Organization UUID. Overrides session context for this call.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `team_id`              | `string`   | No       | —       | Team UUID. Overrides session context for this call.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `conversation_id`      | `string`   | No       | —       | UUID of a previous conversation to resume. The agent will have full context of all prior interactions. Returned in every response — pass it back to continue.                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `attachments`          | `array`    | No       | —       | List of reference files. Each item is **either** a URL-shape object `{url, name?, type?}` (type ∈ `"image"` / `"pdf"` / `"pptx"` / `"url"`) **or** a file-id-shape object `{file_id, role, label?}` where `file_id` is returned by `upload_file` and `role` ∈ `"source"` (extract content) / `"reference"` (emulate style) / `"asset"` (use directly in outputs) / `"import"` (PPTX-only — load the deck onto the canvas as editable slides, then design against it; any other file type is rejected). Prefer the file-id form for uploaded files — it surfaces role metadata to the agent. |
| `reference_canvas_ids` | `string[]` | No       | —       | List of canvas UUIDs to use as design inspiration. The agent can see these designs and reference their style, layout, or content.                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `format_category`      | `string`   | No       | —       | Format category — **recommended** for new designs. One of `"slides"` (presentations), `"social"` (social posts), `"carousel"` (multi-slide social), `"pdf"` (documents/reports), `"diagram"` (flowcharts/diagrams), `"ui"` (UI mockups), `"animation"` (motion graphics), `"prints"` (posters/flyers/cards), `"web-ads"` (display ads), or `"other"`. Without it, a fresh canvas resolves to `"other"` at 1080×1080 — not the format you intended.                                                                                                                                          |
| `format_width`         | `integer`  | No       | —       | Canvas width in pixels. Common: 1920x1080 (slides), 1080x1080 (social square), 1080x1920 (social story).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `format_height`        | `integer`  | No       | —       | Canvas height in pixels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `model_tier`           | `string`   | No       | —       | AI model tier: `"pro"` (best for complex tasks), `"pro-fast"` (Pro quality with faster output and higher credit usage), `"standard"`, or `"lite"` (fastest). Defaults to automatic selection.                                                                                                                                                                                                                                                                                                                                                                                               |
| `wait`                 | `boolean`  | No       | `false` | When `false` (default), return immediately with a task handle so callers can render the live-progress iframe or poll `get_task_status`. When `true`, block until the agent finishes and return the complete design — only needed for synchronous, non-interactive consumers.                                                                                                                                                                                                                                                                                                                |
| `export_on_complete`   | `object`   | No       | —       | Auto-export preferences applied when the task finishes — the rendered file is attached to `result.export` and the cache is warmed so a follow-up `export_canvas` for the same canvas hits it instantly. Shape: `{enabled?: boolean = true, format?: "png" \| "jpeg" \| "pdf" \| "pptx", pixel_ratio?: 1..4}`. Omit to use the canvas category default (`slides`→PPTX, `pdf`→PDF, others→PNG). Pass `{enabled: false}` to skip the auto-export. Multi-page PNG/JPEG bundles into a `.zip` of per-page files; `result.export.format` is `"zip"` in that case.                                 |

### Returns

```json theme={null}
{
  "task_id": "990e8400-e29b-41d4-a716-446655440000",
  "canvas_id": "550e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000",
  "conversation_id": "bb0e8400-e29b-41d4-a716-446655440000",
  "theme_canvas_id": null,
  "status": "queued",
  "message": "Design task started. Use get_task_status to check progress.",
  "retry_after_seconds": 3
}
```

`theme_canvas_id` reports the brand kit's slides theme that was auto-applied to the new canvas — the canvas whose page layouts the agent themes from. It's populated only when you create a **fresh slides deck** (`format_category="slides"`, no `canvas_id`) with a brand kit that has a saved default theme; it's `null` for non-slides designs, edits of existing canvases, and brand kits without a saved theme.

Once `get_task_status` reports `status: "completed"`, the same envelope additionally carries `result.export` with the auto-exported artifact:

```json theme={null}
{
  "result": {
    "canvas_id": "550e8400-e29b-41d4-a716-446655440000",
    "canvas_url": "https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000",
    "export": {
      "url": "https://assets-cdn.moda.app/exports/abc123.pptx",
      "format": "pptx",
      "status": "completed",
      "page_count": 8
    }
  }
}
```

| Field                      | Type      | Description                                                                                                        |
| -------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------ |
| `result.export.url`        | `string`  | Signed download URL for the rendered file (expires after 7 days).                                                  |
| `result.export.format`     | `string`  | Delivered format — `png`, `jpeg`, `pdf`, `pptx`, or `zip` (multi-page PNG/JPEG bundle).                            |
| `result.export.status`     | `string`  | `completed` when the file is ready; `failed` if the auto-export couldn't render (the design task still succeeded). |
| `result.export.page_count` | `integer` | Total pages in the canvas — useful for sizing UIs ahead of downloading.                                            |

### Example usage

```
# Create a new design (returns a conversation_id)
start_design_task(prompt="Create a modern SaaS landing page with a hero section, features grid, and pricing table")

# Resume the conversation to make changes (agent remembers previous context)
start_design_task(prompt="Change the color scheme to use blues and grays", conversation_id="bb0e8400...")

# Edit an existing canvas
start_design_task(prompt="Add a footer section", canvas_id="550e8400...")

# Create slides with specific dimensions
start_design_task(prompt="Create a pitch deck", format_category="slides", format_width=1920, format_height=1080)

# Create a PDF document (report, resume, etc.)
start_design_task(prompt="Create a project report", format_category="pdf")

# Provide reference images as attachments — file-id form (recommended)
# Upload first, then reference the returned file_id with a role:
uploaded = upload_file(file_url="https://example.com/reference.png")
start_design_task(
  prompt="Recreate this design in our brand style",
  attachments=[{"file_id": uploaded["id"], "role": "reference", "label": "brand-style ref"}],
  brand_kit_id="880e8400..."
)

# Or the legacy URL form — still accepted for hosted public URLs:
start_design_task(
  prompt="Recreate this design in our brand style",
  attachments=[{"url": "https://example.com/reference.png", "type": "image"}],
  brand_kit_id="880e8400..."
)

# Mix shapes in one call — a source brief plus a reference image:
start_design_task(
  prompt="Build a pitch deck from the brief; match the reference style",
  attachments=[
    {"file_id": "file_01HT9W…", "role": "source", "label": "Q2 strategy brief"},
    {"file_id": "file_01HT9X…", "role": "reference"},
  ],
)

# Use existing canvases as design inspiration
start_design_task(
  prompt="Create a similar landing page",
  reference_canvas_ids=["550e8400...", "660e8400..."]
)

# Canonical MCP flow: start -> poll -> read result.export (no second render)
task = start_design_task(prompt="Create a sales deck", format_category="slides")
status = get_task_status(task_id=task["task_id"])
while not status["can_export"] and not status["is_terminal"]:
  # Wait status["retry_after_seconds"] seconds, then poll again.
  status = get_task_status(task_id=task["task_id"])

# When the task auto-exports successfully, the rendered file rides in
# status["result"]["export"] — read it directly instead of calling
# export_canvas (the cache would hit, but result.export saves a round trip).
if status["can_export"]:
  export_info = status["result"]["export"]
  print(export_info["url"], export_info["format"])  # e.g. ".../abc.pptx", "pptx"

# Opt out of the auto-export when you don't need it:
start_design_task(prompt="Iterate on the layout", canvas_id="550e8400...", export_on_complete={"enabled": False})

# Or override the format/scale (e.g. PDF instead of the slides category default):
start_design_task(prompt="Build a deck", format_category="slides", export_on_complete={"format": "pdf", "pixel_ratio": 3})
```

### Notes

* The default `wait=False` returns a task handle in milliseconds. Pass `wait=True` only when you specifically want a single blocking call.
* On iframe-aware hosts (claude.ai web, Claude Desktop), the paired Design Task Progress app polls and renders pages on its own — see [Interactive Apps](/mcp/interactive-apps).
* Use `retry_after_seconds` as the default delay before the first `get_task_status(task_id)` call
* Keep polling until `can_export == true` or `is_terminal == true`
* `can_export` becomes `true` only after successful completion with exportable canvas data
* If no `brand_kit_id` is provided, the team's default brand kit is used automatically
* If no `canvas_id` is provided, a new canvas is created with the name from `canvas_name` or "Untitled"
* Every response includes a `conversation_id`. Pass it back in subsequent calls so the agent has context of all previous interactions.
* When resuming a conversation, the `canvas_id` parameter is ignored — the agent automatically operates on the conversation's canvas
* Use `upload_file` to upload local files first, then pass the returned `file_id` in an `attachments` item with a `role` (`source` / `reference` / `asset` / `import`). `import` is PPTX-only — it loads the deck onto the canvas as editable slides. The older URL form is still accepted but drops role metadata.
* Reference canvas IDs let the agent see and draw inspiration from existing designs without modifying them
* Always pass `format_category` when creating new designs — without it, a fresh canvas is created as a generic `"other"` canvas at 1080×1080 with no format skill, so a deck/post/document gets a generic layout
* The auto-export attached to `result.export` is keyed identically to a follow-up `export_canvas(canvas_id=...)`, so calling `export_canvas` afterward with default args hits the cache instead of re-rendering. Pin `export_on_complete.format` and `export_on_complete.pixel_ratio` when you need a non-default artifact — the cache match depends on the exact `(format, pixel_ratio)` pair.

***

## get\_task\_status

Returns the status and progress of a specific design task. Use this to check on tasks started with `start_design_task` or `remix_design`.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter | Type     | Required | Description             |
| --------- | -------- | -------- | ----------------------- |
| `task_id` | `string` | Yes      | ID of the task to check |

### Returns

```json theme={null}
{
  "task_id": "990e8400-e29b-41d4-a716-446655440000",
  "canvas_id": "550e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000",
  "conversation_id": "bb0e8400-e29b-41d4-a716-446655440000",
  "theme_canvas_id": null,
  "status": "running",
  "task": "Create a modern SaaS landing page...",
  "progress_percent": 45,
  "current_step": "Generating hero section",
  "is_terminal": false,
  "can_export": false,
  "retry_after_seconds": 3,
  "operations_streamed": 12,
  "created_at": "2025-03-15T10:30:00",
  "started_at": "2025-03-15T10:30:02",
  "completed_at": null,
  "error": null
}
```

| Field                 | Type      | Description                                                                   |
| --------------------- | --------- | ----------------------------------------------------------------------------- |
| `task_id`             | `string`  | Task UUID                                                                     |
| `canvas_id`           | `string`  | Canvas being edited                                                           |
| `canvas_url`          | `string`  | Direct link to the canvas                                                     |
| `conversation_id`     | `string`  | Conversation UUID. Pass to `start_design_task` to resume the conversation.    |
| `theme_canvas_id`     | `string`  | Brand-kit slides theme attached to the canvas, or `null` when unthemed.       |
| `status`              | `string`  | `queued`, `running`, `completed`, `failed`, or `cancelled`                    |
| `prompt`              | `string`  | The original prompt, in full                                                  |
| `progress_percent`    | `integer` | Estimated progress (0-100), if available                                      |
| `current_step`        | `string`  | Description of what the agent is currently doing                              |
| `is_terminal`         | `boolean` | `true` when the task is finished and polling can stop                         |
| `can_export`          | `boolean` | `true` when the task completed successfully and `export_canvas` can be called |
| `retry_after_seconds` | `integer` | Suggested delay before polling again when the task is still in progress       |
| `operations_streamed` | `integer` | Number of canvas operations applied so far                                    |
| `created_at`          | `string`  | Job creation timestamp (ISO 8601)                                             |
| `started_at`          | `string`  | When the agent started working (ISO 8601)                                     |
| `completed_at`        | `string`  | When the task finished (ISO 8601), or `null`                                  |
| `error`               | `string`  | Error message if the task failed, or `null`                                   |

### Notes

* Use this after `start_design_task` to poll for progress
* Poll again after `retry_after_seconds` while `is_terminal == false`
* On `status="completed"`, the response carries a `result.export` block (`{url, format, status, page_count}`) with the auto-exported design — read it directly instead of calling `export_canvas` for the same canvas. If the auto-export was disabled via `export_on_complete: {enabled: false}` or skipped (animation canvases), `result.export` is absent.
* Failed or cancelled tasks are terminal, but they do not set `can_export`
* The `prompt` field carries the user's original prompt in full (the task-progress iframe surfaces it verbatim)

***

## list\_tasks

Returns a list of recent design tasks. Use this to see what design tasks have been run recently, optionally filtered by canvas or status.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter   | Type      | Required | Default | Description                                                   |
| ----------- | --------- | -------- | ------- | ------------------------------------------------------------- |
| `canvas_id` | `string`  | No       | —       | Filter tasks to a specific canvas                             |
| `status`    | `string`  | No       | —       | Filter by status (`queued`, `running`, `completed`, `failed`) |
| `limit`     | `integer` | No       | `10`    | Number of tasks to return (max 50)                            |

### Returns

```json theme={null}
{
  "tasks": [
    {
      "task_id": "990e8400-e29b-41d4-a716-446655440000",
      "canvas_id": "550e8400-e29b-41d4-a716-446655440000",
      "canvas_url": "https://moda.app/canvas/550e8400-e29b-41d4-a716-446655440000",
      "conversation_id": "bb0e8400-e29b-41d4-a716-446655440000",
      "theme_canvas_id": null,
      "status": "completed",
      "prompt": "Create a modern SaaS landing page...",
      "progress_percent": 100,
      "current_step": null,
      "operations_streamed": 42,
      "created_at": "2025-03-15T10:30:00",
      "started_at": "2025-03-15T10:30:02",
      "completed_at": "2025-03-15T10:32:15",
      "error": null
    }
  ]
}
```

### Notes

* Tasks are returned in order of most recently created
* The maximum `limit` is 50 per request
* Only tasks for canvases you have access to are returned

***

## remix\_design

Duplicates an existing canvas and optionally starts a design task on the copy. Use this to create variations of existing designs without modifying the original. When a prompt is provided, the tool returns a task handle immediately — poll `get_task_status` the same way as `start_design_task`. Uses your session context (set via `set_context`) or defaults to your primary workspace.

<Info>
  This tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. It is not
  available when using the local `stdio` server.
</Info>

### Parameters

| Parameter      | Type     | Required | Default | Description                                                           |
| -------------- | -------- | -------- | ------- | --------------------------------------------------------------------- |
| `canvas_id`    | `string` | Yes      | —       | Canvas to duplicate                                                   |
| `prompt`       | `string` | No       | —       | Design instructions to apply to the copy. Omit for a plain duplicate. |
| `new_name`     | `string` | No       | —       | Name for the new canvas. Defaults to `"<original name> (Remix)"`.     |
| `brand_kit_id` | `string` | No       | —       | Brand kit to apply (only used when `prompt` is provided)              |
| `org_id`       | `string` | No       | —       | Organization UUID. Overrides session context for this call.           |
| `team_id`      | `string` | No       | —       | Team UUID. Overrides session context for this call.                   |

### Returns

Without a prompt (plain duplicate):

```json theme={null}
{
  "canvas_id": "aa0e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/aa0e8400-e29b-41d4-a716-446655440000",
  "canvas_name": "Marketing Website (Remix)",
  "source_canvas_id": "550e8400-e29b-41d4-a716-446655440000",
  "message": "Canvas duplicated successfully."
}
```

With a prompt (duplicate + design task):

```json theme={null}
{
  "canvas_id": "aa0e8400-e29b-41d4-a716-446655440000",
  "canvas_url": "https://moda.app/canvas/aa0e8400-e29b-41d4-a716-446655440000",
  "canvas_name": "Marketing Website (Remix)",
  "source_canvas_id": "550e8400-e29b-41d4-a716-446655440000",
  "task_id": "990e8400-e29b-41d4-a716-446655440000",
  "job_status": "queued",
  "message": "Design task started. Use get_task_status to check progress.",
  "retry_after_seconds": 3
}
```

### Example usage

```
# Plain duplicate
remix_design(canvas_id="550e8400...")

# Duplicate and restyle
remix_design(canvas_id="550e8400...", prompt="Change the color scheme to dark mode")

# Duplicate with new branding
remix_design(canvas_id="550e8400...", prompt="Apply our new brand", brand_kit_id="880e8400...")
```

### Notes

* The original canvas is never modified — all changes are applied to the copy
* When a `prompt` is provided, a design task is started on the duplicate and the tool returns immediately with a task handle
* Poll `get_task_status(task_id)` until `can_export == true` or the task reaches a terminal failure state, same as `start_design_task`

***

## Websites

Moda websites are HTML projects you can draft and manage, then publish from the Moda editor to a public `*.moda.page` URL. These four MCP tools let your agent draft, inspect, and update websites in Moda — available to every authenticated MCP user.

Publishing the resulting draft is opt-in: `create_website_from_upload` and `update_website_from_upload` both accept a `publish` flag that defaults to `false`. Passing `publish=true` requires the `website-publishing` feature on your Moda account (paid plan); otherwise the call errors and the agent should re-call with `publish=false` to save the draft. The user can then publish from the editor at any time.

HTML payloads are uploaded out-of-band (presigned URL → PUT bytes → call the tool with the returned `storage_key`) so large HTML documents — especially ones with inlined base64 images — don't have to pass through MCP tool args. Inline `data:` images are extracted to Moda Files automatically so the published artifact stays small.

<Info>
  Every website tool requires [authentication](/mcp/authentication) via the remote MCP server at `mcp.moda.app`. The
  local `stdio` server does not expose them.
</Info>

***

## list\_my\_websites

Lists the websites visible to the caller in the active team, newest-updated first. Scoped to the team in your session context — call `set_context` first if you belong to multiple teams.

### Parameters

| Parameter | Type      | Required | Description                                         |
| --------- | --------- | -------- | --------------------------------------------------- |
| `limit`   | `integer` | No       | Page size (default 20, clamped to 100 server-side). |
| `offset`  | `integer` | No       | Pagination offset, 0-indexed.                       |

### Returns

```json theme={null}
{
  "websites": [
    {
      "id": "bb0e8400-e29b-41d4-a716-446655440000",
      "title": "Launch Announcement",
      "url": "https://launch-announcement.moda.page",
      "share_state": "public",
      "comments_enabled": true,
      "recent_comments_count": 3,
      "updated_at": "2026-05-26T17:42:11Z"
    }
  ],
  "total": 1,
  "limit": 20,
  "offset": 0,
  "has_more": false
}
```

`url` is `null` for unpublished drafts.

***

## get\_website

Fetches the full agent-facing projection of one website — the source HTML for the home page, title, share state, comments toggle, editor URL, and (if published) the live `*.moda.page` URL. Use this before calling `update_website_from_upload` so the agent has the current source as a base.

### Parameters

| Parameter    | Type     | Required | Description                 |
| ------------ | -------- | -------- | --------------------------- |
| `website_id` | `string` | Yes      | ID of the website to fetch. |

### Returns

```json theme={null}
{
  "website_id": "bb0e8400-e29b-41d4-a716-446655440000",
  "title": "Launch Announcement",
  "html": "<!doctype html><html>…</html>",
  "editor_url": "https://moda.app/website/bb0e8400-e29b-41d4-a716-446655440000",
  "published_url": "https://launch-announcement.moda.page",
  "url": "https://launch-announcement.moda.page",
  "share_state": "public",
  "comments_enabled": true,
  "recent_comments_summary": []
}
```

`published_url` and `url` are both `null` for unpublished drafts. `url` is a deprecated alias kept for backward compatibility — prefer `published_url` in new code.

***

## create\_website\_from\_upload

Creates a new Moda website from an HTML file. The HTML is uploaded out-of-band first so large documents (especially ones with inlined base64 images) don't have to pass through MCP args.

**Required upload sequence:**

1. `create_upload_url(filename="site.html", mime_type="text/html")` → returns `{upload_url, storage_key}`.
2. PUT the HTML bytes to `upload_url` with header `Content-Type: text/html`.
3. Call `create_website_from_upload(storage_key=…)` with the `storage_key` from step 1.

Server-side, Moda extracts every inline `data:image/…` URI to a CDN-hosted Moda File, rewrites the HTML to reference the proxy URL, then creates the website row. Sites default to `category='html-document'`, `share_state='unlisted'`, and `comments_enabled=true`.

### Parameters

| Parameter     | Type      | Required | Description                                                                                                                                                                                                                        |
| ------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `storage_key` | `string`  | Yes      | The `storage_key` from `create_upload_url`, after you PUT the HTML bytes to its `upload_url`. Not the `upload_url` itself, not a filename.                                                                                         |
| `title`       | `string`  | No       | Display title. Omit to let Moda auto-generate a title from the page content.                                                                                                                                                       |
| `publish`     | `boolean` | No       | When `true`, also publish to a slugified `*.moda.page` URL. Defaults to `false` (draft only). Requires the `website-publishing` feature when `true` — otherwise the call errors and the agent should re-call with `publish=false`. |
| `category`    | `string`  | No       | Website category. Defaults to `html-document` for uploaded HTML documents. Valid values are `html-document`, `landing-page`, `email`, and `other`.                                                                                 |

### Returns

```json theme={null}
{
  "website_id": "bb0e8400-e29b-41d4-a716-446655440000",
  "editor_url": "https://moda.app/website/bb0e8400-e29b-41d4-a716-446655440000",
  "published_url": "https://launch-announcement.moda.page",
  "share_state": "unlisted",
  "comments_enabled": true,
  "extracted_image_count": 4,
  "skipped_inline_asset_count": 0
}
```

* `editor_url` is always present — give this link to the user.
* `published_url` is the live `*.moda.page` URL when `publish=true` succeeded; `null` for drafts. Do **not** fabricate a `*.moda.page` URL from the `website_id`.

***

## update\_website\_from\_upload

Replace the HTML and/or title of an existing website. The website\_id is preserved, existing comments stay anchored via a reconciliation pass over the new DOM, and inline `data:` images in the uploaded HTML are extracted to Moda Files (same pipeline as `create_website_from_upload`).

Same upload sequence as `create_website_from_upload` — omit `storage_key` to keep the current HTML and only rename via `title`.

### Parameters

| Parameter     | Type      | Required | Description                                                                                                                                     |
| ------------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `website_id`  | `string`  | Yes      | The website's ID.                                                                                                                               |
| `storage_key` | `string`  | No       | `storage_key` from `create_upload_url` after you PUT the new HTML bytes. Omit to keep the current HTML and only change the title.               |
| `title`       | `string`  | No       | New display title. Omit to keep the current title.                                                                                              |
| `publish`     | `boolean` | No       | When `true`, republish so the live URL reflects the new content. Defaults to `false` (draft update). Requires `website-publishing` when `true`. |

Supply at least one of `storage_key` or `title` — a call with neither is rejected.

### Returns

```json theme={null}
{
  "website_id": "bb0e8400-e29b-41d4-a716-446655440000",
  "editor_url": "https://moda.app/website/bb0e8400-e29b-41d4-a716-446655440000",
  "published_url": "https://launch-announcement.moda.page",
  "updated_at": "2026-05-26T17:42:11Z",
  "extracted_image_count": 4,
  "skipped_inline_asset_count": 0
}
```

`published_url` is `null` when `publish=false` — the live URL keeps serving the previously-published version until the user republishes from the editor.
