CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-mcp-deployment

Quarkus build-time deployment extension for Model Context Protocol (MCP) client integration, handling configuration processing, synthetic bean generation, and framework integration

Overview
Eval results
Files

runtime-integration.mddocs/

Runtime Integration Reference

Runtime classes and integration points for MCP client functionality, authentication, health checks, and observability.

Overview

While the deployment module handles build-time processing, the runtime module (referenced in this document) provides classes that execute at application runtime. Application developers interact with these classes for authentication, health monitoring, log handling, and metrics collection.

Authentication

McpClientAuthProvider

Service Provider Interface for providing authentication credentials to MCP HTTP clients.

package io.quarkiverse.langchain4j.mcp.auth;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * SPI for providing authentication credentials to MCP HTTP/WebSocket clients.
 * Implement this interface to inject Authorization headers into MCP requests.
 *
 * Use @McpClientName qualifier to associate provider with specific client(s).
 */
public interface McpClientAuthProvider {
    /**
     * Provides the Authorization header value for an HTTP request.
     *
     * @param input Request context containing method, URI, and headers
     * @return Authorization header value (e.g., "Bearer token123")
     */
    String getAuthorization(Input input);

    /**
     * Resolves the auth provider for a specific MCP client.
     *
     * @param mcpClientName Client name
     * @return Resolved provider, if any
     */
    static Optional<McpClientAuthProvider> resolve(String mcpClientName) {
        // Implementation provided by runtime
    }

    /**
     * Request context for authentication.
     */
    interface Input {
        /**
         * HTTP method (GET, POST, etc.)
         *
         * @return HTTP method
         */
        String method();

        /**
         * Request URI
         *
         * @return Request URI
         */
        URI uri();

        /**
         * Request headers
         *
         * @return Map of header names to values
         */
        Map<String, List<Object>> headers();
    }
}

Implementation - Basic:

import io.quarkiverse.langchain4j.mcp.auth.McpClientAuthProvider;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class StaticTokenProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        return "Bearer " + System.getenv("MCP_API_TOKEN");
    }
}

Implementation - Per-Client:

import io.quarkiverse.langchain4j.mcp.auth.McpClientAuthProvider;
import io.quarkiverse.langchain4j.mcp.runtime.McpClientName;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@McpClientName("github")
public class GitHubAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        return "Bearer " + System.getenv("GITHUB_TOKEN");
    }
}

@ApplicationScoped
@McpClientName("gitlab")
public class GitLabAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        return "Bearer " + System.getenv("GITLAB_TOKEN");
    }
}

Implementation - Dynamic:

import io.quarkiverse.langchain4j.mcp.auth.McpClientAuthProvider;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
public class DatabaseTokenProvider implements McpClientAuthProvider {
    @Inject
    TokenRepository tokenRepository;

    @Inject
    SecurityContext securityContext;

    @Override
    public String getAuthorization(Input input) {
        String userId = securityContext.getCurrentUserId();
        String token = tokenRepository.getTokenForUser(userId);
        return "Bearer " + token;
    }
}

Resolution Rules:

  1. If provider has matching @McpClientName, use it
  2. If multiple providers match, selection is undefined
  3. If no specific match, use provider without @McpClientName
  4. If no provider matches, no authorization header is added

Health Checks

McpClientHealthCheck

MicroProfile Health readiness check for MCP clients.

package io.quarkiverse.langchain4j.mcp.runtime;

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
import jakarta.enterprise.context.ApplicationScoped;

/**
 * Readiness health check for MCP clients.
 * Automatically registered when:
 * - quarkus-smallrye-health extension is present
 * - quarkus.langchain4j.mcp.health.enabled=true (default)
 *
 * Checks health by:
 * - Sending MCP ping and waiting for pong (all transports)
 * - Calling MicroProfile health endpoint (HTTP transports, if configured)
 */
@Readiness
@ApplicationScoped
public class McpClientHealthCheck implements HealthCheck {
    /**
     * Performs health check on all configured MCP clients.
     *
     * @return Aggregated health check response
     */
    @Override
    public HealthCheckResponse call() {
        // Implementation provided by runtime
    }
}

Response Format:

Healthy:

{
  "status": "UP",
  "checks": [
    {
      "name": "MCP clients health check",
      "status": "UP",
      "data": {
        "github": "OK",
        "filesystem": "OK"
      }
    }
  ]
}

Unhealthy:

