feat(agentex): filter GET /agents by agent card metadata - #411
feat(agentex): filter GET /agents by agent card metadata#411declan-scale wants to merge 2 commits into
Conversation
Adds an optional `agent_card_metadata` query parameter to `GET /agents` that applies an exact JSONB containment (`@>`) filter against `registration_metadata.agent_card.metadata`. Agents whose card is missing or does not contain every requested key/value are excluded; the existing pagination, ordering, task filtering and authorization behavior are preserved. Enables discovery flows where consumers publish opt-in capability flags via the AgentCard and need to enumerate only agents that advertise them.
✱ Stainless preview buildsThis PR will update the openapi python typescript Edit this comment to update them. They will appear in their respective SDK's changelogs.
|
⚠️ agentex-sdk-typescript studio · code · diff
Your SDK build had at least one new warning diagnostic, which is a regression from the base state.
generate ⚠️→build ✅(prev:build ⏭️) →lint ✅(prev:lint ⏭️) →test ✅npm install https://pkg.stainless.com/s/agentex-sdk-typescript/96527b033fb9e94a5d9046904b665e071cdef6e4/dist.tar.gzNew diagnostics (1 warning)
⚠️ Parameter/MissingSchema: Defaulted parameter to `type: string` because no schema was defined.
⚠️ agentex-sdk-python studio · code · diff
Your SDK build had at least one new warning diagnostic, which is a regression from the base state.
generate ⚠️→build ✅(prev:build ⏭️) →lint ✅(prev:lint ⏭️) →test ✅pip install https://pkg.stainless.com/s/agentex-sdk-python/9b8e7881f1bd2e125161e540de69b74cbe7f9afd/agentex_client-0.25.0-py3-none-any.whlNew diagnostics (1 warning)
⚠️ Parameter/MissingSchema: Defaulted parameter to `type: string` because no schema was defined.
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
If you push custom code to the preview branch, re-run this workflow to update the comment.
Last updated: 2026-08-27 20:30:42 UTC
basselatscale
left a comment
There was a problem hiding this comment.
The JSONB containment direction is right, and the integration coverage proves the important subset behavior: an agent whose card contains additional metadata still matches {"permits_capable": true}.
A few changes are needed before this is ready:
-
list_agents() now fails when called directly without agent_card_metadata. Its default is a FastAPI Query object, so json.loads(agent_card_metadata) raises TypeError. This is currently failing the two authorization unit tests. Please use the Annotated[..., Query(...)] = None form, or otherwise ensure the Python default is actually None, and keep the direct-call tests passing.
-
In AgentRepository.list, use if agent_card_metadata is not None: rather than a truthiness check. Otherwise an explicitly supplied {} silently bypasses the metadata predicate and includes agents with missing metadata.
-
Please ensure the OpenAPI/SDK contract supports an ergonomic mapping input rather than requiring every caller to manually json.dumps it. The required consumer shape is:
client.agents.list(
agent_card_metadata={"permits_capable": True},
)
If the wire parameter must remain JSON encoded, the generated/client layer should perform that encoding. The current string schema generates string-typed SDK parameters.
Once those are fixed, this server-side capability is sufficient for our immediate goal: discovering AgentCard-published workflow descriptors and removing the generated input-contract bundle.
- Use Annotated[str | None, Query(...)] = None so list_agents() called
directly (outside FastAPI) defaults to None instead of a Query object
- Apply the containment predicate on `is not None` so an explicit {}
filter still requires a card metadata object to be present
- Declare the query parameter with `content: application/json` and an
object schema so SDK generators expose a mapping-typed parameter and
perform the JSON wire encoding themselves
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed all three review items in 52b3277:
|
🏆 Brought to you by the Golden Agent (Try it out)
Problem
AgentCardpublishes self-description data throughregistration_metadata.agent_card, butGET /agentshas no way to filter on its contents. Discovery flows that want to enumerate agents opting into a specific protocol capability (e.g. Permits' workflow submission protocol) have no server-side hook and must fetch everything client-side.Linear: AGX1-1048
Change
agent_card_metadataquery parameter toGET /agents. The value is a JSON-encoded object; malformed JSON or non-object payloads return400.AgentsUseCase.list, which reserves the keyagent_card_metadatain the repository filters dict.AgentRepository.listapplies a JSONB@>filter at the top level:registration_metadataisNULL, missingagent_card, or missingagent_card.metadataare naturally excluded, and every requested key/value must be present at the correct nesting level.agents.registration_metadatais alreadyJSONB.task_idjoin, authorization id set, andstatus != DELETEDclause).openapi.yamlregenerated by hand to reflect the new query parameter; the paired SDK PR consumes the same spec.Test coverage added
tests/integration/api/agents/test_agents_api.py:limit/page_number.400.tests/unit/use_cases/test_agents_use_case.pythat seed agents directly via the repository and exercise the use-case-to-repo plumbing against real Postgres (single-key, multi-key, absent-card, and omitted-filter cases). Tests use a per-invocation tag so they are safe against session-scoped container reuse.Test plan (for reviewer, since local yarn/uv installs are skipped per Golden Agent policy)
make test-unit) pass, including the two new use-case tests.make test-integration) pass, including the four new API tests.GET /agents?agent_card_metadata={\"permits_capable\":true}against a dev backend seeded with an agent card and confirm only that agent is returned.openapi.yamlstill matches the FastAPI-generated spec (make gen-openapishould produce no further diff).Out of scope / follow-ups
production_deployment_id.Greptile Summary
The PR adds JSON-encoded agent-card metadata filtering to
GET /agents, implemented through PostgreSQL JSONB containment. The latest revision fixes empty-object filtering so{}requires the metadata path to exist.Confidence Score: 5/5
The PR appears safe to merge.
No blocking failure remains; the previously reported empty-object bypass is fixed by applying the containment predicate for every non-null filter, including
{}.Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart LR Client[GET /agents] --> Parse[Parse agent_card_metadata JSON] Parse --> Validate{JSON object?} Validate -->|No| BadRequest[400 response] Validate -->|Yes| UseCase[AgentsUseCase.list] UseCase --> Repository[AgentRepository.list] Repository --> Predicate[JSONB containment predicate] Predicate --> Results[Authorized non-deleted matching agents]Reviews (2): Last reviewed commit: "fix(agentex): address review feedback on..." | Re-trigger Greptile
Context used: