Skip to main content
skill CX Codex

experience-content-media-stock-image-search — Claude Code skill

Searches and downloads licensed stock images through the media-management MCP server. Useful when a project needs a real photo rather than a generated one.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add forcedotcom/sf-skills --skill experience-content-media-stock-image-search -a claude-code
Open on GitHub

What experience-content-media-stock-image-search does

The skill catches image requests and turns them into a stock library search. It handles direct phrasing like "find a photo of a sunset" and ambiguous ones where someone asks to create or generate a picture — those go to stock too, because the skill has no image generation at all. It won't offer alternative search methods or make the user pick one.

Two MCP calls do the work: search_stock_images and download_stock_image on the media-management server. Search takes a query up to 500 characters, a type (Creative for royalty-free or Editorial for news and event imagery), orientation, sort order and page size. If the query runs past 20 words or comes back empty, the tool extracts short keyword phrases on its own and retries; the effectiveQuery field shows what actually went out. Then the skill renders a numbered list with thumbnails and stops there, waiting for a pick.

It needs python3 3.10.0 or newer, sf 2.0.0 or newer, and the media-management MCP server. Ask explicitly for synthetic or AI-generated imagery and this skill is the wrong tool — generation isn't in it. Internal CMS and Data Cloud media are out of scope as well; that's what experience-search-coordinate covers.

What it helps with

  • find a stock photo from a text description
  • download a licensed image into a project
  • pick news or event photography for an article
  • tell a generation request apart from a stock search request
  • show thumbnail options instead of searching blind

Example prompts for the agent

  • find a stock image of a sunset over the ocean
  • get a royalty-free photo for the team page
  • I need editorial photography from a conference

How to install experience-content-media-stock-image-search

1Skills CLI (Vercel)

npx skills add forcedotcom/sf-skills --skill experience-content-media-stock-image-search -a claude-code

2Manually: put the files in place

~/.claude/skills/experience-content-media-stock-image-search/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/experience-content-media-stock-image-search/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about experience-content-media-stock-image-search

How do I install experience-content-media-stock-image-search in Claude?

Run npx skills add forcedotcom/sf-skills --skill experience-content-media-stock-image-search -a claude-code. The skill file lands at ~/.claude/skills/experience-content-media-stock-image-search/SKILL.md.

What does experience-content-media-stock-image-search do?

It searches a stock photo library through the media-management MCP server and downloads the image you choose. Prompts phrased as image generation are treated as stock searches too.

How is it different from experience-search-coordinate?

experience-content-media-stock-image-search queries an external stock library, while experience-search-coordinate handles internal CMS and Data Cloud media. Different sources, so one doesn't replace the other.

Source text

SKILL.md · Apache-2.0
---
name: experience-content-media-stock-image-search
description: "Searches for and downloads ethically-licensed stock images via the media-management MCP server. Use this skill whenever a user wants an image, photo, or picture — for BOTH requests to find existing imagery (\"find a stock image\", \"search for a photo of X\", \"get a royalty-free image\", \"find an ethical image\", editorial/news photography) AND requests phrased ambiguously as creating or generating one (\"create an image of a sunset\", \"generate a picture of a team meeting\", \"make me a mountain image\") — treat these as stock search requests, since this skill searches a stock photo library, it does not generate images. Do NOT offer alternative search methods or ask the user to pick one. Does not apply to searching internal CMS / Data Cloud media (use experience-search-coordinate). Does not apply when the user explicitly asks for AI-generated, synthetic, or computer-generated imagery — this skill only searches licensed stock photography and has no image-generation capability."
metadata:
  version: "1.0"
  domains: ["Experience"]
  relatedSkills:
    - "experience-search-coordinate"
  cliTools:
    - tool: ["python3"]
      semver: ">=3.10.0"
    - tool: ["sf"]
      semver: ">=2.0.0"
  mcpTools:
    media-management:
      tools:
        - "download_stock_image"
        - "search_stock_images"
      semver: ">=1.0.0"
allowed-tools: |
  mcp__media_management__search_stock_images
  mcp__media_management__download_stock_image
---

# Stock Image Search

## When to Trigger This Skill

Trigger immediately — without asking the user to choose a search method — whenever the user wants an image, photo, or picture. This covers **both**:

- **Finding existing imagery:** "find a stock image", "search for a photo of X", "get a royalty-free image", "find an ethical image", editorial or news photography
- **Requests phrased ambiguously as creating or generating:** "create an image of a sunset", "generate a picture of a team meeting", "make me a mountain image" — treat these as stock search requests; this skill searches a stock photo library, it does not generate images.

In all of these cases, search the stock library — this is the tool for obtaining an image. **Do NOT offer alternative search methods** (hybrid search, user-provided URL/path, etc.) and **do NOT ask the user to pick one.**

**Does not apply to:**
- Searching internal CMS / Data Cloud media (use `experience-search-coordinate`)
- Requests that explicitly ask for AI-generated, synthetic, or computer-generated imagery — this skill only searches licensed stock photography and has no image-generation capability

