From df62c32822f1a2e5bd370b7d3fe28aaa6c6e60d1 Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Wed, 26 Aug 2026 15:48:16 -0600 Subject: [PATCH] feat: support mcp.json in the root of bundle repos as well as in the legacy functions directory --- src/config/install_remote.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/config/install_remote.rs b/src/config/install_remote.rs index caaadcb..5013171 100644 --- a/src/config/install_remote.rs +++ b/src/config/install_remote.rs @@ -32,7 +32,7 @@ pub fn install_remote(git_url: &str, filter: Option, force: bool) if layout.is_empty() { println!( "No recognized assets found in {git_url}. Expected one or more of: \ - agents/, roles/, skills/, macros/, functions/tools/, functions/mcp.json" + agents/, roles/, skills/, macros/, functions/tools/, mcp.json" ); return Ok(()); } @@ -1263,6 +1263,11 @@ fn scan_remote_layout(root: &Path) -> Result { layout.macros = Some(macros); } + let root_mcp = root.join("mcp.json"); + if root_mcp.is_file() { + layout.mcp_json = Some(root_mcp); + } + let functions = root.join("functions"); if functions.is_dir() { let tools = functions.join("tools"); @@ -1270,8 +1275,9 @@ fn scan_remote_layout(root: &Path) -> Result { layout.functions_tools = Some(tools); } + // Legacy bundle layout; a root-level mcp.json wins when both exist. let mcp = functions.join("mcp.json"); - if mcp.is_file() { + if layout.mcp_json.is_none() && mcp.is_file() { layout.mcp_json = Some(mcp); } } @@ -2642,6 +2648,30 @@ mod tests { let _ = fs::remove_dir_all(&root); } + #[test] + fn scan_remote_layout_finds_root_mcp_json() { + let root = fresh_temp_dir("scan-root-mcp-test-"); + touch(&root.join("mcp.json")); + + let layout = scan_remote_layout(&root).unwrap(); + + assert_eq!(layout.mcp_json, Some(root.join("mcp.json"))); + let _ = fs::remove_dir_all(&root); + } + + #[test] + fn scan_remote_layout_prefers_root_mcp_json_over_functions() { + let root = fresh_temp_dir("scan-mcp-precedence-test-"); + touch(&root.join("mcp.json")); + fs::create_dir_all(root.join("functions")).unwrap(); + touch(&root.join("functions/mcp.json")); + + let layout = scan_remote_layout(&root).unwrap(); + + assert_eq!(layout.mcp_json, Some(root.join("mcp.json"))); + let _ = fs::remove_dir_all(&root); + } + #[test] fn scan_remote_layout_finds_skills_only() { let root = fresh_temp_dir("scan-skills-only-");