• Uncategorised

How to Build an MCP Client Using the Java SDK

The Model Context Protocol (MCP) allows language models to interface with tools and external systems in a structured, programmable way. If you’re building intelligent apps or agents that need dynamic tool invocation, MCP gives you a solid foundation.

In this post, we’ll walk through how to build a robust MCP Client using the Java MCP SDK, with practical code examples taken from a real-world integration.


🚀 Prerequisites

  • Java 17+
  • Spring Boot (optional for HTTP transport)
  • Maven or Gradle project
  • MCP-compatible server or tool (can be local, HTTP, or cloud-based)

🧱 Step 1: Choose the Right Transport

The MCP client supports different transports:

🔌 A. HTTP/SSE Transport

javaCopyEditString url = "https://mcp.deepwiki.com/";
McpClientTransport transport = new HttpClientSseClientTransport(url);

Or use a Spring WebFlux-compatible SSE transport:

javaCopyEditWebClient.Builder builder = WebClient.builder().baseUrl(url);
McpClientTransport transport = new WebFluxSseClientTransport(builder);

🖥️ B. STDIO Transport (for CLI/Node.js tools)

javaCopyEditServerParameters params = ServerParameters.builder("/usr/local/bin/node")
    .args("/path/to/your/server.js")
    .build();
McpClientTransport transport = new StdioClientTransport(params);

⚙️ Step 2: Create and Configure the MCP Client

Using the selected transport, build the MCP client with optional hooks like sampling, elicitation, or logging.

javaCopyEditMcpSyncClient client = McpClient.sync(transport)
    .clientInfo(new Implementation("Sample client", "1.0.0"))
    .sampling(request -> {
        // Forward to a model or apply business rules
        return new CreateMessageResult(Role.ASSISTANT, new TextContent("response"), "model", StopReason.END_TURN);
    })
    .elicitation(request -> {
        // Example dummy handler
        return new ElicitResult(ElicitResult.Action.COMPLETE, Map.of("key", "value"));
    })
    .loggingConsumer(msg -> System.out.println("Log: " + msg.data()))
    .build();

🧪 Step 3: Initialize and Discover Tools

javaCopyEditInitializeResult init = client.initialize();
System.out.println("Server initialized: " + init);

ListToolsResult tools = client.listTools();
for (Tool tool : tools.tools()) {
    System.out.println("Tool: " + tool.name() + " - " + tool.description());
}

🛠️ Step 4: Wrap Tools as Executables

You can dynamically turn tools into LangChain4j-compatible ToolSpecifications and executors:

javaCopyEditToolSpecification spec = ToolSpecification.builder()
    .name(tool.name())
    .description(tool.description())
    .parameters(JsonObjectSchema.builder()
        .addStringProperty("input") // Adapt to actual schema
        .required("input")
        .build())
    .build();

ToolExecutor executor = (request, context) -> {
    CallToolResult result = client.callTool(new CallToolRequest(tool.name(), request.arguments()));
    return ((TextContent) result.content().get(0)).text();
};

🧠 Optional: Integrate with LLM Agents

Using LangChain4j, you can plug this into a ChatModel-based agent:

javaCopyEditChatModel model = OpenAiChatModel.builder()
    .apiKey("your-key")
    .modelName("gpt-4o-mini")
    .build();

Bot bot = AiServices.builder(Bot.class)
    .chatModel(model)
    .tools(Map.of(spec, executor))
    .build();

String reply = bot.chat("Check weather in Atlanta");
System.out.println(reply);

🪄 Bonus: Set Logging Level via Tool

javaCopyEditclient.callTool(new CallToolRequest("logging.set-level", Map.of(
    "logger", "root",
    "level", "DEBUG"
)));

You may also like...