Saturday, 18 July 2026

Building an AI Employee Assistant with LangChain4j and MCP

  

In the previous post, we interacted with our Employee MCP Server using raw JSON-RPC requests over cURL. We manually initialized the MCP session, discovered available tools, read resources, retrieved prompts, and invoked server-side functionality.

 

While using cURL is an excellent way to understand the MCP protocol, it isn't how real-world AI applications communicate with MCP servers.

 

In practice, AI agents use an MCP Client that automatically handles:

 

·      Protocol initialization

·      Capability negotiation

·      Tool discovery

·      Tool invocation

·      Request/response serialization

·      Session management

 

This allows developers to focus on building intelligent agents instead of manually constructing JSON-RPC requests.

 

In this post, we'll build a simple Employee Assistant using LangChain4j that connects directly to the Employee MCP Server we developed in the previous articles.

 

Instead of writing HTTP requests ourselves, the agent will automatically discover the server's capabilities and invoke the appropriate MCP tools whenever the user asks a question.

 

The result is a conversational assistant capable of answering questions such as:

 

·      Who reports to the CEO?

·      Show all employees in Bangalore.

·      How many employees work in Engineering?

·      Who manages Nikhil Desai?

 

Behind the scenes, LangChain4j takes care of the MCP communication while the language model decides which tool to invoke and how to present the results.

 

What We'll Build

By the end of this article, you'll have a command-line AI assistant similar to the following:

╔══════════════════════════════════════════╗
║        Employee AI Assistant 🤖          ║
╚══════════════════════════════════════════╝

You : Show all employees in Bangalore

🤖 Thinking...

Assistant:

I found 12 employees located in Bangalore.

• Ravi Kumar
• Priya Sharma
• Amit Verma
...

You : Who is the manager of employee 18?

🤖 Thinking...

Assistant:

Employee 18 reports to Sarah Wilson,
Director of Engineering - India.

   

Although the interaction feels like a normal conversation, every answer is powered by the Employee MCP Server, not by the language model's built-in knowledge.

 

1. What You'll Learn

In this article we'll cover:

·      Creating an MCP Client using LangChain4j

·      Connecting to the Employee MCP Server

·      Registering MCP Tools with an AI Agent

·      Configuring conversation memory

·      Building a simple command-line Employee Assistant

·      Watching the LLM automatically discover and invoke MCP tools

·      Running the complete application

 

2. Building the AI Employee Assistant

Now that our Employee MCP Server is up and running, let's build a simple AI agent that can communicate with it using LangChain4j's MCP Client.

 

The architecture is straightforward. The user interacts with the AI agent, the language model decides whether it needs external information, LangChain4j automatically invokes the appropriate MCP tool, and the Employee MCP Server executes the request and returns the result.

Prerequisite

·      Install Ollama

·      Download qwen3.5 model

 

Step 1: Configure the Language Model

First, configure the chat model that powers the AI assistant. In this example, we'll use a locally running Qwen 3.5 model through Ollama.

ChatModel model = OllamaChatModel.builder()
    .baseUrl("http://localhost:11434")
    .modelName("qwen3.5")
    .timeout(Duration.ofMinutes(2))
    .build();

   

You can replace Ollama with any LangChain4j-supported model such as OpenAI, Azure OpenAI, Gemini, Claude, or Amazon Bedrock.

 

Step 2: Configure Conversation Memory

Next, we'll configure chat memory so the assistant remembers previous messages within the same conversation.

 

InMemoryChatMemoryStore memoryStore = new InMemoryChatMemoryStore();

ChatMemoryProvider memoryProvider =
    id -> MessageWindowChatMemory.builder()
            .id(id)
            .maxMessages(20)
            .chatMemoryStore(memoryStore)
            .build();

   

This allows users to ask follow-up questions naturally without repeating context.

 

For example:

·      User: Who is the CEO?

·      User: Where is he located?

 

The assistant can understand that "he" refers to the CEO from the previous interaction.

 

Step 3: Connect to the Employee MCP Server

 

Next, create an MCP transport pointing to the Employee MCP Server.

 

McpTransport transport =
    StreamableHttpMcpTransport.builder()
        .url("http://localhost:8080/mcp")
        .build();

   

Then create the MCP client.

 

McpClient client =
    DefaultMcpClient.builder()
        .key("employee-client")
        .transport(transport)
        .build();

   

