1use crate::error::{Error, Result};
63use crate::mcp::tools::{Tool, ToolHandler, ToolResult};
64use crate::thinktool::modules::{
65 BedRock, BrutalHonesty, GigaThink, LaserLogic, ProofGuard, ThinkToolContext, ThinkToolModule,
66};
67use async_trait::async_trait;
68use serde_json::{json, Value};
69use std::collections::HashMap;
70use std::sync::Arc;
71use tracing::{debug, error, info, instrument};
72
73pub struct ThinkToolHandler {
90 gigathink: Arc<GigaThink>,
92 laserlogic: Arc<LaserLogic>,
94 bedrock: Arc<BedRock>,
96 proofguard: Arc<ProofGuard>,
98 brutalhonesty: Arc<BrutalHonesty>,
100}
101
102impl Default for ThinkToolHandler {
103 fn default() -> Self {
104 Self::new()
105 }
106}
107
108impl ThinkToolHandler {
109 pub fn new() -> Self {
111 Self {
112 gigathink: Arc::new(GigaThink::new()),
113 laserlogic: Arc::new(LaserLogic::new()),
114 bedrock: Arc::new(BedRock::new()),
115 proofguard: Arc::new(ProofGuard::new()),
116 brutalhonesty: Arc::new(BrutalHonesty::new()),
117 }
118 }
119
120 pub fn tool_definitions() -> Vec<Tool> {
124 vec![
125 Self::gigathink_tool(),
126 Self::laserlogic_tool(),
127 Self::bedrock_tool(),
128 Self::proofguard_tool(),
129 Self::brutalhonesty_tool(),
130 ]
131 }
132
133 fn gigathink_tool() -> Tool {
135 Tool::with_schema(
136 "gigathink",
137 "Expansive creative thinking - generates 10+ diverse perspectives across \
138 multiple analytical dimensions (economic, technological, social, environmental, \
139 political, psychological, ethical, historical, competitive, user experience, \
140 risk/opportunity, strategic). Use for brainstorming, exploring problem spaces, \
141 or generating comprehensive viewpoints on complex issues.",
142 json!({
143 "type": "object",
144 "properties": {
145 "query": {
146 "type": "string",
147 "description": "The question, problem, or topic to analyze from multiple perspectives",
148 "minLength": 10
149 },
150 "context": {
151 "type": "array",
152 "items": { "type": "string" },
153 "description": "Optional previous reasoning steps for chained execution",
154 "default": []
155 },
156 "min_perspectives": {
157 "type": "integer",
158 "description": "Minimum number of perspectives to generate",
159 "minimum": 5,
160 "maximum": 20,
161 "default": 10
162 }
163 },
164 "required": ["query"],
165 "additionalProperties": false
166 }),
167 )
168 }
169
170 fn laserlogic_tool() -> Tool {
172 Tool::with_schema(
173 "laserlogic",
174 "Precision deductive reasoning with fallacy detection - validates logical \
175 arguments, identifies formal fallacies (affirming consequent, denying antecedent, \
176 undistributed middle, illicit major/minor), detects contradictions, and assesses \
177 argument soundness. Use for analyzing claims, validating reasoning chains, or \
178 checking logical consistency.",
179 json!({
180 "type": "object",
181 "properties": {
182 "query": {
183 "type": "string",
184 "description": "The argument or statement to analyze logically"
185 },
186 "context": {
187 "type": "array",
188 "items": { "type": "string" },
189 "description": "Optional previous reasoning steps",
190 "default": []
191 },
192 "detect_fallacies": {
193 "type": "boolean",
194 "description": "Enable formal fallacy detection",
195 "default": true
196 },
197 "check_contradictions": {
198 "type": "boolean",
199 "description": "Enable contradiction detection",
200 "default": true
201 }
202 },
203 "required": ["query"],
204 "additionalProperties": false
205 }),
206 )
207 }
208
209 fn bedrock_tool() -> Tool {
211 Tool::with_schema(
212 "bedrock",
213 "First principles decomposition - breaks down complex problems into \
214 fundamental axioms, surfaces hidden assumptions, and identifies core \
215 building blocks. Use for understanding root causes, challenging assumptions, \
216 or building understanding from foundational concepts.",
217 json!({
218 "type": "object",
219 "properties": {
220 "query": {
221 "type": "string",
222 "description": "The problem or concept to decompose to first principles"
223 },
224 "context": {
225 "type": "array",
226 "items": { "type": "string" },
227 "description": "Optional previous reasoning steps",
228 "default": []
229 },
230 "decomposition_depth": {
231 "type": "integer",
232 "description": "Maximum depth of decomposition (levels of 'why')",
233 "minimum": 1,
234 "maximum": 5,
235 "default": 3
236 }
237 },
238 "required": ["query"],
239 "additionalProperties": false
240 }),
241 )
242 }
243
244 fn proofguard_tool() -> Tool {
246 Tool::with_schema(
247 "proofguard",
248 "Multi-source verification - validates claims through triangulation \
249 of evidence from multiple independent sources. Implements 3-source minimum \
250 requirement, source credibility tiers (primary, secondary, tertiary), and \
251 confidence scoring based on source agreement. Use for fact-checking, \
252 verifying claims, or assessing evidence quality.",
253 json!({
254 "type": "object",
255 "properties": {
256 "query": {
257 "type": "string",
258 "description": "The claim or statement to verify"
259 },
260 "context": {
261 "type": "array",
262 "items": { "type": "string" },
263 "description": "Optional previous reasoning steps",
264 "default": []
265 },
266 "min_sources": {
267 "type": "integer",
268 "description": "Minimum number of independent sources required",
269 "minimum": 2,
270 "maximum": 10,
271 "default": 3
272 },
273 "verification_strategy": {
274 "type": "string",
275 "description": "Strategy for source verification",
276 "enum": ["triangulation", "consensus", "hierarchical"],
277 "default": "triangulation"
278 }
279 },
280 "required": ["query"],
281 "additionalProperties": false
282 }),
283 )
284 }
285
286 fn brutalhonesty_tool() -> Tool {
288 Tool::with_schema(
289 "brutalhonesty",
290 "Adversarial self-critique - identifies weaknesses, biases, blind spots, \
291 and implicit assumptions in arguments or plans. Generates devil's advocate \
292 counterarguments, detects cognitive biases, and provides ruthless assessment \
293 of claims. Use for stress-testing ideas, challenging assumptions, or getting \
294 honest feedback on proposals.",
295 json!({
296 "type": "object",
297 "properties": {
298 "query": {
299 "type": "string",
300 "description": "The argument, plan, or idea to critique"
301 },
302 "context": {
303 "type": "array",
304 "items": { "type": "string" },
305 "description": "Optional previous reasoning steps",
306 "default": []
307 },
308 "severity": {
309 "type": "string",
310 "description": "Critique intensity level",
311 "enum": ["gentle", "moderate", "harsh", "ruthless"],
312 "default": "moderate"
313 },
314 "enable_devil_advocate": {
315 "type": "boolean",
316 "description": "Enable devil's advocate mode",
317 "default": true
318 },
319 "detect_cognitive_biases": {
320 "type": "boolean",
321 "description": "Enable cognitive bias detection",
322 "default": true
323 }
324 },
325 "required": ["query"],
326 "additionalProperties": false
327 }),
328 )
329 }
330
331 #[instrument(skip(self, arguments), fields(tool = %name))]
335 pub async fn call_tool(
336 &self,
337 name: &str,
338 arguments: HashMap<String, Value>,
339 ) -> Result<ToolResult> {
340 info!(tool = %name, "Executing ThinkTool");
341
342 match name {
343 "gigathink" => self.handle_gigathink(arguments).await,
344 "laserlogic" => self.handle_laserlogic(arguments).await,
345 "bedrock" => self.handle_bedrock(arguments).await,
346 "proofguard" => self.handle_proofguard(arguments).await,
347 "brutalhonesty" => self.handle_brutalhonesty(arguments).await,
348 _ => {
349 error!(tool = %name, "Unknown tool requested");
350 Ok(ToolResult::error(format!("Unknown ThinkTool: {}", name)))
351 }
352 }
353 }
354
355 async fn handle_gigathink(&self, args: HashMap<String, Value>) -> Result<ToolResult> {
357 let query = extract_required_string(&args, "query")?;
358 let context = extract_context(&args);
359
360 debug!(query = %query, context_len = context.len(), "Executing GigaThink");
361
362 let think_context = ThinkToolContext::with_previous_steps(query, context);
363 let output = self.gigathink.execute(&think_context)?;
364
365 format_output(output)
366 }
367
368 async fn handle_laserlogic(&self, args: HashMap<String, Value>) -> Result<ToolResult> {
370 let query = extract_required_string(&args, "query")?;
371 let context = extract_context(&args);
372
373 debug!(query = %query, "Executing LaserLogic");
374
375 let think_context = ThinkToolContext::with_previous_steps(query, context);
376 let output = self.laserlogic.execute(&think_context)?;
377
378 format_output(output)
379 }
380
381 async fn handle_bedrock(&self, args: HashMap<String, Value>) -> Result<ToolResult> {
383 let query = extract_required_string(&args, "query")?;
384 let context = extract_context(&args);
385
386 debug!(query = %query, "Executing BedRock");
387
388 let think_context = ThinkToolContext::with_previous_steps(query, context);
389 let output = self.bedrock.execute(&think_context)?;
390
391 format_output(output)
392 }
393
394 async fn handle_proofguard(&self, args: HashMap<String, Value>) -> Result<ToolResult> {
396 let query = extract_required_string(&args, "query")?;
397 let context = extract_context(&args);
398
399 debug!(query = %query, "Executing ProofGuard");
400
401 let think_context = ThinkToolContext::with_previous_steps(query, context);
402 let output = self.proofguard.execute(&think_context)?;
403
404 format_output(output)
405 }
406
407 async fn handle_brutalhonesty(&self, args: HashMap<String, Value>) -> Result<ToolResult> {
409 let query = extract_required_string(&args, "query")?;
410 let context = extract_context(&args);
411
412 debug!(query = %query, "Executing BrutalHonesty");
413
414 let think_context = ThinkToolContext::with_previous_steps(query, context);
415 let output = self.brutalhonesty.execute(&think_context)?;
416
417 format_output(output)
418 }
419}
420
421#[async_trait]
423impl ToolHandler for ThinkToolHandler {
424 async fn call(&self, arguments: HashMap<String, Value>) -> Result<ToolResult> {
425 let tool_name = arguments
427 .get("_tool")
428 .and_then(|v| v.as_str())
429 .map(|s| s.to_string())
430 .ok_or_else(|| Error::Mcp("Missing _tool identifier in arguments".into()))?;
431
432 self.call_tool(&tool_name, arguments).await
433 }
434}
435
436pub struct GigaThinkHandler {
454 module: Arc<GigaThink>,
455}
456
457impl Default for GigaThinkHandler {
458 fn default() -> Self {
459 Self::new()
460 }
461}
462
463impl GigaThinkHandler {
464 pub fn new() -> Self {
465 Self {
466 module: Arc::new(GigaThink::new()),
467 }
468 }
469}
470
471#[async_trait]
472impl ToolHandler for GigaThinkHandler {
473 async fn call(&self, arguments: HashMap<String, Value>) -> Result<ToolResult> {
474 let query = extract_required_string(&arguments, "query")?;
475 let context = extract_context(&arguments);
476 let think_context = ThinkToolContext::with_previous_steps(query, context);
477 let output = self.module.execute(&think_context)?;
478 format_output(output)
479 }
480}
481
482pub struct LaserLogicHandler {
496 module: Arc<LaserLogic>,
497}
498
499impl Default for LaserLogicHandler {
500 fn default() -> Self {
501 Self::new()
502 }
503}
504
505impl LaserLogicHandler {
506 pub fn new() -> Self {
507 Self {
508 module: Arc::new(LaserLogic::new()),
509 }
510 }
511}
512
513#[async_trait]
514impl ToolHandler for LaserLogicHandler {
515 async fn call(&self, arguments: HashMap<String, Value>) -> Result<ToolResult> {
516 let query = extract_required_string(&arguments, "query")?;
517 let context = extract_context(&arguments);
518 let think_context = ThinkToolContext::with_previous_steps(query, context);
519 let output = self.module.execute(&think_context)?;
520 format_output(output)
521 }
522}
523
524pub struct BedRockHandler {
538 module: Arc<BedRock>,
539}
540
541impl Default for BedRockHandler {
542 fn default() -> Self {
543 Self::new()
544 }
545}
546
547impl BedRockHandler {
548 pub fn new() -> Self {
549 Self {
550 module: Arc::new(BedRock::new()),
551 }
552 }
553}
554
555#[async_trait]
556impl ToolHandler for BedRockHandler {
557 async fn call(&self, arguments: HashMap<String, Value>) -> Result<ToolResult> {
558 let query = extract_required_string(&arguments, "query")?;
559 let context = extract_context(&arguments);
560 let think_context = ThinkToolContext::with_previous_steps(query, context);
561 let output = self.module.execute(&think_context)?;
562 format_output(output)
563 }
564}
565
566pub struct ProofGuardHandler {
580 module: Arc<ProofGuard>,
581}
582
583impl Default for ProofGuardHandler {
584 fn default() -> Self {
585 Self::new()
586 }
587}
588
589impl ProofGuardHandler {
590 pub fn new() -> Self {
591 Self {
592 module: Arc::new(ProofGuard::new()),
593 }
594 }
595}
596
597#[async_trait]
598impl ToolHandler for ProofGuardHandler {
599 async fn call(&self, arguments: HashMap<String, Value>) -> Result<ToolResult> {
600 let query = extract_required_string(&arguments, "query")?;
601 let context = extract_context(&arguments);
602 let think_context = ThinkToolContext::with_previous_steps(query, context);
603 let output = self.module.execute(&think_context)?;
604 format_output(output)
605 }
606}
607
608pub struct BrutalHonestyHandler {
622 module: Arc<BrutalHonesty>,
623}
624
625impl Default for BrutalHonestyHandler {
626 fn default() -> Self {
627 Self::new()
628 }
629}
630
631impl BrutalHonestyHandler {
632 pub fn new() -> Self {
633 Self {
634 module: Arc::new(BrutalHonesty::new()),
635 }
636 }
637}
638
639#[async_trait]
640impl ToolHandler for BrutalHonestyHandler {
641 async fn call(&self, arguments: HashMap<String, Value>) -> Result<ToolResult> {
642 let query = extract_required_string(&arguments, "query")?;
643 let context = extract_context(&arguments);
644 let think_context = ThinkToolContext::with_previous_steps(query, context);
645 let output = self.module.execute(&think_context)?;
646 format_output(output)
647 }
648}
649
650pub async fn register_thinktools<T: crate::mcp::McpServerTrait + ?Sized>(server: &T) {
663 server
665 .register_tool(
666 ThinkToolHandler::gigathink_tool(),
667 Arc::new(GigaThinkHandler::new()),
668 )
669 .await;
670
671 server
673 .register_tool(
674 ThinkToolHandler::laserlogic_tool(),
675 Arc::new(LaserLogicHandler::new()),
676 )
677 .await;
678
679 server
681 .register_tool(
682 ThinkToolHandler::bedrock_tool(),
683 Arc::new(BedRockHandler::new()),
684 )
685 .await;
686
687 server
689 .register_tool(
690 ThinkToolHandler::proofguard_tool(),
691 Arc::new(ProofGuardHandler::new()),
692 )
693 .await;
694
695 server
697 .register_tool(
698 ThinkToolHandler::brutalhonesty_tool(),
699 Arc::new(BrutalHonestyHandler::new()),
700 )
701 .await;
702
703 tracing::info!(
704 "Registered 5 ThinkTools: gigathink, laserlogic, bedrock, proofguard, brutalhonesty"
705 );
706}
707
708fn extract_required_string(args: &HashMap<String, Value>, key: &str) -> Result<String> {
714 args.get(key)
715 .and_then(|v| v.as_str())
716 .map(|s| s.to_string())
717 .ok_or_else(|| Error::Mcp(format!("Missing required argument: {}", key)))
718}
719
720fn extract_context(args: &HashMap<String, Value>) -> Vec<String> {
722 args.get("context")
723 .and_then(|v| v.as_array())
724 .map(|arr| {
725 arr.iter()
726 .filter_map(|v| v.as_str().map(String::from))
727 .collect()
728 })
729 .unwrap_or_default()
730}
731
732fn format_output(output: crate::thinktool::modules::ThinkToolOutput) -> Result<ToolResult> {
734 let formatted = json!({
735 "module": output.module,
736 "confidence": output.confidence,
737 "result": output.output
738 });
739
740 Ok(ToolResult::text(serde_json::to_string_pretty(&formatted)?))
741}
742
743#[cfg(test)]
748mod tests {
749 use super::*;
750
751 #[test]
752 fn test_handler_creation() {
753 let handler = ThinkToolHandler::new();
754 assert!(Arc::strong_count(&handler.gigathink) > 0);
755 }
756
757 #[test]
758 fn test_tool_definitions_count() {
759 let tools = ThinkToolHandler::tool_definitions();
760 assert_eq!(tools.len(), 5, "Should have 5 ThinkTool definitions");
761 }
762
763 #[test]
764 fn test_tool_definitions_names() {
765 let tools = ThinkToolHandler::tool_definitions();
766 let names: Vec<&str> = tools.iter().map(|t| t.name.as_str()).collect();
767
768 assert!(names.contains(&"gigathink"));
769 assert!(names.contains(&"laserlogic"));
770 assert!(names.contains(&"bedrock"));
771 assert!(names.contains(&"proofguard"));
772 assert!(names.contains(&"brutalhonesty"));
773 }
774
775 #[test]
776 fn test_tool_definitions_have_descriptions() {
777 let tools = ThinkToolHandler::tool_definitions();
778 for tool in tools {
779 assert!(
780 tool.description.is_some(),
781 "Tool {} should have description",
782 tool.name
783 );
784 }
785 }
786
787 #[test]
788 fn test_tool_definitions_have_schemas() {
789 let tools = ThinkToolHandler::tool_definitions();
790 for tool in tools {
791 assert!(
792 tool.input_schema.is_object(),
793 "Tool {} should have JSON schema",
794 tool.name
795 );
796
797 let required = tool.input_schema.get("required").unwrap();
799 assert!(
800 required.as_array().unwrap().contains(&json!("query")),
801 "Tool {} should require 'query' argument",
802 tool.name
803 );
804 }
805 }
806
807 #[test]
808 fn test_extract_required_string_success() {
809 let mut args = HashMap::new();
810 args.insert("query".to_string(), json!("test query"));
811
812 let result = extract_required_string(&args, "query");
813 assert!(result.is_ok());
814 assert_eq!(result.unwrap(), "test query");
815 }
816
817 #[test]
818 fn test_extract_required_string_missing() {
819 let args = HashMap::new();
820 let result = extract_required_string(&args, "query");
821 assert!(result.is_err());
822 }
823
824 #[test]
825 fn test_extract_context_present() {
826 let mut args = HashMap::new();
827 args.insert("context".to_string(), json!(["step 1", "step 2"]));
828
829 let context = extract_context(&args);
830 assert_eq!(context.len(), 2);
831 assert_eq!(context[0], "step 1");
832 }
833
834 #[test]
835 fn test_extract_context_missing() {
836 let args = HashMap::new();
837 let context = extract_context(&args);
838 assert!(context.is_empty());
839 }
840
841 #[tokio::test]
842 async fn test_gigathink_execution() {
843 let handler = ThinkToolHandler::new();
844 let mut args = HashMap::new();
845 args.insert(
846 "query".to_string(),
847 json!("What are the implications of AI in healthcare?"),
848 );
849
850 let result = handler.handle_gigathink(args).await;
851 assert!(result.is_ok());
852
853 let tool_result = result.unwrap();
854 assert!(tool_result.is_error.is_none() || !tool_result.is_error.unwrap());
855 }
856
857 #[tokio::test]
858 async fn test_laserlogic_execution() {
859 let handler = ThinkToolHandler::new();
860 let mut args = HashMap::new();
861 args.insert(
862 "query".to_string(),
863 json!("All birds can fly. Penguins are birds. Therefore, penguins can fly."),
864 );
865
866 let result = handler.handle_laserlogic(args).await;
867 assert!(result.is_ok());
868 }
869
870 #[tokio::test]
871 async fn test_bedrock_execution() {
872 let handler = ThinkToolHandler::new();
873 let mut args = HashMap::new();
874 args.insert(
875 "query".to_string(),
876 json!("Why do companies need to innovate?"),
877 );
878
879 let result = handler.handle_bedrock(args).await;
880 assert!(result.is_ok());
881 }
882
883 #[tokio::test]
884 async fn test_proofguard_execution() {
885 let handler = ThinkToolHandler::new();
886 let mut args = HashMap::new();
887 args.insert(
888 "query".to_string(),
889 json!("Climate change is primarily caused by human activity"),
890 );
891
892 let result = handler.handle_proofguard(args).await;
893 assert!(result.is_ok());
894 }
895
896 #[tokio::test]
897 async fn test_brutalhonesty_execution() {
898 let handler = ThinkToolHandler::new();
899 let mut args = HashMap::new();
900 args.insert(
901 "query".to_string(),
902 json!("Our startup will succeed because we have the best team"),
903 );
904
905 let result = handler.handle_brutalhonesty(args).await;
906 assert!(result.is_ok());
907 }
908
909 #[tokio::test]
910 async fn test_unknown_tool() {
911 let handler = ThinkToolHandler::new();
912 let args = HashMap::new();
913
914 let result = handler.call_tool("nonexistent", args).await;
915 assert!(result.is_ok());
916
917 let tool_result = result.unwrap();
918 assert_eq!(tool_result.is_error, Some(true));
919 }
920
921 #[tokio::test]
922 async fn test_missing_query_argument() {
923 let handler = ThinkToolHandler::new();
924 let args = HashMap::new(); let result = handler.handle_gigathink(args).await;
927 assert!(result.is_err());
928 }
929}