ModelContextProtocol (MCP) Java SDK v0.8.0 Specification

by jayessdeesea

215 downloads
Not rated
GitHub

About

Instructions for an AI on how to create a Java based mcp server and client

Details

Author
jayessdeesea
Downloads
215
Categories
Developer Tools

- Modular architecture with separate transport, protocol, and error‑handling layers
- Synchronous (McpSyncClient) and asynchronous (McpAsyncClient) client implementations
- Server implementations (McpSyncServer, McpAsyncServer) for exposing resources and tools
- Built‑in support for stdio transport; HTTP/SSE transport via Jakarta Servlet API
- Type‑safe MCP schema classes and error utilities in dedicated packages
- Full support for reading resources, calling tools, and managing prompts

Add the MCP BOM to your project's dependency management, then include the core mcp artifact. Use the builder pattern to create a McpSyncClient or McpAsyncClient, providing a transport (e.g., StdioClientTransport) and client info. Then call methods like readResource, callTool, listPrompts, or getPrompt. Close the client after use.

ModelContextProtocol (MCP) Java SDK v0.8.0 Specification

Introduction

The Model Context Protocol (MCP) is a standardized protocol for communication between AI models and external tools or resources. The Java SDK provides a robust implementation of this protocol, enabling Java applications to create MCP servers that expose tools and resources to AI models, as well as MCP clients that can communicate with these servers.

This document serves as a comprehensive specification for the MCP Java SDK version 0.8.0, intended for AI-assisted code generation of MCP clients and servers.

Architecture

The MCP Java SDK follows a modular architecture with clear separation of concerns:

graph TD
    Client[Client] --> Transport[Transport Layer]
    Server[Server] --> Transport
    Transport --> Protocol[Protocol Layer]
    Protocol --> JSON[JSON Schema]
    Client --> Resources[Resources]
    Client --> Tools[Tools]
    Server --> Resources
    Server --> Tools
    Server --> ErrorHandling[Error Handling]
    Client --> ErrorHandling

Core Components

1. Client - Interfaces with MCP servers to access resources and tools
2. Server - Exposes resources and tools to MCP clients
3. Transport Layer - Handles communication between clients and servers
4. Protocol Layer - Implements the MCP protocol specification
5. Resources - Static or dynamic data exposed by servers
6. Tools - Executable functions exposed by servers

Package Structure

The SDK is organized into the following key packages:

- io.modelcontextprotocol.client - Client implementation (McpClient)
- io.modelcontextprotocol.server - Server implementation (McpServer, McpSyncServer, McpAsyncServer)
- io.modelcontextprotocol.client.transport - Client transport implementations
- io.modelcontextprotocol.server.transport - Server transport implementations and providers
- io.modelcontextprotocol.spec - Core protocol specification and schema classes
- io.modelcontextprotocol.transport - Transport layer interfaces and implementations
- io.modelcontextprotocol.types - Type definitions for MCP protocol
- io.modelcontextprotocol.errors - Error handling classes and utilities

Installation

Maven Dependencies

Add the MCP BOM (Bill of Materials) to your project to ensure compatible versions of all components:

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

Then add the specific dependencies you need:

<dependencies>
    <!-- MCP Core dependencies -->
    <dependency>
        <groupId>io.modelcontextprotocol.sdk</groupId>
        <artifactId>mcp</artifactId>
    </dependency>
    
    <!-- Testing utilities -->
    <dependency>
        <groupId>io.modelcontextprotocol.sdk</groupId>
        <artifactId>mcp-test</artifactId>
    </dependency>
    
    <!-- Jakarta Servlet API (required for HTTP/SSE transports) -->
    <dependency>
        <groupId>jakarta.servlet</groupId>
        <artifactId>jakarta.servlet-api</artifactId>
        <version>5.0.0</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Client Implementation

Synchronous Client with Tools

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;

import java.util.HashMap;
import java.util.Map;

