mcp_langbase_reasoning/server/
mcp.rs

1//! MCP protocol implementation for JSON-RPC 2.0 communication.
2//!
3//! This module provides the core MCP server implementation including:
4//! - JSON-RPC 2.0 request/response handling
5//! - Tool definitions and schemas
6//! - Stdio-based server communication
7
8use 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/// JSON-RPC 2.0 request structure.
20#[derive(Debug, Deserialize)]
21pub struct JsonRpcRequest {
22    /// JSON-RPC version (must be "2.0").
23    pub jsonrpc: String,
24    /// Request identifier (None for notifications).
25    pub id: Option<Value>,
26    /// The method name to invoke.
27    pub method: String,
28    /// Optional parameters for the method.
29    #[serde(default)]
30    pub params: Option<Value>,
31}
32
33/// JSON-RPC 2.0 response structure.
34#[derive(Debug, Serialize)]
35pub struct JsonRpcResponse {
36    /// JSON-RPC version (always "2.0").
37    pub jsonrpc: String,
38    /// Request identifier (null if notification, must always be present per spec).
39    pub id: Value,
40    /// The result on success (mutually exclusive with error).
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub result: Option<Value>,
43    /// The error on failure (mutually exclusive with result).
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub error: Option<JsonRpcError>,
46}
47
48/// JSON-RPC 2.0 error object.
49#[derive(Debug, Serialize)]
50pub struct JsonRpcError {
51    /// Error code (negative for predefined errors).
52    pub code: i32,
53    /// Human-readable error message.
54    pub message: String,
55    /// Optional additional error data.
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub data: Option<Value>,
58}
59
60/// MCP server information returned during initialization.
61#[derive(Debug, Serialize)]
62pub struct ServerInfo {
63    /// The server name identifier.
64    pub name: String,
65    /// The server version string.
66    pub version: String,
67}
68
69/// MCP server capabilities advertised to clients.
70#[derive(Debug, Serialize)]
71pub struct Capabilities {
72    /// Tool-related capabilities.
73    pub tools: ToolCapabilities,
74}
75
76/// Tool-specific capabilities.
77#[derive(Debug, Serialize)]
78pub struct ToolCapabilities {
79    /// Whether the tool list can change dynamically.
80    #[serde(rename = "listChanged")]
81    pub list_changed: bool,
82}
83
84/// Result of the MCP initialize handshake.
85#[derive(Debug, Serialize)]
86pub struct InitializeResult {
87    /// The MCP protocol version supported.
88    #[serde(rename = "protocolVersion")]
89    pub protocol_version: String,
90    /// Server capabilities.
91    pub capabilities: Capabilities,
92    /// Server identification information.
93    #[serde(rename = "serverInfo")]
94    pub server_info: ServerInfo,
95}
96
97/// MCP tool definition with JSON Schema.
98#[derive(Debug, Clone, Serialize)]
99pub struct Tool {
100    /// Unique tool name (used in tool calls).
101    pub name: String,
102    /// Human-readable description of the tool.
103    pub description: String,
104    /// JSON Schema for the tool's input parameters.
105    #[serde(rename = "inputSchema")]
106    pub input_schema: Value,
107}
108
109/// Parameters for a tools/call request.
110#[derive(Debug, Deserialize)]
111pub struct ToolCallParams {
112    /// The name of the tool to invoke.
113    pub name: String,
114    /// Optional arguments for the tool.
115    #[serde(default)]
116    pub arguments: Option<Value>,
117}
118
119/// Content item within a tool result.
120#[derive(Debug, Serialize)]
121pub struct ToolResultContent {
122    /// The content type (e.g., "text").
123    #[serde(rename = "type")]
124    pub content_type: String,
125    /// The text content of the result.
126    pub text: String,
127}
128
129/// Result of a tool invocation.
130#[derive(Debug, Serialize)]
131pub struct ToolCallResult {
132    /// The result content items.
133    pub content: Vec<ToolResultContent>,
134    /// Whether the result represents an error.
135    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
136    pub is_error: Option<bool>,
137}
138
139impl JsonRpcResponse {
140    /// Create a success response
141    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    /// Create an error response
151    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
165/// MCP Server running over stdio.
166///
167/// Handles JSON-RPC 2.0 messages over stdin/stdout for MCP protocol
168/// communication with clients.
169pub struct McpServer {
170    /// Shared application state.
171    state: SharedState,
172}
173
174impl McpServer {
175    /// Create a new MCP server
176    pub fn new(state: SharedState) -> Self {
177        Self { state }
178    }
179
180    /// Run the server using async stdio
181    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            // EOF reached
194            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            // Only send response if not a notification (per JSON-RPC 2.0 spec)
219            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    /// Handle a single JSON-RPC request
233    /// Returns None for notifications (requests without id) per JSON-RPC 2.0 spec
234    async fn handle_request(&self, request: JsonRpcRequest) -> Option<JsonRpcResponse> {
235        // Check if this is a notification (no id = no response required)
236        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                // Notification - no response per JSON-RPC 2.0
242                debug!("Received initialized notification");
243                None
244            }
245            "notifications/cancelled" => {
246                // Notification - no response
247                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                // For unknown methods, only respond if it's a request (has id)
258                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    /// Handle initialize request
274    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    /// Handle tools/list request
300    fn handle_tools_list(&self, id: Option<Value>) -> JsonRpcResponse {
301        info!("Handling tools/list request");
302
303        let tools = vec![
304            // Phase 1-2 tools
305            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            // Phase 3 tools
314            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            // Phase 4 tools - Bias & Fallacy Detection
327            get_detect_biases_tool(),
328            get_detect_fallacies_tool(),
329            // Phase 5 tools - Workflow Presets
330            get_preset_list_tool(),
331            get_preset_run_tool(),
332            // Phase 6 tools - Decision Framework & Evidence Assessment
333            get_make_decision_tool(),
334            get_analyze_perspectives_tool(),
335            get_assess_evidence_tool(),
336            get_probabilistic_tool(),
337            // Metrics tools
338            get_metrics_summary_tool(),
339            get_metrics_by_pipe_tool(),
340            get_metrics_invocations_tool(),
341            get_fallback_metrics_tool(),
342            // Debug tools
343            get_debug_config_tool(),
344        ];
345
346        JsonRpcResponse::success(
347            id,
348            serde_json::json!({
349                "tools": tools
350            }),
351        )
352    }
353
354    /// Handle tools/call request
355    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, &params.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
409/// Get the linear reasoning tool definition
410fn 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
438/// Get the tree reasoning tool definition
439fn 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
491/// Get the tree focus tool definition
492fn 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
515/// Get the tree list tool definition
516fn 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
534/// Get the tree complete tool definition
535fn 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
557/// Get the divergent reasoning tool definition
558fn 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
604/// Get the reflection reasoning tool definition
605fn 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
650/// Get the reflection evaluate tool definition
651fn 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
669// ============================================================================
670// Phase 3 Tool Definitions
671// ============================================================================
672
673/// Get the backtracking tool definition
674fn 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
706/// Get the backtracking checkpoint creation tool definition
707fn 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
734/// Get the backtracking list checkpoints tool definition
735fn 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
753/// Get the auto mode router tool definition
754fn 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
781/// Get the GoT initialization tool definition
782fn 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
819/// Get the GoT generate tool definition
820fn 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
853/// Get the GoT score tool definition
854fn 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
881/// Get the GoT aggregate tool definition
882fn 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
910/// Get the GoT refine tool definition
911fn 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
937/// Get the GoT prune tool definition
938fn 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
962/// Get the GoT finalize tool definition
963fn 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
987/// Get the GoT state tool definition
988fn 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
1008// ============================================================================
1009// Phase 4 Tool Definitions - Bias & Fallacy Detection
1010// ============================================================================
1011
1012/// Get the detect biases tool definition
1013fn 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
1043/// Get the detect fallacies tool definition
1044fn 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
1077// ============================================================================
1078// Phase 5 Tool Definitions - Workflow Presets
1079// ============================================================================
1080
1081/// Get the preset list tool definition
1082fn 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
1099/// Get the preset run tool definition
1100fn 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
1127// ============================================================================
1128// Phase 6 Tool Definitions - Decision Framework & Evidence Assessment
1129// ============================================================================
1130
1131/// Get the make decision tool definition
1132fn 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
1182/// Get the analyze perspectives tool definition
1183fn 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
1238/// Get the assess evidence tool definition
1239fn 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
1284/// Get the probabilistic reasoning tool definition
1285fn 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
1337// ============================================================================
1338// Metrics Tools
1339// ============================================================================
1340
1341fn 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}