Spring AI’s Model Context Protocol: Architecture, Code Walkthrough & Debugging

This article provides a comprehensive analysis of Spring AI’s Model Context Protocol (MCP), covering its layered client‑server architecture, core interaction sequences, key source‑code components, and step‑by‑step debugging of the SSE‑based initialization flow, enabling developers to integrate AI capabilities into Java applications with confidence.

AI Architecture Hub
AI Architecture Hub
AI Architecture Hub
Spring AI’s Model Context Protocol: Architecture, Code Walkthrough & Debugging

Introduction

In the past year the large‑model ecosystem has shifted from simple model calls to collaborative, tool‑enabled interactions. Spring AI was created to embed AI capabilities into enterprise‑grade Java applications using the Model Context Protocol (MCP), which standardises how models communicate with external resources.

Architecture Overview

Spring AI MCP follows a highly symmetrical, layered design for both client and server. The layers are:

Entry Layer : Unified entry point represented by McpClient and McpServer. Factory methods create the runtime layer instances with configuration such as time‑outs and capabilities.

Runtime Layer : Core control centre implemented by classes like McpAsyncClient, McpSyncClient, McpAsyncServer and McpSyncServer. Handles initialization, tool registration and tool invocation, using reactive programming for async flows.

Session Management Layer (server‑only): Manages multiple client sessions via McpServerTransportProvider, handling lifecycle, broadcasting, and resource cleanup.

Session Layer : Context carrier between client and server, implemented by McpClientSession and McpServerSession. Encodes messages as JSON‑RPC, decoupling business logic from transport.

Transport Layer : Actual data transmission and (de)serialization, defined by McpClientTransport and McpServerTransport. Supports Stdio, HTTP + SSE, and other protocols.

Both sides share the same four layers (entry, runtime, session, transport); the server adds the session‑management layer. The following images illustrate the client and server layer diagrams:

Core Interaction Process

3.1 Client/Server Construction and Connection

The server is built by configuring the entry layer (e.g., time‑outs, capabilities) and invoking its factory method to obtain a runtime instance.

The client follows the same steps but also creates a session layer object and then calls the transport layer to establish an SSE connection. The server’s session‑management layer receives the GET request, creates a session object, and returns a message‑endpoint URL for subsequent interactions.

3.2 Initialization Process

Initialization consists of two phases: initialisation establishment and initialisation completion notification . During establishment the client sends a POST request containing a JSON‑RPC initialisation request; the server stores the session state as STATE_INITIALIZING, replies with server version and capabilities, and the transport layer pushes the response via SSE.

After the client receives the response it sends a second POST notification signalling completion. The server updates the session state to STATE_INITIALIZED and processes the notification (no further action in the default implementation).

3.3 Tool Discovery and Invocation

Tool discovery follows the same request‑response pattern: the client sends a JSON‑RPC request, the server responds with a list of available tools, and the client receives the list through the SSE transport.

Tool invocation is analogous; the server’s session layer executes the requested tool logic and returns the result via the transport layer.

Key Source Code Snippets

POM Dependencies

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>io.modelcontextprotocol.sdk</groupId>
  <artifactId>mcp</artifactId>
  <version>0.10.0</version>
</dependency>

Server Configuration

@Configuration
@EnableWebMvc
public class McpServerConfig implements WebMvcConfigurer {
    @Bean
    public HttpServletSseServerTransportProvider servletSseServerTransportProvider() {
        return HttpServletSseServerTransportProvider.builder()
                .objectMapper(new ObjectMapper())
                .messageEndpoint("/mcp/message")
                .build();
    }
    @Bean
    public ServletRegistrationBean<HttpServletSseServerTransportProvider> customServletBean(HttpServletSseServerTransportProvider transportProvider) {
        return new ServletRegistrationBean<>(transportProvider);
    }
}

Main Application

@SpringBootApplication
public class McpServerApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(McpServerApplication.class, args);
        HttpServletSseServerTransportProvider transportProvider =
                context.getBean(HttpServletSseServerTransportProvider.class);
        var schema = """
            {"type":"object","id":"urn:jsonschema:Operation","properties":{
                "operation":{"type":"string"},
                "a":{"type":"number"},
                "b":{"type":"number"}}}
            """;
        var syncToolSpecification = McpServerFeatures.SyncToolSpecification(
                new McpSchema.Tool("calculator", "Basic calculator", schema),
                (exchange, arguments) -> {
                    String operation = (String) arguments.get("operation");
                    double a = Double.parseDouble(String.valueOf(arguments.get("a")));
                    double b = Double.parseDouble(String.valueOf(arguments.get("b")));
                    double resultValue;
                    switch (operation) {
                        case "add": resultValue = a + b; break;
                        case "subtract": resultValue = a - b; break;
                        case "multiply": resultValue = a * b; break;
                        case "divide":
                            if (b == 0) return new McpSchema.CallToolResult("Error: Division by zero", true);
                            resultValue = a / b; break;
                        default:
                            return new McpSchema.CallToolResult("Error: Unknown operation '" + operation + "'", true);
                    }
                    return new McpSchema.CallToolResult(String.valueOf(resultValue), false);
                });
        McpSyncServer syncServer = McpServer.sync(transportProvider)
                .serverInfo("my-server", "1.0.0")
                .capabilities(McpSchema.ServerCapabilities.builder()
                        .resources(true, true)
                        .tools(true)
                        .prompts(true)
                        .logging()
                        .completions()
                        .build())
                .build();
        syncServer.addTool(syncToolSpecification);
    }
}

Runtime Breakpoint Tracing

The article demonstrates how to set breakpoints in the server’s HttpServletSseServerTransportProvider.doPost() method to observe the initialization establishment and completion notifications. Screenshots (omitted here) show the POST request to /mcp/message, the handling in handleIncomingRequest(), state transition to STATE_INITIALIZING, and the final SSE response sent by HttpServletMcpSessionTransport.sendMessage(). The completion notification follows a similar path, ending with the session state set to STATE_INITIALIZED.

Summary and Reflections

Spring AI’s MCP implementation showcases a highly consistent and elegantly abstracted architecture. The symmetrical client‑server design, clear separation of concerns across layers, and the use of JSON‑RPC over SSE provide a low‑coupling yet powerful framework for building AI‑augmented services. This design aligns with Spring’s “interface‑driven” philosophy and positions MCP as a potential foundational component for future modular AI platforms.

Understanding the source code and runtime behaviour of MCP equips developers with the knowledge to extend the protocol—such as adding custom transports, new tool‑registration mechanisms, or additional capabilities—making it a versatile bridge between large language models and enterprise systems.

References

Spring AI MCP浅析 – CSDN Blog

SpringAI(GA):MCP源码解读

GitHub – modelcontextprotocol/sdk/java

Deep Dive into MCP Protocol – Bilibili

modelcontextprotocol.io/sdk/java/mc…

debuggingJavaMCPSpring AIAI integration
AI Architecture Hub
Written by

AI Architecture Hub

Focused on sharing high-quality AI content and practical implementation, helping people learn with fewer missteps and become stronger through AI.

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.