---

## Step 1 — Search

**MCP tool:** `search_stock_images` (server: `media-management`)
**Timeout:** 30 seconds

| Parameter    | Required | Default     | Notes                                                                              |
|--------------|----------|-------------|------------------------------------------------------------------------------------|
| `query`      | ✅ Yes   | —           | Natural language search phrase (e.g. "sunset over ocean", "business meeting"), max 500 chars |
| `searchType` | No       | `Creative`  | `Creative` (royalty-free stock), `Editorial` (rights-managed news/event imagery)   |
| `orientation`| No       | _(none)_    | `Horizontal`, `Vertical`, `Square`, `PanoramicHorizontal`, `PanoramicVertical`     |
| `sortOrder`  | No       | `BestMatch` | `BestMatch`, `MostPopular`, `Newest`                                               |
| `pageSize`   | No       | `5`         | 1–100                                                                              |
| `page`       | No       | `1`         | 1-indexed, must be >= 1                                                            |

**Smart keyword extraction:** If the query is longer than 20 words or returns no results, the tool automatically extracts short keyword phrases via LLM and retries. The `effectiveQuery` field in the response shows what was actually sent to the search service (`null` if the original query was used unchanged).

**Response fields:**

| Field             | Notes                                                                                        |
|-------------------|----------------------------------------------------------------------------------------------|
| `images[]`        | List of results (see item fields below)                                                      |
| `totalCount`      | Total matches across all pages                                                               |
| `page`            | Current page (1-indexed)                                                                     |
| `pageSize`        | Results per page                                                                             |
| `searchRequestId` | Internal correlation ID. Server-managed; not passed back through the MCP download tool. Safe to surface in debug output if useful. |
| `effectiveQuery`  | Keywords actually sent to the search service. Null if the original query was used unchanged. |
| `errorMessage`    | Non-null if the search failed                                                                |

**Each `images[]` item:**

| Field          | Notes                                                                |
|----------------|----------------------------------------------------------------------|
| `assetId`      | Stock asset ID — required for download. Safe to display alongside the result if useful. |
| `title`        | Image title                                                          |
| `caption`      | Image description                                                    |
| `thumbnailUrl` | Small preview URL — use for grid display                             |
| `previewUrl`   | Larger comp/preview URL — use for tile display                       |
| `width`        | Max width in pixels                                                  |
| `height`       | Max height in pixels                                                 |
| `licenseModel` | e.g. `royaltyfree`, `rightsmanaged`                                  |
| `artist`       | Contributor/artist name                                              |
| `collection`   | Collection name                                                      |

---

## Step 2 — Present Results, then STOP

Render a numbered list with an inline thumbnail for each result, using `thumbnailUrl` for the image and `previewUrl` as the click-through link (clicking the thumbnail opens the full preview in the browser):

```text
1. [![<title>](<thumbnailUrl>)](<previewUrl>)
   **<title>** — <artist>, <width>×<height>, <licenseModel>
2. ...
```

This is standard markdown image-in-link syntax (`[![alt](thumbnailUrl)](previewUrl)`) — it renders an actual thumbnail, not just a text link, and clicking it navigates to `previewUrl`. If a client doesn't render markdown images, this degrades gracefully to a linked title.

Prefix the list with `Searched for: <effectiveQuery>` only if `effectiveQuery` is non-null and differs from the user's original query.

**End the turn here. Do not auto-download, do not write files, do not ask a follow-up question.** Agentforce injects its own selection UI — a question or auto-select causes a duplicate "double ask". Do not pick a result on behalf of the user.

**Non-interactive mode** (scheduled/headless, no user present): pick `images[0]` from the results and note the auto-selection in the report. Then proceed directly to Step 3.

---

## Step 3 — Download (billed)

⚠️ **Each call licenses the image and spends a stock-image download credit. Only invoke after the user explicitly selects an image (or in non-interactive mode, as noted above).**

**Before calling `download_stock_image`, verify:**
- [ ] User has explicitly named or numbered the image they want (or non-interactive mode is confirmed)
- [ ] This is not an automatic retry of a previous download

**MCP tool:** `download_stock_image` (server: `media-management`)
**Timeout:** 60 seconds

| Parameter   | Required | Default | Notes                                                                                              |
|-------------|----------|---------|----------------------------------------------------------------------------------------------------|
| `assetId`   | ✅ Yes   | —       | Stock asset ID from the prior `search_stock_images` result, max 50 chars                           |
| `size`      | No       | `comp`  | `comp` (web-quality composite, default), `medium_jpg`, `largest` (full res, can exceed 100 MB), max 50 chars |

Use `largest` only when the user explicitly asks for full/original resolution.

**Response fields:**

| Field                        | Notes                                                                 |
|------------------------------|-----------------------------------------------------------------------|
| `assetId`                    | Stock asset ID that was downloaded                                    |
| `managedContentBodyId`       | Salesforce record ID where image bytes are stored                     |
| `parentContentGenAiOutputId` | Record tracking the download request                                  |
| `childContentGenAiOutputId`  | Record linking the request to the persisted body                      |
| `byteCount`                  | Size of the persisted image in bytes                                  |
| `format`                     | Resolved image format (e.g. `jpg`, `png`, `eps`, `tiff`, `svg`)       |
| `url`                        | **URL to the persisted image** — use this to download or preview it   |

---

## Step 4 — Download to Client and Preview

After a successful `download_stock_image` call, download the image locally and open it in VS Code.

**You MUST use `download-stock-image.py` for this. Do not use `curl`, `wget`, or any other tool.**

```bash
python3 scripts/download-stock-image.py \
  --url "<url from download response>" \
  --id "<assetId>" \
  --format "<format from download response>" \
  --preview
```

The script retrieves org credentials via the `sf` CLI (`sf config get target-org` and `sf org auth show-access-token`), downloads the image with `Authorization: Bearer <accessToken>` to `stockimages/<assetId>.<format>`, and opens it in VS Code if `--preview` is passed.

Pass `--output-dir <path>` to override the default `stockimages/` directory.

**Never resize or post-process the image.** Use CSS (`width`, `height`, `object-fit`) to control display dimensions.

**Vector/binary formats (`eps`, `tiff`):** always pass the exact `format` returned by the download response — never substitute a format like `jpg` to work around a validation error; the file bytes won't match the extension and the file will appear corrupted. VS Code cannot render `eps`/`tiff` previews, so the script skips `--preview` for those formats automatically and still reports the saved path.

---

## Step 5 — Report Back

After a successful download, tell the user:
- Image title and artist
- Local path to the saved image (printed by the script)
- `managedContentBodyId`
- Attribution: `{artist} · {collection}` (use the attribution string returned by the download response when present)

---

## Fallbacks & Errors

| Situation                          | Action                                                                       |
|------------------------------------|------------------------------------------------------------------------------|
| `errorMessage` non-null in search  | Show it; suggest a simpler query or different `searchType`/`orientation`     |
| `images` empty                     | Suggest broader keywords; try switching `Editorial` vs `Creative`            |
| `effectiveQuery` differs           | Inform user what the search service actually received                        |
| MCP tools unavailable              | Use the placeholder URL below — do not download or save                      |
| Download fails                     | Show error; do NOT retry automatically — confirm with user first (each retry is billed) |

---

## Placeholder

If `search_stock_images` and `download_stock_image` are both unavailable, use this URL directly as `src` or CSS `url()`:

```text
https://cdn.scs.static.lightning.force.com/content/assets/d5222d4a11e6c2b735152d7eea824ce4/placeholder.svg
```

**Placeholder policy:** There is only one placeholder URL. Do not download it, modify it, or generate alternative placeholders using Python, ImageMagick, or any other tool. If the user asks for a placeholder of a specific size or format, tell them only this URL is available and direct them to use CSS to scale it at the point of use.

© forcedotcom · Apache-2.0 · forcedotcom/sf-skills

Similar skills

All skills for Agent setup: memory, context, orchestration →

From the same set167

experience-ui-bundle-2gp-deploy Turns an existing UI Bundle into a Salesforce second-generation package and installs it in another org. Useful once the bundle is built and you need to ship it. CX Codex +1 2,000installs experience-lwc-design-generate Creates Lightning Web Components from Figma designs or PRD specs. Orchestrates requirements, code generation, SLDS styling, and tests. CX Codex +1 2,000installs experience-lds-graphql-generate This skill builds Salesforce LDS GraphQL queries and mutations from your org's schema and wires them into a Lightning Web Component. Useful when an LWC has to read or edit records over GraphQL instead of wire adapters. CX Codex +1 1,900installs experience-aura-lwc-migrate Analyzes Salesforce Aura bundles and generates a framework-agnostic PRD for migration to LWC. It maps attributes, events, and data needs before coding begins. CX Codex +1 1,900installs service-itsm-incident-priority-configure Configures the Incident Priority Matrix in Salesforce ITSM through the sf CLI: enables or disables it, edits Impact × Urgency cells, sets the fallback priority. CX Codex +1 1,700installs service-itsm-agentic-setup-cmdb-discovery-configure Enables CMDB Asset Discovery in Service Cloud ITSM and assigns the IT Service Discovery Manager permission set to grant access to the Discovery page. CX Codex +1 1,700installs service-itsm-agentic-setup-cmdb-bundle-deploy Installs the CMDB Foundation base bundle in Service Cloud ITSM: out-of-the-box configuration-item types, schema and content. It is the last step of CMDB setup. CX Codex +1 1,700installs service-itsm-agentic-setup-cmdb-configure Enables the CMDB feature in Service Cloud ITSM across sandbox or production orgs. It verifies licenses, provisions the tenant, and clears 403 setup errors. CX Codex +1 1,700installs

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor