Cloud CLI for AI Agents

This guide is written for AI agents that manage Medusa Cloud resources through the Cloud CLI. It covers best practices, working principles, and common workflows for using the CLI in an agentic development context.

If you're a human user, copy the following instructions to your agent to get started:

Prompt
Fetch https://docs.medusajs.com/cloud/cli/agents and set up the Medusa Cloud CLI.

You can also install the instructions in this document as a skill in your agent:

Working Principles#

AI agents should follow these defaults whenever they operate the CLI:

  • Always pass --json when parsing output. The plaintext output is for human readers and may change without warning; machine-readable JSON is stable and structured.
  • Confirm context before acting. Run mcloud whoami before any state change so you know which organization, project, and environment you'll affect.
  • Set context once, not per command. Use mcloud use to persist organization, project, and environment, then drop the flags from subsequent commands.
  • Read before you write. Run a get or list before any delete, redeploy, or trigger-build to verify you're targeting the right resource.
  • Use --yes for irreversible operations. mcloud projects delete and mcloud environments delete require confirmation in interactive mode and refuse to run without --yes in non-interactive mode.
  • Production environments cannot be deleted. mcloud environments delete errors on production environments by design. Do not attempt to bypass it.
  • Pipe to jq for extraction. Avoid string-parsing JSON; pipe --json output into jq for structured access.

Set Up the CLI#

AI agents should run this one-time setup before any other CLI work, then never repeat it on the same machine. Skip the steps whose checks already pass.

1. Check Whether the CLI Is Already Installed#

Before doing anything else, check whether the CLI is already on the system:

Terminal
mcloud --version

If the command exits with 0 and prints a version string, the CLI is installed. Skip the rest of this section and move on to Confirm Authentication and Context. If the command is not found, continue to the next step.

2. Verify Node.js Version#

The CLI requires Node.js v22 or later. Check the installed version:

Terminal
node --version

If the version is below v22, ask the user to upgrade Node.js (for example, via nvm or the official installer) and rerun the check before continuing. Do not attempt to upgrade Node.js without authorization.

3. Install the Cloud CLI#

Install @medusajs/mcloud globally with the user's preferred package manager:

Verify the install:

Terminal
mcloud --version

A successful version string confirms the CLI is on PATH. If the command is not found, ask the user to check their global npm bin directory is on PATH or to reinstall with a different package manager.

4. Log In or Sign Up#

Ask the user whether they already have a Medusa Cloud account.

If yes, run mcloud login to authenticate. The CLI opens a browser to complete the flow:

Terminal
mcloud login

If no, run mcloud signup to open the sign-up page. After the user creates an account, run mcloud login:

Terminal
mcloud signupmcloud login

For non-interactive environments (CI, headless containers, restricted shells), skip the browser flow entirely and use an access key instead:

Terminal
export MCLOUD_TOKEN=<access-key>

When MCLOUD_TOKEN is set, the CLI uses it on every command and mcloud login is rejected. See the login command reference for personal vs organization access keys.

5. Confirm the Setup#

Run mcloud whoami to verify authentication, then move on to setting the active context:

Terminal
mcloud whoami --json

Confirm Authentication and Context#

Before running any command that mutates state, AI agents should verify the active identity and scope:

Terminal
mcloud whoami --json

The response includes the auth kind, expiry (for JWT-based auth), and the active organization, project, and environment. Inspect auth.kind and organization.id to assert the CLI is ready to use:

Terminal
mcloud whoami --json | jq -e '.auth.kind != "none" and .organization.id != null'

Exit code 0 means authenticated and scoped; on a non-zero exit, stop and ask the user to authenticate or set the active context.

Authentication precedence: If MCLOUD_TOKEN is set, it overrides any credentials stored on disk and mcloud login is rejected. To switch accounts, unset the variable first.

Set the Active Context Programmatically#

AI agents should persist the active organization, project, and environment so subsequent commands can run without --organization, --project, or --environment flags. Pass values directly when running non-interactively:

