Skip to content

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

Merged
JackYPCOnline merged 1 commit into
strands-agents:mainfrom
waitasecant:fix-a2a-server
May 28, 2026
Merged

feat(a2a): add agent_card_url property to A2AServer for customizable url in AgentCard#2003
JackYPCOnline merged 1 commit into
strands-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.

@codecov
Copy link
Copy Markdown

codecov Bot commented May 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Comment thread strands-py/src/strands/multiagent/a2a/server.py
@github-actions
Copy link
Copy Markdown
Contributor

Assessment: Comment

The feature addresses a real use case (decoupling the advertised AgentCard URL from the internal server URL for reverse-proxy/load-balancer deployments). The implementation is clean and follows the existing agent_skills property/setter pattern.

Review Details
  • API Discoverability: The main suggestion is to also expose agent_card_url as a constructor parameter (like skills) so users don't need to discover the setter separately. This aligns with the "obvious path is the happy path" tenet.
  • Consistency: If accepted as a constructor param, it would also be good to add a test covering construction-time configuration.

Overall, a small well-scoped change with good test coverage.

@JackYPCOnline JackYPCOnline enabled auto-merge (squash) May 27, 2026 15:16
@waitasecant
Copy link
Copy Markdown
Contributor Author

The integration test failures are due to external API quota/auth issues (Anthropic API limit exhausted, Mistral API auth invalid, OpenAI web search config) and are not related to the code changes in this PR. These are pre-existing infrastructure issues. The code review is complete and approved by @JackYPCOnline — just awaiting final approval from another reviewer per branch protection rules.

auto-merge was automatically disabled May 28, 2026 16:00

Head branch was pushed to by a user without write access

@JackYPCOnline JackYPCOnline enabled auto-merge (squash) May 28, 2026 16:09
@JackYPCOnline JackYPCOnline merged commit 5afa290 into strands-agents:main May 28, 2026
35 of 56 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants