Sending Messages
The most common operation: send a message to an agent and get a response.
Synchronous Send
send_message sends a message and waits for the task to complete:
#![allow(unused)] fn main() { use a2a_protocol_sdk::prelude::*; let params = MessageSendParams { tenant: None, message: Message { id: MessageId::new(uuid::Uuid::new_v4().to_string()), role: MessageRole::User, parts: vec![Part::text("What is the capital of France?")], task_id: None, context_id: None, reference_task_ids: None, extensions: None, metadata: None, }, configuration: None, metadata: None, }; let response = client.send_message(params).await?; }
Handling the Response
SendMessageResponse is an enum with two variants:
#![allow(unused)] fn main() { match response { SendMessageResponse::Task(task) => { println!("Task ID: {}", task.id); println!("Status: {:?}", task.status.state); // Extract text from artifacts if let Some(artifacts) = &task.artifacts { for artifact in artifacts { for part in &artifact.parts { if let a2a_protocol_types::message::PartContent::Text { text } = &part.content { println!("Result: {text}"); } } } } } SendMessageResponse::Message(msg) => { // Some agents respond with a direct message instead of a task println!("Direct message: {:?}", msg); } _ => {} } }
Configuration
Customize the send with SendMessageConfiguration:
#![allow(unused)] fn main() { use a2a_protocol_sdk::types::params::SendMessageConfiguration; let params = MessageSendParams { tenant: None, message: make_message("Translate to French"), configuration: Some(SendMessageConfiguration { accepted_output_modes: vec!["text/plain".into()], task_push_notification_config: None, history_length: Some(5), // Include last 5 messages return_immediately: Some(false), // Wait for completion }), metadata: None, }; }
Continuing a Conversation
To continue a conversation, include the context_id from a previous task:
#![allow(unused)] fn main() { let first_response = client.send_message(MessageSendParams { message: Message { id: MessageId::new(uuid::Uuid::new_v4().to_string()), role: MessageRole::User, parts: vec![Part::text("Tell me about Rust")], task_id: None, context_id: None, // New conversation reference_task_ids: None, extensions: None, metadata: None, }, tenant: None, configuration: None, metadata: None, }).await?; // Get the context ID from the first response let context_id = if let SendMessageResponse::Task(task) = &first_response { Some(task.context_id.clone()) } else { None }; // Continue the conversation let follow_up = client.send_message(MessageSendParams { message: Message { id: MessageId::new(uuid::Uuid::new_v4().to_string()), role: MessageRole::User, parts: vec![Part::text("What about error handling?")], task_id: None, context_id: context_id.map(|c| ContextId::new(c.to_string())), reference_task_ids: None, extensions: None, metadata: None, }, tenant: None, configuration: None, metadata: None, }).await?; }
Multi-Part Messages
Send messages with multiple content types:
#![allow(unused)] fn main() { let message = Message { id: MessageId::new(uuid::Uuid::new_v4().to_string()), role: MessageRole::User, parts: vec![ Part::text("Analyze this image:"), Part::url("https://example.com/chart.png"), Part::data(serde_json::json!({ "analysis_type": "detailed", "language": "en" })), ], task_id: None, context_id: None, reference_task_ids: None, extensions: None, metadata: None, }; }
Next Steps
- Streaming Responses — Real-time event streams
- Task Management — Querying and canceling tasks
- Error Handling — Handling failures gracefully