When we talk about agents, that doesn’t necessarily mean we’re talking about Python. In today’s modern ecosystem, an agent might run as a high-concurrency Go microservice on Cloud Run, or live inside a browser tab calling Chrome’s on-device model. Yet many agent frameworks assume a Python-only stack. That assumption forces teams to either spin up a Python service just for their agent workloads, or to build agent orchestration logic from scratch in their own language.
Google’s Agent Development Kit (ADK) removes that trade-off with coverage for Python, Go, TypeScript, Java, and Kotlin. Each version of the ADK is built from the ground up for its own runtime and type system, while sharing a unified architecture with interoperability through open protocols.
A shared foundation
Picking a language doesn’t lock in your architecture: all five support the same open protocols. ADK includes a Model Context Protocol client in every language, so a custom tool exposed as an MCP server can be called from any of them immediately.
For multi-agent systems, ADK supports the Agent2Agent (A2A) protocol, allowing an agent to communicate with a remote agent over HTTP. Through A2A, agents written in different languages or frameworks are interoperable.
Starting agent-first
To get started, run the one-time setup command to check your Google Cloud authentication, install the CLI, and then set up first-party ADK skills in coding agents like Antigravity, Claude Code, or Codex:
- code_block
- <ListValue: [StructValue([('code', 'uvx google-agents-cli setup'), ('language', ''), ('caption', )])]>
Then, run the scaffold command to generate a working project with A2A endpoints and deployment config for Cloud Run, Gemini Enterprise Agent Runtime, or Google Kubernetes Engine (GKE):
- code_block
- <ListValue: [StructValue([('code', '# Scaffold an ADK project (use –agent adk for Python or –agent adk_go for Go)rnagents-cli scaffold create my-go-agent \rn –agent adk_go \rn –deployment-target cloud_run \rn –region us-central1'), ('language', ''), ('caption', )])]>
Both the ADK for Python and ADK for Kotlin repositories also ship agent builder skills in their source trees, and the adk.dev/llms.txt resource gives any coding agent on-demand API documentation across all five languages.
Coding-agent skills scaffold your project while MCP and A2A connect all five language runtimes.
Hands-on view of each ADK
The clearest way to feel the difference between the five languages is to define the same weather tool in each one and see how its design fits the runtime.
Python
In ADK for Python, you don’t need a decorator or a schema builder class. You pass a plain Python function into the tools parameter, and ADK inspects its signature, type hints, and docstring at startup to automatically construct the tool declaration and Pydantic parameter schema:
- code_block
- dict:rn “””Retrieves the current weather report for a specified city.rnrn Args:rn city (str): The name of the city for which to retrieve the weather report.rn “””rn return {“status”: “success”, “report”: f”Sunny and 72F in {city}.”}rnrnrnroot_agent = Agent(rn name=”weather_agent”,rn model=”gemini-3.8-flash”,rn instruction=”Answer questions about the weather in a city.”,rn tools=[get_weather],rn)’), (‘language’, ‘lang-py’), (‘caption’, )])]>
Beyond fast prototyping, Python is ADK’s reference implementation, shipping the broadest set of Google Cloud integrations (BigQuery, Spanner, Bigtable) and model adapters (Gemini, Anthropic, OpenAI, LiteLLM). Paired with the adk eval or agents-cli eval run commands, its evaluation tooling scores agent trajectories against golden datasets in continuous integration (CI) to test the whole system end to end, even when your production service runs in another language.
Go
While Python leads in experimentation and evaluation, ADK for Go hits the sweet spot for production backend engineering.
A Go tool function takes the agent.Context configuration and a strongly typed input struct. The functiontool.New function uses Go generics to infer both the input and output schemas from your struct tags:
- code_block
- <ListValue: [StructValue([('code', 'type GetWeatherArgs struct {rntCity string `json:"city" jsonschema:"The city to look up weather for."`rn}rnrntype GetWeatherResult struct {rntReport string `json:"report"`rn}rnrnfunc getWeather(_ agent.Context, args GetWeatherArgs) (GetWeatherResult, error) {rntreturn GetWeatherResult{Report: "Sunny and 72F in " + args.City}, nilrn}rnrnweatherTool, err := functiontool.New(functiontool.Config{rntName: "get_weather",rntDescription: "Retrieves the current weather report for a city.",rn}, getWeather)'), ('language', ''), ('caption', )])]>
Because the functiontool.New function is generic over your handler’s signature, the Go compiler verifies that your function matches the expected contract before your binary ever runs.
Go was also the first language after Python to ship the ADK 2.0 Graph Workflow runtime. When you need deterministic control flow alongside LLM reasoning, the workflow.New function wires agents and plain Go functions into a directed graph with conditional routing and parallel steps:
- code_block
- <ListValue: [StructValue([('code', 'ag, err := workflowagent.New("order_pipeline", []workflow.Edge{rnt{From: workflow.Start, To: triageNode},rnt{From: triageNode, To: fulfillNode, Route: workflow.StringRoute("fulfill")},rnt{From: fulfillNode, To: notifyNode},rn})'), ('language', ''), ('caption', )])]>
Where Go really stands out is in production on Cloud Run and GKE. A compiled Go agent packages onto a roughly 2 MiB Google distroless static base image, with no language VM or heavy dependency tree. A static Go binary binds to its port as soon as the container starts, skipping the module imports of dynamic languages and the JAR unpacking and JIT warmup of JVM services.
After it’s running, Go’s concurrency model fits agent workloads naturally. Each goroutine starts with a 2 KB stack and minimal scheduling overhead, so a single instance can multiplex thousands of concurrent streaming requests and tool calls. ADK for Go also includes built-in A2A server handlers and OpenTelemetry tracing out of the box.
TypeScript
TypeScript erases type annotations when compiling to JavaScript, so runtime reflection isn’t an option. Instead, ADK for TypeScript pairs with Zod. You pass a z.object(...) schema to the FunctionTool function, and TypeScript statically infers the parameter types of your execute callback from that single declaration:
- code_block
- {rn return { status: “success”, report: `Sunny and 72F in ${city}.` };rn },rn});rnrnexport const rootAgent = new LlmAgent({rn name: “weather_agent”,rn model: “gemini-3.8-flash”,rn instruction: “Answer questions about the weather in a city.”,rn tools: [getWeatherTool],rn});’), (‘language’, ”), (‘caption’, )])]>
Your runtime input validation, the JSON Schema sent to the model, and your IDE autocomplete inside the execute callback all flow from one source of truth.
TypeScript also runs on the ADK 2.0 Graph Workflow engine and ships a standalone browser bundle. Beyond Node.js services, you can run an ADK agent directly in a browser tab using WebMCP for page-level tools and Chrome’s on-device Prompt API.
Java
ADK for Java snaps right in for teams who have standardized on Maven, Gradle, and Spring Boot. You add @Schema annotations to method parameters and assemble agents by using a fluent LlmAgent.builder() API:
- code_block
- <ListValue: [StructValue([('code', 'public class WeatherTools {rn public static Map getWeather(rn @Schema(name = “city”, description = “The name of the city”)rn String city) {rn return Map.of(“status”, “success”, “report”, “Sunny and 72F in ” + city);rn }rn}rnrnLlmAgent agent = LlmAgent.builder()rn .name(“weather_agent”)rn .model(“gemini-3.8-flash”)rn .instruction(“Answer questions about the weather in a city.”)rn .tools(FunctionTool.create(WeatherTools.class, “getWeather”))rn .build();’), (‘language’, ”), (‘caption’, )])]>
At runtime, the runner streams agent events reactively through RxJava. For local testing and existing JVM apps, ADK for Java ships a Maven Dev UI plugin alongside first-party bridges for Spring AI and LangChain4j.
Kotlin
ADK for Kotlin takes a strong stance on build-time safety. Its Kotlin Symbol Processing (KSP) compiler plugin reads @Tool and @Param annotations at build time and emits the tool schema directly into bytecode:
- code_block
- <ListValue: [StructValue([('code', 'class WeatherTools {rn @Tool(rn name = "get_weather",rn description = "Retrieves the current weather report for a city.",rn )rn suspend fun getWeather(rn @Param("The name of the city.") city: Stringrn ): Map = mapOf(“status” to “success”, “report” to “Sunny and 72F in $city”)rn}rnrnval agent = LlmAgent(rn model = Gemini(“gemini-3.8-flash”),rn name = “weather_agent”,rn instruction = Instruction(“Answer questions about the weather in a city.”),rn tools = WeatherTools().generatedTools(),rn)’), (‘language’, ”), (‘caption’, )])]>
If a tool signature uses an unsupported type or declares a default value on a non-nullable parameter, your Gradle build fails immediately instead of breaking in production. Because the .generatedTools() function uses zero runtime reflection, it survives Android R8 shrinking.
That zero-reflection design makes it possible to run the entire agent loop locally on Android. It offers JVM server support, including a Spring AI bridge and experimental ADK 2.0 graph workflows. On-device, ADK for Kotlin integrates with LiteRT-LM for tool calling and ML Kit (Gemini Nano) for text generation. It also works with Room and AppSearch for local state, and Android AppFunctions for OS-level actions, all without requiring a network connection.
Choice without compromise
Having five native languages in ADK means you don’t have to pick a single runtime for your entire organization. Because every version of ADK speaks MCP and A2A, each component of your system can use the language that fits it best.
What languages have you used to build agents with Google’s ADK? Let me know what stack you’re running on X, LinkedIn, or Bluesky.