a2a_protocol_server/handler/lifecycle/
extended_card.rs1use std::collections::HashMap;
9use std::time::Instant;
10
11use a2a_protocol_types::agent_card::AgentCard;
12
13use crate::error::{ServerError, ServerResult};
14
15use super::super::helpers::build_call_context;
16use super::super::RequestHandler;
17
18impl RequestHandler {
19 pub async fn on_get_extended_agent_card(
25 &self,
26 headers: Option<&HashMap<String, String>>,
27 ) -> ServerResult<AgentCard> {
28 let start = Instant::now();
29 self.metrics.on_request("GetExtendedAgentCard");
30
31 let result: ServerResult<_> = async {
32 let call_ctx = build_call_context("GetExtendedAgentCard", headers);
33 self.interceptors.run_before(&call_ctx).await?;
34
35 let card = match &self.agent_card {
39 Some(card) => {
40 let has_capability = card.capabilities.extended_agent_card.unwrap_or(false);
41 if !has_capability {
42 return Err(ServerError::UnsupportedOperation(
43 "agent does not support extended agent card".into(),
44 ));
45 }
46 card.clone()
47 }
48 None => {
49 return Err(ServerError::Protocol(
50 a2a_protocol_types::error::A2aError::new(
51 a2a_protocol_types::error::ErrorCode::ExtendedAgentCardNotConfigured,
52 "extended agent card not configured",
53 ),
54 ));
55 }
56 };
57
58 self.interceptors.run_after(&call_ctx).await?;
59 Ok(card)
60 }
61 .await;
62
63 let elapsed = start.elapsed();
64 match &result {
65 Ok(_) => {
66 self.metrics.on_response("GetExtendedAgentCard");
67 self.metrics.on_latency("GetExtendedAgentCard", elapsed);
68 }
69 Err(e) => {
70 self.metrics
71 .on_error("GetExtendedAgentCard", &e.to_string());
72 self.metrics.on_latency("GetExtendedAgentCard", elapsed);
73 }
74 }
75 result
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use a2a_protocol_types::agent_card::{AgentCapabilities, AgentCard, AgentInterface};
82
83 use crate::agent_executor;
84 use crate::builder::RequestHandlerBuilder;
85 use crate::error::ServerError;
86
87 struct DummyExecutor;
88 agent_executor!(DummyExecutor, |_ctx, _queue| async { Ok(()) });
89
90 fn make_agent_card() -> AgentCard {
91 AgentCard {
92 url: None,
93 name: "Test Agent".into(),
94 description: "A test agent".into(),
95 version: "1.0.0".into(),
96 supported_interfaces: vec![AgentInterface {
97 url: "http://localhost:8080".into(),
98 protocol_binding: "JSONRPC".into(),
99 protocol_version: "1.0.0".into(),
100 tenant: None,
101 }],
102 default_input_modes: vec![],
103 default_output_modes: vec![],
104 skills: vec![],
105 capabilities: AgentCapabilities::none(),
106 provider: None,
107 icon_url: None,
108 documentation_url: None,
109 security_schemes: None,
110 security_requirements: None,
111 signatures: None,
112 }
113 }
114
115 #[tokio::test]
116 async fn get_extended_agent_card_no_card_returns_not_configured_error() {
117 let handler = RequestHandlerBuilder::new(DummyExecutor).build().unwrap();
118 let result = handler.on_get_extended_agent_card(None).await;
119 assert!(
120 matches!(result, Err(ServerError::Protocol(ref e)) if e.code == a2a_protocol_types::error::ErrorCode::ExtendedAgentCardNotConfigured),
121 "expected ExtendedAgentCardNotConfigured when no card configured, got: {result:?}"
122 );
123 }
124
125 #[tokio::test]
126 async fn get_extended_agent_card_without_capability_returns_unsupported() {
127 let card = make_agent_card(); let handler = RequestHandlerBuilder::new(DummyExecutor)
129 .with_agent_card(card)
130 .build()
131 .unwrap();
132 let result = handler.on_get_extended_agent_card(None).await;
133 assert!(
134 matches!(result, Err(ServerError::UnsupportedOperation(_))),
135 "expected UnsupportedOperation when capability is false, got: {result:?}"
136 );
137 }
138
139 #[tokio::test]
140 async fn get_extended_agent_card_with_capability_returns_ok() {
141 let mut card = make_agent_card();
142 card.capabilities = AgentCapabilities::none().with_extended_agent_card(true);
143 let handler = RequestHandlerBuilder::new(DummyExecutor)
144 .with_agent_card(card)
145 .build()
146 .unwrap();
147 let result = handler.on_get_extended_agent_card(None).await;
148 assert!(
149 result.is_ok(),
150 "expected Ok when agent card is configured, got: {result:?}"
151 );
152 assert_eq!(result.unwrap().name, "Test Agent");
153 }
154
155 #[tokio::test]
156 async fn get_extended_agent_card_error_path_records_metrics() {
157 let handler = RequestHandlerBuilder::new(DummyExecutor).build().unwrap();
159 let result = handler.on_get_extended_agent_card(None).await;
160 assert!(
161 result.is_err(),
162 "expected error for error metrics path, got: {result:?}"
163 );
164 }
165}