1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! Message conversion between A2A and RMCP protocols
use crate::error::{Error, Result};
use a2a_rs::domain::{message::{Message, MessagePart}, task::Task};
use rmcp::{ClientJsonRpcMessage, ServerJsonRpcMessage, ToolCall, ToolResponse};
use serde_json::Value;
/// Converts between RMCP and A2A message formats
#[derive(Debug, Default)]
pub struct MessageConverter {}
impl MessageConverter {
/// Create a new message converter
pub fn new() -> Self {
Self {}
}
/// Convert RMCP request to A2A message
pub fn rmcp_to_a2a_request(&self, req: &ClientJsonRpcMessage) -> Result<Message> {
// Extract method and params from RMCP JSON-RPC request
let method = req.method.clone();
let params = req.params.clone();
// Create A2A message with appropriate content
let mut parts = Vec::new();
// Add text part describing the tool call
parts.push(MessagePart::Text {
text: format!("Call method: {}", method)
});
// Add data part with the parameters
if let Some(params_value) = params {
parts.push(MessagePart::Data {
data: params_value.clone(),
mime_type: Some("application/json".to_string()),
});
}
Ok(Message {
role: "user".to_string(),
parts,
})
}
/// Convert A2A message to RMCP response
pub fn a2a_to_rmcp_response(&self, msg: &Message, id: Option<Value>) -> Result<ServerJsonRpcMessage> {
// Extract content from A2A message parts
let mut result_value = Value::Null;
for part in &msg.parts {
match part {
MessagePart::Data { data, .. } => {
// Use the data part as the result if available
result_value = data.clone();
break;
},
MessagePart::Text { text } => {
// If only text is available, convert to string result
if result_value == Value::Null {
result_value = Value::String(text.clone());
}
},
_ => continue,
}
}
// Create RMCP JSON-RPC response
Ok(ServerJsonRpcMessage {
jsonrpc: "2.0".to_string(),
id,
result: Some(result_value),
error: None,
})
}
/// Convert RMCP tool call to A2A message
pub fn tool_call_to_message(&self, call: &ToolCall) -> Result<Message> {
let mut parts = Vec::new();
// Add text part for the method
parts.push(MessagePart::Text {
text: format!("Tool call: {}", call.method)
});
// Add data part for the parameters
parts.push(MessagePart::Data {
data: call.params.clone(),
mime_type: Some("application/json".to_string()),
});
Ok(Message {
role: "user".to_string(),
parts,
})
}
/// Convert A2A message to RMCP tool response
pub fn message_to_tool_response(&self, msg: &Message) -> Result<ToolResponse> {
let mut result = Value::Null;
// Extract content from message parts
for part in &msg.parts {
match part {
MessagePart::Data { data, .. } => {
result = data.clone();
break;
},
MessagePart::Text { text } => {
if result == Value::Null {
result = Value::String(text.clone());
}
},
_ => continue,
}
}
Ok(ToolResponse { result })
}
/// Extract the last agent message from a task
pub fn extract_agent_message<'a>(&self, task: &'a Task) -> Result<&'a Message> {
task.messages.iter()
.filter(|msg| msg.role == "agent" || msg.role == "assistant")
.last()
.ok_or_else(|| Error::TaskProcessing("No agent message found".into()))
}
/// Extract the last user message from a task
pub fn extract_user_message<'a>(&self, task: &'a Task) -> Result<&'a Message> {
task.messages.iter()
.filter(|msg| msg.role == "user" || msg.role == "human")
.last()
.ok_or_else(|| Error::TaskProcessing("No user message found".into()))
}
}