Mastering Model Context Protocol (MCP) with Spring AI: From Theory to Hands‑On Implementation

This article explains the Model Context Protocol (MCP) introduced by Anthropic, compares it with Function Call, details its architecture and communication flow, and provides a step‑by‑step Spring AI guide—including server and client development, configuration, code examples, and testing—so developers can integrate AI models with back‑end services efficiently.

Architect
Architect
Architect
Mastering Model Context Protocol (MCP) with Spring AI: From Theory to Hands‑On Implementation

Overview

Large language models such as ChatGPT and DeepSeek require sufficient context to answer questions accurately. Manually supplying this context is cumbersome, so an intermediary program is needed to let the model retrieve context automatically.

Function Call

Before MCP, OpenAI introduced Function Call, a JSON‑based standard that describes tool specifications and enables the model to invoke external functions.

Model Context Protocol (MCP)

MCP (Model Context Protocol) is an open communication standard released by Anthropic in November 2024. It defines how applications exchange context with AI models, providing a standardized toolbox that allows models to interact with external tools, data sources, and services.

Principles

MCP Architecture

Host : receives user queries and interacts with the LLM.

MCP client : communicates with the MCP server using the MCP protocol.

MCP server : lightweight service exposing specific capabilities.

Local data source : files, databases, or services accessible to the server.

Remote service : external APIs reachable by the server.

MCP Call Process

User asks a question to the Host.

Host sends the question, the list of available MCP tools, and usage instructions to the LLM.

LLM selects a tool and returns the tool name and parameters.

Host starts the MCP client.

MCP client packages the request in MCP format and sends it to the MCP server.

MCP server accesses the required data source, packages the response in MCP format, and returns it.

Client forwards the response to the Host.

Host includes the MCP call context in the next LLM request.

LLM produces the final answer, which the Host returns to the user.

MCP Packet Analysis

Unlike Function Call, MCP embeds the full tool description directly in the prompt, which can reach tens of thousands of characters, making the request size much larger.

Transmission Mechanisms

MCP defines two standard transports, both using JSON‑RPC:

Stdio : the client launches the server as a subprocess; communication occurs via stdin/stdout.

HTTP with SSE : the server runs as an HTTP service exposing an SSE endpoint for push messages and a POST endpoint for client‑to‑server requests.

Key differences between the two mechanisms:

Location: Stdio runs locally; HTTP with SSE can be local or remote.

Client count: Stdio supports a single client; HTTP with SSE supports multiple clients.

Performance: Stdio offers low latency; HTTP with SSE adds network latency.

Complexity: Stdio is simpler; HTTP with SSE requires an HTTP server.

Security: Stdio relies on local permissions; HTTP with SSE needs explicit security measures such as TLS.

Deployment: Stdio is installed per user; HTTP with SSE is centrally deployed.

Practice

Implementation using Spring AI.

Prerequisites

JDK 17

Spring Boot 3.4.4

Maven

LLM: Qwen2.5‑72B‑Instruct

MCP Server Development

Core dependencies (choose one):

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server</artifactId>
    <version>1.0.0-M7</version>
</dependency>

<!-- Spring MVC SSE -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
    <version>1.0.0-M7</version>
</dependency>

<!-- Spring WebFlux SSE -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
    <version>1.0.0-M7</version>
</dependency>

Configuration files (choose one):

# Stdio configuration (application.yml)
spring:
  ai:
    mcp:
      server:
        name: mcp-server
        version: 1.0.0
        type: SYNC # SYNC or ASYNC
# SSE configuration (application.yml)
spring:
  ai:
    mcp:
      server:
        name: mcp-server
        version: 1.0.0
        type: SYNC
        sse-message-endpoint: /mcp/messages

Tool implementation using the @Tool annotation:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;

@Service
public class DateTimeTools {
    private final Logger logger = LoggerFactory.getLogger(DateTimeTools.class);

    @Tool(description = "Get the current date‑time")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

    @Tool(description = "Set an alarm, ISO‑8601 formatted time required")
    void setAlarm(String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        // System.out.println("Alarm set for " + alarmTime);
    }
}

Register the tools:

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 ToolsConfig {
    @Bean
    public ToolCallbackProvider tools(DateTimeTools dateTimeTools) {
        return MethodToolCallbackProvider.builder().toolObjects(dateTimeTools).build();
    }
}

Testing can be performed with tools such as Cherry Studio, which allows selecting the configured MCP server and invoking the defined tools.

MCP Client Development

Client dependencies (choose one):

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
    <version>1.0.0-M7</version>
</dependency>

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
    <version>1.0.0-M7</version>
</dependency>

Additional dependencies for LLM interaction (example with OpenAI) and web support:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-model-openai</artifactId>
    <version>1.0.0-M7</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Configuration for Stdio:

server:
  port: 8081

spring:
  ai:
    mcp:
      client:
        toolcallback:
          enabled: true
        stdio:
          root-change-notification: true
          connections:
            server1:
              command: java
              args:
                - -jar
                - /path/to/mcp-server.jar

# Disable banner and logging for the server when using Stdio
spring:
  main:
    banner-mode: off
logging:
  level:
    root: off

Configuration for SSE:

spring:
  ai:
    mcp:
      client:
        toolcallback:
          enabled: true
        sse:
          connections:
            server1:
              url: http://localhost:8080

spring:
  ai:
    openai:
      base-url: YOUR_BASE_URL
      api-key: YOUR_API_KEY
      chat:
        options:
          model: YOUR_MODEL

Declare the ChatClient bean:

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {
    @Bean
    public ChatClient chatClient(ChatClient.Builder builder, ToolCallbackProvider mcpTools) {
        return builder.defaultTools(mcpTools).build();
    }
}

Controller that forwards user input to the LLM:

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import jakarta.servlet.http.HttpServletResponse;

@RestController
public class DateTimeController {
    @Autowired
    private ChatClient chatClient;

    @GetMapping("/chat")
    public String chat(String input) {
        return chatClient.prompt().user(input).call().content();
    }

    @GetMapping("/chat/stream")
    public Flux<String> streamChat(HttpServletResponse response, String input) {
        response.setCharacterEncoding("UTF-8");
        return chatClient.prompt().user(input).stream().content();
    }
}

When using Stdio, ensure the server does not write non‑protocol data to stdout; disable banner and logging as shown above.

Testing

Start the MCP server (Stdio or SSE), then start the MCP client service. Access the client endpoints (e.g., /chat?input=What+time+is+it?) and observe that the client invokes the server‑side tools (current time, alarm setting) and returns the model’s answer.

Outlook

MCP acts as a bridge between traditional back‑end systems and AI models, offering a promising new direction for back‑end developers. Although still in its early stages—with immature tooling and high token consumption—it is expected to mature into a foundational layer for AI‑enabled services as the technology evolves.

MCPSpring AIModel Context ProtocolAI integration
Architect
Written by

Architect

Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.

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.