← back to A2a Lab

server.py

99 lines

"""Minimal A2A (Agent2Agent) demo server — a2a-sdk 1.1.2, current proto API.

An "Echo Agent": receives a text message and returns it upper-cased. It publishes
a standard A2A agent card at /.well-known/agent-card.json and serves the JSON-RPC
A2A endpoint at / . Proves the SDK server stack (AgentExecutor + DefaultRequestHandler
+ InMemoryTaskStore + Starlette route builders).

Run: . .venv/bin/activate && python server.py   (listens on 127.0.0.1:41241)
"""
from __future__ import annotations

import uuid

import uvicorn
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes
from a2a.server.tasks import InMemoryTaskStore
from a2a.types import (
    AgentCapabilities,
    AgentCard,
    AgentInterface,
    AgentSkill,
    Message,
    Part,
    Role,
)
from starlette.applications import Starlette

HOST = "127.0.0.1"
PORT = 41241


class EchoAgentExecutor(AgentExecutor):
    """The agent's brain: read the user's text, reply with it upper-cased."""

    async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
        # get_user_input() concatenates the text parts of the incoming message.
        try:
            user_text = context.get_user_input() or ""
        except Exception:
            user_text = ""
        reply = Message(
            message_id=str(uuid.uuid4()),
            role=Role.ROLE_AGENT,
            parts=[Part(text=f"echo: {user_text.upper()}")],
        )
        await event_queue.enqueue_event(reply)

    async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
        raise NotImplementedError("Echo agent does not support cancellation.")


def build_agent_card() -> AgentCard:
    return AgentCard(
        name="Echo Agent",
        description="Minimal A2A demo agent — returns your text upper-cased.",
        version="0.1.0",
        supported_interfaces=[
            AgentInterface(
                url=f"http://{HOST}:{PORT}/",
                protocol_binding="JSONRPC",
                protocol_version="1.0",
            )
        ],
        capabilities=AgentCapabilities(streaming=False, push_notifications=False),
        default_input_modes=["text/plain"],
        default_output_modes=["text/plain"],
        skills=[
            AgentSkill(
                id="echo",
                name="Echo",
                description="Uppercase-echo the input text.",
                tags=["demo", "echo"],
                examples=["hello world"],
            )
        ],
    )


def build_app() -> Starlette:
    card = build_agent_card()
    handler = DefaultRequestHandler(
        agent_executor=EchoAgentExecutor(),
        task_store=InMemoryTaskStore(),
        agent_card=card,
    )
    routes = [
        *create_agent_card_routes(card),          # GET /.well-known/agent-card.json
        *create_jsonrpc_routes(handler, "/"),      # POST /  (A2A JSON-RPC)
    ]
    return Starlette(routes=routes)


if __name__ == "__main__":
    print(f"A2A Echo Agent on http://{HOST}:{PORT}  (card: /.well-known/agent-card.json)")
    uvicorn.run(build_app(), host=HOST, port=PORT, log_level="warning")