Skip to content

Feat: serve detailed app metadata from /list-apps in the dev server - #727

Open
AmaadMartin wants to merge 2 commits into
mainfrom
feat/list-apps-detailed
Open

Feat: serve detailed app metadata from /list-apps in the dev server#727
AmaadMartin wants to merge 2 commits into
mainfrom
feat/list-apps-detailed

Conversation

@AmaadMartin

@AmaadMartin AmaadMartin commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Please ensure you have read the contribution guide before creating a pull request.

Link to Issue or Description of Change

  1. Link to an existing issue (if applicable):
    N/A

  2. Or, if no issue exists, describe the change:

Problem: The adk-web agent picker reads each agent's description and root-agent name from GET /list-apps?detailed=true. The Python dev server serves that form; the JS dev server ignores the parameter and always returns a bare array of names. A developer on the JS dev server therefore sees a picker with no descriptions.

Solution: AgentLoader.listAgentsDetailed() returns one AppInfo per discovered agent, sorted by name, and the route returns {"apps": AppInfo[]} when detailed is true or 1. The field names are camelCase to match the JSON the Python server emits, because the same front-end bundle consumes both. Every other value of detailed keeps the current array response.

Three deliberate differences from the Python AppInfo model:

  • agents is omitted. list_agents_detailed() never populates it, so it is always null on this route.
  • language is the literal type 'typescript'. adk-js has no YAML agent format, and the source extension is not recoverable after compilation.
  • isComputerUse is always false. adk-js has no computer-use toolset to detect. The field stays because the shared bundle reads it.

?detailed=<anything else> returns the legacy array rather than a 400. The legacy shape is the safe fallback for existing clients.

The parser accepts true and 1, which is this repo's convention for a stringly-typed boolean (getBoolean at dev/src/cli/cli.ts:91). FastAPI also accepts yes and on, so the two servers differ on those two values. I chose the local convention because adk-web only ever sends detailed=true, so the difference is not observable to the shared client.

Collision check: I listed all 619 open PRs on the fork and diffed every PR that touches dev/src/utils/agent_loader.ts or dev/src/server/adk_api_server.ts (#633, #365, #726, #703, #674). None adds listAgentsDetailed or a detailed parameter. git log --all -S listAgentsDetailed is also empty. The overlap is file-level only, in different regions, so this branches from main.

Testing Plan

Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.
Unit Tests:
[x] I have added or updated unit tests for my change.
[x] All unit tests pass locally.

npx vitest run --project unit:dev dev/test/utils/agent_loader_test.ts dev/test/server/adk_api_server_test.ts
# Test Files 2 passed (2) | Tests 92 passed (92)
npx tsc --noEmit    # 280 errors, all pre-existing in core/test on main; 0 in the 4 files I changed
npx eslint "dev/**/*.ts"           # clean
npx prettier --check "dev/**/*.ts" # clean
npm run build                      # exit 0

I added 5 loader cases and 5 server cases. I edited no existing test. The listAgentsDetailed stub added to the shared server mock is a fixture extension; the two existing List Apps assertions are unchanged, because they are the regression signal for the unchanged default response.

Coverage: 100% of the new statements and 100% of the new branches, measured with @vitest/coverage-v8 over lines 484-508 of agent_loader.ts and lines 250-266 of adk_api_server.ts. Zero uncovered statements and zero uncovered branch paths in both ranges.

Proof that the tests can fail. I ran 7 mutations and confirmed the named tests fail.

Mutation Result
language: 'typescript' -> 'javascript' lists agents with detailed metadata fails: expected [...] to deep equally contain { name: 'described_agent', …(4) }
rootAgentName -> root_agent_name (parity guard) 2 fail, including unwraps the root agent of an App: expected undefined to be 'agent_for_app'
Drop the ?? '' on description defaults description to an empty string fails: expected undefined to be ''
Remove the per-agent try/catch skips and logs agents that fail to load fails: Error: boom
for (const name of names.reverse()) sorts detailed entries by name fails: expected [ 'agent3', 'agent2', 'agent1' ] to deeply equal [ 'agent1', 'agent2', 'agent3' ]
Boolean(detailedParam) instead of the two literal comparisons returns the plain array when detailed=false and ...for an unrecognised detailed value fail: expected { apps: [...] } to deeply equal [ 'testApp' ]
Return the bare array instead of the {apps} envelope returns detailed app info when detailed=true and accepts detailed=1 fail: expected [...] to deeply equal { apps: [...] }

Manual End-to-End (E2E) Tests:
Please provide instructions on how to manually test your changes, including any necessary setup or configuration.

npm run build --workspace dev
node dev/dist/esm/cli_entrypoint.js api_server dev/samples --port 8000
curl -s localhost:8000/list-apps
curl -s 'localhost:8000/list-apps?detailed=true' | jq .

I ran this against the 7 bundled samples. The detailed form returns the envelope, and agent_with_tool reports "rootAgentName": "weather_time_agent" with its real description, which proves the unwrap and that name need not equal rootAgentName. Samples that declare no description report "".

I also captured the default response from main and from this branch on the same samples directory. diff reports the two are byte-identical:

["agent_as_tool","agent_transfer_return","agent_with_tool","google_search_agent","loop_agent","parallel_agent","sequential_agent"]

Over real HTTP, ?detailed=1 returns the envelope, and ?detailed=false, ?detailed=yes, ?detailed=, a repeated ?detailed=true&detailed=true, and a nested ?detailed[x]=1 all return 200 with the legacy array.

Checklist

[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.

Amaad Martin added 2 commits August 6, 2026 08:21
The adk-web bundle renders an agent picker from `/list-apps?detailed=true`.
The Python dev server serves that form; the JS dev server did not, so the
picker had no descriptions.

`AgentLoader.listAgentsDetailed()` returns one `AppInfo` per discovered
agent, sorted by name. An agent that fails to load is logged and skipped.
The route keeps its current array response for every other query value.
A failing assertion left the spy installed, which silenced error logging
for every later test in the file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant