Conectarse a un servidor Model Context Protocol (MCP) con Java y LangChain4j
Introducción
Integrar tus aplicaciones Java con un servidor Model Context Protocol (MCP) te abre un mundo de posibilidades para implementar agentes de inteligencia artificial (IA) que pueden realizar tareas complejas e interactuar con herramientas y servicios externos.
En este artículo, te mostraré cómo conectar tus aplicaciones Java a un servidor MCP usando LangChain4j, lo que te permitirá que tus asistentes de IA realicen acciones del mundo real, como gestionar contenedores de Kubernetes. Cubriré tanto los transportes STDIO como SSE, y proporcionaré algunas ideas sobre cómo usar LangChain4j para testear tus implementaciones de servidores MCP.
Configuración de clientes MCP con LangChain4j
LangChain4j proporciona una flexible API de cliente MCP para conectarse a servidores MCP a través de diferentes mecanismos de transporte. El cliente actúa como un puente entre tu aplicación Java y el servidor MCP, permitiéndote enviar solicitudes y recibir respuestas con facilidad.
Existe dos transportes principales disponibles: Entrada/Salida Estándar (STDIO) y Server-Sent Events (SSE). Veamos cómo configurar cada uno de ellos.
Configuración de un cliente de transporte STDIO
El método de transporte STDIO lanza un servidor MCP como un subproceso y se comunica con él a través de flujos de entrada/salida estándar. Este método es más adecuado para ejecuciones locales, lo que es conveniente para agentes que realizan tareas en la misma máquina donde se está ejecutando el servidor. Gestión de recursos locales, ejecución de comandos de shell o interacción con servicios locales son casos de uso más comunes para el transporte STDIO.
El siguiente fragmento de código muestra cómo configurar un cliente de transporte STDIO en LangChain4j:
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.stdio.StdioMcpTransport;
public final class LangChainMcpClient {
private static McpClient initStdioClient(String... command) {
return new DefaultMcpClient.Builder()
// Optional client name to identify with the server, defaults to "langchain4j"
.clientName("blog.marcnuri.com")
// Optional MCP Protocol version, defaults to 2024-11-05
.protocolVersion("2024-11-05")
// Optional timeout for each individual tool execution, defaults to 60 seconds
.toolExecutionTimeout(Duration.ofSeconds(10))
.transport(new StdioMcpTransport.Builder()
// The command to execute the MCP server
.command(Arrays.asList(command))
// Optional, should the MCP server communication events be logged to the logger
.logEvents(true)
.build())
.build();
}
}
Estos son los componentes clave de la configuración del cliente de transporte STDIO:
clientName
: Un parámetro opcional para identificar el cliente con el servidor. Si no se especifica, el nombre del cliente se establece por defecto comolangchain4j
.protocolVersion
: Un parámetro opcional para especificar la versión del protocolo MCP. Si no se especifica, la versión del protocolo por defecto es2024-11-05
.toolExecutionTimeout
: Un parámetro opcional para establecer el tiempo de espera para cada ejecución de herramienta. Si no se especifica, el tiempo de espera por defecto es 60 segundos.StdioMcpTransport.command
: El comando para ejecutar el servidor MCP. Debe ser una lista de Strings que representan el comando y sus argumentos.StdioMcpTransport.logEvents
: Parámetro opcional para habilitar la traza de eventos de mensajes de comunicación del servidor MCP.
Configuración de un cliente de transporte SSE
El método de transporte Server-Sent Events (SSE) establece una conexión persistente con el servidor MCP a través de HTTP utilizando SSE para la comunicación en modo streaming. Este método es adecuado para ejecuciones remotas, donde el servidor se ejecuta en una máquina diferente o en un entorno en la nube. Interactuar con servicios web, gestionar recursos en la nube (como Kubernetes) o ejecutar tareas de larga duración son los casos de uso más comunes para el transporte SSE.
El siguiente fragmento de código muestra cómo configurar un cliente de transporte SSE en LangChain4j:
import dev.langchain4j.mcp.client.DefaultMcpClient;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.mcp.client.transport.http.HttpMcpTransport;
public final class LangChainMcpClient {
private static McpClient initSseClient(String sseUrl) {
return new DefaultMcpClient.Builder()
.clientName("blog.marcnuri.com")
.protocolVersion("2024-11-05")
.toolExecutionTimeout(Duration.ofSeconds(10))
.transport(new HttpMcpTransport.Builder()
// The URL to connect to the MCP server
.sseUrl(sseUrl)
// Optional HTTP connect, read, and write timeouts, defaults to 60 seconds
.timeout(Duration.ofSeconds(10))
// Optional, should the MCP server requests be logged to the logger
.logRequests(true)
// Optional, should the MCP server responses be logged to the logger
.logResponses(true)
.build())
.build();
}
}
Estos son los componentes clave de la configuración del cliente de transporte SSE:
HttpMcpTransport.sseUrl
: La URL para conectarse al servidor MCP utilizando SSE.HttpMcpTransport.timeout
: Un parámetro opcional para establecer los tiempos de espera de conexión, lectura y escritura HTTP. Si no se especifica, el tiempo de espera por defecto es de 60 segundos.HttpMcpTransport.logRequests
: Un parámetro opcional para habilitar la traza de mensajes de solicitud del servidor MCP.HttpMcpTransport.logResponses
: Un parámetro opcional para habilitar la traza de mensajes de respuesta del servidor MCP.
Integración de MCP con agentes de IA basados en Java
Una vez que tengas configurado tu cliente MCP, puedes integrarlo con los servicios de IA de LangChain4j para crear potentes asistentes que pueden interactuar con el mundo real.
Aquí te muestro cómo crear un asistente muy sencillo para gestionar un clúster de Kubernetes usando el kubernetes-mcp-server:
import dev.langchain4j.mcp.McpToolProvider;
import dev.langchain4j.mcp.client.McpClient;
import dev.langchain4j.model.github.GitHubModelsChatModel;
import dev.langchain4j.service.AiServices;
public final class LangChainMcpClient {
private interface Assistant {
String chat(String userMessage);
}
private static Assistant assistantIntegrationExample(McpClient client) {
return AiServices.builder(Assistant.class)
.chatLanguageModel(GitHubModelsChatModel.builder()
.gitHubToken(System.getenv("GITHUB_TOKEN"))
.modelName("gpt-4o-mini")
.build())
.toolProvider(McpToolProvider.builder().mcpClients(client).build())
.build();
}
public static void main(String[] args) {
final var assistant = assistantIntegrationExample(initStdioClient("npx", "-y", "kubernetes-mcp-server@latest"));
System.out.println(assistant.chat("Run a Pod with the image marcnuri/chuck-norris and expose port 8080"));
System.out.println(assistant.chat("List the Pods running in my cluster as a markdown table"));
}
}
En este ejemplo, creamos un asistente muy sencillo que conecta la herramienta kubernetes-mcp-server
con el modelo de lenguaje gpt-4o-mini
proporcionado por GitHub.
Como puedes ver, la API de LangChain4j hace que sea extremadamente fácil e intuitivo integrar tus aplicaciones Java con servidores MCP y modelos de IA.
Estos son los componentes clave del ejemplo de integración del asistente:
Assistant
:
La interfaz más simple para un asistente que proporciona un métodochat
para interactuar con el modelo de IA. Los servicios de IA de LangChain4j utilizan esta interfaz para crear automáticamente la implementación del asistente.GitHubModelsChatModel
:
Un modelo de chat que utiliza la API de GitHub para interactuar con el modelo de lenguaje GPT-4o-mini. Podríamos usar cualquier otro modelo e implementación de ChatModel proporcionado por LangChain4j.gitHubToken
: El token de GitHub para autenticarse con la API de GitHub.modelName
: El nombre del modelo de lenguaje proporcionado por GitHub para usar.
McpToolProvider
:
Un proveedor de herramientas que utiliza el cliente MCP para ejecutar herramientas en el servido MCPr. El métodomcpClients
acepta uno o más clientes MCP para la ejecución de herramientas.
En el método main(String[])
, creamos una instancia del asistente utilizando el método assistantIntegrationExample
.
Luego usamos este asistente para chatear con el modelo de IA enviando mensajes e imprimiendo las respuestas.
Para el primer mensaje, pedimos al asistente que ejecute un Pod con la imagen marcnuri/chuck-norris
y exponga el puerto 8080
.
Este es el resultado de la respuesta del asistente:
The Pod with the image `marcnuri/chuck-norris` has been successfully created and is exposing port 8080.
Here are the details:
### Pod Information
- **Name**: kubernetes-mcp-server-run-lt6r8
- **Namespace**: mnurisan-dev
- **Status**: Pending
- **Container Image**: marcnuri/chuck-norris
- **Port**: 8080 (TCP)
### Service Information
- **Name**: kubernetes-mcp-server-run-lt6r8
- **Type**: ClusterIP
- **Cluster IP**: 172.30.90.85
- **Port**: 8080 (Target Port: 8080)
You can access the service via the provided service once the Pod is in a running state.
If you need further assistance or checks, let me know!
Para el segundo mensaje, pedimos al asistente que liste los Pods en ejecución en el clúster como una tabla de Markdown. Este es el resultado de la respuesta del asistente:
| Name | Namespace | Status | Container Image |
|----------------------------------------|-----------------|----------|-----------------------|
| kubernetes-mcp-server-run-lt6r8 | namespace-dev | Pending | marcnuri/chuck-norris |
Testeo de servidores MCP con LangChain4j
Más allá de los casos de uso cotidianos, tambi én aprovecho esta metodología para testear mis implementaciones de servidores MCP en Java. Al configurar un cliente MCP que se conecta al servidor, puedo simular interacciones del mundo real y verificar el comportamiento del servidor MCP.
En mis pruebas, utilizo estas técnicas con tres niveles de testing de integración:
1. Unit testing / Tests unitarios
Para testear unidades individuales de lógica, en aplicaciones Quarkus, puedes iniciar un cliente con el siguiente código:
@QuarkusTest
class MCPServerKubernetesTest {
@TestHTTPResource
URL url;
private McpClient mcpClient;
@BeforeEach
void setUpMcpClient() {
mcpClient = new DefaultMcpClient.Builder()
.clientName("test-mcp-client-kubernetes")
.toolExecutionTimeout(Duration.ofSeconds(10))
.transport(new HttpMcpTransport.Builder().sseUrl(url.toString() + "mcp/sse").build())
.build();
}
}
Dado que Quarkus proporciona la URL del servidor MCP SSE como un recurso de test, es extremadamente fácil configurar un cliente MCP para testear.
Al combinar el cliente MCP con un servidor mock de Kubernetes como el proporcionado por el Cliente Java Kubernetes de Fabric8, puedes realizar solicitudes individuales utilizando el cliente y verificar el comportamiento de unidades específicas de lógica.
mcpClient.executeTool(
ToolExecutionRequest.builder().name("name_of_the_tool_in_test").arguments("{}").build());
2. Integration testing / Tests de integración
La implementación de tests de integración más grandes para tu aplicación también es sencilla con LangChain4j y Quarkus. Considerando una aplicación construida, puedes iniciar el servidor MCP real como un subproceso y conectarte a él utilizando el método de transporte STDIO.
En el siguiente fragmento de código, te muestro cómo lograr esto en Quarkus:
public class MCPServerKubernetesIT {
private static McpClient mcpClient;
@BeforeAll
static void setUpMcpClient() throws Exception {
mcpClient = new DefaultMcpClient.Builder()
.clientName("test-mcp-client-kubernetes")
.toolExecutionTimeout(Duration.ofSeconds(10))
.transport(new StdioMcpTransport.Builder().command(
List.of(
// Retrieve the java binary location:
ProcessHandle.current().info().command().orElseThrow(),
"-jar",
// Location of the built application (provided by Quarkus):
System.getProperty("java.jar.path")
)).logEvents(true).build())
.build();
}
}
En este ejemplo, configuramos un cliente MCP utilizando el método de transporte STDIO para conectarnos a la aplicación Quarkus empaquetada. Esto instruye al cliente MCP a iniciar un subproceso real con la aplicación compilada y comunicarse con ella a través de flujos de entrada/salida estándar.
Al igual que en el ejemplo de tests unitarios, puedes entonces ejecutar herramientas específicas en el servidor y verificar el comportamiento de la aplicación en su conjunto.
3. End-to-end testing / Tests de extremo a extremo
Para pruebas end-to-end, puedes emplear la misma configuración que en las pruebas de integración pero conectándote a un modelo real en lugar de uno simulado. Siguiendo la misma configuración que describí en la sección Integración de MCP con agentes de IA basados en Java, puedes crear una prueba completa de extremo a extremo que simule interacciones del mundo real con tu aplicación.
Automatizando consultas de chat y comparando las respuestas con las salidas esperadas, puedes asegurarte de que tu implementación del servidor MCP es robusta y fiable. No obstante, ten en cuenta la naturaleza no determinista de los modelos de IA, que puede llevar a respuestas diferentes para una misma entrada. Debes considerar esto al escribir tus pruebas de extremo a extremo y permitir cierta flexibilidad en las salidas esperadas.
Conclusión
LangChain4j abre emocionantes posibilidades para los desarrolladores Java que trabajan con IA. Ya estés utilizando un servidor MCP de terceros o desarrollando el tuyo propio, LangChain4j proporciona una API flexible y potente para la integración. Conectando tus aplicaciones Java a servidores MCP y modelos de lenguaje grandes (LLMs), puedes crear asistentes de IA potentes que pueden realizar tareas complejas e interactuar con herramientas y servicios externos.
En este artículo, te he mostrado lo fácil que es configurar un cliente MCP con LangChain4j e integrarlo con tus aplicaciones Java. También te he proporcionado algunas ideas sobre cómo usar LangChain4j para testear tus implementaciones de servidores MCP, desde pruebas unitarias hasta pruebas de extremo a extremo. Al aprovechar estas técnicas, puedes agilizar tu proceso de desarrollo, mejorar la fiabilidad y crear asistentes de IA que pueden realizar acciones del mundo real.
Puedes encontrar el código fuente de este artículo en GitHub.