Enterprise Java

Getting Started with the Java Model Context Protocol (MCP) SDK

The rapid growth of AI-powered applications has created a need for standardized ways to connect models with external tools, services, and data sources. As systems grow more complex, integration approaches often become scattered and difficult to manage consistently. This is where the Model Context Protocol (MCP) comes in. MCP defines a common protocol that allows AI models and applications to discover and interact with tools and contextual resources in a structured and consistent way.

The MCP Java SDK provides us with a set of libraries that implement the MCP specification and make it easier to build both MCP servers and MCP clients. With the SDK, we can expose tools, prompts, and resources through standardized interfaces and connect AI applications to those capabilities using multiple transport mechanisms such as STDIO and HTTP-based streaming. This enables clean separation between AI models and the systems they depend on, while still allowing rich, dynamic interactions.

In this article, we introduce the core ideas behind MCP and demonstrate how to build a simple MCP server and client using the Java SDK.

1. MCP Architecture

MCP follows a client–server architecture designed specifically for AI tool and context integration. The MCP server is responsible for exposing capabilities such as tools, prompts, and resources, while the MCP client is responsible for discovering and invoking those capabilities on behalf of an AI application or agent. This separation enables the development and deployment of tools and data sources independently of AI models.

An MCP server publishes a set of features, which may include tools (executable actions), resources (data providers), and prompts (structured templates for interactions). Each feature is described using schemas, allowing clients to automatically understand the required inputs and expected outputs. This makes MCP self-describing and suitable for dynamic AI agents that need to reason about which tools to use at runtime.

Communication between the client and server happens over a transport layer, which is abstracted by the MCP SDK. The Java SDK supports multiple transports, including STDIO for local process-based communication and HTTP Server-Sent Events (SSE) for network-based communication. Because the protocol is transport-agnostic, the same MCP server logic can be reused in different deployment environments without changing the core business logic.

When an MCP client connects to a server, it performs a handshake to retrieve server metadata and available features. The client can then invoke tools by sending structured requests that match the tool’s schema. The server executes the tool and returns a structured response. This request–response model makes MCP suitable for both synchronous workflows and more complex agent-driven interactions, where multiple tool calls may occur during a single reasoning session.

By standardizing how tools and context are exposed and consumed, MCP enables interoperable AI systems where models, tools, and data services can evolve independently while still working together through a consistent protocol.

2. Setting Up an MCP Java Project

To get started with the MCP Java SDK, we first need a standard Java project with Maven for dependency management. The MCP SDK requires Java 17 or higher, so ensure your development environment is configured accordingly. Using Maven also simplifies version alignment across the MCP modules by relying on the official Bill of Materials (BOM) provided by the SDK.

Create a new Maven project and add the MCP BOM and core dependency to your pom.xml file.

    <dependencies>
        <dependency>
            <groupId>io.modelcontextprotocol.sdk</groupId>
            <artifactId>mcp</artifactId>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.modelcontextprotocol.sdk</groupId>
                <artifactId>mcp-bom</artifactId>
                <version>0.17.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

3. Building a Simple MCP Server

Next, we build an MCP server that exposes a simple tool. In this example, the server provides a greet tool that accepts a user’s name and returns a personalized greeting message. This demonstrates how tools are registered, validated using schemas, and executed when invoked by a client.

Create a new class called GreetingServer.java.

public class GreetingServer {

    public static void main(String[] args) {

        // JSON mapper used by MCP for message serialization
        var jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());

        // Create STDIO transport provider
        var transport = new StdioServerTransportProvider(jsonMapper);

        // Build MCP synchronous server with advertised capabilities
        McpSyncServer server = McpServer.sync(transport)
                .serverInfo("greeting-server", "1.0.0")
                .capabilities(ServerCapabilities.builder()
                        .resources(true, true)
                        .tools(true)
                        .prompts(true)
                        .logging()
                        .build())
                .build();

        // Send logging notifications
        server.loggingNotification(LoggingMessageNotification.builder()
                .level(LoggingLevel.INFO)
                .logger("custom-logger")
                .data("Server initialized")
                .build());
        
        // Register greeting tool
        server.addTool(createGreetingTool());
    }

    private static McpServerFeatures.SyncToolSpecification createGreetingTool() {

        // Define JSON schema for tool input
        McpSchema.JsonSchema inputSchema = new McpSchema.JsonSchema(
                "object",
                Map.of("name", String.class),
                List.of("name"),
                false,
                null,
                null
        );

        // Create tool registration
        return new McpServerFeatures.SyncToolSpecification(
                new McpSchema.Tool(
                        "greet",
                        "Simple Greeting",
                        "Returns a personalized greeting message",
                        inputSchema,
                        null,
                        null,
                        null
                ),
                (exchange, argsMap) -> {
                    String name = (String) argsMap.get("name");
                    String message = "Hello, " + name + "! Welcome to MCP.";

                    return McpSchema.CallToolResult.builder()
                            .content(List.of(
                                    new McpSchema.TextContent(message)
                            ))
                            .isError(false)
                            .build();
                }
        );
    }
}

