Building a Memory Lane with a Spring Boot MCP Server
This blog post will guide you through the process of creating an MCP server in Spring Boot. We'll use a practical example of a JournalTool that allows an AI to create and retrieve journal entries.
What is an MCP Server?
Imagine an AI assistant that can't access your calendar, send an email, or look up information on the internet. Its usefulness would be severely limited. An MCP server acts as a bridge between your AI model and the outside world. It exposes a set of tools—essentially, functionalities that the AI can call upon to perform specific tasks. This could be anything from fetching data from a database, calling a REST API, or, in our case, managing a journal.
Spring AI provides excellent support for building MCP servers, making it surprisingly simple to expose your application's capabilities to an AI.
Key Concepts in Our MCP Server
Let's break down the important annotations and components in our JournalTool:
@Service: This is a standard Spring annotation that marks the class as a service bean, making it a candidate for dependency injection and part of the application's business logic.@Tool: By annotating a method with@Tool, you are exposing it as a callable function to the AI model.name: This attribute provides a specific name for the tool. The AI will use this name to refer to the function.description: This is crucial. The description provides the AI with the necessary context to understand what the tool does, what its parameters are for, and when to use it. A well-written description is key to the AI's ability to effectively use your tool.
@Service
public class JournalTool {
private final JournalRepository journalRepository;
public JournalTool(JournalRepository journalRepository) {
this.journalRepository = journalRepository;
}
@Tool(name = "createJournalEntry",
description = "Creates a new journal entry with the given journal text, type, and hashtag." +
" Use this when no date is given for the journal entry. Also, use the most" +
" important words from the journal in hashtag, seperated by a -")
public String createJournalEntry(String journal, String journalType, String hashtag) {
try {
// Create a new journal entry
var journalEntry = new com.manitaggarwal.memory.entity.JournalEntry();
journalEntry.setJournal(journal);
journalEntry.setJournalType(journalType);
journalEntry.setHashtag(hashtag);
journalEntry.setJournalDateTime(new java.util.Date());
// Save the journal entry to the repository
journalRepository.save(journalEntry);
return "Journal entry created successfully";
} catch (Exception e) {
return "Error creating journal entry: " + e.getMessage();
}
}
@Tool(name = "createJournalEntryForParticularDate",
description = "Creates a new journal entry with the given journal text, type, and hashtag." +
" Use this when a date is given for the journal entry. Also, use the most" +
" important words from the journal in hashtag, seperated by a -")
public String createJournalEntryForSomeday(String journal, String journalType, String hashtag,
Date date) {
try {
// Create a new journal entry
var journalEntry = new com.manitaggarwal.memory.entity.JournalEntry();
journalEntry.setJournal(journal);
journalEntry.setJournalType(journalType);
journalEntry.setHashtag(hashtag);
journalEntry.setJournalDateTime(date);
// Save the journal entry to the repository
journalRepository.save(journalEntry);
return "Journal entry created successfully";
} catch (Exception e) {
return "Error creating journal entry: " + e.getMessage();
}
}
@Tool(name = "get_journal_entry_by_date",
description = "Retrieves journal entries for a specific date.")
public String getJournalEntryByDate(Date date) {
try {
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
// Set to start of day
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
Date startOfDay = calendar.getTime();
// Set to start of next day
calendar.add(Calendar.DATE, 1);
Date startOfNextDay = calendar.getTime();
// Retrieve journal entries by date
var journalEntries = journalRepository.findAllByJournalDateTimeBetween(startOfDay,
startOfNextDay);
if (journalEntries.isEmpty()) {
return "No journal entries found for the specified date.";
}
StringBuilder response = new StringBuilder("Journal entries for " + date + ":\n");
for (var entry : journalEntries) {
response.append("Serial Number: ").append(entry.getSerialNumber())
.append(", Journal: ").append(entry.getJournal())
.append(", Type: ").append(entry.getJournalType())
.append(", Hashtag: ").append(entry.getHashtag())
.append(", Date: ").append(entry.getJournalDateTime())
.append("\n");
}
return response.toString();
} catch (Exception e) {
return "Error retrieving journal entries: " + e.getMessage();
}
}
@Tool(name = "get_journal_entry_between_date",
description = "Retrieves journal entries between two specific dates.")
public String getJournalEntryByDate(Date startDate, Date endDate) {
try {
var journalEntries = journalRepository.findAllByJournalDateTimeBetween(startDate,
endDate);
if (journalEntries.isEmpty()) {
return "No journal entries found for the specified date.";
}
StringBuilder response = new StringBuilder("Journal entries between " + startDate + ":\n"
+ " and " + endDate + ":\n");
for (var entry : journalEntries) {
response.append("Serial Number: ").append(entry.getSerialNumber())
.append(", Journal: ").append(entry.getJournal())
.append(", Type: ").append(entry.getJournalType())
.append(", Hashtag: ").append(entry.getHashtag())
.append(", Date: ").append(entry.getJournalDateTime())
.append("\n");
}
return response.toString();
} catch (Exception e) {
return "Error retrieving journal entries: " + e.getMessage();
}
}
@Tool(name = "get_journal_entry_by_search_params",
description = "Retrieves journal entries based on search parameters in hashtags.")
public String getJournalEntryByHashtag(List<String> searchParams) {
try {
StringBuilder response = new StringBuilder("Journal entries matching search parameters:\n");
for (String param : searchParams) {
var journalEntries = journalRepository.findAllByHashtagContaining(param);
if (journalEntries.isEmpty()) {
response.append("No journal entries found for hashtag: ").append(param).append("\n");
} else {
for (var entry : journalEntries) {
response.append("Serial Number: ").append(entry.getSerialNumber())
.append(", Journal: ").append(entry.getJournal())
.append(", Type: ").append(entry.getJournalType())
.append(", Hashtag: ").append(entry.getHashtag())
.append(", Date: ").append(entry.getJournalDateTime())
.append("\n");
}
}
}
return response.toString();
} catch (Exception e) {
return "Error retrieving journal entries: " + e.getMessage();
}
}
}
The Model Context Protocol and Spring AI are democratizing the ability to build powerful, context-aware AI applications. By creating a simple yet effective MCP server, you can unlock a new level of interaction and intelligence for your AI models. The JournalTool example is just the beginning.
Git repository link can be found here.

