Embabel Framework - Confluence MCP Client

Build Confluence MCP Client with Embabel Framework

Learn to build a Confluence MCP client using Embabel framework. Connect to Confluence MCP server and execute operations with AI agents using natural language commands.

1. Introduction

Welcome back to our Embabel Framework series! If you’ve been following along, you’ve already learned how to build powerful AI agents that can interact with various systems using the Model Context Protocol (MCP). In this eighth installment, we’re going to build our second MCP client—one that connects to the Confluence MCP server we built earlier.

For those just joining us, here’s a quick recap of our journey so far:

  1. Embabel Framework: Create Goal-Driven AI Agents for the JVM: We covered the basics and built a shell-based meeting summarizer.:
  2. Embabel Framework: Build a REST API Based AI Agent: We learned how to expose agents over web endpoints.
  3. Embabel Framework Condition Annotation: Building Decision Making AI Agents: We added logic to our agents.
  4. Embabel Framework: How to Build MCP Server: We built a server that performs file operations.
  5. Build Confluence MCP Server Using Embabel Framework: We connected AI to the Confluence Cloud.
  6. Build MongoDB MCP Server Using Embabel Framework: We gave our agents database access.
  7. MCP Client: Build File Operations Agent with Embabel – We built our first MCP client for file operations

Today, we’re taking the next logical step: building an intelligent Confluence MCP client that can understand natural language commands and execute Confluence operations seamlessly. Think of it as giving your AI agent a direct line to your Confluence workspace!

2. What We’re Building – Confluence MCP Client

We’ll create a Spring Boot application that acts as an MCP client, connecting to our previously built Confluence MCP server. This client will allow users to:

  • List all Confluence spaces
  • List documents within a specific space
  • Create new Confluence pages
  • Fetch page history and version information
  • Retrieve document metadata

The best part? Users can interact with all these operations using simple, natural language commands like “List all spaces in Confluence” or “Create a new page titled ‘Q4 Planning’ in the ENG space.”



3. Prerequisites

Before we begin, make sure you have:

  1. Java 21 or higher installed
  2. Maven for dependency management
  3. Spring Boot knowledge (basic understanding is sufficient)
  4. A running instance of the Confluence MCP Server (from our fifth article)
  5. An OpenRouter API key (for accessing free AI models)


4. Project Structure

Before we dive into the code, let’s understand how our project is organized:

embabel-confluence-mcp-client
├── src
│   └── main
│       ├── java
│       │   └── com
│       │       └── bootcamptoprod
│       │               ├── agents
│       │               │   └── SmartConfluenceAgent.java          # AI Agent that interprets natural language for Confluence
│       │               ├── config
│       │               │   ├── ConfigureOpenRouterModels.java     # Connects Embabel to LLMs via OpenRouter
│       │               │   └── ConfluenceToolsConfig.java         # Connects to MCP Server and groups Confluence tools
│       │               ├── controller
│       │               │   └── ConfluenceOperationsController.java # REST API to interact with the Confluence AI Agent
│       │               ├── dto                                    # Data Transfer Objects (Request/Response)
│       │               │   ├── ConfluenceRequest.java             # Natural language command input
│       │               │   └── ConfluenceResponse.java            # Response containing Confluence metadata and status
│       │               └── EmbabelConfluenceMcpClientApplication.java # The main Spring Boot application class
│       └── resources
│           └── application.yml                                    # MCP server URL and LLM configurations
└── pom.xml                                                        # Project dependencies and Embabel BOM
Project Structure

Structure Breakdown:

