feat(mcp): serve mise's command effects to agents - #11389
Conversation
mise classifies all 167 of its commands as `read`, `write` or `destructive` (#11306), and nothing an agent talks to can see any of it. `mise mcp` exposes two tools, `install_tool` and `run_task`, neither of which says what a command does. So an agent about to run `mise prune` has the answer sitting in the binary and no way to ask for it. `list_commands` returns the command tree with each command's effect, help and hidden flag. 156 visible commands: 74 read, 65 write, 7 destructive, 10 deliberately unclassified. The spec is built once in `MiseServer::new` from `cli::usage::spec()`, extracted from `Usage::run` so the spec `mise usage` prints and the one `mise mcp` serves cannot drift. Deriving it walks the whole clap tree, which is not work to repeat per request. Hidden commands take their subtree with them, since clap does not propagate `hide` and a visible child of a hidden parent is not a documented path — `bootstrap launchd apply` would otherwise surface along with the hidden path it sits under. A missing effect means unknown, not safe, and the tool description says so. Ten commands are deliberately unset because they run code the user supplies — `run`, `exec`, `watch`, `tool-stub` — where any label would be a lie in one direction or the other. A test asserts every unclassified command has an entry in `command_effects::UNCLASSIFIED`, so a new command cannot go unlabeled by accident. `describe_command` (per-command flags and args, with their own effects) needs `usage::available_flags` from jdx/usage#746 to resolve inherited globals correctly — mise's `run` re-declares root globals as non-global flags, which is exactly the case a hand-rolled walk gets wrong. It follows once that releases. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Central YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughAdds the MCP ChangesMCP command discovery
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant MCPClient
participant MiseServer
participant UsageSpec
MCPClient->>MiseServer: list_commands(include_hidden)
MiseServer->>UsageSpec: traverse command tree
UsageSpec-->>MiseServer: command metadata
MiseServer-->>MCPClient: JSON result
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryAdds an MCP
Confidence Score: 5/5The PR appears safe to merge, with no concrete correctness or security failures identified in the changed paths. The MCP tool traverses the same usage command tree that receives effect annotations, hidden subtrees are pruned deliberately and tested, and the usage refactor preserves the previous output construction. Important Files Changed
Reviews (1): Last reviewed commit: "feat(mcp): serve mise's command effects ..." | Re-trigger Greptile |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.14 x -- echo |
19.2 ± 1.5 | 16.7 | 25.0 | 1.00 |
mise x -- echo |
20.7 ± 1.7 | 17.2 | 27.5 | 1.08 ± 0.12 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.14 env |
19.0 ± 1.5 | 16.1 | 27.9 | 1.00 |
mise env |
20.5 ± 1.6 | 16.4 | 32.4 | 1.08 ± 0.12 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.14 hook-env |
19.2 ± 1.2 | 16.6 | 23.6 | 1.00 |
mise hook-env |
20.3 ± 1.5 | 17.5 | 24.8 | 1.06 ± 0.10 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.7.14 ls |
16.5 ± 1.1 | 14.2 | 21.8 | 1.00 |
mise ls |
18.9 ± 1.6 | 15.3 | 25.8 | 1.14 ± 0.12 |
ls measured 14% slower, but the relative uncertainty overlaps the 10% threshold. |
xtasks/test/perf
| Command | mise-2026.7.14 | mise | Variance |
|---|---|---|---|
| install (cached) | 163ms | 179ms | -8% |
| ls (cached) | 64ms | 68ms | -5% |
| bin-paths (cached) | 68ms | 72ms | -5% |
| task-ls (cached) | 94ms | 97ms | -3% |
mise classifies all 167 of its commands as
read,writeordestructive(#11306) — and nothing an agent talks to can see any of it.mise mcpexposesinstall_toolandrun_task, neither of which says what a command does. An agent about to runmise prunehas the answer sitting in the binary and no way to ask for it.This adds
list_commands, returning the command tree with each command's effect, help and hidden flag:How
cli::usage::spec()is extracted out ofUsage::run, so the specmise usageprints and the onemise mcpserves are the same construction rather than two that drift. It's built once inMiseServer::new— deriving it walks the whole clap tree, which isn't work to repeat per request.Hidden commands take their subtree with them. clap doesn't propagate
hide, sobootstrap launchdis hidden whilebootstrap launchd applyisn't; a flat filter onhidewould surface the child along with the hidden path it sits under.On unclassified commands
A missing effect means unknown, not safe, and the tool description says so explicitly. Ten commands are deliberately unset because they run code the user supplies —
run,exec,watch,tool-stub,en,oci run— where any label would be a lie in one direction or the other, andreadin particular would be dangerous.The risk is a new command going unlabeled by accident and reading the same way. A test asserts every unclassified command in the served output has an entry in
command_effects::UNCLASSIFIED, which is where the reason lives.Not in this PR
describe_command— per-command flags and args with their own effects — needsusage::available_flagsfrom jdx/usage#746 to resolve inherited globals correctly. mise'srunre-declares root globals as non-global flags, which is precisely the case a hand-rolled ancestor walk gets wrong (it reports the local declaration's absent effect instead of the global's). It follows once that releases.Verified
Against a real client handshake —
initialize→notifications/initialized→tools/list→tools/call— plus five unit tests covering the effects surviving into the output, nested paths, hidden-subtree pruning, and the unclassified guard.This PR was generated by an AI coding assistant.
Note
Low Risk
Additive MCP surface and refactored spec construction; no changes to how mise commands execute on disk.
Overview
Exposes mise’s read / write / destructive command classifications to MCP clients via a new
list_commandstool, so agents can see what a CLI subcommand does before invoking it.list_commandswalks the shared usage spec and returns JSON rows (command,help,effect,hidden). Optionalinclude_hiddencontrols whether hidden commands appear; when hidden, entire subtrees are omitted (so children of a hidden parent do not leak). Server instructions now tell clients to calllist_commandsfirst and treat a missingeffectas unknown, not safe.cli::usage::spec()is extracted frommise usageand reused byMiseServer, with the spec cached inArcat startup. Docs (docs/cli/mcp.md, usage KDL) list the new tool. Unit tests cover effects in output, nesting, hidden pruning, and a guard that only deliberately unclassified commands lack aneffect.Reviewed by Cursor Bugbot for commit 1dcf0fd. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit
New Features
list_commandsMCP tool and resource to enumerate availablemisecommands.include_hiddenoption to include hidden commands when needed.Documentation