{
  "status": "DOWN",
  "checks": [
    {
      "name": "MCP clients health check",
      "status": "DOWN",
      "data": {
        "github": "OK",
        "filesystem": "Connection refused"
      }
    }
  ]
}

Configuration:

# Enable health check (default: true)
quarkus.langchain4j.mcp.health.enabled=true

# Ping timeout (per client)
quarkus.langchain4j.mcp.github.ping-timeout=10s

# Use MicroProfile health endpoint (HTTP only)
quarkus.langchain4j.mcp.remote.microprofile-health-check=true
quarkus.langchain4j.mcp.remote.microprofile-health-check-path=/q/health

Health Check Behavior:

  1. Iterates through all configured MCP clients
  2. For each client:
    • If HTTP transport and microprofile-health-check=true:
      • Attempts to call MicroProfile health endpoint
      • Falls back to ping/pong if endpoint returns 404
    • Otherwise:
      • Sends MCP ping message and waits for pong
  3. Aggregates results: overall DOWN if any client fails

Logging

QuarkusDefaultMcpLogHandler

Default handler for log messages received from MCP servers.

package io.quarkiverse.langchain4j.mcp.runtime;

import dev.langchain4j.mcp.client.logging.McpLogMessageHandler;
import dev.langchain4j.mcp.client.logging.McpLogMessage;
import jakarta.enterprise.context.ApplicationScoped;

/**
 * Default log handler for MCP client log events.
 * Automatically registered for all MCP clients.
 *
 * Behavior:
 * - Logs messages via JBoss Logger with appropriate level
 * - Fires CDI events for custom handling
 */
@ApplicationScoped
public class QuarkusDefaultMcpLogHandler implements McpLogMessageHandler {
    /**
     * Handles log message from MCP server.
     *
     * @param message Log message from server
     */
    @Override
    public void handleLogMessage(McpLogMessage message) {
        // Implementation provided by runtime:
        // 1. Log to JBoss Logger
        // 2. Fire CDI event with @McpClientName qualifier
    }
}

Observing Log Events:

import dev.langchain4j.mcp.client.logging.McpLogMessage;
import dev.langchain4j.mcp.client.logging.McpLogLevel;
import io.quarkiverse.langchain4j.mcp.runtime.McpClientName;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

@ApplicationScoped
public class McpLogObserver {
    // Observe logs from specific client
    public void onGitHubLog(@Observes @McpClientName("github") McpLogMessage log) {
        if (log.level() == McpLogLevel.ERROR) {
            alertOps("GitHub MCP error: " + log.data());
        }
    }

    // Observe logs from all clients
    public void onAnyLog(@Observes McpLogMessage log) {
        storeInDatabase(log);
    }

    // Observe specific log levels
    public void onError(@Observes McpLogMessage log) {
        if (log.level() == McpLogLevel.ERROR ||
            log.level() == McpLogLevel.CRITICAL) {
            handleError(log);
        }
    }
}

McpLogMessage API:

package dev.langchain4j.mcp.client.logging;

import com.fasterxml.jackson.databind.JsonNode;

/**
 * Log message from MCP server.
 */
interface McpLogMessage {
    /**
     * Log message data as JSON.
     *
     * @return Message data
     */
    JsonNode data();

    /**
     * Log level.
     *
     * @return Log level
     */
    McpLogLevel level();

    /**
     * Logger name from MCP server.
     *
     * @return Logger name
     */
    String logger();
}

/**
 * MCP log levels (from MCP specification).
 */
enum McpLogLevel {
    DEBUG,
    INFO,
    NOTICE,
    WARNING,
    ERROR,
    CRITICAL,
    ALERT,
    EMERGENCY
}

Tool Provider

QuarkusMcpToolProvider

Quarkus-integrated tool provider that aggregates tools from MCP clients.

package io.quarkiverse.langchain4j.mcp.runtime;

import dev.langchain4j.mcp.client.tool.McpToolProvider;
import jakarta.enterprise.context.ApplicationScoped;

/**
 * Tool provider integrating MCP clients with LangChain4j AI services.
 *
 * Features:
 * - Aggregates tools from all configured MCP clients
 * - Filters tools based on @McpToolBox annotation
 * - Integrates with OpenTelemetry for tracing (if available)
 * - Supports resource-as-tools conversion (if enabled)
 *
 * Automatically generated when:
 * - quarkus.langchain4j.mcp.generate-tool-provider=true (default)
 * - At least one MCP client is configured
 */