Here’s a quick breakdown of what each key file and package does:

  • SmartConfluenceAgent.java: This is the “Brain.” It analyzes your text command (e.g., “Create a page in the Eng space”) and uses the LLM to decide which Confluence tool to call.
  • ConfigureOpenRouterModels.java: Handles the connection to the LLM (Large Language Model). We use OpenRouter to access models like DeepSeek to provide the “reasoning” for our agent.
  • ConfluenceToolsConfig.java: This class acts as a bridge. It connects to the external Confluence MCP server and registers its tools so the AI Agent can see and use them.
  • ConfluenceOperationsController.java: A standard Spring RestController. It takes your HTTP request and hands it over to the Embabel agent to process.
  • dto package: These are simple Java Records that define the “Contracts” for data moving between your API, the AI, and the Confluence server.
  • application.yml: The configuration hub. Here, we define the MCP server’s URL (localhost:8080/sse) and set the default LLM.
  • pom.xml: Manages the project dependencies, specifically the embabel-agent-starter and the OpenAI compatibility libraries.


5. Diving Into the Code

Let’s break down each component of our application.

Step 1: Setting Up Maven Dependencies

First, let’s set up our Maven project with all necessary dependencies. The pom.xml file defines our project configuration and required libraries:

<properties>
	<java.version>21</java.version>
	<embabel-agent.version>0.3.2-SNAPSHOT</embabel-agent.version>
</properties>

<dependencies>
	<!-- Spring Boot Web for REST endpoints -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!-- Spring Boot Actuator for health checks -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-actuator</artifactId>
	</dependency>
	<!-- Embabel Agent Starter - Core framework -->
  <dependency>
    <groupId>com.embabel.agent</groupId>
    <artifactId>embabel-agent-starter</artifactId>
  </dependency>
  <!-- OpenAI Compatible Model Support -->
  <dependency>
    <groupId>com.embabel.agent</groupId>
    <artifactId>embabel-agent-starter-openai</artifactId>
  </dependency>	
</dependencies>

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>com.embabel.agent</groupId>
			<artifactId>embabel-agent-dependencies</artifactId>
			<version>${embabel-agent.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

<repositories>
	<repository>
		<id>embabel-releases</id>
		<url>https://repo.embabel.com/artifactory/libs-release</url>
		<releases>
			<enabled>true</enabled>
		</releases>
		<snapshots>
			<enabled>false</enabled>
		</snapshots>
	</repository>
	<repository>
		<id>embabel-snapshots</id>
		<url>https://repo.embabel.com/artifactory/libs-snapshot</url>
		<releases>
			<enabled>false</enabled>
		</releases>
		<snapshots>
			<enabled>true</enabled>
		</snapshots>
	</repository>
</repositories>
pom.xml

What’s Important Here:

  • This Maven configuration sets up our Spring Boot project with Java 21, ensuring we can use the latest Java features for our AI agent.
  • The core dependency is embabel-agent-starter. This is the engine that provides the framework for building goal-driven AI agents. It handles the internal logic of how an agent plans tasks and executes actions based on the tools it has available.
  • We have included embabel-agent-starter-openai. Since this is an MCP Client, we need a “brain” (the LLM) to do the reasoning. This dependency allows our agent to communicate with OpenAI-compatible providers like OpenRouter. The LLM will analyze your natural language request (e.g., “Find the documentation for the API in the Engineering space“) and decide which specific MCP tool to call to get the job done.
  • The spring-boot-starter-web dependency is essential here because it allows us to expose our AI agent through REST endpoints. This way, we can trigger confluence operations from external tools like Postman or a frontend application.
  • We also included custom repositories. These are necessary to fetch the Embabel framework artifacts, as they are hosted on Embabel’s own repository.
  • Finally, the Actuator dependency is included for production-readiness, providing built-in endpoints to monitor the health and metrics of our client application.

Step 2: Configure Application Properties

Next, let’s configure our application settings in application.yml:

spring:
  application:
    name: embabel-confluence-mcp-client
  ai:
    mcp:
      client:
        enabled: true
        name: embabel-confluence-mcp-client
        version: 1.0.0
        type: SYNC
        sse:
          connections:
            # Connection to the Confluence MCP Server
            embabel-confluence-mcp-server:
              url: http://localhost:8080/sse # The URL of your MCP server

server:
  port: 8081

embabel:
  models:
    defaultLlm: nex-agi/deepseek-v3.1-nex-n1:free
application.yaml

📄 Configuration Overview

  • spring.ai.mcp.client.enabled: Enables MCP client functionality in our application
  • spring.ai.mcp.client.type: Set to SYNC for synchronous communication with MCP servers
  • sse.connections: Defines the MCP server connection details
    • embabel-confluence-mcp-server: A unique identifier for our confluence server
    • url: The SSE (Server-Sent Events) endpoint of our MCP server running on port 8080. Must match our Confluence MCP server’s SSE endpoint
  • server.port: Our client runs on port 8081 to avoid conflicts with the MCP server
  • embabel.models.defaultLlm: The AI model we’ll use (a free DeepSeek model via OpenRouter)

Step 3: Application Entry Point

This is the main class that bootstraps our entire application.

package com.bootcamptoprod;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class EmbabelConfluenceMcpClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(EmbabelConfluenceMcpClientApplication.class, args);
    }

}
EmbabelConfluenceMcpClientApplication.java