The MCP client automatically performs protocol initialization and communicates with the server using the MCP specification. You don't need to manually send JSON-RPC requests as we did in the previous article.

 

Step 4: Register the MCP Tool Provider

LangChain4j exposes MCP tools to the language model through a McpToolProvider.

 

McpToolProvider toolProvider =
    McpToolProvider.builder()
        .mcpClients(client)
        .build();

When the model decides it needs external information, LangChain4j automatically discovers and invokes the appropriate MCP tool on your behalf.

 

Step 5: Define the AI Agent

Now let's define the AI agent itself.

 

public interface EmployeeAssistant {

    @SystemMessage(...)
    String chat(@MemoryId String memoryId,
                @UserMessage String question);
}

   

The @SystemMessage acts as the agent's personality and operating instructions. It tells the model:

 

·      Its role as an HR assistant,

·      When to use MCP tools,

·      How to present responses,

·      and, most importantly, never to invent employee information.

 

This prompt is the "personality" of the assistant, it guides the model's reasoning while the MCP server provides the actual data.

 

Step 6: Build the AI Service

With all the pieces in place, creating the agent is remarkably simple.

 

EmployeeAssistant assistant =
    AiServices.builder(EmployeeAssistant.class)
        .chatModel(model)
        .chatMemoryProvider(memoryProvider)
        .toolProvider(toolProvider)
        .build();

That's it!

 

LangChain4j generates the implementation at runtime, wires together the language model, conversation memory, and MCP client, and produces a fully functional AI assistant.

 

Step 7: Start Chatting

Finally, create a simple console loop and send each user question to the agent.

 

String response = assistant.chat(conversationId, input);

   

From this point onward, everything happens automatically:

 

·      The user asks a question.

·      The LLM determines whether an MCP tool is required.

·      LangChain4j invokes the appropriate tool through the MCP client.

·      The Employee MCP Server executes the request.

·      The tool result is returned to the LLM.

·      The LLM generates a natural language response for the user.

 

As developers, we never have to manually invoke tools/call or parse JSON-RPC responses. LangChain4j abstracts the entire MCP communication layer, allowing us to focus on building intelligent applications rather than protocol details.

 

Find the below working application.

 

Step 1: Create new maven project 'employee-mcp-client'.

 

Step 2: Update pom.xml with maven dependencies.

 

pom.xml

 

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sample.app</groupId>
    <artifactId>employee-mcp-client</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <properties>

        <!-- Java -->
        <maven.compiler.release>23</maven.compiler.release>
        <jline.version>3.30.6</jline.version>
        <jansi.version>2.4.2</jansi.version>
        <jackson.version>2.20.0</jackson.version>
        <slf4j.version>2.0.17</slf4j.version>
        <logback.version>1.5.20</logback.version>
        <langchain4j.version>1.17.2</langchain4j.version>

    </properties>

    <dependencies>

        <!-- ========================= -->
        <!-- LangChain4j               -->
        <!-- ========================= -->

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <!-- Source:
        https://mvnrepository.com/artifact/dev.langchain4j/langchain4j-ollama -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-ollama</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>

        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-mcp</artifactId>
            <version>1.17.2-beta27</version>
            <scope>compile</scope>
        </dependency>

        <!-- ========================= -->
        <!-- Console UI                -->
        <!-- ========================= -->

        <!-- Interactive Terminal -->

        <dependency>
            <groupId>org.jline</groupId>
            <artifactId>jline</artifactId>
            <version>${jline.version}</version>
        </dependency>

        <!-- ASCII Banner -->

        <dependency>
            <groupId>com.github.lalyos</groupId>
            <artifactId>jfiglet</artifactId>
            <version>0.0.9</version>
        </dependency>

        <!-- ========================= -->
        <!-- JSON                      -->
        <!-- ========================= -->

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <!-- ========================= -->
        <!-- Logging                   -->
        <!-- ========================= -->

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>${logback.version}</version>
        </dependency>

        <!-- ========================= -->
        <!-- Testing                   -->
        <!-- ========================= -->

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.13.4</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>

        <plugins>

            <!-- Java Compiler -->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.14.1</version>
                <configuration>
                    <release>23</release>
                </configuration>
            </plugin>

            <!-- Unit Tests -->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.5.4</version>
            </plugin>

            <!-- Build Executable JAR -->

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.6.1</version>

                <executions>

                    <execution>

                        <phase>package</phase>

                        <goals>
                            <goal>shade</goal>
                        </goals>

                        <configuration>

                            <createDependencyReducedPom>false</createDependencyReducedPom>

                            <transformers>

                                <transformer
                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">

                                    <mainClass>
                                        com.sample.app.App
                                    </mainClass>

                                </transformer>

                            </transformers>

                        </configuration>

                    </execution>

                </executions>

            </plugin>

            <plugin>
                <groupId>com.diffplug.spotless</groupId>
                <artifactId>spotless-maven-plugin</artifactId>
                <version>2.46.1</version> <!-- Use the latest version -->
                <configuration>
                    <!-- Target only relevant files -->
                    <java>
                        <includes>
                            <include>src/main/java/**/*.java</include>
                            <include>src/test/java/**/*.java</include>
                        </includes>

                        <!-- Apply Google Java Format -->
                        <googleJavaFormat>
                            <version>1.24.0</version>
                            <style>GOOGLE</style> <!-- Or AOSP for 4-space
                            indents -->
                        </googleJavaFormat>

                        <!-- Extra clean-up steps -->
                        <removeUnusedImports />
                        <trimTrailingWhitespace />
                        <endWithNewline />
                    </java>
                </configuration>
                <executions>
                    <execution>
                        <!-- Automatically check style during the compile phase -->
                        <goals>
                            <goal>check</goal>
                        </goals>
                        <phase>compile</phase>
                    </execution>
                </executions>
            </plugin>

        </plugins>

    </build>