@ApplicationScoped
public class QuarkusMcpToolProvider extends McpToolProvider {
    // Implementation provided by runtime
    // Constructor is package-private
}

Usage (automatically injected into AI services with @McpToolBox):

import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.mcp.runtime.McpToolBox;
import dev.langchain4j.service.UserMessage;

@RegisterAiService
public interface Assistant {
    @McpToolBox("github")
    String chat(@UserMessage String message);
}

Resource-as-Tools:

When quarkus.langchain4j.mcp.expose-resources-as-tools=true:

// Synthetic tools automatically created:

/**
 * Lists all available resources from MCP server.
 */
String list_resources();

/**
 * Retrieves content of specific resource.
 *
 * @param uri Resource URI
 */
String get_resource(String uri);

Metrics

MetricsMcpListener

Micrometer-based metrics listener for MCP operations.

package io.quarkiverse.langchain4j.mcp.runtime;

import dev.langchain4j.mcp.client.McpClientListener;

/**
 * Micrometer metrics listener for MCP operations.
 *
 * Requirements:
 * - quarkus-micrometer-registry-* extension present
 * - quarkus.langchain4j.mcp.{client}.metrics.enabled=true
 *
 * Metrics recorded:
 * - mcp.client.tool.call.duration - Tool execution timer
 * - mcp.client.resource.get.duration - Resource retrieval timer
 * - mcp.client.prompt.get.duration - Prompt retrieval timer
 *
 * Tags:
 * - mcp_client: Client name
 * - tool_name: Tool name (tool calls only)
 * - outcome: success/failure/error
 */
public class MetricsMcpListener implements McpClientListener {
    // Implementation provided by runtime
}

Configuration:

# Enable metrics for specific client
quarkus.langchain4j.mcp.github.metrics.enabled=true

# Micrometer must be present
# Example: Prometheus registry
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-micrometer-registry-prometheus</artifactId>
</dependency>

Metrics Example (Prometheus format):

# Tool execution duration
mcp_client_tool_call_duration_seconds_sum{mcp_client="github",tool_name="search_repositories",outcome="success"} 1.234
mcp_client_tool_call_duration_seconds_count{mcp_client="github",tool_name="search_repositories",outcome="success"} 5

# Resource retrieval duration
mcp_client_resource_get_duration_seconds_sum{mcp_client="filesystem",outcome="success"} 0.123
mcp_client_resource_get_duration_seconds_count{mcp_client="filesystem",outcome="success"} 10

# Prompt retrieval duration
mcp_client_prompt_get_duration_seconds_sum{mcp_client="github",outcome="success"} 0.045
mcp_client_prompt_get_duration_seconds_count{mcp_client="github",outcome="success"} 3

DevUI Integration

McpClientsJsonRpcService

JSON-RPC service for DevUI to interact with MCP clients.

package io.quarkiverse.langchain4j.mcp.runtime.devui;

import java.util.List;

/**
 * JSON-RPC service providing DevUI backend for MCP clients.
 * Enables interactive exploration and testing of MCP tools.
 *
 * Access via: http://localhost:8080/q/dev-ui (development mode only)
 */
public class McpClientsJsonRpcService {
    /**
     * Retrieves information about all configured MCP clients.
     *
     * @return List of client information
     */
    public List<McpClientInfo> clientInfos() {
        // Implementation provided by runtime
    }

    /**
     * Executes a tool and returns the result.
     *
     * @param clientName MCP client name
     * @param toolName Tool name
     * @param arguments Tool arguments as JSON string
     * @return Tool execution result as JSON string
     */
    public String executeTool(String clientName, String toolName, String arguments) {
        // Implementation provided by runtime
    }
}

DevUI Data Models:

package io.quarkiverse.langchain4j.mcp.runtime.devui.json;

import java.util.List;

/**
 * MCP client information for DevUI.
 */
public class McpClientInfo {
    public String getCdiName();
    public void setCdiName(String cdiName);
    public List<McpToolInfo> getTools();
    public void setTools(List<McpToolInfo> tools);
}

/**
 * MCP tool information for DevUI.
 */
public class McpToolInfo {
    public String getName();
    public void setName(String name);
    public String getDescription();
    public void setDescription(String description);
    public String getExampleInput();
    public void setExampleInput(String exampleInput);
    public List<McpToolArgInfo> getArgs();
    public void setArgs(List<McpToolArgInfo> args);
}

