aws / aws/bedrock-agentcore-sdk-python
[Feature] Auto-populate AgentCard.skills[] from Strands tool_registry in serve_a2a
- Dominant language
- Python
- Stars
- 761
- Forks
- 147
- Avg merge
- 1d 23h
- Merged PRs (30d)
- 7
Description
## Problem
When hosting a Strands Agent with `serve_a2a(StrandsA2AExecutor(agent))`, the
auto-generated `AgentCard.skills[]` always contains a single stub entry with
`id="main"` / `tags=["main"]`, regardless of how many `@tool`s the agent has.
```python
# Strands Agent with tools=[lookup_city_weather, ...]
serve_a2a(StrandsA2AExecutor(agent))
# → GET /.well-known/agent-card.json
# "skills": [{"id": "main", "name": "", "tags": ["main"]}]
```
This diverges from Strands' own
[`A2AServer._get_skills_from_tools`](https://github.com/strands-agents/sdk-python/blob/main/src/strands/multiagent/a2a/server.py),
which walks `agent.tool_registry.get_all_tools_config()` and emits one
`AgentSkill` per registered tool. As a result, users who follow the documented
`serve_a2a` path lose tool-level discovery metadata that Strands' own
`A2AServer` would have exposed automatically.
Root cause is in `bedrock_agentcore.runtime.a2a._build_agent_card`
([link](https://github.com/aws/bedrock-agentcore-sdk-python/blob/main/src/bedrock_agentcore/runtime/a2a.py)):
```python
skills=[AgentSkill(id="main", name=name, description=description, tags=["main"])],
```
which hard-codes a single skill and never inspects `agent.tool_registry`.
### Impact
In our use case (AWS Agent Registry synchronisation via `synchronizationType=URL`),
consumers searching the registry only see the generic `main` skill instead of
the actual tool capabilities (`lookup_city_weather`, etc.). This hurts discovery
even though the underlying Agent still routes tool calls correctly.
## Proposed Solution
Detect a Strands-compatible `tool_registry` via duck typing and, when present,
expand it into one `AgentSkill` per registered tool. Fall back to the existing
`main` stub for executors without a tool registry (LangGraph, Google ADK, etc.)
or when the registry is empty / raises.
Proof-of-concept branch (fork):
Diff summary:
- `src/bedrock_agentcore/runtime/a2a.py`: +59 / -5
- New helper `_skills_from_tool_registry(agent)` extracts skills when
`agent.tool_registry.get_all_tools_config()` is duck-typed present.
- `_build_agent_card` delegates to it; falls back to `id="main"` when no
tools are found.
- `tests/bedrock_agentcore/runtime/test_a2a.py`: +121 / 0
- 5 new unit tests covering populated registry, no registry, empty
registry, registry that raises, and tool configs with missing fields.
- All 29 tests in `test_a2a.py` pass locally (`pytest`).
Key design properties:
- **Framework-agnostic preserved**: duck typing (`hasattr(registry, "get_all_tools_config")`)
avoids hard dependency on Strands; other executors see no change.
- **Backwards compatible**: agents without tools still emit the `main` stub, so
existing callers and tests that assert on `id="main"` continue to work.
- **Defensive**: any exception from `get_all_tools_config()` is logged at
DEBUG and falls back to the `main` stub, so a broken registry never
breaks agent card generation.
## Alternatives considered
1. **Keep the current behaviour and require callers to pass a custom `AgentCard`
via `serve_a2a(..., agent_card=...)`.** This is the current workaround we
use, but it forces every Strands user to re-implement logic that Strands
itself provides out of the box, which feels like a regression rather than a
deliberate design choice.
2. **Push the change into Strands (e.g. add `build_agent_card()` on
`StrandsA2AExecutor`).** This would be cleaner long term, but requires
coordination across two repos and still leaves AgentCore users on the
current SDK version without a fix. The SDK-side duck-typing change is a
self-contained improvement that works today.
3. **Do nothing.** Users who notice the discrepancy can pass
`agent_card=` manually, but for most Strands users this behaviour is
surprising relative to the official Strands documentation.
## Additional context
- AWS public docs show `serve_a2a(StrandsA2AExecutor(agent))` as the
recommended path:
- Strands docs describe automatic skill generation:
> `skills`: Custom list of agent skills (default: auto-generated from tools)
- I understand this repo does not accept external PRs per `CONTRIBUTING.md`,
so I'm filing this as a feature request. If the AgentCore team would like
to pick up the implementation, the fork branch above is ready for
cherry-picking or adaptation.
Contributor guide
Assessment
This issue has not been assessed yet.