Quickstart: Build Your First A2A Server Agent

Get a working A2A server agent running in under 5 minutes using Python and the official SDK. By the end of this guide, your agent will be discoverable, invokable, and ready to register with Foldspace.

Prerequisites


Step 1: Install the SDK

pip install a2a-sdk uvicorn

Step 2: Implement Your Agent

Create a file called agent.py:

import uvicorn
from a2a.server.apps.jsonrpc import A2AFastAPIApplication
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.agent_execution import AgentExecutor, RequestContext
from a2a.server.events import EventQueue
from a2a.server.tasks import InMemoryTaskStore, TaskUpdater
from a2a.types import (
    AgentCard,
    AgentCapabilities,
    AgentSkill,
    Part,
    TextPart,
)


class TravelBookingExecutor(AgentExecutor):
    """Handles flight and hotel search requests."""

    async def execute(self, context: RequestContext, event_queue: EventQueue) -> None:
        updater = TaskUpdater(event_queue, context.task_id, context.context_id)
        await updater.start_work()

        user_input = context.get_user_input()
        result = await self._search_travel_options(user_input)

        await updater.add_artifact(parts=[Part(root=TextPart(text=result))])
        await updater.complete()

    async def cancel(self, context: RequestContext, event_queue: EventQueue) -> None:
        updater = TaskUpdater(event_queue, context.task_id, context.context_id)
        await updater.cancel()

    async def _search_travel_options(self, query: str) -> str:
        # Replace with your actual logic — call an LLM, query APIs, etc.
        return f"Found 3 flight options and 5 hotels matching: {query}"


# --- Agent Card: describes what your agent can do ---

agent_card = AgentCard(
    name="Travel Booking Agent",
    description="Searches flights, hotels, and packages based on travel preferences",
    url="http://localhost:8080/",
    version="1.0.0",
    capabilities=AgentCapabilities(streaming=True),
    default_input_modes=["text/plain"],
    default_output_modes=["text/plain", "application/json"],
    skills=[
        AgentSkill(
            id="search-flights",
            name="Search Flights",
            description="Find available flights between destinations",
            tags=["travel", "flights", "booking"],
            examples=["Find flights from NYC to London next Friday"],
        ),
        AgentSkill(
            id="search-hotels",
            name="Search Hotels",
            description="Find hotels at a destination with filters",
            tags=["travel", "hotels", "accommodation"],
            examples=["Find 4-star hotels in Paris for 3 nights"],
        ),
    ],
)

# --- Wire up and start the server ---

request_handler = DefaultRequestHandler(
    agent_executor=TravelBookingExecutor(),
    task_store=InMemoryTaskStore(),
)

app = A2AFastAPIApplication(
    agent_card=agent_card,
    http_handler=request_handler,
).build()

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8080)

Step 3: Run It

python agent.py

Your agent is now serving at http://localhost:8080.


Step 4: Test It

Fetch the Agent Card:

curl http://localhost:8080/.well-known/agent.json | jq .

Send a message:

curl -X POST http://localhost:8080/ \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": "test-001",
    "method": "message/send",
    "params": {
      "message": {
        "role": "user",
        "parts": [{"kind": "text", "text": "Find flights from SF to Tokyo next week"}],
        "messageId": "msg-001"
      }
    }
  }'

You should get a JSON-RPC response with a completed task and an artifact containing your agent's result.

Validate with the A2A Inspector:

npx @a2a-js/inspector http://localhost:8080

How It Works

The three components of an A2A server:

  1. AgentExecutor — Your business logic. Receives a RequestContext (the user's message) and publishes results to an EventQueue.
  2. DefaultRequestHandler — Protocol plumbing. Routes incoming JSON-RPC calls to your executor, manages task lifecycle and storage.
  3. A2AFastAPIApplication — HTTP layer. Builds a FastAPI/Starlette app with the correct endpoints (/.well-known/agent.json, JSON-RPC POST).

Artifact vs. status: The text you send via add_artifact(...) is what the Foldspace Copilot shows as the answer. Status updates (update_status(state="working", ...)) are treated as progress only — don't put your final answer there. See How Foldspace renders your response for details.

Request flow:

  1. A client sends a JSON-RPC request to A2AFastAPIApplication (the HTTP layer).
  2. It routes the call to DefaultRequestHandler, which manages the task lifecycle.
  3. The handler invokes YourExecutor with the user's message.
  4. Your executor uses TaskUpdater to publish status updates and artifacts to the EventQueue.
  5. Those events are returned to the client as an SSE stream (streaming) or a single JSON-RPC response (non-streaming).

Troubleshooting

  • Problem: Agent Card not found at /.well-known/agent.json.
    What to do: Ensure you're hitting the correct port and the server started without errors.

  • Problem: Tasks stay in working state forever.
    What to do: Ensure your executor always calls complete(), fail(), or cancel() — including in error paths.

  • Problem: Authentication fails when Foldspace invokes your agent.
    What to do: Confirm your Agent Card's authentication.schemes matches what your server validates.




What’s Next

LangGraph integration, streaming, multi-turn, native implementations, TypeScript and Java

Did this page help you?