Skip to main content

cf-agent

A command-line tool for managing Adobe AEM Content Fragments for the Moveworks Marketplace — connectors and plugins.

Create, edit, publish, and inspect fragments across PROD / STAGE / DEV with an interactive guided mode or scriptable one-liners. Every rule (field types, allowed values, length limits, required fields) is read live from the AEM model, so the CLI always matches what AEM enforces.


Features

  • Interactive guided mode (-i) for creating and editing — pick from lists, see field descriptions, validate as you type.
  • Scriptable one-liners (-f name=value) for automation.
  • Edit without a UUID — find and select a fragment by model + name filter, or by slug.
  • Live validation against the model: required fields, enums, max-length, regex, kebab-case slugs, duplicate-slug detection, and cross-field rules.
  • Content guides from markdown files, with automatic checking that every referenced AEM image exists.
  • Upload assets straight from your machine (asset upload) — one file, or every image in a folder, into the DAM without opening AEM.
  • Missing images offered inline — when a guide references an image that isn't in AEM yet, upload it without leaving the form.
  • Per-fragment image folders — a fragment's guide images are filed under images/<slug>/ instead of one shared folder.
  • Drive it from Claude (cf-agent mcp) — ask for fragments in plain language, with the same validation the CLI enforces.
  • Smart defaults — logo/asset folders auto-prefixed, plugin slugs seeded with the system name, model folders chosen automatically.
  • Guardrails — immutable fields and review-locked fragments are caught up front, not after you fill in a form.
  • Command history — ↑/↓ recall previous entries in interactive prompts.

Table of Contents


Getting started

Five steps, about five minutes. Nothing here touches your system Python or any other project — see Will this affect my other Python projects? below.

1. Create your workspace

cf-agent edits content in AEM, so there is no repository to clone. But you will accumulate local files — content guides, screenshots, a shared preset — and they need somewhere to live. Make a folder for them once:

mkdir -p ~/marketplace-content/guides ~/marketplace-content/images
cd ~/marketplace-content
Folder What goes in it
guides/ Markdown content guides you pass to -f content_guide=...
images/ Screenshots and logos you upload with cf-agent asset upload

Run cf-agent from this folder and the paths in the examples below work as written. The name and location are yours to choose — cf-agent never writes into this folder on its own, and never reads from it unless you name a file.

2. Install cf-agent

Requires Python 3.10+. Install with pipx, which gives the tool an isolated environment of its own and puts the cf-agent command on your PATH:

# macOS
brew install pipx && pipx ensurepath

# Windows / Linux
python3 -m pip install --user pipx && python3 -m pipx ensurepath

Open a new terminal so PATH takes effect, then:

pipx install "cf-agent[mcp]"
cf-agent --help

The [mcp] part adds the server that lets Claude drive cf-agent. Plain pipx install cf-agent gives you the CLI on its own.

There is no environment to activate — cf-agent works in any terminal, from any folder, straight away.

No pipx? Install into a virtual environment instead

On a managed machine where pipx can't be installed, a dedicated virtual environment gives you the same isolation, at the cost of having to activate it each session:

python3 -m venv ~/.venvs/cf-agent
source ~/.venvs/cf-agent/bin/activate
pip install "cf-agent[mcp]"
cf-agent --help

Every new terminal needs source ~/.venvs/cf-agent/bin/activate first.

Do not add that line to ~/.zshrc or ~/.bashrc. It would activate cf-agent's environment in every shell you open, so the next pip install you run for a different project would install into cf-agent's environment instead of that project's — exactly the conflict the virtual environment is there to prevent.

3. Sign in

cf-agent login

Adobe IMS OAuth in the browser. See Authentication for what it prompts for and where credentials are stored.

4. Pick an environment

cf-agent env select

This decides which of PROD / STAGE / DEV every later command acts on. Start on STAGE or DEV while you are finding your feet. See Environments.

5. Check it works

A read-only command that proves the install and the login in one go:

cf-agent models list

You should see the Content Fragment Models — marketplace-connector, marketplace-plugin, and so on. If you do, you are ready to create a fragment.

Will this affect my other Python projects?

No. pipx builds cf-agent a private environment of its own and links only the cf-agent command onto your PATH. Nothing is installed into your system Python, your Homebrew or pyenv Python, or any project's virtual environment, and cf-agent's dependencies can't collide with another project's — even where you both use different versions of the same library.

Everything cf-agent stores outside that environment is in two places:

Where What
~/.cf-agent/ Adobe config and session tokens
OS keychain AWS credentials for asset staging (details)