public class SyncClientStdioToolsExample {
public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");

// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();

// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);

// Create the client using the builder pattern
McpSyncClient client = McpClient.sync(transport)
.clientInfo(clientInfo)
.build();

try {
// Read a resource
McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("example://resource");
McpSchema.ReadResourceResult result = client.readResource(request);

// Access the resource contents
if (result.contents() != null && !result.contents().isEmpty()) {
McpSchema.ResourceContents contents = result.contents().get(0);
if (contents instanceof McpSchema.TextResourceContents textContents) {
System.out.println("Resource content: " + textContents.text());
}
}

// Call a tool with a Map of arguments
Map<String, Object> toolArgs = new HashMap();
toolArgs.put("param1", "value1");
toolArgs.put("param2", 42);

McpSchema.CallToolRequest toolRequest = new McpSchema.CallToolRequest("example-tool", toolArgs);
McpSchema.CallToolResult toolResponse = client.callTool(toolRequest);

// Access the tool response content
if (toolResponse.content() != null && !toolResponse.content().isEmpty()) {
McpSchema.Content content = toolResponse.content().get(0);
if (content instanceof McpSchema.TextContent textContent) {
System.out.println("Tool response: " + textContent.text());
}
}
} finally {
// Close the client
client.close();
}
}
}

Synchronous Client with Prompts

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpSyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.ListPromptsResult;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SyncClientStdioPromptsExample {
public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");

// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();

// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);

// Create the client using the builder pattern
McpSyncClient client = McpClient.sync(transport)
.clientInfo(clientInfo)
.build();

try {
// List available prompts
ListPromptsResult promptsResult = client.listPrompts();

if (promptsResult.prompts() != null && !promptsResult.prompts().isEmpty()) {
System.out.println("Available prompts:");
for (Prompt prompt : promptsResult.prompts()) {
System.out.println("- " + prompt.name() + ": " + prompt.description());
}

// Get a specific prompt
String promptName = promptsResult.prompts().get(0).name();

// Create arguments for the prompt if needed
Map<String, Object> promptArgs = new HashMap();
promptArgs.put("language", "Java");
promptArgs.put("code", "public class Example { public static void main(String[] args) { } }");

GetPromptRequest promptRequest = new GetPromptRequest(promptName, promptArgs);
GetPromptResult promptResult = client.getPrompt(promptRequest);

// Process the prompt result
if (promptResult.messages() != null && !promptResult.messages().isEmpty()) {
System.out.println("Prompt messages:");
for (PromptMessage message : promptResult.messages()) {
System.out.println("Role: " + message.role());
System.out.println("Content: " + message.content());
}
}
} else {
System.out.println("No prompts available from the server.");
}
} finally {
// Close the client
client.close();
}
}
}

Asynchronous Client with Tools

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

public class AsyncClientStdioToolsExample {

public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");

// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();

// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);

// Create the client using the builder pattern
McpAsyncClient client = McpClient.async(transport)
.clientInfo(clientInfo)
.build();

try {
// Initialize the client (connects to the server)
client.initialize().block(); // Block until initialization completes

// Read a resource
McpSchema.ReadResourceRequest request = new McpSchema.ReadResourceRequest("example://resource");
McpSchema.ReadResourceResult result = client.readResource(request).block();

// Access the resource contents
if (result.contents() != null && !result.contents().isEmpty()) {
McpSchema.ResourceContents contents = result.contents().get(0);
if (contents instanceof McpSchema.TextResourceContents textContents) {
System.out.println("Resource content: " + textContents.text());
}
}

// Call a tool with a Map of arguments
Map<String, Object> toolArgs = new HashMap();
toolArgs.put("param1", "value1");
toolArgs.put("param2", 42);

McpSchema.CallToolRequest toolRequest = new McpSchema.CallToolRequest("example-tool", toolArgs);
McpSchema.CallToolResult toolResponse = client.callTool(toolRequest).block();

// Access the tool response content
if (toolResponse.content() != null && !toolResponse.content().isEmpty()) {
McpSchema.Content content = toolResponse.content().get(0);
if (content instanceof McpSchema.TextContent textContent) {
System.out.println("Tool response: " + textContent.text());
}
}
} catch (Exception e) {
if (e.getCause() instanceof McpError) {
McpError mcpError = (McpError) e.getCause();
System.err.println("MCP Error: " + mcpError.getMessage());
} else {
System.err.println("Error: " + e.getMessage());
}
} finally {
// Close the client
client.close();
}
}
}

Asynchronous Client with Prompts

import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.McpAsyncClient;
import io.modelcontextprotocol.client.transport.ServerParameters;
import io.modelcontextprotocol.client.transport.StdioClientTransport;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import io.modelcontextprotocol.spec.McpSchema.ListPromptsResult;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpError;

import java.util.HashMap;
import java.util.Map;

