CtrlK
BlogDocsLog inGet started
Tessl Logo

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

Quarkus extension for integrating Model Context Protocol (MCP) client capabilities with LangChain4j

Overview
Eval results
Files

authentication.mddocs/

Authentication

Pluggable authentication provider interface for supplying credentials (access tokens, API keys) to MCP servers, with support for client-specific and global providers.

Capabilities

McpClientAuthProvider Interface

Main authentication provider interface for supplying credentials to MCP clients.

package io.quarkiverse.langchain4j.mcp.auth;

public interface McpClientAuthProvider {
    /**
     * Provide authorization data which will be set as an HTTP Authorization header value.
     *
     * @param input representation of an HTTP request to the MCP server
     * @return authorization data which must include an HTTP Authorization scheme value,
     *         for example: "Bearer the_access_token".
     *         Returning null will result in no Authorization header being set.
     */
    String getAuthorization(Input input);

    /**
     * Resolve McpClientAuthProvider for a given MCP client name.
     *
     * @param mcpClientName the MCP client name. If not null, providers with matching
     *                      @McpClientName annotation are preferred to global providers.
     * @return Resolved McpClientAuthProvider as an Optional value which will be empty
     *         if no McpClientAuthProvider is available.
     */
    static Optional<McpClientAuthProvider> resolve(String mcpClientName) { ... }
}

Input Interface

Represents HTTP request context for authentication.

package io.quarkiverse.langchain4j.mcp.auth;

public interface McpClientAuthProvider.Input {
    /**
     * HTTP request method, such as POST or GET.
     */
    String method();

    /**
     * HTTP request URI.
     */
    URI uri();

    /**
     * HTTP request headers.
     */
    Map<String, List<Object>> headers();
}

Authentication Patterns

Global Authentication Provider

Applies to all MCP clients that don't have a client-specific provider.

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

@ApplicationScoped
public class GlobalAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        // Return authorization header value for all clients
        return "Bearer " + getGlobalToken();
    }

    private String getGlobalToken() {
        // Retrieve token from configuration, vault, etc.
        return System.getenv("MCP_GLOBAL_TOKEN");
    }
}

This provider is used for any MCP client that doesn't have a specific provider.

Client-Specific Authentication Provider

Applies to a specific MCP client identified by @McpClientName.

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 " + getGitHubToken();
    }

    private String getGitHubToken() {
        // Retrieve GitHub-specific token
        return System.getenv("GITHUB_TOKEN");
    }
}

This provider is used only for the "github" MCP client.

Multiple Client-Specific Provider

Single provider for multiple MCP clients using multiple @McpClientName annotations.

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

@ApplicationScoped
@McpClientName("github")
@McpClientName("gitlab")
@McpClientName("bitbucket")
public class GitAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        // Determine which client is making the request
        String host = input.uri().getHost();

        if (host.contains("github")) {
            return "Bearer " + getGitHubToken();
        } else if (host.contains("gitlab")) {
            return "Bearer " + getGitLabToken();
        } else if (host.contains("bitbucket")) {
            return "Bearer " + getBitbucketToken();
        }

        return null;
    }

    private String getGitHubToken() {
        return System.getenv("GITHUB_TOKEN");
    }

    private String getGitLabToken() {
        return System.getenv("GITLAB_TOKEN");
    }

    private String getBitbucketToken() {
        return System.getenv("BITBUCKET_TOKEN");
    }
}

Dynamic Authentication

Authentication based on request context.

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

@ApplicationScoped
@McpClientName("api-server")
public class DynamicAuthProvider implements McpClientAuthProvider {
    @Inject
    TokenService tokenService;

    @Override
    public String getAuthorization(Input input) {
        // Generate token based on request context
        String method = input.method();
        URI uri = input.uri();

        if (method.equals("POST") && uri.getPath().contains("/sensitive")) {
            // Use elevated permissions for sensitive operations
            return "Bearer " + tokenService.getElevatedToken();
        } else {
            // Use regular permissions for normal operations
            return "Bearer " + tokenService.getRegularToken();
        }
    }
}

API Key Authentication

Using API keys instead of Bearer tokens.

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

@ApplicationScoped
@McpClientName("api-service")
public class ApiKeyAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        // Return API key in custom format
        return "ApiKey " + System.getenv("API_KEY");
    }
}

No Authentication

Return null to skip authorization header.

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

@ApplicationScoped
@McpClientName("public-server")
public class NoAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        // No authentication required
        return null;
    }
}

Conditional Authentication

Authentication only for certain operations.

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

@ApplicationScoped
@McpClientName("mixed-server")
public class ConditionalAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        // Only authenticate for specific paths
        if (input.uri().getPath().startsWith("/api/private")) {
            return "Bearer " + getToken();
        }
        // No authentication for public paths
        return null;
    }

    private String getToken() {
        return System.getenv("AUTH_TOKEN");
    }
}

Provider Resolution

The extension resolves authentication providers using the following priority:

  1. Client-specific provider: Provider with @McpClientName matching the client name
  2. Global provider: Provider without any @McpClientName annotation
  3. No provider: If no provider is found, requests proceed without authentication

Example:

// Global provider (lowest priority)
@ApplicationScoped
public class GlobalAuth implements McpClientAuthProvider { ... }

// Client-specific provider for "github" (highest priority for "github" client)
@ApplicationScoped
@McpClientName("github")
public class GitHubAuth implements McpClientAuthProvider { ... }

// Client-specific provider for "gitlab" (highest priority for "gitlab" client)
@ApplicationScoped
@McpClientName("gitlab")
public class GitLabAuth implements McpClientAuthProvider { ... }

When "github" client makes a request: GitHubAuth is used When "gitlab" client makes a request: GitLabAuth is used When "other" client makes a request: GlobalAuth is used When "anonymous" client makes a request and no global provider exists: No authentication

Static Headers Configuration

For simple static authentication, use configuration properties:

# Bearer token
quarkus.langchain4j.mcp.github.header.Authorization=Bearer ghp_xxxxxxxxxxxxx

# API key
quarkus.langchain4j.mcp.api.header.X-API-Key=my-api-key

# Multiple headers
quarkus.langchain4j.mcp.service.header.Authorization=Bearer token123
quarkus.langchain4j.mcp.service.header.X-Client-ID=client-abc
quarkus.langchain4j.mcp.service.header.X-Client-Secret=secret-xyz

Static headers are always included in requests. Use McpClientAuthProvider for dynamic authentication.

Integration with External Systems

Vault Integration

Retrieve credentials from HashiCorp Vault.

import io.quarkiverse.langchain4j.mcp.auth.McpClientAuthProvider;
import io.quarkiverse.langchain4j.mcp.runtime.McpClientName;
import io.quarkus.vault.VaultKVSecretEngine;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
@McpClientName("secure-api")
public class VaultAuthProvider implements McpClientAuthProvider {
    @Inject
    VaultKVSecretEngine vault;

    @Override
    public String getAuthorization(Input input) {
        Map<String, String> secrets = vault.readSecret("mcp/secure-api");
        String token = secrets.get("token");
        return "Bearer " + token;
    }
}

OIDC Token Propagation

Use authenticated user's OIDC token (requires separate OIDC extension).

import io.quarkiverse.langchain4j.mcp.auth.McpClientAuthProvider;
import io.quarkiverse.langchain4j.mcp.runtime.McpClientName;
import io.quarkus.oidc.AccessTokenCredential;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
@McpClientName("user-api")
public class OidcAuthProvider implements McpClientAuthProvider {
    @Inject
    AccessTokenCredential accessToken;

    @Override
    public String getAuthorization(Input input) {
        // Propagate user's access token to MCP server
        return "Bearer " + accessToken.getToken();
    }
}

Environment Variables

Retrieve credentials from environment variables.

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

@ApplicationScoped
@McpClientName("env-api")
public class EnvAuthProvider implements McpClientAuthProvider {
    @Override
    public String getAuthorization(Input input) {
        String token = System.getenv("MCP_API_TOKEN");
        if (token == null || token.isEmpty()) {
            throw new IllegalStateException("MCP_API_TOKEN not set");
        }
        return "Bearer " + token;
    }
}

Configuration Properties

Retrieve credentials from Quarkus configuration.

import io.quarkiverse.langchain4j.mcp.auth.McpClientAuthProvider;
import io.quarkiverse.langchain4j.mcp.runtime.McpClientName;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

@ApplicationScoped
@McpClientName("config-api")
public class ConfigAuthProvider implements McpClientAuthProvider {
    @Inject
    @ConfigProperty(name = "mcp.config-api.token")
    String token;

    @Override
    public String getAuthorization(Input input) {
        return "Bearer " + token;
    }
}

Configuration:

mcp.config-api.token=${MCP_TOKEN}

Authorization Header Formats

Different MCP servers may expect different authorization formats:

Bearer Token

return "Bearer " + token;

API Key

return "ApiKey " + apiKey;

Basic Authentication

String credentials = username + ":" + password;
String encoded = Base64.getEncoder().encodeToString(credentials.getBytes());
return "Basic " + encoded;

Custom Format

return "Token " + token;

Or:

return "X-API-Token " + token;

Important Notes

  • McpClientAuthProvider only applies to HTTP-based transports (HTTP, Streamable HTTP, WebSocket)
  • STDIO transport doesn't use authentication providers (uses process environment variables instead)
  • Client-specific providers take precedence over global providers
  • Return null to skip authentication for a request
  • Authorization header value must include the scheme (e.g., "Bearer", "ApiKey")
  • Static headers (from configuration) are always included, regardless of provider
  • The Input parameter provides request context for dynamic authentication decisions

Install with Tessl CLI

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

docs

ai-service-integration.md

authentication.md

configuration.md

dev-ui.md

index.md

observability.md

registry-client.md

transport.md

tile.json