Skip to main content

agentic_evolve_mcp/protocol/
compact.rs

1//! Compact facade layer — groups 14 tools into 3 compact facades.
2//!
3//! Env var: `AEVOLVE_MCP_TOOL_SURFACE` (fallback `MCP_TOOL_SURFACE`).
4//! Set to "compact" to enable facade mode.
5
6use serde_json::Value;
7
8use crate::types::ToolDefinition;
9
10/// A facade grouping that maps one compact tool to multiple underlying operations.
11struct FacadeGroup {
12    name: &'static str,
13    description: &'static str,
14    operations: &'static [&'static str],
15}
16
17const FACADES: &[FacadeGroup] = &[
18    FacadeGroup {
19        name: "evolve_patterns",
20        description: "Store, retrieve, delete, search, and list evolution patterns",
21        operations: &[
22            "pattern_store",
23            "pattern_get",
24            "pattern_delete",
25            "pattern_search",
26            "pattern_list",
27        ],
28    },
29    FacadeGroup {
30        name: "evolve_matching",
31        description: "Match patterns by signature or context, crystallize, compose, and retrieve bodies",
32        operations: &[
33            "match_signature",
34            "match_context",
35            "crystallize",
36            "compose",
37            "get_body",
38        ],
39    },
40    FacadeGroup {
41        name: "evolve_analytics",
42        description: "Check coverage and confidence, update usage stats, and optimize the pattern library",
43        operations: &[
44            "coverage",
45            "confidence",
46            "update_usage",
47            "optimize",
48        ],
49    },
50];
51
52/// Check whether compact tool mode is enabled via environment variable.
53pub fn is_compact_mode() -> bool {
54    let val = std::env::var("AEVOLVE_MCP_TOOL_SURFACE")
55        .or_else(|_| std::env::var("MCP_TOOL_SURFACE"))
56        .unwrap_or_default();
57    val.eq_ignore_ascii_case("compact")
58}
59
60/// Build the compact facade tool definitions for tools/list.
61pub fn compact_tool_definitions() -> Vec<ToolDefinition> {
62    FACADES
63        .iter()
64        .map(|f| {
65            let ops_enum: Vec<Value> = f
66                .operations
67                .iter()
68                .map(|o| Value::String(o.to_string()))
69                .collect();
70
71            ToolDefinition {
72                name: f.name.to_string(),
73                description: Some(f.description.to_string()),
74                input_schema: serde_json::json!({
75                    "type": "object",
76                    "properties": {
77                        "operation": {
78                            "type": "string",
79                            "enum": ops_enum,
80                            "description": "Operation to perform"
81                        },
82                        "params": {
83                            "type": "object",
84                            "description": "Parameters for the operation"
85                        }
86                    },
87                    "required": ["operation"]
88                }),
89            }
90        })
91        .collect()
92}
93
94/// Normalize a compact facade call into the underlying tool name and arguments.
95///
96/// Mapping: facade "evolve_patterns" + operation "pattern_store"
97///          -> tool "evolve_pattern_store"
98pub fn normalize_compact_call(
99    facade_name: &str,
100    arguments: &Option<Value>,
101) -> Option<(String, Option<Value>)> {
102    let facade = FACADES.iter().find(|f| f.name == facade_name)?;
103
104    let args = arguments.as_ref().unwrap_or(&Value::Null);
105    let operation = args.get("operation").and_then(|v| v.as_str())?;
106
107    if !facade.operations.contains(&operation) {
108        return None;
109    }
110
111    let real_name = format!("evolve_{}", operation);
112    let params = args.get("params").cloned();
113
114    Some((real_name, params))
115}
116
117/// Check if a tool name is a compact facade name.
118pub fn is_compact_facade(name: &str) -> bool {
119    FACADES.iter().any(|f| f.name == name)
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn compact_definitions_count() {
128        let defs = compact_tool_definitions();
129        assert_eq!(defs.len(), 3);
130        assert_eq!(defs[0].name, "evolve_patterns");
131        assert_eq!(defs[1].name, "evolve_matching");
132        assert_eq!(defs[2].name, "evolve_analytics");
133    }
134
135    #[test]
136    fn normalize_patterns_facade() {
137        let args = Some(serde_json::json!({
138            "operation": "pattern_store",
139            "params": { "name": "test", "domain": "web" }
140        }));
141        let (name, params) = normalize_compact_call("evolve_patterns", &args).unwrap();
142        assert_eq!(name, "evolve_pattern_store");
143        assert_eq!(
144            params.unwrap().get("name").unwrap().as_str().unwrap(),
145            "test"
146        );
147    }
148
149    #[test]
150    fn normalize_matching_facade() {
151        let args = Some(serde_json::json!({
152            "operation": "crystallize",
153            "params": { "pattern_id": "p1" }
154        }));
155        let (name, _) = normalize_compact_call("evolve_matching", &args).unwrap();
156        assert_eq!(name, "evolve_crystallize");
157    }
158
159    #[test]
160    fn normalize_analytics_facade() {
161        let args = Some(serde_json::json!({
162            "operation": "coverage",
163            "params": {}
164        }));
165        let (name, _) = normalize_compact_call("evolve_analytics", &args).unwrap();
166        assert_eq!(name, "evolve_coverage");
167    }
168
169    #[test]
170    fn normalize_unknown_facade_returns_none() {
171        let args = Some(serde_json::json!({ "operation": "pattern_store" }));
172        assert!(normalize_compact_call("evolve_unknown", &args).is_none());
173    }
174
175    #[test]
176    fn normalize_invalid_operation_returns_none() {
177        let args = Some(serde_json::json!({ "operation": "nonexistent" }));
178        assert!(normalize_compact_call("evolve_patterns", &args).is_none());
179    }
180
181    #[test]
182    fn is_compact_facade_checks() {
183        assert!(is_compact_facade("evolve_patterns"));
184        assert!(is_compact_facade("evolve_matching"));
185        assert!(is_compact_facade("evolve_analytics"));
186        assert!(!is_compact_facade("evolve_pattern_store"));
187    }
188
189    #[test]
190    fn compact_mode_off_by_default() {
191        // Without env var set, should be false
192        // (This test is valid as long as the env var isn't set in CI)
193        assert!(!is_compact_mode());
194    }
195}