Skip to content

feat(a2a): add agent_card_url property to A2AServer for customizable url in AgentCard#2003

Open
waitasecant wants to merge 1 commit intostrands-agents:mainfrom
waitasecant:fix-a2a-server
Open

feat(a2a): add agent_card_url property to A2AServer for customizable url in AgentCard#2003
waitasecant wants to merge 1 commit intostrands-agents:mainfrom
waitasecant:fix-a2a-server

Conversation

@waitasecant
Copy link
Copy Markdown
Contributor

@waitasecant waitasecant commented Mar 30, 2026

Description

A2AServer currently sets the url field in the AgentCard to http_url, which always appends a trailing slash. In load-balanced or reverse-proxy deployments, the advertised URL may need to differ from the internal http_url - for example, to omit the trailing slash or use a completely different base URL.

This PR adds an agent_card_url property to A2AServer that defaults to http_url but can be overridden via a setter, giving users control over the URL advertised in the AgentCard.

Related Issues

No related issues.

Documentation PR

To be created after approval

Type of Change

New feature

Testing

Yes, I have tested the change and verified it doesn't break functionality.

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

Use Cases

  1. Trailing slashes are not preferred.
# The agent will be accessible at http://my-alb.amazonaws.com/calculator/
a2a_server = A2AServer(
    agent=agent,
    http_url="http://my-alb.amazonaws.com/calculator"
)
# The agent will be accessible at http://my-alb.amazonaws.com/calculator
a2a_server = A2AServer(
    agent=agent,
    http_url="http://my-alb.amazonaws.com/calculator"
)
a2a_server.agent_card_url("http://my-alb.amazonaws.com/calculator")
  1. In load-balanced or reverse-proxy deployments, the advertised URL may need to differ.
from unittest.mock import MagicMock
from strands import Agent
from strands.models.model import Model
from strands.multiagent.a2a import A2AServer
from a2a.types import AgentSkill


INTERNAL_HOST = "0.0.0.0"
INTERNAL_PORT = 8001
INTERNAL_URL = f"http://{INTERNAL_HOST}:{INTERNAL_PORT}/chat"
PUBLIC_URL = "https://api.example.com"

SKILLS = [
    AgentSkill(
        id="skill-id",
        name="Agent Name",
        description="Agent Description",
        tags=["tag"],
        examples=["Example."],
    )
]

mock_model = MagicMock(spec=Model)

agent = Agent(
    model=mock_model,
    name="Agent Name",
    description="Agent Description",
    tools=[],
    callback_handler=None,
)


# Without agent_card_url
server = A2AServer(
    agent=agent,
    http_url=INTERNAL_URL,
    skills=SKILLS,
)

# The agent card advertises `http://0.0.0.0:8001/chat/`. External clients cannot reach this address.
# Any orchestrator or peer agent that reads the card will fail to connect.
card = server.public_agent_card
"""
One workaround could be to pass public url as `http_url`. The card.url would be correct, but
- The server derives its internal mount_path from http_url.
- Starlette routes are built around that external hostname / path.
- In the container, the process still binds to {INTERNAL_HOST}:{INTERNAL_PORT}.
- The mismatch between what A2AServer thinks is its base URL and
  what uvicorn actually serves on leads to routing confusion.
- You'd also have to manually reconcile the  /agent-card mount that live outside the A2A server.
"""

A2AServer(agent=agent, http_url="{PUBLIC_URL}/chat")
# With agent_card_url
server_with = A2AServer(
    agent=agent,
    http_url=INTERNAL_HTTP_URL,  # container-internal address
    skills=SKILLS,
)

# One-liner override - the card now advertises the gateway URL
server.agent_card_url = f"{PUBLIC_URL}/chat"

card = server.public_agent_card

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant