use std::collections::HashMap;
use agentic_config::types::SubagentsConfig;
use claudecode::config::MCPConfig;
use claudecode::config::MCPServer;
use claudecode::types::Model;
use super::prompts::compose_prompt_impl;
use crate::types::AgentLocation;
use crate::types::AgentType;
pub fn model_for(agent_type: AgentType, cfg: &SubagentsConfig) -> Model {
let raw = match agent_type {
AgentType::Locator => cfg.locator_model.as_str(),
AgentType::Analyzer => cfg.analyzer_model.as_str(),
};
match raw.trim().to_lowercase().as_str() {
"haiku" | "claude-haiku-4-5" => Model::Haiku,
"sonnet" | "claude-sonnet-4-6" => Model::Sonnet,
"opus" | "claude-opus-4-6" => Model::Opus,
_ => {
match agent_type {
AgentType::Locator => Model::Haiku,
AgentType::Analyzer => Model::Sonnet,
}
}
}
}
pub fn enabled_tools_for(agent_type: AgentType, location: AgentLocation) -> Vec<String> {
use AgentLocation::Codebase;
use AgentLocation::References;
use AgentLocation::Thoughts;
use AgentLocation::Web;
use AgentType::Analyzer;
use AgentType::Locator;
match (agent_type, location) {
(Locator, Codebase) => vec![
"mcp__agentic-mcp__cli_ls".into(),
"mcp__agentic-mcp__cli_grep".into(),
"mcp__agentic-mcp__cli_glob".into(),
],
(Locator, Thoughts) => vec![
"mcp__agentic-mcp__cli_ls".into(),
"mcp__agentic-mcp__thoughts_list_documents".into(),
"mcp__agentic-mcp__cli_grep".into(),
"mcp__agentic-mcp__cli_glob".into(),
],
(Locator, References) => vec![
"mcp__agentic-mcp__cli_ls".into(),
"mcp__agentic-mcp__thoughts_list_references".into(),
"mcp__agentic-mcp__cli_grep".into(),
"mcp__agentic-mcp__cli_glob".into(),
],
(Locator, Web) => vec![
"mcp__agentic-mcp__web_search".into(),
"mcp__agentic-mcp__web_fetch".into(),
],
(Analyzer, Codebase) => vec![
"Read".into(),
"mcp__agentic-mcp__cli_ls".into(),
"mcp__agentic-mcp__cli_grep".into(),
"mcp__agentic-mcp__cli_glob".into(),
"TodoWrite".into(),
],
(Analyzer, Thoughts) => vec![
"Read".into(),
"mcp__agentic-mcp__cli_ls".into(),
"mcp__agentic-mcp__thoughts_list_documents".into(),
"mcp__agentic-mcp__cli_grep".into(),
"mcp__agentic-mcp__cli_glob".into(),
],
(Analyzer, References) => vec![
"Read".into(),
"mcp__agentic-mcp__cli_ls".into(),
"mcp__agentic-mcp__thoughts_list_references".into(),
"mcp__agentic-mcp__cli_grep".into(),
"mcp__agentic-mcp__cli_glob".into(),
"TodoWrite".into(),
],
(Analyzer, Web) => vec![
"mcp__agentic-mcp__web_search".into(),
"mcp__agentic-mcp__web_fetch".into(),
"TodoWrite".into(),
"Read".into(),
"mcp__agentic-mcp__cli_grep".into(),
"mcp__agentic-mcp__cli_glob".into(),
"mcp__agentic-mcp__cli_ls".into(),
],
}
}
pub fn compose_prompt(agent_type: AgentType, location: AgentLocation) -> String {
compose_prompt_impl(agent_type, location)
}
fn agentic_mcp_allowlist_from(enabled: &[String]) -> Vec<String> {
use std::collections::BTreeSet;
const PREFIX: &str = "mcp__agentic-mcp__";
let mut set = BTreeSet::new();
for e in enabled {
if let Some(rest) = e.strip_prefix(PREFIX) {
let name = rest.trim();
if !name.is_empty() {
set.insert(name.to_string());
}
}
}
set.into_iter().collect()
}
pub fn build_mcp_config(_location: AgentLocation, enabled_tools: &[String]) -> MCPConfig {
let mut servers: HashMap<String, MCPServer> = HashMap::new();
let allowlist = agentic_mcp_allowlist_from(enabled_tools);
if allowlist.is_empty() {
return MCPConfig {
mcp_servers: servers,
};
}
let args = vec![
"--allow".to_string(),
allowlist.join(","),
"--suppress-search-reminder".to_string(),
];
servers.insert(
"agentic-mcp".to_string(),
MCPServer::stdio("agentic-mcp", args),
);
MCPConfig {
mcp_servers: servers,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_model_for_locator_default() {
let cfg = SubagentsConfig::default();
assert_eq!(model_for(AgentType::Locator, &cfg), Model::Haiku);
}
#[test]
fn test_model_for_analyzer_default() {
let cfg = SubagentsConfig::default();
assert_eq!(model_for(AgentType::Analyzer, &cfg), Model::Sonnet);
}
#[test]
fn test_model_for_with_custom_config() {
let cfg = SubagentsConfig {
locator_model: "sonnet".into(),
analyzer_model: "opus".into(),
};
assert_eq!(model_for(AgentType::Locator, &cfg), Model::Sonnet);
assert_eq!(model_for(AgentType::Analyzer, &cfg), Model::Opus);
}
#[test]
fn test_model_for_fallback_on_unknown() {
let cfg = SubagentsConfig {
locator_model: "unknown-model".into(),
analyzer_model: "another-unknown".into(),
};
assert_eq!(model_for(AgentType::Locator, &cfg), Model::Haiku);
assert_eq!(model_for(AgentType::Analyzer, &cfg), Model::Sonnet);
}
#[test]
fn locator_default_model_string_is_explicitly_recognized() {
let mut cfg = SubagentsConfig::default();
let locator_default = cfg.locator_model.clone();
cfg.analyzer_model = locator_default;
assert_eq!(model_for(AgentType::Analyzer, &cfg), Model::Haiku);
}
#[test]
fn analyzer_default_model_string_is_explicitly_recognized() {
let mut cfg = SubagentsConfig::default();
let analyzer_default = cfg.analyzer_model.clone();
cfg.locator_model = analyzer_default;
assert_eq!(model_for(AgentType::Locator, &cfg), Model::Sonnet);
}
#[test]
fn test_enabled_tools_locator_codebase() {
let tools = enabled_tools_for(AgentType::Locator, AgentLocation::Codebase);
assert!(tools.contains(&"mcp__agentic-mcp__cli_ls".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_grep".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_glob".to_string()));
assert!(!tools.contains(&"Read".to_string())); }
#[test]
fn test_enabled_tools_analyzer_codebase() {
let tools = enabled_tools_for(AgentType::Analyzer, AgentLocation::Codebase);
assert!(tools.contains(&"Read".to_string())); assert!(tools.contains(&"mcp__agentic-mcp__cli_ls".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_grep".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_glob".to_string()));
}
#[test]
fn test_enabled_tools_locator_thoughts() {
let tools = enabled_tools_for(AgentType::Locator, AgentLocation::Thoughts);
assert!(tools.contains(&"mcp__agentic-mcp__thoughts_list_documents".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_ls".to_string()));
}
#[test]
fn test_enabled_tools_locator_references() {
let tools = enabled_tools_for(AgentType::Locator, AgentLocation::References);
assert!(tools.contains(&"mcp__agentic-mcp__thoughts_list_references".to_string()));
}
#[test]
fn test_enabled_tools_analyzer_thoughts_has_ls() {
let tools = enabled_tools_for(AgentType::Analyzer, AgentLocation::Thoughts);
assert!(tools.contains(&"mcp__agentic-mcp__cli_ls".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__thoughts_list_documents".to_string()));
assert!(tools.contains(&"Read".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_grep".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_glob".to_string()));
}
#[test]
fn test_enabled_tools_analyzer_references_has_ls() {
let tools = enabled_tools_for(AgentType::Analyzer, AgentLocation::References);
assert!(tools.contains(&"mcp__agentic-mcp__cli_ls".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__thoughts_list_references".to_string()));
assert!(tools.contains(&"Read".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_grep".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__cli_glob".to_string()));
assert!(tools.contains(&"TodoWrite".to_string()));
}
#[test]
fn test_enabled_tools_locator_web() {
let tools = enabled_tools_for(AgentType::Locator, AgentLocation::Web);
assert_eq!(
tools,
vec![
"mcp__agentic-mcp__web_search".to_string(),
"mcp__agentic-mcp__web_fetch".to_string()
]
);
}
#[test]
fn test_enabled_tools_analyzer_web() {
let tools = enabled_tools_for(AgentType::Analyzer, AgentLocation::Web);
assert!(tools.contains(&"mcp__agentic-mcp__web_search".to_string()));
assert!(tools.contains(&"mcp__agentic-mcp__web_fetch".to_string()));
}
#[test]
fn test_enabled_tools_analyzer_web_full_set() {
let tools = enabled_tools_for(AgentType::Analyzer, AgentLocation::Web);
let expected = [
"mcp__agentic-mcp__web_search",
"mcp__agentic-mcp__web_fetch",
"TodoWrite",
"Read",
"mcp__agentic-mcp__cli_grep",
"mcp__agentic-mcp__cli_glob",
"mcp__agentic-mcp__cli_ls",
];
for t in expected {
assert!(tools.contains(&t.to_string()), "missing tool: {t}");
}
assert_eq!(tools.len(), 7);
}
#[test]
fn test_compose_prompt_locator_codebase() {
let prompt = compose_prompt(AgentType::Locator, AgentLocation::Codebase);
assert!(prompt.contains("finding WHERE"));
assert!(prompt.contains("Local codebase"));
}
#[test]
fn test_compose_prompt_analyzer_web() {
let prompt = compose_prompt(AgentType::Analyzer, AgentLocation::Web);
assert!(prompt.contains("understanding HOW"));
assert!(prompt.contains("web_fetch"));
}
#[test]
fn test_build_mcp_config_codebase() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::Codebase);
let config = build_mcp_config(AgentLocation::Codebase, &enabled);
assert!(config.mcp_servers.contains_key("agentic-mcp"));
assert_eq!(config.mcp_servers.len(), 1); }
#[test]
fn test_build_mcp_config_thoughts() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::Thoughts);
let config = build_mcp_config(AgentLocation::Thoughts, &enabled);
assert!(config.mcp_servers.contains_key("agentic-mcp"));
assert_eq!(config.mcp_servers.len(), 1); }
#[test]
fn test_build_mcp_config_references() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::References);
let config = build_mcp_config(AgentLocation::References, &enabled);
assert!(config.mcp_servers.contains_key("agentic-mcp"));
assert_eq!(config.mcp_servers.len(), 1); }
#[test]
fn test_build_mcp_config_web() {
let enabled = enabled_tools_for(AgentType::Analyzer, AgentLocation::Web);
let config = build_mcp_config(AgentLocation::Web, &enabled);
assert!(config.mcp_servers.contains_key("agentic-mcp"));
assert_eq!(config.mcp_servers.len(), 1); }
#[test]
fn test_all_combinations_have_tools() {
for agent_type in [AgentType::Locator, AgentType::Analyzer] {
for location in [
AgentLocation::Codebase,
AgentLocation::Thoughts,
AgentLocation::References,
AgentLocation::Web,
] {
let tools = enabled_tools_for(agent_type, location);
assert!(
!tools.is_empty(),
"No tools for {agent_type:?} + {location:?}"
);
}
}
}
#[test]
fn test_all_combinations_have_prompts() {
for agent_type in [AgentType::Locator, AgentType::Analyzer] {
for location in [
AgentLocation::Codebase,
AgentLocation::Thoughts,
AgentLocation::References,
AgentLocation::Web,
] {
let prompt = compose_prompt(agent_type, location);
assert!(
!prompt.is_empty(),
"Empty prompt for {agent_type:?} + {location:?}"
);
assert!(
prompt.len() > 100,
"Prompt too short for {agent_type:?} + {location:?}"
);
}
}
}
#[test]
fn test_agentic_mcp_allowlist_locator_codebase() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::Codebase);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(list, vec!["cli_glob", "cli_grep", "cli_ls"]);
}
#[test]
fn test_agentic_mcp_allowlist_locator_web_and_server_present() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::Web);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(list, vec!["web_fetch", "web_search"]);
let cfg = build_mcp_config(AgentLocation::Web, &enabled);
assert!(cfg.mcp_servers.contains_key("agentic-mcp"));
assert_eq!(cfg.mcp_servers.len(), 1);
}
#[test]
fn test_agentic_mcp_allowlist_locator_thoughts() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::Thoughts);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(
list,
vec!["cli_glob", "cli_grep", "cli_ls", "thoughts_list_documents"]
);
}
#[test]
fn test_agentic_mcp_allowlist_locator_references() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::References);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(
list,
vec!["cli_glob", "cli_grep", "cli_ls", "thoughts_list_references"]
);
}
#[test]
fn test_agentic_mcp_allowlist_analyzer_codebase() {
let enabled = enabled_tools_for(AgentType::Analyzer, AgentLocation::Codebase);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(list, vec!["cli_glob", "cli_grep", "cli_ls"]);
}
#[test]
fn test_agentic_mcp_allowlist_analyzer_thoughts() {
let enabled = enabled_tools_for(AgentType::Analyzer, AgentLocation::Thoughts);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(
list,
vec!["cli_glob", "cli_grep", "cli_ls", "thoughts_list_documents"]
);
}
#[test]
fn test_agentic_mcp_allowlist_analyzer_references() {
let enabled = enabled_tools_for(AgentType::Analyzer, AgentLocation::References);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(
list,
vec!["cli_glob", "cli_grep", "cli_ls", "thoughts_list_references"]
);
}
#[test]
fn test_agentic_mcp_allowlist_analyzer_web() {
let enabled = enabled_tools_for(AgentType::Analyzer, AgentLocation::Web);
let list = agentic_mcp_allowlist_from(&enabled);
assert_eq!(
list,
vec!["cli_glob", "cli_grep", "cli_ls", "web_fetch", "web_search"]
);
}
#[test]
fn test_build_mcp_config_includes_suppress_search_reminder_flag() {
let enabled = enabled_tools_for(AgentType::Locator, AgentLocation::Codebase);
let config = build_mcp_config(AgentLocation::Codebase, &enabled);
let Some(server) = config.mcp_servers.get("agentic-mcp") else {
panic!("expected agentic-mcp server to be configured");
};
match server {
MCPServer::Stdio { command, args, .. } => {
assert_eq!(command, "agentic-mcp");
assert!(args.contains(&"--suppress-search-reminder".to_string()));
}
MCPServer::Http { .. } => panic!("expected stdio MCP server"),
}
}
}