1use serde::{Deserialize, Serialize};
2use serde_json::{Value, json};
3
4pub const MCP_PROTOCOL_VERSION: &str = "2025-06-18";
5
6#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
7pub struct JsonRpcRequest {
8 pub jsonrpc: String,
9 pub id: Option<Value>,
10 pub method: String,
11 #[serde(default)]
12 pub params: Option<Value>,
13}
14
15#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
16pub struct JsonRpcResponse {
17 pub jsonrpc: String,
18 pub id: Value,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub result: Option<Value>,
21 #[serde(skip_serializing_if = "Option::is_none")]
22 pub error: Option<JsonRpcError>,
23}
24
25#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
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(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
34pub struct McpBridgeRequest {
35 pub line: String,
36}
37
38#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
39pub struct McpBridgeResponse {
40 pub line: Option<String>,
41}
42
43pub fn json_rpc_result(id: Value, result: Value) -> JsonRpcResponse {
44 JsonRpcResponse {
45 jsonrpc: "2.0".to_owned(),
46 id,
47 result: Some(result),
48 error: None,
49 }
50}
51
52pub fn json_rpc_failure(id: Value, error: JsonRpcError) -> JsonRpcResponse {
53 JsonRpcResponse {
54 jsonrpc: "2.0".to_owned(),
55 id,
56 result: None,
57 error: Some(error),
58 }
59}
60
61pub fn json_rpc_error(code: i32, message: impl Into<String>) -> JsonRpcError {
62 JsonRpcError {
63 code,
64 message: message.into(),
65 data: None,
66 }
67}
68
69pub fn tool_success<T>(text: String, structured: &T) -> Value
70where
71 T: Serialize,
72{
73 json!({
74 "content": [{"type": "text", "text": text}],
75 "structuredContent": serde_json::to_value(structured)
76 .expect("structured MCP result serializes")
77 })
78}
79
80pub fn tool_error(message: impl Into<String>) -> Value {
81 let message = message.into();
82 json!({
83 "content": [{"type": "text", "text": format!("ERROR: {message}")}],
84 "_meta": {
85 "rtm_tool_error": {
86 "is_error": true,
87 "message": message
88 }
89 }
90 })
91}