Skip to content

[FEATURE] Path Prefix Support for A2A Server #382

@mukitmomin

Description

@mukitmomin

Problem Statement

When deploying multiple A2A agents behind an Application Load Balancer (ALB) with path-based routing, the current A2A Server implementation doesn't natively support path prefixes. This requires custom middleware implementation to strip path prefixes from incoming requests, as ALBs don't provide a mechanism to rewrite URL paths.

Currently, users must create a custom PathAwareA2AServer class that extends A2AServer and implements middleware to handle path prefixes:

class PathAwareA2AServer(A2AServer):
   """A2A Server that can handle path prefixes using middleware."""
   
   def init(self, agent: SAAgent, path_prefix: str = "", kwargs):
       super().init__(agent, kwargs)
       self.path_prefix = path_prefix.rstrip('/')
       
   def to_starlette_app(self) -> Starlette:
       """Create a Starlette application with path handling middleware."""
       app = super().to_starlette_app()
       
       if self.path_prefix:
           # Wrap the app with path handling middleware
           app.add_middleware(PathHandlingMiddleware, path_prefix=self.path_prefix)
       
       return app

The PathHandlingMiddleware setup is as follows:

class PathHandlingMiddleware(BaseHTTPMiddleware):
    """Middleware that handles path prefixes by stripping them before processing."""
    
    def __init__(self, app, path_prefix: str = ""):
        super().__init__(app)
        self.path_prefix = path_prefix.rstrip('/')
    
    async def dispatch(self, request: Request, call_next):
        # If we have a path prefix and the request path starts with it, strip it
        if self.path_prefix and request.url.path.startswith(self.path_prefix):
            # Create a new request with the stripped path
            stripped_path = request.url.path[len(self.path_prefix):]
            if not stripped_path:
                stripped_path = "/"
            
            # Modify the request's path_info for downstream processing
            request.scope["path"] = stripped_path
            request.scope["raw_path"] = stripped_path.encode()
        
        response = await call_next(request)
        return response

Proposed Solution

Add native path prefix support to the A2AServer class with the following changes:

  1. Add a path_prefix parameter to the A2AServer constructor:
def init(self, agent: SAAgent, port: int = 8080, host: str = "0.0.0.0", path_prefix: str = "", kwargs):
      self.path_prefix = path_prefix.rstrip('/')
      # existing initialization code
  1. Modify the to_starlette_app() method to automatically add path handling middleware when a prefix is specified:
def to_starlette_app(self) -> Starlette:
     app = super().to_starlette_app()
     
     if self.path_prefix:
         # Add path handling middleware
         app.add_middleware(PathHandlingMiddleware, path_prefix=self.path_prefix)
     
     return app
  1. Include a PathHandlingMiddleware
    class in the Strands library that handles path prefix stripping.

Use Case

This feature would provide a standardized way of handling path prefixes and support a common deployment process for containerized A2A agents.

# Create A2A server with path prefix support
a2a_agent = A2AServer(
   agent=agent,
   port=8080,
   host="0.0.0.0",
   path_prefix="/basic"  # Path prefix for ALB routing
)

This would allow the agent to correctly handle requests to both /basic/chat and /basic/.well-known/agent.json by internally routing them to /chat and /.well-known/agent.json respectively.

Alternatives Solutions

No response

Additional Context

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions