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

build-time-api.mddocs/

Build-Time API Reference

Build-time API for Quarkus extension developers extending or integrating with the MCP deployment module.

Overview

The MCP deployment module provides build items and build processors that operate during Quarkus build phase. Extension developers can consume these build items or contribute additional functionality.

Build Items

Build items communicate information between build steps in the Quarkus build pipeline.

McpConfigFileContentsBuildItem

Represents parsed contents of a Claude Desktop configuration file.

package io.quarkiverse.langchain4j.mcp.deployment;

import io.quarkus.builder.item.SimpleBuildItem;
import io.quarkiverse.langchain4j.mcp.runtime.config.LocalLaunchParams;
import java.util.Map;

/**
 * Build item containing parsed MCP server configurations from Claude Desktop config file.
 * Produced when quarkus.langchain4j.mcp.config-file is specified.
 */
public final class McpConfigFileContentsBuildItem extends SimpleBuildItem {
    /**
     * Constructs build item with parsed configuration contents.
     *
     * @param contents Map of server name to launch parameters
     */
    public McpConfigFileContentsBuildItem(Map<String, LocalLaunchParams> contents);

    /**
     * Returns the parsed MCP server configurations.
     * Key: Server name (used as MCP client name)
     * Value: Launch parameters (command and environment)
     *
     * @return Map of server configurations
     */
    public Map<String, LocalLaunchParams> getContents();
}

Usage Example:

import io.quarkiverse.langchain4j.mcp.deployment.McpConfigFileContentsBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import java.util.Optional;

class CustomExtensionProcessor {
    @BuildStep
    void consumeClaudeConfig(Optional<McpConfigFileContentsBuildItem> configItem) {
        if (configItem.isPresent()) {
            var contents = configItem.get().getContents();
            contents.forEach((serverName, params) -> {
                // Access server name and launch parameters
                var command = params.command();
                var envVars = params.envVars();
                // Custom processing logic
            });
        }
    }
}

Build Processors

Build processors contain @BuildStep methods that execute during the build phase.

McpProcessor

Main build processor for the MCP extension. Located in io.quarkiverse.langchain4j.mcp.deployment package.

Note: This class is package-private and not intended for direct extension. Extensions should consume produced build items instead.

Build Step: generateMcpConfigFileContents

Parses Claude Desktop configuration file from classpath.

@BuildStep
void generateMcpConfigFileContents(
    McpBuildTimeConfiguration mcpBuildTimeConfiguration,
    BuildProducer<McpConfigFileContentsBuildItem> producer
);

Behavior:

  • Reads config file path from mcpBuildTimeConfiguration.configFile()
  • Loads file from classpath using context class loader
  • Parses JSON and extracts mcpServers section
  • Creates LocalLaunchParams for each server entry
  • Produces McpConfigFileContentsBuildItem if file exists

Error Handling: Throws RuntimeException if file cannot be read or parsed

Build Step: recordConfigFile

Records configuration file contents for runtime access.

@BuildStep
@Record(ExecutionTime.STATIC_INIT)
void recordConfigFile(
    Optional<McpConfigFileContentsBuildItem> maybeMcpConfigFileContents,
    McpRecorder recorder
);

Behavior:

  • Calls recorder to store config contents at static init time
  • Only executes if McpConfigFileContentsBuildItem is present

Build Step: registerMcpClients

Generates synthetic CDI beans for MCP clients and optional ToolProvider.

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void registerMcpClients(
    McpBuildTimeConfiguration mcpBuildTimeConfiguration,
    Optional<McpConfigFileContentsBuildItem> maybeMcpConfigFileContents,
    BuildProducer<SyntheticBeanBuildItem> beanProducer,
    BuildProducer<HealthBuildItem> healthBuildItems,
    ShutdownContextBuildItem shutdown,
    Capabilities capabilities,
    Optional<MetricsCapabilityBuildItem> metricsCapability,
    CoreVertxBuildItem vertxBuildItem,
    BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitializedClasses,
    McpRecorder recorder
);

Behavior:

  1. Aggregates MCP client configurations from both config sources
  2. For each client:
    • Creates @McpClientName qualifier annotation
    • Produces SyntheticBeanBuildItem for McpClient bean
    • Configures scope as @ApplicationScoped
    • Sets bean as unremovable and default
  3. If generateToolProvider is enabled:
    • Produces SyntheticBeanBuildItem for ToolProvider bean
    • Injects all MCP clients via qualifiers
    • Injects OpenTelemetry Tracer if available
  4. If health checks enabled and SmallRye Health present:
    • Produces HealthBuildItem for McpClientHealthCheck
  5. If Micrometer not present:
    • Produces RuntimeInitializedClassBuildItem for MetricsMcpListener

Build Step: registerRegistryClients

Generates synthetic CDI beans for MCP registry clients.

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void registerRegistryClients(
    McpBuildTimeConfiguration mcpBuildTimeConfiguration,
    BuildProducer<SyntheticBeanBuildItem> beanProducer,
    McpRecorder recorder
);

Behavior:

  • Iterates through configured registry clients
  • For each registry client:
    • Creates @McpRegistryClientName qualifier annotation
    • Produces SyntheticBeanBuildItem for McpRegistryClient bean
    • Configures scope as @ApplicationScoped
    • Sets bean as unremovable and default

Build Step: indexMcpClientDependency

Indexes the langchain4j-mcp dependency for reflection.

@BuildStep
void indexMcpClientDependency(BuildProducer<IndexDependencyBuildItem> index);

Behavior:

  • Produces IndexDependencyBuildItem for dev.langchain4j:langchain4j-mcp
  • Enables Jandex indexing of MCP protocol classes
  • Required for reflection registration build step

Build Step: reflectionRegistrations

Registers MCP protocol classes for reflection (native compilation support).

@BuildStep
void reflectionRegistrations(
    BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
    CombinedIndexBuildItem indexBuildItem
);

Behavior:

  • Scans index for classes matching pattern dev\.langchain4j\.mcp\.protocol\..+
  • For each matching class:
    • Produces ReflectiveClassBuildItem with fields and methods enabled
  • Ensures JSON serialization/deserialization works in native images

Build Step: addMcpAuthProvider

Marks authentication provider beans as unremovable.

@BuildStep
void addMcpAuthProvider(BuildProducer<UnremovableBeanBuildItem> unremovableProducer);

Behavior:

  • Produces UnremovableBeanBuildItem for McpClientAuthProvider interface
  • Prevents CDI from removing auth provider beans during optimization

McpDevUiProcessor

Build processor for DevUI integration. Located in io.quarkiverse.langchain4j.mcp.deployment.devui package.

Note: This class is package-private and not intended for direct extension.

Build Step: cardPage

Registers MCP clients card in Quarkus DevUI.

@BuildStep(onlyIf = IsDevelopment.class)
CardPageBuildItem cardPage();

Behavior:

  • Only executes in development mode
  • Creates card with title "MCP clients"
  • Links to web component qwc-mcp-clients.js
  • Uses robot icon from Font Awesome

Build Step: jsonRpcService

Registers JSON-RPC service for DevUI backend.

@BuildStep
void jsonRpcService(BuildProducer<JsonRPCProvidersBuildItem> producers);

Behavior:

  • Produces JsonRPCProvidersBuildItem for McpClientsJsonRpcService
  • Enables DevUI frontend to query MCP clients and execute tools

Recorder API

Recorders generate bytecode for runtime initialization.

McpRecorder

Bytecode recorder for MCP runtime initialization.

package io.quarkiverse.langchain4j.mcp.runtime;

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

/**
 * Recorder for generating runtime initialization code.
 * Used by build processors to create suppliers for synthetic beans.
 */
@Recorder
public class McpRecorder {
    /**
     * Records Claude Desktop configuration contents for static initialization.
     *
     * @param contents Parsed configuration map
     */
    public void claudeConfigContents(Map<String, LocalLaunchParams> contents);

    /**
     * Creates supplier for MCP client synthetic bean.
     *
     * @param clientName Client name for identification and logging
     * @param transportType Transport protocol type
     * @param shutdown Shutdown context for cleanup
     * @param vertx Vert.x instance for async operations
     * @param metricsEnabled Whether to enable Micrometer metrics
     * @return Supplier creating McpClient instance
     */
    public Supplier<McpClient> mcpClientSupplier(
        String clientName,
        McpTransportType transportType,
        ShutdownContextBuildItem shutdown,
        Vertx vertx,
        boolean metricsEnabled
    );

    /**
     * Creates function for ToolProvider synthetic bean.
     *
     * @param mcpClientNames Set of MCP client names to aggregate
     * @return Function creating QuarkusMcpToolProvider instance
     */
    public Function<SyntheticCreationalContext<ToolProvider>, ToolProvider>
        toolProviderFunction(Set<String> mcpClientNames);

    /**
     * Creates supplier for MCP registry client synthetic bean.
     *
     * @param key Registry client name
     * @return Supplier creating McpRegistryClient instance
     */
    public Supplier<McpRegistryClient> mcpRegistryClientSupplier(String key);
}

Usage: Called by build processors; not directly used by application code.

Extension Integration Patterns

Consuming MCP Configuration

Extensions can access MCP client configuration:

import io.quarkiverse.langchain4j.mcp.deployment.McpConfigFileContentsBuildItem;
import io.quarkus.deployment.annotations.BuildStep;

class CustomProcessor {
    @BuildStep
    void processConfig(
        Optional<McpConfigFileContentsBuildItem> configItem,
        BuildProducer<CustomBuildItem> producer
    ) {
        configItem.ifPresent(item -> {
            item.getContents().forEach((name, params) -> {
                // Custom processing based on MCP configuration
                producer.produce(new CustomBuildItem(name, params));
            });
        });
    }
}

Adding Custom Build Logic

Extensions can add logic after MCP clients are registered:

import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;

class EnhancedMcpProcessor {
    @BuildStep
    @Consume(SyntheticBeanBuildItem.class)
    void afterMcpClientsRegistered() {
        // Custom logic that runs after MCP clients are generated
        // Guaranteed to execute after synthetic beans are produced
    }
}

Producing Additional Beans

Extensions can generate beans that depend on MCP clients:

import dev.langchain4j.mcp.client.McpClient;
import io.quarkiverse.langchain4j.mcp.runtime.McpClientName;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import org.jboss.jandex.AnnotationInstance;

class CustomMcpBeanProcessor {
    @BuildStep
    @Record(ExecutionTime.RUNTIME_INIT)
    void produceCustomBean(
        BuildProducer<SyntheticBeanBuildItem> producer,
        CustomRecorder recorder
    ) {
        AnnotationInstance qualifier = AnnotationInstance.builder(
            DotName.createSimple(McpClientName.class.getName())
        ).add("value", "github").build();

        producer.produce(SyntheticBeanBuildItem
            .configure(CustomService.class)
            .setRuntimeInit()
            .scope(ApplicationScoped.class)
            .addInjectionPoint(ClassType.create(McpClient.class), qualifier)
            .supplier(recorder.createCustomService())
            .done()
        );
    }
}

Build-Time Constraints

Bean Generation Order

  1. Parse configuration and Claude Desktop config
  2. Generate MCP client beans with qualifiers
  3. Generate optional ToolProvider bean (depends on MCP clients)
  4. Generate health check registration
  5. Register reflection for native compilation

Native Compilation Considerations

When building native images:

  • All MCP protocol classes are registered for reflection
  • Micrometer metrics listener is runtime-initialized if Micrometer is absent
  • JSON serialization requires reflection for all protocol message types
  • WebSocket and HTTP client classes must be accessible at runtime

Configuration Constraints

  • Client names must be unique across all sources (properties and Claude config)
  • Transport type determines required runtime configuration (URL vs command)
  • Registry client names must be unique
  • Build-time configuration cannot be changed at runtime

Testing Build Steps

Use @QuarkusTest to test build-time behavior:

import io.quarkus.test.QuarkusUnitTest;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

class McpBuildTimeTest {
    @RegisterExtension
    static final QuarkusUnitTest config = new QuarkusUnitTest()
        .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class))
        .withConfigurationResource("test-application.properties");

    @Test
    void testMcpClientGeneration() {
        // Test that MCP clients are generated correctly
        // Inject and verify beans
    }
}

Related Build Items

Extensions may also interact with standard Quarkus build items:

  • SyntheticBeanBuildItem - Generated CDI beans
  • HealthBuildItem - Health check registration
  • ReflectiveClassBuildItem - Native compilation reflection
  • UnremovableBeanBuildItem - Bean removal protection
  • RuntimeInitializedClassBuildItem - Runtime initialization control
  • IndexDependencyBuildItem - Jandex indexing
  • JsonRPCProvidersBuildItem - DevUI backend services
  • CardPageBuildItem - DevUI frontend pages

Install with Tessl CLI

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

docs

annotations.md

build-time-api.md

configuration.md

index.md

runtime-integration.md

tile.json