1use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
11use tracing::{debug, error, info};
12
13use super::{handle_tool_call, SharedState};
14
15#[cfg(test)]
16#[path = "mcp_tests.rs"]
17mod mcp_tests;
18
19#[derive(Debug, Deserialize)]
21pub struct JsonRpcRequest {
22 pub jsonrpc: String,
24 pub id: Option<Value>,
26 pub method: String,
28 #[serde(default)]
30 pub params: Option<Value>,
31}
32
33#[derive(Debug, Serialize)]
35pub struct JsonRpcResponse {
36 pub jsonrpc: String,
38 pub id: Value,
40 #[serde(skip_serializing_if = "Option::is_none")]
42 pub result: Option<Value>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub error: Option<JsonRpcError>,
46}
47
48#[derive(Debug, Serialize)]
50pub struct JsonRpcError {
51 pub code: i32,
53 pub message: String,
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub data: Option<Value>,
58}
59
60#[derive(Debug, Serialize)]
62pub struct ServerInfo {
63 pub name: String,
65 pub version: String,
67}
68
69#[derive(Debug, Serialize)]
71pub struct Capabilities {
72 pub tools: ToolCapabilities,
74}
75
76#[derive(Debug, Serialize)]
78pub struct ToolCapabilities {
79 #[serde(rename = "listChanged")]
81 pub list_changed: bool,
82}
83
84#[derive(Debug, Serialize)]
86pub struct InitializeResult {
87 #[serde(rename = "protocolVersion")]
89 pub protocol_version: String,
90 pub capabilities: Capabilities,
92 #[serde(rename = "serverInfo")]
94 pub server_info: ServerInfo,
95}
96
97#[derive(Debug, Clone, Serialize)]
99pub struct Tool {
100 pub name: String,
102 pub description: String,
104 #[serde(rename = "inputSchema")]
106 pub input_schema: Value,
107}
108
109#[derive(Debug, Deserialize)]
111pub struct ToolCallParams {
112 pub name: String,
114 #[serde(default)]
116 pub arguments: Option<Value>,
117}
118
119#[derive(Debug, Serialize)]
121pub struct ToolResultContent {
122 #[serde(rename = "type")]
124 pub content_type: String,
125 pub text: String,
127}
128
129#[derive(Debug, Serialize)]
131pub struct ToolCallResult {
132 pub content: Vec<ToolResultContent>,
134 #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
136 pub is_error: Option<bool>,
137}
138
139impl JsonRpcResponse {
140 pub fn success(id: Option<Value>, result: Value) -> Self {
142 Self {
143 jsonrpc: "2.0".to_string(),
144 id: id.unwrap_or(Value::Null),
145 result: Some(result),
146 error: None,
147 }
148 }
149
150 pub fn error(id: Option<Value>, code: i32, message: impl Into<String>) -> Self {
152 Self {
153 jsonrpc: "2.0".to_string(),
154 id: id.unwrap_or(Value::Null),
155 result: None,
156 error: Some(JsonRpcError {
157 code,
158 message: message.into(),
159 data: None,
160 }),
161 }
162 }
163}
164
165pub struct McpServer {
170 state: SharedState,
172}
173
174impl McpServer {
175 pub fn new(state: SharedState) -> Self {
177 Self { state }
178 }
179
180 pub async fn run(&self) -> std::io::Result<()> {
182 info!("MCP Langbase Reasoning Server starting...");
183
184 let stdin = tokio::io::stdin();
185 let mut stdout = tokio::io::stdout();
186 let mut reader = BufReader::new(stdin);
187 let mut line = String::new();
188
189 loop {
190 line.clear();
191 let bytes_read = reader.read_line(&mut line).await?;
192
193 if bytes_read == 0 {
195 info!("EOF received, shutting down");
196 break;
197 }
198
199 let trimmed = line.trim();
200 if trimmed.is_empty() {
201 continue;
202 }
203
204 debug!(request = %trimmed, "Received request");
205
206 let response = match serde_json::from_str::<JsonRpcRequest>(trimmed) {
207 Ok(request) => self.handle_request(request).await,
208 Err(e) => {
209 error!(error = %e, "Failed to parse request");
210 Some(JsonRpcResponse::error(
211 None,
212 -32700,
213 format!("Parse error: {}", e),
214 ))
215 }
216 };
217
218 if let Some(response) = response {
220 let response_json = serde_json::to_string(&response)?;
221 debug!(response = %response_json, "Sending response");
222
223 stdout.write_all(response_json.as_bytes()).await?;
224 stdout.write_all(b"\n").await?;
225 stdout.flush().await?;
226 }
227 }
228
229 Ok(())
230 }
231
232 async fn handle_request(&self, request: JsonRpcRequest) -> Option<JsonRpcResponse> {
235 let is_notification = request.id.is_none();
237
238 match request.method.as_str() {
239 "initialize" => Some(self.handle_initialize(request.id)),
240 "initialized" => {
241 debug!("Received initialized notification");
243 None
244 }
245 "notifications/cancelled" => {
246 debug!("Received cancelled notification");
248 None
249 }
250 "tools/list" => Some(self.handle_tools_list(request.id)),
251 "tools/call" => Some(self.handle_tool_call(request.id, request.params).await),
252 "ping" => Some(JsonRpcResponse::success(
253 request.id,
254 Value::Object(Default::default()),
255 )),
256 method => {
257 if is_notification {
259 debug!(method = %method, "Unknown notification, ignoring");
260 None
261 } else {
262 error!(method = %method, "Unknown method");
263 Some(JsonRpcResponse::error(
264 request.id,
265 -32601,
266 format!("Method not found: {}", method),
267 ))
268 }
269 }
270 }
271 }
272
273 fn handle_initialize(&self, id: Option<Value>) -> JsonRpcResponse {
275 info!("Handling initialize request");
276
277 let result = InitializeResult {
278 protocol_version: "2024-11-05".to_string(),
279 capabilities: Capabilities {
280 tools: ToolCapabilities {
281 list_changed: false,
282 },
283 },
284 server_info: ServerInfo {
285 name: "mcp-langbase-reasoning".to_string(),
286 version: env!("CARGO_PKG_VERSION").to_string(),
287 },
288 };
289
290 match serde_json::to_value(result) {
291 Ok(val) => JsonRpcResponse::success(id, val),
292 Err(e) => {
293 error!(error = %e, "Failed to serialize initialize result");
294 JsonRpcResponse::error(id, -32603, format!("Internal error: {}", e))
295 }
296 }
297 }
298
299 fn handle_tools_list(&self, id: Option<Value>) -> JsonRpcResponse {
301 info!("Handling tools/list request");
302
303 let tools = vec![
304 get_linear_tool(),
306 get_tree_tool(),
307 get_tree_focus_tool(),
308 get_tree_list_tool(),
309 get_tree_complete_tool(),
310 get_divergent_tool(),
311 get_reflection_tool(),
312 get_reflection_evaluate_tool(),
313 get_backtracking_tool(),
315 get_backtracking_checkpoint_tool(),
316 get_backtracking_list_tool(),
317 get_auto_tool(),
318 get_got_init_tool(),
319 get_got_generate_tool(),
320 get_got_score_tool(),
321 get_got_aggregate_tool(),
322 get_got_refine_tool(),
323 get_got_prune_tool(),
324 get_got_finalize_tool(),
325 get_got_state_tool(),
326 get_detect_biases_tool(),
328 get_detect_fallacies_tool(),
329 get_preset_list_tool(),
331 get_preset_run_tool(),
332 get_make_decision_tool(),
334 get_analyze_perspectives_tool(),
335 get_assess_evidence_tool(),
336 get_probabilistic_tool(),
337 get_metrics_summary_tool(),
339 get_metrics_by_pipe_tool(),
340 get_metrics_invocations_tool(),
341 get_fallback_metrics_tool(),
342 get_debug_config_tool(),
344 ];
345
346 JsonRpcResponse::success(
347 id,
348 serde_json::json!({
349 "tools": tools
350 }),
351 )
352 }
353
354 async fn handle_tool_call(&self, id: Option<Value>, params: Option<Value>) -> JsonRpcResponse {
356 let params: ToolCallParams = match params {
357 Some(p) => match serde_json::from_value(p) {
358 Ok(p) => p,
359 Err(e) => {
360 return JsonRpcResponse::error(id, -32602, format!("Invalid params: {}", e));
361 }
362 },
363 None => {
364 return JsonRpcResponse::error(id, -32602, "Missing params");
365 }
366 };
367
368 info!(tool = %params.name, "Handling tool call");
369
370 let (content, is_error) =
371 match handle_tool_call(&self.state, ¶ms.name, params.arguments).await {
372 Ok(result) => {
373 let text = serde_json::to_string_pretty(&result).unwrap_or_else(|e| {
374 error!(error = %e, "Failed to serialize tool result");
375 format!("{{\"error\": \"Serialization failed: {}\"}}", e)
376 });
377 (
378 ToolResultContent {
379 content_type: "text".to_string(),
380 text,
381 },
382 None,
383 )
384 }
385 Err(e) => (
386 ToolResultContent {
387 content_type: "text".to_string(),
388 text: format!("Error: {}", e),
389 },
390 Some(true),
391 ),
392 };
393
394 let tool_result = ToolCallResult {
395 content: vec![content],
396 is_error,
397 };
398
399 match serde_json::to_value(tool_result) {
400 Ok(val) => JsonRpcResponse::success(id, val),
401 Err(e) => {
402 error!(error = %e, "Failed to serialize tool call result");
403 JsonRpcResponse::error(id.clone(), -32603, format!("Internal error: {}", e))
404 }
405 }
406 }
407}
408
409fn get_linear_tool() -> Tool {
411 Tool {
412 name: "reasoning_linear".to_string(),
413 description: "Single-pass sequential reasoning. Process a thought and get a logical continuation or analysis.".to_string(),
414 input_schema: serde_json::json!({
415 "type": "object",
416 "properties": {
417 "content": {
418 "type": "string",
419 "description": "The thought content to process"
420 },
421 "session_id": {
422 "type": "string",
423 "description": "Optional session ID for context continuity"
424 },
425 "confidence": {
426 "type": "number",
427 "minimum": 0,
428 "maximum": 1,
429 "description": "Confidence threshold (0.0-1.0)"
430 }
431 },
432 "required": ["content"],
433 "additionalProperties": false
434 }),
435 }
436}
437
438fn get_tree_tool() -> Tool {
440 Tool {
441 name: "reasoning_tree".to_string(),
442 description: "Branching exploration with multiple reasoning paths. Explores 2-4 distinct approaches and recommends the most promising one.".to_string(),
443 input_schema: serde_json::json!({
444 "type": "object",
445 "properties": {
446 "content": {
447 "type": "string",
448 "description": "The thought content to explore"
449 },
450 "session_id": {
451 "type": "string",
452 "description": "Optional session ID for context continuity"
453 },
454 "branch_id": {
455 "type": "string",
456 "description": "Optional branch ID to extend (creates root branch if not provided)"
457 },
458 "num_branches": {
459 "type": "integer",
460 "minimum": 2,
461 "maximum": 4,
462 "description": "Number of branches to explore (default: 3)"
463 },
464 "confidence": {
465 "type": "number",
466 "minimum": 0,
467 "maximum": 1,
468 "description": "Confidence threshold (0.0-1.0)"
469 },
470 "cross_refs": {
471 "type": "array",
472 "items": {
473 "type": "object",
474 "properties": {
475 "to_branch": { "type": "string" },
476 "type": { "type": "string", "enum": ["supports", "contradicts", "extends", "alternative", "depends"] },
477 "reason": { "type": "string" },
478 "strength": { "type": "number", "minimum": 0, "maximum": 1 }
479 },
480 "required": ["to_branch", "type"]
481 },
482 "description": "Optional cross-references to other branches"
483 }
484 },
485 "required": ["content"],
486 "additionalProperties": false
487 }),
488 }
489}
490
491fn get_tree_focus_tool() -> Tool {
493 Tool {
494 name: "reasoning_tree_focus".to_string(),
495 description: "Focus on a specific branch, making it the active branch for the session."
496 .to_string(),
497 input_schema: serde_json::json!({
498 "type": "object",
499 "properties": {
500 "session_id": {
501 "type": "string",
502 "description": "The session ID"
503 },
504 "branch_id": {
505 "type": "string",
506 "description": "The branch ID to focus on"
507 }
508 },
509 "required": ["session_id", "branch_id"],
510 "additionalProperties": false
511 }),
512 }
513}
514
515fn get_tree_list_tool() -> Tool {
517 Tool {
518 name: "reasoning_tree_list".to_string(),
519 description: "List all branches in a session.".to_string(),
520 input_schema: serde_json::json!({
521 "type": "object",
522 "properties": {
523 "session_id": {
524 "type": "string",
525 "description": "The session ID"
526 }
527 },
528 "required": ["session_id"],
529 "additionalProperties": false
530 }),
531 }
532}
533
534fn get_tree_complete_tool() -> Tool {
536 Tool {
537 name: "reasoning_tree_complete".to_string(),
538 description: "Mark a branch as completed or abandoned.".to_string(),
539 input_schema: serde_json::json!({
540 "type": "object",
541 "properties": {
542 "branch_id": {
543 "type": "string",
544 "description": "The branch ID to update"
545 },
546 "completed": {
547 "type": "boolean",
548 "description": "True to mark as completed, false to mark as abandoned (default: true)"
549 }
550 },
551 "required": ["branch_id"],
552 "additionalProperties": false
553 }),
554 }
555}
556
557fn get_divergent_tool() -> Tool {
559 Tool {
560 name: "reasoning_divergent".to_string(),
561 description: "Creative reasoning that generates novel perspectives and unconventional solutions. Challenges assumptions and synthesizes diverse viewpoints.".to_string(),
562 input_schema: serde_json::json!({
563 "type": "object",
564 "properties": {
565 "content": {
566 "type": "string",
567 "description": "The thought content to explore creatively"
568 },
569 "session_id": {
570 "type": "string",
571 "description": "Optional session ID for context continuity"
572 },
573 "branch_id": {
574 "type": "string",
575 "description": "Optional branch ID for tree mode integration"
576 },
577 "num_perspectives": {
578 "type": "integer",
579 "minimum": 2,
580 "maximum": 5,
581 "description": "Number of perspectives to generate (default: 3)"
582 },
583 "challenge_assumptions": {
584 "type": "boolean",
585 "description": "Whether to explicitly identify and challenge assumptions"
586 },
587 "force_rebellion": {
588 "type": "boolean",
589 "description": "Enable maximum creativity mode with contrarian viewpoints"
590 },
591 "confidence": {
592 "type": "number",
593 "minimum": 0,
594 "maximum": 1,
595 "description": "Confidence threshold (0.0-1.0, default: 0.7)"
596 }
597 },
598 "required": ["content"],
599 "additionalProperties": false
600 }),
601 }
602}
603
604fn get_reflection_tool() -> Tool {
606 Tool {
607 name: "reasoning_reflection".to_string(),
608 description: "Meta-cognitive reasoning that analyzes and improves reasoning quality. Evaluates strengths, weaknesses, and provides recommendations.".to_string(),
609 input_schema: serde_json::json!({
610 "type": "object",
611 "properties": {
612 "thought_id": {
613 "type": "string",
614 "description": "ID of an existing thought to reflect upon"
615 },
616 "content": {
617 "type": "string",
618 "description": "Content to reflect upon (used if thought_id not provided)"
619 },
620 "session_id": {
621 "type": "string",
622 "description": "Optional session ID for context continuity"
623 },
624 "branch_id": {
625 "type": "string",
626 "description": "Optional branch ID for tree mode integration"
627 },
628 "max_iterations": {
629 "type": "integer",
630 "minimum": 1,
631 "maximum": 5,
632 "description": "Maximum iterations for iterative refinement (default: 3)"
633 },
634 "quality_threshold": {
635 "type": "number",
636 "minimum": 0,
637 "maximum": 1,
638 "description": "Quality threshold to stop iterating (default: 0.8)"
639 },
640 "include_chain": {
641 "type": "boolean",
642 "description": "Whether to include full reasoning chain in context"
643 }
644 },
645 "additionalProperties": false
646 }),
647 }
648}
649
650fn get_reflection_evaluate_tool() -> Tool {
652 Tool {
653 name: "reasoning_reflection_evaluate".to_string(),
654 description: "Evaluate a session's overall reasoning quality, coherence, and provide recommendations.".to_string(),
655 input_schema: serde_json::json!({
656 "type": "object",
657 "properties": {
658 "session_id": {
659 "type": "string",
660 "description": "The session ID to evaluate"
661 }
662 },
663 "required": ["session_id"],
664 "additionalProperties": false
665 }),
666 }
667}
668
669fn get_backtracking_tool() -> Tool {
675 Tool {
676 name: "reasoning_backtrack".to_string(),
677 description: "Restore from a checkpoint and explore alternative reasoning paths. Enables non-linear exploration with state restoration.".to_string(),
678 input_schema: serde_json::json!({
679 "type": "object",
680 "properties": {
681 "checkpoint_id": {
682 "type": "string",
683 "description": "ID of the checkpoint to restore from"
684 },
685 "new_direction": {
686 "type": "string",
687 "description": "Optional new direction or approach to try from the checkpoint"
688 },
689 "session_id": {
690 "type": "string",
691 "description": "Optional session ID (must match checkpoint's session)"
692 },
693 "confidence": {
694 "type": "number",
695 "minimum": 0,
696 "maximum": 1,
697 "description": "Confidence threshold (0.0-1.0, default: 0.8)"
698 }
699 },
700 "required": ["checkpoint_id"],
701 "additionalProperties": false
702 }),
703 }
704}
705
706fn get_backtracking_checkpoint_tool() -> Tool {
708 Tool {
709 name: "reasoning_checkpoint_create".to_string(),
710 description: "Create a checkpoint at the current reasoning state for later backtracking."
711 .to_string(),
712 input_schema: serde_json::json!({
713 "type": "object",
714 "properties": {
715 "session_id": {
716 "type": "string",
717 "description": "The session ID to checkpoint"
718 },
719 "name": {
720 "type": "string",
721 "description": "Name for the checkpoint"
722 },
723 "description": {
724 "type": "string",
725 "description": "Optional description of the checkpoint state"
726 }
727 },
728 "required": ["session_id", "name"],
729 "additionalProperties": false
730 }),
731 }
732}
733
734fn get_backtracking_list_tool() -> Tool {
736 Tool {
737 name: "reasoning_checkpoint_list".to_string(),
738 description: "List all checkpoints available for a session.".to_string(),
739 input_schema: serde_json::json!({
740 "type": "object",
741 "properties": {
742 "session_id": {
743 "type": "string",
744 "description": "The session ID to list checkpoints for"
745 }
746 },
747 "required": ["session_id"],
748 "additionalProperties": false
749 }),
750 }
751}
752
753fn get_auto_tool() -> Tool {
755 Tool {
756 name: "reasoning_auto".to_string(),
757 description: "Automatically select the most appropriate reasoning mode based on content analysis. Routes to linear, tree, divergent, reflection, or got mode.".to_string(),
758 input_schema: serde_json::json!({
759 "type": "object",
760 "properties": {
761 "content": {
762 "type": "string",
763 "description": "The content to analyze for mode selection"
764 },
765 "hints": {
766 "type": "array",
767 "items": { "type": "string" },
768 "description": "Optional hints about the problem type"
769 },
770 "session_id": {
771 "type": "string",
772 "description": "Optional session ID for context"
773 }
774 },
775 "required": ["content"],
776 "additionalProperties": false
777 }),
778 }
779}
780
781fn get_got_init_tool() -> Tool {
783 Tool {
784 name: "reasoning_got_init".to_string(),
785 description: "Initialize a new Graph-of-Thoughts reasoning graph with a root node."
786 .to_string(),
787 input_schema: serde_json::json!({
788 "type": "object",
789 "properties": {
790 "content": {
791 "type": "string",
792 "description": "Initial thought content for the root node"
793 },
794 "problem": {
795 "type": "string",
796 "description": "Optional problem context"
797 },
798 "session_id": {
799 "type": "string",
800 "description": "Optional session ID"
801 },
802 "config": {
803 "type": "object",
804 "properties": {
805 "max_nodes": { "type": "integer", "minimum": 10, "maximum": 1000 },
806 "max_depth": { "type": "integer", "minimum": 1, "maximum": 20 },
807 "default_k": { "type": "integer", "minimum": 1, "maximum": 10 },
808 "prune_threshold": { "type": "number", "minimum": 0, "maximum": 1 }
809 },
810 "description": "Optional configuration overrides"
811 }
812 },
813 "required": ["content"],
814 "additionalProperties": false
815 }),
816 }
817}
818
819fn get_got_generate_tool() -> Tool {
821 Tool {
822 name: "reasoning_got_generate".to_string(),
823 description: "Generate k diverse continuations from a node in the reasoning graph."
824 .to_string(),
825 input_schema: serde_json::json!({
826 "type": "object",
827 "properties": {
828 "session_id": {
829 "type": "string",
830 "description": "The session ID"
831 },
832 "node_id": {
833 "type": "string",
834 "description": "Optional node ID to generate from (uses active nodes if not specified)"
835 },
836 "k": {
837 "type": "integer",
838 "minimum": 1,
839 "maximum": 10,
840 "description": "Number of continuations to generate (default: 3)"
841 },
842 "problem": {
843 "type": "string",
844 "description": "Optional problem context"
845 }
846 },
847 "required": ["session_id"],
848 "additionalProperties": false
849 }),
850 }
851}
852
853fn get_got_score_tool() -> Tool {
855 Tool {
856 name: "reasoning_got_score".to_string(),
857 description: "Score a node's quality based on relevance, validity, depth, and novelty."
858 .to_string(),
859 input_schema: serde_json::json!({
860 "type": "object",
861 "properties": {
862 "session_id": {
863 "type": "string",
864 "description": "The session ID"
865 },
866 "node_id": {
867 "type": "string",
868 "description": "The node ID to score"
869 },
870 "problem": {
871 "type": "string",
872 "description": "Optional problem context"
873 }
874 },
875 "required": ["session_id", "node_id"],
876 "additionalProperties": false
877 }),
878 }
879}
880
881fn get_got_aggregate_tool() -> Tool {
883 Tool {
884 name: "reasoning_got_aggregate".to_string(),
885 description: "Merge multiple reasoning nodes into a unified insight.".to_string(),
886 input_schema: serde_json::json!({
887 "type": "object",
888 "properties": {
889 "session_id": {
890 "type": "string",
891 "description": "The session ID"
892 },
893 "node_ids": {
894 "type": "array",
895 "items": { "type": "string" },
896 "minItems": 2,
897 "description": "Node IDs to aggregate (minimum 2)"
898 },
899 "problem": {
900 "type": "string",
901 "description": "Optional problem context"
902 }
903 },
904 "required": ["session_id", "node_ids"],
905 "additionalProperties": false
906 }),
907 }
908}
909
910fn get_got_refine_tool() -> Tool {
912 Tool {
913 name: "reasoning_got_refine".to_string(),
914 description: "Improve a reasoning node through self-critique and refinement.".to_string(),
915 input_schema: serde_json::json!({
916 "type": "object",
917 "properties": {
918 "session_id": {
919 "type": "string",
920 "description": "The session ID"
921 },
922 "node_id": {
923 "type": "string",
924 "description": "The node ID to refine"
925 },
926 "problem": {
927 "type": "string",
928 "description": "Optional problem context"
929 }
930 },
931 "required": ["session_id", "node_id"],
932 "additionalProperties": false
933 }),
934 }
935}
936
937fn get_got_prune_tool() -> Tool {
939 Tool {
940 name: "reasoning_got_prune".to_string(),
941 description: "Remove low-scoring nodes from the reasoning graph.".to_string(),
942 input_schema: serde_json::json!({
943 "type": "object",
944 "properties": {
945 "session_id": {
946 "type": "string",
947 "description": "The session ID"
948 },
949 "threshold": {
950 "type": "number",
951 "minimum": 0,
952 "maximum": 1,
953 "description": "Score threshold - nodes below this are pruned (default: 0.3)"
954 }
955 },
956 "required": ["session_id"],
957 "additionalProperties": false
958 }),
959 }
960}
961
962fn get_got_finalize_tool() -> Tool {
964 Tool {
965 name: "reasoning_got_finalize".to_string(),
966 description: "Mark terminal nodes and retrieve final conclusions from the reasoning graph."
967 .to_string(),
968 input_schema: serde_json::json!({
969 "type": "object",
970 "properties": {
971 "session_id": {
972 "type": "string",
973 "description": "The session ID"
974 },
975 "terminal_node_ids": {
976 "type": "array",
977 "items": { "type": "string" },
978 "description": "Optional node IDs to mark as terminal (auto-selects best nodes if empty)"
979 }
980 },
981 "required": ["session_id"],
982 "additionalProperties": false
983 }),
984 }
985}
986
987fn get_got_state_tool() -> Tool {
989 Tool {
990 name: "reasoning_got_state".to_string(),
991 description:
992 "Get the current state of the reasoning graph including node counts and structure."
993 .to_string(),
994 input_schema: serde_json::json!({
995 "type": "object",
996 "properties": {
997 "session_id": {
998 "type": "string",
999 "description": "The session ID"
1000 }
1001 },
1002 "required": ["session_id"],
1003 "additionalProperties": false
1004 }),
1005 }
1006}
1007
1008fn get_detect_biases_tool() -> Tool {
1014 Tool {
1015 name: "reasoning_detect_biases".to_string(),
1016 description: "Analyze content for cognitive biases such as confirmation bias, anchoring, availability heuristic, sunk cost fallacy, and others. Returns detected biases with severity, confidence, explanation, and remediation suggestions.".to_string(),
1017 input_schema: serde_json::json!({
1018 "type": "object",
1019 "properties": {
1020 "content": {
1021 "type": "string",
1022 "description": "The content to analyze for cognitive biases"
1023 },
1024 "thought_id": {
1025 "type": "string",
1026 "description": "ID of an existing thought to analyze (alternative to content)"
1027 },
1028 "session_id": {
1029 "type": "string",
1030 "description": "Session ID for context and persistence"
1031 },
1032 "check_types": {
1033 "type": "array",
1034 "items": { "type": "string" },
1035 "description": "Specific bias types to check (optional, checks all if not specified)"
1036 }
1037 },
1038 "additionalProperties": false
1039 }),
1040 }
1041}
1042
1043fn get_detect_fallacies_tool() -> Tool {
1045 Tool {
1046 name: "reasoning_detect_fallacies".to_string(),
1047 description: "Analyze content for logical fallacies including ad hominem, straw man, false dichotomy, appeal to authority, circular reasoning, and others. Returns detected fallacies with severity, confidence, explanation, and remediation suggestions.".to_string(),
1048 input_schema: serde_json::json!({
1049 "type": "object",
1050 "properties": {
1051 "content": {
1052 "type": "string",
1053 "description": "The content to analyze for logical fallacies"
1054 },
1055 "thought_id": {
1056 "type": "string",
1057 "description": "ID of an existing thought to analyze (alternative to content)"
1058 },
1059 "session_id": {
1060 "type": "string",
1061 "description": "Session ID for context and persistence"
1062 },
1063 "check_formal": {
1064 "type": "boolean",
1065 "description": "Check for formal logical fallacies (default: true)"
1066 },
1067 "check_informal": {
1068 "type": "boolean",
1069 "description": "Check for informal logical fallacies (default: true)"
1070 }
1071 },
1072 "additionalProperties": false
1073 }),
1074 }
1075}
1076
1077fn get_preset_list_tool() -> Tool {
1083 Tool {
1084 name: "reasoning_preset_list".to_string(),
1085 description: "List available workflow presets. Presets are pre-defined multi-step reasoning workflows that compose existing modes into higher-level operations like code review, debug analysis, and architecture decisions.".to_string(),
1086 input_schema: serde_json::json!({
1087 "type": "object",
1088 "properties": {
1089 "category": {
1090 "type": "string",
1091 "description": "Filter by category (e.g., 'code', 'architecture', 'research')"
1092 }
1093 },
1094 "additionalProperties": false
1095 }),
1096 }
1097}
1098
1099fn get_preset_run_tool() -> Tool {
1101 Tool {
1102 name: "reasoning_preset_run".to_string(),
1103 description: "Execute a workflow preset. Runs a multi-step reasoning workflow with automatic step sequencing, dependency management, and result aggregation.".to_string(),
1104 input_schema: serde_json::json!({
1105 "type": "object",
1106 "properties": {
1107 "preset_id": {
1108 "type": "string",
1109 "description": "ID of the preset to run (e.g., 'code-review', 'debug-analysis', 'architecture-decision')"
1110 },
1111 "inputs": {
1112 "type": "object",
1113 "description": "Input parameters for the preset workflow",
1114 "additionalProperties": true
1115 },
1116 "session_id": {
1117 "type": "string",
1118 "description": "Optional session ID for context persistence"
1119 }
1120 },
1121 "required": ["preset_id"],
1122 "additionalProperties": false
1123 }),
1124 }
1125}
1126
1127fn get_make_decision_tool() -> Tool {
1133 Tool {
1134 name: "reasoning_make_decision".to_string(),
1135 description: "Multi-criteria decision analysis using weighted scoring, pairwise comparison, or TOPSIS methods. Evaluates alternatives against criteria with optional weights and provides ranked recommendations.".to_string(),
1136 input_schema: serde_json::json!({
1137 "type": "object",
1138 "properties": {
1139 "question": {
1140 "type": "string",
1141 "description": "The decision question to analyze"
1142 },
1143 "options": {
1144 "type": "array",
1145 "items": { "type": "string" },
1146 "minItems": 2,
1147 "description": "Options to evaluate (minimum 2)"
1148 },
1149 "criteria": {
1150 "type": "array",
1151 "items": {
1152 "type": "object",
1153 "properties": {
1154 "name": { "type": "string", "description": "Criterion name" },
1155 "weight": { "type": "number", "minimum": 0, "maximum": 1, "description": "Importance weight (0-1)" },
1156 "description": { "type": "string", "description": "Optional criterion description" }
1157 },
1158 "required": ["name"]
1159 },
1160 "description": "Evaluation criteria with optional weights"
1161 },
1162 "method": {
1163 "type": "string",
1164 "enum": ["weighted_sum", "pairwise", "topsis"],
1165 "description": "Analysis method (default: weighted_sum)"
1166 },
1167 "session_id": {
1168 "type": "string",
1169 "description": "Optional session ID for context persistence"
1170 },
1171 "context": {
1172 "type": "string",
1173 "description": "Additional context for the decision"
1174 }
1175 },
1176 "required": ["question", "options"],
1177 "additionalProperties": false
1178 }),
1179 }
1180}
1181
1182fn get_analyze_perspectives_tool() -> Tool {
1184 Tool {
1185 name: "reasoning_analyze_perspectives".to_string(),
1186 description: "Stakeholder power/interest matrix analysis. Maps stakeholders to quadrants (KeyPlayer, KeepSatisfied, KeepInformed, MinimalEffort) and identifies conflicts, alignments, and strategic engagement recommendations.".to_string(),
1187 input_schema: serde_json::json!({
1188 "type": "object",
1189 "properties": {
1190 "topic": {
1191 "type": "string",
1192 "description": "The topic or decision to analyze from multiple perspectives"
1193 },
1194 "stakeholders": {
1195 "type": "array",
1196 "items": {
1197 "type": "object",
1198 "properties": {
1199 "name": { "type": "string", "description": "Stakeholder name" },
1200 "role": { "type": "string", "description": "Stakeholder role" },
1201 "interests": {
1202 "type": "array",
1203 "items": { "type": "string" },
1204 "description": "Key interests"
1205 },
1206 "power_level": {
1207 "type": "number",
1208 "minimum": 0,
1209 "maximum": 1,
1210 "description": "Power/influence level (0-1)"
1211 },
1212 "interest_level": {
1213 "type": "number",
1214 "minimum": 0,
1215 "maximum": 1,
1216 "description": "Interest/stake level (0-1)"
1217 }
1218 },
1219 "required": ["name"]
1220 },
1221 "description": "Stakeholders to consider (optional - will infer if not provided)"
1222 },
1223 "session_id": {
1224 "type": "string",
1225 "description": "Optional session ID for context persistence"
1226 },
1227 "context": {
1228 "type": "string",
1229 "description": "Additional context for the analysis"
1230 }
1231 },
1232 "required": ["topic"],
1233 "additionalProperties": false
1234 }),
1235 }
1236}
1237
1238fn get_assess_evidence_tool() -> Tool {
1240 Tool {
1241 name: "reasoning_assess_evidence".to_string(),
1242 description: "Evidence quality assessment with source credibility analysis, corroboration tracking, and chain of custody evaluation. Returns credibility scores, confidence assessments, and evidence synthesis.".to_string(),
1243 input_schema: serde_json::json!({
1244 "type": "object",
1245 "properties": {
1246 "claim": {
1247 "type": "string",
1248 "description": "The claim to assess evidence for"
1249 },
1250 "evidence": {
1251 "type": "array",
1252 "items": {
1253 "type": "object",
1254 "properties": {
1255 "content": { "type": "string", "description": "Evidence content or description" },
1256 "source": { "type": "string", "description": "Source of the evidence" },
1257 "source_type": {
1258 "type": "string",
1259 "enum": ["primary", "secondary", "tertiary", "expert", "anecdotal"],
1260 "description": "Type of source"
1261 },
1262 "date": { "type": "string", "description": "Date of evidence (ISO format)" }
1263 },
1264 "required": ["content"]
1265 },
1266 "minItems": 1,
1267 "description": "Evidence items to assess"
1268 },
1269 "session_id": {
1270 "type": "string",
1271 "description": "Optional session ID for context persistence"
1272 },
1273 "context": {
1274 "type": "string",
1275 "description": "Additional context for the assessment"
1276 }
1277 },
1278 "required": ["claim", "evidence"],
1279 "additionalProperties": false
1280 }),
1281 }
1282}
1283
1284fn get_probabilistic_tool() -> Tool {
1286 Tool {
1287 name: "reasoning_probabilistic".to_string(),
1288 description: "Bayesian probability updates for belief revision. Takes prior probabilities and new evidence to compute posterior probabilities with entropy and uncertainty metrics.".to_string(),
1289 input_schema: serde_json::json!({
1290 "type": "object",
1291 "properties": {
1292 "hypothesis": {
1293 "type": "string",
1294 "description": "The hypothesis to evaluate"
1295 },
1296 "prior": {
1297 "type": "number",
1298 "minimum": 0,
1299 "maximum": 1,
1300 "description": "Prior probability (0-1)"
1301 },
1302 "evidence": {
1303 "type": "array",
1304 "items": {
1305 "type": "object",
1306 "properties": {
1307 "description": { "type": "string", "description": "Evidence description" },
1308 "likelihood_if_true": {
1309 "type": "number",
1310 "minimum": 0,
1311 "maximum": 1,
1312 "description": "P(evidence|hypothesis true)"
1313 },
1314 "likelihood_if_false": {
1315 "type": "number",
1316 "minimum": 0,
1317 "maximum": 1,
1318 "description": "P(evidence|hypothesis false)"
1319 }
1320 },
1321 "required": ["description"]
1322 },
1323 "minItems": 1,
1324 "description": "Evidence items with likelihood ratios"
1325 },
1326 "session_id": {
1327 "type": "string",
1328 "description": "Optional session ID for context persistence"
1329 }
1330 },
1331 "required": ["hypothesis", "prior", "evidence"],
1332 "additionalProperties": false
1333 }),
1334 }
1335}
1336
1337fn get_metrics_summary_tool() -> Tool {
1342 Tool {
1343 name: "reasoning_metrics_summary".to_string(),
1344 description: "Get aggregated usage statistics for all Langbase pipes. Returns call counts, success rates, and latency statistics for each pipe that has been invoked.".to_string(),
1345 input_schema: serde_json::json!({
1346 "type": "object",
1347 "properties": {},
1348 "additionalProperties": false
1349 }),
1350 }
1351}
1352
1353fn get_metrics_by_pipe_tool() -> Tool {
1354 Tool {
1355 name: "reasoning_metrics_by_pipe".to_string(),
1356 description: "Get detailed usage statistics for a specific Langbase pipe. Returns call counts, success/failure counts, success rate, and latency statistics (avg, min, max).".to_string(),
1357 input_schema: serde_json::json!({
1358 "type": "object",
1359 "properties": {
1360 "pipe_name": {
1361 "type": "string",
1362 "description": "Name of the pipe to get metrics for"
1363 }
1364 },
1365 "required": ["pipe_name"],
1366 "additionalProperties": false
1367 }),
1368 }
1369}
1370
1371fn get_metrics_invocations_tool() -> Tool {
1372 Tool {
1373 name: "reasoning_metrics_invocations".to_string(),
1374 description: "Query invocation history with optional filtering. Returns detailed logs of pipe calls including inputs, outputs, latency, and success status.".to_string(),
1375 input_schema: serde_json::json!({
1376 "type": "object",
1377 "properties": {
1378 "pipe_name": {
1379 "type": "string",
1380 "description": "Filter by pipe name"
1381 },
1382 "tool_name": {
1383 "type": "string",
1384 "description": "Filter by MCP tool name"
1385 },
1386 "session_id": {
1387 "type": "string",
1388 "description": "Filter by session ID"
1389 },
1390 "success_only": {
1391 "type": "boolean",
1392 "description": "If true, only successful calls; if false, only failed calls"
1393 },
1394 "limit": {
1395 "type": "integer",
1396 "minimum": 1,
1397 "maximum": 1000,
1398 "default": 100,
1399 "description": "Maximum number of results to return"
1400 }
1401 },
1402 "additionalProperties": false
1403 }),
1404 }
1405}
1406
1407fn get_debug_config_tool() -> Tool {
1408 Tool {
1409 name: "reasoning_debug_config".to_string(),
1410 description: "Debug tool to inspect the current pipe configuration. Returns the actual pipe names being used by the server.".to_string(),
1411 input_schema: serde_json::json!({
1412 "type": "object",
1413 "properties": {},
1414 "additionalProperties": false
1415 }),
1416 }
1417}
1418
1419fn get_fallback_metrics_tool() -> Tool {
1420 Tool {
1421 name: "reasoning_fallback_metrics".to_string(),
1422 description: "Get metrics about fallback usage across invocations. Returns total fallbacks, breakdown by type (parse_error, api_unavailable, local_calculation) and by pipe, plus recommendations for enabling strict mode.".to_string(),
1423 input_schema: serde_json::json!({
1424 "type": "object",
1425 "properties": {},
1426 "additionalProperties": false
1427 }),
1428 }
1429}