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    auth: Option<AuthenticationInfo>,
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            auth: None,
22        }
23    }
24
25    /// Set authentication token
26    pub fn with_token(mut self, token: impl Into<String>) -> Self {
27        self.auth = Some(AuthenticationInfo {
28            schemes: vec!["bearer".to_string()],
29            credentials: Some(token.into()),
30        });
31        self
32    }
33
34    /// Get the agent card
35    pub async fn get_agent_card(&self) -> Result<AgentCard> {
36        let url = format!("{}/.well-known/agent.json", self.base_url);
37        let mut req = self.client.get(&url);
38
39        if let Some(ref auth) = self.auth
40            && let Some(ref creds) = auth.credentials
41        {
42            req = req.bearer_auth(creds);
43        }
44
45        let res = req.send().await?.error_for_status()?;
46        let card: AgentCard = res.json().await?;
47        Ok(card)
48    }
49
50    /// Send a message to the agent
51    pub async fn send_message(&self, params: MessageSendParams) -> Result<SendMessageResponse> {
52        let request = JsonRpcRequest {
53            jsonrpc: "2.0".to_string(),
54            id: serde_json::json!(1),
55            method: "message/send".to_string(),
56            params: serde_json::to_value(&params)?,
57        };
58
59        let response = self.call_rpc(request).await?;
60
61        if let Some(error) = response.error {
62            anyhow::bail!("RPC error {}: {}", error.code, error.message);
63        }
64
65        let result: SendMessageResponse = serde_json::from_value(
66            response
67                .result
68                .ok_or_else(|| anyhow::anyhow!("No result"))?,
69        )?;
70        Ok(result)
71    }
72
73    /// Get task status
74    #[allow(dead_code)]
75    pub async fn get_task(&self, id: &str, history_length: Option<usize>) -> Result<Task> {
76        let request = JsonRpcRequest {
77            jsonrpc: "2.0".to_string(),
78            id: serde_json::json!(1),
79            method: "tasks/get".to_string(),
80            params: serde_json::to_value(TaskQueryParams {
81                id: id.to_string(),
82                history_length,
83            })?,
84        };
85
86        let response = self.call_rpc(request).await?;
87
88        if let Some(error) = response.error {
89            anyhow::bail!("RPC error {}: {}", error.code, error.message);
90        }
91
92        let task: Task = serde_json::from_value(
93            response
94                .result
95                .ok_or_else(|| anyhow::anyhow!("No result"))?,
96        )?;
97        Ok(task)
98    }
99
100    /// Cancel a task
101    #[allow(dead_code)]
102    pub async fn cancel_task(&self, id: &str) -> Result<Task> {
103        let request = JsonRpcRequest {
104            jsonrpc: "2.0".to_string(),
105            id: serde_json::json!(1),
106            method: "tasks/cancel".to_string(),
107            params: serde_json::json!({ "id": id }),
108        };
109
110        let response = self.call_rpc(request).await?;
111
112        if let Some(error) = response.error {
113            anyhow::bail!("RPC error {}: {}", error.code, error.message);
114        }
115
116        let task: Task = serde_json::from_value(
117            response
118                .result
119                .ok_or_else(|| anyhow::anyhow!("No result"))?,
120        )?;
121        Ok(task)
122    }
123
124    /// Make a JSON-RPC call
125    pub async fn call_rpc(&self, request: JsonRpcRequest) -> Result<JsonRpcResponse> {
126        let mut req = self.client.post(&self.base_url);
127
128        if let Some(ref auth) = self.auth
129            && let Some(ref creds) = auth.credentials
130        {
131            req = req.bearer_auth(creds);
132        }
133
134        let res = req
135            .header("Content-Type", "application/json")
136            .json(&request)
137            .send()
138            .await?;
139
140        let response: JsonRpcResponse = res.json().await?;
141        Ok(response)
142    }
143}