Backend Development 18 min read

Turn a Spring Boot CRUD App into an AI‑Powered Service with Model Context Protocol

This guide shows how to integrate Spring Boot with the Model Context Protocol (MCP) to let large language models interact with existing services via natural language, covering entity definitions, Maven dependencies, configuration, tool annotations, and testing to create an AI‑enabled book management API.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Turn a Spring Boot CRUD App into an AI‑Powered Service with Model Context Protocol

Introduction

With the rapid development of artificial intelligence, large language models (LLM) are reshaping how users interact with software. Instead of learning complex APIs or filling out forms, users can simply speak natural language commands such as "Find all books published in 2023" or "Create a new user named Zhang San with email [email protected]". This intuitive interaction reduces learning curves and cuts training costs for B‑side systems.

The Model Context Protocol (MCP) brings this value to the application layer.

Understanding MCP

MCP acts as a "universal adapter" for the AI world. Imagine many services and databases each speaking their own language; AI would need to learn each one. MCP provides a single translation layer so AI only needs to learn one language to communicate with all services.

If you are a backend developer, you may be familiar with gRPC , which standardizes communication between services written in different languages. MCP is a similar bridge, but specifically designed for AI models, allowing them to query services uniformly.

Refactoring an Existing Spring Boot Service

We start with a simple book‑management service. The

Book

entity is defined as follows:

<code>import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PastOrPresent;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDate;

@Entity
@Table(name = "books")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Book {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @NotBlank(message = "书名不能为空")
  @Column(nullable = false)
  private String title;

  @NotBlank(message = "分类不能为空")
  @Column(nullable = false)
  private String category;

  @NotBlank(message = "作者不能为空")
  @Column(nullable = false)
  private String author;

  @NotNull(message = "出版日期不能为空")
  @PastOrPresent(message = "出版日期不能是未来日期")
  @Column(nullable = false)
  private LocalDate publicationDate;

  @NotBlank(message = "ISBN编码不能为空")
  @Column(nullable = false, unique = true)
  private String isbn;
}</code>

The service interface provides two query methods:

<code>import com.example.entity.Book;
import java.util.List;

public interface BookService {
  // 根据作者查询
  List<Book> findBooksByAuthor(String author);
  // 根据分类查询
  List<Book> findBooksByCategory(String category);
}</code>

1. Import Dependencies

Add the following dependencies to

pom.xml

. Note that these are preview versions and require additional repository definitions.

<code><!-- Spring AI core dependency -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-core</artifactId>
</dependency>

<!-- Anthropic model support -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
</dependency>

<!-- MCP server support – WebMVC version -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
</dependency></code>

Because these artifacts are not in the central Maven repository, add the following repository entries:

<code><repositories>
  <repository>
    <id>spring-milestones</id>
    <name>Spring Milestones</name>
    <url>https://repo.spring.io/milestone</url>
    <snapshots><enabled>false</enabled></snapshots>
  </repository>
  <repository>
    <id>spring-snapshots</id>
    <name>Spring Snapshots</name>
    <url>https://repo.spring.io/snapshot</url>
    <snapshots><enabled>false</enabled></snapshots>
  </repository>
  <repository>
    <id>central-portal-snapshots</id>
    <name>Central Portal Snapshots</name>
    <url>https://central.sonatype.com/repository/maven-snapshots/</url>
    <releases><enabled>false</enabled></releases>
    <snapshots><enabled>true</enabled></snapshots>
  </repository>
</repositories></code>

2. Add Configuration

Configure the AI key and enable the MCP server in

application.properties

(or

application.yml

).

<code># Spring AI api-key
spring.ai.anthropic.api-key=YOUR_API_KEY

# Enable MCP server
spring.ai.mcp.server.enabled=true

# MCP server settings
spring.ai.mcp.server.name=book-management-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.sse-message-endpoint=/mcp/message</code>

3. Refactor Service Methods

Annotate the service implementation with

@Tool

and

@ToolParam

so that Spring AI can expose them as MCP tools.

<code>import com.example.entity.Book;
import com.example.repository.BookRepository;
import com.example.service.BookService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
@RequiredArgsConstructor
public class BookServiceImpl implements BookService {

  @Resource
  private BookRepository bookRepository;

  @Override
  @Tool(name = "findBooksByTitle", description = "根据书名模糊查询图书,支持部分标题匹配")
  public List<Book> findBooksByTitle(@ToolParam(description = "书名关键词") String title) {
    return bookRepository.findByTitleContaining(title);
  }

  @Override
  @Tool(name = "findBooksByAuthor", description = "根据作者精确查询图书")
  public List<Book> findBooksByAuthor(@ToolParam(description = "作者姓名") String author) {
    return bookRepository.findByAuthor(author);
  }

  @Override
  @Tool(name = "findBooksByCategory", description = "根据图书分类精确查询图书")
  public List<Book> findBooksByCategory(@ToolParam(description = "图书分类") String category) {
    return bookRepository.findByCategory(category);
  }
}
</code>

