use crate::multi_agent::types::PlanAction;
pub const MCP_TOOL_NAMES: &[&str] = &[
"wacc_calculator",
"dcf_model",
"comps_table",
"comparable_companies",
"calculate_target_price",
"sotp_valuation",
"credit_metrics",
"debt_capacity",
"covenant_check",
"altman_z_score",
"cds_pricing",
"lbo_model",
"irr_moic",
"debt_schedule",
"sources_uses",
"waterfall_distribution",
"merger_model",
"accretion_dilution",
"sharpe_ratio",
"value_at_risk",
"kelly_criterion",
"factor_attribution",
"black_litterman",
"risk_parity",
"stress_test",
"monte_carlo_dcf",
"bond_pricing",
"duration_convexity",
"yield_curve_bootstrap",
"option_pricing",
"implied_volatility",
"swap_valuation",
"fmp_quote",
"fmp_company_profile",
"fmp_income_statement",
"fmp_balance_sheet",
"fmp_cash_flow",
"fmp_key_metrics",
"fmp_ratios",
"fred_series",
"edgar_filing",
"figi_lookup",
"yf_history",
"wb_indicator",
"lseg_curve",
"sp_credit_rating",
"factset_factor_exposure",
"moodys_rating",
];
pub const SLASH_COMMAND_NAMES: &[&str] = &[
"initiate-coverage",
"earnings",
"earnings-preview",
"morning-note",
"thesis",
"screen",
"screen-deal",
"sector",
"model-update",
"catalysts",
"one-pager",
"dcf",
"comps",
"lbo",
"merger-model",
"3-statement-model",
"ic-memo",
"dd-checklist",
"dd-prep",
"value-creation",
"unit-economics",
"returns",
"portfolio",
"source",
"deal-tracker",
"cim",
"teaser",
"pitch-deck",
"buyer-list",
"process-letter",
"client-review",
"client-report",
"financial-plan",
"rebalance",
"tlh",
"proposal",
"competitive-analysis",
"debug-model",
"macro-rates",
"fx-carry",
"swap-curve",
"bond-analysis",
"bond-rv",
"fi-portfolio",
"credit-analysis",
"derivatives-valuation",
"option-vol",
"property-valuation",
"acquisition-model",
"jurisdiction-comparison",
"fund-migration",
"fund-ops",
];
#[derive(Debug, Clone)]
pub struct ActionCatalogue {
pub mcp_tools: Vec<&'static str>,
pub slash_commands: Vec<&'static str>,
}
impl ActionCatalogue {
pub fn total_actions(&self) -> usize {
self.mcp_tools.len() + self.slash_commands.len()
}
}
pub fn load_action_catalogue() -> ActionCatalogue {
ActionCatalogue {
mcp_tools: MCP_TOOL_NAMES.to_vec(),
slash_commands: SLASH_COMMAND_NAMES.to_vec(),
}
}
pub fn validate_action(catalogue: &ActionCatalogue, action: &PlanAction) -> bool {
match action {
PlanAction::McpTool { name, .. } => catalogue.mcp_tools.iter().any(|t| t == name),
PlanAction::SlashCommand { name, .. } => catalogue.slash_commands.iter().any(|c| c == name),
PlanAction::SpecialistAgent { slug, .. } => {
crate::multi_agent::agent_invoke::validate_target_agent(slug)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn catalogue_loads_with_actions() {
let cat = load_action_catalogue();
assert!(!cat.mcp_tools.is_empty());
assert!(!cat.slash_commands.is_empty());
assert!(cat.total_actions() >= 80);
}
#[test]
fn validate_known_mcp_tool() {
let cat = load_action_catalogue();
let action = PlanAction::McpTool {
name: "dcf_model".into(),
input_hint: json!({}),
};
assert!(validate_action(&cat, &action));
}
#[test]
fn validate_unknown_mcp_tool_rejected() {
let cat = load_action_catalogue();
let action = PlanAction::McpTool {
name: "ghost_tool".into(),
input_hint: json!({}),
};
assert!(!validate_action(&cat, &action));
}
#[test]
fn validate_known_slash_command() {
let cat = load_action_catalogue();
let action = PlanAction::SlashCommand {
name: "initiate-coverage".into(),
args: vec![],
};
assert!(validate_action(&cat, &action));
}
#[test]
fn validate_specialist_delegates_to_agent_invoke() {
let cat = load_action_catalogue();
let action = PlanAction::SpecialistAgent {
slug: "cfa-equity-analyst".into(),
brief: "x".into(),
};
assert!(validate_action(&cat, &action));
let bad = PlanAction::SpecialistAgent {
slug: "ghost-analyst".into(),
brief: "x".into(),
};
assert!(!validate_action(&cat, &bad));
}
}