use crate::error::{Error, Result};
use crate::message::MessageConverter;
use a2a_rs::domain::{agent::{AgentCard, Capabilities, Authentication, Skill}, task::{Task, TaskState, TaskStatus}, message::{Message, MessagePart}};
use rmcp::{Tool, ToolCall, ToolResponse};
use std::sync::Arc;
use uuid::Uuid;
pub struct ToolToAgentAdapter {
tools: Vec<Tool>,
agent_name: String,
agent_description: String,
converter: Arc<MessageConverter>,
}
impl ToolToAgentAdapter {
pub fn new(tools: Vec<Tool>, agent_name: String, agent_description: String) -> Self {
Self {
tools,
agent_name,
agent_description,
converter: Arc::new(MessageConverter::new()),
}
}
pub fn generate_agent_card(&self) -> AgentCard {
let skills = self.tools.iter().map(|tool| {
Skill {
name: tool.name.clone(),
description: tool.description.clone(),
inputs: None,
outputs: None,
input_modes: Some(vec!["text".to_string(), "data".to_string()]),
output_modes: Some(vec!["text".to_string(), "data".to_string()]),
metadata: None,
}
}).collect();
AgentCard {
name: self.agent_name.clone(),
description: self.agent_description.clone(),
url: "https://example.com/agent".to_string(), version: "1.0.0".to_string(),
capabilities: Capabilities {
streaming: true,
push_notifications: false,
state_transition_history: true,
},
authentication: Authentication {
schemes: vec!["Bearer".to_string()],
},
default_input_modes: vec!["text".to_string()],
default_output_modes: vec!["text".to_string()],
skills,
metadata: None,
}
}
pub fn tool_call_to_task(&self, call: &ToolCall) -> Result<Task> {
let task_id = Uuid::new_v4().to_string();
let initial_message = Message {
role: "user".to_string(),
parts: vec![
MessagePart::Text {
text: format!("Call tool: {}", call.method)
},
MessagePart::Data {
data: call.params.clone(),
mime_type: Some("application/json".to_string()),
},
],
};
Ok(Task {
id: task_id,
status: TaskStatus {
state: TaskState::Submitted,
message: Some("Task submitted".to_string()),
},
messages: vec![initial_message],
artifacts: Vec::new(),
history_ttl: Some(3600), metadata: None,
})
}
pub fn task_to_tool_response(&self, task: &Task) -> Result<ToolResponse> {
let last_message = self.converter.extract_agent_message(task)?;
self.converter.message_to_tool_response(last_message)
}
pub fn find_tool(&self, name: &str) -> Option<&Tool> {
self.tools.iter().find(|tool| tool.name == name)
}
pub fn extract_tool_call(&self, message: &Message) -> Result<(String, serde_json::Value)> {
let tool_name = message.parts.iter()
.find_map(|part| {
if let MessagePart::Text { text } = part {
if text.starts_with("Call tool: ") {
Some(text.trim_start_matches("Call tool: ").to_string())
} else {
None
}
} else {
None
}
})
.ok_or_else(|| Error::Translation("Unable to extract tool name from message".into()))?;
let params = message.parts.iter()
.find_map(|part| {
if let MessagePart::Data { data, .. } = part {
Some(data.clone())
} else {
None
}
})
.unwrap_or(serde_json::Value::Null);
Ok((tool_name, params))
}
}