Skip to main content

codetether_agent/a2a/
client.rs

1//! A2A Client - for connecting to other A2A agents
2
3use super::types::*;
4use anyhow::Result;
5use reqwest::Client;
6
7/// A2A Client for interacting with A2A agents
8pub struct A2AClient {
9    client: Client,
10    base_url: String,
11    token: Option<String>,
12}
13
14#[allow(dead_code)]
15impl A2AClient {
16    /// Create a new A2A client
17    pub fn new(base_url: impl Into<String>) -> Self {
18        Self {
19            client: Client::new(),
20            base_url: base_url.into().trim_end_matches('/').to_string(),
21            token: None,
22        }
23    }
24
25    /// Set authentication token
26    pub fn with_token(mut self, token: impl Into<String>) -> Self {
27        self.token = Some(token.into());
28        self
29    }
30
31    /// Get the agent card
32    pub async fn get_agent_card(&self) -> Result<AgentCard> {
33        let url = format!("{}/.well-known/agent.json", self.base_url);
34        let res = self.client.get(&url).send().await?;
35        let card: AgentCard = res.json().await?;
36        Ok(card)
37    }
38
39    /// Send a message to the agent
40    pub async fn send_message(&self, params: MessageSendParams) -> Result<Task> {
41        let request = JsonRpcRequest {
42            jsonrpc: "2.0".to_string(),
43            id: serde_json::json!(1),
44            method: "message/send".to_string(),
45            params: serde_json::to_value(&params)?,
46        };
47
48        let response = self.call_rpc(request).await?;
49
50        if let Some(error) = response.error {
51            anyhow::bail!("RPC error {}: {}", error.code, error.message);
52        }
53
54        let task: Task = serde_json::from_value(
55            response
56                .result
57                .ok_or_else(|| anyhow::anyhow!("No result"))?,
58        )?;
59        Ok(task)
60    }
61
62    /// Get task status
63    #[allow(dead_code)]
64    pub async fn get_task(&self, id: &str, history_length: Option<usize>) -> Result<Task> {
65        let request = JsonRpcRequest {
66            jsonrpc: "2.0".to_string(),
67            id: serde_json::json!(1),
68            method: "tasks/get".to_string(),
69            params: serde_json::to_value(TaskQueryParams {
70                id: id.to_string(),
71                history_length,
72            })?,
73        };
74
75        let response = self.call_rpc(request).await?;
76
77        if let Some(error) = response.error {
78            anyhow::bail!("RPC error {}: {}", error.code, error.message);
79        }
80
81        let task: Task = serde_json::from_value(
82            response
83                .result
84                .ok_or_else(|| anyhow::anyhow!("No result"))?,
85        )?;
86        Ok(task)
87    }
88
89    /// Cancel a task
90    #[allow(dead_code)]
91    pub async fn cancel_task(&self, id: &str) -> Result<Task> {
92        let request = JsonRpcRequest {
93            jsonrpc: "2.0".to_string(),
94            id: serde_json::json!(1),
95            method: "tasks/cancel".to_string(),
96            params: serde_json::json!({ "id": id }),
97        };
98
99        let response = self.call_rpc(request).await?;
100
101        if let Some(error) = response.error {
102            anyhow::bail!("RPC error {}: {}", error.code, error.message);
103        }
104
105        let task: Task = serde_json::from_value(
106            response
107                .result
108                .ok_or_else(|| anyhow::anyhow!("No result"))?,
109        )?;
110        Ok(task)
111    }
112
113    /// Make a JSON-RPC call
114    pub async fn call_rpc(&self, request: JsonRpcRequest) -> Result<JsonRpcResponse> {
115        let mut req = self.client.post(&self.base_url);
116
117        if let Some(ref token) = self.token {
118            req = req.bearer_auth(token);
119        }
120
121        let res = req
122            .header("Content-Type", "application/json")
123            .json(&request)
124            .send()
125            .await?;
126
127        let response: JsonRpcResponse = res.json().await?;
128        Ok(response)
129    }
130}