Explanation:

  • This is our Spring Boot application entry point. This class bootstraps both Spring Boot and Embabel frameworks simultaneously.


Step 4: Configuring LLM Models

This configuration class sets up multiple LLM models via OpenRouter, a unified API gateway for various AI models. Here’s what’s happening:

package com.bootcamptoprod.config;

import com.embabel.agent.openai.OpenAiCompatibleModelFactory;
import com.embabel.common.ai.model.Llm;
import com.embabel.common.ai.model.PerTokenPricingModel;
import io.micrometer.observation.ObservationRegistry;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDate;

@Configuration
public class ConfigureOpenRouterModels extends OpenAiCompatibleModelFactory {

    public ConfigureOpenRouterModels(@NotNull ObservationRegistry observationRegistry,
                                     @Value("${OPENAI_API_KEY}") String apiKey) {
        super(
                "https://openrouter.ai",
                apiKey,
                "/api/v1/chat/completions",
                null,
                observationRegistry
        );
    }

    @Bean
    public Llm mistral7b_free() {
        return openAiCompatibleLlm(
                "mistralai/mistral-7b-instruct:free",
                new PerTokenPricingModel(0.0, 0.0),
                "OpenRouter",
                LocalDate.of(2024, 10, 1)
        );
    }

    @Bean
    public Llm deepseek_r1_t2() {
        return openAiCompatibleLlm(
                "tngtech/deepseek-r1t2-chimera:free",
                new PerTokenPricingModel(0.0, 0.0),
                "OpenRouter",
                LocalDate.of(2025, 5, 28)
        );
    }

    @Bean
    public Llm deepseek_3_1() {
        return openAiCompatibleLlm(
                "nex-agi/deepseek-v3.1-nex-n1:free",
                new PerTokenPricingModel(0.0, 0.0),
                "OpenRouter",
                LocalDate.of(2025, 1, 28)
        );
    }

    @Bean
    public Llm glm_4_5_air() {
        return openAiCompatibleLlm(
                "z-ai/glm-4.5-air:free",
                new PerTokenPricingModel(0.0, 0.0),
                "OpenRouter",
                LocalDate.of(2025, 5, 28)
        );
    }
}
ConfigureOpenRouterModels.java

Explanation:

  • This configuration class registers OpenRouter as an AI model provider.
  • By extending OpenAiCompatibleModelFactory, we leverage OpenRouter’s OpenAI-compatible API.
  • The constructor configures the base URL and endpoint for OpenRouter API.
  • The @Value("${OPENAI_API_KEY}") annotation injects your API key, which you must provide as an environment variable when running the application.
  • The LLM beans methods create different model instances with zero pricing (useful for free tier or internal tracking) and metadata about the model.
  • The PerTokenPricingModel(0.0, 0.0) indicates these are free-tier models with no cost per token.
  • The ObservationRegistry integration enables automatic metrics collection for model usage.

Why Multiple Models? Having multiple models configured gives you flexibility to switch between them without code changes


Step 5: Configuring MCP Tool Groups