Register the tool callbacks with the MCP server:

<code>import com.example.service.BookService;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class McpServerConfig {

  @Bean
  public ToolCallbackProvider bookToolCallbackProvider(BookService bookService) {
    return MethodToolCallbackProvider.builder()
        .toolObjects(bookService)
        .build();
  }
}
</code>

Configure the chat client to use the system prompt and register the tools:

<code>import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {

  @Autowired
  private ToolCallbackProvider toolCallbackProvider;

  @Bean
  public ChatClient chatClient(ChatClient.Builder builder) {
    return builder
        .defaultSystem(
            "你是一个图书管理助手,可以帮助用户查询图书信息。" +
            "你可以根据书名模糊查询、根据作者查询和根据分类查询图书。" +
            "回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。")
        .defaultTools(toolCallbackProvider)
        .build();
  }
}
</code>

Alternatively, expose the query methods as function beans and register them by name:

<code>import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.function.Function;

@Service
public class BookQueryService {

  @Resource
  private BookService bookService;

  @Bean
  public Function<String, List<Book>> findBooksByTitle() {
    return title -> bookService.findBooksByTitle(title);
  }

  @Bean
  public Function<String, List<Book>> findBooksByAuthor() {
    return author -> bookService.findBooksByAuthor(author);
  }

  @Bean
  public Function<String, List<Book>> findBooksByCategory() {
    return category -> bookService.findBooksByCategory(category);
  }
}
</code>

When using function beans, the chat client registers them directly:

<code>import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {

  @Bean
  public ChatClient chatClient(ChatClient.Builder builder) {
    return builder
        .defaultSystem(
            "你是一个图书管理助手,可以帮助用户查询图书信息。" +
            "你可以根据书名模糊查询、根据作者查询和根据分类查询图书。" +
            "回复时,请使用简洁友好的语言,并将图书信息整理为易读的格式。")
        .defaultTools("findBooksByTitle", "findBooksByAuthor", "findBooksByCategory")
        .build();
  }
}
</code>

4. Interface Testing

Expose a REST controller that forwards user messages to the AI chat client:

<code>import com.example.model.ChatRequest;
import com.example.model.ChatResponse;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/chat")
public class ChatController {

  @Resource
  private ChatClient chatClient;

  @PostMapping
  public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) {
    try {
      String userMessage = request.getMessage();
      String content = chatClient.prompt()
          .user(userMessage)
          .call()
          .content();
      return ResponseEntity.ok(new ChatResponse(content));
    } catch (Exception e) {
      e.printStackTrace();
      return ResponseEntity.ok(new ChatResponse("处理请求时出错: " + e.getMessage()));
    }
  }
}
</code>

Initialize sample data at application startup using

CommandLineRunner

:

<code>import com.example.entity.Book;
import com.example.repository.BookRepository;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

@Component
@RequiredArgsConstructor
public class DataInitializer implements CommandLineRunner {

  @Resource
  private BookRepository bookRepository;

  @Override
  public void run(String... args) throws Exception {
    List<Book> sampleBooks = Arrays.asList(
        new Book(null, "Spring实战(第6版)", "编程", "Craig Walls", LocalDate.of(2022, 1, 15), "9787115582247"),
        new Book(null, "深入理解Java虚拟机", "编程", "周志明", LocalDate.of(2019, 12, 1), "9787111641247"),
        new Book(null, "Java编程思想(第4版)", "编程", "Bruce Eckel", LocalDate.of(2007, 6, 1), "9787111213826"),
        new Book(null, "算法(第4版)", "计算机科学", "Robert Sedgewick", LocalDate.of(2012, 10, 1), "9787115293800"),
        new Book(null, "云原生架构", "架构设计", "张三", LocalDate.of(2023, 3, 15), "9781234567890"),
        new Book(null, "微服务设计模式", "架构设计", "张三", LocalDate.of(2021, 8, 20), "9789876543210"),
        new Book(null, "领域驱动设计", "架构设计", "Eric Evans", LocalDate.of(2010, 4, 10), "9787111214748"),
        new Book(null, "高性能MySQL", "数据库", "Baron Schwartz", LocalDate.of(2013, 5, 25), "9787111464747"),
        new Book(null, "Redis实战", "数据库", "Josiah L. Carlson", LocalDate.of(2015, 9, 30), "9787115419378"),
        new Book(null, "深入浅出Docker", "容器技术", "李四", LocalDate.of(2022, 11, 20), "9787123456789")
    );
    bookRepository.saveAll(sampleBooks);
    System.out.println("数据初始化完成,共加载 " + sampleBooks.size() + " 本图书");
  }
}
</code>

Conclusion

By integrating Spring Boot with MCP, we effortlessly convert a traditional CRUD system into an AI‑driven assistant. MCP serves as a bridge between AI models and services, simplifying integration and paving the way for a future where "conversational‑as‑a‑service" becomes the standard development paradigm.

backendJavaMCPSpring BootModel Context ProtocolAI integration
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.