ftl_sdk_rs/
types.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use thiserror::Error;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct JsonRpcRequest {
7    pub jsonrpc: String,
8    pub method: String,
9    #[serde(skip_serializing_if = "Option::is_none")]
10    pub params: Option<Value>,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub id: Option<Value>,
13}
14
15#[derive(Debug, Serialize, Deserialize)]
16pub struct JsonRpcResponse {
17    pub jsonrpc: String,
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub result: Option<Value>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub error: Option<JsonRpcError>,
22    pub id: Value,
23}
24
25#[derive(Debug, Serialize, Deserialize)]
26pub struct JsonRpcError {
27    pub code: i32,
28    pub message: String,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub data: Option<Value>,
31}
32
33#[derive(Debug, Error)]
34pub enum McpError {
35    #[error("Invalid request: {0}")]
36    InvalidRequest(String),
37
38    #[error("Method not found: {0}")]
39    MethodNotFound(String),
40
41    #[error("Tool error: {0}")]
42    ToolError(#[from] ToolError),
43
44    #[error("JSON error: {0}")]
45    JsonError(#[from] serde_json::Error),
46}
47
48#[derive(Debug, Error)]
49pub enum ToolError {
50    #[error("Invalid arguments: {0}")]
51    InvalidArguments(String),
52
53    #[error("Execution error: {0}")]
54    ExecutionError(String),
55
56    #[error("Network error: {0}")]
57    NetworkError(String),
58
59    #[error("Timeout")]
60    Timeout,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct ToolResult {
65    #[serde(rename = "type")]
66    pub result_type: String,
67    pub content: Vec<TextContent>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct TextContent {
72    #[serde(rename = "type")]
73    pub content_type: String,
74    pub text: String,
75}
76
77impl ToolResult {
78    pub fn text(content: String) -> Self {
79        Self {
80            result_type: "content".to_string(),
81            content: vec![TextContent {
82                content_type: "text".to_string(),
83                text: content,
84            }],
85        }
86    }
87}
88
89impl JsonRpcError {
90    pub fn invalid_request(message: String) -> Self {
91        Self {
92            code: -32600,
93            message,
94            data: None,
95        }
96    }
97
98    pub fn method_not_found(method: String) -> Self {
99        Self {
100            code: -32601,
101            message: format!("Method not found: {method}"),
102            data: None,
103        }
104    }
105
106    pub fn internal_error(message: String) -> Self {
107        Self {
108            code: -32603,
109            message,
110            data: None,
111        }
112    }
113}
114
115impl JsonRpcResponse {
116    pub fn error(id: Option<Value>, code: i32, message: &str) -> Self {
117        Self {
118            jsonrpc: "2.0".to_string(),
119            result: None,
120            error: Some(JsonRpcError {
121                code,
122                message: message.to_string(),
123                data: None,
124            }),
125            id: id.unwrap_or(serde_json::Value::Null),
126        }
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use serde_json::json;
133
134    use super::*;
135
136    #[test]
137    fn test_jsonrpc_request_serialization() {
138        let request = JsonRpcRequest {
139            jsonrpc: "2.0".to_string(),
140            method: "test".to_string(),
141            params: Some(json!({"key": "value"})),
142            id: Some(json!(1)),
143        };
144
145        let serialized = serde_json::to_string(&request).unwrap();
146        let deserialized: JsonRpcRequest = serde_json::from_str(&serialized).unwrap();
147
148        assert_eq!(deserialized.jsonrpc, "2.0");
149        assert_eq!(deserialized.method, "test");
150        assert_eq!(deserialized.params, Some(json!({"key": "value"})));
151        assert_eq!(deserialized.id, Some(json!(1)));
152    }
153
154    #[test]
155    fn test_jsonrpc_response_with_result() {
156        let response = JsonRpcResponse {
157            jsonrpc: "2.0".to_string(),
158            result: Some(json!({"status": "ok"})),
159            error: None,
160            id: json!(1),
161        };
162
163        let serialized = serde_json::to_string(&response).unwrap();
164        assert!(!serialized.contains("\"error\""));
165        assert!(serialized.contains("\"result\""));
166    }
167
168    #[test]
169    fn test_jsonrpc_response_with_error() {
170        let response = JsonRpcResponse {
171            jsonrpc: "2.0".to_string(),
172            result: None,
173            error: Some(JsonRpcError::method_not_found("unknown".to_string())),
174            id: json!(1),
175        };
176
177        let serialized = serde_json::to_string(&response).unwrap();
178        assert!(serialized.contains("\"error\""));
179        assert!(!serialized.contains("\"result\""));
180    }
181
182    #[test]
183    fn test_tool_result_creation() {
184        let result = ToolResult::text("Hello, world!".to_string());
185
186        assert_eq!(result.result_type, "content");
187        assert_eq!(result.content.len(), 1);
188        assert_eq!(result.content[0].content_type, "text");
189        assert_eq!(result.content[0].text, "Hello, world!");
190    }
191
192    #[test]
193    fn test_jsonrpc_error_codes() {
194        let invalid_request = JsonRpcError::invalid_request("bad request".to_string());
195        assert_eq!(invalid_request.code, -32600);
196
197        let method_not_found = JsonRpcError::method_not_found("unknown".to_string());
198        assert_eq!(method_not_found.code, -32601);
199
200        let internal_error = JsonRpcError::internal_error("server error".to_string());
201        assert_eq!(internal_error.code, -32603);
202    }
203
204    #[test]
205    fn test_tool_error_display() {
206        let error = ToolError::InvalidArguments("missing field".to_string());
207        assert_eq!(error.to_string(), "Invalid arguments: missing field");
208
209        let error = ToolError::ExecutionError("failed to process".to_string());
210        assert_eq!(error.to_string(), "Execution error: failed to process");
211
212        let error = ToolError::Timeout;
213        assert_eq!(error.to_string(), "Timeout");
214    }
215}