RRTutors
Advanced Features & State Control Module 3: Topic 5

Custom Tools in Google ADK: Master the @Tool and @Param Architecture

By RR Tutors Editorial Updated July 2026

AI agents achieve operational utility only when they interact with the physical device state. Google ADK bridges the gap between language model logic and native system APIs using a compile-time schema pipeline driven by annotations.

Under the Hood: How KSP Parses Code to Schema

When you decorate a class with tool annotations, the Kotlin Symbol Processing (KSP) compiler steps in during the compilation phase. It extracts the structural types of your parameters, honors the descriptions inside your @Param statements, and writes a pristine Open-API compliant JSON schema. The LLM processes this schema to handle complex function calling tasks safely.

Step-by-Step Implementation: Binding Hardware Sensors to an LLM

Let's map native Android APIs—specifically checking system battery metrics and hardware connectivity states—into an interface digestible by an ADK agent.

1. Build the Native Hardware Tool Class

Create a standard service class that uses the Android BatteryManager and exposes its state using clean annotations.

package com.example.adkdemoapp.tools

import android.content.Context
import android.os.BatteryManager
import com.google.adk.kt.annotations.Tool
import com.google.adk.kt.annotations.Param

class DeviceHardwareTool(private val context: Context) {

    @Tool(
        name = "get_device_power_status",
        description = "Retrieves the current battery level percentage and checks if the device is plugged into a power source."
    )
    fun getDevicePowerStatus(): String {
        val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
        val batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
        val isCharging = batteryManager.isCharging
        
        return "Battery Capacity: $batteryLevel%, Device Charging Status: $isCharging"
    }
}

2. Bind the Tool Instance into the Agent Layer

To expose this capability, instantiate your class and register it directly into the agent definition using the generated companion extension helpers:

package com.example.adkdemoapp.agents

import android.content.Context
import com.example.adkdemoapp.BuildConfig
import com.example.adkdemoapp.tools.DeviceHardwareTool
import com.google.adk.kt.agents.Instruction
import com.google.adk.kt.agents.LlmAgent
import com.google.adk.kt.models.Gemini

class AgentConfigurationManager(private val context: Context) {

    fun getHardwareOrchestratorAgent(): LlmAgent {
        // Instantiating our tool class with the appropriate application context
        val hardwareToolInstance = DeviceHardwareTool(context)

        return LlmAgent(
            name = "hardware_diagnostics_agent",
            description = "An agent permitted to analyze the physical device health and status profiles.",
            model = Gemini(name = "gemini-2.5-flash", apiKey = BuildConfig.GEMINI_API_KEY),
            instruction = Instruction(
                """
                You are a system diagnostic tool. Use the 'get_device_power_status' tool to check 
                hardware capabilities before offering user support instructions.
                """.trimIndent()
            ),
            // Bind the tool seamlessly to the agent's runtime capabilities
            tools = listOf(hardwareToolInstance.generatedTools())
        )
    }
}