Popular searches
//

Building MCP Servers with Spring AI

30.11.2026 | 4 minutes reading time

Introduction

The Model Context Protocol (MCP) is an open standard that defines how AI models communicate with external tools, services, and data sources. It replaces ad-hoc integrations with a single, well-defined JSON-RPC 2.0 protocol, making it easy to connect any MCP-compatible AI client to any MCP server.

With Spring AI 2.0, building an MCP server is as simple as annotating a Spring bean method — no manual schema authoring, no boilerplate transport code. This article walks through a complete example: a Spring Boot 4 application that exposes MCP tools and resources, and a companion client that calls them interactively from the command line.

The full source code is available on GitHub.

MCP Services

An MCP server exposes three types of primitives:

PrimitiveDescription
ToolsCallable functions with structured parameters and return values
ResourcesRead-only, URI-addressable content (files, records, live data)
PromptsReusable prompt templates with named parameters

This article covers tools and resources. The transport used is Streamable HTTP: a single POST /mcp endpoint that speaks JSON-RPC, making it straightforward to test with curl or the official MCP Inspector UI.

Project Setup

The project is a multi-module Maven build with two modules:

ai/
├── pom.xml                  ← parent POM
├── mcp-server/              ← Spring Boot app, port 8080
└── mcp-client/              ← Spring Boot app, interactive REPL

Parent POM (relevant excerpts):

1<parent>
2    <groupId>org.springframework.boot</groupId>
3    <artifactId>spring-boot-starter-parent</artifactId>
4    <version>4.0.6</version>
5</parent>
6
7<properties>
8    <java.version>21</java.version>
9    <spring-ai.version>2.0.0-M5</spring-ai.version>
10</properties>
11
12<dependencyManagement>
13    <dependencies>
14        <dependency>
15            <groupId>org.springframework.ai</groupId>
16            <artifactId>spring-ai-bom</artifactId>
17            <version>${spring-ai.version}</version>
18            <type>pom</type>
19            <scope>import</scope>
20        </dependency>
21    </dependencies>
22</dependencyManagement>

Building the MCP Server

The server module pulls in a single starter:

1<dependency>
2    <groupId>org.springframework.ai</groupId>
3    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
4</dependency>

Configuring the Transport

1# mcp-server/src/main/resources/application.yml
2spring:
3  ai:
4    mcp:
5      server:
6        name: ai-mcp-server
7        version: 1.0.0-SNAPSHOT
8        type: SYNC
9        protocol: STREAMABLE
10server:
11  port: 8080

protocol: STREAMABLE selects the Streamable HTTP transport. Spring AI wires up the POST /mcp endpoint automatically — no controller code needed.

Exposing Tools with @McpTool

Tools are plain Spring beans. Annotate a method with @McpTool and Spring AI generates the JSON schema for the parameters automatically:

1@Component
2public class PolicyNumberTools {
3
4  private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
5
6  @McpTool(description = "Creates a new policy number")
7  public String createPolicyNumber(
8          @McpToolParam(description = "Id of the LOB (line of business)", required = true)
9          String lobId) {
10    String timestamp = LocalDateTime.now().format(FORMATTER);
11    String randomPart = RandomStringUtils.randomAlphanumeric(5);
12    return (lobId + timestamp + randomPart).toUpperCase();
13  }
14
15}

At startup, Spring AI logs Registered tools: 1 — that is all that is needed.

Exposing Resources with @McpResource

Resources are identified by a URI and return their content as a string. The annotation works the same way as @McpTool:

1@Component
2public class DemoResource {
3
4    @McpResource(
5            uri = "file:///demo.txt",
6            name = "demo-text",
7            description = "Demo text resource loaded from the classpath"
8    )
9    public String demoText() {
10        try {
11            return new ClassPathResource("demo.txt")
12                    .getContentAsString(StandardCharsets.UTF_8);
13        } catch (IOException e) {
14            throw new RuntimeException("Failed to read demo.txt", e);
15        }
16    }
17}

The uri attribute is the protocol-level identifier returned to clients in resources/list. Its scheme (file:///, db://, etc.) is free to choose — it carries no technical meaning for the file access; ClassPathResource handles the actual reading.

Testing with MCP Inspector

The official MCP Inspector is an easy way to explore our MCP server without writing any client code:

1npx @modelcontextprotocol/inspector http://localhost:8080/mcp

Select Streamable HTTP as the transport type. The UI shows all registered tools and resources and lets you call them directly from the browser.

Building your own MCP Client

Spring AI also supports writing your own MCP clients. The client module depends on:

1<dependency>
2    <groupId>org.springframework.ai</groupId>
3    <artifactId>spring-ai-starter-mcp-client</artifactId>
4</dependency>

Client Configuration

1# mcp-client/src/main/resources/application.yml
2spring:
3  main:
4    web-application-type: none
5  ai:
6    mcp:
7      client:
8        enabled: true
9        type: SYNC
10        request-timeout: 30s
11        streamable-http:
12          connections:
13            demo:
14              url: http://localhost:8080
15              endpoint: /mcp

Calling the Server

Spring AI auto-configures a List<McpSyncClient> bean. Inject it and use the first (and only) client:

1@Service
2public class McpClientService {
3
4    private final McpSyncClient mcpClient;
5
6    public McpClientService(List<McpSyncClient> mcpClients) {
7        this.mcpClient = mcpClients.getFirst();
8    }
9
10    public List<McpSchema.Tool> listTools() {
11        return mcpClient.listTools().tools();
12    }
13
14    public List<McpSchema.Resource> listResources() {
15        return mcpClient.listResources().resources();
16    }
17
18    public String callTool(String toolName, Map<String, Object> arguments) {
19        McpSchema.CallToolResult result = mcpClient.callTool(
20                new McpSchema.CallToolRequest(toolName, arguments));
21        return result.content().stream()
22                .filter(c -> c instanceof McpSchema.TextContent)
23                .map(c -> ((McpSchema.TextContent) c).text())
24                .findFirst()
25                .orElse("");
26    }
27}

Interactive REPL

A CommandLineRunner wraps a simple Scanner loop, making the client usable as a command-line tool without any additional framework:

mcp-client> list-tools
  createPolicyNumber        Creates a new policy number

mcp-client> create-policy-number HR
HR20260511103235X0WJT

mcp-client> list-resources
  file:///demo.txt                    Demo text resource loaded from the classpath

Agent Smith - it's your turn

In times where more and more agents appear at your IT landscape, MCP will be the protocol to integrate them with your existing applications. If a significant part of your applications use the Spring (Boot) ecosystem, Spring AI will be the tool of your choice.

From an architectural viewpoint there are at last two major patterns how do introduce MCP readiness. You can either create a thin layer into your existing business services to expose additional MCP endpoints ...

mcp_int_scenario1.png

... or you add a separate adapter/gateway application in between:

mcp_int_scenario2.png

Further Reading

If you are interested in MCP in general, maybe these blog post are worth a look:

share post

//

More articles in this subject area

Discover exciting further topics and let the codecentric world inspire you.