Quarkus extension for integrating Model Context Protocol (MCP) client capabilities with LangChain4j
Pluggable authentication provider interface for supplying credentials (access tokens, API keys) to MCP servers, with support for client-specific and global providers.
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) { ... }
}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();
}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.
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.
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");
}
}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();
}
}
}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");
}
}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;
}
}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");
}
}The extension resolves authentication providers using the following priority:
@McpClientName matching the client name@McpClientName annotationExample:
// 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
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-xyzStatic headers are always included in requests. Use McpClientAuthProvider for dynamic authentication.
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;
}
}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();
}
}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;
}
}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}Different MCP servers may expect different authorization formats:
return "Bearer " + token;return "ApiKey " + apiKey;String credentials = username + ":" + password;
String encoded = Base64.getEncoder().encodeToString(credentials.getBytes());
return "Basic " + encoded;return "Token " + token;Or:
return "X-API-Token " + token;McpClientAuthProvider only applies to HTTP-based transports (HTTP, Streamable HTTP, WebSocket)null to skip authentication for a requestInput parameter provides request context for dynamic authentication decisionsInstall with Tessl CLI
npx tessl i tessl/maven-io-quarkiverse-langchain4j--quarkus-langchain4j-mcp@1.7.0