</project>

Step 3: Create logback.xml file in src/main/resources folder.

 

logback.xml

 

<configuration>

    <logger name="dev.langchain4j.mcp.client" level="OFF"/>

    <!-- or just disable DefaultMcpClient -->
    <!--
    <logger name="dev.langchain4j.mcp.client.DefaultMcpClient" level="OFF"/>
    -->

</configuration>

   

Step 4: Define EmployeeAssistant class.

 

EmployeeAssistant.java

 

package com.sample.app.agent;

import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

/**
 * AI Employee Assistant.
 *
 * <p>An AI assistant that answers questions about employees by interacting with the Employee MCP
 * Server. The assistant uses MCP Tools, Resources, and Prompts to retrieve accurate information
 * instead of relying on its own knowledge.
 *
 * <p>This example demonstrates how an AI agent can leverage MCP as an external knowledge and action
 * provider.
 */
public interface EmployeeAssistant {

  @SystemMessage(
      """
      You are an intelligent HR and Organization Assistant.

      You have access to an Employee MCP Server that exposes Tools,
      Resources, and Prompts for retrieving employee information.

      Your goal is to help users answer questions about employees,
      managers, departments, countries, cities, and organizational
      structure.

      Follow these guidelines:

      1. Carefully understand the user's request.
      2. Decide whether a Tool, Resource, or Prompt is needed.
      3. Use the most appropriate MCP capability.
      4. Never invent employee information.
      5. If the requested employee or data cannot be found,
         clearly explain that no matching information exists.
      6. Summarize retrieved information in a clear and friendly way.
      7. When listing multiple employees, present them in a table.
      8. When appropriate, include useful statistics or observations.
      9. If the request is ambiguous, ask a clarifying question before proceeding.
      10. Keep responses concise, professional, and easy to understand.

      Choose the appropriate MCP capability:

      • Use Tools when business logic or filtering is required.
        Examples:
        - Find employees in a city
        - Find employees under a manager
        - Search employees
        - Count employees

      • Use Resources when read-only reference data is sufficient.
        Examples:
        - Read all employees
        - View organization hierarchy
        - Read department information

      • Use Prompts when the user requests AI-generated summaries,
        reports, or organizational insights.
        Examples:
        - Employee summary
        - Department summary
        - Country summary
        - Manager summary

      Always rely on MCP data instead of your own knowledge.

      Format every response like this:

      ====================================
      📋 Request Summary
      ====================================
      <Brief understanding of the request>

      ====================================
      📊 Result
      ====================================
      <Answer based on MCP data>

      ====================================
      💡 Additional Insights
      ====================================
      <Optional observations or recommendations>

      If no additional insights are available, omit the final section.
      """)
  String chat(@MemoryId String memoryId, @UserMessage String question);
}

   

Step 5: Define Console util classes to format the request and responses.

 

