Expand description
Handling incoming messages with on_receive_* callbacks.
So far we’ve seen how to send messages. But ACP is bidirectional - the remote peer can also send messages to you. Use callbacks to handle them.
§Handling Requests
Use on_receive_request to handle incoming requests that expect a response:
Client.builder()
.on_receive_request(async |req: ValidateRequest, responder, cx| {
// Process the request
let is_valid = req.data.len() > 0;
// Send the response
responder.respond(ValidateResponse { is_valid, error: None })
}, agent_client_protocol::on_receive_request!())
.connect_with(transport, async |cx| { Ok(()) })
.await?;Your callback receives three arguments:
- The request payload (e.g.,
PermissionRequest) - A
Responderfor sending the response - A
ConnectionTofor sending other messages
§Handling Notifications
Use on_receive_notification for fire-and-forget messages that don’t need
a response:
Client.builder()
.on_receive_notification(async |notif: StatusUpdate, cx| {
println!("Status: {}", notif.message);
Ok(())
}, agent_client_protocol::on_receive_notification!())§The Request Context
The Responder lets you send a response to the request:
// Send a successful response
responder.respond(MyResponse { status: "ok".into() })?;Or send an error:
responder.respond_with_error(agent_client_protocol::Error::invalid_params())?;You must send exactly one response per request. If your callback returns without responding, an error response is sent automatically.
§Multiple Handlers
You can register multiple handlers. They’re tried in order until one handles the message:
Client.builder()
.on_receive_request(async |req: ValidateRequest, responder, cx| {
// Handle validation requests
responder.respond(ValidateResponse { is_valid: true, error: None })
}, agent_client_protocol::on_receive_request!())
.on_receive_request(async |req: ExecuteRequest, responder, cx| {
// Handle execution requests
responder.respond(ExecuteResponse { result: "done".into() })
}, agent_client_protocol::on_receive_request!())§Ordering Guarantees
Callbacks run inside the dispatch loop and block further message processing until they complete. This gives you ordering guarantees but also means you need to be careful about deadlocks.
See Ordering for the full details.
§Next Steps
- Explicit Peers - Use
_fromvariants to specify the source peer - Ordering - Understand dispatch loop semantics