The server is configured with an STDIO transport, which is useful for local integrations and agent-style execution. A tool named greet is registered with a JSON schema defining its expected input. When the tool is invoked, the server extracts the name parameter, generates a response, and returns it using CallToolResult. The server then listens indefinitely for incoming requests.

4. Implementing an MCP Client

Now that the server exposes a tool, we create a client that connects to the server and invokes the greet tool. The client uses the same transport type so that both sides can communicate correctly.

Create a class called GreetingClient.java.

public class GreetingClient {

    private static final Logger log = Logger.getLogger(GreetingClient.class.getName());

    public static void main(String[] args) {

        String jarPath = new java.io.File("target//mcp-java-demo-1.0.jar")
                .getAbsolutePath();
        ServerParameters params = ServerParameters.builder("java")
                .args("-jar", jarPath)
                .build();

        // Create STDIO client transport
        JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(new ObjectMapper());
        var transport = new StdioClientTransport(params, jsonMapper);

        // Build synchronous MCP client
        McpSyncClient client = McpClient.sync(transport).build();

        try {
            // Initialize MCP handshake
            client.initialize();

            // List tools exposed by the server
            ListToolsResult tools = client.listTools();
            log.info("Tools exposed by the server:");
            tools.tools().forEach(tool -> log.log(Level.INFO, " - {0}", tool.name()));

            // Call greeting tool
            log.info("Calling 'greet' tool...");
            CallToolResult result = client.callTool(
                    new CallToolRequest("greet", Map.of("name", "Thomas"))
            );

            log.log(Level.INFO, "Result: {0}", result.content());

        } catch (Exception ex) {
            log.log(Level.SEVERE, "MCP client error", ex);
        } finally {
            // Shutdown client and server process
            client.closeGracefully();
        }
    }
}

The client first prepares the command used to start the MCP server as a subprocess. The jarPath resolves the absolute path to the server JAR, and ServerParameters.builder("java").args("-jar", jarPath) instructs the client to launch the server using java -jar <server-jar>. This means the client automatically starts and stops the server process as part of its lifecycle.

Next, the MCP transport is configured using StdioClientTransport, which communicates with the server over standard input and output. JacksonMcpJsonMapper handles JSON serialization and deserialization of MCP protocol messages using Jackson.

The client instance is created with McpClient.sync(transport).build(), producing a synchronous, blocking API that simplifies control flow in small applications. Calling client.initialize() performs the MCP handshake, where the client and server exchange initialization messages and negotiate available capabilities.

After initialization, client.listTools() retrieves the tools exposed by the server, allowing the client to verify that the expected tool (such as greet) is available. Finally, client.callTool(...) sends a request to invoke the greet tool with the provided arguments, and the server returns a CallToolResult containing the greeting text.

5. Running the Server and the Client

To run this example successfully, the MCP server must be packaged as an executable (fat) JAR so that the client can launch it using java -jar. This is required because the MCP client starts the server as a separate subprocess via ServerParameters, and that subprocess must include all dependencies.

Add the following build configuration to the server project’s pom.xml:

<build>
    <plugins>
        <!-- Create an executable fat JAR for the MCP server -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.5.0</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <transformers>
                            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.jcg.example.GreetingServer</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

This configuration is necessary for two reasons. First, the Maven Shade plugin bundles all project dependencies into a single JAR, ensuring that the MCP server can run independently without requiring a classpath setup. Second, the ManifestResourceTransformer adds the Main-Class attribute to the JAR manifest, which allows the server to be started with a simple java -jar command. Without this, the MCP client would fail to start the server process and would time out during initialization.

Build the Server JAR

From the server project directory, run:

mvn clean package

This produces an executable JAR similar to:

java -jar target/mcp-java-demo-1.0.jar

Run the Client

Now run the client application:

mvn exec:java -Dexec.mainClass=com.jcg.example.GreetingClient

The client will automatically start the server as a subprocess, perform the MCP handshake, list available tools, and invoke the greet tool.

Sample Output

Jan 19, 2026 7:22:49 P.M. com.jcg.example.GreetingClient main
INFO: Tools exposed by the server:
Jan 19, 2026 7:22:49 P.M. com.jcg.example.GreetingClient lambda$main$0
INFO:  - greet
Jan 19, 2026 7:22:49 P.M. com.jcg.example.GreetingClient main
INFO: Calling 'greet' tool...
Jan 19, 2026 7:22:50 P.M. com.jcg.example.GreetingClient main
INFO: Result: [TextContent[annotations=null, text=Hello, Thomas! Welcome to MCP., meta=null]]

This output confirms that the server was started successfully, the MCP handshake completed, the greet tool was discovered, and the tool invocation returned the expected response.

6. Conclusion

In this tutorial, we examined how the MCP SDK enables Java applications to expose their features to an AI model using a common execution context and a standard protocol. By creating a basic MCP server and client, packaging them as an executable, and executing the full request–response workflow, we demonstrated how we can consistently expose tools without tightly coupling different components.

7. Download the Source Code

This article presented an introduction to the Model Context Protocol through the Java SDK.

Download
You can download the full source code of this example here: java sdk model context protocol

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button