eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

1. Overview

With recent developments around AI, more and more tools and systems are integrating with AI models. However, this poses a challenge, as each implementation defines its own standards for incorporating external tools, resources, and systems with the AI models.

The Model Context Protocol (MCP) is an open-source standard that defines the integration of AI applications, LLM models, image generators, etc., with tools, data sources, and other resources. This enables AI applications to access data, use tools, and execute workflows as defined in the external systems.

The Java SDK provides developers with a set of libraries supporting multiple protocols and mechanisms to connect with AI applications.

In this tutorial, we’ll explore the SDK and perform a simple test using MCP.

2. Architecture

The key components of MCP architecture include the following:

  • MCP Host, which manages multiple MCP Clients
  • MCP Client that receives context for the MCP Server for the MCP host to use
  • MCP Server, which provides context to MCP Clients

MCP defines communication through two conceptual layers. The Data Layer, which defines the protocol for client-server communication and life-cycle management, and the Transport Layer, which defines the communication channels and mechanisms used by the client and server.

The Java SDK for MCP maps these concepts into the following layers:

  • Client/Server Layer, which implements and manages the client/server operations using McpClient/McpServer
  • Session Layer manages communication patterns and state via McpSession
  • Transport Layer handles message serialization and deserialization with McpTransport

The clients invoke one or more tools exposed by an MCP server, and the communication occurs via the transport layer.

Primitives are fundamental building blocks of MCP. They define the type of contextual information and range of actions that can be performed. Both the server and the client offer some primitives.

Server primitives include tools, resources, and prompts. Tools are executable functions that AI applications can use to perform actions, such as querying a database, file operations, etc. Resources are data sources that provide contextual information to clients, like database schemas, file contents, etc. Prompts are reusable templates that help with interactions with the language models.

The Client primitives, on the other hand, allow McpServer authors to build richer interactions and include sampling, elicitation, and logging. Sampling allows servers to request language model completions from clients when they don’t want to include the model SDK on the server itself. Elicitation gives servers a way to request additional information from the user or confirm any action. Logging allows servers to send log messages to clients for debugging and monitoring

3. Setup

To use the SDK, we’ll use the mcp dependency:

<dependency>
    <groupId>io.modelcontextprotocol.sdk</groupId>
    <artifactId>mcp</artifactId>
    <version>0.15.0</version>
</dependency>

3.1. Defining an MCP Tool

We’ll define a simple MCP tool that will print the received prompt via the LoggingTool class, which returns a SyncToolSpecification:

public class LoggingTool {

    public static McpServerFeatures.SyncToolSpecification logPromptTool() {
        McpSchema.JsonSchema inputSchema = new McpSchema.JsonSchema("object",
            Map.of("prompt", String.class), List.of("prompt"), false, null, null);

        return new McpServerFeatures.SyncToolSpecification(
            new McpSchema.Tool(
              "logPrompt", "Log Prompt","Logs a provided prompt", inputSchema, null, null, null),
                (exchange, args) -> {
                    String prompt = (String) args.get("prompt");
                    return McpSchema.CallToolResult.builder()
                      .content(List.of(new McpSchema.TextContent("Input Prompt: " + prompt)))
                      .isError(false)
                      .build();
                });
    }
}

Initially, we’ve defined the input schema, which creates a clear contract for user input. Next, this input schema is used to instantiate a Tool, which extracts the prompt argument and finally returns a TextContent result including the extracted prompt.

4. MCP Client and Server Setup

We’ll need an MCP server to expose our custom tools, as well as one or more MCP clients, capable of connecting to a server and invoking a tool.

4.1. MCP Server Implementation

The McpServer has certain capabilities that tell the clients which categories of protocol operations-such as logging, prompt completion, and resources, are available. Additionally, tools allow the clients to call the actionable functions that the server exposes.

Let’s begin by defining an implementation of the McpServer:

public class McpServerApp {

    public static McpSyncServer createServer() {
        JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());
        StdioServerTransportProvider transportProvider = new StdioServerTransportProvider(
            jsonMapper);

        return McpServer.sync(transportProvider)
          .serverInfo("baeldung-demo-server", "0.0.1")
          .capabilities(McpSchema.ServerCapabilities.builder()
            .tools(true)
            .logging()
            .build())
          .tools(LoggingTool.logPromptTool())
          .build();
    }

    public static void main(String[] args) {
        createServer();
    }
}

We’ve defined a synchronous McpServer which communicates via standard input/output streams using JSON message format. Next, the server capabilities are defined to include tools and logging (via SLF4J), and finally, we include our custom logPromptTool.

4.3. MCP Client Implementation

Next, we’ll define a simple McpClient that connects to a server:

public class McpClientApp {

    public static McpSyncClient getClient() {
        ServerParameters params = ServerParameters
          .builder("npx")
          .args("-y", "@modelcontextprotocol/server-everything")
          .build();

        JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());
        McpClientTransport transport = new StdioClientTransport(params, jsonMapper);

        return io.modelcontextprotocol.client.McpClient.sync(transport)
         .build();

    }

    public static void main(String[] args) {
        McpSyncClient client = getClient();
        client.initialize();
    }
}

We’ve used the sample server provided by MCP, defined in the ServerParameters. In addition, the communication is via standard input/output streams using JSON message format for our synchronous McpClient.

