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:
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
--jsonwhen 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 whoamibefore any state change so you know which organization, project, and environment you'll affect. - Set context once, not per command. Use
mcloud useto persist organization, project, and environment, then drop the flags from subsequent commands. - Read before you write. Run a
getorlistbefore anydelete,redeploy, ortrigger-buildto verify you're targeting the right resource. - Use
--yesfor irreversible operations.mcloud projects deleteandmcloud environments deleterequire confirmation in interactive mode and refuse to run without--yesin non-interactive mode. - Production environments cannot be deleted.
mcloud environments deleteerrors on production environments by design. Do not attempt to bypass it. - Pipe to
jqfor extraction. Avoid string-parsing JSON; pipe--jsonoutput intojqfor 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:
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:
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:
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:
If no, run mcloud signup to open the sign-up page. After the user creates an account, run mcloud login:
For non-interactive environments (CI, headless containers, restricted shells), skip the browser flow entirely and use an access key instead:
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:
Confirm Authentication and Context#
Before running any command that mutates state, AI agents should verify the active identity and scope:
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:
Exit code 0 means authenticated and scoped; on a non-zero exit, stop and ask the user to authenticate or set the active context.
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:
If you only have names instead of IDs or handles, look them up first:
❯# Resolve organization ID by name❯ORGANIZATION_ID=$(❯ mcloud organizations list --json \❯ | jq -r '.[] | select(.name == "My Organization") | .id'❯)❯ ❯# Resolve project handle by name❯PROJECT_HANDLE=$(❯ mcloud projects list --organization "$ORGANIZATION_ID" --json \❯ | jq -r '.[] | select(.name == "My Store") | .handle'❯)❯ ❯# Resolve environment handle by name❯ENVIRONMENT_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:
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:
❯# Most recent failed deployment for the active environment❯mcloud deployments list --json \❯ | jq -r '[.[] | select(.backend_status == "build-failed" or .backend_status == "deployment-failed")][0].id'❯ ❯# All deployments for a specific commit❯mcloud deployments list --commit a1b2c3d --json | jq '.'❯ ❯# Only preview deployments❯mcloud deployments list --environment-type preview --json | jq '.'
For one specific deployment, use mcloud deployments get:
Debug a Failed Deployment#
The right log source depends on the failure mode. AI agents should route on backend_status (or storefront_status):
| What failed | Where to look |
|---|---|---|
| Build step (Docker, dependencies, compilation). | |
| Rollout step (the build succeeded but the runtime crashed). | |
| Build or rollout exceeded the time budget. | Both: |
Build Failure Recipe#
❯# Find the most recent build-failed deployment❯DEPLOYMENT_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 output❯mcloud deployments build-logs "$DEPLOYMENT_ID"❯ ❯# For storefront build failures, pass --type storefront❯mcloud 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.
❯# Find the most recent deployment-failed deployment❯DEPLOYMENT_ID=$(❯ mcloud deployments list --json \❯ | jq -r '[.[] | select(.backend_status == "deployment-failed")][0].id'❯)❯ ❯# Read the runtime logs for that specific deployment❯mcloud logs --deployment "$DEPLOYMENT_ID" --limit 1000❯ ❯# Filter to error-level lines only❯mcloud 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 analysis❯mcloud logs --deployment "$DEPLOYMENT_ID" --json | jq '.[] | {timestamp, source, message}'
--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 |
|---|---|---|
Re-runs the active deployment's existing build. | The fix is environment-side (variable change, infra issue) and the build itself is fine. | |
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.
❯# After fixing an environment variable❯mcloud environments redeploy env_123❯ ❯# After pushing a fix to the tracked branch❯mcloud environments trigger-build env_123❯ ❯# Verify the new build is running❯mcloud 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.
❯# 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 requested❯mcloud variables get STRIPE_SECRET_KEY --reveal --json | jq -r '.value'
--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:
Manage Environments#
Use this flow when creating a preview environment for a feature branch or tearing down environments after a pull request closes:
❯# Create a long-lived environment that tracks a branch❯mcloud environments create \❯ --name "Staging" \❯ --branch develop❯ ❯# Inspect an environment's current state❯mcloud 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--yesrequire an interactive TTY. They will error in CI runners, DockerRUNsteps, or piped input. MCLOUD_TOKENprecedence. WhenMCLOUD_TOKENis set, file-based credentials are ignored andmcloud loginis rejected. To switch accounts,unset MCLOUD_TOKENfirst.- 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 listrequires 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 --deploymentaccepts both. IDs that start withdeplare 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. --jsonand--followare incompatible. Streaming logs with--followalways prints plaintext. For programmatic ingestion, use bounded time windows with--from/--toand--json.