To remove it completely, leaving no trace in any other project:

cf-agent asset credentials clear   # first, to clear the keychain entry
pipx uninstall cf-agent
rm -rf ~/.cf-agent

To upgrade or reinstall, see Troubleshooting / Debug.


Authentication

Login uses Adobe IMS OAuth (browser-based). Credentials are stored in ~/.cf-agent/config, tokens in ~/.cf-agent/tokens.

cf-agent login          # prompts for Adobe Client ID + Secret, opens browser, then pick an environment
cf-agent whoami         # show the authenticated user, org, scopes, and active environment
cf-agent logout         # clear stored tokens

If your team shares a preset with pre-filled config:

cf-agent login --preset shared.env

Tokens expire; if you see "Not logged in" or a 401, run cf-agent login again.


Environments

One login works across all environments. The active environment is where every fragment operation runs.

cf-agent env list       # show PROD / STAGE / DEV and which is current
cf-agent env select     # switch interactively
cf-agent env current    # show the active environment

Field reference

Field names, types, and rules are read from the live model. These are the fields for each type today.

Connector (marketplace-connector)

Field Required Rules
marketplace_name ✅ Title Case; max 255
slug ✅ lowercase kebab-case; contains the system name; unique; immutable
description ✅ ends with .; max 400
logo ✅ an SVG hosted in AEM; enter the file name only (auto-prefixed to the logos folder)
solution_tags ✅ pick from the list or type a new Title Case tag; multiple allowed
product_family ✅ from the model list (e.g. Google Cloud, Microsoft Graph)
content_guide — markdown guide; supply a file path
video — YouTube / Vimeo / Loom URL

Plugin (marketplace-plugin)

Field Required Rules
marketplace_name ✅ Title Case; must not include the system name; max 255
slug ✅ lowercase kebab-case; must start with a system (e.g. workday-view-pto); unique; immutable
description ✅ ends with .; max 400
availability ✅ from the model (VALIDATED, INSTALLABLE, IDEA, BUILT_IN); immutable after create
installation_asset_uuid ⛔/✅ required when availability = INSTALLABLE, forbidden otherwise; lowercase UUID
solution_tags ✅ pick from the list or type a new Title Case tag; multiple allowed
purple_chat_link ✅ starts with https://marketplace.moveworks.com/purple-chat?conversation; no mock_id; max 30000
systems ✅ from the model list; multiple allowed; immutable
agent_capabilities — from the model list (e.g. Ambient Agent)
content_guide — markdown guide; supply a file path
video — YouTube / Vimeo / Loom URL

Field names come from the live model — the interactive prompts always show the exact current name. installation_asset_uuid was formerly installation_uuid; the CLI accepts both during the transition.

reviewRequired — setting this to true sends the fragment to review, which locks it from further edits until an approver releases it. Leave it off until the content is final.


Creating a fragment

cf-agent fragments create -i

Walks you through it: pick the model, then it fills the folder automatically and prompts each field with its rules. For plugins it asks for systems first and pre-seeds the slug (workday-…); installation_asset_uuid is only asked when availability is INSTALLABLE. Each value is validated as you enter it.

One-liner — Connector

cf-agent fragments create \
  --parent-path "/content/dam/marketplace/content-fragment-resources/connector" \
  --model-path  "/conf/marketplace/settings/dam/cfm/models/marketplace-connector" \
  --name  "google-drive-connector" \
  --title "Google Drive Connector" \
  -f marketplace_name="Google Drive Connector" \
  -f slug="google-drive-connector" \
  -f description="Connects to Google Drive to search and retrieve files." \
  -f logo="google-drive.svg" \
  -f solution_tags="IT,Productivity" \
  -f product_family="Google Workspace" \
  -f content_guide=guides/connector-guide.md

One-liner — Plugin

cf-agent fragments create \
  --parent-path "/content/dam/marketplace/content-fragment-resources/plugin" \
  --model-path  "/conf/marketplace/settings/dam/cfm/models/marketplace-plugin" \
  --name  "workday-view-pto-balance" \
  --title "View PTO Balance" \
  -f marketplace_name="View PTO Balance" \
  -f slug="workday-view-pto-balance" \
  -f description="Check your current PTO balance from the Moveworks AI Assistant." \
  -f availability="VALIDATED" \
  -f solution_tags="HR - Time & Absence,HR - Employee Records" \
  -f purple_chat_link="https://marketplace.moveworks.com/purple-chat?conversation=%7B%22messages%22%3A%5B%5D%7D" \
  -f systems="workday" \
  -f agent_capabilities="Ambient Agent" \
  -f content_guide=guides/plugin-guide.md