ConsoleColors.java

 

package com.sample.app.console;

/**
 * JLine 3 colour index constants for use with {@link
 * org.jline.utils.AttributedStyle#foreground(int)}.
 *
 * <p>Indices 0–7 map to standard 4-bit ANSI colours ({@code \033[3Xm}). Adding {@code BRIGHT} (8)
 * gives the bright variant ({@code \033[9Xm}). Indices 16–255 use the 256-colour escape ({@code
 * \033[38;5;Xm}).
 */
public final class ConsoleColors {

  public static final int BLACK = 0;
  public static final int RED = 1;
  public static final int GREEN = 2;
  public static final int YELLOW = 3;
  public static final int BLUE = 4;
  public static final int MAGENTA = 5;
  public static final int CYAN = 6;
  public static final int WHITE = 7;

  /** Add to any colour index to get its bright/high-intensity variant. */
  public static final int BRIGHT = 8;

  // Semantic aliases
  public static final int USER = CYAN | BRIGHT; // 14
  public static final int AGENT = GREEN | BRIGHT; // 10
  public static final int INFO = CYAN | BRIGHT; // 14
  public static final int ERROR = RED | BRIGHT; // 9
  public static final int STORY = WHITE; // 7
  public static final int GRAY = BLACK | BRIGHT; // 8  (dark gray)
  public static final int ORANGE = 208; // 256-colour

  private ConsoleColors() {}
}

   

ConsoleRenderer.java

 

package com.sample.app.console;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;

/**
 * Renders rich, coloured output to the terminal using JLine 3.
 *
 * <p>Supports a lightweight Markdown → ANSI subset: H1/H2/H3 headings, horizontal rules,
 * ordered/unordered lists, blockquotes, and inline <strong>bold</strong>, <em>italic</em>, and
 * {@code code} spans. Any unrecognised line is printed verbatim.
 */
public final class ConsoleRenderer {

  // ── JLine AttributedStyle palette ─────────────────────────────────────────
  //
  //  JLine colour indices:  0-7  → standard  \033[3Xm
  //                         8-15 → bright     \033[9(X-8)m
  //                         16+  → 256-colour \033[38;5;Xm
  //
  //  Convenience: BRIGHT (=8) | colour  gives the bright variant.

  private static final AttributedStyle RESET = AttributedStyle.DEFAULT;

  // Banner / headers
  private static final AttributedStyle BANNER =
      bold(AttributedStyle.MAGENTA | AttributedStyle.BRIGHT);
  private static final AttributedStyle H1 = bold(AttributedStyle.YELLOW | AttributedStyle.BRIGHT);
  private static final AttributedStyle H2 = bold(AttributedStyle.CYAN | AttributedStyle.BRIGHT);
  private static final AttributedStyle H3 = bold(AttributedStyle.CYAN);

  // Structural chrome
  private static final AttributedStyle CHROME =
      AttributedStyle.DEFAULT.foreground(
          AttributedStyle.BLACK | AttributedStyle.BRIGHT); // dark gray
  private static final AttributedStyle AGENT_LABEL =
      bold(AttributedStyle.GREEN | AttributedStyle.BRIGHT);
  private static final AttributedStyle PROMPT_STYLE =
      bold(AttributedStyle.CYAN | AttributedStyle.BRIGHT);