This is where the magic happens! We need to tell our application which tools from the MCP server our agents can use.

package com.bootcamptoprod.config;

import com.embabel.agent.core.ToolGroup;
import com.embabel.agent.core.ToolGroupDescription;
import com.embabel.agent.core.ToolGroupPermission;
import com.embabel.agent.tools.mcp.McpToolGroup;
import io.modelcontextprotocol.client.McpSyncClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.util.Assert;

import java.util.List;
import java.util.Set;

@Configuration
public class ConfluenceToolsConfig {

    private final List<McpSyncClient> mcpSyncClients;

    @Autowired
    public ConfluenceToolsConfig(@Lazy List<McpSyncClient> mcpSyncClients) {
        Assert.notNull(mcpSyncClients, "McpSyncClients must not be null");
        this.mcpSyncClients = mcpSyncClients;
    }

    @Bean
    public ToolGroup confluenceMcpToolGroup() {
        return new McpToolGroup(
                ToolGroupDescription.Companion.invoke(
                        "Confluence operations including list spaces, list documents, create documents, get history, and get metadata via MCP",
                        "confluence-mcp-tool-group"  // ← MUST match toolGroups = {"confluence-mcp-tool-group"} in agent
                ),
                "Confluence Provider",
                "confluence-tool-group",
                Set.of(ToolGroupPermission.INTERNET_ACCESS),
                mcpSyncClients,
                toolCallback -> {
                    String toolName = toolCallback.getToolDefinition().name().toLowerCase();
                    return toolName.contains("space") ||
                            toolName.contains("document") ||
                            toolName.equals("page");
                }
        );
    }
}
ConfluenceToolsConfig.java

What’s Important Here:

  • The “Bridge” to Confluence: This class serves as the “Bridge” between the external Confluence MCP server (the one we built in Article 5) and our AI Agent. Its main job is to discover the tools available on that server and organize them so the agent can understand and use them.
  • The Communication Lines: We are injecting a list of McpSyncClient objects. These clients are created based on the connection details we provided in our application.yml.
  • The Tool Group Configuration: The star of this class is the confluenceMcpToolGroup bean. This creates an McpToolGroup, which is essentially a collection of tools. There are three critical parts to this configuration:
    • The Identifier: we assigned this group the ID “confluence-mcp-tool-group”. This is a “handshake” name—it must exactly match the toolGroups name used in our SmartConfluenceAgent.java. If these names don’t match perfectly, the agent won’t be able to “see” its toolbox!
    • Permissions: We set the permission to INTERNET_ACCESS. Since Confluence is a cloud-based or remote platform, this tells the Embabel framework that these tools are allowed to reach out the network to fetch the confluence pages or update documents.
    • The Tool Filter (The Logic): This is the “smart” part. An MCP server might offer many different functions, but we want this agent to be a Confluence specialist. We use a filter to look for tool names containing “space”“document”, or “page”. This ensures the agent only focuses on relevant tasks and doesn’t get confused by tools it doesn’t need.

Step 6: Create Data Transfer Object (DTOs)

Our DTOs define the structure of data flowing through the application.

package com.bootcamptoprod.dto;

public record ConfluenceRequest(
        String command
) {}
ConfluenceRequest.java

Purpose: A simple record that holds the user’s natural language command (e.g., “Create a new page titled ‘Project Roadmap’ in the ‘ENG’ space”). This is the only input our smart agent needs to begin interpreting your request.


package com.bootcamptoprod.dto;

import java.util.Map;

public record ConfluenceResponse(
        Map<String, Object> confluenceMetadata,
        String message,
        boolean success,
        String operationType
) {}
ConfluenceResponse.java

Purpose: This response record contains the results of a Confluence operation. It includes a flexible confluenceMetadata map to hold various Confluence-specific information (like page IDs, space keys, titles), a human-readable message, a success boolean flag, and an operationType string indicating what kind of operation was performed (e.g., “LIST_SPACES”, “CREATE_DOCUMENT”)