For an INSTALLABLE plugin, use -f availability="INSTALLABLE" and add -f installation_asset_uuid="34cff60f-f3c8-48f9-b1c9-7658ead0d994".

Multi-value fields are comma-separated inside one flag: -f solution_tags="HR - Benefits,IT".


Updating a fragment

You can identify the fragment three ways — no UUID required.

cf-agent fragments update -i
  • Pick the model (connector / plugin).
  • Type a filter (name or slug) — or Enter to list all.
  • Choose from the numbered results.
  • Edit each editable field; press Enter to skip (keep the current value). Enums show their pick-list, exactly like create.

2. By slug (scriptable)

cf-agent fragments update --slug google-drive-connector \
  --model-path "/conf/marketplace/settings/dam/cfm/models/marketplace-connector" \
  -f content_guide=guides/updated-guide.md

3. By id

cf-agent fragments update <id> -f description="Updated description."

Which fields are editable

Only these fields can be changed on update — everything else is locked.

Model Editable fields
Connector logo, content_guide
Plugin marketplace_name, description, purple_chat_link, solution_tags, installation_asset_uuid, content_guide

Locked (cannot be changed after creation): slug, systems, availability. Attempting to change one — or to edit any non-listed field — is rejected with a clear message.

Review-locked fragments: if a fragment was sent to review (reviewRequired = true), the CLI tells you immediately on selection that it's locked and can't be edited until the review is completed or cancelled in AEM — so you don't fill in a form only to be refused at the end.


Content guides & images

content_guide is a markdown guide supplied as a file path (both create and update):

-f content_guide=guides/my-guide.md

Relative paths resolve against the folder you run cf-agent from, so these examples assume the workspace from Getting started. An absolute path works from anywhere.

When you provide the file, the CLI reads it and verifies every AEM image it references exists. It checks /content/dam/... paths in markdown images ![](…), links [](…), and HTML <img src="…"> (full AEM URLs too). If any referenced image is missing, the operation is blocked and the missing paths are listed.

Missing images are offered inline. In interactive mode, if the guide references an image that isn't in the DAM yet, the CLI asks for the local file and uploads it without you leaving the form:

  The markdown references AEM asset(s) not found in the DAM:
    - /content/dam/marketplace/images/shot.png

  Missing: /content/dam/marketplace/images/shot.png
  Will upload to /content/dam/marketplace/images/workday/shot.png
  Upload a local file to create shot.png? [y/N]: y
  Local file path: images/shot.png
  ✓ Uploaded: /content/dam/marketplace/images/workday/shot.png

When creating a fragment, images are filed under the fragment's own slug folder and the guide is repointed to match — so you can keep writing whatever path suits you and the stored guide always points at the file that was actually created. Uploading several? Do them in one go first with cf-agent asset upload <folder> --slug <slug>, then the guide validates cleanly.

This needs AWS credentials (one-time setup). Without them the CLI explains the setup instead of offering. You can always upload separately or by hand in AEM and reference the /content/dam/... path yourself.

Relative image paths from a raw export (e.g. ![](image.png)) are not picked up — reference images by DAM path.


Other fragment commands

# List (optionally by folder)
cf-agent fragments list --path /content/dam/marketplace/content-fragment-resources/connector --limit 25

# Get one by id
cf-agent fragments get <id>

# Dry-run validate a payload against the model — no write
cf-agent fragments validate --model-path "$CONN_M" -f description="A connector." --partial

# Publish one or more
cf-agent fragments publish <id> [<id> ...]

# Delete (‑‑yes to skip the prompt)
cf-agent fragments delete <id> --yes

# Copy to another folder (‑‑deep to include references)
cf-agent fragments copy <id> --destination /content/dam/.../archive [--deep]

# List variations
cf-agent fragments variations <id>

cf-agent fragments search exists but the underlying AEM search endpoint is not available on all environments — prefer fragments list or the update -i filter to find fragments.


Models & assets

# List Content Fragment Models (connector, plugin, …)
cf-agent models list

# Check whether an asset exists in the DAM (bare name resolved against a known folder)
cf-agent asset exists workday.svg --logo
cf-agent asset exists /content/dam/marketplace/logos/workday.svg

asset exists returns exit code 0 if present, 1 if not — usable in scripts. It requires the aem.assets.author scope.


Uploading assets

asset upload puts a local file into the AEM DAM, so logos and content-guide images no longer have to be uploaded by hand in AEM.

One-time setup

