← All playbooks · Raw API

task-media-reuse-and-upload

# Capability: Media — reuse and upload

## Intent phrases

- upload image
- find existing asset
- add photo to page or article
- update person headshot / portrait

## Requires capabilities

`assetSearch` and `assetUpload`

## Prerequisites

- **Always run `index sync` first** if the catalog may be stale
- On **hosted MCP**, the server has **no filesystem** — use **staged upload** for all binary uploads (never `--base64` or a local file path in `args`)

## Reuse-first workflow (default)

1. `cms_edit ["index", "sync"]`
2. **Search existing assets:**
   - `cms_edit ["asset", "search", "--filename-match", "<partial-name>"]`
   - `cms_edit ["list", "--type", "media", "--asset-filename-match", "<partial>"]`
3. If match found — link by asset ID or filename in `set` / `create from-json` (`featuredImageAssetFilename`, `visualAssetFilename`, etc.)
4. For **`visuals`** (Media entry array): prefer an **existing Media entry** — `cms_edit ["list", "--type", "media", "--asset-id", "<assetId>"]` — then `set @root visuals <mediaEntryId> --links`. Do **not** `create media` or use `asset upload --with-media` when a wrapper already exists.
5. `create media` only when no Media entry wraps that asset (RTF embeds, or first article in a new composite batch — then **reuse that media ID** on sibling entries)

## Upload only when no match

### Hosted MCP (default)

**Always use staged upload** for images and other binary files. Do not pass base64 in `cms_edit` args — hosted MCP rejects `asset upload --base64`.

1. `cms_edit_request_staged_upload` with `fileName`, `mimeType`, optional `byteLength`, optional `title`
2. Run the returned `curlCommand` in a shell (POST file to `uploadUrl`)
3. `cms_edit` with the returned `consumeArgs` (e.g. `["asset","upload","--staged","<uploadId>",…]`)

Rules:

- Staged upload keeps binary **out of MCP args** — only a short `uploadId` is passed to Contentful
- Hosted limit: **4 MB** per file; **1000 upload URLs per user per hour**; TTL **60 minutes**; single-use
- **PNG** when the source has transparency (portraits on brand-colour cards); JPEG flattens alpha
- Upload alone does **not** attach the asset — follow with **link + save** (below)
- Use `--if-exists-by-filename` (with `--file-name`) to avoid duplicate assets when re-running

After staged upload, create a Media wrapper **only when none exists** for that asset:

```
cms_edit ["list", "--type", "media", "--asset-id", "<newPortraitAssetId>"]
cms_edit ["create", "media", "--asset-id", "<newPortraitAssetId>"]
```

**Media entry `name` (CMS list label):** defaults to the **linked asset title**. Only pass `--name` when you deliberately need a different label (e.g. two Media wrappers on one asset with distinct roles). Never use `visual-{assetId}` or `Wrapper for {assetId}` — those are migration leftovers.

To rename an existing Media wrapper: `cms_edit ["batch", "set", "<media-entry-id>:name=<asset-title>"]` then `batch save`.

Avoid `asset upload --with-media` when multiple entries will share the same image (e.g. news composites) — it creates duplicate Media entries. Upload the asset only, then one shared `create media`, then link the media ID on each entry.

**Last resort `--url`** only when the file is already on public HTTPS and staged upload is impractical:

```
cms_edit ["asset", "upload", "--url", "https://<public-https-url>/file.png", "--mime", "image/png", "--file-name", "headshot-name-1200.png", "--title", "…"]
```

The URL must be fetchable by the hosted MCP server. Do not use local-only URLs.

### Local CLI (only when user approves)

Local `cms-edit` may use `--base64` or `--url` when the user explicitly approves local CLI (see repo `Agents.md`). Hosted agents must not use local CLI without permission.

## Link uploaded asset to an entry

Upload returns an **asset ID**. Attach it, then save:

**Person portrait (`media` is an Asset link):**

```
cms_edit ["open", "--id", "<person-entry-id>"]
cms_edit ["asset", "set", "@root", "media", "<new-asset-id>"]
cms_edit ["diff"]
cms_edit ["save"]
```

**Component visual / article featured image:** same pattern — `asset set <ref> <field> <asset-id>` (or `set … --asset`).

Always `diff` → `save`. Verify with `preview urls` on the public path.

## Confirmation gates

1. Report search results before uploading
2. Confirm upload filename/title with user if ambiguous
3. After upload, confirm `asset info <id>` shows expected width/height/size before linking

## Out of scope

- Publish assets (draft upload; human publishes in Contentful UI if needed)
- Deleting orphan assets from failed uploads (human cleanup in Contentful)

## Related resources

- `cms-edit://customer/defaults` (featured image rules)
- `cms-edit://customer/people` (person `media` field, when present)