Quick Start
The details of MCP protocol is out of scope of this book. The MCP protocol itself is not complicated. We just need to understand the methods that client and the server can use. Each method has fixed formats for parameters and return value.
Java Development Basics
MCP provides SDKs that can be used in different programming languages. These SDKs encapsulates the implementation details of the underlying protocol, which simplifies the development of servers and clients.
When developing an MCP server or client in Java, we can use the official SDKs. There are currently two SDKs available for Java and Kotlin.
When using MCP with Spring AI, the SDK version required by Spring AI should be used. This book uses the Java SDK of version 1.1.1.
In order to add the MCP SDK dependencies to the project, we can import the mcp-bom dependency first. This removes the need to declare the version numbers of the different modules in MCP SDK.
1 <dependency>
2 <groupId>io.modelcontextprotocol.sdk</groupId>
3 <artifactId>mcp-bom</artifactId>
4 <version>1.1.1</version>
5 <type>pom</type>
6 <scope>import</scope>
7 </dependency>
The MCP SDK contains several modules, and in most cases we will only need to use the mcp module. This module already contains the MCP server and client implementations, as well as the basic implementation of stdio and HTTP transports.
1 <dependency>
2 <groupId>io.modelcontextprotocol.sdk</groupId>
3 <artifactId>mcp</artifactId>
4 </dependency>
The SDK supports both synchronous and asynchronous programming modes. There is no functional difference between synchronous and asynchronous programming modes. The only difference is that in asynchronous programming mode, objects such as Flux and Mono from the Reactor project are used as the return values of these methods. If you have used Spring WebFlux before, you should be familiar with the use of Flux and Mono. If you’re not familiar with Flux and Mono, you’ll have no problem using the synchronous programming model. In order to reduce complexity, the code in this book focuses on the synchronous programming model and does not require familiarity with Flux and Mono.
Sample MCP Server
There are four different implementations for each of the MCP servers and clients, taking into account the transport and programming modes. For example, a stdio server implemented in synchronous programming mode, or an HTTP client implemented in asynchronous programming mode. Synchronous or asynchronous mode is only an internal implementation detail and does not affect the external use.
In total, there are 4 different development scenarios. These are stdio and HTTP transport for MCP servers, and stdio and HTTP transport for MCP clients.
A simple MCP server will be created as an example. The server provides a simple tool that the client can call.
The functionality of this tool is very simple. When using the HTTP Basic authentication method, given a username and password, they are encoded using base64 format. The encode value is used as the value of the HTTP Authorization header.
For example, given the username admin and password password, value of the header is the base64 encoding of admin:password.
The goal of this tool is to calculate this value. The tool takes two parameters, username and password, which represent the username and password, respectively. The return value of the tool contains only one value, which represents the value of the Authorization header.
stdio Server
Let’s start from the stdio transport of MCP server and client. In this approach, the server is packaged into a standalone JAR file. The client runs this JAR file to start the server and then interacts with the standard input and output streams of the server process. The client writes messages to the server’s input stream and reads messages from the server’s output stream. The basic process is similar for both the server and the client. Inside a loop, messages are first read from the input, then processed, and finally written to the output.
Create Server
The first step is to create a server that uses stdio transport. The MCP server is represented by the McpServer interface, which has two static methods, sync and async, to create a server in synchronous and asynchronous modes, respectively. The parameters of these methods are the McpServerTransportProvider interface, which indicates the transport mode of the server. stdio transport mode uses StdioServerTransportProvider.
The return value of the sync method of McpServer interface is SyncSpecification, which is a builder used to create the server. The methods in SyncSpecification configure the server, then the build method is used to finish building the server.
The following describes the methods to configure the server.
The
serverInfomethod sets the basic information of the server, including the server name and version number.The
capabilitiesmethod sets the capabilities of the server, represented byServerCapabilities.
ServerCapabilities is a record type with attributes corresponding to the different capabilities of the server in the MCP protocol. Each capability has its own corresponding record type. If a capability has sub-capabilities, the corresponding record type contains attributes to set the sub-capabilities. For example, ToolCapabilities, which represents the tools capability, contains the sub-capability listChanged, while LoggingCapabilities, which represents the logging capability, does not contain any attributes. ServerCapabilities provides a builder. It is a common practice to first create a builder using the builder method, then use the build method to get a ServerCapabilities object after the configuration is complete.
The example MCP server only declares support for the tools capability. The parameter false indicates that the sub-capability listChanged is not supported.
After setting the ServerCapabilities, we can use the build method to build the server. At this point, the server only supports the tools capability, and no tools have been added.
1 public class SimpleStdioServer {
2
3 public void start() {
4 var provider = new StdioServerTransportProvider();
5 McpServer.sync(provider)
6 .serverInfo("Sample", "1.0.0")
7 .capabilities(ServerCapabilities.builder()
8 .tools(false)
9 .build())
10 .build();
11 }
12
13 public static void main(String[] args) {
14 new SimpleStdioServer().start();
15 }
16 }
Add Tool
The next step is to add the previously mentioned tool that generates the HTTP basic authentication header. Tools can be added during the build phase or dynamically after the server has been started. Tools are also divided into synchronous and asynchronous types. Tools are referred to as SyncToolSpecification and AsyncToolSpecification, respectively. Synchronous servers can only use synchronous tools, while asynchronous servers can only use asynchronous tools.
SyncToolSpecification is a record type that contains the attributes tool and call. tool represents the metadata of the tool. call represents the code implementation of the tool call.
The attribute tool is of type Tool and has three attributes, name, description and inputSchema, which represent the name of the tool, the description and the JSON schema of the input parameters, respectively. The JSON schema of the input parameters can be represented either as an object or as a JSON string, which is automatically deserialized to an object.
The attribute call is of type BiFunction<McpSyncServerExchange, CallToolRequest, CallToolResult> and represents the code implementation of the tool with two parameters and a return value. The first parameter, McpSyncServerExchange, is used to interact with the client, where methods can call client methods.
A parameter of type CallToolRequest represents the request for the method call. It has the arguments attribute of Map<String, Object>, which contains the actual tool call parameters. The value of the input parameter is retrieved from this map.
The return value of a tool call is CallToolResult. CallToolResult has four attributes, content, isError, structuredContent, and meta.
contentrepresents a list of contents of the tool’s output.Contentis an interface that represents different types of content. The concrete implementations areTextContent,ImageContent, andEmbeddedResource, which represents text, image, and embedded resource types, respectively.isErrorindicates whether or not the result of the tool call was successful.structuredContentis an optional JSON object that represents the structured result of the tool call.metarepresents metadata which is part of MCP spec.
The following section describes how to implement the previously mentioned tool related to HTTP Basic authentication. The first part is the description of the tool. The tool’s input parameters contain two values, username and password, and the JsonSchema object is used to describe the method’s input parameters. When describing the method, we need to provide the type of the schema, the properties it contains, the names of the mandatory properties, whether it contains additional properties, and the type definitions in the schema. properties is a Map, and the values in the map are the sub-properties.
The implementation of the tool is shown below. In the definition of the tool, the JSON schema of the tool’s input parameters has been defined. Values of input parameters can be retrieved directly from the parameter map based on their names. After obtaining the values of username and password, the value of the Authorization header is generated according to Basic authentication specification.
SyncToolSpecification and Tool both have builders to create objects. Once we have the definition and implementation of the tool, we can create the SyncToolSpecification object corresponding to the tool and use the tools method of SyncSpecification to add the tool.
1 public static final SyncToolSpecification CALCULATE_HTTP_BASIC_AUTH_HEADER =
2 SyncToolSpecification.builder()
3 .tool(
4 Tool.builder()
5 .name("calculateHttpBasicAuthHeader")
6 .title("Calculate HTTP basic auth header")
7 .description(
8 "HTTP Authorization header for basic authentication")
9 .inputSchema(
10 new JsonSchema(
11 "object",
12 Map.of(
13 "username",
14 new JsonSchema(
15 "string", Map.of(), List.of(), false,
16 Map.of(), Map.of()),
17 "password",
18 new JsonSchema(
19 "string", Map.of(), List.of(), false,
20 Map.of(), Map.of())),
21 List.of("username", "password"),
22 false,
23 Map.of(),
24 Map.of()))
25 .outputSchema(
26 Map.of(
27 "type",
28 "object",
29 "properties",
30 Map.of(
31 "value",
32 new JsonSchema(
33 "string", Map.of(), List.of(), false,
34 Map.of(), Map.of()))))
35 .annotations(
36 new ToolAnnotations(null, true, false,
37 true, false, false))
38 .build())
39 .callHandler(
40 (exchange, request) -> {
41 var args = request.arguments();
42 String username = (String) args.get("username");
43 String password = (String) args.get("password");
44 String header =
45 "Basic "
46 + Base64.getEncoder()
47 .encodeToString(
48 (username + ":" + password)
49 .getBytes(StandardCharsets.UTF_8));
50 Map<String, Object> result = Map.of("value", header);
51 return CallToolResult.builder()
52 .structuredContent(result)
53 .addTextContent(toJson(result))
54 .build();
55 })
56 .build();
In the case of an asynchronous tool implementation, the corresponding type is AsyncToolSpecification. AsyncToolSpecification is also a record type. This record type also contains two attributes, tool and call. tool is also of type Tool. call is of type BiFunction<McpAsyncServerExchange, CallToolRequest, Mono<CallToolResult>>, which differs from the call attribute of SyncToolSpecification in that the return value of the tool call is of type Mono<CallToolResult>, indicating that this is an asynchronous tool call.
Once the build method of SyncSpecification has been called to build the server, the server is already running. By encapsulating the method call that starts the server in the main method, we have a Java program that can run as a MCP server.
Package Server
To simplify starting of the server, we package the Java program into a single executable JAR file. The Maven assembly plugin, which comes with its own jar-with-dependencies descriptor, is used here. We need to specify the name of the Java class to start.
1 <plugin>
2 <artifactId>maven-assembly-plugin</artifactId>
3 <version>3.7.1</version>
4 <executions>
5 <execution>
6 <phase>package</phase>
7 <goals>
8 <goal>single</goal>
9 </goals>
10 </execution>
11 </executions>
12 <configuration>
13 <archive>
14 <manifest>
15 <addClasspath>true</addClasspath>
16 <mainClass>com.javaaidev.mcp.server.SimpleStdioServer</mainClass>
17 </manifest>
18 </archive>
19 <descriptorRefs>
20 <descriptorRef>jar-with-dependencies</descriptorRef>
21 </descriptorRefs>
22 </configuration>
23 </plugin>
After packaging with the mvn package command, the corresponding JAR file is generated and can be run directly with the java command.
Test Server
The final step is to test the MCP server. A separate tool MCP Inspector is used here.
To run this tool, we need NodeJS support, and use the following command to run it.
1 npx @modelcontextprotocol/inspector node build/index.js
Once it is running, open a browser and access port 6274 on localhost to see the web interface.
During the test, we need to select the connection method of the MCP server, and since we are using stdio, we need to specify the way to start the server. Here we use the java command, and provide relevant command line parameters, including -jar and the path to the generated JAR file.
Click the Connect button to connect to the server. After connecting to the server, switch to the Tools tab and click List Tools to list all the tools. We can see the tool used as an example. Select the tool and we will see the input boxes for the parameters username and password. Enter the values for these two parameters and click Run Tool to invoke the tool and see the results of the tool invocation.

