Skip to main content

distri_a2a/
a2a_types.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5/// Represents the Agent-to-Agent (A2A) specification version.
6pub const A2A_VERSION: &str = "0.10.0";
7
8/// Describes an agent's capabilities, skills, and metadata, serving as a public profile.
9/// See: https://google.github.io/A2A/specification/#agentcard-object-structure
10#[derive(Serialize, Deserialize, Debug, JsonSchema)]
11#[serde(rename_all = "camelCase")]
12pub struct AgentCard {
13    /// The version of the A2A specification this agent adheres to.
14    pub version: String,
15    /// The agent's unique name.
16    pub name: String,
17    /// A short description of the agent's purpose.
18    pub description: String,
19    /// The URL where the agent can be reached.
20    pub url: String,
21    /// A URL to an icon for the agent.
22    #[serde(default)]
23    pub icon_url: Option<String>,
24    /// A URL to the agent's documentation.
25    #[serde(default)]
26    pub documentation_url: Option<String>,
27    /// Information about the agent's provider.
28    #[serde(default)]
29    pub provider: Option<AgentProvider>,
30    /// The preferred transport method for communicating with the agent.
31    #[serde(default)]
32    pub preferred_transport: Option<String>,
33    /// The agent's capabilities.
34    pub capabilities: AgentCapabilities,
35    /// The default input modes the agent accepts.
36    pub default_input_modes: Vec<String>,
37    /// The default output modes the agent produces.
38    pub default_output_modes: Vec<String>,
39    /// The skills the agent possesses.
40    pub skills: Vec<AgentSkill>,
41    /// The security schemes supported by the agent.
42    #[serde(default)]
43    pub security_schemes: HashMap<String, SecurityScheme>,
44    /// The security requirements for the agent.
45    #[serde(default)]
46    pub security: Vec<HashMap<String, Vec<String>>>,
47}
48
49/// Provides information about the organization or individual that created the agent.
50#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
51#[serde(rename_all = "camelCase")]
52pub struct AgentProvider {
53    /// The name of the organization.
54    pub organization: String,
55    /// A URL to the provider's website.
56    pub url: String,
57}
58
59/// Defines the agent's supported features and extensions.
60#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Default)]
61#[serde(rename_all = "camelCase")]
62pub struct AgentCapabilities {
63    /// Whether the agent supports streaming responses. Default is true.
64    #[serde(default = "default_true")]
65    pub streaming: bool,
66    /// Whether the agent can send push notifications. Default is false.
67    #[serde(default)]
68    pub push_notifications: bool,
69    /// Whether the agent can provide a history of state transitions.
70    #[serde(default = "default_true")]
71    pub state_transition_history: bool,
72    /// Any extensions the agent supports.
73    #[serde(default)]
74    pub extensions: Vec<AgentExtension>,
75}
76fn default_true() -> bool {
77    true
78}
79
80/// Describes a custom extension supported by the agent.
81#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
82#[serde(rename_all = "camelCase")]
83pub struct AgentExtension {
84    /// A URI that uniquely identifies the extension.
85    pub uri: String,
86    /// A description of the extension.
87    #[serde(default)]
88    pub description: Option<String>,
89    /// Whether the extension is required for the agent to function.
90    #[serde(default)]
91    pub required: bool,
92    /// Any parameters the extension requires.
93    #[serde(default)]
94    pub params: Option<serde_json::Value>,
95}
96
97/// Describes a specific skill or capability of the agent.
98#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
99#[serde(rename_all = "camelCase")]
100pub struct AgentSkill {
101    /// A unique identifier for the skill.
102    pub id: String,
103    /// The name of the skill.
104    pub name: String,
105    /// A description of what the skill does.
106    pub description: String,
107    /// Tags for categorizing the skill.
108    #[serde(default)]
109    pub tags: Vec<String>,
110    /// Examples of how to use the skill.
111    #[serde(default)]
112    pub examples: Vec<String>,
113    /// The input modes the skill accepts, overriding agent defaults.
114    #[serde(default)]
115    pub input_modes: Option<Vec<String>>,
116    /// The output modes the skill produces, overriding agent defaults.
117    #[serde(default)]
118    pub output_modes: Option<Vec<String>>,
119}
120
121/// Defines a security scheme for authenticating with the agent.
122#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
123#[serde(tag = "type", rename_all = "camelCase")]
124pub enum SecurityScheme {
125    ApiKey(APIKeySecurityScheme),
126    Http(HTTPAuthSecurityScheme),
127    Oauth2(Box<OAuth2SecurityScheme>),
128    OpenIdConnect(OpenIdConnectSecurityScheme),
129}
130
131/// An API key-based security scheme.
132#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
133#[serde(rename_all = "camelCase")]
134pub struct APIKeySecurityScheme {
135    /// The name of the header, query, or cookie parameter to be used.
136    pub name: String,
137    /// The location of the API key.
138    #[serde(rename = "in")]
139    pub location: String,
140    /// A description of the security scheme.
141    #[serde(default)]
142    pub description: Option<String>,
143}
144
145/// An HTTP authentication-based security scheme.
146#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
147#[serde(rename_all = "camelCase")]
148pub struct HTTPAuthSecurityScheme {
149    /// The name of the HTTP Authorization scheme to be used.
150    pub scheme: String,
151    /// A hint to the client about the format of the bearer token.
152    #[serde(default)]
153    pub bearer_format: Option<String>,
154    /// A description of the security scheme.
155    #[serde(default)]
156    pub description: Option<String>,
157}
158
159/// An OAuth2-based security scheme.
160#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
161#[serde(rename_all = "camelCase")]
162pub struct OAuth2SecurityScheme {
163    /// The OAuth2 flows supported by this scheme.
164    pub flows: OAuthFlows,
165    /// A description of the security scheme.
166    #[serde(default)]
167    pub description: Option<String>,
168}
169
170/// An OpenID Connect-based security scheme.
171#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
172#[serde(rename_all = "camelCase")]
173pub struct OpenIdConnectSecurityScheme {
174    /// The OpenID Connect discovery URL.
175    pub open_id_connect_url: String,
176    /// A description of the security scheme.
177    #[serde(default)]
178    pub description: Option<String>,
179}
180
181/// Defines the OAuth2 flows.
182#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, Default)]
183#[serde(rename_all = "camelCase")]
184pub struct OAuthFlows {
185    #[serde(default)]
186    pub implicit: Option<ImplicitOAuthFlow>,
187    #[serde(default)]
188    pub password: Option<PasswordOAuthFlow>,
189    #[serde(default)]
190    pub client_credentials: Option<ClientCredentialsOAuthFlow>,
191    #[serde(default)]
192    pub authorization_code: Option<AuthorizationCodeOAuthFlow>,
193}
194
195/// The implicit OAuth2 flow.
196#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
197#[serde(rename_all = "camelCase")]
198pub struct ImplicitOAuthFlow {
199    pub authorization_url: String,
200    #[serde(default)]
201    pub refresh_url: Option<String>,
202    pub scopes: HashMap<String, String>,
203}
204
205/// The password-based OAuth2 flow.
206#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
207#[serde(rename_all = "camelCase")]
208pub struct PasswordOAuthFlow {
209    pub token_url: String,
210    #[serde(default)]
211    pub refresh_url: Option<String>,
212    pub scopes: HashMap<String, String>,
213}
214
215/// The client credentials OAuth2 flow.
216#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
217#[serde(rename_all = "camelCase")]
218pub struct ClientCredentialsOAuthFlow {
219    pub token_url: String,
220    #[serde(default)]
221    pub refresh_url: Option<String>,
222    pub scopes: HashMap<String, String>,
223}
224
225/// The authorization code OAuth2 flow.
226#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
227#[serde(rename_all = "camelCase")]
228pub struct AuthorizationCodeOAuthFlow {
229    pub authorization_url: String,
230    pub token_url: String,
231    #[serde(default)]
232    pub refresh_url: Option<String>,
233    pub scopes: HashMap<String, String>,
234}
235
236// JSON-RPC Types
237
238/// A JSON-RPC request object.
239#[derive(Serialize, Deserialize, Debug)]
240pub struct JsonRpcRequest {
241    pub jsonrpc: String,
242    pub method: String,
243    pub params: serde_json::Value,
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub id: Option<serde_json::Value>,
246}
247
248/// A JSON-RPC response object.
249#[derive(Serialize, Debug)]
250pub struct JsonRpcResponse {
251    pub jsonrpc: String,
252    #[serde(skip_serializing_if = "Option::is_none")]
253    pub result: Option<serde_json::Value>,
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub error: Option<JsonRpcError>,
256    pub id: Option<serde_json::Value>,
257}
258
259/// A JSON-RPC error object.
260#[derive(Serialize, Deserialize, Debug, Clone)]
261pub struct JsonRpcError {
262    pub code: i32,
263    pub message: String,
264    #[serde(default, skip_serializing_if = "Option::is_none")]
265    pub data: Option<serde_json::Value>,
266}
267
268/// Typed read-side view of a JSON-RPC response, parameterized over the
269/// `result` payload `T` (e.g. [`SendMessageResult`]). [`JsonRpcResponse`] is
270/// write-only — servers build it from a `serde_json::Value` result; this is
271/// its deserialize counterpart so clients decode straight into typed values
272/// instead of hand-poking `serde_json::Value`. Reusable across every A2A
273/// method (`message/send`, `tasks/get`, `tasks/cancel`, …).
274#[derive(Deserialize, Debug)]
275#[serde(bound(deserialize = "T: serde::Deserialize<'de>"))]
276pub struct JsonRpcResponseFor<T> {
277    #[serde(default)]
278    pub result: Option<T>,
279    #[serde(default)]
280    pub error: Option<JsonRpcError>,
281}
282
283impl JsonRpcResponse {
284    /// Construct a successful JSON-RPC response.
285    pub fn success(id: Option<serde_json::Value>, result: serde_json::Value) -> Self {
286        Self {
287            jsonrpc: "2.0".to_string(),
288            id,
289            result: Some(result),
290            error: None,
291        }
292    }
293
294    /// Construct a failed JSON-RPC response.
295    pub fn error(id: Option<serde_json::Value>, error: JsonRpcError) -> Self {
296        Self {
297            jsonrpc: "2.0".to_string(),
298            id,
299            result: None,
300            error: Some(error),
301        }
302    }
303}
304
305impl JsonRpcError {
306    /// Generic constructor.
307    pub fn new(code: i32, message: impl Into<String>) -> Self {
308        Self {
309            code,
310            message: message.into(),
311            data: None,
312        }
313    }
314
315    /// `-32602 Invalid params` JSON-RPC error.
316    pub fn invalid_params(msg: impl Into<String>) -> Self {
317        Self::new(-32602, msg)
318    }
319
320    /// `-32601 Method not found` JSON-RPC error.
321    pub fn method_not_found(method: &str) -> Self {
322        Self::new(-32601, format!("Method not found: {method}"))
323    }
324
325    /// `-32603 Internal error` JSON-RPC error.
326    pub fn internal(msg: impl Into<String>) -> Self {
327        Self::new(-32603, msg)
328    }
329}
330
331// A2A Method Params
332
333/// Parameters for the `message/send` method.
334#[derive(Serialize, Deserialize, Debug)]
335#[serde(rename_all = "camelCase")]
336pub struct MessageSendParams {
337    pub message: Message,
338    #[serde(default)]
339    pub configuration: Option<MessageSendConfiguration>,
340    #[serde(default)]
341    pub metadata: Option<serde_json::Value>,
342}
343
344/// Configuration for sending a message.
345#[derive(Serialize, Deserialize, Debug, Default)]
346#[serde(rename_all = "camelCase")]
347pub struct MessageSendConfiguration {
348    pub accepted_output_modes: Vec<String>,
349    #[serde(default)]
350    pub blocking: bool,
351    #[serde(default)]
352    pub history_length: Option<u32>,
353    #[serde(default)]
354    pub push_notification_config: Option<PushNotificationConfig>,
355}
356
357#[derive(Serialize, Deserialize, Debug, Clone)]
358#[serde(untagged)]
359pub enum MessageKind {
360    Message(Message),
361    TaskStatusUpdate(TaskStatusUpdateEvent),
362    Artifact(Artifact),
363}
364
365impl MessageKind {
366    pub fn set_update_props(&mut self, metadata: serde_json::Value, context_id: String) {
367        match self {
368            MessageKind::Message(ref mut m) => {
369                m.metadata = Some(metadata);
370                m.context_id = Some(context_id);
371            }
372            MessageKind::TaskStatusUpdate(ref mut m) => {
373                m.metadata = Some(metadata);
374                m.context_id = context_id;
375            }
376            MessageKind::Artifact(_) => {}
377        }
378    }
379}
380/// A message exchanged between a user and an agent.
381#[derive(Serialize, Deserialize, Debug, Clone)]
382#[serde(rename_all = "camelCase")]
383pub struct Message {
384    pub kind: EventKind,
385    pub message_id: String,
386    pub role: Role,
387    pub parts: Vec<Part>,
388    #[serde(default)]
389    pub context_id: Option<String>,
390    #[serde(default)]
391    pub task_id: Option<String>,
392    #[serde(default)]
393    pub reference_task_ids: Vec<String>,
394    #[serde(default)]
395    pub extensions: Vec<String>,
396    #[serde(default)]
397    pub metadata: Option<serde_json::Value>,
398}
399impl Default for Message {
400    fn default() -> Self {
401        Self {
402            message_id: Default::default(),
403            kind: EventKind::Message,
404            role: Role::Agent,
405            parts: vec![],
406            context_id: None,
407            task_id: None,
408            reference_task_ids: vec![],
409            extensions: vec![],
410            metadata: None,
411        }
412    }
413}
414#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
415pub enum EventKind {
416    #[default]
417    #[serde(rename = "message")]
418    Message,
419    #[serde(rename = "task")]
420    Task,
421    #[serde(rename = "status-update")]
422    TaskStatusUpdate,
423    #[serde(rename = "artifact-update")]
424    TaskArtifactUpdate,
425}
426
427#[cfg(test)]
428mod tests {
429    use super::*;
430    #[test]
431    fn serialize_message_kind() {
432        let message_kind = MessageKind::Message(Message::default());
433        let serialized = serde_json::to_string(&message_kind).unwrap();
434
435        println!("{}", serialized);
436        // Verify it deserializes back correctly (round-trip test)
437        let deserialized: MessageKind = serde_json::from_str(&serialized).unwrap();
438        match deserialized {
439            MessageKind::Message(msg) => {
440                assert_eq!(msg.kind, EventKind::Message);
441                assert_eq!(msg.role, Role::Agent);
442            }
443            _ => panic!("Expected MessageKind::Message"),
444        }
445    }
446}
447/// The role of the message sender.
448#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
449#[serde(rename_all = "camelCase")]
450pub enum Role {
451    User,
452    #[default]
453    Agent,
454}
455
456/// A part of a message.
457#[derive(Serialize, Deserialize, Debug, Clone)]
458#[serde(tag = "kind", rename_all = "camelCase")]
459pub enum Part {
460    #[serde(rename = "text")]
461    Text(TextPart),
462    #[serde(rename = "file")]
463    File(FilePart),
464    #[serde(rename = "data")]
465    Data(DataPart),
466}
467
468/// A text part of a message.
469#[derive(Serialize, Deserialize, Debug, Clone)]
470pub struct TextPart {
471    pub text: String,
472}
473
474/// A file part of a message.
475#[derive(Serialize, Deserialize, Debug, Clone)]
476#[serde(rename_all = "camelCase")]
477pub struct FilePart {
478    pub file: FileObject,
479    #[serde(default)]
480    pub metadata: Option<serde_json::Value>,
481}
482
483impl FilePart {
484    pub fn mime_type(&self) -> Option<&str> {
485        match &self.file {
486            FileObject::WithUri { mime_type, .. } => mime_type.as_deref(),
487            FileObject::WithBytes { mime_type, .. } => mime_type.as_deref(),
488        }
489    }
490}
491
492/// A file object, which can be represented by a URI or by its raw bytes.
493#[derive(Serialize, Deserialize, Debug, Clone)]
494#[serde(untagged, rename_all = "camelCase")]
495pub enum FileObject {
496    WithUri {
497        uri: String,
498        #[serde(default, rename = "mimeType")]
499        mime_type: Option<String>,
500        #[serde(default)]
501        name: Option<String>,
502    },
503    WithBytes {
504        bytes: String,
505        #[serde(default, rename = "mimeType")]
506        mime_type: Option<String>,
507        #[serde(default)]
508        name: Option<String>,
509    },
510}
511
512/// A data part of a message, containing arbitrary JSON data.
513#[derive(Serialize, Deserialize, Debug, Clone)]
514pub struct DataPart {
515    pub data: serde_json::Value,
516}
517
518/// Configuration for push notifications.
519#[derive(Serialize, Deserialize, Debug, Default)]
520#[serde(rename_all = "camelCase")]
521pub struct PushNotificationConfig {
522    pub url: String,
523    #[serde(default)]
524    pub token: Option<String>,
525    #[serde(default)]
526    pub id: Option<String>,
527}
528
529/// Parameters for methods that operate on a task by its ID.
530#[derive(Serialize, Deserialize, Debug)]
531#[serde(rename_all = "camelCase")]
532pub struct TaskIdParams {
533    pub id: String,
534}
535
536/// Represents a task being executed by the agent.
537#[derive(Serialize, Deserialize, Debug, Clone, Default)]
538#[serde(rename_all = "camelCase")]
539pub struct Task {
540    pub kind: EventKind,
541    pub id: String,
542    pub context_id: String,
543    pub status: TaskStatus,
544    #[serde(default)]
545    pub artifacts: Vec<Artifact>,
546    #[serde(default)]
547    pub history: Vec<Message>,
548    #[serde(default)]
549    pub metadata: Option<serde_json::Value>,
550}
551
552/// The status of a task.
553#[derive(Serialize, Deserialize, Debug, Clone, Default)]
554#[serde(rename_all = "camelCase")]
555pub struct TaskStatus {
556    pub state: TaskState,
557    #[serde(default)]
558    pub message: Option<Message>,
559    #[serde(default)]
560    pub timestamp: Option<String>,
561}
562
563/// The state of a task.
564#[derive(Serialize, Deserialize, Debug, Clone, Default)]
565#[serde(rename_all = "camelCase")]
566pub enum TaskState {
567    #[default]
568    Submitted,
569    Working,
570    InputRequired,
571    Completed,
572    Canceled,
573    Failed,
574    Rejected,
575    AuthRequired,
576    Unknown,
577}
578
579/// The `result` of a `message/send` JSON-RPC response
580/// (spec `SendMessageSuccessResponse.result`, `anyOf: [Task, Message]`).
581///
582/// Untagged because both variants self-describe via their required fields —
583/// a `Task` carries `id` + `status`, a bare `Message` carries `messageId` +
584/// `parts` + `role` — so the whole payload deserializes in one typed step
585/// instead of hand-poking `serde_json::Value`. The current servers always
586/// return the `Task` arm; `Message` is kept for spec compliance.
587#[derive(Serialize, Deserialize, Debug, Clone)]
588#[serde(untagged)]
589pub enum SendMessageResult {
590    Task(Task),
591    Message(Message),
592}
593
594/// An artifact produced by a task.
595#[derive(Serialize, Deserialize, Debug, Clone)]
596#[serde(rename_all = "camelCase")]
597pub struct Artifact {
598    pub artifact_id: String,
599    pub parts: Vec<Part>,
600    #[serde(default)]
601    pub name: Option<String>,
602    #[serde(default)]
603    pub description: Option<String>,
604}
605
606// A2A Streaming Response Types
607
608/// Task Status Update Event - sent during streaming
609#[derive(Serialize, Deserialize, Debug, Clone)]
610#[serde(rename_all = "camelCase")]
611pub struct TaskStatusUpdateEvent {
612    pub kind: EventKind,
613    pub task_id: String,
614    pub context_id: String,
615    pub status: TaskStatus,
616    pub r#final: bool,
617    #[serde(default)]
618    pub metadata: Option<serde_json::Value>,
619}
620
621/// Task Artifact Update Event - sent during streaming
622#[derive(Serialize, Deserialize, Debug, Clone)]
623#[serde(rename_all = "camelCase")]
624pub struct TaskArtifactUpdateEvent {
625    pub kind: EventKind,
626    pub task_id: String,
627    pub context_id: String,
628    pub artifact: Artifact,
629    #[serde(default)]
630    pub append: Option<bool>,
631    #[serde(default)]
632    pub last_chunk: Option<bool>,
633    #[serde(default)]
634    pub metadata: Option<serde_json::Value>,
635}
636
637// Event Broadcasting Types
638
639/// Event for broadcasting task status changes
640#[derive(Serialize, Deserialize, Debug, Clone)]
641#[serde(rename_all = "camelCase")]
642pub struct TaskStatusBroadcastEvent {
643    pub r#type: String,
644    pub task_id: String,
645    pub thread_id: String,
646    pub agent_id: String,
647    pub status: String,
648}