Step 7: Building the Confluence Agent

This is where the magic happens! Our agent understands natural language and executes appropriate Confluence operations:

package com.bootcamptoprod.agents;

import com.bootcamptoprod.dto.ConfluenceRequest;
import com.bootcamptoprod.dto.ConfluenceResponse;
import com.embabel.agent.api.annotation.AchievesGoal;
import com.embabel.agent.api.annotation.Action;
import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.common.OperationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Agent(
        name = "smart-confluence-agent",
        description = "Intelligent agent that understands natural language commands and performs Confluence operations via MCP",
        version = "1.0.0"
)
public class SmartConfluenceAgent {

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

    @Action(
            description = "Process natural language Confluence operation requests and execute appropriate Confluence operations",
            toolGroups = {"confluence-mcp-tool-group"}
    )
    @AchievesGoal(description = "Understand user's natural language command and execute the appropriate Confluence operation")
    public ConfluenceResponse processConfluenceOperation(ConfluenceRequest request, OperationContext context) {

        log.info("[ACTION] processConfluenceOperation START - user command: {}", request.command());

        // Construct a detailed prompt for the LLM to understand and execute the command
        String prompt = String.format("""
                You are a Confluence operations assistant. The user has given you a command in natural language.
                Analyze the command and perform the appropriate Confluence operation using the available tools.
                
                User Command: "%s"
                
                Available Operations:
                1. LIST SPACES - List all Confluence spaces
                2. LIST DOCUMENTS - List all documents/pages in a specific space
                3. CREATE DOCUMENT - Create a new page in a space
                4. GET PAGE HISTORY - Fetch version history of a document
                5. GET DOCUMENT METADATA - Retrieve metadata for a specific document
                
                Your Task:
                1. Analyze the user's command to determine which operation they want to perform
                2. Extract any required parameters from the command:
                   - spaceKey: The key/identifier of the Confluence space (e.g., "ENG", "DOCS")
                   - documentId: The unique identifier of a Confluence page
                   - title: The title for a new document
                   - content: The content/body for a new document
                3. Use the appropriate MCP tool if required
                4. Draft a user-friendly response that includes:
                   - A clear success/failure message
                   - Relevant Confluence metadata (page title, page ID, space title, space ID, etc.)
                   - The operation type that was performed
                   - Any additional helpful information from the response
                5. In case of missing details like space key, space id, page id or document id use respective tools to fetch the information if it is not provided by user
                
                Important Guidelines:
                - Always include relevant Confluence metadata in your response (page IDs, space keys, titles, etc.)
                - Make the response conversational and easy to understand
                - If parameters are missing or unclear, use your best judgment or ask for clarification
                - Include page/space identifiers so users can reference them in future operations
                - Format dates and technical details in a user-friendly way
                
                Execute the Confluence operation and provide a comprehensive response.
                """, request.command());

        // Invoke the LLM with access to Confluence MCP tools to execute the operation
        ConfluenceResponse response = context.ai()
                .withDefaultLlm()
                .createObject(prompt, ConfluenceResponse.class);

        log.info("[ACTION] processConfluenceOperation END");

        return response;
    }
}
SmartConfluenceAgent.java

What’s Important Here:

  • The “Brain” of the Application: While our MCP server (from Article 5) acts as the “Hands” that physically talk to the Confluence API, this Agent is the “Brain.” It has the intelligence to understand what a user wants and decide exactly which tools to use to make it happen.
  • @Agent Annotation: This tells the Embabel framework that this isn’t just a regular Java class—it’s a “smart” agent capable of understanding human language.
  • The Single Action Approach: Notice that we don’t have separate methods for “create page,” “list spaces,” or “delete page.” We have just one action that handles everything! This keeps our code incredibly clean.
  • @Action with toolGroups: This is the critical link. By adding toolGroups = {“confluence-mcp-tool-group”}, we are handing the agent a “toolbox” containing all the Confluence operations discovered from our MCP server.
  • @AchievesGoal: This annotation clearly defines the agent’s mission: to take a natural language command from a human and turn it into a successful Confluence operation.
  • The Magic Prompt: The prompt inside the method acts as the agent’s “instruction manual.” It tells the AI:
    • Its Role: You are a “Confluence operations assistant.”
    • The Input: Here is what the user said (the command).
    • The Strategy: Analyze the intent, extract details (like spaceKey, title, or pageId), and choose the right tool from the toolbox.
    • The Output Format: Always return a structured response with clear metadata so the user knows exactly what happened.
  • context.ai().withDefaultLlm(): This tells the agent to use our configured “reasoning engine” (like DeepSeek) to think through the problem.
  • createObject(): This is where the magic happens! The AI performs a complex series of steps in the background:
    1. Analyzes the command (e.g., “Make a new page…”).
    2. Determines which tool is needed (create_document).
    3. Extracts parameters like the space key and the page content automatically.
    4. Calls the MCP tool on the server for you.
    5. Parses the technical response from Confluence.
    6. Returns a neatly packaged ConfluenceResponse object.

The beauty of this approach: You don’t have to write complex if-else logic or manual API mapping. You just give the AI a goal and a toolbox, and it figures out the rest—which tool to call, what parameters to pass, and how to explain the result back to the user!


Step 8: Creating the REST Controller

Finally, let’s expose our agent’s capabilities through REST API:

package com.bootcamptoprod.controller;

import com.bootcamptoprod.dto.ConfluenceRequest;
import com.bootcamptoprod.dto.ConfluenceResponse;
import com.embabel.agent.api.invocation.AgentInvocation;
import com.embabel.agent.core.AgentPlatform;
import com.embabel.agent.core.ProcessOptions;
import com.embabel.agent.core.Verbosity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/confluence")
public class ConfluenceOperationsController {

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

    private final AgentPlatform agentPlatform;
    private final ProcessOptions processOptions;

    public ConfluenceOperationsController(AgentPlatform agentPlatform) {
        this.agentPlatform = agentPlatform;

        // Configure verbosity for detailed logging of agent operations
        Verbosity verbosity = Verbosity.DEFAULT
                .showLlmResponses()
                .showPrompts()
                .showPlanning();

        this.processOptions = ProcessOptions.DEFAULT.withVerbosity(verbosity);
    }

    @PostMapping
    public ConfluenceResponse executeConfluenceOperation(@RequestBody ConfluenceRequest request) {
        log.info("Natural language Confluence operation request received: {}", request.command());

        // Build agent invocation with configured process options
        var agentInvocation = AgentInvocation
                .builder(agentPlatform)
                .options(processOptions)
                .build(ConfluenceResponse.class);

        // Invoke the agent to process the request
        ConfluenceResponse response = agentInvocation.invoke(request);

        log.info("Confluence operation result - success: {}, operation: {}, message: {}",
                response.success(), response.operationType(), response.message());

        return response;
    }
}
ConfluenceOperationsController.java

Explanation:

This class acts as the “Front Door” or entry point of our application. While the Agent contains the intelligence and the MCP Server contains the tools, the Controller provides the REST API endpoint that allows us (or our frontend) to actually trigger the AI to perform tasks in Confluence.

  • Single Endpoint: We have just one endpoint (POST /api/v1/confluence) that handles every type of Confluence operation. Whether you want to list spaces or create a new document, you send your request here, and the AI figures out what to do.
  • AgentPlatform: Injected by Spring, this serves as the “Command Center.” It keeps track of all our registered agents, the tools we’ve imported from the MCP server, and our active connections.
  • Verbosity settings: Inside the constructor, we configure detailed logging. This allows us to see the LLM’s responses, the specific prompts being sent, and the internal planning steps. This is a lifesaver for debugging and seeing “how” the AI makes its decisions.
  • AgentInvocation: Think of this as the “Mission Launcher.” For every incoming web request, we build an AgentInvocation. It uses a clean, fluent API to handle the execution:
    • builder(agentPlatform): Initialize with the platform
    • .options(processOptions): Configure execution options
    • .build(ConfluenceResponse.class): Specify the expected response type
    • .invoke(request): Execute the agent with the request