This completes the development and testing of a MCP server using stdio.
HTTP SSE Server
After introduction of stdio transport, HTTP transport is described below. The HTTP transport has been changed between version 2025-03-26 and 2024-11-05.The MCP Java SDK 1.1.1 supports the HTTP SSE transport in version 2024-11-05. The choice of transport doesn’t affect functionalities of MCP server. We can easily switch to HTTP transport from stdio transport.
Servers using the HTTP transport method are created in the same way as servers using stdio, only the transport method is different. The implementation class for the HTTP transport method is HttpServletSseServerTransportProvider, which is also an implementation of the McpServerTransportProvider interface. HttpServletSseServerTransportProvider provides a builder, and there are several configuration properties that can be set.
baseUrl, the base url to access the HTTP server, defaults to an empty string.messageEndpoint, the path to the HTTP endpoint for receiving messages.sseEndpoint, the path to the SSE endpoint, default is/sse.jsonMapper, theMcpJsonMapperused for JSON serialization, default value is created fromMcpJsonDefaults.getMapper().contextExtractor, theMcpTransportContextExtractorused for extracting metadata from the request.keepAliveInterval, the interval for keep-alive pings.securityValidator, theServerTransportSecurityValidatorfor validating HTTP requests.
Of the above parameters, only messageEndpoint is required.
The following code creates a new HttpServletSseServerTransportProvider by specifying the value of messageEndpoint.
1 var provider = HttpServletSseServerTransportProvider.builder()
2 .messageEndpoint("/messages")
3 .build();
The sync method of McpServer is called with the provider as an argument, and the rest of the configuration is the same as the stdio transport. The complete code is shown below.
1 var provider = HttpServletSseServerTransportProvider.builder()
2 .messageEndpoint("/messages")
3 .build();
4
5 McpServer.sync(provider)
6 .serverInfo("Sample", "1.0.0")
7 .capabilities(ServerCapabilities.builder()
8 .logging()
9 .tools(false)
10 .build())
11 .tools(Tools.CALCULATE_HTTP_BASIC_AUTH_HEADER)
12 .build();
Unlike the stdio transport, the HttpServletSseServerTransportProvider cannot run independently because it is only a servlet implementation and requires runtime support from the servlet container. There are three different scenarios depending on how the MCP server is used.
The first is a minimalist deployment approach that does not rely on other web frameworks and only runs the MCP server’s servlet. In this approach, an embedded servlet container such as Tomcat or Jetty needs to be started to run as a separate process.
The second is to use Spring WebMVC or WebFlux. The Java SDK for MCP provides integration modules with Spring that can be used directly. If you need to extend MCP functionality for an existing Spring web project, It’s recommended to use this approach to provide a new MCP access endpoint on top of the existing HTTP service.
The third is to deploy to a shared web server , such as IBM WebSphere or RedHat JBoss. The MCP server is typically packaged as a WAR file to deploy.
Here I introduce the first way which uses an embedded Jetty to start the MCP server. The code is shown below. The overall logic is relatively simple. It creates a Jetty server, adds the HttpServletSseServerTransportProvider servlet, and finally starts Jetty server.
1 var server = new Server(9000);
2 var context = new ServletContextHandler();
3 context.setContextPath("/");
4 context.addServlet(provider, "/*");
5 server.setHandler(context);
6 server.start();
7 server.join();
The implementation of this server is also packaged into a JAR file to run independently.
The tests also use MCP Interceptor. When connecting to the server, select the HTTP transport method and specify the hostname, port and context path of the HTTP server. Once connected, we can test the server in the same way as the stdio transport.

