A2A Ruby Gem: Agent-to-Agent Communication Made Simple
Remember when APIs were just REST endpoints that returned JSON and we called it a day? Those were simpler times. Now we’re living in the age of agents talking to agents, and frankly, it’s both exciting and slightly terrifying—like watching your smart home devices gossip about your Netflix habits.
Enter the A2A Ruby gem—our fresh implementation of Google’s Agent2Agent protocol that makes building conversational agents as easy as writing a regular Ruby class. No PhD in distributed systems required, though a sense of humor about talking computers definitely helps.
What the Heck is A2A?
Agent2Agent (A2A) is Google’s protocol for—you guessed it—agents talking to other agents. Think of it as a standardized way for AI services to have polite conversations instead of just screaming JSON at each other across the internet.
The protocol is built on JSON-RPC 2.0 (because apparently we needed to make RPC cool again) and includes all the grown-up features you’d expect:
- Agent Discovery via “Agent Cards” (like business cards, but for robots)
- Streaming Responses for when your agent is chatty
- Task Management with proper lifecycle handling
- Security that doesn’t make you cry
- Multiple Transports because variety is the spice of distributed life
Why Another Ruby Gem?
Great question! While Google provides SDKs for Python, JavaScript, and Java, the Ruby ecosystem was feeling a bit left out of the agent party. So we built the A2A Ruby gem to bring first-class A2A support to Ruby applications.
But this isn’t just a “me too” implementation. We’ve designed it with Ruby developers in mind:
# This is all you need to create an A2A agent
class MyAgent
include A2A::Server::Agent
a2a_method "greet" do |params|
{ message: "Hello, #{params['name']}!" }
end
end
That’s it. No XML configuration files, no annotation processors, no sacrificial offerings to the framework gods. Just Ruby being Ruby.
The Gem in Action: Real Examples
Let’s look at some actual agents built with the gem, because examples are worth a thousand architecture diagrams.
The Dice Agent: When Probability Meets Personality
class DiceAgent
include A2A::Server::Agent
a2a_capability "dice_rolling" do
method "roll_dice"
description "Roll dice and track statistics"
tags ["gaming", "probability", "fun"]
end
a2a_method "roll_dice" do |params|
sides = params['sides'] || 6
count = params['count'] || 1
rolls = count.times.map { rand(1..sides) }
{
rolls: rolls,
sum: rolls.sum,
timestamp: Time.now.utc.iso8601,
message: "🎲 You rolled: #{rolls.join(', ')}"
}
end
# Natural language processing? Sure, why not!
a2a_method "process_natural_language" do |params|
text = params['text'].downcase
if text.match?(/roll|dice/)
# Parse "roll two 20-sided dice" into actual parameters
sides = text.match(/(\d+)[- ]?sided?/)&.captures&.first&.to_i || 6
count = text.match(/(\d+)\s+dice/)&.captures&.first&.to_i || 1
roll_dice('sides' => sides, 'count' => count)
else
{ message: "I can help you roll dice! Try 'roll two 6-sided dice'" }
end
end
end
This agent doesn’t just roll dice—it understands natural language, tracks statistics, and even adds emoji because why should humans have all the fun?
The Weather Agent: Meteorology Meets Conversation
class WeatherAgent
include A2A::Server::Agent
a2a_capability "weather_info" do
method "get_current_weather"
description "Get current weather conditions"
input_schema type: "object",
properties: { location: { type: "string" } }
end
a2a_method "get_current_weather" do |params|
location = params['location']
weather_data = WeatherService.fetch(location)
{
location: location,
temperature: weather_data[:temp],
condition: weather_data[:condition],
message: "🌤️ It's #{weather_data[:temp]}°C and #{weather_data[:condition]} in #{location}"
}
end
# This agent also speaks human
a2a_method "process_natural_language" do |params|
text = params['text']
# Extract location from "What's the weather in Tokyo?"
if match = text.match(/weather.*?in\s+([a-zA-Z\s,]+)/i)
location = match[1].strip
get_current_weather('location' => location)
else
{ message: "Ask me about the weather! Try 'What's the weather in London?'" }
end
end
end
The Magic Behind the Scenes
JSON-RPC 2.0 with Style
The gem handles all the JSON-RPC plumbing so you don’t have to think about it:
# Your agent method
a2a_method "echo" do |params|
{ message: params['text'] }
end
# Gets called via JSON-RPC like this:
{
"jsonrpc": "2.0",
"method": "echo",
"params": { "text": "Hello, World!" },
"id": 1
}
# Returns this:
{
"jsonrpc": "2.0",
"result": { "message": "Hello, World!" },
"id": 1
}
Agent Cards: The Business Cards of the Future
Every agent automatically generates an “Agent Card” that describes its capabilities:
{
"name": "Weather Agent",
"description": "Provides weather information and forecasts",
"version": "1.0.0",
"capabilities": [
{
"name": "weather_info",
"methods": ["get_current_weather", "get_forecast"],
"description": "Weather data and forecasting"
}
],
"connection_info": {
"endpoint": "https://weather-agent.example.com/a2a/rpc",
"transport": "JSONRPC"
}
}
This lets other agents discover what your agent can do without having to read documentation (because let’s be honest, who reads documentation?).
Streaming Support for Chatty Agents
Some agents have a lot to say. The gem supports streaming responses:
a2a_method "stream_data", streaming: true do |params|
Enumerator.new do |yielder|
10.times do |i|
yielder << { count: i, message: "Processing step #{i}" }
sleep 0.1 # Simulate work
end
end
end
Rails Integration That Just Works
If you’re building Rails applications (and let’s face it, you probably are), the gem includes a Rails engine:
# In your Gemfile
gem 'a2a-ruby'
# In config/routes.rb
mount A2A::Engine => "/a2a"
# In your controller
class MyAgentController < ApplicationController
include A2A::Server::Agent
a2a_method "hello" do |params|
{ message: "Hello from Rails!" }
end
end
Boom. Your Rails app is now an A2A agent with endpoints at /a2a/rpc
and /a2a/agent-card
.
The Developer Experience We Actually Want
Configuration That Makes Sense
A2A.configure do |config|
config.protocol_version = "0.3.0"
config.default_transport = "JSONRPC"
config.streaming_enabled = true
config.log_level = :info
# Because security matters
config.authentication_required = true
config.rate_limiting_enabled = true
end
Error Handling That Doesn’t Suck
a2a_method "might_fail" do |params|
raise A2A::Errors::InvalidParams, "Missing required field" unless params['required_field']
# Your logic here
end
# Automatically becomes a proper JSON-RPC error response:
{
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "Missing required field"
},
"id": 1
}
Testing That Actually Works
RSpec.describe MyAgent do
include A2A::Testing::Helpers
it "greets users properly" do
response = call_a2a_method("greet", name: "Alice")
expect(response).to be_a2a_success
expect(response['result']['message']).to eq("Hello, Alice!")
end
end
Real-World Use Cases
Microservices That Talk
Instead of REST APIs between services, use A2A for richer communication:
# User service
class UserAgent
a2a_method "get_user_profile" do |params|
user = User.find(params['user_id'])
{ profile: user.to_h, preferences: user.preferences }
end
end
# Recommendation service
class RecommendationAgent
a2a_method "get_recommendations" do |params|
# Call the user service
user_client = A2A::Client::HttpClient.new("https://user-service/a2a")
profile = user_client.call("get_user_profile", user_id: params['user_id'])
# Generate recommendations based on profile
recommendations = RecommendationEngine.generate(profile['preferences'])
{ recommendations: recommendations }
end
end
AI Agent Orchestration
Build systems where AI agents collaborate:
class OrchestratorAgent
a2a_method "process_complex_request" do |params|
# Break down the request
tasks = TaskPlanner.plan(params['request'])
results = tasks.map do |task|
agent_url = AgentRegistry.find_agent_for(task.type)
client = A2A::Client::HttpClient.new(agent_url)
client.call(task.method, task.params)
end
# Combine results
FinalProcessor.combine(results)
end
end
Customer Service Automation
class CustomerServiceAgent
a2a_method "handle_inquiry" do |params|
inquiry = params['inquiry']
# Route to appropriate specialist agent
if inquiry.match?(/billing|payment/)
billing_agent.call("handle_billing_inquiry", inquiry: inquiry)
elsif inquiry.match?(/technical|bug/)
tech_agent.call("handle_tech_support", inquiry: inquiry)
else
general_agent.call("handle_general_inquiry", inquiry: inquiry)
end
end
end
Performance and Production Readiness
Built for Scale
The gem includes production-ready features out of the box:
- Connection pooling for client connections
- Rate limiting with Redis backend
- Comprehensive logging with structured output
- Metrics collection for monitoring
- Health checks for load balancers
- Graceful shutdown handling
Benchmarks That Matter
In our testing, the gem handles:
- 1000+ requests/second on modest hardware
- Sub-10ms latency for simple methods
- Efficient memory usage with connection reuse
- Proper backpressure handling under load
Monitoring Integration
A2A.configure do |config|
config.metrics_enabled = true
config.metrics_backend = :prometheus
# Custom metrics
config.on_method_call do |method_name, duration, success|
MyMetrics.record("a2a_method_duration", duration, method: method_name)
MyMetrics.increment("a2a_method_calls", method: method_name, success: success)
end
end
Getting Started: Your First Agent in 5 Minutes
Installation
gem install a2a-ruby
# or add to Gemfile
gem 'a2a-ruby'
Hello World Agent
# hello_agent.rb
require 'a2a'
class HelloAgent
include A2A::Server::Agent
a2a_method "greet" do |params|
name = params['name'] || 'World'
{ message: "Hello, #{name}!" }
end
end
# Start the server
agent = HelloAgent.new
server = A2A::Server::HttpServer.new(agent, port: 3000)
server.start
Test It
curl -X POST http://localhost:3000/a2a/rpc \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "greet",
"params": {"name": "Ruby Developer"},
"id": 1
}'
Response:
{
"jsonrpc": "2.0",
"result": {
"message": "Hello, Ruby Developer!"
},
"id": 1
}
The Road Ahead
What’s Coming Next
- gRPC transport for high-performance scenarios
- WebSocket support for real-time communication
- Agent discovery service for dynamic agent networks
- Visual agent builder for non-developers
- More authentication schemes (OAuth2, JWT, mTLS)
Community and Contributions
The gem is open source and we’re actively looking for contributors. Whether you want to:
- Add new transport protocols
- Improve documentation
- Build example agents
- Report bugs (please do!)
- Suggest features
Check out our GitHub repository and join the conversation.
Why This Matters
We’re at the beginning of the agent economy. Soon, every application will have some form of AI agent capability, and these agents will need to work together. The A2A protocol provides a standard way for this to happen, and the A2A Ruby gem makes it accessible to the Ruby community.
Think of it as building the roads for the agent economy. Sure, you could build your own proprietary communication protocol, but why would you when there’s a perfectly good highway system available?
The Bigger Picture
This isn’t just about Ruby or even just about agents. It’s about creating interoperable systems that can evolve and scale. When your customer service agent can seamlessly hand off to a billing agent, which can then coordinate with a fulfillment agent, you’ve built something powerful.
And when all of this happens through standardized protocols with proper error handling, authentication, and monitoring, you’ve built something production-ready.
Conclusion: Agents All the Way Down
The A2A Ruby gem represents our bet on the future of distributed systems. Instead of services that just exchange data, we’re building services that can have conversations.
Your Ruby applications can now:
- Expose their capabilities through standardized agent cards
- Communicate naturally with other agents
- Handle complex workflows through agent orchestration
- Scale gracefully with proper production features
And the best part? It feels like writing regular Ruby code, because that’s exactly what it is.
So go ahead, give your Ruby apps a voice. Build agents that roll dice, predict weather, or orchestrate complex business processes. The A2A Ruby gem makes it all possible, and frankly, it’s pretty fun too.
After all, in a world where everything is becoming an agent, shouldn’t your Ruby code get to join the conversation?
Ready to build your first agent? Check out the A2A Ruby gem documentation and start building the future of agent communication today.
About the Author
Sebastian Schkudlara is a Software Architect with over 19 years of experience in designing scalable, cloud-native platforms and secure, agentic AI systems. He specializes in OAuth-based identity frameworks, large language model orchestration, and event-driven architectures for enterprise environments. Sebastian’s expertise consistently bridges the gap between theoretical AI capabilities and practical enterprise implementation, with particular emphasis on security, scalability, and operational excellence.
🔗 Connect: LinkedIn