Cloud actions say what they do: action describe and MCP tools carry a description
Audit group J (cloud ergonomics, p0-machines): action descriptions for cmux action describe and the MCP tools.
Leo: "so chieff related stuff we dont need to worry too much about but ergo of commands and docs and error/hint messages we do need ... anyways those are just quite a few things that we already kinda own/should prob own"
Lawrence: "start new VMs from acp chat"
The problem
An agent saw only an action's title. cmux action describe <id> showed title, arguments and targets, and every MCP action tool's description was the same title-only boilerplate ("Move to Cloud. cmux app action cloudMove ..."). Nothing said what an action does, what it needs, what it returns, or that an agent's cloud move only asks the person.
The change
ActionDescriptorgetspublic var description: String?(init parameterdescription: String? = nil, right aftertitle:). English and agent-facing, like the Rust MCP text. The GUI doesn't show it.ControlActionInfo.descriptioncarries it.action.listandaction.describeadd adescriptionmember when an action has one.cmux action describeprints the result generically, so the member is enough: human output shows anaction.descriptionrow and--jsonhas the field.- Rust MCP (
action_tools.rsfn description): the action's own description comes first, then a blank line, then the existing boilerplate unchanged. If the description is missing or blank, the tool keeps the old text. - Every Cloud action in
CloudActionCatalog(50 of them) gets 1-3 sentences. Each was checked against its handler (CloudHandlers*.swift,CloudGitHubHandlers.swift,CloudMove*.swift): the args, what a CLI/agent run gets back, the 25 s / 30 s waits, what answers at once before the work is done (fork, restore, rename, kill, resize, new-workspace), what only a person can run (bring-back), and what asks the person first (an agent'scloud movecard, which ignores--path/--machine; an agent's or script'snew-machinemay ask). - Network and firewall actions take their id in the
pathargument. The CLI turns a relative--pathinto a path under the caller's folder, so these descriptions say--arg path=<id>, which passes the value as is. Group A2 owns renaming that key.
Choices
- Localization: none, on purpose. Descriptions are agent-facing English, like the MCP boilerplate they head and the hard-coded network titles already in the catalog. No lint forbids it:
check-l10n.shchecks only tables, andlocalize_changes.pyonlyString(localized:)calls. Localizing 50 keys would cost about 131 lines each, about 6,500 lines across 20 locales, so this stays one PR. - Own table file (
Catalog/CloudActionDescriptions.swift), applied indescriptors()the same way aswaitsForResult.CloudActionCatalog.swiftis at 305 of the 400-line Swift file cap. Putting the text inline would push it over and would touch every entry that #25217 and the sibling cloud PRs are editing. A descriptor that passesdescription:itself wins over the table. - Export unchanged.
ActionSurfaceExport(plans/cmux-next/action-surfaces.json) does not include descriptions, so nothing needs regenerating. Adding them to the generated docs reference can be its own PR. - A command named in a description must be CLI-offered. A test enforces it:
cmux cloud open-machineandmachine-toolsare GUI-only, so their descriptions point atnew-terminal-on-machineandexecinstead.
No capture: not a UI change. The text goes only into action.list / action.describe and the MCP tool descriptions; the GUI shows none of it.
Before / after (derived from the code: not a UI change)
cmux action describe cloud exec --json, abridged:
before: {"action":{"id":"cloudExec","title":"Run Command on Cloud Machine…","cli_name":"cloud exec","arguments":[{"name":"command","title":"Command","kind":"string","required":true}],"targets":["machine"],"requires":["cloudWorkspace"],...}}
after: {"action":{"id":"cloudExec","title":"Run Command on Cloud Machine…","description":"Runs a shell command on the machine through the Cloud API and returns machine_id, exit_code, stdout and stderr; no tab opens. Needs --target machine:<id> and --command; the command stops after 25 s. A person's run from the palette types it into a new terminal tab instead.","cli_name":"cloud exec",...}}
MCP app_cloud_exec tool description:
before:
Run Command on Cloud Machine. cmux app action `cloudExec` (CLI: `cmux cloud exec`). Runs in the cmux app and waits until its work is done. An MCP run changes the user's focus only when focus is true or the action's purpose is focus. Needs: cloudWorkspace.
after:
Runs a shell command on the machine through the Cloud API and returns machine_id, exit_code, stdout and stderr; no tab opens. Needs --target machine:<id> and --command; the command stops after 25 s. A person's run from the palette types it into a new terminal tab instead.
Run Command on Cloud Machine. cmux app action `cloudExec` (CLI: `cmux cloud exec`). Runs in the cmux app and waits until its work is done. An MCP run changes the user's focus only when focus is true or the action's purpose is focus. Needs: cloudWorkspace.
Tests
CloudActionDescriptionTests: every Cloud action has a non-empty description, no table key is stale,ActionCatalog.allcarries them, everycmux <noun> <verb>named is CLI-offered, and a machine command named carries--target machine:<id>.ActionDescriptionWireTests:ControlActionInfo.jsoncarriesdescription, omits it when nil, and the registry's catalog takes it from the descriptor.- Rust
cli/mcp/tests/action_descriptions.rs: the description comes first and the boilerplate follows; a missing or blank one keeps the old text.
Risk
Low. It adds one optional field to the wire, and older CLIs ignore unknown members. The MCP text changes only for actions that have a description, and the only behaviour change is tool description text. Overlaps: #25217 changes the newCloudMachine entry. When it lands, the newCloudMachine description gets its --persistent --size --name flags in a follow-up (or here if it lands first).
Agent session: Air-Blue
🤖 Generated with Claude Code