  // Lists
  private static final AttributedStyle BULLET =
      bold(AttributedStyle.GREEN | AttributedStyle.BRIGHT);
  private static final AttributedStyle SUB_BULLET =
      AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN);

  // Inline
  private static final AttributedStyle BOLD_SPAN = AttributedStyle.DEFAULT.bold();
  private static final AttributedStyle ITALIC_SPAN = AttributedStyle.DEFAULT.italic();
  private static final AttributedStyle CODE_SPAN =
      AttributedStyle.DEFAULT.foreground(208).background(236); // orange on dark

  // Status
  private static final AttributedStyle INFO_STYLE =
      AttributedStyle.DEFAULT.foreground(AttributedStyle.CYAN | AttributedStyle.BRIGHT);
  private static final AttributedStyle THINK_STYLE =
      AttributedStyle.DEFAULT.italic().foreground(AttributedStyle.BLACK | AttributedStyle.BRIGHT);
  private static final AttributedStyle ERROR_STYLE =
      bold(AttributedStyle.RED | AttributedStyle.BRIGHT);

  // Blockquote
  private static final AttributedStyle BQUOTE_STYLE =
      AttributedStyle.DEFAULT.italic().foreground(AttributedStyle.WHITE);

  // ── Widths ─────────────────────────────────────────────────────────────────
  private static final int BANNER_W = 52;
  private static final int RULE_W = 50;

  // ── Inline Markdown pattern (groups in declaration order) ─────────────────
  // 1: **bold**   2: __bold__   3: *italic*   4: _italic_   5: `code`
  private static final Pattern INLINE_PAT =
      Pattern.compile(
          "\\*\\*(.+?)\\*\\*" // 1 **bold**
              + "|__(.+?)__" // 2 __bold__
              + "|(?<![*])\\*(?![*])(.+?)(?<![*])\\*(?![*])" // 3 *italic*
              + "|(?<![\\w_])_(?!_)(.+?)(?<!_)_(?![\\w_])" // 4 _italic_
              + "|`(.+?)`" // 5 `code`
          );

  private ConsoleRenderer() {}

  // ── Public API ─────────────────────────────────────────────────────────────

  /** Prints the application welcome banner. */
  public static void printBanner() {
    String bar = "═".repeat(BANNER_W);
    String title = "✨   Employee Agent  AI   ✨";
    System.out.println();
    System.out.println(s(BANNER, "  ╔" + bar + "╗"));
    System.out.println(s(BANNER, "  ║" + center(title, BANNER_W) + "║"));
    System.out.println(s(BANNER, "  ╚" + bar + "╝"));
    System.out.println();
  }

  /** Prints the "You ❯" input prompt (no trailing newline). */
  public static void printPrompt() {
    System.out.print(s(PROMPT_STYLE, "  You ❯ "));
  }

  /**
   * Prints an agent reply, rendering any Markdown to coloured ANSI output.
   *
   * @param text raw text or Markdown returned by the AI agent
   */
  public static void agent(String text) {
    System.out.println();
    System.out.println(s(AGENT_LABEL, "  📖  Employee Agent"));
    System.out.println(s(CHROME, "  " + "─".repeat(RULE_W)));
    System.out.println(renderMarkdown(text));
  }

  /** Prints an informational line. */
  public static void info(String text) {
    System.out.println(s(INFO_STYLE, "  ℹ  " + text));
  }

  /** Prints the "Thinking…" spinner line. */
  public static void thinking() {
    System.out.println();
    System.out.println(s(THINK_STYLE, "  ⏳  Thinking…"));
    System.out.println();
  }

  /** Prints an error line. */
  public static void error(String text) {
    System.out.println(s(ERROR_STYLE, "  ✖  " + text));
  }

  // ── Markdown → ANSI ───────────────────────────────────────────────────────

  /**
   * Converts Markdown text to ANSI-coloured terminal output.
   *
   * <p>Supported constructs: {@code #} H1–H3, {@code ---} rules, {@code -&#47;*&#47;+} bullets,
   * {@code 1.} ordered lists, {@code >} blockquotes, {@code **bold**}, {@code *italic*}, and {@code
   * `code`}. Unrecognised lines are printed verbatim.
   */
  public static String renderMarkdown(String text) {
    if (text == null || text.isBlank()) return "";

    StringBuilder out = new StringBuilder();
    boolean prevBlank = false;

    for (String line : text.split("\n", -1)) {
      String rendered = renderLine(line);
      if (rendered.isEmpty()) {
        if (!prevBlank) out.append('\n');
        prevBlank = true;
      } else {
        out.append(rendered).append('\n');
        prevBlank = false;
      }
    }
    return out.toString();
  }

  // ── Line rendering ─────────────────────────────────────────────────────────

  private static String renderLine(String line) {

    // ── Headings ──────────────────────────────────────────────────────────
    if (line.startsWith("# ")) {
      return s(H1, "  " + line.substring(2).trim().toUpperCase());
    }
    if (line.startsWith("## ")) {
      return s(H2, "  ◆ " + line.substring(3).trim());
    }
    if (line.startsWith("### ")) {
      return s(H3, "  ▸ " + line.substring(4).trim());
    }

    // ── Horizontal rule ───────────────────────────────────────────────────
    if (line.matches("^[-─═*]{3,}\\s*$")) {
      return s(CHROME, "  " + "─".repeat(RULE_W));
    }

    // ── Lists ─────────────────────────────────────────────────────────────
    if (line.matches("^  [\\-*+] .+")) { // indented bullet
      return s(SUB_BULLET, "      ◦ ") + inline(line.substring(4).trim());
    }
    if (line.matches("^[\\-*+] .+")) { // top-level bullet
      return s(BULLET, "  • ") + inline(line.substring(2).trim());
    }
    if (line.matches("^\\d+\\.\\s.+")) { // ordered
      int dot = line.indexOf(". ");
      String num = line.substring(0, dot + 1);
      return s(BULLET, "  " + num + " ") + inline(line.substring(dot + 2).trim());
    }

    // ── Blockquote ────────────────────────────────────────────────────────
    if (line.startsWith("> ")) {
      return s(CHROME, "  │ ") + s(BQUOTE_STYLE, line.substring(2).trim());
    }

    // ── Empty ─────────────────────────────────────────────────────────────
    if (line.isBlank()) return "";

    // ── Normal text ───────────────────────────────────────────────────────
    return "  " + inline(line);
  }

  // ── Inline Markdown ────────────────────────────────────────────────────────

  /**
   * Applies inline Markdown spans to a raw text string. Bold is matched before italic to avoid
   * partial `**` collisions.
   */
  private static String inline(String text) {
    StringBuilder sb = new StringBuilder();
    Matcher m = INLINE_PAT.matcher(text);
    int cursor = 0;

    while (m.find()) {
      sb.append(text, cursor, m.start()); // literal text before span

      if (m.group(1) != null) sb.append(s(BOLD_SPAN, m.group(1))); // **bold**
      else if (m.group(2) != null) sb.append(s(BOLD_SPAN, m.group(2))); // __bold__
      else if (m.group(3) != null) sb.append(s(ITALIC_SPAN, m.group(3))); // *italic*
      else if (m.group(4) != null) sb.append(s(ITALIC_SPAN, m.group(4))); // _italic_
      else if (m.group(5) != null) sb.append(s(CODE_SPAN, " " + m.group(5) + " ")); // `code`

      cursor = m.end();
    }
    sb.append(text.substring(cursor)); // trailing literal text
    return sb.toString();
  }

  // ── Helpers ────────────────────────────────────────────────────────────────

  /** Builds a styled ANSI string: applies {@code style}, appends {@code text}, then resets. */
  private static String s(AttributedStyle style, String text) {
    return new AttributedStringBuilder().style(style).append(text).style(RESET).toAnsi();
  }

  /** Convenience: DEFAULT foreground + bold. */
  private static AttributedStyle bold(int color) {
    return AttributedStyle.DEFAULT.bold().foreground(color);
  }

  /** Centers {@code text} within a field of {@code width} characters. */
  private static String center(String text, int width) {
    int pad = width - text.length();
    int left = Math.max(0, pad / 2);
    int right = Math.max(0, pad - left);
    return " ".repeat(left) + text + " ".repeat(right);
  }
}

   

