Build a Custom MCP Server with Spring AI in Under 10 Minutes
Welcome back to our MCP Server series. In a previous article, we explored the fundamentals of the Model Context Protocol (MCP) and how to integrate MCP servers with clients like Claude Desktop. Today, we're taking the next exciting step: building our very own MCP server from scratch.
In this hands-on guide, I'll show you how quick and simple it is to create a fully functional MCP server using Spring AI's powerful starter packages. Whether you're looking to extend your AI applications, create custom tool integrations, or simply experiment with the MCP ecosystem, this article will give you everything you need to get up and running in just minutes.
Understanding the Spring AI Framework
A great place to begin is the official Spring AI reference documentation. It's a valuable resource for understanding both Spring AI and MCP servers. The documentation covers the starter packages, including the standard MCP server which supports stdio
(standard input/output). This is the transport protocol that clients like Claude Desktop support, so we will use this transport in our example. While other transports like Web MVC and WebFlux exist for different use cases, we'll stick with stdio
for this guide.
The documentation also details configuration options, such as choosing between asynchronous or synchronous server types and other transport settings. For this tutorial, we will build a basic Spring Boot application with the MCP starters to expose some information using tools. This is done with the @Tool
annotation and a callback provider to inform the client which tools are available.
We won't delve into advanced topics like Resource Management or Prompt Management in this introductory guide. The goal here is to create a "Hello, World!" for MCP servers. Once you're ready to move beyond the basics, you can find more than 5+ complex examples on the Spring AI GitHub repository.
Even if you're working with plain Java without the Spring framework, you can still leverage this technology. The official Java SDK for the Model Context Protocol was contributed by the Spring AI team, and its repository provides guidance on getting started with Java and MCP.
Getting Started with start.spring.io
To begin, head over to start.spring.io
to create a new application. We'll configure the project metadata. For this example, we'll call the project courses
. The goal is to expose a list of created courses that a client like Claude can query.
To do this, we only need to add one key dependency. Search for "MCP" and add the Model Context Protocol Server. We are creating a server that a client, such as Claude Desktop, can connect to and utilize. That's the only dependency required to get started.
Generate the project, which will download a zip file. You can open this project in your preferred IDE; for this demonstration, IntelliJ IDEA is used.
Implementing the MCP Server Logic
Once the project is open, you'll see a standard Spring Boot application structure.
First, let's define a Java record
to represent our data structure. This Course
record will simply hold a title
and a URL
.
public record Course(String title, String url) {}
Next, we'll create a service component to manage our course data. This service will be annotated with @Service
so Spring can manage it as a bean.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
@Service
public class CourseService {
private static final Logger log = LoggerFactory.getLogger(CourseService.class);
private final List<Course> courses = new ArrayList<>();
@PostConstruct
private void setup() {
courses.add(new Course("Building Web Applications with Spring Boot and Spring Web MVC", "https://www.youtube.com/watch?v=UgX5lgv4uVM"));
courses.add(new Course("Spring Boot Tutorial for Beginners", "https://www.youtube.com/watch?v=UgX5lgv4uVM"));
}
// Methods will be added here
}
Inside this service, we use @PostConstruct
to populate the list with some sample data when the application starts. This makes the information available to the Large Language Model (LLM).
Now, we need to expose this functionality to the client. We'll add two methods to our CourseService
: one to get all courses and another to get a single course by its title.
// Inside CourseService class
public List<Course> getCourses() {
return courses;
}
public Course getCourse(String title) {
return courses.stream()
.filter(course -> course.title().equalsIgnoreCase(title))
.findFirst()
.orElse(null);
}
To expose these methods as tools for the MCP client, we use the @Tool
annotation. We provide a name and a description for each tool, which helps the LLM understand what the tool does.
import org.springframework.ai.model.tool.Tool;
// ... other imports
// Inside CourseService class
@Tool(name = "dv-get-courses", description = "Get a list of available courses")
public List<Course> getCourses() {
// ...
}
@Tool(name = "dv-get-course", description = "Get a single course by title")
public Course getCourse(String title) {
// ...
}
As mentioned in our previous article, clients like Claude Desktop display a list of available tools from each connected MCP server. We are declaring that our server exposes these two specific tools. This is achieved by defining a Bean
in our main application class that provides a list of ToolCallback
objects.
import org.springframework.ai.model.tool.ToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
@Configuration
public class AppConfig {
private final CourseService courseService;
public AppConfig(CourseService courseService) {
this.courseService = courseService;
}
@Bean
public List<ToolCallback> danTools() {
return List.of(
ToolCallback.of("dv-get-courses", courseService::getCourses),
ToolCallback.of("dv-get-course", courseService::getCourse)
);
}
}
Since CourseService
is annotated with @Service
, Spring's dependency injection will provide the instance for us. With this bean defined, our tools will be discoverable by any connected MCP client.
Configuration and Building the Application
A few final configuration steps are necessary in the application.properties
file.
spring.main.web-application-type=none
spring.ai.mcp.server.name=Dan-Vega-MCP
spring.ai.mcp.server.version=0.0.1
spring.main.banner-mode=off
Note: It is critical to disable the Spring Boot banner (spring.main.banner-mode=off
). If enabled, it can interfere with the stdio
transport by sending non-JSON data, which leads to parsing errors.
Now, we can build the application using Maven.
./mvnw clean package -DskipTests
This command produces a JAR file in the target/
directory, which is the artifact we will deploy. For easier distribution, especially to users without a Java runtime, you could compile this into a native executable using GraalVM.
Integrating with an MCP Client (Claude Desktop)
In the previous article, we discussed the Claude Desktop configuration file, which is where you register custom MCP servers. The file is typically located at ~/Library/Application Support/Claude/claude_desktop_config.json
on macOS or a similar path on other operating systems. We will add a new entry for our server.
{
"mcp_servers": [
{
"name": "Dan-Vega-MCP",
"command": "/path/to/your/java/bin/java",
"args": [
"-jar",
"/path/to/your/project/target/courses-0.0.1-SNAPSHOT.jar"
]
}
]
}
Note: It's a good practice to provide the full path to your Java executable and the project JAR file to avoid potential PATH
issues.
After saving the configuration, open Claude Desktop.
Testing the Integration
If you encounter an error on startup, the logs are the first place to check. You can access the logs directly from the MCP settings menu in Claude Desktop. A common issue is a simple typo or incorrect path in the configuration. Correcting the path should resolve the problem.
Once running correctly, you can verify the installation in the MCP settings. Our new server, Dan-Vega-MCP
, will appear in the list of installed servers. The total tool count will increase, confirming our two new tools are registered. Our custom tools, dv-get-course
and dv-get-courses
, will be listed with their descriptions.
Now, you can interact with the LLM and ask it a question that utilizes your new tool: "Can you tell me what courses are available?"
The client will prompt for permission before executing the tool for the first time. After granting permission, the server processes the request and returns the data. The LLM will then present the information in a helpful format.
Sure, there are two courses available: 1. Building Web Applications with Spring Boot and Spring Web MVC: https://www.youtube.com/watch?v=UgX5lgv4uVM 2. Spring Boot Tutorial for Beginners: https://www.youtube.com/watch?v=UgX5lgv4uVM
Both courses focus on Spring Boot, a popular Java framework for building web applications. Would you like more information about either of these courses?
Beyond the Basics: The Power of MCP
While this is a simple example, it demonstrates the fundamental process of creating an MCP server to expose new capabilities to an LLM. The possibilities are extensive. For instance, you could build integrations for platforms like Beehiiv or Notion.
It's important to think beyond simple API wrappers. What we are truly doing is augmenting the LLM, enabling it to perform complex, multi-step workflows. For example, a newsletter workflow could involve pulling content from an API, transforming it by embedding social media posts, stripping out ads, and then formatting it as markdown for a personal website.
MCP is about connecting clients to various data sources, whether they are databases, APIs, or even static, embedded information. The Model Context Protocol provides the standard for this interaction, creating a cohesive ecosystem of tools for large language models.
Join the 10xdev Community
Subscribe and get 8+ free PDFs that contain detailed roadmaps with recommended learning periods for each programming language or field, along with links to free resources such as books, YouTube tutorials, and courses with certificates.