# Create AI sign design source (`create_ai_sign_design_source`)

Declare a PNG, JPEG, WebP or AVIF image or an SVG of at most 5 MiB, or a PDF of at most 25 MiB, for the AI sign designer and receive its source id, presigned PUT target and required headers. PUT the bytes to the target. The signature and size are checked when the job is created. The full check runs in the job: an AVIF, SVG or PDF is converted to an image there, and a file that cannot be read fails the job with no credit charged. The source belongs to the current connection, including after token refresh. Replay returns no target. Spends no credit. Requires ai_sign_designs:write, supports dry_run, and requires an idempotency_key for an actual declaration.

- Source URL: https://www.signcustomiser.com/help/mcp/tools/create_ai_sign_design_source/
- Markdown URL: https://www.signcustomiser.com/help/mcp/tools/create_ai_sign_design_source.md
- MCP endpoint: https://mcp.signcustomiser.com
- Required scope: `ai_sign_designs:write`
- Behaviour: write

## Purpose

Reserve a private upload target for the image, SVG or PDF an AI sign design starts from.

## Annotations

| Hint | Value |
| --- | --- |
| readOnlyHint | false |
| destructiveHint | false |
| idempotentHint | true |
| openWorldHint | false |

## Prerequisites

- The filename, its content type and its exact byte size.
- A merchant has connected this store and approved the ai_sign_designs:write scope.
- Call get_ai_sign_design_capabilities first to confirm the AI sign designer is available.

## Side effects

- Creates one AI sign design source and mints a presigned target that expires after about ten minutes.
- Spends no credit and starts no design.
- Claims the idempotency_key for at least 24 hours.

## Arguments

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `filename` | string | yes | The original filename. Its extension must match the content type: .png, .jpg, .jpeg, .jfif, .webp, .avif, .svg or .pdf. The server chooses the stored object name. |
| `content_type` | string | yes | The media type: image/png, image/jpeg, image/webp, image/avif, image/svg+xml or application/pdf. |
| `byte_size` | integer | yes | The exact size of the bytes that will be sent: at most 5242880, or 26214400 for a PDF. The job refuses the source if the stored bytes are a different size. |
| `idempotency_key` | string | unless `dry_run` is true | A client-generated key unique to this logical write, such as a UUID. Required: repeating the call with the same key and the same arguments replays the original result instead of writing twice, and the same key with different arguments is an idempotency_key_conflict. |
| `dry_run` | boolean | no | When true, the declaration is checked against the source limits is checked and nothing is written: the result is the same verdict the apply would have reached, and a problem the apply would have raised comes back as the same tool error. Defaults to false. A dry run claims no key. |

## Result

The source record and the presigned target to PUT the bytes to, with the headers the PUT must carry. The target is returned once and cannot be re-read. A dry run returns the create verdict and mints nothing.

| Property | Type | Required | Description |
| --- | --- | --- | --- |
| `request_id` | string | yes |  |
| `store` | object | yes |  |
| `store.id` | integer | yes |  |
| `store.name` | string | yes |  |
| `source` | object | no | The declared source. Absent on a dry run. |
| `source.id` | string | yes |  |
| `source.filename` | string | no |  |
| `source.content_type` | string | no |  |
| `source.byte_size` | integer | no |  |
| `source.status` | string | no | One of: `pending`, `uploaded`, `consumed`, `rejected`, `expired`. |
| `source.verification_failure_code` | string or null | no | One of: `size_mismatch`, `too_large`, `content_type_mismatch`, `dimensions_exceeded`, `pixel_limit_exceeded`, `undecodable`, `unsafe_content`, `source_changed`. |
| `source.width_px` | integer or null | no |  |
| `source.height_px` | integer or null | no |  |
| `source.expires_at` | string | no |  |
| `source.consumed_at` | string or null | no |  |
| `source.created_at` | string | no |  |
| `upload_target` | object | no | Where to PUT the bytes, and the headers the PUT must carry. Returned only by this tool, only once, and null on a replay. |
| `upload_target.method` | string | yes |  |
| `upload_target.url` | string | yes |  |
| `upload_target.headers` | object | no |  |
| `upload_target.expires_at` | string or null | no |  |
| `idempotent_replay` | boolean | no | True when this call replayed an earlier declaration with the same idempotency_key rather than creating a second source. |
| `dry_run` | boolean | no | Present and true only when dry_run was requested. |
| `valid` | boolean | no | Present only on a dry run, and always true: a failing dry run returns a tool error instead. |
| `verdict` | object | no | Present only on a dry run: the create verdict. |

## Error cases

| Code | Recovery |
| --- | --- |
| `ai_sign_designer_unavailable` | The AI sign designer is turned off for this store. |
| `idempotency_subject_changed` | The source recorded by this key no longer exists. Issue a new key and declare the source again. |
| `insufficient_scope` | The connection was approved without ai_sign_designs:write. |
| `source_size_exceeded` | byte_size is above the limit for the content type (5 MiB, or 25 MiB for a PDF); the problem reports it as max_byte_size. |
| `source_type_not_allowed` | The content type is not PNG, JPEG, WebP, AVIF, SVG or PDF, or the filename extension does not match it; allowed_values lists the accepted values. |

## Example: Declare a PNG logo

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_ai_sign_design_source",
    "arguments": {
      "filename": "shopfront-logo.png",
      "content_type": "image/png",
      "byte_size": 482133,
      "idempotency_key": "b8f1c2d3-4e5f-4a6b-9c7d-8e9f0a1b2c3d"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "request_id": "req_01jz9x2k7c8f3m5n6p7q8r9s0t",
      "store": {
        "id": 12,
        "name": "Demo Signs"
      },
      "source": {
        "id": "aisrc_9f2c4b7ad13e6058ba41cd4201",
        "filename": "shopfront-logo.png",
        "content_type": "image/png",
        "byte_size": 482133,
        "status": "pending",
        "verification_failure_code": null,
        "width_px": null,
        "height_px": null,
        "expires_at": "2026-10-21T09:30:00Z",
        "consumed_at": null,
        "created_at": "2026-10-20T09:30:00Z"
      },
      "upload_target": {
        "method": "PUT",
        "url": "https://uploads.example.invalid/ai-sign-design-source-target",
        "headers": [],
        "expires_at": "2026-10-20T09:40:00Z"
      },
      "idempotent_replay": false
    }
  }
}
```

## Example: A GIF file, which a source does not accept (error)

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "create_ai_sign_design_source",
    "arguments": {
      "filename": "logo.gif",
      "content_type": "image/gif",
      "byte_size": 18044,
      "idempotency_key": "b8f1c2d3-4e5f-4a6b-9c7d-8e9f0a1b2c3d"
    }
  }
}
```

Response:

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "isError": true,
    "structuredContent": {
      "error": {
        "code": "source_type_not_allowed",
        "title": "Source type not allowed",
        "detail": "AI sign design sources do not accept image/gif files.",
        "recovery": "Use a value from allowed_values for the field named by pointer. A source is a PNG, JPEG, WebP or AVIF image, an SVG or a PDF, and its filename extension must match its content type.",
        "pointer": "/content_type",
        "allowed_values": [
          "image/png",
          "image/jpeg",
          "image/webp",
          "image/avif",
          "image/svg+xml",
          "application/pdf"
        ]
      }
    }
  }
}
```