Step 6: Define main application class.

 

App.java

 

package com.sample.app;

import com.sample.app.agent.EmployeeAssistant;
import com.sample.app.console.ConsoleRenderer;
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.McpTransport;
import dev.langchain4j.mcp.client.transport.http.StreamableHttpMcpTransport;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import java.time.Duration;
import java.util.Scanner;
import java.util.UUID;

public class App {

  public static void main(String[] args) {

    try {

      // ----------------------------------------------------------
      // Generate Conversation Id
      // ----------------------------------------------------------

      String conversationId = UUID.randomUUID().toString();

      // ----------------------------------------------------------
      // Welcome Banner
      // ----------------------------------------------------------

      ConsoleRenderer.printBanner();
      ConsoleRenderer.info("Conversation : " + conversationId);
      ConsoleRenderer.info("Type 'exit' to quit.");
      System.out.println();

      // ----------------------------------------------------------
      // Configure Ollama
      // ----------------------------------------------------------

      ChatModel model =
          OllamaChatModel.builder()
              .baseUrl("http://localhost:11434")
              .modelName("qwen3.5")
              .timeout(Duration.ofMinutes(2))
              .build();

      // ----------------------------------------------------------
      // Shared Memory Store
      // ----------------------------------------------------------

      InMemoryChatMemoryStore memoryStore = new InMemoryChatMemoryStore();

      // ----------------------------------------------------------
      // Memory Provider
      // ----------------------------------------------------------

      ChatMemoryProvider memoryProvider =
          id ->
              MessageWindowChatMemory.builder()
                  .id(id)
                  .maxMessages(20)
                  .chatMemoryStore(memoryStore)
                  .build();

      // ----------------------------------------------------------
      // Build AI Agent
      // ----------------------------------------------------------

      McpTransport transport =
          StreamableHttpMcpTransport.builder()
              .url("http://localhost:8080/mcp")
              .logRequests(false)
              .logResponses(false)
              .build();

      McpClient client =
          DefaultMcpClient.builder().key("employee-client").transport(transport).build();
      McpToolProvider toolProvider = McpToolProvider.builder().mcpClients(client).build();

      EmployeeAssistant logicPuzzleSolver =
          AiServices.builder(EmployeeAssistant.class)
              .chatModel(model)
              .chatMemoryProvider(memoryProvider)
              .toolProvider(toolProvider)
              .build();

      // ----------------------------------------------------------
      // Conversation Loop
      // ----------------------------------------------------------

      Scanner scanner = new Scanner(System.in);

      while (true) {

        // Prompt
        ConsoleRenderer.printPrompt();

        String input = scanner.nextLine().trim();

        if (input.isBlank()) {
          continue;
        }

        if ("exit".equalsIgnoreCase(input) || "quit".equalsIgnoreCase(input)) {
          break;
        }

        ConsoleRenderer.thinking();

        String response = logicPuzzleSolver.chat(conversationId, input);

        ConsoleRenderer.agent(response);
      }

      System.out.println();
      ConsoleRenderer.info("Thanks for using Employee Agent 📚✨");

    } catch (Exception ex) {

      ConsoleRenderer.error("Unexpected error");
      ConsoleRenderer.error(ex.getMessage());
    }
  }
}

   