MCP Client
Like servers, MCP clients can use both stdio and HTTP transports. Clients are created in a similar way to servers.
The static methods sync and async in the interface McpClient create synchronous and asynchronous clients, respectively. The arguments to these methods are the McpClientTransport interface, which represents the transport used by the client. The sync method returns a SyncSpec, which is a builder that provides methods to set different properties, and finally calls the build method to complete the creation.
The stdio transport uses StdioClientTransport.
StdioClientTransport needs to provide ServerParameters when it is created.ServerParameters specifies the way to start the MCP server. We need to provide the command to start the server, the parameter values, and the values of the environment variables. ServerParameters provides a builder to create objects.
The ServerParameters value is used to start an external process to the server, using ProcessBuilder from the Java Standard Library.
As an example client, we can use the java -jar command to start the MCP server described earlier. In the following code, jarFilePath is the path to the JAR file.
1 var transport = new StdioClientTransport(
2 ServerParameters.builder("java")
3 .args("-jar", jarFilePath)
4 .build());
After completing the creation of the client, we can connect to the server and invoke the tool according to the client-server interaction process. This process is divided into 3 steps.
The first step is initialization, which is done by calling the client’s initialize method. From the result of the method call, we can view information about the server and the capabilities it provides.
The second step is the normal interaction when the client calls different methods. For example, the callTool method calls a tool. The parameters of the call are the name of the method and the values of the input parameters. The return value is the result of the tool call.
The last step is to close the client. The client implements Java’s AutoClosable interface, which can be placed in a try-with-resource to enable automatic closing of the client. It can also be closed manually by calling the close method. In addition to the close method, there is also a closeGracefully method to gracefully close the client. closeGracefully waits for the operation currently in progress to finish processing.
The following is the complete sample code for the MCP client. It first creates a StdioClientTransport and starts the MCP server with the java command. The sync method of McpClient is used to create a synchronous client. The initialize method is called to complete the initialization. After initialization, the callTool method is used to call the tools provided by the server. Finally the client is automatically closed by using a try-with-resource.
1 public class SimpleStdioClient {
2
3 private static final Logger LOGGER = LoggerFactory.getLogger(
4 SimpleStdioClient.class);
5
6 public void connect(String jarFilePath) {
7 var transport = new StdioClientTransport(
8 ServerParameters.builder("java")
9 .args("-jar", jarFilePath)
10 .build());
11 try (var client = McpClient.sync(transport).build()) {
12 var initializeResult = client.initialize();
13 LOGGER.info("Client initialized: {}", initializeResult);
14 var callToolResult = client.callTool(
15 new CallToolRequest("calculateHttpBasicAuthHeader", Map.of(
16 "username", "admin",
17 "password", "password"
18 )));
19 LOGGER.info("Tool call callToolResult: {}", callToolResult);
20 }
21 }
22
23 public static void main(String[] args) {
24 if (args.length < 1) {
25 System.out.println("JAR file is required");
26 return;
27 }
28 var client = new SimpleStdioClient();
29 client.connect(args[0]);
30 }
31 }
The HTTP client is used in much the same way as stdio. The only difference is that when calling the sync or async method of McpClient, the argument provided is a transport of type HttpClientSseClientTransport. HttpClientSseClientTransport provides a builder to create the object. When creating a HttpClientSseClientTransport, the baseUri is required. baseUri represents the address of the HTTP server to which the connection is made. Once the client is created, the interaction with the server is the same as the stdio transport described previously.
The complete code for using an HTTP client is shown below. The rest of the code is identical to the stdio client except for the creation of the transport.
1 public class SimpleHttpClient {
2
3 private static final Logger LOGGER = LoggerFactory.getLogger(
4 SimpleHttpClient.class);
5
6 public void connect(String baseUri) {
7 var transport = HttpClientSseClientTransport.builder(baseUri)
8 .build();
9 try (var client = McpClient.sync(transport).build()) {
10 var initializeResult = client.initialize();
11 LOGGER.info("Client initialized: {}", initializeResult);
12 var callToolResult = client.callTool(
13 new CallToolRequest("calculateHttpBasicAuthHeader", Map.of(
14 "username", "admin",
15 "password", "password"
16 )));
17 LOGGER.info("Tool call callToolResult: {}", callToolResult);
18 }
19 }
20
21 public static void main(String[] args) {
22 if (args.length < 1) {
23 System.out.println("Base url is required");
24 return;
25 }
26 var client = new SimpleHttpClient();
27 client.connect(args[0]);
28 }
29 }
Spring Integration
If you are using the Spring framework to develop an MCP server or client, you can use the integration modules provided by Spring to further simplify development.
There are two Spring modules for the HTTP transport, including Spring WebMVC and Spring WebFlux. These two modules can be used directly if Spring is already used in the project.
These two modules use org.springframework.ai as the group id, see Maven dependency below.
1 <dependency>
2 <groupId>org.springframework.ai</groupId>
3 <artifactId>mcp-spring-webmvc</artifactId>
4 </dependency>
Spring WebMVC
The Spring WebMVC implementation is included in the module mcp-spring-webmvc, which contains the HTTP server transport WebMvcSseServerTransportProvider. The WebMvcSseServerTransportProvider is created using the builder, which provides the the configuration properties the same as for the servlet transport, namely jsonMapper, baseUrl, messageEndpoint, sseEndpoint, keepAliveInterval, contextExtractor, and securityValidator.
After creating the WebMvcSseServerTransportProvider, we can call the getRouterFunction method to get a RouterFunction object. This RouterFunction is added as a Spring bean to enable the MCP HTTP server.
1 @Configuration
2 public class AppConfiguration {
3
4 @Bean
5 public RouterFunction<ServerResponse> mcpServerRouterFunction() {
6 var provider = WebMvcSseServerTransportProvider.builder()
7 .messageEndpoint("/message").build();
8 McpServer.sync(provider)
9 .serverInfo("spring-webmvc-server", "1.0.0")
10 .capabilities(ServerCapabilities.builder()
11 .tools(false)
12 .build())
13 .tools(Tools.CALCULATE_HTTP_BASIC_AUTH_HEADER)
14 .build();
15 return provider.getRouterFunction();
16 }
17 }
Spring WebFlux
The Spring WebFlux implementation is included in the module mcp-spring-webflux, which contains both the server and client implementations of the HTTP transport. The server implementation of the HTTP transport is the WebFluxSseServerTransportProvider, which can be created using the builder and provides the same configuration properties as the WebMvcSseServerTransportProvider.
WebFluxSseServerTransportProvider also has a getRouterFunction method to get the RouterFunction object, which needs to be added as a Spring bean.
1 @Configuration
2 public class AppConfiguration {
3
4 @Bean
5 public RouterFunction<?> mcpServerRouterFunction() {
6 var provider = WebFluxSseServerTransportProvider.builder()
7 .messageEndpoint("/message")
8 .build();
9 McpServer.sync(provider)
10 .serverInfo("spring-webflux-server", "1.0.0")
11 .capabilities(ServerCapabilities.builder()
12 .tools(false)
13 .build())
14 .tools(Tools.CALCULATE_HTTP_BASIC_AUTH_HEADER)
15 .build();
16 return provider.getRouterFunction();
17 }
18 }
Client
The implementation of the client-side HTTP transport, WebFluxSseClientTransport, is also created using the builder. The builder has the following configuration properties.
WebClientBuilderof typeWebClient.Builder, which is used to create the WebClient that sends out the HTTP requests.McpJsonMapperwith a default value retrieved fromMcpJsonDefaults.getMapper().sseEndpointwith a default value of/sse.
By using the implementation of the client-side HTTP transport provided by Spring, we can connect to a server that also uses HTTP. The complete code is shown below. The implementation is similar to the previously described HTTP client.
1 public class SpringHttpClient {
2
3 private static final Logger LOGGER = LoggerFactory.getLogger(
4 SpringHttpClient.class);
5
6 public void connect(String baseUrl) {
7 var transport = WebFluxSseClientTransport.builder(
8 WebClient.builder().baseUrl(baseUrl)).build();
9 try (var client = McpClient.sync(transport).build()) {
10 var initializeResult = client.initialize();
11 LOGGER.info("Client initialized: {}", initializeResult);
12 var callToolResult = client.callTool(
13 new CallToolRequest("calculateHttpBasicAuthHeader", Map.of(
14 "username", "admin",
15 "password", "password"
16 )));
17 LOGGER.info("Tool call callToolResult: {}", callToolResult);
18 }
19 }
20
21 public static void main(String[] args) {
22 if (args.length < 1) {
23 System.out.println("Base url is required");
24 return;
25 }
26 var client = new SpringHttpClient();
27 client.connect(args[0]);
28 }
29 }
Spring AI Integration
Spring AI provides deeper integration with MCP and can leverage the auto-configuration capabilities of Spring Boot, further simplifying the development of MCP servers and clients.
Server
To quickly create an MCP server, we can use Spring Boot and add the Spring Boot starter spring-ai-starter-mcp-server. We need to add the relevant dependencies based on the transport. For the HTTP transport, we should add the Spring Boot web starter and the Spring integration module in the MCP SDK.
Below is the Maven dependencies used for Spring AI MCP server with WebMVC.
1 <dependency>
2 <groupId>org.springframework.ai</groupId>
3 <artifactId>spring-ai-starter-mcp-server</artifactId>
4 </dependency>
5 <dependency>
6 <groupId>org.springframework.ai</groupId>
7 <artifactId>mcp-spring-webmvc</artifactId>
8 </dependency>
9 <dependency>
10 <groupId>org.springframework.boot</groupId>
11 <artifactId>spring-boot-starter-web</artifactId>
12 </dependency>
The advantage of using Spring Boot is that we can define the behavior of the MCP server through configuration without writing code.
The prefix for MCP server-related configuration properties is spring.ai.mcp.server. Supported configuration properties are listed as below.
enabled: Whether to enable the server. The default value istrue.stdio: Whether to enable the stdio transport. The default value isfalse.name: The name of the MCP server. The default value ismcp-server.version: The version of the MCP server. The default value is1.0.0.instructions: Additional instructions provided by the MCP server to the client. The default value isnull.baseUrl: The context path for the HTTP transport. The default value is an empty string.sseEndpoint: The context path for SSE. The default value is/sse.sseMessageEndpoint: The context path for SSE message handling. This is valid only for WebMVC and WebFlux. The default value is/mcp/message.type: The server type. The possible values areSYNCandASYNC, indicating asynchronous and synchronous modes, respectively. The default value isSYNC.promptChangeNotification: Whether to enable notifications when the list of prompt templates changes. The default value istrue.resourceChangeNotification: Whether to enable notifications when the list of resources changes. The default value istrue.toolChangeNotification: Whether to enable notifications when the list of tools changes. The default value istrue.toolResponseMimeType: AMap<String, String>type that specifies the MIME type for each tool’s return value. The map key is the tool name, and the corresponding value is the MIME type.
The prompt templates, resources, and tools included in the MCP server are created as Spring beans. We can simply define beans of type SyncPromptSpecification, SyncResourceSpecification, and SyncToolSpecification in Spring configuration. These entities will be automatically added to the MCP server. If the server type is ASYNC, the added beans will be of the corresponding asynchronous type.
To customize the capabilities of the MCP server, we can add a bean of type ServerCapabilities.Builder. Spring Boot automatically configures a ServerCapabilities.Builder object, which determines whether to enable corresponding capabilities based on whether it contains beans of certain types. For example, if a bean of type SyncToolSpecification or AsyncToolSpecification is found, the tools capability is enabled. If you dynamically add prompt templates, resources, or tools to the server, you typically need to explicitly enable the corresponding capabilities by adding a bean of type ServerCapabilities.Builder.
Spring provides two MCP HTTP servers: WebMVC and WebFlux.
- To enable WebMVC, add the
mcp-spring-webmvcandspring-boot-starter-webmodules as dependencies. Set the value ofspring.ai.mcp.server.transportproperty toWEBMVC. - To enable WebFlux, add the
mcp-spring-webfluxandspring-boot-starter-webfluxmodules as dependencies. Set the value ofspring.ai.mcp.server.transportproperty toWEBFLUX.
Below is a sample configuration of MCP server.
1 spring:
2 ai:
3 mcp:
4 server:
5 name: sample-server
6 version: 1.0.0
7 transport: WEBMVC
Client
To integrate the MCP client, add the spring-ai-starter-mcp-client Spring Boot starter to Spring Boot project.
We can configure the created MCP client. Configuration properties are prefixed with spring.ai.mcp.client. Supported configuration properties are listed as below.
enabled: Whether the client is enabled. The default value istrue.name: The name of the MCP client. The default value isspring-ai-mcp-client.version: The version of the MCP client. The default value is1.0.0.initialized: Whether to initialize the client. The default value istrue.requestTimeout: The timeout for client requests. The default value is20seconds.type: The client type. Optional values areSYNCandASYNC. The default value isSYNC.rootChangeNotification: Whether to enable notifications when the root path list changes. The default value istrue.toolcallback.enabled: Whether to automatically integrate the tools provided by the MCP server. The default value isfalse.
The prefix of configuration properties for connecting to the stdio server is spring.ai.mcp.client.stdio.
The MCP client can connect to multiple stdio servers. The server configuration is contained in connections, which is of type Map<String, Parameters>. Parameters represents the parameters for starting the MCP server. The configuration properties are listed as below.
command: The command to start the server.args: The command line arguments used to start the server.env: The environment variables used when starting the server. The type isMap<String, String>.
The prefix of configuration properties for connecting to an HTTP server is spring.ai.mcp.client.sse.
We can also connect to multiple HTTP SSE servers. Server configuration is contained in connections, which is a Map<String, SseParameters> object. SseParameters represents the parameters for connecting to the HTTP SSE server. The only configuration property is url, which represents the server URL.
Below is a sample configuration of MCP client.
1 spring:
2 ai:
3 mcp.client:
4 type: SYNC
5 toolcallback:
6 enabled: true
7 sse:
8 connections:
9 defaultServer:
10 url: http://localhost:9100
After the MCP client connects to different MCP servers, tools found from the MCP server can be automatically registered with Spring AI. When using Spring AI’s ChatClient to send requests to a large language model, we can specify the name of the tool found from the MCP server. When Spring AI calls a tool, the tool call is sent from the MCP client to the MCP server.
There are two points to note here. First, Spring AI’s integration with the tools provided by the MCP server is disabled by default. To do this, we need to set the configuration property toolcallback.enabled to true. Second, Spring AI adds a prefix to the tool names found from the MCP server to prevent name conflicts. This prefix is the name of the MCP client. When sending requests using ChatClient, you must provide the prefixed tool name; otherwise, you will receive a tool not found error.