public class AsyncClientStdioPromptsExample {

public static void main(String[] args) throws Exception {
// Create client info
McpSchema.Implementation clientInfo = new McpSchema.Implementation("example-client", "1.0.0");

// Create server parameters
ServerParameters serverParams = ServerParameters.builder("example-server-command")
.build();

// Create transport with server parameters
StdioClientTransport transport = new StdioClientTransport(serverParams);

// Create the client using the builder pattern
McpAsyncClient client = McpClient.async(transport)
.clientInfo(clientInfo)
.build();

try {
// Initialize the client (connects to the server)
client.initialize().block(); // Block until initialization completes

// List available prompts
ListPromptsResult promptsResult = client.listPrompts().block();

if (promptsResult.prompts() != null && !promptsResult.prompts().isEmpty()) {
System.out.println("Available prompts:");
for (Prompt prompt : promptsResult.prompts()) {
System.out.println("- " + prompt.name() + ": " + prompt.description());
}

// Get a specific prompt
String promptName = promptsResult.prompts().get(0).name();

// Create arguments for the prompt if needed
Map<String, Object> promptArgs = new HashMap();
promptArgs.put("language", "Java");
promptArgs.put("code", "public class Example { public static void main(String[] args) { } }");

GetPromptRequest promptRequest = new GetPromptRequest(promptName, promptArgs);
GetPromptResult promptResult = client.getPrompt(promptRequest).block();

// Process the prompt result
if (promptResult.messages() != null && !promptResult.messages().isEmpty()) {
System.out.println("Prompt messages:");
for (PromptMessage message : promptResult.messages()) {
System.out.println("Role: " + message.role());
System.out.println("Content: " + message.content());
}
}
} else {
System.out.println("No prompts available from the server.");
}
} catch (Exception e) {
if (e.getCause() instanceof McpError) {
McpError mcpError = (McpError) e.getCause();
System.err.println("MCP Error: " + mcpError.getMessage());
} else {
System.err.println("Error: " + e.getMessage());
}
} finally {
// Close the client
client.close();
}
}
}

Server Implementation

Synchronous Server with Tools

import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SyncServerStdioToolsExample {

public static void main(String[] args) throws Exception {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("example-server", "1.0.0");

// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();

// Create server using the builder pattern
McpSyncServer server = McpServer.sync(transportProvider)
.serverInfo(serverInfo)
.tool(
new McpSchema.Tool(
"example-tool",
"An example tool",
createToolSchema()
),
(exchange, toolArgs) -> {
String param1 = (String) toolArgs.get("param1");
Number param2 = (Number) toolArgs.get("param2");

List<McpSchema.Content> content = new ArrayList();
content.add(new McpSchema.TextContent(
null,
null,
"Tool executed with param1=" + param1 + ", param2=" + param2
));

return new McpSchema.CallToolResult(content, false);
}
)
.build();

System.err.println("Server started");
}

/
Creates the JSON schema for the example tool.
/
private static McpSchema.JsonSchema createToolSchema() {
// Create input schema for the tool
Map<String, Object> properties = new HashMap();

Map<String, Object> param1 = new HashMap();
param1.put("type", "string");
param1.put("description", "A string parameter");

Map<String, Object> param2 = new HashMap();
param2.put("type", "number");
param2.put("description", "A numeric parameter");

properties.put("param1", param1);
properties.put("param2", param2);

List<String> required = List.of("param1");

return new McpSchema.JsonSchema("object", properties, required, null);
}
}

Synchronous Server with Prompts

