Skip to main content

ftl_sdk_rs/
mcp.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4/// MCP protocol version
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub enum McpProtocolVersion {
7    #[serde(rename = "2025-03-26")]
8    V1,
9}
10
11/// JSON-RPC request
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Request {
14    pub jsonrpc: String,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub id: Option<Value>,
17    pub method: String,
18    pub params: Value,
19}
20
21/// JSON-RPC response
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct Response {
24    pub jsonrpc: String,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub id: Option<Value>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub result: Option<Value>,
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub error: Option<Error>,
31}
32
33/// JSON-RPC error
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct Error {
36    pub code: ErrorCode,
37    pub message: String,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub data: Option<Value>,
40}
41
42/// Error codes
43#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
44#[serde(transparent)]
45pub struct ErrorCode(pub i32);
46
47impl ErrorCode {
48    pub const PARSE_ERROR: ErrorCode = ErrorCode(-32700);
49    pub const INVALID_REQUEST: ErrorCode = ErrorCode(-32600);
50    pub const METHOD_NOT_FOUND: ErrorCode = ErrorCode(-32601);
51    pub const INVALID_PARAMS: ErrorCode = ErrorCode(-32602);
52    pub const INTERNAL_ERROR: ErrorCode = ErrorCode(-32603);
53}
54
55/// Initialize request
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct InitializeRequest {
58    #[serde(rename = "protocolVersion")]
59    pub protocol_version: McpProtocolVersion,
60    #[serde(rename = "clientInfo")]
61    pub client_info: ClientInfo,
62}
63
64/// Initialize response
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct InitializeResponse {
67    #[serde(rename = "protocolVersion")]
68    pub protocol_version: McpProtocolVersion,
69    pub capabilities: Value,
70    #[serde(rename = "serverInfo")]
71    pub server_info: ServerInfo,
72}
73
74/// Client info
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct ClientInfo {
77    pub name: String,
78    pub version: String,
79}
80
81/// Server info
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct ServerInfo {
84    pub name: String,
85    pub version: String,
86}
87
88/// List tools request
89#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct ListToolsRequest {}
91
92/// List tools response
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct ListToolsResponse {
95    pub tools: Vec<Tool>,
96}
97
98/// Tool definition
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct Tool {
101    pub name: String,
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub description: Option<String>,
104    #[serde(rename = "inputSchema")]
105    pub input_schema: Value,
106}
107
108/// Call tool request
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct CallToolRequest {
111    pub name: String,
112    pub arguments: Value,
113}
114
115/// Call tool response
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct CallToolResponse {
118    pub content: Vec<Content>,
119}
120
121/// Content item
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct Content {
124    #[serde(rename = "type")]
125    pub content_type: String,
126    pub text: String,
127}
128
129impl Response {
130    /// Create a success response
131    pub fn success(id: Value, result: Value) -> Self {
132        Self {
133            jsonrpc: "2.0".to_string(),
134            id: Some(id),
135            result: Some(result),
136            error: None,
137        }
138    }
139
140    /// Create an error response
141    pub fn error(id: Value, code: ErrorCode, message: String, data: Option<Value>) -> Self {
142        Self {
143            jsonrpc: "2.0".to_string(),
144            id: Some(id),
145            result: None,
146            error: Some(Error {
147                code,
148                message,
149                data,
150            }),
151        }
152    }
153
154    /// Create an error response without an ID (for parse errors)
155    pub fn error_without_id(code: ErrorCode, message: String, data: Option<Value>) -> Self {
156        Self {
157            jsonrpc: "2.0".to_string(),
158            id: None,
159            result: None,
160            error: Some(Error {
161                code,
162                message,
163                data,
164            }),
165        }
166    }
167}