Transform a Spring Boot CRUD Service into an AI‑Powered MCP Endpoint

This guide walks through converting a traditional Spring Boot book‑management API into a Model Context Protocol (MCP) service that can be invoked via natural‑language prompts, covering dependency setup, proxy configuration, @Tool annotations, MCP server registration, chat client wiring, data initialization, and end‑to‑end testing.

Java Web Project
Java Web Project
Java Web Project
Transform a Spring Boot CRUD Service into an AI‑Powered MCP Endpoint

Understanding MCP

The Model Context Protocol (MCP) acts as an AI‑world universal adapter. It abstracts the heterogeneous communication protocols of services and databases into a single contract that a language model can invoke, similar to how gRPC standardizes RPC but purpose‑built for AI agents.

Traditional API vs MCP – concrete comparison

Consider a Spring Boot Book service that provides CRUD operations. In a classic REST client the caller must know the exact endpoint and request format. With MCP the same request – e.g. “Find all books published in 2023” – is expressed as natural language, and the model automatically selects the matching tool method.

Traditional API vs MCP illustration
Traditional API vs MCP illustration

Step‑by‑step conversion of the Spring Boot service

1. Import MCP‑related dependencies

<!-- 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 (WebMVC) -->
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-mcp-server-webmvc-spring-boot-starter</artifactId>
</dependency>

These are preview versions and are not available in Maven Central, so the Spring milestone and snapshot repositories must be added:

<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>true</enabled></snapshots>
  </repository>
</repositories>

2. Configure proxy (required for Anthropic access)

import jakarta.annotation.PostConstruct;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ProxyConfig {
  private final String PROXY_HOST = "127.0.0.1";
  private final int PROXY_PORT = 10080;

  @PostConstruct
  public void setSystemProxy() {
    System.setProperty("http.proxyHost", PROXY_HOST);
    System.setProperty("http.proxyPort", String.valueOf(PROXY_PORT));
    System.setProperty("https.proxyHost", PROXY_HOST);
    System.setProperty("https.proxyPort", String.valueOf(PROXY_PORT));
    System.out.println("System proxy configured: http://" + PROXY_HOST + ":" + PROXY_PORT);
  }
}

3. Add MCP‑specific properties

# Spring AI API key
spring.ai.anthropic.api-key=YOUR_API_KEY

# Enable MCP server
spring.ai.mcp.server.enabled=true
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

4. Annotate service methods with @Tool

Two approaches are possible: direct @Tool / @ToolParam annotations on the implementation class, or exposing the methods as Spring Function beans.

import com.example.entity.Book;
import com.example.repository.BookRepository;
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 = "Fuzzy search by book title")
  public List<Book> findBooksByTitle(@ToolParam(description = "title keyword") String title) {
    return bookRepository.findByTitleContaining(title);
  }

  @Override
  @Tool(name = "findBooksByAuthor", description = "Exact search by author")
  public List<Book> findBooksByAuthor(@ToolParam(description = "author name") String author) {
    return bookRepository.findByAuthor(author);
  }

  @Override
  @Tool(name = "findBooksByCategory", description = "Exact search by category")
  public List<Book> findBooksByCategory(@ToolParam(description = "category") String category) {
    return bookRepository.findByCategory(category);
  }
}

5. Register the tools with the MCP server

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();
  }
}

6. Configure the chat client to use the registered tools

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(
            "You are a book‑management assistant. " +
            "You can perform fuzzy title search, author search, and category search. " +
            "Reply concisely and format results for readability.")
        .defaultTools(toolCallbackProvider)
        .build();
  }
}

Alternative: expose query methods as function beans

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

@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);
  }
}

When using function beans, the chat client registers them by name:

@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
  return builder
      .defaultSystem("You are a book‑management assistant…")
      .defaultTools("findBooksByTitle", "findBooksByAuthor", "findBooksByCategory")
      .build();
}

7. Expose a REST controller for the chat endpoint

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 content = chatClient.prompt()
          .user(request.getMessage())
          .call()
          .content();
      return ResponseEntity.ok(new ChatResponse(content));
    } catch (Exception e) {
      return ResponseEntity.ok(new ChatResponse("Error handling request: " + e.getMessage()));
    }
  }
}

8. Load sample data with a CommandLineRunner

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("Data initialization complete, loaded " + sampleBooks.size() + " books");
  }
}

9. End‑to‑end test

Sending a POST request to /api/chat with the message “帮我查找所有2023年出版的图书” triggers the LLM to invoke the findBooksByCategory tool, which queries the database and returns the matching records. The response contains the sample books from 2023.

Chat test result
Chat test result

Conclusion

Integrating Spring Boot with MCP turns a conventional CRUD service into an AI‑driven conversational interface with minimal code changes. MCP abstracts the glue logic, allowing developers to focus on business methods while the model handles natural‑language routing. As the MCP ecosystem matures, "dialogue‑as‑a‑service" may become a standard pattern for complex enterprise applications.

Technical reference repository: https://github.com/Pitayafruits/spring-boot-mcp-demo

backendMCPSpring BootREST APIModel Context ProtocolAI integrationChatClientTool annotation
Java Web Project
Written by

Java Web Project

Focused on Java backend technologies, trending internet tech, and the latest industry developments. The platform serves over 200,000 Java developers, inviting you to learn and exchange ideas together. Check the menu for Java learning resources.

0 followers
Reader feedback

How this landed with the community

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.