diff --git a/examples/README.md b/examples/README.md
index 65768a1b9..d7304d1eb 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -19,3 +19,5 @@ contains more complete end-to-end samples for customers to use or modify.
prime-checking tools.
- [`mcpfilesystem`](mcpfilesystem/README.md): an agent that uses the MCP
filesystem server as a toolset.
+- [`spring_chat`](spring_chat/README.md): a Spring Boot chat page backed by an
+ agent that calls its model through Spring AI.
diff --git a/examples/pom.xml b/examples/pom.xml
index b52d70446..8df3003ef 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -37,5 +37,6 @@
contextcaching
helloworld
mcpfilesystem
+ spring_chat
diff --git a/examples/spring_chat/README.md b/examples/spring_chat/README.md
new file mode 100644
index 000000000..f60236ed0
--- /dev/null
+++ b/examples/spring_chat/README.md
@@ -0,0 +1,46 @@
+# Spring Chat Sample
+
+A minimal Spring Boot web app with a browser chat window backed by an ADK agent.
+The agent calls its model through Spring AI, using the ADK Spring AI integration
+(`google-adk-spring-ai`).
+
+## How it fits together
+
+- `spring-ai-starter-model-google-genai` creates a Spring AI `ChatModel`.
+- The ADK Spring AI auto-configuration wraps it in a `SpringAI` bean, which is
+ an ADK `BaseLlm`.
+- `AgentConfig` builds an `LlmAgent` on that model and an `InMemoryRunner`.
+- `ChatController` exposes `POST /api/chat`, and `static/index.html` is the
+ chat page. Each page load gets its own ADK session, so the agent remembers
+ the conversation until you reload. Sessions are kept in memory until the app
+ stops.
+
+To use another provider, replace the Google GenAI starter in `pom.xml` with any
+other Spring AI chat model starter (OpenAI, Anthropic, Ollama, ...) and set its
+`spring.ai.*` properties.
+
+## Prerequisites
+
+- Java 17+
+- A Google Cloud project with Vertex AI enabled, and Application Default
+ Credentials (`gcloud auth application-default login`).
+
+The app uses Vertex AI by default. Set `GOOGLE_CLOUD_PROJECT`, and optionally
+`GOOGLE_CLOUD_LOCATION` (default `global`) and `GEMINI_MODEL`. To use the Gemini
+Developer API instead, set `GOOGLE_GENAI_USE_VERTEXAI=false` and
+`GOOGLE_API_KEY`.
+
+## Run
+
+From the repository root:
+
+```bash
+export GOOGLE_CLOUD_PROJECT=your-project-id
+./mvnw install -DskipTests -pl examples/spring_chat -am
+./mvnw spring-boot:run -pl examples/spring_chat
+```
+
+Then open http://localhost:8080. The chat endpoint has no authentication, so the
+app listens on `localhost` only, which keeps other machines out. Set
+`SERVER_ADDRESS=0.0.0.0` to accept connections from other hosts. Don't expose it
+beyond a trusted environment without adding authentication.
diff --git a/examples/spring_chat/pom.xml b/examples/spring_chat/pom.xml
new file mode 100644
index 000000000..b2e259703
--- /dev/null
+++ b/examples/spring_chat/pom.xml
@@ -0,0 +1,91 @@
+
+
+
+ 4.0.0
+
+
+ com.google.adk
+ google-adk-examples
+ 1.13.0
+ ..
+
+
+ com.google.adk.samples
+ google-adk-sample-spring-chat
+ Google ADK - Sample - Spring Chat
+
+ A Spring Boot web app with a chat page backed by an ADK agent that calls its model through
+ Spring AI.
+
+ jar
+
+
+ 2.0.1
+
+
+
+
+
+ org.springframework.ai
+ spring-ai-bom
+ ${spring-ai.version}
+ pom
+ import
+
+
+
+
+
+
+ com.google.adk
+ google-adk
+ ${project.version}
+
+
+ com.google.adk
+ google-adk-spring-ai
+ ${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-webmvc
+
+
+
+ org.springframework.ai
+ spring-ai-starter-model-google-genai
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+ true
+
+
+
+
+
diff --git a/examples/spring_chat/src/main/java/com/example/springchat/AgentConfig.java b/examples/spring_chat/src/main/java/com/example/springchat/AgentConfig.java
new file mode 100644
index 000000000..374f4d7a7
--- /dev/null
+++ b/examples/spring_chat/src/main/java/com/example/springchat/AgentConfig.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.springchat;
+
+import com.google.adk.agents.BaseAgent;
+import com.google.adk.agents.LlmAgent;
+import com.google.adk.models.springai.SpringAI;
+import com.google.adk.runner.InMemoryRunner;
+import com.google.adk.runner.Runner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/** Spring configuration for the ADK agent and runner. */
+@Configuration(proxyBeanMethods = false)
+class AgentConfig {
+
+ private static final String APP_NAME = "spring-chat";
+
+ /**
+ * Returns the chat agent. Its {@link SpringAI} model is auto-configured by the ADK Spring AI
+ * integration from whichever Spring AI {@code ChatModel} starter is on the classpath.
+ */
+ @Bean
+ BaseAgent chatAgent(SpringAI model) {
+ return LlmAgent.builder()
+ .name("spring_chat_agent")
+ .description("A friendly general-purpose chat assistant.")
+ .model(model)
+ .instruction("You are a friendly, concise assistant. Answer the user's questions clearly.")
+ .build();
+ }
+
+ @Bean
+ Runner runner(BaseAgent chatAgent) {
+ return new InMemoryRunner(chatAgent, APP_NAME);
+ }
+}
diff --git a/examples/spring_chat/src/main/java/com/example/springchat/ChatController.java b/examples/spring_chat/src/main/java/com/example/springchat/ChatController.java
new file mode 100644
index 000000000..f1272a0d2
--- /dev/null
+++ b/examples/spring_chat/src/main/java/com/example/springchat/ChatController.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.springchat;
+
+import com.google.adk.agents.RunConfig;
+import com.google.adk.events.Event;
+import com.google.adk.runner.Runner;
+import com.google.genai.types.Content;
+import com.google.genai.types.Part;
+import java.util.stream.Collectors;
+import org.jspecify.annotations.Nullable;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.server.ResponseStatusException;
+
+/** REST endpoint that forwards chat messages from the browser to the ADK runner. */
+@RestController
+class ChatController {
+
+ // The sample has no login, so every browser tab shares one user and keeps its own session.
+ private static final String USER_ID = "spring-chat-user";
+
+ private static final RunConfig RUN_CONFIG = RunConfig.builder().autoCreateSession(true).build();
+
+ private final Runner runner;
+
+ ChatController(Runner runner) {
+ this.runner = runner;
+ }
+
+ /** A message from the browser; {@code sessionId} is generated client-side once per page load. */
+ private record ChatRequest(@Nullable String sessionId, @Nullable String message) {}
+
+ private record ChatResponse(String reply) {}
+
+ @PostMapping("/api/chat")
+ ChatResponse chat(@RequestBody ChatRequest request) {
+ if (isBlank(request.sessionId()) || isBlank(request.message())) {
+ throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
+ }
+ // The session service trims ids it creates, so look them up trimmed too.
+ String sessionId = request.sessionId().trim();
+ Content userMessage = Content.fromParts(Part.fromText(request.message()));
+ String reply =
+ runner
+ .runAsync(USER_ID, sessionId, userMessage, RUN_CONFIG)
+ .filter(Event::finalResponse)
+ .map(ChatController::replyText)
+ .collect(Collectors.joining("\n"))
+ .blockingGet();
+ return new ChatResponse(reply.strip());
+ }
+
+ /** Returns the event's text, followed by why the model stopped, if it ended early. */
+ private static String replyText(Event event) {
+ String text = event.stringifyContent();
+ return event.errorMessage().map(error -> text + "\n[" + error + "]").orElse(text);
+ }
+
+ private static boolean isBlank(@Nullable String s) {
+ return s == null || s.trim().isEmpty();
+ }
+}
diff --git a/examples/spring_chat/src/main/java/com/example/springchat/SpringChatApplication.java b/examples/spring_chat/src/main/java/com/example/springchat/SpringChatApplication.java
new file mode 100644
index 000000000..c0bbfaf82
--- /dev/null
+++ b/examples/spring_chat/src/main/java/com/example/springchat/SpringChatApplication.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2026 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.springchat;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+/** Spring Boot entry point for the chat sample. */
+@SpringBootApplication
+public class SpringChatApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringChatApplication.class, args);
+ }
+}
diff --git a/examples/spring_chat/src/main/resources/application.properties b/examples/spring_chat/src/main/resources/application.properties
new file mode 100644
index 000000000..5136a5856
--- /dev/null
+++ b/examples/spring_chat/src/main/resources/application.properties
@@ -0,0 +1,11 @@
+# Spring AI Google GenAI chat model. Vertex AI with Application Default Credentials is the default:
+# set GOOGLE_CLOUD_PROJECT (and optionally GOOGLE_CLOUD_LOCATION). To use the Gemini Developer API
+# instead, set GOOGLE_GENAI_USE_VERTEXAI=false and GOOGLE_API_KEY.
+spring.ai.google.genai.vertex-ai=${GOOGLE_GENAI_USE_VERTEXAI:true}
+spring.ai.google.genai.project-id=${GOOGLE_CLOUD_PROJECT:}
+spring.ai.google.genai.location=${GOOGLE_CLOUD_LOCATION:global}
+spring.ai.google.genai.api-key=${GOOGLE_API_KEY:}
+spring.ai.google.genai.chat.model=${GEMINI_MODEL:gemini-3.8-flash}
+
+# The chat endpoint has no authentication, so listen on loopback only unless told otherwise.
+server.address=${SERVER_ADDRESS:localhost}
diff --git a/examples/spring_chat/src/main/resources/static/index.html b/examples/spring_chat/src/main/resources/static/index.html
new file mode 100644
index 000000000..50671484d
--- /dev/null
+++ b/examples/spring_chat/src/main/resources/static/index.html
@@ -0,0 +1,82 @@
+
+
+
+
+
+ ADK Spring Chat
+
+
+
+ ADK Spring Chat
+
+
+
+
+
+