diff --git a/COMPONENTS.md b/COMPONENTS.md
index 095b3b4af3..179651550b 100644
--- a/COMPONENTS.md
+++ b/COMPONENTS.md
@@ -153,7 +153,7 @@ For example, the Python SDK integrations page pre-filters to Python:
```
-Valid SDK values are `"Java"`, `"Python"`, `"TypeScript"`, and `"Ruby"`.
+Valid SDK values are `"Go"`, `"Java"`, `"Python"`, `"TypeScript"`, and `"Ruby"`.
### Filter behavior
diff --git a/docs/develop/go/index.mdx b/docs/develop/go/index.mdx
index 62c05b03d0..d4070f9257 100644
--- a/docs/develop/go/index.mdx
+++ b/docs/develop/go/index.mdx
@@ -93,7 +93,11 @@ From there, you can dive deeper into any of the Temporal primitives to start bui
- [Testing](/develop/go/best-practices/testing-suite)
- [Data handling](/develop/go/data-handling)
-## Temporal Go Technical Resources
+## [Integrations](/develop/go/integrations)
+
+- [Google ADK integration](/develop/go/integrations/google-adk)
+
+## Temporal Go technical resources
- [Go SDK Quickstart - Setup Guide](/develop/go/set-up-your-local-go)
- [Go API Documentation](https://pkg.go.dev/go.temporal.io/sdk)
diff --git a/docs/develop/go/integrations/google-adk.mdx b/docs/develop/go/integrations/google-adk.mdx
new file mode 100644
index 0000000000..cee828b798
--- /dev/null
+++ b/docs/develop/go/integrations/google-adk.mdx
@@ -0,0 +1,612 @@
+---
+id: google-adk
+title: Google ADK integration
+sidebar_label: Google ADK
+toc_max_heading_level: 2
+keywords:
+ - ai
+ - agents
+ - google
+ - adk
+ - durable execution
+ - ai workflows
+tags:
+ - Google ADK
+ - Go SDK
+ - Temporal SDKs
+description: Run Google ADK agents with durable execution using the Temporal Go SDK and the googleadk contrib integration.
+---
+
+Temporal's integration with [Google ADK](https://google.github.io/adk-docs/) (`adk-go`) gives your agents
+[Durable Execution](/temporal#durable-execution): the agent's orchestration loop runs inside a Temporal Workflow, each
+LLM call becomes a durable Temporal Activity, and any tool that does I/O runs as an Activity too — so every step is
+retried, timed out, recorded in Workflow history, and replayable after crashes or restarts.
+
+You keep building agents the native ADK way — `llmagent.New(...)` with a `model.LLM`, `tool.Tool`s / `tool.Toolset`s and
+`SubAgents`, wrapped in `runner.New(...)` and driven by `r.Run(...)`. You change two things:
+
+1. Use `googleadk.NewModel("")` as your agent's `Model`. It is a `model.LLM` whose calls dispatch to the
+ `InvokeModel` Activity; the real model is reconstructed **worker-side**, never in the Workflow.
+2. Pass `googleadk.NewContext(workflowCtx)` to `r.Run`, which installs Temporal-deterministic time, UUID, and
+ task-fan-out providers so the agent loop replays deterministically.
+
+Tools run **in-workflow by default** (the idiomatic Temporal model: the Workflow is deterministic, and anything touching
+the network, clock, or disk goes through an Activity). Opt a tool into an Activity with `googleadk.ActivityAsTool`, or
+use `googleadk.NewMCPToolset` for MCP.
+
+:::info
+
+The `googleadk` contrib module is new. It depends on determinism seams (`platform.WithTimeProvider`,
+`WithUUIDProvider`, `WithTaskRunner`) that merged into `google.golang.org/adk/v2` after its latest tagged release, so
+`go.mod` pins `adk/v2` to a `main`-branch pseudo-version until a release ships that includes them.
+
+:::
+
+Code snippets in this guide are taken from the
+[Google ADK plugin samples](https://github.com/temporalio/samples-go/tree/main/googleadk). Refer to the samples for the
+complete, runnable code.
+
+## Prerequisites
+
+- This guide assumes you are already familiar with Google ADK. If you aren't, refer to the
+ [Google ADK documentation](https://google.github.io/adk-docs/) for more details.
+- If you are new to Temporal, read [Understanding Temporal](/evaluate/understanding-temporal) or take the
+ [Temporal 101](https://learn.temporal.io/courses/temporal_101/) course.
+- Set up your local development environment by following the
+ [Set up your local development environment](/develop/go/set-up-your-local-go) guide. Leave the Temporal development
+ server running if you want to test your code locally.
+- Provide model credentials **worker-side** — for Gemini, set `GEMINI_API_KEY` (or `GOOGLE_API_KEY`) in the worker's
+ environment. Credentials are captured in the worker's `ModelFactory` and never cross the Activity boundary into the
+ Workflow.
+
+## Install the plugin
+
+Install the `googleadk` contrib module:
+
+```bash
+go get go.temporal.io/sdk/contrib/googleadk@latest
+```
+
+```go
+import "go.temporal.io/sdk/contrib/googleadk"
+```
+
+## Run an agent with Durable Execution
+
+An agent has two halves: the **worker** registers the real model behind the `InvokeModel` Activity, and the **workflow**
+builds a vanilla ADK agent and drives it.
+
+### Configure the Worker
+
+Build the worker-side registry with `googleadk.NewActivities` and register it. The real Gemini model lives here, behind
+the Activity boundary; the API key is read worker-side. Disable the model SDK's own retries so Temporal's `RetryPolicy`
+is the single source of truth.
+
+
+[googleadk/worker/main.go](https://github.com/temporalio/samples-go/blob/main/googleadk/worker/main.go)
+```go
+w := worker.New(c, adk.TaskQueue, worker.Options{})
+
+w.RegisterWorkflow(adk.AgentWorkflow)
+// Register GetWeather under the tool name the ActivityAsTool dispatches, so the
+// agent's get_weather call resolves to this activity.
+w.RegisterActivityWithOptions(adk.GetWeather, activity.RegisterOptions{Name: adk.WeatherToolName})
+
+// Register the integration's model Activity. The real Gemini model lives here,
+// behind the Activity boundary; the API key is read worker-side from the env
+// and never crosses into the workflow. Disable the model SDK's own retries so
+// Temporal's RetryPolicy is the single source of truth.
+acts, err := googleadk.NewActivities(googleadk.Config{
+ Models: map[string]googleadk.ModelFactory{
+ adk.ModelName: func(ctx context.Context, name string) (model.LLM, error) {
+ // nil config reads GEMINI_API_KEY / GOOGLE_API_KEY from the env.
+ return gemini.NewModel(ctx, name, nil)
+ },
+ },
+})
+if err != nil {
+ log.Fatalln("Unable to build googleadk activities", err)
+}
+acts.Register(w)
+
+if err := w.Run(worker.InterruptCh()); err != nil {
+ log.Fatalln("Unable to start worker", err)
+}
+```
+
+
+`Config.Models` is optional for providers ADK's registry already knows (for example `gemini-*`): when a model name is
+absent, `InvokeModel` falls back to `model.NewLLM`. Supply a factory to inject credentials, disable the model SDK's own
+retries, or override the default.
+
+### Define the Workflow
+
+Build the agent the ordinary ADK way, using `googleadk.NewModel` for the model and passing `googleadk.NewContext(ctx)`
+to `r.Run`. The `get_weather` tool is an ordinary Temporal Activity exposed to the agent with `googleadk.ActivityAsTool`.
+
+
+[googleadk/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/workflow.go)
+```go
+func AgentWorkflow(ctx workflow.Context, question string) (string, error) {
+ weatherTool, err := googleadk.ActivityAsTool(GetWeather, googleadk.ActivityToolOptions{
+ Name: WeatherToolName,
+ Description: "Get the current weather for a city.",
+ })
+ if err != nil {
+ return "", err
+ }
+
+ // Build the agent the ordinary ADK way. NewModel is a model.LLM that carries
+ // only the model name in-workflow; the real Gemini client lives worker-side.
+ root, err := llmagent.New(llmagent.Config{
+ Name: "assistant",
+ Description: "a helpful weather assistant",
+ Model: googleadk.NewModel(ModelName),
+ Instruction: "Answer the user's question. Use the get_weather tool when asked about the weather.",
+ Tools: []tool.Tool{weatherTool},
+ })
+ if err != nil {
+ return "", err
+ }
+
+ r, err := runner.New(runner.Config{
+ AppName: "weather",
+ Agent: root,
+ SessionService: session.InMemoryService(),
+ AutoCreateSession: true,
+ })
+ if err != nil {
+ return "", err
+ }
+
+ // NewContext bridges the workflow.Context into the context ADK reads its
+ // determinism/executor seams from. Pass it straight to Run.
+ adkCtx := googleadk.NewContext(ctx)
+ msg := genai.NewContentFromText(question, genai.RoleUser)
+
+ var answer string
+ for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) {
+ if err != nil {
+ return "", err
+ }
+ if ev != nil && ev.Content != nil {
+ for _, p := range ev.Content.Parts {
+ if p != nil && p.Text != "" {
+ answer = p.Text
+ }
+ }
+ }
+ }
+ return answer, nil
+}
+
+```
+
+
+The tool itself is an ordinary Temporal Activity — register it on the worker as usual, and expose it to the agent with
+`ActivityAsTool` (its parameter schema is inferred from the argument type):
+
+
+[googleadk/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/workflow.go)
+```go
+func GetWeather(ctx context.Context, in GetWeatherInput) (GetWeatherOutput, error) {
+ return GetWeatherOutput{City: in.City, Conditions: "sunny, 72°F"}, nil
+}
+
+```
+
+
+### Start the Workflow
+
+Start the Workflow like any other and read its result:
+
+
+[googleadk/starter/main.go](https://github.com/temporalio/samples-go/blob/main/googleadk/starter/main.go)
+```go
+question := "What's the weather in San Francisco?"
+we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, adk.AgentWorkflow, question)
+if err != nil {
+ log.Fatalln("Unable to execute workflow", err)
+}
+
+log.Println("Started workflow", "WorkflowID", we.GetID(), "RunID", we.GetRunID())
+
+// Synchronously wait for the workflow completion.
+var answer string
+if err := we.Get(context.Background(), &answer); err != nil {
+ log.Fatalln("Unable to get workflow result", err)
+}
+log.Println("Agent answer:", answer)
+```
+
+
+## Tools
+
+- **Function tools run in-workflow by default.** Ordinary `functiontool.New(...)` tools run on Temporal's deterministic
+ dispatcher inside the Workflow — no Activity overhead — and their session-state mutations propagate normally. Their
+ code must be deterministic and replay-safe: no direct network, clock, randomness, or goroutines.
+- **Opt a tool into an Activity when it does I/O.** `googleadk.ActivityAsTool(myActivity, ...)` exposes an existing
+ `func(context.Context, TArgs) (TResults, error)` Temporal Activity to the agent as a tool (shown in the Hello World
+ Workflow above); its call dispatches the Activity, so it is retried, timed out, and visible in the UI.
+- **MCP, statelessly.** `googleadk.NewMCPToolset(...)` is a workflow-side proxy that lists remote tools via the
+ `ListMcpTools` Activity and executes calls via `CallMcpTool`. The live, stateful `mcptoolset.New(...)` runs worker-side
+ (registered in `Config.MCPToolsets`), never in the Workflow.
+
+## Multi-agent systems
+
+Build a coordinator agent with specialist `SubAgents`; ADK wires the parent/child relationship and exposes the built-in
+`transfer_to_agent` tool automatically. The entire tree — including the transfer hop — runs in the Workflow; only the
+model calls and any Activity-backed tools leave it.
+
+
+[googleadk/multiagent/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/multiagent/workflow.go)
+```go
+func MultiAgentWorkflow(ctx workflow.Context, question string) (string, error) {
+ weatherTool, err := googleadk.ActivityAsTool(GetWeather, googleadk.ActivityToolOptions{
+ Name: WeatherToolName,
+ Description: "Get the current weather for a city.",
+ })
+ if err != nil {
+ return "", err
+ }
+
+ // The weather specialist owns the get_weather tool.
+ weather, err := llmagent.New(llmagent.Config{
+ Name: "weather",
+ Description: "answers questions about the current weather in a city",
+ Model: googleadk.NewModel(WeatherModelName),
+ Instruction: "You are a weather specialist. Use the get_weather tool to answer weather questions.",
+ Tools: []tool.Tool{weatherTool},
+ })
+ if err != nil {
+ return "", err
+ }
+
+ // The jokes specialist just tells jokes.
+ jokes, err := llmagent.New(llmagent.Config{
+ Name: "jokes",
+ Description: "tells a light-hearted joke",
+ Model: googleadk.NewModel(JokesModelName),
+ Instruction: "You are a comedian. Respond with a short, friendly joke.",
+ })
+ if err != nil {
+ return "", err
+ }
+
+ // The coordinator delegates to whichever specialist fits the question. ADK
+ // wires the parent/child relationship from SubAgents and exposes the built-in
+ // transfer_to_agent tool automatically.
+ coordinator, err := llmagent.New(llmagent.Config{
+ Name: "coordinator",
+ Description: "routes the user's request to the right specialist",
+ Model: googleadk.NewModel(CoordinatorModelName),
+ Instruction: "You are a router. Delegate weather questions to the weather agent " +
+ "and requests for a joke to the jokes agent. Do not answer directly.",
+ SubAgents: []agent.Agent{weather, jokes},
+ })
+ if err != nil {
+ return "", err
+ }
+
+ r, err := runner.New(runner.Config{
+ AppName: "multiagent",
+ Agent: coordinator,
+ SessionService: session.InMemoryService(),
+ AutoCreateSession: true,
+ })
+ if err != nil {
+ return "", err
+ }
+
+ adkCtx := googleadk.NewContext(ctx)
+ msg := genai.NewContentFromText(question, genai.RoleUser)
+
+ var answer string
+ for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) {
+ if err != nil {
+ return "", err
+ }
+ if ev == nil || ev.Content == nil {
+ continue
+ }
+ // Keep the last non-empty text produced by any agent in the tree; after a
+ // transfer_to_agent hop this is the specialist's answer.
+ for _, p := range ev.Content.Parts {
+ if p != nil && p.Text != "" {
+ answer = p.Text
+ }
+ }
+ }
+ return answer, nil
+}
+
+```
+
+
+## Human-in-the-loop tool confirmation
+
+A sensitive tool calls ADK's `ctx.RequestConfirmation(hint, payload)`, which ends the turn with an
+`adk_request_confirmation` function call. The Workflow detects pending confirmations with
+`googleadk.PendingConfirmations`, durably waits for the human's decision (delivered as a Temporal signal), and resumes
+the agent with `googleadk.ConfirmationResponse`. Because the wait is durable, the Workflow can sit idle for days and
+survive worker restarts — when the approval signal arrives, the agent resumes exactly where it paused.
+
+
+[googleadk/humanintheloop/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/humanintheloop/workflow.go)
+```go
+func ApprovalWorkflow(ctx workflow.Context, request string) (Result, error) {
+ delTool, err := functiontool.New[DeleteArgs, map[string]any](
+ functiontool.Config{
+ Name: DeleteToolName,
+ Description: "Delete a named resource. Requires human confirmation before it runs.",
+ },
+ deleteResource,
+ )
+ if err != nil {
+ return Result{}, err
+ }
+
+ root, err := llmagent.New(llmagent.Config{
+ Name: "assistant",
+ Description: "an assistant that can delete resources with human approval",
+ Model: googleadk.NewModel(ModelName),
+ Instruction: "Use the delete_resource tool when the user asks to delete something.",
+ Tools: []tool.Tool{delTool},
+ })
+ if err != nil {
+ return Result{}, err
+ }
+
+ r, err := runner.New(runner.Config{
+ AppName: "hitl",
+ Agent: root,
+ SessionService: session.InMemoryService(),
+ AutoCreateSession: true,
+ })
+ if err != nil {
+ return Result{}, err
+ }
+
+ adkCtx := googleadk.NewContext(ctx)
+ msg := genai.NewContentFromText(request, genai.RoleUser)
+
+ var res Result
+ // Drive the run in passes: each Run call is one pass over the same session. A
+ // pass either completes (no pending confirmation) or pauses awaiting a human.
+ for {
+ var events []*session.Event
+ for ev, err := range r.Run(adkCtx, "user-1", "session-1", msg, agent.RunConfig{}) {
+ if err != nil {
+ return Result{}, err
+ }
+ if ev == nil {
+ continue
+ }
+ events = append(events, ev)
+ if ev.Content != nil {
+ for _, p := range ev.Content.Parts {
+ if p != nil && p.Text != "" {
+ res.Answer = p.Text
+ }
+ }
+ }
+ }
+
+ pending := googleadk.PendingConfirmations(events)
+ if len(pending) == 0 {
+ // The agent finished without (further) confirmations needed.
+ return res, nil
+ }
+
+ // The agent paused. Durably wait for the human's decision to arrive as a
+ // Temporal signal. This is the whole point: the workflow can sit here for
+ // as long as it takes — across worker restarts — without losing state.
+ //
+ // This handles one pending confirmation per pass — the recommended
+ // pattern (see the googleadk.ConfirmationResponse docs): resuming
+ // several decisions at once can re-dispatch the approved tool calls in
+ // an order that is not replay-stable. Any other pending confirmations
+ // simply surface again on the next pass.
+ var decision googleadk.ConfirmationDecision
+ workflow.GetSignalChannel(ctx, googleadk.ConfirmationSignalName).Receive(ctx, &decision)
+ res.Approved = decision.Confirmed
+
+ // Match the decision to the pending confirmation and resume the run with
+ // it as the next message. ADK re-dispatches (or blocks) the original tool
+ // call based on Confirmed.
+ if decision.FunctionCallID == "" {
+ decision.FunctionCallID = pending[0].FunctionCallID
+ }
+ msg = googleadk.ConfirmationResponse(decision)
+ }
+}
+
+```
+
+
+## Continue-as-new for long conversations
+
+A conversation's history lives in the ADK session. To keep a Workflow's history bounded, snapshot the session with
+`googleadk.ExportSession` and [continue-as-new](/develop/go/workflows/continue-as-new); rebuild it on the next run with
+`googleadk.ImportSession`. `SessionSnapshot` is JSON-serializable (session-scoped state plus the full event history), so
+every value in session state and every tool result must be JSON-encodable.
+
+
+[googleadk/chat/workflow.go](https://github.com/temporalio/samples-go/blob/main/googleadk/chat/workflow.go)
+```go
+func ChatWorkflow(ctx workflow.Context, in ChatInput) error {
+ // A fresh in-memory session service, kept in a local so we can Export it later.
+ svc := session.InMemoryService()
+
+ adkCtx := googleadk.NewContext(ctx)
+
+ // Resume a prior conversation if this run was continued-as-new.
+ if in.Snapshot != nil {
+ if _, err := googleadk.ImportSession(adkCtx, svc, in.Snapshot); err != nil {
+ return err
+ }
+ }
+
+ root, err := llmagent.New(llmagent.Config{
+ Name: "assistant",
+ Description: "a friendly conversational assistant",
+ Model: googleadk.NewModel(ModelName),
+ Instruction: "You are a helpful assistant. Answer the user, using the conversation history for context.",
+ })
+ if err != nil {
+ return err
+ }
+
+ r, err := runner.New(runner.Config{
+ AppName: AppName,
+ Agent: root,
+ SessionService: svc,
+ AutoCreateSession: true,
+ })
+ if err != nil {
+ return err
+ }
+
+ turns := 0
+ // One agent turn runs at a time: serialize concurrent Updates so they can't
+ // interleave on the shared ADK session.
+ busy := false
+
+ err = workflow.SetUpdateHandlerWithOptions(
+ ctx,
+ SendMessageUpdateName,
+ func(ctx workflow.Context, text string) (string, error) {
+ if err := workflow.Await(ctx, func() bool { return !busy }); err != nil {
+ return "", err
+ }
+ busy = true
+ defer func() { busy = false }()
+
+ // Build the ADK context from this Update handler's own workflow.Context so
+ // the model Activity is scheduled on the handler's coroutine.
+ turnCtx := googleadk.NewContext(ctx)
+ var answer string
+ msg := genai.NewContentFromText(text, genai.RoleUser)
+ for ev, err := range r.Run(turnCtx, UserID, SessionID, msg, agent.RunConfig{}) {
+ if err != nil {
+ return "", err
+ }
+ if ev == nil || ev.Content == nil {
+ continue
+ }
+ for _, p := range ev.Content.Parts {
+ if p != nil && p.Text != "" {
+ answer = p.Text
+ }
+ }
+ }
+ turns++
+ return answer, nil
+ },
+ workflow.UpdateHandlerOptions{
+ Validator: func(ctx workflow.Context, text string) error {
+ if text == "" {
+ return fmt.Errorf("message must not be empty")
+ }
+ return nil
+ },
+ },
+ )
+ if err != nil {
+ return err
+ }
+
+ // Serve messages until Temporal suggests continue-as-new (history getting large)
+ // or the demo turn cap is reached.
+ if err := workflow.Await(ctx, func() bool {
+ return workflow.GetInfo(ctx).GetContinueAsNewSuggested() || (in.MaxTurns > 0 && turns >= in.MaxTurns)
+ }); err != nil {
+ return err
+ }
+
+ // Let any in-flight Update finish so its turn is captured in the snapshot.
+ if err := workflow.Await(ctx, func() bool { return workflow.AllHandlersFinished(ctx) }); err != nil {
+ return err
+ }
+
+ snap, err := googleadk.ExportSession(adkCtx, svc, AppName, UserID, SessionID)
+ if err != nil {
+ return err
+ }
+ return workflow.NewContinueAsNewError(ctx, ChatWorkflow, ChatInput{
+ Snapshot: snap,
+ MaxTurns: in.MaxTurns,
+ })
+}
+
+```
+
+
+## Streaming
+
+`googleadk.NewModel(name, googleadk.WithStreaming(topic, 0))` drives the model in streaming mode: the `InvokeModel`
+Activity calls the model with `stream=true`, heartbeats, and publishes each chunk to a per-run
+[`workflowstreams`](https://pkg.go.dev/go.temporal.io/sdk/contrib/workflowstreams) topic for external (UI) consumers,
+then returns the aggregated final response into the Workflow so replay stays deterministic.
+
+Call `googleadk.StreamServer(ctx)` once near the top of the Workflow that drives `r.Run`, and set
+`agent.RunConfig{StreamingMode: agent.StreamingModeSSE}`:
+
+```go
+func StreamingAgentWorkflow(ctx workflow.Context, q string) (string, error) {
+ if err := googleadk.StreamServer(ctx); err != nil { // required when streaming
+ return "", err
+ }
+ topic := "run-" + workflow.GetInfo(ctx).WorkflowExecution.ID
+ root, _ := llmagent.New(llmagent.Config{
+ Model: googleadk.NewModel("gemini-2.0-flash", googleadk.WithStreaming(topic, 0)),
+ // ...
+ })
+ // ... build the runner, set agent.RunConfig{StreamingMode: agent.StreamingModeSSE}, and drive r.Run
+}
+```
+
+External consumers read chunks with `workflowstreams.NewClient(c, workflowID, ...).Subscribe(...)`. The bidirectional
+`RunLive` path (hard-coded goroutines/channels) is **not** supported.
+
+## Error handling
+
+Model, tool, and MCP failures surface as Temporal `ApplicationError`s tagged `googleadk.ModelError`, `.ToolError`, and
+`.McpError`. Classify them with `googleadk.IsNonRetryable(err)` rather than string-matching. For model calls, the
+upstream HTTP status drives retryability (`408`/`409`/`429`/`5xx` are retryable; other `4xx` are not).
+
+:::tip
+
+Disable your model client's own retries in the `ModelFactory`. `InvokeModel` already runs under Temporal's
+`RetryPolicy`; leaving the model SDK's retries on retries a transient failure twice over. Let Temporal own retries.
+
+:::
+
+## Composing with other plugins
+
+The Temporal-side Activities use the default JSON data converter and ship no client/worker interceptor, so this
+integration composes with Temporal interceptor- or converter-based plugins (for example
+[`sdk-go/contrib/opentelemetry`](https://pkg.go.dev/go.temporal.io/sdk/contrib/opentelemetry)) without conflict. ADK
+emits its own OpenTelemetry spans; register your tracing interceptor on the worker as usual.
+
+## Testing without a live LLM
+
+The plugin ships test helpers so you can unit-test agent Workflows with no network: `FakeModel` (with `TextResponse` /
+`FunctionCallResponse` builders) and `FakeMCPServer`. Register them through the same `googleadk.Config` your production
+worker uses.
+
+## Supported and not-yet-supported
+
+- **Supported:** single- and multi-agent (`SubAgents`) trees, in-workflow function tools, `ActivityAsTool`, stateless
+ MCP, Gemini built-in tools (executed server-side inside `InvokeModel`), human-in-the-loop tool confirmation,
+ continue-as-new session-state carry, the in-memory session service, and SSE streaming.
+- **Not yet:** `RunLive` (bidirectional streaming), sub-agent-as-child-workflow, live memory/artifact tools that require
+ in-workflow network I/O, and database/Vertex session services. These raise or are documented rather than silently
+ degrading.
+
+## Samples
+
+The [Google ADK plugin samples](https://github.com/temporalio/samples-go/tree/main/googleadk) demonstrate a basic agent
+with a tool, a [multi-agent](https://github.com/temporalio/samples-go/tree/main/googleadk/multiagent) system, durable
+[human-in-the-loop](https://github.com/temporalio/samples-go/tree/main/googleadk/humanintheloop) tool approval, and a
+[continue-as-new chat](https://github.com/temporalio/samples-go/tree/main/googleadk/chat).
diff --git a/docs/develop/go/integrations/index.mdx b/docs/develop/go/integrations/index.mdx
new file mode 100644
index 0000000000..a8b37e1574
--- /dev/null
+++ b/docs/develop/go/integrations/index.mdx
@@ -0,0 +1,22 @@
+---
+id: index
+title: Integrations - Go SDK
+sidebar_label: Integrations
+description: This section covers integrations with the Go SDK
+toc_max_heading_level: 4
+keywords:
+ - Go SDK
+ - integrations
+ - ai frameworks
+tags:
+ - Go SDK
+ - Temporal SDKs
+ - Integrations
+---
+
+import IntegrationsGrid from '@site/src/components/IntegrationsGrid';
+
+The following integrations are available for the Temporal Go SDK.
+You can also use the Temporal Go SDK's [Plugin system](/develop/plugins-guide) to build your own integrations.
+
+
diff --git a/sidebars.js b/sidebars.js
index 4dab1212bf..d7c89ec3a6 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -232,6 +232,17 @@ module.exports = {
},
],
},
+ {
+ type: 'category',
+ label: 'Integrations',
+ className: 'sidebar-integrations',
+ collapsed: true,
+ link: {
+ type: 'doc',
+ id: 'develop/go/integrations/index',
+ },
+ items: ['develop/go/integrations/google-adk'],
+ },
],
},
{
diff --git a/src/components/IntegrationsGrid/index.tsx b/src/components/IntegrationsGrid/index.tsx
index 707af4bd92..b19649e926 100644
--- a/src/components/IntegrationsGrid/index.tsx
+++ b/src/components/IntegrationsGrid/index.tsx
@@ -10,12 +10,13 @@ import integrations, { type SDK, type Integration } from "./integrations-data";
import SdkSvg from "../elements/SdkSvgs/SdkSvg";
import styles from "./IntegrationsGrid.module.css";
-const ALL_SDKS: SDK[] = ["Java", "Python", "Ruby", "TypeScript"];
+const ALL_SDKS: SDK[] = ["Go", "Java", "Python", "Ruby", "TypeScript"];
const LANGUAGE_AGNOSTIC = "Language-agnostic";
type SdkFilter = SDK | typeof LANGUAGE_AGNOSTIC;
const ALL_SDK_FILTERS: SdkFilter[] = [...ALL_SDKS, LANGUAGE_AGNOSTIC];
const SDK_BLOCK_NAMES: Record = {
+ Go: "goLangBlock",
Java: "javaBlock",
Python: "pythonBlock",
Ruby: "rubyBlock",
diff --git a/src/components/IntegrationsGrid/integrations-data.json b/src/components/IntegrationsGrid/integrations-data.json
index 6abe200f5e..b4d0c791e8 100644
--- a/src/components/IntegrationsGrid/integrations-data.json
+++ b/src/components/IntegrationsGrid/integrations-data.json
@@ -62,6 +62,15 @@
"sdk": "Python",
"href": "https://adk.dev/integrations/temporal/"
},
+ {
+ "name": "Google ADK",
+ "description": "Run Google ADK agents with durable execution using the Temporal Go SDK.",
+ "tags": [
+ "Agent framework"
+ ],
+ "sdk": "Go",
+ "href": "/develop/go/integrations/google-adk"
+ },
{
"name": "Grafana Cloud",
"description": "Export Temporal Cloud metrics to Grafana Cloud with a serverless integration and pre-built dashboard.",
diff --git a/src/components/IntegrationsGrid/integrations-data.ts b/src/components/IntegrationsGrid/integrations-data.ts
index 537a282c89..3b3729c443 100644
--- a/src/components/IntegrationsGrid/integrations-data.ts
+++ b/src/components/IntegrationsGrid/integrations-data.ts
@@ -1,6 +1,6 @@
import integrationsData from "./integrations-data.json";
-export type SDK = "Java" | "Python" | "TypeScript" | "Ruby";
+export type SDK = "Go" | "Java" | "Python" | "TypeScript" | "Ruby";
export type Integration = {
name: string;