Terminal
mcloud use \  --organization org_123 \  --project proj_123 \  --environment env_123

If you only have names instead of IDs or handles, look them up first:

Terminal
# Resolve organization ID by nameORGANIZATION_ID=$(  mcloud organizations list --json \    | jq -r '.[] | select(.name == "My Organization") | .id')
# Resolve project handle by namePROJECT_HANDLE=$(  mcloud projects list --organization "$ORGANIZATION_ID" --json \    | jq -r '.[] | select(.name == "My Store") | .handle')
# Resolve environment handle by nameENVIRONMENT_HANDLE=$(  mcloud environments list --organization "$ORGANIZATION_ID" --project "$PROJECT_HANDLE" --json \    | jq -r '.[] | select(.name == "Production") | .handle')
mcloud use \  --organization "$ORGANIZATION_ID" \  --project "$PROJECT_HANDLE" \  --environment "$ENVIRONMENT_HANDLE"

To clear the active context (for example, before switching to a different account or organization), pass --clear:

Terminal
mcloud use --clear
Warning: mcloud use without flags is interactive (it requires a TTY) and will not work inside CI runners, Docker RUN steps, or piped input. Always pass flags instead of relying on the interactive picker.

Inspect Deployments#

The deployment table returned by mcloud deployments list carries two computed status fields per row that AI agents should route on:

  • backend_status: lifecycle of the backend (build + rollout)
  • storefront_status: lifecycle of the storefront

Both fields use the same value set: created, building, built, deploying, deployed, build-failed, deployment-failed, timed-out (backend only), canceled, and idle. See the full reference in the deployments command guide.

Use --json and jq to filter for the relevant deployments:

Terminal
# Most recent failed deployment for the active environmentmcloud deployments list --json \  | jq -r '[.[] | select(.backend_status == "build-failed" or .backend_status == "deployment-failed")][0].id'
# All deployments for a specific commitmcloud deployments list --commit a1b2c3d --json | jq '.'
# Only preview deploymentsmcloud deployments list --environment-type preview --json | jq '.'

For one specific deployment, use mcloud deployments get:

Terminal
mcloud deployments get bld_01ABC123 --json

Debug a Failed Deployment#

The right log source depends on the failure mode. AI agents should route on backend_status (or storefront_status):

backend_status

What failed

Where to look

build-failed

Build step (Docker, dependencies, compilation).

mcloud deployments build-logs

deployment-failed

Rollout step (the build succeeded but the runtime crashed).

mcloud logs --deployment <id>

timed-out

Build or rollout exceeded the time budget.

Both: build-logs first, then runtime logs.

Build Failure Recipe#

Terminal
# Find the most recent build-failed deploymentDEPLOYMENT_ID=$(  mcloud deployments list --json \    | jq -r '[.[] | select(.backend_status == "build-failed")][0].id')
# Inspect deployment metadata (commit, environment, timestamps)mcloud deployments get "$DEPLOYMENT_ID" --json
# Read the build outputmcloud deployments build-logs "$DEPLOYMENT_ID"
# For storefront build failures, pass --type storefrontmcloud deployments build-logs "$DEPLOYMENT_ID" --type storefront

build-logs returns a build_status field (created, in-progress, succeeded, failed, timed-out, canceled). When failed, check the build's metadata.failed_docker_layer (visible via mcloud deployments get --json) to identify the failing layer.

Deployment Failure Recipe#

When the build succeeded but the rollout failed, the build logs won't help. Read the runtime logs scoped to that deployment instead. The --deployment flag accepts both deployment IDs (depl_...) and build IDs (anything else); the CLI resolves a build ID to its latest deployment automatically.