This controller acts as a thin layer between standard HTTP requests and our intelligent agent, making it incredibly easy to integrate Confluence automation into any external tool like Postman, a Slack bot, or a custom company dashboard!



6. Testing the Application

Now let’s test our MCP client! Follow these steps carefully:

Step 1: Start the Confluence MCP Server

First, you need to have your Confluence MCP Server running on port 8080:

cd /path/to/embabel-confluence-mcp-server
mvn clean install
export OPENAI_API_KEY="your-openrouter-api-key"
export CONFLUENCE_AUTH_TOKEN="confluence-base64-encoded-token"
export CONFLUENCE_BASE_URL="https://your-domain.atlassian.net/wiki/api/v2"
mvn spring-boot:run
Terminal

Wait until you see logs indicating the server is running successfully on port 8080.

Step 2: Start the MCP Client Application

In a new terminal window, navigate to your MCP client project, set your OpenRouter API key, and run:

cd /path/to/embabel-confluence-mcp-client
mvn clean install
export OPENAI_API_KEY=your_openrouter_api_key_here
mvn spring-boot:run
Terminal

Your MCP client should start on port 8081. You’ll see logs showing the MCP connection being established.

Step 3: Test the application using a tool like Postman, Insomnia, or curl by sending a POST request to http://localhost:8081/api/v1/confluence:

  • List Spaces: {“command”: “List all available spaces in my Confluence”}
  • List Documents: {“command”: “Show me all the pages inside the ‘Software development’ space”}
  • Create Page: {“command”: “Create a new page in the ‘Software development’ space titled ‘Project Roadmap’ with content ‘This is our initial project plan for the year.'”}
  • Get Metadata: {“command”: “Retrieve the metadata and details for the ‘Project Roadmap’ page in the ‘Software development’ space”}
  • Get Page History: {“command”: “Show me the version history for the ‘Project Roadmap’ document”}


6. Understanding the Agent Workflow

Let’s trace exactly what happens when you send the natural language command: “List all Confluence spaces”:

  1. Request Reception: Our REST controller acts as the front door, receiving your POST request containing the command.
  2. Agent Invocation: The AgentInvocation (our “Mission Launcher”) routes the command directly to the SmartConfluenceAgent.
  3. Prompt Construction: Inside the Agent, Embabel builds a detailed prompt—a set of instructions that explains the task and the user’s intent to the LLM.
  4. LLM Analysis: The “Brain” (DeepSeek v3.1) analyzes the text “List all Confluence spaces” and intelligently identifies it as a LIST_SPACES intent.
  5. Tool Discovery: The LLM looks into the confluence-mcp-tool-group (our “Toolbox”) to see what functions are available to fulfill this request.
  6. Tool Selection: The LLM identifies and selects the specific MCP tool required (e.g., list_confluence_spaces) from the server.
  7. MCP Communication: The MCP client initiates a “handshake” and sends the tool execution request to our Confluence MCP Server via the SSE (Server-Sent Events) connection.
  8. Server Execution: The Confluence MCP Server (the “Hands”) receives the call and executes the actual API request against the real Confluence platform.
  9. Result Processing: Confluence sends the raw data back to the MCP server, which then passes it back to our client application.
  10. Response Formatting: The LLM takes that raw technical data and formats it into a conversational, user-friendly response.
  11. Object Mapping: Embabel automatically maps this final “thought” from the AI into our structured Java record, the ConfluenceResponse.
  12. JSON Response: Finally, the controller sends the structured JSON response back to you (the caller).

While we are using the “List all Confluence spaces” command as our primary example to trace the journey, it is important to remember that this same intelligent flow applies to every single operation—whether you are creating a page, fetching history, or metadata!

7. Video Tutorial

For a complete step-by-step walkthrough, check out our video tutorial. We build the Smart Confluence MCP Client from scratch, demonstrating how a simple natural language command can manage your documentation—listing spaces and creating pages—through the power of Embabel and the Model Context Protocol!

📺 Watch on YouTube:

8. Source Code

The full source code for our Smart Confluence MCP Client is available on our GitHub repository. The best way to learn is by doing, so we encourage you to clone the repo, set your OPENAI_API_KEY (from OpenRouter or OpenAI) as an environment variable, and launch the Spring Boot application.

Important Note: Remember to have your Confluence MCP Server (which we built in our fifth article) running on port 8080 before starting this client. This setup will allow you to interact with your AI agent using natural language and watch it manage your Confluence instance through the seamless integration of the Embabel framework and the Model Context Protocol.

🔗 Embabel Confluence MCP Client: https://github.com/BootcampToProd/embabel-confluence-mcp-client

9. Things to Consider

As you build and deploy your Confluence AI Agent, keep these critical points in mind to ensure a smooth and secure experience:

  1. Sequential Startup: Always start your Confluence MCP Server (Port 8080) before the Client (Port 8081). The client performs a “handshake” upon startup to discover which tools (like list_spaces or create_page) are available.
  2. LLM Connection & API Keys: Ensure your OPENAI_API_KEY for OpenRouter is correctly set in your environment variables. Without this, the “Brain” of your agent won’t be able to process any natural language.
  3. AI Model Selection (Function Calling): The intelligence of your agent depends heavily on the model you choose. Ensure your selected model (like DeepSeek v3 or GPT-4o) explicitly supports Tool/Function Calling. This is the specialized capability that allows the AI to translate a sentence like “Make a page” into a technical execution of a specific MCP tool.
  4. Port Management: Since you are running both a server and a client on the same machine, ensure there is no conflict. In our setup, we use 8080 for the Server and 8081 for the Client.
  5. Prompt Engineering for Confluence: The prompt is the agent’s “instruction manual.” For Confluence, it is helpful to instruct the AI to always ask for a Space Key if one isn’t provided, or to list spaces first if it’s unsure where to create a document.
  6. Handling Ambiguity: Natural language can be vague (e.g., “Update the roadmap”). Since there might be multiple “roadmap” pages, you should refine your agent’s prompt to either search for the most recent page or ask the user for a specific Page ID.
  7. MCP Server Availability: Your client is essentially a “Brain” without “Hands” if the MCP server goes down. In a production environment, you should implement proper error handling to inform the user if the Confluence service is currently unreachable.
  8. Security & Data Privacy: Confluence often contains sensitive company data. Never hardcode your API keys, and ensure that your AI agent logic includes validation to prevent “Prompt Injection” (where a user might try to trick the AI into deleting all pages in a space).
  9. Testing with Phrasing Variations: Unlike traditional APIs with fixed inputs, users might ask for the same thing in ten different ways. Build a test suite that checks if your agent understands “Create a page,” “Make a new doc,” and “Start a new wiki entry” all as the same operation.


10. FAQs

What if my Confluence MCP server is on a different host?

How do I handle multiple MCP servers?

Can I customize the response format?

Can I use this pattern for other systems besides Confluence?

Does this client application need my Confluence username or API Token?

Can I connect one client to multiple MCP servers at once?

11. Conclusion

Building our second MCP client marks a significant milestone in our journey with the Embabel framework, as we have successfully bridged the gap between static documentation and dynamic AI intelligence. By decoupling the “intelligence” of the client from the “connectivity” of the Confluence server, we have created a modular architecture that allows us to manage complex enterprise data using simple, natural language commands. This approach demonstrates how easily you can empower your AI agents to perform real-world tasks across different platforms. Whether you are automating documentation updates or building a smart knowledge-base assistant, the combination of Embabel and the Model Context Protocol provides the perfect foundation for the next generation of JVM-based AI applications.



12. Learn More

#

Interested in learning more?

MCP Client: Build File Operations Agent with Embabel



Add a Comment

Your email address will not be published.