Skip to main content

ADK Agent Structure Validation Hook

Validates ADK agent directory structure before running agents.

Trigger

  • Event: PreToolUse
  • Pattern: Commands containing adk run, adk eval, adk web

Validation Rules

Required Structure

agent_name/
├── __init__.py # Must contain: from . import agent
└── agent.py # Must define: root_agent OR app

Validation Checks

  1. Directory exists - Agent path is valid directory
  2. __init__.py exists - Required for Python module
  3. __init__.py imports agent - Contains from . import agent
  4. agent.py exists - Contains agent definition
  5. Defines root_agent or app - Entry point for ADK

Implementation

#!/usr/bin/env python3
"""ADK Agent Structure Validation Hook"""

import json
import os
import re
import sys


def validate_adk_agent(agent_path: str) -> list[str]:
"""Validate ADK agent directory structure."""
errors = []

# Check directory exists
if not os.path.isdir(agent_path):
errors.append(f"Agent path is not a directory: {agent_path}")
return errors

# Check __init__.py
init_path = os.path.join(agent_path, "__init__.py")
if not os.path.exists(init_path):
errors.append(f"Missing __init__.py in {agent_path}")
else:
with open(init_path, "r") as f:
init_content = f.read()
if "from . import agent" not in init_content and "from .agent import" not in init_content:
errors.append("__init__.py must contain 'from . import agent'")

# Check agent.py
agent_file = os.path.join(agent_path, "agent.py")
if not os.path.exists(agent_file):
errors.append(f"Missing agent.py in {agent_path}")
else:
with open(agent_file, "r") as f:
agent_content = f.read()

# Check for root_agent or app definition
has_root_agent = re.search(r"^root_agent\s*=", agent_content, re.MULTILINE)
has_app = re.search(r"^app\s*=", agent_content, re.MULTILINE)

if not has_root_agent and not has_app:
errors.append("agent.py must define 'root_agent' or 'app'")

return errors


def extract_agent_path(command: str) -> str | None:
"""Extract agent path from ADK command."""
# Patterns: adk run path/to/agent, adk web path/, adk eval path/agent
patterns = [
r"adk\s+run\s+([^\s]+)",
r"adk\s+web\s+([^\s]+)",
r"adk\s+eval\s+([^\s]+)",
]

for pattern in patterns:
match = re.search(pattern, command)
if match:
return match.group(1)

return None


def main():
"""Hook entry point."""
input_data = json.load(sys.stdin)

tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})

# Only validate Bash commands with adk
if tool_name != "Bash":
print(json.dumps({"decision": "allow"}))
return

command = tool_input.get("command", "")

# Check if it's an ADK command
if not re.search(r"adk\s+(run|web|eval)", command):
print(json.dumps({"decision": "allow"}))
return

# Extract agent path
agent_path = extract_agent_path(command)

if not agent_path:
print(json.dumps({"decision": "allow"}))
return

# Expand home directory
agent_path = os.path.expanduser(agent_path)

# For adk web, path might be a parent directory containing multiple agents
if "adk web" in command or "adk api_server" in command:
# Just check directory exists for web/api
if not os.path.isdir(agent_path):
print(json.dumps({
"decision": "block",
"reason": f"Agents directory not found: {agent_path}"
}))
else:
print(json.dumps({"decision": "allow"}))
return

# Validate agent structure
errors = validate_adk_agent(agent_path)

if errors:
print(json.dumps({
"decision": "block",
"reason": f"ADK agent validation failed:\n" + "\n".join(f" - {e}" for e in errors)
}))
else:
print(json.dumps({"decision": "allow"}))


if __name__ == "__main__":
main()

Configuration

Add to ~/.claude/settings.json:

{
"hooks": {
"PreToolUse": [
{
"pattern": "adk (run|web|eval)",
"command": "python3 ~/.coditect/hooks/adk-agent-structure-validation.py"
}
]
}
}

Common Errors

ErrorCauseFix
Not a directoryInvalid pathCheck agent path
Missing __init__.pyIncomplete setupCreate file with import
Missing importWrong init contentAdd from . import agent
Missing agent.pyNo agent definitionCreate agent.py
No root_agent/appMissing entry pointDefine root_agent = Agent(...)

Quick Fix Template

# __init__.py
from . import agent

# agent.py
from google.adk.agents import Agent

root_agent = Agent(
name="my_agent",
model="gemini-2.5-flash",
instruction="You are a helpful assistant.",
)

Version: 1.0.0 Created: 2026-01-13