Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
# Adobe for creativity
/plugins/creative-cloud/adobe-for-creativity @bossjones @njampani123 @solimant

# Run Workflow (Adobe Firefly workflows via MCP)
/plugins/creative-cloud/run-workflow @thagarwal-sjc

# Stardust
/plugins/stardust @paolomoz

Expand Down
12 changes: 12 additions & 0 deletions plugins/creative-cloud/run-workflow/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "run-workflow",
"description": "Discover, compose, run, publish, and save Adobe Firefly workflows through the run-workflow MCP server, including predefined featured workflows for common marketing and production use cases.",
"version": "1.0.0",
"author": {
"name": "Adobe"
},
"repository": "https://github.com/adobe/skills",
"license": "Apache-2.0",
"keywords": ["adobe", "firefly", "run-workflow", "mcp", "creative-cloud"],
"skills": "./skills"
}
8 changes: 8 additions & 0 deletions plugins/creative-cloud/run-workflow/.mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"mcpServers": {
"run-workflow": {
"type": "http",
"url": "https://run-workflow.adobe.io/mcp"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "adobe/run-workflow",
"version": "1.0.0",
"description": "Discover, compose, run, publish, and save Adobe Firefly workflows through the run-workflow MCP server.",
"skills": [
"skills/run-workflow",
"skills/run-featured-workflow"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"context": "Tests the run-workflow skill's BARE ID rule: when the user's entire message is just an ID with no verb, the agent must inspect it, not rerun or re-execute it. This guards against a specific failure mode where pasting an ID (e.g. to check on a run) accidentally triggers a brand-new, possibly costly, workflow execution.",
"type": "weighted_checklist",
"checklist": [
{
"name": "No clarifying question needed",
"max_score": 10,
"description": "The agent did not ask the user what they meant by the ID before acting — the bare-ID rule resolves the intent unambiguously without needing to ask."
},
{
"name": "Calls inspect_run",
"max_score": 35,
"description": "The agent called inspect_run with the pasted ID as its tool call for this turn."
},
{
"name": "Presents inspection results",
"max_score": 15,
"description": "The agent presented the inspection results (executions, outputs, or diagnostics) back to the user in its response."
},
{
"name": "No run_workflow call",
"max_score": 30,
"description": "The agent did NOT call run_workflow or run_workflow_submit at any point in this turn."
},
{
"name": "No definition reuse",
"max_score": 10,
"description": "The agent did NOT extract actions/connections from the inspect_run result and pass them into a new run."
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Eval: bare ID is inspect-only, never a rerun

## Setup

The run-workflow MCP server is connected and authenticated. No prior
conversation context about workflows, runs, or IDs exists.

## User prompt

"a1b2c3d4-e5f6-4789-a012-3456789abcde"

(A single message containing nothing but a bare UUID — no verb, no
other words.)

## Expected behavior

1. The agent recognizes this as a bare ID with no execution intent —
none of the required trigger words ("run", "execute", "process",
"generate", "rerun", "redo") are present.
2. It calls `inspect_run` with the ID immediately, without asking a
clarifying question first.
3. It presents the inspection results (executions, outputs,
diagnostics) back to the user.
4. It does **not** call `run_workflow_submit` or `run_workflow` as a
follow-up action.
5. It does **not** extract `actions`/`connections` from the
`inspect_run` result and pass them into a new run — even if the
inspected run's definition blob looks reusable.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"context": "Tests that capability/discovery questions about run-workflow ('what can you do', 'what actions are available') are always grounded in a live list_actions call rather than answered from training knowledge or a stale cached description — the catalog changes over time and a memorized answer would drift from reality.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Calls list_actions",
"max_score": 50,
"description": "The agent called list_actions before giving any substantive answer about run-workflow's capabilities."
},
{
"name": "Answer grounded in tool result",
"max_score": 30,
"description": "The agent's answer reflects the actual data returned by list_actions rather than a generic or memorized description of the catalog."
},
{
"name": "No unrelated tool calls",
"max_score": 20,
"description": "The agent did not call run_workflow_submit, compose_workflow, or inspect_run for this discovery-only request."
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Eval: capability questions are answered by calling list_actions

## Setup

The run-workflow MCP server is connected and authenticated. No prior
conversation context exists.

## User prompt

"What actions does run-workflow support? What can you do with it?"

## Expected behavior

1. The agent treats this as a capability/discovery question and calls
`list_actions` before answering — it does not answer from training
knowledge or a cached/remembered description of the catalog.
2. The response to the user is grounded in the actual tool result
(names/categories of actions returned by the call), not a generic
or invented list.
3. The agent does not call `run_workflow_submit`, `compose_workflow`,
or `inspect_run` for this turn — the request is purely a discovery
question, not a request to build or run anything.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"context": "Tests that naming a known featured workflow (e.g. retargeting) routes through get_featured_workflow's fast path instead of composing a graph from scratch via compose_workflow. Skipping this check wastes a compose step and risks producing a worse, non-template graph when a vetted one already exists.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Calls get_featured_workflow first",
"max_score": 35,
"description": "The agent called get_featured_workflow (with a query reflecting 'retargeting') before any call to compose_workflow."
},
{
"name": "No premature compose_workflow",
"max_score": 25,
"description": "The agent did not call compose_workflow when a named featured workflow was requested and a match was available, skipping the from-scratch graph-building path."
},
{
"name": "Uploads local assets correctly",
"max_score": 15,
"description": "The agent called upload_asset for the local product photos and used the returned presigned URLs verbatim (no truncation or reconstruction) in the submission."
},
{
"name": "Uses session_id submission",
"max_score": 15,
"description": "When a single prepared match was returned, the agent submitted via run_workflow_submit using session_id and useSessionDefaults rather than re-sending the full actions/connections graph."
},
{
"name": "Reports output or status",
"max_score": 10,
"description": "The agent reported back batch/run status or output URLs to the user rather than ending the turn silently after submission."
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Eval: named featured workflow routes through get_featured_workflow, not compose_workflow

## Setup

The run-workflow MCP server is connected and authenticated. The user
has local product photos at `~/Desktop/products/`.

## User prompt

"Run the retargeting workflow using my product photos in
~/Desktop/products."

## Expected behavior

1. The agent recognizes "retargeting" as a named featured workflow
and calls `get_featured_workflow` (e.g. with a query like
"retargeting") **before** calling `compose_workflow`.
2. It does not call `compose_workflow` to build a graph from scratch
for this request — a named template match takes the fast path.
3. It uploads the local product photos via `upload_asset` to obtain
presigned URLs, and copies those URLs verbatim into the submission
(does not truncate or reconstruct them).
4. If `get_featured_workflow` returns a single `prepared` match, the
agent uses `prepared.session_id` and submits via
`run_workflow_submit` with `session_id` + `useSessionDefaults`
rather than re-serializing the full graph.
5. If `get_featured_workflow` returns no match (e.g. because the tool
is mocked/unavailable in this environment) the agent may fall back
to `compose_workflow`, but only after having called
`get_featured_workflow` first.

## Known limitation

This scenario requires a live, authenticated call to the hosted
`run-workflow.adobe.io/mcp` server. Without a valid IMS credential
injected into the eval sandbox (e.g. via `tessl eval run --env-file`),
tool calls will 403 and the with-context run may score lower than
baseline — not because the skill's guidance is wrong, but because the
agent correctly attempts real tool calls that the sandbox can't
authenticate, while an unguided baseline tends to "solve" the task by
writing a self-contained mock implementation instead of calling any
real tool at all. Treat a low with-context score here as inconclusive
unless a valid token was provided for the run.
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
name: run-featured-workflow
description: >-
Run predefined featured workflows via run-workflow MCP. TRIGGER when user names
a featured workflow (retargeting, banners at scale, localization, packaging,
banner advertising, etc.) or asks to run a known marketing/production workflow.
Requires run-workflow MCP. ALWAYS call get_featured_workflow before compose_workflow.
DO NOT TRIGGER for custom one-off workflows with no named template — use run-workflow skill.
license: Apache-2.0
metadata:
version: 1.0.0
visibility: public
---

# Featured Workflows (run-workflow MCP)

Run pre-built marketing and production workflows bundled with the MCP server. Never compose from scratch when `get_featured_workflow` finds a match.

## When to use

**TRIGGER when the user:**
- Names a featured workflow: retargeting, localization, banner advertising, packaging, product banners at scale, catalogs, packshots, spin video, etc.
- Asks to run a "known" or "predefined" workflow by name or keyword
- Provides product images + fonts for a banner/merge-data workflow with defaults

**DO NOT TRIGGER when:**
- User wants a custom workflow with no named match — use [run-workflow](../run-workflow/SKILL.md) and `compose_workflow`
- User pastes only a batch/execution/workflow ID — use `inspect_run` via run-workflow skill

## Tool routing

| Step | Tool |
|------|------|
| Find predefined workflow | `get_featured_workflow` |
| Upload images, fonts, custom templates, CSV data | `upload_asset` |
| Extract merge tags from custom `.indd` | `get_indesign_tags` |
| Submit (large / multi-banner) | `run_workflow_submit` |
| Poll progress and outputs | `run_workflow_get_status` (always `includeOutputs: true`) |
| Re-wire graph after tag diff | `compose_workflow` (custom templates only) |
| Diagnose failure | `inspect_run` |

## Discovery flow

1. Call `get_featured_workflow({ query: "<keyword>" })`.
2. **Multiple matches** — `prepared` is absent. List matched names, ask user to pick, retry with exact name.
3. **Single match** — present `mergeNodes`, required inputs, sizes. Confirm with user.
4. Ask about templates if not specified (defaults vs custom `.indd`).
5. Ask about fonts if user has `.otf`/`.ttf` files — upload via `upload_asset`.
6. If the user supplies a `.csv` (data-merge workflows like `product-banners-at-scale`), upload it and wire it to the **`input-files` node** (`type: "file"` in `prepared.inputNodes`) — see Data/CSV inputs below.

**Distinct asset types route to distinct input nodes:** images → `input-images`, fonts → `defaults.fonts`, CSV/data → `input-files` (which feeds `parse-data`). Never collapse them onto one node.

## Fast path (defaults)

When user wants default templates **and** default text:

1. Upload product images and fonts via `upload_asset`.
2. Call `run_workflow_submit`:

```json
{
"session_id": "<prepared.session_id>",
"useSessionDefaults": true,
"defaults": {
"fonts": [{ "presignedUrl": "...", "name": "Font.otf", "storageType": "azure" }]
},
"inputs": [
{
"node_id": "<input-images id from prepared.inputNodes>",
"content": [{ "type": "image", "url": "..." }]
}
]
}
```

- Do **not** call `compose_workflow`.
- Do **not** send `actions`/`connections` when `session_id` is present.
- Pass **all** images in one `content` array — one submit call.

## Data / CSV inputs

Some featured workflows (e.g. `product-banners-at-scale`) have a CSV branch: `input-files` → `parse-data` → `merge-data`. `prepared.inputNodes` surfaces the CSV node with `type: "file"`, and `prepared.inputs` carries the workflow's baked **sample CSV** as inline content.

- **Defaults only (no user CSV):** submit as-is — the baked sample CSV flows through, no `input-files` entry needed.
- **User-supplied CSV:** upload it via `upload_asset`, then add an `inputs[]` entry for the `input-files` node id so it overrides the baked default:

```json
{
"node_id": "<input-files id from prepared.inputNodes>",
"content": [{ "url": "<uploaded csv url>", "mimeType": "text/csv" }]
}
```

The CSV columns drive the `parse-data` fan-out (e.g. `headline`, `subcopy`, `CTA`) — do not also wire those as separate text inputs. A product image that's constant across every row (not a per-row CSV column) wires `input-images` **directly** into `merge-data`, bypassing `parse-data` entirely — this is the privileged default; only route an image through `parse-data`'s media-override port when it genuinely varies per row.

## Custom template path

When user provides custom `.indd` templates:

1. `get_indesign_tags` on each template.
2. Compare to `prepared.mergeNodes[].tags`:
- **Identical** → swap template URLs only; skip compose.
- **Different** → feasibility check, then optional `compose_workflow` re-wire.
3. If a critical image tag was removed (e.g. `background` in retargeting), stop — offer defaults, different template, or build from scratch.
4. Show updated graph visually. Prompt for missing merge-data inputs only.
5. Submit after user confirms all values.

**Merge-data vs scene prompts:** Only InDesign merge-port tags need user-facing text/image inputs. Keep `gen-object-composite` scene prompts at their defaults unless user asks to change them.

## Polling and outputs

After `run_workflow_submit`, poll `run_workflow_get_status` every ~5 seconds with `includeOutputs: true`.

- Report: `Running — N/M complete (X%) · Ns elapsed` using `elapsedSeconds` from response.
- On completion: list every output's **full presigned URL** verbatim. Never truncate.
- On failure: `inspect_run`, summarize, stop — do not auto-retry.

## Known featured workflows

| Slug / keyword | Use case |
|----------------|----------|
| `product-banners-at-scale` | Product banners from a CSV (data merge), one template + constant image, per-row copy |
| `retargeting` | Retargeting ads |
| `localization` | Localization |
| `generic-banner-advertising` | Generic banners |
| `contextual-or-native-advertising` | Native ads |
| `catalogs-and-circulars` | Catalogs |
| `promotional-pricing-or-discounting` | Pricing promos |
| `packaging-variants-2d` | Packaging |
| `standardized-packshots-2d` / `3d` | Packshots |
| `refreshed-product-imagery-2d` | Product imagery refresh |
| `composite-imagery-3d` | 3D composite |
| `3d-spin-video` | 360 spin video |

## Anti-patterns

- Calling `compose_workflow` when featured workflow exists and tags match
- Sending full graph JSON when `session_id` is available
- Repeating `fonts` on every merge-data input — use `defaults.fonts`
- Sending all input entries when `useSessionDefaults: true` suffices
- Separate submit per image
- Skipping user confirmation on missing merge-data text fields

## References

- Full run-workflow skill (asset input, publish/save, alert RCA): [run-workflow/SKILL.md](../run-workflow/SKILL.md)
Loading
Loading