5. Test

We’ve all the components needed to test some of the MCP interactions and concepts.

5.1. Testing the MCP Tool and Client Implementations

Let’s begin by testing the LoggingTool and verifying the output for the same:

    @Test
    void whenLogPromptToolCalled_thenReturnsResult() {
        McpSchema.CallToolRequest request = new McpSchema.CallToolRequest("",
            Map.of("prompt", "Unit test message"));

        McpServerFeatures.SyncToolSpecification toolSpec = LoggingTool.logPromptTool();
        McpSchema.CallToolResult result 
          = toolSpec.callHandler().apply(null, request);
        assertNotNull(result);
        assertFalse(result.isError());
        assertEquals(
          "Input Prompt: Unit test message",((McpSchema.TextContent) (result.content()
            .getFirst()))
            .text());
    }

In this test, we’re creating a CallToolRequest with a prompt, which is then passed to the SyncToolSpecification of the LoggingTool.

Next, we’ll test the McpClient by connecting to the sample server exposed by MCP:

@Test
    void whenCalledViaClient_thenReturnsLoggedResult() {
        McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(
          "echo", Map.of("message", "Client-server test message"));
        McpSchema.CallToolResult result = client.callTool(request);

        assertNotNull(result);
        assertNull(result.isError());
        assertEquals("Echo: Client-server test message",
          ((McpSchema.TextContent) (result.content()
          .getFirst())).text());
    }

The MCP sample server exposes a tool called “echo”, which returns the input prompt, similar to what we created with the LoggingTool.

5.2. Testing the Local Server

Finally, let’s test the local server we’ve written. We’ll need to define a separate McpClient with different server params pointing to the local jar:

public class McpClientApp2 {

    private static final Logger log = LoggerFactory.getLogger(McpClientApp2.class);

    public static void main(String[] args) {
        String jarPath = new java.io.File("java-mcp/target/java-mcp-1.0.0-SNAPSHOT.jar")
                             .getAbsolutePath();
        ServerParameters params = ServerParameters.builder("java")
            .args("-jar", jarPath)
            .build();

        JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());
        McpClientTransport transport = new StdioClientTransport(params, jsonMapper);

        McpSyncClient client = McpClient.sync(transport)
            .build();

        client.initialize();

        ListToolsResult tools = client.listTools();
        McpClientApp2.log.info("Tools exposed by the server:");
        tools
          .tools()
          .forEach(tool -> System.out.println(" - " + tool.name()));

        McpClientApp2.log.info("\nCalling 'logPrompt' tool...");
        CallToolResult result = client.callTool(
          new CallToolRequest("logPrompt", Map.of("prompt", "Hello from MCP client!")));
        McpClientApp2.log.info("Result: " + result.content());

        client.closeGracefully();
    }
}

We’ll run the client and check the logs to verify that it is connected to our local server defined in the jar file:

14:04:27.879 [boundedElastic-1] INFO  i.m.c.transport.StdioClientTransport - MCP server starting.
14:04:27.920 [boundedElastic-1] INFO  i.m.c.transport.StdioClientTransport - MCP server started
14:04:28.517 [pool-4-thread-1] INFO  i.m.c.transport.StdioClientTransport - STDERR Message received: 14:04:28.504 [pool-1-thread-1] INFO  i.m.server.McpAsyncServer - Client initialize request - Protocol: 2024-11-05, Capabilities: ClientCapabilities[experimental=null, roots=null, sampling=null, elicitation=null], Info: Implementation[name=Java SDK MCP Client, title=null, version=0.15.0]
14:04:28.575 [pool-1-thread-1] INFO  i.m.client.LifecycleInitializer - Server response with Protocol: 2024-11-05, Capabilities: ServerCapabilities[completions=null, experimental=null, logging=LoggingCapabilities[], prompts=null, resources=null, tools=ToolCapabilities[listChanged=true]], Info: Implementation[name=baeldung-demo-server, title=null, version=0.0.1] and Instructions null
14:04:28.626 [main] INFO  mcp.McpClientApp2 - Tools exposed by the server:
14:04:28.626 [main] INFO  mcp.McpClientApp2 - 
Calling 'logPrompt' tool...
 - logPrompt
14:04:28.671 [main] INFO  mcp.McpClientApp2 - Result: [TextContent[annotations=null, text=Input Prompt: Hello from MCP client!, meta=null]]
14:04:28.784 [ForkJoinPool.commonPool-worker-1] WARN  i.m.c.transport.StdioClientTransport - Process terminated with code 143

Process finished with exit code 0

As we can see here, firstly, the McpServer is started, then the client initializes and tries to connect with the server, which is successful.

Next, the client requests a list of tools exposed by the server, and finally, upon the client’s invocation of the logPrompt tool, we see the response from the server.

6. Conclusion

In this tutorial, we’ve explored the architecture of MCP and Java SDK.  The main components are the McpServer, which exposes various functionalities that the McpClient can use from within the McpHost, while the transport channels and their details are handled by the McpTransport.

Next, we explored primitives and the different types available on the server and the client.

Finally, we implemented one of the tools and verified the McpClient connection and invocation with both the MCP sample server and our local McpServer.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)