Store your AWS credentials once — they are used to stage the file (see How it works below). Ask the team for a key; it will be sent to you securely.

cf-agent asset credentials set

You are prompted for both values. The secret is hidden as you paste it, so it never lands in your shell history.

cf-agent asset credentials show    # confirm they're stored (never prints the secret)
cf-agent asset credentials clear   # remove them

Credentials are kept in your OS keychain — macOS Keychain, Windows Credential Manager, or the Linux Secret Service — encrypted at rest, never in a plaintext file and never in the repo. Nothing else needs configuring: the staging bucket, region and prefix ship with the CLI.

Upload one file

# Into the marketplace logos folder
cf-agent asset upload ./workday.svg --logo

# Into a fragment's own image folder — /content/dam/marketplace/images/<slug>/
cf-agent asset upload ./screenshot.png --slug workday

# Into the shared images folder
cf-agent asset upload ./screenshot.png --image

# Into any DAM folder
cf-agent asset upload ./diagram.png --root /content/dam/marketplace/screenshots

# Rename on the way in
cf-agent asset upload ./Untitled_204.png --slug workday --name workflow-diagram.png

On success it prints the DAM path and assetId:

✓ Uploaded: /content/dam/marketplace/logos/workday.svg
  assetId: urn:aaid:aem:d3fc8f49-4c8f-49ee-b01f-3f00999201b0

Use that /content/dam/... path directly as a logo value or in content-guide markdown. Add --json for scripting.

Upload a whole folder

Point at a folder instead of a file and every image inside it is uploaded. Guide screenshots usually come out of an export tool together, so this is the quickest way to seed a fragment's images:

cf-agent asset upload images/workday --slug workday
Uploading 3 image(s) to /content/dam/marketplace/images/workday
  · shot-1.png
  · shot-2.png
  · shot-3.png

✓ 3 uploaded to /content/dam/marketplace/images/workday
  • Top level only — nested folders are not walked.
  • Images only — .png .jpg .jpeg .gif .svg .webp .bmp .tif .tiff. A stray notes.txt or .DS_Store is ignored.
  • Already-uploaded files are skipped, so re-running after a partial upload sends only what's missing. Use --overwrite to replace them.
  • One bad file doesn't stop the batch — it's reported and the rest continue; the command exits non-zero if anything failed.

--name applies to a single file only, since one name can't cover many files.

Where images live

Each fragment's content-guide images belong in its own folder, named for the slug:

/content/dam/marketplace/images/<slug>/

The folder is created automatically the first time you upload into it. Logos stay in the shared logos folder.

Fragments created before this convention have their images in the flat images folder. Those still work — nothing was moved.

How it works

AEM's Assets API cannot accept a binary directly from your user token — it can only pull an asset from a URL. So the CLI uploads the file to an S3 staging bucket, hands AEM a short-lived pre-signed URL to fetch it from, waits for the import to finish, then deletes the staged copy. This is why AWS credentials are needed at all; the staged object is temporary and removed automatically.

Credential precedence: AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY environment variables win if set (useful in CI), then the OS keychain, then any ~/.aws profile. Set nothing and the keychain is used.


Using cf-agent from Claude

cf-agent mcp runs an MCP server, so Claude Desktop (or any MCP client) can carry out these operations for you — listing fragments, checking fields, creating and updating content, uploading assets — from a plain-language request.

It ships with the [mcp] extra, so if you installed with pipx install "cf-agent[mcp]" in Getting started you already have it. If you installed plain cf-agent, add the extra now:

pipx install --force "cf-agent[mcp]"

Then add it to your Claude Desktop config — Settings → Developer → Edit Config, or edit the file directly:

macOS ~/Library/Application Support/Claude/claude_desktop_config.json
Windows %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "cf-agent": {
      "command": "/Users/you/.local/bin/cf-agent",
      "args": ["mcp"]
    }
  }
}

Use the absolute path — run which cf-agent (macOS) or where cf-agent (Windows) to get it. With pipx this is normally ~/.local/bin/cf-agent, spelled out in full. Claude Desktop doesn't inherit your shell PATH, so a bare cf-agent will not resolve. On Windows, double the backslashes in JSON. Then quit Claude Desktop completely (⌘Q, or Quit from the Windows tray) and reopen.

What it can do: read operations (list_fragments, get_fragment, search_fragments, list_models, get_model_schema, list_variations, asset_exists) and write operations (create_fragment, update_fragment, copy_fragment, upload_asset).

What it can't: deleting and publishing are deliberately excluded — both are permanent or externally visible, so they stay a deliberate action at the command line.

