← 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)