import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpSyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class SyncServerStdioPromptsExample {

public static void main(String[] args) throws Exception {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("prompts-example-server", "1.0.0");

// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();

// Create a code analysis prompt
List<PromptArgument> codeAnalysisArgs = new ArrayList();
codeAnalysisArgs.add(new PromptArgument(
"language",
"The programming language of the code",
true
));
codeAnalysisArgs.add(new PromptArgument(
"code",
"The code to analyze",
true
));

Prompt codeAnalysisPrompt = new Prompt(
"code-analysis",
"Analyzes code for potential issues and improvements",
codeAnalysisArgs
);

// Create server using the builder pattern
McpSyncServer server = McpServer.sync(transportProvider)
.serverInfo(serverInfo)
.prompt(
codeAnalysisPrompt,
(exchange, request) -> {
// Extract arguments from the request
String language = (String) request.arguments().get("language");
String code = (String) request.arguments().get("code");

// Create prompt messages
List<PromptMessage> messages = new ArrayList();

// System message
messages.add(new PromptMessage(
"system",
"You are a code analysis assistant that helps identify issues and suggest improvements."
));

// User message with the code
messages.add(new PromptMessage(
"user",
"Please analyze this " + language + " code:\n\n

" + language + "\n" + code + "\n```"
));

// Assistant message with the analysis
messages.add(new PromptMessage(
"assistant",
"Here's my analysis of your " + language + " code:\n\n" +
"1. The code is very minimal and doesn't do anything yet.\n" +
"2. Consider adding some functionality to the main method.\n" +
"3. Add comments to explain the purpose of the class."
));

// Return the prompt result
return new GetPromptResult(
"Code analysis for " + language,
messages
);
}
)
.build();

System.err.println("Prompts server started");
}
}

Asynchronous Server with Tools

java
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;

public class AsyncServerStdioToolsExample {

public static void main(String[] args) {
try {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("example-server", "1.0.0");

// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();

// Create server using the builder pattern
McpAsyncServer server = McpServer.async(transportProvider)
.serverInfo(serverInfo)
.tool(
new McpSchema.Tool(
"example-tool",
"An example tool",
createToolSchema()
),
(exchange, toolArgs) -> {
String param1 = (String) toolArgs.get("param1");
Number param2 = (Number) toolArgs.get("param2");

List<McpSchema.Content> content = new ArrayList();
content.add(new McpSchema.TextContent(
null,
null,
"Tool executed with param1=" + param1 + ", param2=" + param2
));

return Mono.just(new McpSchema.CallToolResult(content, false));
}
)
.build();

System.err.println("Server started");
} catch (Exception e) {
System.err.println("Failed to start server: " + e.getMessage());
}
}

/

Creates the JSON schema for the example tool.
/
private static McpSchema.JsonSchema createToolSchema() {
// Create input schema for the tool
Map<String, Object> properties = new HashMap();

Map<String, Object> param1 = new HashMap();
param1.put("type", "string");
param1.put("description", "A string parameter");

Map<String, Object> param2 = new HashMap();
param2.put("type", "number");
param2.put("description", "A numeric parameter");

properties.put("param1", param1);
properties.put("param2", param2);

List<String> required = List.of("param1");

return new McpSchema.JsonSchema("object", properties, required, null);
}
}


Asynchronous Server with Resources

java
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpError;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import reactor.core.publisher.Mono;

public class AsyncServerStdioResourcesExample {

// Pattern for matching resource URIs
private static final Pattern RESOURCE_PATTERN = Pattern.compile("data://users/(.+)");

public static void main(String[] args) {
try {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("async-resources-example", "1.0.0");

// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();

// Create server using the builder pattern
McpAsyncServer server = McpServer.async(transportProvider)
.serverInfo(serverInfo)
.resourceTemplate(
new McpSchema.ResourceTemplate(
"data://users/{userId}",
"User Data",
"Data for a specific user",
"application/json",
null
),
(exchange, request) -> {
String uri = request.uri();

// Parse the URI to extract parameters
Matcher matcher = RESOURCE_PATTERN.matcher(uri);

if (matcher.matches()) {
String userId = matcher.group(1);

// Simulate an asynchronous database lookup
return Mono.fromCallable(() -> {
// In a real implementation, this would be a database query
// For this example, we'll just generate some data
String userData = String.format(
"{\"id\":\"%s\",\"name\":\"User %s\",\"email\":\"user%s@example.com\",\"created\":\"2025-03-24\"}",
userId, userId, userId
);

List<McpSchema.ResourceContents> contents = new ArrayList();
contents.add(new McpSchema.TextResourceContents(
uri,
"application/json",
userData
));

return new McpSchema.ReadResourceResult(contents);
});
}

return Mono.error(new McpError(
new McpSchema.JSONRPCResponse.JSONRPCError(
McpSchema.ErrorCodes.RESOURCE_NOT_FOUND,
"Resource not found: " + uri,
null
)
));
}
)
.build();

System.err.println("Async resources server started");
} catch (Exception e) {
System.err.println("Failed to start server: " + e.getMessage());
}
}
}


Asynchronous Server with Prompts

java
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.Prompt;
import io.modelcontextprotocol.spec.McpSchema.PromptArgument;
import io.modelcontextprotocol.spec.McpSchema.PromptMessage;
import io.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import io.modelcontextprotocol.spec.McpSchema.GetPromptResult;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import reactor.core.publisher.Mono;

public class AsyncServerStdioPromptsExample {

public static void main(String[] args) {
try {
// Create server info
McpSchema.Implementation serverInfo = new McpSchema.Implementation("async-prompts-example", "1.0.0");

// Create transport provider
StdioServerTransportProvider transportProvider = new StdioServerTransportProvider();

// Cr

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.