fix(mcp): harvest trusted workspace MCP config#2770
Conversation
Merge global MCP config with trusted workspace .codewhale/mcp.json files so project MCP servers appear in TUI, CLI, doctor, and runtime API flows. Project stdio servers default cwd to the workspace, project cwd escapes are rejected, and project MCP is ignored until workspace trust is recorded in user-owned config. Fixes #2749 Reported by @yekern Harvested from PR #2751 by @cyq1017
There was a problem hiding this comment.
Hmbown has reached the 50-review limit for trial accounts. To continue receiving code reviews, upgrade your plan.
There was a problem hiding this comment.
Code Review
This pull request introduces support for workspace-local MCP configurations (".codewhale/mcp.json") that merge with the global configuration, allowing project-specific MCP servers and custom working directories ("cwd") for stdio servers. It updates "McpPool" to track multiple configuration sources and dynamically reload on changes, while also making CLI commands and the runtime API workspace-aware. Feedback on the changes highlights a potential path traversal vulnerability in "crates/tui/src/mcp.rs" where comparing non-canonicalized paths against canonicalized paths could bypass workspace boundary checks; canonicalizing the "cwd" path before validation is recommended to ensure robust security.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| let cwd = match server.cwd.as_deref() { | ||
| Some(cwd) if cwd.is_relative() => normalize_path_components(&workspace.join(cwd)), | ||
| Some(cwd) => normalize_path_components(cwd), | ||
| None => workspace.to_path_buf(), | ||
| }; |
There was a problem hiding this comment.
Comparing non-canonicalized paths against canonicalized paths can lead to both false positives (rejecting valid paths due to symlinks in the workspace path) and false negatives (failing to detect path traversal escapes via symlinks pointing outside the workspace).
Resolving symlinks by canonicalizing the cwd path (with a fallback to syntactic normalization if the path does not exist yet) makes the security check robust.
let cwd = match server.cwd.as_deref() {
Some(cwd) if cwd.is_relative() => workspace.join(cwd),
Some(cwd) => cwd.to_path_buf(),
None => workspace.to_path_buf(),
};
let cwd = cwd.canonicalize().unwrap_or_else(|_| normalize_path_components(&cwd));
Summary
.codewhale/mcp.jsonwith global MCP configcwdto the workspace and reject projectcwdescapescodewhale mcpread/connect/validate flows through the merged viewVerification
cargo test -p codewhale-tui workspace_mcp --all-features --locked -- --nocapturecargo test -p codewhale-tui reload_if_config_changed --all-features --locked -- --nocapturecargo test -p codewhale-tui doctor_mcp --all-features --locked -- --nocapturecargo test -p codewhale-tui mcp_pool_call_tool --all-features --locked -- --nocapturecargo fmt --all -- --checkgit diff --check./scripts/release/check-versions.shcargo check -p codewhale-tui --all-features --lockedcargo clippy -p codewhale-tui --all-features --locked -- -D warningspython3 scripts/check-coauthor-trailers.py --author-map .github/AUTHOR_MAP --range origin/codex/v0.9.0-stewardship..HEAD --check-authorsStewardship
.codewhale/mcp.jsonauto-merge #2749; thanks @yekern for the clear Laravel MCP repro and acceptance criteria