Skip to main content

agentic_workflow_mcp/tools/
template_tools.rs

1use serde_json::json;
2use std::collections::HashMap;
3
4use crate::types::{ToolDefinition, ToolResult};
5use super::registry::EngineState;
6
7fn def(name: &str, desc: &str, props: serde_json::Value) -> ToolDefinition {
8    ToolDefinition {
9        name: name.to_string(),
10        description: desc.to_string(),
11        input_schema: json!({ "type": "object", "properties": props }),
12    }
13}
14
15fn s(d: &str) -> serde_json::Value { json!({ "type": "string", "description": d }) }
16
17pub fn definitions() -> Vec<ToolDefinition> {
18    vec![
19        // Template (5)
20        def("workflow_template_list", "List available workflow templates",
21            json!({ "tag": s("Filter by tag") })),
22        def("workflow_template_use", "Instantiate a workflow from a template with parameters",
23            json!({ "template_id": s("Template ID"), "params": json!({ "type": "object", "description": "Template parameters" }) })),
24        def("workflow_template_create", "Create a reusable workflow template",
25            json!({ "name": s("Template name"), "description": s("Description"), "workflow_definition": json!({ "type": "object", "description": "Workflow JSON" }), "tags": json!({ "type": "array", "items": { "type": "string" } }), "author": s("Author") })),
26        def("workflow_template_share", "Share a template with the community",
27            json!({ "template_id": s("Template ID"), "shared_by": s("Sharer identity") })),
28        def("workflow_template_compose", "Compose multiple templates into a pipeline",
29            json!({ "template_ids": json!({ "type": "array", "items": { "type": "string" }, "description": "Template IDs to compose" }), "name": s("Composed workflow name") })),
30        // Natural language (5)
31        def("workflow_natural_create", "Create a workflow from a natural language description",
32            json!({ "description": s("Plain-English workflow description") })),
33        def("workflow_natural_preview", "Preview the synthesized workflow from a natural language request",
34            json!({ "request_index": json!({ "type": "integer", "description": "Request index" }) })),
35        def("workflow_natural_clarify", "Add a clarification question to a natural language request",
36            json!({ "request_index": json!({ "type": "integer", "description": "Request index" }), "question": s("Clarification question"), "options": json!({ "type": "array", "items": { "type": "string" } }) })),
37        def("workflow_natural_refine", "Answer a clarification to refine the workflow",
38            json!({ "request_index": json!({ "type": "integer", "description": "Request index" }), "clarification_index": json!({ "type": "integer", "description": "Clarification index" }), "answer": s("Answer to clarification") })),
39        // Composition algebra (5)
40        def("workflow_compose_sequence", "Compose workflows in sequence: A then B then C",
41            json!({ "name": s("Composition name"), "workflow_ids": json!({ "type": "array", "items": { "type": "string" }, "description": "Workflow IDs in order" }) })),
42        def("workflow_compose_parallel", "Compose workflows in parallel: A and B and C simultaneously",
43            json!({ "name": s("Composition name"), "workflow_ids": json!({ "type": "array", "items": { "type": "string" } }) })),
44        def("workflow_compose_conditional", "Compose workflows conditionally: if P then A else B",
45            json!({ "name": s("Composition name"), "predicate": s("Condition expression"), "if_true": s("Workflow if true"), "if_false": s("Workflow if false") })),
46        def("workflow_compose_validate", "Validate a composed meta-workflow",
47            json!({ "meta_id": s("Meta-workflow ID") })),
48        def("workflow_compose_run", "Execute a composed meta-workflow",
49            json!({ "meta_id": s("Meta-workflow ID") })),
50        // Collective (4)
51        def("workflow_collective_share", "Share a workflow with the collective community",
52            json!({ "name": s("Workflow name"), "description": s("Description"), "workflow_definition": json!({ "type": "object", "description": "Workflow JSON" }), "author": s("Author"), "tags": json!({ "type": "array", "items": { "type": "string" } }) })),
53        def("workflow_collective_search", "Search community-shared workflows",
54            json!({ "query": s("Search query") })),
55        def("workflow_collective_apply", "Download and apply a community workflow",
56            json!({ "id": s("Collective item ID") })),
57        def("workflow_collective_rate", "Rate a community workflow",
58            json!({ "id": s("Collective item ID"), "rating": json!({ "type": "number", "description": "Rating 0-5" }) })),
59        def("workflow_collective_private", "Verify no private data in a shared workflow",
60            json!({ "id": s("Collective item ID") })),
61    ]
62}
63
64pub fn dispatch(
65    name: &str,
66    params: serde_json::Value,
67    state: &mut EngineState,
68) -> Result<ToolResult, (i32, String)> {
69    match name {
70        // --- Template ---
71        "workflow_template_list" => {
72            let templates = if let Some(tag) = params["tag"].as_str() {
73                state.template.search_by_tag(tag)
74            } else {
75                state.template.list_templates()
76            };
77            let items: Vec<_> = templates.iter().map(|t| json!({
78                "id": t.id, "name": t.name, "tags": t.tags, "usage_count": t.usage_count
79            })).collect();
80            Ok(ToolResult::text(json!({ "templates": items }).to_string()))
81        }
82        "workflow_template_use" => {
83            let tid = params["template_id"].as_str().unwrap_or("");
84            let p: HashMap<String, serde_json::Value> = params["params"]
85                .as_object()
86                .map(|o| o.iter().map(|(k, v)| (k.clone(), v.clone())).collect())
87                .unwrap_or_default();
88            match state.template.instantiate(tid, &p) {
89                Ok(wf) => Ok(ToolResult::text(json!({
90                    "template_id": tid, "workflow": wf, "status": "instantiated"
91                }).to_string())),
92                Err(e) => Ok(ToolResult::error(format!("{}", e))),
93            }
94        }
95        "workflow_template_create" => {
96            let tname = params["name"].as_str().unwrap_or("");
97            let desc = params["description"].as_str().unwrap_or("");
98            let wf_def = params["workflow_definition"].clone();
99            let tags: Vec<String> = params["tags"].as_array()
100                .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect())
101                .unwrap_or_default();
102            let author = params["author"].as_str().unwrap_or("anonymous");
103            match state.template.create_template(tname, desc, Vec::new(), wf_def, tags, author) {
104                Ok(tid) => Ok(ToolResult::text(json!({ "template_id": tid, "status": "created" }).to_string())),
105                Err(e) => Ok(ToolResult::error(format!("{}", e))),
106            }
107        }
108        "workflow_template_share" => {
109            let tid = params["template_id"].as_str().unwrap_or("");
110            let by = params["shared_by"].as_str().unwrap_or("anonymous");
111            match state.template.share_template(tid, by) {
112                Ok(sid) => Ok(ToolResult::text(json!({ "shared_id": sid, "status": "shared" }).to_string())),
113                Err(e) => Ok(ToolResult::error(format!("{}", e))),
114            }
115        }
116        "workflow_template_compose" => {
117            let ids: Vec<String> = params["template_ids"].as_array()
118                .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect())
119                .unwrap_or_default();
120            let cname = params["name"].as_str().unwrap_or("composed");
121            match state.composer.sequence(cname, ids) {
122                Ok(mid) => Ok(ToolResult::text(json!({ "meta_id": mid, "status": "composed" }).to_string())),
123                Err(e) => Ok(ToolResult::error(format!("{}", e))),
124            }
125        }
126        // --- Natural language ---
127        "workflow_natural_create" => {
128            let desc = params["description"].as_str().unwrap_or("");
129            let idx = state.natural.create_request(desc);
130            Ok(ToolResult::text(json!({ "request_index": idx, "status": "created" }).to_string()))
131        }
132        "workflow_natural_preview" => {
133            let idx = params["request_index"].as_u64().unwrap_or(0) as usize;
134            match state.natural.get_request(idx) {
135                Some(req) => Ok(ToolResult::text(json!({
136                    "description": req.description,
137                    "clarifications": req.clarifications.len(),
138                    "synthesized": req.synthesized_workflow.is_some()
139                }).to_string())),
140                None => Ok(ToolResult::error("Request not found")),
141            }
142        }
143        "workflow_natural_clarify" => {
144            let idx = params["request_index"].as_u64().unwrap_or(0) as usize;
145            let question = params["question"].as_str().unwrap_or("");
146            let options = params["options"].as_array().map(|a| {
147                a.iter().filter_map(|v| v.as_str().map(String::from)).collect()
148            });
149            match state.natural.add_clarification(idx, question, options) {
150                Ok(()) => Ok(ToolResult::text(json!({ "status": "clarification_added" }).to_string())),
151                Err(e) => Ok(ToolResult::error(format!("{}", e))),
152            }
153        }
154        "workflow_natural_refine" => {
155            let idx = params["request_index"].as_u64().unwrap_or(0) as usize;
156            let cidx = params["clarification_index"].as_u64().unwrap_or(0) as usize;
157            let answer = params["answer"].as_str().unwrap_or("");
158            match state.natural.answer_clarification(idx, cidx, answer) {
159                Ok(()) => Ok(ToolResult::text(json!({ "status": "refined" }).to_string())),
160                Err(e) => Ok(ToolResult::error(format!("{}", e))),
161            }
162        }
163        // --- Composition ---
164        "workflow_compose_sequence" => {
165            let cname = params["name"].as_str().unwrap_or("sequence");
166            let ids: Vec<String> = params["workflow_ids"].as_array()
167                .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect())
168                .unwrap_or_default();
169            match state.composer.sequence(cname, ids) {
170                Ok(mid) => Ok(ToolResult::text(json!({ "meta_id": mid, "status": "created" }).to_string())),
171                Err(e) => Ok(ToolResult::error(format!("{}", e))),
172            }
173        }
174        "workflow_compose_parallel" => {
175            let cname = params["name"].as_str().unwrap_or("parallel");
176            let ids: Vec<String> = params["workflow_ids"].as_array()
177                .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect())
178                .unwrap_or_default();
179            match state.composer.parallel(cname, ids) {
180                Ok(mid) => Ok(ToolResult::text(json!({ "meta_id": mid, "status": "created" }).to_string())),
181                Err(e) => Ok(ToolResult::error(format!("{}", e))),
182            }
183        }
184        "workflow_compose_conditional" => {
185            let cname = params["name"].as_str().unwrap_or("conditional");
186            let pred = params["predicate"].as_str().unwrap_or("true");
187            let if_t = params["if_true"].as_str().unwrap_or("");
188            let if_f = params["if_false"].as_str().unwrap_or("");
189            match state.composer.conditional(cname, pred, if_t, if_f) {
190                Ok(mid) => Ok(ToolResult::text(json!({ "meta_id": mid, "status": "created" }).to_string())),
191                Err(e) => Ok(ToolResult::error(format!("{}", e))),
192            }
193        }
194        "workflow_compose_validate" => {
195            let mid = params["meta_id"].as_str().unwrap_or("");
196            match state.composer.validate(mid) {
197                Ok(warnings) => Ok(ToolResult::text(json!({
198                    "meta_id": mid, "valid": warnings.is_empty(), "warnings": warnings
199                }).to_string())),
200                Err(e) => Ok(ToolResult::error(format!("{}", e))),
201            }
202        }
203        "workflow_compose_run" => {
204            let mid = params["meta_id"].as_str().unwrap_or("");
205            match state.composer.get_meta(mid) {
206                Ok(meta) => Ok(ToolResult::text(json!({
207                    "meta_id": meta.id, "name": meta.name, "status": "execution_started"
208                }).to_string())),
209                Err(e) => Ok(ToolResult::error(format!("{}", e))),
210            }
211        }
212        // --- Collective ---
213        "workflow_collective_share" => {
214            let cname = params["name"].as_str().unwrap_or("");
215            let desc = params["description"].as_str().unwrap_or("");
216            let wf_def = params["workflow_definition"].clone();
217            let author = params["author"].as_str().unwrap_or("anonymous");
218            let tags: Vec<String> = params["tags"].as_array()
219                .map(|a| a.iter().filter_map(|v| v.as_str().map(String::from)).collect())
220                .unwrap_or_default();
221            let id = state.collective.share(cname, desc, wf_def, author, tags);
222            Ok(ToolResult::text(json!({ "id": id, "status": "shared" }).to_string()))
223        }
224        "workflow_collective_search" => {
225            let query = params["query"].as_str().unwrap_or("");
226            let results = state.collective.search(query);
227            let items: Vec<_> = results.iter().map(|r| json!({
228                "id": r.id, "name": r.name, "rating": r.rating, "downloads": r.download_count
229            })).collect();
230            Ok(ToolResult::text(json!({ "results": items }).to_string()))
231        }
232        "workflow_collective_apply" => {
233            let id = params["id"].as_str().unwrap_or("");
234            match state.collective.apply(id) {
235                Some(wf) => Ok(ToolResult::text(json!({ "id": id, "workflow": wf, "status": "applied" }).to_string())),
236                None => Ok(ToolResult::error(format!("Collective item not found: {}", id))),
237            }
238        }
239        "workflow_collective_rate" => {
240            let id = params["id"].as_str().unwrap_or("");
241            let rating = params["rating"].as_f64().unwrap_or(3.0);
242            if state.collective.rate(id, rating) {
243                Ok(ToolResult::text(json!({ "id": id, "rating": rating, "status": "rated" }).to_string()))
244            } else {
245                Ok(ToolResult::error(format!("Collective item not found: {}", id)))
246            }
247        }
248        "workflow_collective_private" => {
249            let id = params["id"].as_str().unwrap_or("");
250            let clean = state.collective.verify_privacy(id);
251            Ok(ToolResult::text(json!({ "id": id, "privacy_verified": clean }).to_string()))
252        }
253        _ => Ok(ToolResult::error(format!("Unknown template tool: {}", name))),
254    }
255}