Creating A2A Agents with Python: Building a Simple Math Agent - Part II

In this blog post, I'll walk through how I built a simple Agent-to-Agent (A2A) math service using Python. The A2A protocol is Google's initiative to standardize how AI agents communicate with each other, creating an interoperable ecosystem of specialized AI services. What is the A2A Protocol? The Agent-to-Agent (A2A) protocol is designed to enable AI agents to communicate with each other using a standardized messaging format. This allows developers to create specialized agents that can work together, each handling specific tasks they're optimized for. Key benefits include: Standardized communication between AI systems Ability to chain specialized services together Easier integration of AI capabilities into applications Building a Simple Math Agent I created a basic A2A agent that performs a simple but useful function: summing numbers from a comma-separated list while filtering out non-numeric values. The A2A Server Component 1. Agent Card The Agent Card is a public metadata document (typically JSON) describing a remote agent’s identity, capabilities, skills, endpoint URL, and authentication requirements. It allows client agents to discover and understand how to interact with remote agents. Key Fields: name, description, version: Basic agent identification. provider: Information about the agent's provider/organization. url: The base endpoint URL for the agent's A2A service. documentationUrl: Link to human-readable documentation. capabilities: Flags indicating support for features like streaming, pushNotifications, stateTransitionHistory. authentication: Specifies supported authentication schemes (e.g., "bearer", "oauth2") aligned with OpenAPI standards. defaultInputModes, defaultOutputModes: Default communication modalities (e.g., "text", "data", "audio"). skills: An array describing specific functions the agent can perform, including id, name, description, tags, examples, and supported inputModes/outputModes for each skill. Let's examine the key parts of the server implementation: pythonclass SreeniMathAgent(BaseA2AServer): def __init__(self, url: str = "http://localhost:5018"): self.metadata = { "name": "Sreeni Math A2A Agent Server", "description": "A simple A2A server that Sums up the given list of numbers", "url": f"{url}/a2a", "version": "1.0.0", "capabilities": { "function_calls": False, "streaming": False }, "skills": [ { "name": "Sreeni Math Agent", "description": "Sums up the given comma separted numbers even filters only digit", "examples": "5018, 1, 5, 6 ,a,b,c" } ] } The metadata defines what the agent can do, providing essential information for other services that might want to use it. It specifies the agent's capabilities and skills. The real work happens in the handle_message method: from python_a2a import (BaseA2AServer, Message, MessageRole, TextContent, pretty_print_message, run_server) import logging from fastapi import FastAPI, Request logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s') class SreeniMathAgent(BaseA2AServer): def __init__(self, url: str = "http://localhost:5018"): """ Initialize the math agent server. Args: url: The server URL """ # Create an agent card with math capabilities self.metadata = { "name": "Sreeni Math A2A Agent Server", "description": "A simple A2A server that Sums up the given list of numbers ", "url": f"{url}/a2a", # Add /a2a endpoint to the URL "version": "1.0.0", "capabilities": { "function_calls": False, "streaming": False }, "skills": [ { "name": "Sreeni Math Agent", "description": "Sums up the given comma separted numbers even filters only digit ", "examples": "5018, 1, 5, 6 ,a,b,c" } ] } def get_metadata(self): """Return the agent's metadata.""" return self.metadata def handle_message(self, message: Message) -> Message: logging.info(f"The following message was received :{message}") if not (hasattr(message,'content') and message.content.type=="text"): response = Message( content=TextContent("Input must be a text Message"), role=MessageRole.AGENT, parent_message_id=getattr(message,"message_id",None), conversation_id=getattr(message,"conversation_id",None)) return response input_message = message.content.text # Handle metadata request if input_message == "__metadata__":

May 7, 2025 - 00:29
 0
Creating A2A Agents with Python: Building a Simple Math Agent - Part II

In this blog post, I'll walk through how I built a simple Agent-to-Agent (A2A) math service using Python. The A2A protocol is Google's initiative to standardize how AI agents communicate with each other, creating an interoperable ecosystem of specialized AI services.
What is the A2A Protocol?