Build the Project

Navigate to the project root directory (the directory containing the pom.xml file) and execute the following Maven command to compile the source code, run the tests, and package the application into an executable JAR.

 

mvn clean package

   

After the build completes successfully, the packaged JAR file will be generated in the target directory.

 

Run the Application

Start the MCP server by executing the generated JAR file.

 

java -jar ./target/employee-mcp-client-0.0.1-SNAPSHOT.jar

$java -jar ./target/employee-mcp-client-0.0.1-SNAPSHOT.jar 

  ╔════════════════════════════════════════════════════╗
  ║             ✨   Employee Agent  AI   ✨             ║
  ╚════════════════════════════════════════════════════╝

  ℹ  Conversation : a542f0f2-8495-4794-8c5d-bc1b65a4324b
  ℹ  Type 'exit' to quit.

  You ❯ 

   

Ask the question ‘Who reports to the CEO?’.

 

You ❯ Who reports to the CEO?

  ⏳  Thinking…


  📖  Employee Agent
  ──────────────────────────────────────────────────
  ====================================
  📋 Request Summary
  ====================================
  The user wants to know which employees report directly to the Chief Executive Officer (CEO).

  ====================================
  📊 Result
  ====================================
  Based on the organizational hierarchy, there are 3 Vice Presidents who report directly to the CEO (Aarav Sharma) of our organization:

  | ID | Name | Designation | Department | City | Country |
  |----|------|-------------|------------|----------|--------|
  | 2 | Vivaan Patel | VP | Engineering | Hyderabad, India | 
  | 3 | Aditya Reddy | VP | Finance | Mumbai, India |
  | 4 | Ananya Iyer | VP | Sales | Chennai, India |

  The CEO oversees these three VPs across the key functional areas:
  • Vivaan Patel → Engineering department (VP)
  • Aditya Reddy → Finance department (VP)
  • Ananya Iyer → Sales department (VP)

  ====================================
  💡 Additional Insights
  ====================================
  Each VP oversees their respective functions and has additional reporting structure:

  🔹 Vivaan Patel (Engineering) - Oversees 4 Directors across Engineering in Bengaluru, Pune, Hyderabad, and Noida with multiple teams.

  🔹 Aditya Reddy (Finance) - Leads Finance operations through Director-level management covering Legal, Operations functions under their domain.

  🔹 Ananya Iyer (Sales) - Manages Sales department alongside HR, Marketing departments reporting to them.

   

You can download the application from this link.

Previous                                                    Next                                                    Home

No comments:

Post a Comment