Everything written is validated against the live AEM model first, exactly as the CLI does, so an agent can't create a fragment the CLI would have rejected.

Your Adobe sign-in lasts about a day. When it lapses every request reports an expired token — run cf-agent login in a terminal; Claude Desktop can't open the browser sign-in for you.


Troubleshooting / Debug

Update the CLI to the latest version

pipx upgrade cf-agent
cf-agent --help        # confirm it still runs

pipx upgrade keeps the [mcp] extra you installed with.

Uninstall & reinstall

pipx uninstall cf-agent
pipx install "cf-agent[mcp]"

pipx install --force "cf-agent[mcp]" does both in one step, rebuilding the environment from scratch — that is the clean slate, since the environment pipx manages is thrown away with it.

Uninstalling does not remove your login. Config and tokens live in ~/.cf-agent/ — delete that folder to fully reset (rm -rf ~/.cf-agent), then cf-agent login again. See Will this affect my other Python projects? for everything cf-agent stores outside its own environment.

Using the virtual-environment fallback instead of pipx?
source ~/.venvs/cf-agent/bin/activate

# update
pip install --upgrade "cf-agent[mcp]"

# uninstall
pip uninstall cf-agent

For a clean slate, deactivate, delete ~/.venvs/cf-agent, then recreate it and reinstall as in Getting started.

Common errors

Message Cause & fix
Not logged in / 401 Token expired → cf-agent login.
403 Forbidden — You are not allowed to modify this fragment The fragment is review-locked (reviewRequired = true) or your Adobe ID lacks write access on this environment. Release the review in AEM, or ask an admin for access.
… is in review and locked Sent to review — complete/cancel the review in AEM, then retry.
Field 'X' is not editable on update Only the editable fields above can change on update.
Field 'X' cannot be changed after creation slug / systems / availability are immutable.
Slug '…' is already in use Choose a unique slug.
Referenced asset does not exist in AEM The logo / a content-guide image isn't in the DAM — upload it with cf-agent asset upload … --logo / --image, or check with cf-agent asset exists ….
cf-agent: command not found pipx's bin folder isn't on PATH → run pipx ensurepath and open a new terminal. On the venv fallback, activate it first: source ~/.venvs/cf-agent/bin/activate.
Asset upload requires boto3 / requires 'keyring' Your install predates 1.1.0, when these became base dependencies → pipx upgrade cf-agent.
Could not stage file to S3: access denied The AWS key lacks permission on the staging bucket, or none is set → cf-agent asset credentials show.
The staged file isn't readable via its pre-signed URL The AWS key can write but not read the staging bucket — it needs s3:GetObject too. Ask the team for a corrected key.
No image files found directly in … The folder has no recognised images at its top level — nested folders aren't walked.
--name applies to a single file, not a folder Drop --name; one name can't cover a batch.
Cannot resolve folder … The destination DAM folder doesn't exist. --slug creates it for you; --root does not.
No cf-agent tools in Claude Desktop Quit the app fully (⌘Q, or Quit from the Windows tray) and reopen. Check the config is valid JSON and the command path is absolute.

Inspect your session

cf-agent whoami        # user, org, scopes, token expiry, active environment
cf-agent env current   # which environment you're pointed at

whoami is the fastest way to debug 403s — confirm the token's org matches the environment.

Metadata

Release files for cf-agent 1.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cf-agent 1.3.2
File Size Uploaded
cf_agent-1.3.2.tar.gz 60.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cf-agent 1.3.2
File Interpreter ABI Platform
cf_agent-1.3.2-py3-none-any.whl Python 3 none any Details

Total release size: 124.1 kB

Release files / cf_agent-1.3.2.tar.gz

Download URL cf_agent-1.3.2.tar.gz
Size 60.8 kB
Tags Source
SHA-256 checksum
How to use checksums
749a10333c540b21a584a0dec8df41dd2215e226b409ae82f4d37508c87014a7
BLAKE2b-256 checksum
How to use checksums
13ae4de2387c3b56e77058a0972f700bcded73a496c4e7e2333a295f0a3c6c42
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release files / cf_agent-1.3.2-py3-none-any.whl

Download URL cf_agent-1.3.2-py3-none-any.whl
Size 63.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cfff7cd191164b75f170dc409592af45e1ff4d6052e3030c009b3536a4628855
BLAKE2b-256 checksum
How to use checksums
9d43caa1ed56a4f52113505c033be4e58c01441748536400bb057decd7fb005d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.15

Release history Release notifications | RSS feed

1.3.3

2 release files

This release

1.3.2 This release

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page