In Part 4 of this series, we added tool calling to our AI agent, allowing it to access real-time information like weather forecasts and current dates. However, we discovered another limitation: when asked to book flights or hotels, the agent couldn't access those systems because we'd need to hardcode every API integration into our application.
Hardcoding API integrations creates maintenance challenges. Every new service requires code changes, recompilation, and redeployment. As your organization adds more systems (booking platforms, inventory systems, CRM tools), your AI agent becomes increasingly difficult to maintain and extend.
In this post, we'll add Model Context Protocol (MCP) to our AI agent, allowing it to dynamically discover and use tools from external services without code changes or redeployment. You'll learn how MCP enables you to expose your legacy systems, enterprise applications, or microservices to AI agents without rewriting them.
Overview of the Solution
The Integration Challenge
Hardcoding tool integrations has several problems:
- Every new API requires code changes and redeployment
- Tools are tightly coupled to the AI agent application
- Different teams can't independently develop and deploy their tools
- Testing and versioning tools becomes complex
- Scaling to dozens or hundreds of integrations becomes unmanageable
We need a way to add new capabilities to our AI agent without modifying its code.
We'll solve this with Model Context Protocol (MCP):
- Create MCP servers that expose tools via a standardized protocol
- Configure MCP client in our AI agent to discover available tools
- Let the AI automatically call tools from any connected MCP server
- Add new capabilities by simply starting new MCP servers
What is Model Context Protocol?
Model Context Protocol (MCP) is an open protocol that standardizes how AI applications connect to external tools and data sources. Think of it as a universal adapter that lets AI agents discover and use tools from any service that implements the protocol.
Key benefits:
- Standardized Interface: All tools use the same protocol, regardless of implementation
- Dynamic Discovery: AI agents automatically discover available tools from connected servers
- Loose Coupling: Tools and AI agents can be developed, deployed, and scaled independently
- Multiple Transports: Supports Streamable HTTP (recommended), SSE (Server-Sent Events), stdio, and custom transports
- Language Agnostic: Servers can be written in any language (Java, Python, Node.js, etc.)
Architecture Overview
AI Agent (MCP Client)
↓
[MCP Protocol - Streamable HTTP Transport]
↓
┌─────────────────────────────┐ ┌─────────────────────────────┐
│ Travel Server │ │ More MCP Servers can be │
│ (URL:8082) │ │ added via URL:PORT │
├─────────────────────────────┤ │ - GitHub Server │
│ - findFlightsByRoute │ │ - Jira Server │
│ - findFlightByNumber │ │ - CRM Server │
│ - findHotelsByCity │ │ - Inventory Server │
│ - findHotelsByName │ │ - ... │
│ - getHotel │ └─────────────────────────────┘
└─────────────────────────────┘
Key Spring AI Components
- MCP Server Starter: Auto-configures MCP server capabilities in Spring Boot applications
- MCP Client Starter: Connects to MCP servers and registers their tools automatically
- ToolCallbackProvider: Spring AI interface for tool registration and invocation
Prerequisites
Before you start, ensure you have:
- Completed Part 4 of this series with the working
ai-agentapplication - Java 21 JDK installed (Amazon Corretto 21)
- Maven 3.6+ installed
- Docker Desktop running (for Testcontainers with PostgreSQL/PGVector)
- AWS CLI configured with access to Amazon Bedrock
Navigate to your project directory from Part 4:
cd ai-agent
MCP Server
We'll create a separate Spring Boot application that acts as an MCP server, exposing travel-related tools (flight and hotel search/booking) that our AI agent can use.
Why Separate Server?
Separating the MCP server from the AI agent provides several benefits:
- Independent Deployment: Update travel services without redeploying the AI agent
- Team Autonomy: Different teams can own and maintain their own MCP servers
- Scalability: Scale travel services independently based on demand
- Reusability: Multiple AI agents or applications can use the same MCP server
Clone Travel Server
We'll use a pre-built travel server that demonstrates MCP server capabilities:
cd ..
git clone https://github.com/aws-samples/java-on-aws.git
cd java-on-aws/samples/spring-ai-te-agent/travel
Explore Travel Server Structure
The travel server provides comprehensive travel management capabilities:
# View the project structure
ls -la src/main/java/com/example/travel/
Key components:
- HotelTools: Search and booking tools for hotels
- FlightTools: Search and booking tools for flights
- AirportTools: Airport information lookup
- Services: Business logic for travel operations
- Repositories: Data access layer with Spring Data JPA
The travel server is configured as an MCP server with these key dependencies in pom.xml:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
And configuration in application.properties:
# MCP Server properties
spring.ai.mcp.server.name=travel
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.instructions="Travel management: search for airports, flights, hotels; book flights and hotels"
spring.ai.mcp.server.protocol=STREAMABLE
logging.level.org.springframework.ai=DEBUG
These properties configure the MCP server identity and provide instructions that help AI agents understand the server's capabilities.
Note: Starting with Spring AI 1.1.0, we use the Streamable HTTP MCP Server which provides better performance and reliability compared to SSE transport.
Review HotelTools
Let's examine how tools are exposed via MCP:
cat src/main/java/com/example/travel/accommodations/HotelTools.java
package com.example.travel.accommodations;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.List;
@Component
public class HotelTools {
private final HotelService hotelService;
public HotelTools(HotelService hotelService) {
this.hotelService = hotelService;
}
@Bean
public ToolCallbackProvider hotelToolsProvider(HotelTools hotelTools) {
return MethodToolCallbackProvider.builder()
.toolObjects(hotelTools)
.build();
}
@Tool(description = """
Find hotels in a city for specific dates.
Requires: city - Name of the city to search in,
checkInDate - Check-in date (YYYY-MM-DD),
numberOfNights - Number of nights to stay.
Returns: List of available hotels sorted by price from lowest to highest.
Errors: NOT_FOUND if no hotels found in the specified city.
""")
public List<Hotel> findHotelsByCity(String city, LocalDate checkInDate, Integer numberOfNights) {
return hotelService.findHotelsByCity(city, checkInDate, numberOfNights);
}
@Tool(description = """
Find hotel details by hotel name.
Requires: hotelName - The name of the hotel.
Returns: Complete hotel details including amenities, pricing, and availability.
Errors: NOT_FOUND if hotel doesn't exist with the specified hotelName.
""")
public List<Hotel> findHotelsByName(String hotelName) {
return hotelService.findHotelsByName(hotelName);
}
@Tool(description = """
Get hotel details by ID.
Requires: id - The unique identifier of the hotel.
Returns: Complete hotel details including amenities, pricing, and availability.
Errors: NOT_FOUND if hotel doesn't exist with the specified ID.
""")
public Hotel getHotel(String id) {
return hotelService.getHotel(id);
}
}
Key points:
- @Tool annotation: Marks methods as callable tools with detailed descriptions
- ToolCallbackProvider bean: Registers tools with Spring AI's MCP server
- Separation of concerns: Tools delegate to service layer for business logic
- Clear descriptions: Help the AI understand when and how to use each tool
- Tools files pattern: Create separate Tools classes instead of annotating service methods directly, allowing you to expand complex objects into individual parameters and use descriptive method names that help the AI model understand which method to call with which parameters
Important: The real power of Spring AI's MCP server starter is that you can add MCP capabilities to almost any existing Java application or microservice. By simply adding the starter dependency and annotating methods with
@Tool, you can expose your legacy systems, enterprise applications, or microservices to AI agents without rewriting them. This enables AI integration across your entire application landscape.
Start Travel Server
Open a new terminal and run the travel server. It uses Testcontainers for automatic PostgreSQL management:
./mvnw spring-boot:test-run
You should see:
Creating container for image: postgres:16
Container postgres:16 is starting...
Container postgres:16 started
Started TravelApplication in X.XXX seconds
The server is now running on http://localhost:8082 with:
- Over 100 hotels across major cities
- Sample flights between popular destinations
- MCP server endpoints for tool discovery
Keep this terminal open - the travel server needs to run while we test the AI agent.
Test Travel Server
Open a new terminal and verify the server is working:
# Search for hotels in Paris
curl "http://localhost:8082/api/hotels/search?city=Paris&checkInDate=2025-11-15&numberOfNights=3" | jq '.[0:2]'
# Search for flights from London to Paris
curl "http://localhost:8082/api/flights/search?departureCity=London&arrivalCity=Paris" | jq '.[0:2]'
✅ Success! The travel server is running and responding to requests.
MCP Client
Now we'll configure our AI agent to connect to the travel server via MCP, automatically discovering and using its tools.
Add MCP Client Dependency
Navigate back to your AI agent project:
Open pom.xml and add the MCP client dependency to the <dependencies> section:
<!-- MCP Client -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
Configure MCP Client
Add MCP client configuration to src/main/resources/application.properties:
cat >> src/main/resources/application.properties << 'EOF'
# MCP Client Configuration
spring.ai.mcp.client.toolcallback.enabled=true
spring.ai.mcp.client.streamablehttp.connections.travel.url=http://localhost:8082
EOF
This configuration:
- Enables automatic tool callback registration
- Connects to the travel server via Streamable HTTP transport
- Names the connection "travel" for identification in logs
Update ChatService with MCP
Update ChatService to use tools from MCP servers:
src/main/java/com/example/ai/agent/service/ChatService.java
...
public ChatService(ChatMemoryService chatMemoryService,
VectorStore vectorStore,
DateTimeService dateTimeService,
WeatherService weatherService,
ToolCallbackProvider tools,
ChatClient.Builder chatClientBuilder) {
// Build ChatClient with RAG, tools, and memory
this.chatClient = chatClientBuilder
.defaultSystem(SYSTEM_PROMPT)
.defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore).build()) // RAG for policies
.defaultTools(dateTimeService, weatherService) // Custom tools
.defaultToolCallbacks(tools) // Auto-registered tools from MCP
.build();
this.chatMemoryService = chatMemoryService;
}
...
The key change is adding ToolCallbackProvider tools parameter and .defaultToolCallbacks(tools) method. Spring AI automatically:
- Connects to configured MCP servers
- Discovers available tools
- Registers them with the ChatClient
- Makes them available to the AI model
Testing MCP Integration
Let's test the complete MCP integration with travel bookings:
./mvnw spring-boot:test-run
You should see in the logs:
...
Server response with Protocol: 2024-11-05, Capabilities: ServerCapabilities[completions=CompletionCapabilities[], experimental=null, logging=LoggingCapabilities[], prompts=PromptCapabilities[listChanged=true], resources=ResourceCapabilities[subscribe=false, listChanged=true], tools=ToolCapabilities[listChanged=true]], Info: Implementation[name=travel, version=1.0.0] and Instructions "Travel management: search for airports, flights, hotels; book flights and hotels"
...1
Started AiAgentApplication in 3.088 seconds (process running for 3.31)
Test with REST API:
# Search for hotels in Paris
curl -X POST http://localhost:8080/api/chat/message \
-H "Content-Type: application/json" \
-d '{"prompt": "Find me hotels in Paris for 3 nights starting November 15, 2025", "userId": "alice"}' \
--no-buffer
# Response: Lists available hotels with prices, amenities, and ratings
# Search for flights
curl -X POST http://localhost:8080/api/chat/message \
-H "Content-Type: application/json" \
-d '{"prompt": "Find flights from London to Paris", "userId": "alice"}' \
--no-buffer
# Response: Lists available flights with times, airlines, and prices
# Complex travel planning
curl -X POST http://localhost:8080/api/chat/message \
-H "Content-Type: application/json" \
-d '{
"prompt": "Please find me inbound and outbound flights and accommodations for a trip from London to Paris next week, from Monday to Friday. I travel alone, prefer BA flights in the first part of the day, and choose accommodation which is the most expensive but complies with our travel policy. Give me a travel itinerary with flights, accommodation, prices and weather forecast for each day of the travel.",
"userId": "alice"
}' \
--no-buffer
# Response: Complete travel itinerary with flights, hotel, prices, and weather
✅ Success! The AI agent now has access to travel booking capabilities through MCP without any hardcoded integrations.
You can also test in the UI at http://localhost:8080 - ask about flights, hotels, or complete travel planning.
How MCP Works
When you ask "Find hotels in Paris for 3 nights", here's what happens:
- AI analyzes the question: Recognizes it needs hotel search capabilities
- Tool discovery: Finds
findHotelsByCitytool from the travel MCP server - Parameter extraction: Extracts city="Paris", numberOfNights=3, and determines check-in date
- Tool invocation: Calls the travel server via MCP protocol
- Response synthesis: Formats hotel results into a natural language response
The AI automatically decides which tools to call from which servers, all through the standardized MCP protocol.
Let's continue the chat. Alice returned from her Paris trip and wants to submit her expenses:
curl -X POST http://localhost:8080/api/chat/message \
-H "Content-Type: application/json" \
-d '{"prompt": "I just returned from Paris and need to submit my hotel receipt and restaurant expenses. Can you help me process them?", "userId": "alice"}' \
--no-buffer
# Response: "I can help you submit expenses, but I need the receipt details. Please provide the amounts, dates, and merchant names."
❌ Problem discovered: Alice has photos of her receipts on her phone, but the agent can only process text—it cannot analyze images or extract information from visual documents like receipts, invoices, or travel confirmations.
We'll address this limitation in the next part of the series! Stay tuned!
Add More MCP Servers
The power of MCP is that you can add new capabilities without changing the AI agent code:
# Start another MCP server on a different port
# The AI agent will automatically discover its tools
export SPRING_AI_MCP_CLIENT_STREAMABLEHTTP_CONNECTIONS_TRAVEL_URL=http://localhost:8083
./mvnw spring-boot:test-run
You can add as many MCP servers as needed, each providing different capabilities (CRM, inventory, analytics, etc.).
Cleanup
To stop the applications:
- Press
Ctrl+Cin the AI agent terminal - Press
Ctrl+Cin the travel server terminal
The PostgreSQL containers will continue running (due to withReuse(true)). If necessary, stop and remove them:
docker stop ai-agent-postgres
docker rm ai-agent-postgres
(Optional) To remove all data and start fresh:
docker volume prune
Commit Changes
git add .
git commit -m "Add MCP client integration"
Conclusion
In this post, we've added dynamic tool integration to our AI agent through MCP:
- MCP Server: Created a separate travel service exposing hotel and flight tools
- MCP Client: Configured AI agent to discover and use tools from MCP servers
- Dynamic Discovery: AI automatically finds and calls tools without hardcoded integrations
- Loose Coupling: Travel services can be updated independently of the AI agent
- Scalability: Add new capabilities by starting new MCP servers
Our AI agent now has a complete, production-ready architecture: memory (Part 2), knowledge (Part 3), real-time information (Part 4), and dynamic tool integration (Part 5). It can remember conversations, answer policy questions, access current information, and integrate with any service that implements the MCP protocol—all essential capabilities for enterprise AI applications.
What's Next
Explore advanced features like multi-modal support (image and document analysis) using various models.
Learn More
- Model Context Protocol Documentation
- Spring AI MCP Documentation
- MCP Java SDK
- Amazon Bedrock User Guide
- Part 1: Create an AI Agent
- Part 2: Add Memory
- Part 3: Add Knowledge
- Part 4: Add Tools
Let's continue building intelligent Java applications with Spring AI!



Top comments (0)