diff --git a/src/bedrock_agentcore/runtime/a2a.py b/src/bedrock_agentcore/runtime/a2a.py index 4a05d871..ad6a4204 100644 --- a/src/bedrock_agentcore/runtime/a2a.py +++ b/src/bedrock_agentcore/runtime/a2a.py @@ -252,9 +252,10 @@ def build_a2a_app( executor: An ``AgentExecutor`` that implements the agent logic. agent_card: Optional ``a2a.types.AgentCard`` describing the agent. If ``None``, one is built automatically by introspecting the executor. - runtime_url: URL advertised by an automatically generated agent card. - Defaults to ``http://localhost:9000/``. ``AGENTCORE_RUNTIME_URL`` - takes precedence when set. + runtime_url: URL advertised by the agent card. When an explicit card is + supplied, its JSON-RPC URL is updated when this argument is set. + Defaults to ``http://localhost:9000/`` for automatically generated + cards. ``AGENTCORE_RUNTIME_URL`` takes precedence when set. task_store: Optional ``TaskStore``; defaults to ``InMemoryTaskStore``. context_builder: Optional ``ServerCallContextBuilder``; defaults to ``BedrockCallContextBuilder``. @@ -273,13 +274,14 @@ def build_a2a_app( from starlette.responses import JSONResponse from starlette.routing import Route - runtime_url = os.environ.get(AGENTCORE_RUNTIME_URL_ENV, runtime_url or "http://localhost:9000/") + runtime_url_override = os.environ.get(AGENTCORE_RUNTIME_URL_ENV) or runtime_url + advertised_url = runtime_url_override or "http://localhost:9000/" is_a2a_v1 = _is_a2a_v1() if agent_card is None: - agent_card = _build_agent_card(executor, runtime_url) - elif os.environ.get(AGENTCORE_RUNTIME_URL_ENV): - _set_jsonrpc_url(agent_card, runtime_url) + agent_card = _build_agent_card(executor, advertised_url) + elif runtime_url_override: + _set_jsonrpc_url(agent_card, advertised_url) if task_store is None: task_store = InMemoryTaskStore() diff --git a/tests/bedrock_agentcore/runtime/test_a2a.py b/tests/bedrock_agentcore/runtime/test_a2a.py index 6b804d8f..dd3c0062 100644 --- a/tests/bedrock_agentcore/runtime/test_a2a.py +++ b/tests/bedrock_agentcore/runtime/test_a2a.py @@ -236,8 +236,15 @@ def test_agent_card_url_auto_populated_from_env(self): build_a2a_app(_EchoExecutor(), card) assert _card_url(card) == "https://deployed.example.com/" + def test_agent_card_url_uses_explicit_runtime_url(self): + """A supplied runtime_url overrides the URL on an explicit card.""" + card = _make_agent_card() + with patch.dict("os.environ", {}, clear=True): + build_a2a_app(_EchoExecutor(), card, runtime_url="http://localhost:9002/") + assert _card_url(card) == "http://localhost:9002/" + def test_agent_card_url_unchanged_without_env(self): - """When AGENTCORE_RUNTIME_URL is not set, the agent card URL stays as-is.""" + """Without a runtime URL override, an explicit card URL stays as-is.""" card = _make_agent_card() original_url = _card_url(card) with patch.dict("os.environ", {}, clear=False): @@ -466,11 +473,41 @@ def test_port_from_environment(self, mock_uvicorn_run): assert mock_uvicorn_run.call_args.kwargs["port"] == 9001 assert _card_response_url(response.json()) == "http://localhost:9001/" + @patch("uvicorn.run") + def test_port_from_environment_updates_explicit_card(self, mock_uvicorn_run): + with patch.dict("os.environ", {"PORT": "9002"}, clear=True): + serve_a2a(_EchoExecutor(), _make_agent_card()) + + app = mock_uvicorn_run.call_args.args[0] + response = TestClient(app).get("/.well-known/agent-card.json") + assert mock_uvicorn_run.call_args.kwargs["port"] == 9002 + assert _card_response_url(response.json()) == "http://localhost:9002/" + @patch("uvicorn.run") def test_explicit_port_overrides_environment(self, mock_uvicorn_run): with patch.dict("os.environ", {"PORT": "9001"}): serve_a2a(_EchoExecutor(), _make_agent_card(), port=8888) + app = mock_uvicorn_run.call_args.args[0] + response = TestClient(app).get("/.well-known/agent-card.json") assert mock_uvicorn_run.call_args.kwargs["port"] == 8888 + assert _card_response_url(response.json()) == "http://localhost:8888/" + + @patch("uvicorn.run") + def test_runtime_url_environment_overrides_port_for_card(self, mock_uvicorn_run): + with patch.dict( + "os.environ", + { + "PORT": "9002", + "AGENTCORE_RUNTIME_URL": "https://deployed.example.com/", + }, + clear=True, + ): + serve_a2a(_EchoExecutor(), _make_agent_card()) + + app = mock_uvicorn_run.call_args.args[0] + response = TestClient(app).get("/.well-known/agent-card.json") + assert mock_uvicorn_run.call_args.kwargs["port"] == 9002 + assert _card_response_url(response.json()) == "https://deployed.example.com/" @patch("uvicorn.run") def test_docker_detection_dockerenv(self, mock_uvicorn_run):