The Agent-to-Agent (A2A) protocol is designed to enable AI agents to communicate with each other using a standardized messaging format. This allows developers to create specialized agents that can work together, each handling specific tasks they're optimized for.

Key benefits include:

  1. Standardized communication between AI systems
  2. Ability to chain specialized services together
  3. Easier integration of AI capabilities into applications

Building a Simple Math Agent

I created a basic A2A agent that performs a simple but useful function: summing numbers from a comma-separated list while filtering out non-numeric values.

The A2A Server Component

1. Agent Card

The Agent Card is a public metadata document (typically JSON) describing a remote agent’s identity, capabilities, skills, endpoint URL, and authentication requirements. It allows client agents to discover and understand how to interact with remote agents.

Key Fields:

name, description, version: Basic agent identification.
provider: Information about the agent's provider/organization.

url: The base endpoint URL for the agent's A2A service.
documentationUrl: Link to human-readable documentation.

capabilities: Flags indicating support for features like streaming, pushNotifications, stateTransitionHistory.

authentication: Specifies supported authentication schemes (e.g., "bearer", "oauth2") aligned with OpenAPI standards.
defaultInputModes, defaultOutputModes: Default communication modalities (e.g., "text", "data", "audio").

skills: An array describing specific functions the agent can perform, including id, name, description, tags, examples, and supported inputModes/outputModes for each skill.

Let's examine the key parts of the server implementation:

pythonclass SreeniMathAgent(BaseA2AServer):
    def __init__(self, url: str = "http://localhost:5018"):
        self.metadata = {
            "name": "Sreeni Math A2A Agent Server",
            "description": "A simple A2A server that Sums up the given list of numbers",
            "url": f"{url}/a2a",
            "version": "1.0.0",
            "capabilities": {
                "function_calls": False,
                "streaming": False
            },
            "skills": [
                {
                    "name": "Sreeni Math Agent",
                    "description": "Sums up the given comma separted numbers even filters only digit",
                    "examples": "5018, 1, 5, 6 ,a,b,c"
                }
            ]
        }

The metadata defines what the agent can do, providing essential information for other services that might want to use it. It specifies the agent's capabilities and skills.

Image description

The real work happens in the handle_message method:


from python_a2a import (BaseA2AServer, Message, MessageRole, TextContent, pretty_print_message, run_server)
import logging
from fastapi import FastAPI, Request

logging.basicConfig(level=logging.INFO,format='%(asctime)s - %(levelname)s - %(message)s')

class SreeniMathAgent(BaseA2AServer):
    def __init__(self, url: str = "http://localhost:5018"):
        """
        Initialize the math agent server.

        Args:
            url: The server URL
        """
        # Create an agent card with math capabilities
        self.metadata = {
            "name": "Sreeni Math A2A Agent Server",
            "description": "A simple A2A server that Sums up the given list of numbers ",
            "url": f"{url}/a2a",  # Add /a2a endpoint to the URL
            "version": "1.0.0",
            "capabilities": {
                "function_calls": False,
                "streaming": False
            },
            "skills": [
                {
                    "name": "Sreeni Math Agent",
                    "description": "Sums up the given comma separted numbers even filters only digit ",
                    "examples": "5018, 1, 5, 6 ,a,b,c"
                }
            ]
        }

    def get_metadata(self):
        """Return the agent's metadata."""
        return self.metadata

    def handle_message(self, message: Message) -> Message:

       logging.info(f"The following message was received :{message}")

       if not (hasattr(message,'content') and message.content.type=="text"):
           response = Message(
               content=TextContent("Input must be a text Message"),
               role=MessageRole.AGENT,
               parent_message_id=getattr(message,"message_id",None),
               conversation_id=getattr(message,"conversation_id",None))
           return response

       input_message = message.content.text

       # Handle metadata request
       if input_message == "__metadata__":
           return Message(
               content=TextContent(text=str(self.metadata)),
               role=MessageRole.AGENT,
               parent_message_id=message.message_id,
               conversation_id=message.conversation_id
           )

       # Split by comma and strip whitespace from each number
       parts = [part.strip() for part in input_message.split(',')]
       # Filter only digits and convert to integers
       numbers = [int(x) for x in parts if x.strip().isdigit()]
       total = sum(numbers)

       # Create a more informative response
       response_text = f"Found {len(numbers)} valid numbers: {numbers}\nThe sum is: {total}"

       return Message (
           content=TextContent(text=response_text),
           role=MessageRole.AGENT,
           parent_message_id=message.message_id,
           conversation_id=message.conversation_id
       )



if __name__=="__main__":
    print("SreeniMathAgent Server is up and running....")
    sreeni_math_agent = SreeniMathAgent()
    run_server(sreeni_math_agent, host="127.0.0.1", port=5018)

Image description

*Note: For production deployment we host this server using FastAPI or Flask framework *

This method:

Validates the incoming message
Handles special metadata requests
Processes the input by splitting on commas and filtering for digits
Calculates the sum and returns a formatted response

The A2A Client Component

For testing, I created a simple client that connects to the math agent and sends test requests:

from python_a2a import A2AClient, Message, TextContent, MessageRole, pretty_print_message
import json
import requests

def get_agent_metadata():
    """Get and display the agent's metadata."""
    # Get metadata directly from the /metadata endpoint
    response = requests.get("http://127.0.0.1:5018/a2a/metadata")
    metadata = response.json()
    print("\nAgent Metadata:")
    print("-" * 50)
    print(json.dumps(metadata, indent=2))
    print("-" * 50)
    return metadata

def test_math_agent(metadata):
    """Test the math agent with various inputs."""
    # Use URL from metadata
    client = A2AClient(metadata["url"])

    # Test cases
    test_cases = [
        "5,6,6,3,2,3,a,b,c",  # Mixed with non-digits
        "10,20,30",           # Larger numbers
        "1, 2, 3",            # Numbers with spaces
        "1,2,3,4,5"           # Basic numbers
    ]

    for test_input in test_cases:
        print(f"\nTesting input: {test_input}")
        message = Message(
            content=TextContent(text=test_input),
            role=MessageRole.USER
        )

        print("\nSending message:")
        pretty_print_message(message)

        response = client.send_message(message)
        print("\nReceived response:")
        pretty_print_message(response)
        print(f"\nResponse text: {response.content.text}")

if __name__ == "__main__":
    # First get and display the agent's metadata
    metadata = get_agent_metadata()

    # Then run the test cases using the metadata
    print("\nRunning test cases...")
    test_math_agent(metadata)

How It Works

When running this code:

The server starts on localhost port 5018
The client fetches the agent's metadata ( Discover)
It sends various test cases to the server
The server processes each input, filtering out non-numeric values
The server returns the valid numbers found and their sum
The client displays the results

For example, given the input "5,6,6,3,2,3,a,b,c", the agent will respond with:
Found 6 valid numbers: [5, 6, 6, 3, 2, 3]
The sum is: 25

A2A Server side logs

Image description

A2A client Side logs

Image description

Image description

Why A2A Matters

This simple example demonstrates the power of the A2A protocol. By creating specialized agents with well-defined capabilities, we can build complex AI systems composed of many purpose-built components.
My math agent is intentionally simple, but it showcases several

important aspects of A2A:

Metadata Discovery: Agents publish their capabilities for others to discover
Standardized Messages: Communication follows a consistent format
Error Handling: The agent handles invalid inputs gracefully
Specialized Logic: Each agent can focus on doing one thing well

Extending the Concept

This basic math agent could be extended in several ways:

Add more mathematical operations (multiplication, division, etc.)
Support for more complex mathematical expressions
Integration with other agents that need numerical processing

Getting Started with A2A

If you're interested in building your own A2A agents:

Install the Python A2A library (python_a2a)
Define your agent's capabilities and logic by extending BaseA2AServer
Implement the handle_message method to process incoming requests
Create a client to test your agent

Conclusion

The A2A protocol represents an exciting step forward in building interoperable AI systems. By creating standardized ways for agents to communicate, we open up possibilities for complex systems made of specialized components.
Even simple agents like my math calculator can be valuable when combined with other services. I'm excited to see how the A2A ecosystem evolves and what innovative agent combinations developers will create!

Thanks
Sreeni Ramadorai