Terminal
# Find the most recent deployment-failed deploymentDEPLOYMENT_ID=$(  mcloud deployments list --json \    | jq -r '[.[] | select(.backend_status == "deployment-failed")][0].id')
# Read the runtime logs for that specific deploymentmcloud logs --deployment "$DEPLOYMENT_ID" --limit 1000
# Filter to error-level lines onlymcloud logs --deployment "$DEPLOYMENT_ID" --search error --limit 1000
# Filter by HTTP status (5xx)mcloud logs --deployment "$DEPLOYMENT_ID" --metadata status=500 --limit 1000
# Machine-readable: pipe to jq for structured analysismcloud logs --deployment "$DEPLOYMENT_ID" --json | jq '.[] | {timestamp, source, message}'
Note: --follow cannot be combined with --json. To process logs in a script, use a bounded time window with --from/--to and --json.

Rerun a Deployment#

After fixing the underlying issue, AI agents have two options. They are not interchangeable:

Command

What it does

When to use

mcloud environments redeploy <env>

Re-runs the active deployment's existing build.

The fix is environment-side (variable change, infra issue) and the build itself is fine.

mcloud environments trigger-build <env>

Starts a new build from the tracked branch.

The fix is in the source code that's already on the tracked branch.

redeploy requires the environment to have an active deployment. If it doesn't, run trigger-build first to create one.

Terminal
# After fixing an environment variablemcloud environments redeploy env_123
# After pushing a fix to the tracked branchmcloud environments trigger-build env_123
# Verify the new build is runningmcloud deployments list --environment env_123 --limit 5 --json \  | jq '.[] | {id, backend_status, commit_hash, updated_at}'

Manage Environment Variables#

Environment variables are scoped to a single environment. AI agents should pass --reveal to print plaintext secret values only when the user has explicitly asked to see them.

Terminal
# List all variables (secrets masked)mcloud variables list --json
# Get a single variable by key (requires active project and environment)mcloud variables get DATABASE_URL --json
# Get a single variable by ID (works without project/environment context)mcloud variables get var_01XYZ --json
# Reveal a secret value when explicitly requestedmcloud variables get STRIPE_SECRET_KEY --reveal --json | jq -r '.value'
Warning: Avoid passing --reveal unless the user explicitly asked to see the secret value. The plaintext value is captured by terminal scrollback, log aggregators, and process listings.

To replicate a Cloud environment's variables locally, export them to a .env file:

Terminal
mcloud variables list --reveal --json \  | jq -r '.[] | "\(.key)=\(.value)"' \  > .env

Manage Environments#

Use this flow when creating a preview environment for a feature branch or tearing down environments after a pull request closes:

Terminal
# Create a long-lived environment that tracks a branchmcloud environments create \  --name "Staging" \  --branch develop
# Inspect an environment's current statemcloud environments get env_123 --json | jq '{id, name, type, status, external_id}'
# Delete an environment without prompting (production environments are protected)mcloud environments delete env_123 --yes

environments delete returns a non-zero exit code on production environments. Always check the type field via environments get --json before attempting a delete in an automated workflow.


Common Pitfalls#

  • TTY-only commands. mcloud login (browser flow), mcloud use (without flags), and any delete command without --yes require an interactive TTY. They will error in CI runners, Docker RUN steps, or piped input.
  • MCLOUD_TOKEN precedence. When MCLOUD_TOKEN is set, file-based credentials are ignored and mcloud login is rejected. To switch accounts, unset MCLOUD_TOKEN first.
  • Personal vs organization access keys. A personal access key requires --organization (or an organization in the active context); an organization access key is already scoped to one organization. See the login reference for details.
  • organizations list requires personal auth. Organization access keys cannot list organizations and the command will return 401. Use a personal access key or browser login to enumerate organizations.
  • Build IDs vs deployment IDs. mcloud logs --deployment accepts both. IDs that start with depl are deployments; everything else is treated as a build ID and resolved to the build's latest deployment. Other commands (deployments get, deployments build-logs, environments redeploy) take build IDs only.
  • --json and --follow are incompatible. Streaming logs with --follow always prints plaintext. For programmatic ingestion, use bounded time windows with --from/--to and --json.
Was this guide helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break