| name | google-adk |
| description | Google ADK (Agent Development Kit) Python skill. Use when building AI agents with google-adk, Gemini models, SequentialAgent, ParallelAgent, LoopAgent, FunctionTool, McpToolset, session management, memory services, artifact storage, callbacks, or FastAPI integration for ADK agents. |
| allowed-tools | Bash, Read, Write, Edit |
| metadata | {"triggers":"google-adk, ADK, gemini agent, SequentialAgent, ParallelAgent, LoopAgent, FunctionTool, McpToolset, google genai, adk agent, vertex ai agent","related-skills":"python-dev, agentic-ai-dev, mcp-builder","domain":"backend","role":"specialist","scope":"implementation","output-format":"code"} |
| last-reviewed | 2026-03-15 |
Google ADK Development Skill — Python + Gemini + FastAPI
Iron Law
NEVER call the real Gemini API in unit tests. Always use InMemoryRunner for tests. Production agents use Runner with real session services. Mixing these means real API costs, flaky tests, and non-deterministic CI.
NEVER connect to external services from a FunctionTool. All external I/O (HTTP, DB, vector DB, cloud APIs) belongs in MCPTools accessed via McpToolset. FunctionTools contain ONLY pure logic or session state access.
ALWAYS use McpToolset for MCP connections — never instantiate McpToolset(connection_params=...) directly in agent code; use the factory provided by your project. Rules:
tool_filter is MANDATORY and non-empty in every McpToolset call — limits which MCP tools the LLM can call; empty list raises ValueError at startup
- NEVER bypass McpToolset with direct
httpx/requests calls — all external I/O routes through the MCP server via McpToolset
Quick Scaffold (Two Options)
Option A: agent-starter-pack (Recommended for production)
uvx agent-starter-pack create my-agent --agent adk --prototype --agent-guidance-filename CLAUDE.md -y
Generates project with Terraform, Dockerfile, CI/CD, eval harness. Use enhance to add deployment later.
Option B: Manual setup (Simpler, no deployment scaffold)
uv init my-adk-service && cd my-adk-service
uv add google-adk "google-genai>=1.0.0" "fastapi>=0.135.2" "uvicorn[standard]" pydantic pydantic-settings structlog
uv add --dev pytest pytest-asyncio httpx ruff mypy
Option C: Enhance existing project (add deployment to scaffolded project)
uvx agent-starter-pack enhance .
Choose deployment target when prompted: cloud_run or agent_engine.
Process
- Write DESIGN_SPEC.md — Before any code, write a spec covering: purpose, example use cases, required tools, safety constraints, success criteria, edge cases. Save as
DESIGN_SPEC.md in the project root. This is your contract — all implementation must align with it.
- Scaffold —
uv init + install google-adk + google-genai; confirm uv add "google-adk>=1.28.0" resolves without error
- Configure —
.env with GOOGLE_API_KEY / GOOGLE_CLOUD_PROJECT; load via pydantic-settings BaseSettings; never hardcode keys
- Define Agent —
Agent(name, model, instruction, tools) using model="gemini-3.1-flash"; write docstrings on every tool function
- Compose Agents — use
SequentialAgent, ParallelAgent, or LoopAgent for multi-step workflows; set output_key on each sub-agent that passes state downstream
- Define Tools — plain Python functions with type hints and docstrings; use
pydantic.BaseModel for complex inputs; accept tool_context: ToolContext to read/write session state
- Add Callbacks —
before_model_callback, after_model_callback, before_tool_callback, after_tool_callback, on_model_error_callback, on_tool_error_callback for rate limiting, logging, and structured error handling
- Session Management —
InMemorySessionService() for dev/test; VertexAiSessionService(project_id, location) for production; always call create_session before first runner.run
- Add Memory —
InMemoryMemoryService for dev; pass memory_service to Runner; add load_memory built-in tool to agents that need long-term recall
- Expose API — FastAPI routes using
runner.run_async() with StreamingResponse for SSE; one instance per app lifecycle
Key Patterns
| Pattern | Implementation | Reference |
|---|
| Single Agent | Agent(name, model, instruction, tools) | adk-core-patterns.md |
| Sequential Pipeline | SequentialAgent(sub_agents=[a, b, c]) + output_key per agent | adk-agent-types.md |
| Parallel Analysis | ParallelAgent(sub_agents=[...]) + output_key per agent | adk-agent-types.md |
| Iterative Refinement | LoopAgent(sub_agents=[...], max_iterations=N) + exit_loop | adk-agent-types.md |
| Agent Handoff | transfer_to_agent built-in + sub_agents=[...] on root | adk-agent-handoff.md |
| Custom Tools | Plain function with docstring + ToolContext for state | adk-tools-basic.md |
| MCP Integration | McpToolset(connection_params=StdioConnectionParams(...)) | adk-tools-basic.md |
| Structured Output | output_schema=PydanticModel + output_key="key" | adk-core-patterns.md |
| Callbacks | before_model_callback, after_model_callback, before_tool_callback | adk-tools-callbacks.md |
| Session State | tool_context.state["key"] read/write | adk-core-patterns.md |
| Memory | InMemoryMemoryService + load_memory tool | adk-memory-artifacts.md |
| Testing | InMemoryRunner + pytest-asyncio | adk-testing.md |
| FastAPI SSE | runner.run_async() + |
R1/R2 Tool Placement Rule
Before writing any tool, apply this binary test:
"Does this tool perform I/O outside the agent process (DB, HTTP, vector DB, cloud APIs)?"
| Answer | Tool type | Location |
|---|
| YES | R1 — MCPTool | MCP server — agent accesses via McpToolset |
| NO | R2 — FunctionTool | Agent tools directory — pure logic only |
ToolContext exception: If a tool needs BOTH external data AND ToolContext session state, it MUST be a FunctionTool — only in-process tools can access ToolContext. The FunctionTool reads data from session state previously populated by an MCPTool.
NEVER put import httpx, import sqlalchemy, or import requests in FunctionTool files.
Eval-First Development
Write golden test cases BEFORE writing agent code. This prevents writing code that passes no evaluation criteria.
Order of operations:
- Define
tests/golden/agents/<agent_name>/ directory
- Write at minimum: one happy-path case, one error-path case, one edge-case
- Each case: input → expected output with
confidence_min, contains_keywords, or pattern_* assertions
- Run eval skeleton to confirm test infrastructure works
- THEN implement the agent
- Iterate until all golden cases pass
Eval Skeleton (generate this first, before implementing the agent)
AGENT=my_agent
mkdir -p tests/golden/agents/$AGENT tests/evals
cat > tests/evals/$AGENT.evalset.json << 'JSON'
[
{
"name": "happy_path_1",
"input": { "query": "your test input here" },
"expected_tool_use": [{ "tool_name": "your_tool_name" }],
"expected_final_response": { "contains": "expected keyword" }
}
]
JSON
cat > tests/evals/eval_config.json << 'JSON'
{
"criteria": [
{ "type": "tool_trajectory_avg_score", "config": { "match_type": "IN_ORDER" } },
{ "type": "final_response_match_v2", "config": { "threshold": 0.8 } }
]
}
JSON
Code Preservation Rules
- NEVER change the model in existing code unless explicitly asked — changing
gemini-3.1-flash to another model is a breaking change
- NEVER rewrite working agent code — if the agent works, refactor incrementally
- NEVER remove tools from an agent without explicit approval — tools are part of the agent's contract
- NEVER rename output_key values — downstream agents reference them by name
Documentation Sources
| Source | URL / Tool | Purpose |
|---|
| Google ADK Python | https://context7.com/google/adk-python/llms.txt | Official ADK API reference |
| Google GenAI types | Context7 MCP — resolve google-genai | types.Part, types.Content, GenerateContentConfig |
| Pydantic v2 | Context7 MCP — resolve pydantic | BaseModel, Field, validators |
| ADK Docs MCP | adk-docs MCP server (installed) | Live ADK documentation |
Reference Files
| File | Contents |
|---|
reference/adk-core-patterns.md | Agent config, Runner patterns (sync/async), App class, session management |
reference/adk-structured-output.md | Session state access, structured output schemas, UserContent construction |
reference/adk-agent-types.md | SequentialAgent, ParallelAgent, LoopAgent, composition patterns |
reference/adk-agent-handoff.md | Agent handoff via transfer_to_agent, output_key state passing rules |
reference/adk-tools-basic.md | FunctionTool, ToolContext, async tools, Pydantic inputs, McpToolset (all 4 connection modes) |
reference/adk-tools-callbacks.md | Callbacks: before/after model, on_model_error, before/after tool, on_tool_error |
reference/adk-memory-artifacts.md | Memory services, load_memory tool, artifact storage, semantic search |
reference/adk-fastapi-integration.md | FastAPI + StreamingResponse SSE, lifespan runner setup, request/response models |
reference/adk-testing.md | InMemoryRunner unit tests, pytest-asyncio patterns, tool isolation, agent routing |
reference/adk-project-config.md | pyproject.toml, .env setup, directory structure, Dockerfile, logging, commands |
reference/adk-gemini-prompt-templates.md | Gemini-specific LlmAgent instruction templates — base structure, RAG with citations, constitutional AI (2-agent SequentialAgent), Tree-of-Thoughts, multi-step analysis, model selection guide (Flash vs Pro) |
Common Commands
adk web
uvicorn src.main:app --reload --port 8000
uv run pytest tests/ -v
uv run mypy src/
uv run ruff check src/ && uv run ruff format src/
uv sync
Error Handling
ADK-specific error handling rules:
- Provider errors — wrap
runner.run() / runner.run_async() in try/except; catch google.api_core.exceptions.GoogleAPICallError; log with full context (user_id, session_id, model); rethrow or return structured error response — never swallow
- Tool errors — use
on_tool_error_callback to intercept and log; return a descriptive error string from tools (ADK surfaces it to the model); never return empty string or None silently
- Loop limits —
LoopAgent stops at max_iterations; ensure exit_loop is called by the agent's instruction before the limit; log when loop exits by limit vs. by tool call
- Callback abort — returning a non-None value from
before_model_callback skips the model call; document this explicitly in the callback with a comment
- Session not found — always call
session_service.get_session() before runner.run(); if None, call create_session() first
All error paths must:
- Log with structured logger (structlog) including
user_id, session_id, agent_name
- Either rethrow or return an error state — no silent empty returns
- Surface the failure to the user (API error response, SSE error event)
Post-Code Review
After implementing any ADK agent feature:
- Dispatch
agentic-ai-reviewer agent — pass the agent graph structure and tool implementations
- Dispatch
security-reviewer — flag any tool that calls external APIs or handles user PII
- Confirm: no hardcoded API keys, all inputs validated, all error paths logged