error_handling/
error_handling.rs1use async_trait::async_trait;
7use std::collections::HashMap;
8use tenx_mcp::{
9 error::{MCPError, Result},
10 schema::*,
11 server::{MCPServer, ToolHandler},
12 transport::{StdioTransport, Transport},
13};
14use tracing::info;
15
16struct FailingTool;
18
19#[async_trait]
20impl ToolHandler for FailingTool {
21 fn metadata(&self) -> Tool {
22 Tool {
23 name: "always_fail".to_string(),
24 description: Some("A tool that always fails for testing".to_string()),
25 input_schema: ToolInputSchema {
26 schema_type: "object".to_string(),
27 properties: None,
28 required: None,
29 },
30 annotations: None,
31 }
32 }
33
34 async fn execute(&self, _arguments: Option<serde_json::Value>) -> Result<Vec<Content>> {
35 Err(MCPError::tool_execution_failed(
36 "always_fail",
37 "This tool always fails for testing purposes",
38 ))
39 }
40}
41
42struct StrictTool;
44
45#[async_trait]
46impl ToolHandler for StrictTool {
47 fn metadata(&self) -> Tool {
48 Tool {
49 name: "strict_tool".to_string(),
50 description: Some("A tool that requires specific parameters".to_string()),
51 input_schema: ToolInputSchema {
52 schema_type: "object".to_string(),
53 properties: Some({
54 let mut props = HashMap::new();
55 props.insert(
56 "required_field".to_string(),
57 serde_json::json!({
58 "type": "string",
59 "description": "This field is required"
60 }),
61 );
62 props
63 }),
64 required: Some(vec!["required_field".to_string()]),
65 },
66 annotations: None,
67 }
68 }
69
70 async fn execute(&self, arguments: Option<serde_json::Value>) -> Result<Vec<Content>> {
71 let args = arguments
72 .ok_or_else(|| MCPError::invalid_params("strict_tool", "Missing arguments object"))?;
73
74 let required_field = args
75 .get("required_field")
76 .and_then(|v| v.as_str())
77 .ok_or_else(|| {
78 MCPError::invalid_params(
79 "strict_tool",
80 "Missing or invalid 'required_field' parameter",
81 )
82 })?;
83
84 Ok(vec![Content::Text(TextContent {
85 text: format!("Received: {required_field}"),
86 annotations: None,
87 })])
88 }
89}
90
91#[tokio::main]
92async fn main() -> Result<()> {
93 tracing_subscriber::fmt().with_target(false).init();
95
96 let mut server = MCPServer::new("error-example-server".to_string(), "0.1.0".to_string())
98 .with_capabilities(ServerCapabilities {
99 tools: Some(ToolsCapability {
100 list_changed: Some(true),
101 }),
102 ..Default::default()
103 });
104
105 server.register_tool(Box::new(FailingTool)).await;
107 server.register_tool(Box::new(StrictTool)).await;
108
109 info!("Starting MCP server with error handling examples...");
110 info!("Try these requests to see error handling:");
111 info!(
112 "1. Call a non-existent method: {{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"unknown_method\"}}"
113 );
114 info!(
115 "2. Call failing tool: {{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{{\"name\":\"always_fail\"}}}}"
116 );
117 info!(
118 "3. Call strict tool without params: {{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{{\"name\":\"strict_tool\"}}}}"
119 );
120
121 let transport: Box<dyn Transport> = Box::new(StdioTransport::new());
123 server.serve(transport).await
124}