feat: MCP server for artifact cleanup (supersedes #17, with delete guard) - #19
Merged
Conversation
Expose irona over stdio MCP while reusing the existing scanner and deleter paths. Document PATH-based configuration so MCP clients can call irona portably instead of hardcoding a user-local binary. Constraint: MCP clients launch servers by command and args, so the binary must support a non-TUI stdio mode. Rejected: Separate irona-mcp crate | unnecessary split for two tools that reuse existing core behavior. Confidence: high Scope-risk: moderate Directive: Keep MCP cleanup destructive only through explicit clean_artifacts path arguments; do not make scan_artifacts delete anything. Tested: cargo test; pre-commit fmt and clippy hook; installed binary smoke-tested with initialize and tools/list; Codex MCP command verified after local config wiring. Not-tested: End-to-end startup from a freshly restarted Codex process.
clean_artifacts passed caller-supplied paths straight to remove_dir_all with no validation. Since the caller is a model, a hallucinated or injected path meant silent recursive deletion of arbitrary directories — verified against the built binary on a plain directory holding a text file. Every path is now canonicalized and checked with scanner::is_artifact, which accepts a directory only when a marker file sits beside it or a .gitignore rule matches it. One bad path fails the whole call. Splitting scan into scan_artifacts also cost the TUI its streaming: the parallel dir_size phase collected into a Vec before sending anything, so the list stayed empty until the whole scan finished. Phase 1 is now a shared collect_candidates helper, letting scan keep sending each entry as its size lands while scan_artifacts collects for MCP. Also reports isError when some deletions fail rather than always false. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Supersedes #17. Contains @NotYash1066's original commit unchanged, plus one fix commit on top.
I could not push directly to the fork branch for #17, so this branch carries their work forward. Their commit is preserved, so authorship survives in history. Close #17 once this merges.
Why the extra commit
clean_artifactsdeleted any path handed to it. Caller-supplied strings went straight toremove_dir_allwith no validation. The caller here is a model, so a hallucinated or prompt-injected path meant silent recursive deletion. Verified against the built binary before the fix:That directory held a text file and no marker.
destructiveHint: trueis advisory — clients are not obliged to confirm on it.Every path is now canonicalized and gated on a new
scanner::is_artifact, which accepts a directory only when a marker file sits beside it (target/next to aCargo.toml) or a.gitignorerule matches it. One bad path fails the whole call and deletes nothing. Same input after the fix:The TUI lost its streaming scan. Splitting
scanintoscan_artifactsmade the paralleldir_sizephase collect into aVecbefore sending anything, so the list stayed empty until the whole scan finished — noticeable on a large workspace, sincedir_sizeis the slow phase. Phase 1 is now a sharedcollect_candidateshelper:scanstreams each entry as its size lands,scan_artifactscollects for MCP.Also flips
isErrorwhen some deletions fail, instead of always reportingfalse.Verification
cargo nextest run— 63 pass (54 from feat: add MCP server for artifact cleanup #17, 9 new)cargo clippy --all-targets -- -D warnings— cleanis_artifactaccepts marker-backed and gitignore-matched dirs, rejects plain dirs /target/withoutCargo.toml/ denylisted.git, andscanemits allFoundmessages beforeDonetarget/still being cleanedNot addressed
initializestill echoes back whateverprotocolVersionthe client sends, including unsupported ones, andscan_artifactshas no cap on result count. Both are minor and worth a follow-up rather than holding this up.🤖 Generated with Claude Code