The ADK Spring AI Integration Library provides a bridge between the Agent Development Kit (ADK) and Spring AI, enabling developers to use Spring AI models within the ADK framework. This library supports multiple AI providers, streaming responses, function calling, and comprehensive observability.
To use ADK Java with the Spring AI integration in your application, add the following dependencies to your pom.xml:
<dependencies>
<!-- ADK Core -->
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk</artifactId>
<version>1.0.1-rc.1-SNAPSHOT</version>
</dependency>
<!-- ADK Spring AI Integration -->
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk-spring-ai</artifactId>
<version>1.0.1-rc.1-SNAPSHOT</version>
</dependency>
<!-- Spring AI BOM for version management -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>2.0.0-M3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>Add the Spring AI provider dependencies for the AI services you want to use:
OpenAI:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
</dependency>Anthropic (Claude):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-anthropic</artifactId>
</dependency>Google Gemini:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-google-genai</artifactId>
</dependency>Vertex AI:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-vertex-ai-gemini</artifactId>
</dependency>Azure OpenAI:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-azure-openai</artifactId>
</dependency>Ollama (Local models):
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama</artifactId>
</dependency><?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>my-adk-spring-ai-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.0.2</version>
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
<spring-ai.version>2.0.0-M3</spring-ai.version>
<adk.version>1.0.1-rc.1-SNAPSHOT</adk.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring Boot Starters -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- ADK Dependencies -->
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk</artifactId>
<version>${adk.version}</version>
</dependency>
<dependency>
<groupId>com.google.adk</groupId>
<artifactId>google-adk-spring-ai</artifactId>
<version>${adk.version}</version>
</dependency>
<!-- Spring AI Providers (choose the ones you need) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-anthropic</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-google-genai</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>Once you have the dependencies set up, you can create a simple ADK agent with Spring AI:
@SpringBootApplication
public class MyAdkSpringAiApplication {
public static void main(String[] args) {
SpringApplication.run(MyAdkSpringAiApplication.class, args);
}
@Bean
public LlmAgent scienceTeacher(SpringAI springAI) {
// SpringAI is auto-configured based on available ChatModel beans
return LlmAgent.builder()
.name("science-teacher")
.description("A helpful science teacher")
.model(springAI)
.instruction("You are a helpful science teacher. Explain concepts clearly.")
.build();
}
}@SpringBootApplication
public class MyAdkSpringAiApplication {
public static void main(String[] args) {
SpringApplication.run(MyAdkSpringAiApplication.class, args);
}
@Bean
public SpringAI springAI() {
// Configure OpenAI
OpenAiApi openAiApi = OpenAiApi.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(openAiApi)
.build();
return new SpringAI(chatModel, "gpt-4o-mini");
}
@Bean
public LlmAgent scienceTeacher(SpringAI springAI) {
return LlmAgent.builder()
.name("science-teacher")
.description("A helpful science teacher")
.model(springAI)
.instruction("You are a helpful science teacher. Explain concepts clearly.")
.build();
}
}@SpringBootApplication
public class MyAdkSpringAiApplication {
public static void main(String[] args) {
SpringApplication.run(MyAdkSpringAiApplication.class, args);
}
@Bean
@Primary
public SpringAI openAiSpringAI() {
OpenAiApi openAiApi = OpenAiApi.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
OpenAiChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(openAiApi)
.build();
return new SpringAI(chatModel, "gpt-4o-mini");
}
@Bean
@Qualifier("anthropic")
public SpringAI anthropicSpringAI() {
AnthropicApi anthropicApi = AnthropicApi.builder()
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.build();
AnthropicChatModel chatModel = AnthropicChatModel.builder()
.anthropicApi(anthropicApi)
.build();
return new SpringAI(chatModel, "claude-sonnet-4-6");
}
@Bean
public LlmAgent openAiAgent(SpringAI springAI) {
return LlmAgent.builder()
.name("openai-teacher")
.model(springAI) // Uses @Primary SpringAI bean
.instruction("You are a helpful science teacher using OpenAI.")
.build();
}
@Bean
public LlmAgent anthropicAgent(@Qualifier("anthropic") SpringAI anthropicSpringAI) {
return LlmAgent.builder()
.name("anthropic-teacher")
.model(anthropicSpringAI) // Uses specific Anthropic SpringAI bean
.instruction("You are a helpful science teacher using Claude.")
.build();
}
}Add these properties to your application.yml or application.properties:
# Spring AI Provider Configuration
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o-mini
temperature: 0.7
anthropic:
api-key: ${ANTHROPIC_API_KEY}
chat:
options:
model: claude-sonnet-4-6
temperature: 0.7
# ADK Spring AI Configuration
adk:
spring-ai:
default-model: "gpt-4o-mini"
auto-configuration:
enabled: true
validation:
enabled: true
fail-fast: false
observability:
enabled: true
metrics-enabled: trueIn addition to wrapping Spring AI ChatModels as ADK BaseLlms, this library can wrap any Spring AI ToolCallback as an ADK BaseTool via SpringAiToolCallbackBackedAdkTool. This unlocks the full Spring AI tool ecosystem for ADK agents:
- MCP tools —
SyncMcpToolCallback/AsyncMcpToolCallbackproduced byspring-ai-starter-mcp-clientfromspring.ai.mcp.client.*properties @Tool-annotated methods — Spring AI's annotation-driven function callingFunctionToolCallback— programmatically declared tools- Any other implementation of
org.springframework.ai.tool.ToolCallback
The bridge is the reverse direction of the existing ToolConverter (which goes ADK → Spring AI). Together they make ADK and Spring AI tool ecosystems fully interoperable.
SpringAiToolCallbackBackedAdkTool reads ToolCallback.getToolDefinition() to extract the tool name, description, and JSON Schema. The schema is converted to ADK's Schema type via Schema.fromJson(...); if parsing fails the bridge falls back to the parametersJsonSchema(Object) escape hatch (no hard failure). At invocation time the bridge serializes the Map<String, Object> arguments to JSON, dispatches to ToolCallback.call(String), and parses the JSON response back to Map<String, Object>. Non-object responses (primitives / arrays / arbitrary strings) are wrapped under a "result" key for structural consistency.
application.yaml:
spring:
ai:
mcp:
client:
sse:
connections:
filesystem:
url: http://localhost:3000Java:
import com.google.adk.agents.LlmAgent;
import com.google.adk.models.springai.SpringAI;
import com.google.adk.models.springai.bridge.SpringAiToolCallbackBackedAdkTool;
import org.springframework.ai.tool.ToolCallback;
@Configuration
class AgentConfig {
@Bean
public LlmAgent rootAgent(SpringAI springAI, List<ToolCallback> mcpToolCallbacks) {
return LlmAgent.builder()
.name("root_agent")
.model(springAI)
.tools(SpringAiToolCallbackBackedAdkTool.wrapAll(mcpToolCallbacks))
.instruction("Use the available tools to answer the user.")
.build();
}
}That's it. The List<ToolCallback> is auto-injected by spring-ai-starter-mcp-client's McpToolCallbackAutoConfiguration. The bridge converts every callback into a BaseTool. The agent uses them transparently.
When you only need to wrap one callback:
ToolCallback callback = /* obtained from any Spring AI source */;
BaseTool adkTool = new SpringAiToolCallbackBackedAdkTool(callback);
LlmAgent agent = LlmAgent.builder()
.name("my_agent")
.model(springAI)
.tools(List.of(adkTool))
.build();The library also ships SpringAiToolBridgeAutoConfiguration which auto-discovers every ToolCallback bean in the Spring context and exposes them as a single @Bean("springAiTools") List<BaseTool>. Wiring becomes:
@Bean
public LlmAgent rootAgent(
SpringAI llm,
@Qualifier("springAiTools") List<BaseTool> springAiTools) {
return LlmAgent.builder().name("root").model(llm).tools(springAiTools).build();
}Active only when at least one ToolCallback bean exists (from spring-ai-starter-mcp-client, @Tool methods, FunctionToolCallback beans, etc.).
If the underlying ToolCallback.call(...) throws, the bridge catches the exception and returns a structured Map.of("error", "<message>") result — matching ADK's native AbstractMcpTool.wrapCallResult(...) shape. The agent sees a tool result with an error key rather than aborting the invocation. Falls back to the exception's simple class name when the message is null.
The bridge sets FunctionDeclaration.parametersJsonSchema(Map) from the parsed JSON schema, not parameters(Schema.fromJson(...)). This routes through the faithful branch of Spring AI's ToolConverter and preserves items, enum, format, anyOf/oneOf, additionalProperties, $defs and $ref. Unparseable schemas leave the field unset (with a logged warning) rather than emit a degraded or double-encoded schema.
ADK ships its own MCP client in com.google.adk.tools.mcp.* (CLI / non-Spring-Boot scenarios). The two paths can be mixed at the .tools(...) boundary — both produce BaseTool instances — but it is strongly recommended to pick one in any given application. The Spring AI MCP route is the natural choice for Spring Boot apps because everything is property-driven; ADK's native McpToolset remains the right choice for non-Spring usage.
The library is structured around several key components that work together to provide seamless integration:
adk-spring-ai/
├── src/main/java/com/google/adk/models/springai/
│ ├── SpringAI.java # Main adapter class
│ ├── SpringAIEmbedding.java # Embedding model wrapper
│ ├── MessageConverter.java # Message format conversion
│ ├── ToolConverter.java # Function/tool conversion
│ ├── ConfigMapper.java # Configuration mapping
│ ├── autoconfigure/ # Spring Boot auto-configuration
│ ├── observability/ # Metrics and logging
│ ├── properties/ # Configuration properties
│ └── error/ # Error handling and mapping
The main adapter class that implements BaseLlm and wraps Spring AI ChatModel and StreamingChatModel instances.
Key Features:
- Supports both blocking and streaming chat models
- Reactive API using RxJava3 Flowable
- Comprehensive error handling and observability
- Token usage tracking
- Multiple constructor overloads for different scenarios
Usage:
// With ChatModel only
SpringAI springAI = new SpringAI(chatModel, "claude-sonnet-4-6");
// With both ChatModel and StreamingChatModel
SpringAI springAI = new SpringAI(chatModel, streamingChatModel, "claude-sonnet-4-6");
// With observability configuration
SpringAI springAI = new SpringAI(chatModel, "claude-sonnet-4-6", observabilityConfig);Handles conversion between ADK's Content/Part format and Spring AI's Message/ChatResponse format.
Key Features:
- Converts ADK
LlmRequestto Spring AIPrompt - Converts Spring AI
ChatResponseto ADKLlmResponse - Supports system, user, and assistant messages
- Handles function calls and responses
- Gemini Compatibility: Combines multiple system messages into one for Gemini API compatibility
- Streaming response detection and partial response handling
Message Type Mapping:
- ADK
Contentwith role "user" → Spring AIUserMessage - ADK
Contentwith role "model"/"assistant" → Spring AIAssistantMessage - ADK
Contentwith role "system" → Spring AISystemMessage - Function calls and responses are converted appropriately
Converts between ADK tools and Spring AI function calling format.
Key Features:
- Converts ADK
BaseToolto Spring AIToolCallback - Schema conversion from ADK format to Spring AI JSON schema
- Intelligent argument processing for different provider formats
- Function Schema Registration: Properly registers JSON schemas with Spring AI using
inputSchema()method - Debug logging for troubleshooting function calling issues
Function Calling Flow:
- ADK
FunctionDeclaration→ Spring AIFunctionToolCallback - ADK schema → JSON schema string
- Runtime argument conversion and validation
- Tool execution and result serialization
Wrapper for Spring AI embedding models providing ADK-compatible embedding generation.
Key Features:
- Single text and batch text embedding
- Reactive API using RxJava3 Single
- Full EmbeddingRequest/EmbeddingResponse support
- Observability and error handling
- Dimension information access
Maps ADK GenerateContentConfig to Spring AI ChatOptions.
Supported Configurations:
- Temperature (Float → Double conversion)
- Max output tokens
- Top-P (Float → Double conversion)
- Stop sequences
- Configuration validation
Unsupported/Provider-Specific:
- Top-K (not directly supported by Spring AI)
- Presence/frequency penalties (provider-specific)
- Response schema and MIME type
- Package:
com.google.adk.models.springai - Purpose: Main integration classes
- Key Classes:
SpringAI,MessageConverter,ToolConverter,ConfigMapper
- Package:
com.google.adk.models.springai - Purpose: Embedding model integration
- Key Classes:
SpringAIEmbedding,EmbeddingConverter
- Package:
com.google.adk.models.springai.autoconfigure - Purpose: Spring Boot auto-configuration
- Key Classes:
SpringAIAutoConfiguration
- Package:
com.google.adk.models.springai.observability - Purpose: Metrics, logging, and monitoring
- Key Classes:
SpringAIObservabilityHandler
- Package:
com.google.adk.models.springai.properties - Purpose: Configuration properties
- Key Classes:
SpringAIProperties
- Package:
com.google.adk.models.springai.error - Purpose: Error mapping and handling
- Key Classes:
SpringAIErrorMapper
// Non-streaming
Flowable<LlmResponse> response = springAI.generateContent(llmRequest, false);
// Streaming
Flowable<LlmResponse> stream = springAI.generateContent(llmRequest, true);The library supports function calling through ADK tools:
// Create agent with tools
LlmAgent agent = LlmAgent.builder()
.name("weather-agent")
.model(springAI)
.tools(FunctionTool.create(WeatherTools.class, "getWeatherInfo"))
.build();
// Tools are automatically converted to Spring AI format// Single text embedding
Single<float[]> embedding = springAIEmbedding.embed("Hello world");
// Batch embedding
Single<List<float[]>> embeddings = springAIEmbedding.embed(texts);
// Full request/response
Single<EmbeddingResponse> response = springAIEmbedding.embedForResponse(request);// ADK config automatically mapped to Spring AI ChatOptions
LlmRequest request = LlmRequest.builder()
.contents(contents)
.config(GenerateContentConfig.builder()
.temperature(0.7f)
.maxOutputTokens(1000)
.topP(0.9f)
.build())
.build();The library works with any Spring AI provider:
-
OpenAI (
spring-ai-openai)- Models: GPT-4o, GPT-4o-mini, GPT-3.5-turbo
- Features: Chat, streaming, function calling, embeddings
-
Anthropic (
spring-ai-anthropic)- Models: Claude 4.x Sonnet, Claude 4.x Haiku
- Features: Chat, streaming, function calling
- Note: Requires proper function schema registration
-
Google Gemini (
spring-ai-google-genai)- Models: Gemini 2.0 Flash, Gemini 1.5 Pro
- Features: Chat, streaming, function calling
- Note: Requires single system message (automatically handled)
-
Vertex AI (
spring-ai-vertex-ai-gemini)- Models: Vertex AI Gemini models
- Features: Chat, streaming, function calling
-
Azure OpenAI (
spring-ai-azure-openai)- Models: Azure-hosted OpenAI models
- Features: Chat, streaming, function calling
-
Ollama (
spring-ai-ollama)- Models: Local Llama, Mistral, etc.
- Features: Chat, streaming
- System Messages: Only one system message allowed - library automatically combines multiple system messages
- Model Names: Use
gemini-2.0-flash,gemini-1.5-pro - API Key: Requires
GOOGLE_API_KEYenvironment variable
- Function Calling: Requires explicit schema registration using
inputSchema()method - Model Names: Use full model names like
claude-sonnet-4-6 - API Key: Requires
ANTHROPIC_API_KEYenvironment variable
- Standard Support: Full feature compatibility
- Model Names: Use
gpt-4o-mini,gpt-4o, etc. - API Key: Requires
OPENAI_API_KEYenvironment variable
The library provides Spring Boot auto-configuration for seamless integration:
adk:
spring-ai:
default-model: "gpt-4o-mini"
temperature: 0.7
max-tokens: 1000
top-p: 0.9
top-k: 40
auto-configuration:
enabled: true
validation:
enabled: true
fail-fast: false
observability:
enabled: true
metrics-enabled: true
include-content: falseThe auto-configuration creates beans based on available Spring AI models:
@Bean
@ConditionalOnBean({ChatModel.class, StreamingChatModel.class})
public SpringAI springAIWithBothModels(
ChatModel chatModel,
StreamingChatModel streamingChatModel,
SpringAIProperties properties) {
// Auto-configured SpringAI instance
}
@Bean
@ConditionalOnBean(EmbeddingModel.class)
public SpringAIEmbedding springAIEmbedding(
EmbeddingModel embeddingModel,
SpringAIProperties properties) {
// Auto-configured SpringAIEmbedding instance
}The library includes comprehensive integration tests for different providers:
-
OpenAiApiIntegrationTest.java
- Tests OpenAI integration with real API calls
- Covers blocking, streaming, and function calling
-
GeminiApiIntegrationTest.java
- Tests Google Gemini integration with real API calls
- Covers blocking, streaming, and function calling
- Tests configuration options
-
MessageConverterTest.java
- Unit tests for message conversion logic
- Tests system message combining for Gemini compatibility
# Set required environment variables
export OPENAI_API_KEY=your_key
export GOOGLE_API_KEY=your_key
export ANTHROPIC_API_KEY=your_key
# Run specific integration test
mvn test -Dtest=OpenAiApiIntegrationTest
# Run all tests
mvn testThe library provides comprehensive error handling through SpringAIErrorMapper:
- Spring AI exceptions → ADK-compatible errors
- Provider-specific error normalization
- Detailed error context preservation
- Request/response logging
- Token usage tracking
- Error metrics collection
- Performance monitoring
- Always specify explicit model names rather than relying on defaults
- Use environment variables for API keys
- Configure appropriate timeouts for your use case
- Enable observability for production monitoring
- Ensure function schemas are properly defined in ADK tools
- Test function calling with each provider separately
- Handle provider-specific argument format differences
- Use debug logging to troubleshoot function calling issues
- Use streaming for long responses
- Implement proper backpressure handling
- Configure connection pooling for high-throughput scenarios
- Monitor token usage and costs
- Implement retry logic for transient failures
- Handle provider-specific error conditions
- Use circuit breakers for external API calls
- Log errors with sufficient context for debugging
- Spring AI Model (
spring-ai-model) - ADK Core (
google-adk) - Google GenAI Types (
google-genai) - RxJava3 for reactive programming
- Jackson for JSON processing
spring-ai-openaispring-ai-anthropicspring-ai-google-genaispring-ai-vertex-ai-geminispring-ai-azure-openaispring-ai-ollama
spring-boot-autoconfigure(optional)spring-boot-configuration-processor(optional)jakarta.validation-api(optional)
- Enhanced provider-specific optimizations
- Advanced streaming aggregation
- Multi-modal content support
- Enhanced observability and metrics
- Performance optimization for high-throughput scenarios
- Live connection mode not supported (returns
UnsupportedOperationException) - Some provider-specific features may not be fully supported
- Response schema and MIME type configuration limited
- Top-K parameter not directly mapped to Spring AI
- Replace Spring AI
ChatModel.call()withSpringAI.generateContent() - Update message formats from Spring AI to ADK format
- Configure auto-configuration properties
- Update dependency management to include ADK Spring AI
- Spring AI: 1.1.0-M3+
- Spring Boot: 3.0+
- Java: 17+
- ADK: 0.3.1+
This library provides a robust foundation for integrating Spring AI models with the ADK framework, offering enterprise-grade features like observability, error handling, and multi-provider support while maintaining the flexibility and power of both frameworks.