/**
 * Tool argument information for DevUI.
 */
public class McpToolArgInfo {
    public String getName();
    public void setName(String name);
    public String getType();
    public void setType(String type);
    public String getDescription();
    public void setDescription(String description);
    public boolean isRequired();
    public void setRequired(boolean required);
}

DevUI Features:

  • Browse all configured MCP clients
  • View available tools with descriptions
  • See tool parameter schemas
  • Execute tools with example inputs
  • View execution results
  • Edit arguments before execution

Recorder Integration

McpRecorder

Bytecode recorder for runtime initialization (used by build processors).

package io.quarkiverse.langchain4j.mcp.runtime;

import io.quarkus.runtime.annotations.Recorder;
import java.util.function.Supplier;
import java.util.function.Function;

/**
 * Recorder for generating runtime initialization bytecode.
 * Used internally by build processors - not for direct application use.
 */
@Recorder
public class McpRecorder {
    /**
     * Records Claude Desktop config contents at static init.
     */
    public void claudeConfigContents(Map<String, LocalLaunchParams> contents);

    /**
     * Creates supplier for MCP client bean.
     */
    public Supplier<McpClient> mcpClientSupplier(
        String clientName,
        McpTransportType transportType,
        ShutdownContextBuildItem shutdown,
        Vertx vertx,
        boolean metricsEnabled
    );

    /**
     * Creates function for ToolProvider bean.
     */
    public Function<SyntheticCreationalContext<ToolProvider>, ToolProvider>
        toolProviderFunction(Set<String> mcpClientNames);

    /**
     * Creates supplier for registry client bean.
     */
    public Supplier<McpRegistryClient> mcpRegistryClientSupplier(String key);
}

Integration Patterns

Custom Authentication with Multiple Strategies

@ApplicationScoped
public class StrategyAuthProvider implements McpClientAuthProvider {
    @Inject
    AuthStrategyResolver resolver;

    @Override
    public String getAuthorization(Input input) {
        AuthStrategy strategy = resolver.resolveStrategy(input.uri());
        return strategy.getAuthorizationHeader();
    }
}

Monitoring and Alerting

@ApplicationScoped
public class McpMonitor {
    @Inject
    AlertService alertService;

    @Inject
    MetricsCollector metrics;

    public void onError(@Observes McpLogMessage log) {
        if (log.level() == McpLogLevel.ERROR) {
            alertService.sendAlert("MCP Error", log.data().toString());
            metrics.incrementErrorCount(log.logger());
        }
    }
}

Conditional Health Checks

import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;

@Readiness
@ApplicationScoped
public class CustomMcpHealthCheck implements HealthCheck {
    @Inject
    @McpClientName("critical")
    McpClient criticalClient;

    @Override
    public HealthCheckResponse call() {
        try {
            criticalClient.checkHealth();
            return HealthCheckResponse.up("critical-mcp");
        } catch (Exception e) {
            return HealthCheckResponse.down("critical-mcp");
        }
    }
}

Resource-Based Tool Usage

@RegisterAiService
public interface ResourceAssistant {
    @SystemMessage("""
        You have access to resources from MCP servers.
        Use list_resources to discover available resources.
        Use get_resource to retrieve resource content.
        """)
    @McpToolBox
    String chat(@UserMessage String message);
}

Observability Stack Integration

OpenTelemetry Tracing

Automatic when quarkus-opentelemetry is present:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-opentelemetry</artifactId>
</dependency>

Traces include:

  • Tool execution spans with tool name
  • Resource retrieval spans
  • MCP protocol operation spans

Request/Response Logging

# Global logging
quarkus.langchain4j.log-requests=true
quarkus.langchain4j.log-responses=true

# Per-client logging
quarkus.langchain4j.mcp.github.log-requests=true
quarkus.langchain4j.mcp.github.log-responses=true

# MCP-specific debug logging
quarkus.log.category."dev.langchain4j.mcp".level=DEBUG
quarkus.log.category."io.quarkiverse.langchain4j.mcp".level=DEBUG

Native Compilation

Automatic configuration for native images:

  • All MCP protocol classes registered for reflection
  • JSON serialization support
  • Runtime initialization of optional dependencies
  • WebSocket and HTTP client native support

No additional configuration required for native compilation.

Install with Tessl CLI

npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-mcp-deployment@1.7.0

docs

annotations.md

build-time-api.md

configuration.md

index.md

runtime-integration.md

tile.json