A2A Server Configuration
Once you have an A2A server running (see Agent-to-Agent (A2A) Protocol), this page is the reference for tuning it: every constructor option, the custom task stores and request-handler components you can plug in, and path-based mounting for deployments behind a load balancer.
Server Configuration Options
Section titled “Server Configuration Options”The A2AServer constructor accepts several configuration options:
agent_factory: Callable that takes acontext_idand returns a fresh agent per context (recommended)agent: A single Strands agent reused across contexts (deprecated; preferagent_factory)max_contexts: Maximum number of per context agents to retain concurrently (default: 1000, must be at least 1)host: Hostname or IP address to bind to (default: “127.0.0.1”)port: Port to bind to (default: 9000)version: Version of the agent (default: “0.0.1”)skills: Custom list of agent skills (default: auto-generated from tools)http_url: Public HTTP URL where this agent will be accessible (optional, enables path-based mounting)serve_at_root: Forces server to serve at root path regardless of http_url path (default: False)task_store: Custom task storage implementation (defaults to InMemoryTaskStore)queue_manager: Custom message queue management (optional)push_config_store: Custom push notification configuration storage (optional)push_sender: Custom push notification sender implementation (optional)enable_a2a_compliant_streaming: Streams responses as A2A artifact updates whenTrue(default:False). The default uses legacy status-update streaming and emits a warning; set this toTrueto conform to the A2A spec. It becomes the default in the next major version.
Provide exactly one of agent_factory or agent, recommend agent_factory.
The TypeScript SDK provides two server classes:
A2AServer: Base class that manages the agent card and request handler. Use this when integrating with your own HTTP framework.A2AExpressServer: Express based server withserve()andcreateMiddleware()methods.
The A2AExpressServer constructor accepts a config object:
agentFactory: Callable that takes acontextIdand returns a fresh agent per context (recommended)agent: A single Strands Agent reused across contexts (deprecated; preferagentFactory)maxContexts: Maximum number of per context agents to retain concurrently (default: 1000, must be at least 1)name(required): Human-readable name for the agentdescription: Description of the agent’s purposehost: Host to bind the server to (default:'127.0.0.1')port: Port to listen on (default:9000)version: Version string for the agent card (default:'0.0.1')httpUrl: Public URL override for the agent cardskills: Skills to advertise in the agent cardtaskStore: Task store for persisting task state (defaults to InMemoryTaskStore)userBuilder: User builder for authentication (default: no authentication)
Provide exactly one of agentFactory or agent, recommend agentFactory.
const server = new A2AExpressServer({ agentFactory: (contextId) => new Agent({ systemPrompt: 'You are a helpful agent.', }), name: 'My Agent', description: 'A helpful agent', // Retain at most 1000 per context agents; evict least recently used maxContexts: 1000, host: '0.0.0.0', port: 8080, version: '1.0.0', httpUrl: 'https://my-agent.example.com', // Public URL override skills: [ { id: 'math', name: 'Math', description: 'Performs calculations', tags: [] }, ],})
await server.serve()Advanced Server Customization
Section titled “Advanced Server Customization”The A2AServer provides access to the underlying FastAPI or Starlette application objects allowing you to further customize server behavior.
from contextlib import asynccontextmanagerfrom strands import Agentfrom strands.multiagent.a2a import A2AServerimport uvicorn
# Create your agent factory and A2A serverdef create_agent(context_id: str) -> Agent: return Agent(name="My Agent", description="A customizable agent", callback_handler=None)
a2a_server = A2AServer(agent_factory=create_agent)
@asynccontextmanagerasync def lifespan(app: FastAPI): """Manage application lifespan with proper error handling.""" # Startup tasks yield # Application runs here # Shutdown tasks
# Access the underlying FastAPI app# Allows passing keyword arguments to FastAPI constructor for further customizationfastapi_app = a2a_server.to_fastapi_app(app_kwargs={"lifespan": lifespan})# Add custom middleware, routes, or configurationfastapi_app.add_middleware(...)
# Or access the Starlette app# Allows passing keyword arguments to FastAPI constructor for further customizationstarlette_app = a2a_server.to_starlette_app(app_kwargs={"lifespan": lifespan})# Customize as needed
# You can then serve the customized app directlyuvicorn.run(fastapi_app, host="127.0.0.1", port=9000)The A2AExpressServer exposes a createMiddleware() method that returns an Express Router, which you can mount in your own Express app:
const express = (await import('express')).default
const server = new A2AExpressServer({ agentFactory: (contextId) => new Agent({ systemPrompt: 'You are a customizable agent.' }), name: 'My Agent', description: 'A customizable agent',})
// Get the A2A middleware as an Express Routerconst a2aRouter = server.createMiddleware()
// Create your own Express app with custom routes/middlewareconst app = express()app.get('/health', (_req, res) => { res.json({ status: 'ok' })})app.use(a2aRouter)
app.listen(9000, '127.0.0.1', () => { console.log('Server listening on http://127.0.0.1:9000')})You can also use an AbortSignal for graceful shutdown:
const server = new A2AExpressServer({ agentFactory: (contextId) => new Agent({ systemPrompt: 'You are a helpful agent.' }), name: 'My Agent',})
const controller = new AbortController()await server.serve({ signal: controller.signal })
// Later, to stop the server:controller.abort()Configurable Request Handler Components
Section titled “Configurable Request Handler Components”The A2AServer supports configurable request handler components for advanced customization:
from strands import Agentfrom strands.multiagent.a2a import A2AServerfrom a2a.server.tasks import TaskStore, PushNotificationConfigStore, PushNotificationSenderfrom a2a.server.events import QueueManager
# Custom task storage implementationclass CustomTaskStore(TaskStore): # Implementation details... pass
# Custom queue managerclass CustomQueueManager(QueueManager): # Implementation details... pass
# Create an agent factory with custom componentsdef create_agent(context_id: str) -> Agent: return Agent(name="My Agent", description="A customizable agent", callback_handler=None)
a2a_server = A2AServer( agent_factory=create_agent, task_store=CustomTaskStore(), queue_manager=CustomQueueManager(), push_config_store=MyPushConfigStore(), push_sender=MyPushSender())Interface Requirements:
Custom implementations must follow these interfaces:
task_store: Must implementTaskStoreinterface froma2a.server.tasksqueue_manager: Must implementQueueManagerinterface froma2a.server.eventspush_config_store: Must implementPushNotificationConfigStoreinterface froma2a.server.taskspush_sender: Must implementPushNotificationSenderinterface froma2a.server.tasks
The TypeScript A2AExpressServer supports a custom taskStore for persisting task state:
import { Agent } from '@strands-agents/sdk'import { A2AExpressServer } from '@strands-agents/sdk/a2a/express'
const server = new A2AExpressServer({ agentFactory: (contextId) => new Agent({ systemPrompt: 'You are a helpful agent.' }), name: 'My Agent', taskStore: myCustomTaskStore, // Must implement TaskStore from @a2a-js/sdk/server})Path-Based Mounting for Containerized Deployments
Section titled “Path-Based Mounting for Containerized Deployments”The A2AServer supports automatic path-based mounting for deployment scenarios involving load balancers or reverse proxies. This allows you to deploy agents behind load balancers with different path prefixes.
from strands import Agentfrom strands.multiagent.a2a import A2AServer
# Create an agent factorydef create_agent(context_id: str) -> Agent: return Agent( name="Calculator Agent", description="A calculator agent", callback_handler=None )
# Deploy with path-based mounting# The agent will be accessible at http://my-alb.amazonaws.com/calculator/a2a_server = A2AServer( agent_factory=create_agent, http_url="http://my-alb.amazonaws.com/calculator")
# For load balancers that strip path prefixes, use serve_at_root=Truea2a_server_with_root = A2AServer( agent_factory=create_agent, http_url="http://my-alb.amazonaws.com/calculator", serve_at_root=True # Serves at root even though URL has /calculator path)Use the httpUrl option to set the public URL for the agent card. For custom path mounting, use createMiddleware() and mount the router at any path in your Express app:
import { Agent } from '@strands-agents/sdk'import { A2AExpressServer } from '@strands-agents/sdk/a2a/express'
const server = new A2AExpressServer({ agentFactory: (contextId) => new Agent({ systemPrompt: 'A calculator agent.' }), name: 'Calculator Agent', httpUrl: 'http://my-alb.amazonaws.com/calculator',})
const express = (await import('express')).defaultconst app = express()app.use('/calculator', server.createMiddleware())app.listen(9000)