En este último capítulo de la serie de Spring AI veremos los términos importantes a comprender cuando se trabaja con MCP, además de exponer a alto nivel cuál es la arquitectura del MCP. Finalmente, terminaremos explicando la forma de crear aplicaciones que hagan uso de MCP tanto de la parte cliente como de la parte servidora con Spring AI.

A modo de recordatorio, por si te has perdido alguno de los artículos anteriores, puedes echarle un vistazo al resto de contenidos de la serie de Spring AI en los siguientes enlaces:

  1. "Deep Learning" sobre Spring AI: primeros pasos
  2. “Deep Learning” sobre Spring AI: multimodularidad, prompts y observabilidad
  3. "Deep Learning" sobre Spring AI: Advisors, Structured Output y Tool Calling
  4. “Deep Learning” sobre Spring AI: RAG, embeddings y vector databases
  5. “Deep Learning” sobre Spring AI: ETL y MCP

Bases del MCP

El MCP se basa en una arquitectura cliente-servidor donde una aplicación se puede conectar a varios servidores. En esta arquitectura se pueden identificar los siguientes componentes:

your computer: host with mcp client. 1. mcp protocol - mcp server A - local data soruce A. 2. mcp protocol - mcp server b - local data soruce b. 3. mcp protocol - mcp protocol server C - web APIs - remote service C (internet)
  • Programas (hosts): programas, IDEs o herramientas de IA que quieren acceder a datos a través del MCP.
  • Clientes: clientes de protocolo MCP que tiene conexiones 1:1 a los servidores.
  • Servidores: programas ligeros que exponen funcionalidades a través del MCP.
  • Orígenes de datos locales: sistema de ficheros del ordenador, bases de datos, servicios, etc.
  • Servicios remotos: servicios disponibles en internet por ejemplo a través de APIs.

Algunos de los conceptos importantes con los que se trabajan en este protocolo son:

  • Recursos (Resources): concepto core que permite a los servidores exponer datos. Algunos de estos datos pueden ser:
    • Ficheros
    • Registros de bases de datos
    • Respuestas de APIs
    • Imágenes
    • Logs
  • Prompts: a través de plantillas de prompts, los servidores ofrecen una forma de interactuar con los LLMs.
  • Funciones (Tools): permiten a los servidores ofrecer funcionalidades a los clientes para interactuar con otros sistemas.
  • Muestras (Sampling): mecanismo que permite a los servidores pedir la ejecución del LLM a través del cliente. Un ejemplo sería en el escenario de un chatbot cuando la conversación incluye términos como “integración de API”. El MCP podría bajar la “temperatura” para reducir la aleatoriedad de las respuestas y que sean más precisas.
  • Roots: concepto que define los límites en los que se pueden ejecutar los servidores (se refiere a los límites en cuanto a ficheros, carpetas, endpoints, etc). Por ejemplo, en un servidor que proporciona funciones sobre el sistema de ficheros se definen las carpetas a las que tiene acceso.
  • Transportes (Transports): son la base de comunicación entre clientes y servidores.

Java y Spring

Existe una SDK para Java (también para otros lenguajes como Python, Kotlin, Typescript, C#) que implementa el MCP y que es extendida por el módulo Spring AI MCP, integrando Spring Boot para ofrecer los respectivos starters de cliente y servidor.

La implementación de Java se basa en la siguiente arquitectura de capas:

En la base: stdio, sse… primera columna englobada en la base, de abajo a arriba: MCP transport (client), MCP session, mcp client. en la segunda columna paralela, de abajo a arriba: mcp transport (server), MCP session, mcp client

Cada capa gestiona distintas características:

  1. Capa de cliente/servidor: administra las distintas operaciones de cada parte:
  • Algunas operaciones de cliente:
    • Negociación con los servidores para asegurar la compatibilidad.
    • Diagnosticar qué funcionalidades están disponibles.
    • Descubrimiento y ejecución de funciones.
    • Acceso y gestión de recursos.
    • Interacciones con prompts.
  • Algunas operaciones de servidor:
    • Negociación con los clientes además de gestión concurrente de sus conexiones.
    • Exponer funciones.
    • Provisión y manejo de prompts.
    • Gestión de recursos.
    • Logs y notificaciones.
  1. Capa de sesión: gestiona los patrones de comunicación y estado.
  2. Capa de transporte: maneja la serialización-deserialización de mensajes JSON-RPC para varias implementaciones de transporte (stdio, SSE o custom).
Estructura de MCP Server (SSE) y MCP Server (STD IO)

MCP Client

El starter para el cliente de MCP que proporciona Spring AI ofrece la autoconfiguración para crear un cliente MCP en aplicaciones Spring Boot. Además, da soporte a las implementaciones síncrona y asíncrona con varias opciones de transporte. El starter ofrece:

  • Gestión de varias instancias cliente.
  • Inicialización automática.
  • Soporte para varios transportes.
  • Integración con la ejecución de tools de Spring AI.
  • Gestión del ciclo de vida con manejo automático de recursos cuando se cierra el contexto.
  • Personalización en la creación de clientes.

Starters

El starter estándar se conecta simultáneamente a uno o más servidores MCP sobre transportes stdio y/o SSE (este tipo de conexión usa la implementación de transporte de HttpClient). Cada conexión a un servidor MCP crea una nueva instancia de cliente MCP y se puede seleccionar clientes síncronos o asíncronos, pero no de los dos tipos a la vez.

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

El starter de WebFlux ofrece una funcionalidad similar al starter normal pero usando una implementación basada en WebFlux para el SSE.

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>

Propiedades

Las propiedades se dividen en comunes, stdio y SSE. Algunas propiedades relevantes son:

  • Comunes: se deben poner con el prefijo spring.ai.mcp.client:
Property Descripción Default
request-timeout Timeout para las peticiones del cliente 20s
type Tipo del servidor (SYNC/ASYNC). Todos los clientes solo pueden ser de un único tipo: sync o async. SYNC
root-change-notification Habilitar las notificaciones de cambios de roots para todos los clientes true
toolcallback.enabled Habilitar la integración del tool callback de MCP con la ejecución de tools de Spring AI true
  • stdio: se deben poner con el prefijo spring.ai.mcp.client.stdio:
Property Descripción Default
servers-configuration Recurso que contiene la configuración de los servidores MCP en formato JSON -
connections Mapa con las distintas configuraciones por nombre de las conexiones stdio -
connections.[name].command Comando para ejecutar el servidor MCP -
connections.[name].args Lista de los argumentos del comando -
connections.[name].env Mapa de variables de entorno para el proceso del servidor -

En caso de configurar la propiedad servers-configuration se indicará la ruta en donde se encuentra el fichero con la configuración en formato Claude Desktop (este formato actualmente solo permite configurar conexiones de tipo stdio). Un ejemplo del formato de este fichero es:

{
  "mcpServers": {
    "filesystem": {
      "command": "docker",
      "args": [
        "run",
        "-i",
        "--rm",
        "--mount", "type=bind,src=/home/user/test,dst=/projects/test",
        "mcp/filesystem",
        "/projects/test"
      ]
    }
  }
}
  • SSE: se deben poner con el prefijo spring.ai.mcp.client.sse:
Property Descripción Default
connections Mapa con las distintas configuraciones por nombre de las conexiones sse -
connections.[name].url Url base para la comunicación SSE con el servidor MCP -
connections.[name].sse-endpoint Endpoint sse a usar para la conexión /sse

Funcionalidades

Como hemos comentado, el starter soporta dos tipos de clientes:

  • Síncronos (tipo cliente por defecto): adecuado para aplicaciones tradicionales con operaciones bloqueantes.
  • Asíncronos: adecuado para aplicaciones reactivas con operaciones no bloqueantes y activado por la propiedad spring.ai.mcp.client.type=ASYNC.

Además, la autoconfiguración del starter permite la personalización de varios aspectos del cliente como timeouts, manejo de eventos o procesamiento de mensajes. Estas personalizaciones se podrán configurar mediante la implementación de las clases McpSyncClientCustomizer y McpAsyncClientCustomizer. Algunas personalizaciones disponibles son:

  • Peticiones: configuración de timeouts.
  • Manejadores de muestras (sampling): configuración de la forma en la que los servidores piden muestras (sampling) al LLM. Esto permite a los clientes tener control sobre el acceso a los modelos.
  • Acceso al sistema de ficheros (roots): configuración de la forma en la que los clientes exponen los “roots” del sistema de ficheros a los servidores. Los “roots” definen los límites que tendrán los servidores para operar con el sistema de ficheros, es decir, a qué ficheros y carpetas tienen acceso. Los servidores podrán pedir el listado de “roots” y recibir notificaciones cuando este listado cambie.
@Component
public class CustomMcpSyncClient implements McpSyncClientCustomizer {
    
    @Override
    public void customize(String serverConfigurationName, McpClient.SyncSpec spec) {      
      RootCapabilities rootCapabilities = new RootCapabilities(true);
      spec.capabilities(new ClientCapabilities(null, rootCapabilities, null));
      // Sets the root URIs that this client can access.
      spec.roots(Arrays.asList(new Root(System.getProperty("user.home") + "/out", "Out foler")));
    }
}
  • Manejadores de eventos: manejadores que son notificados cuando el servidor produce un evento. Estos eventos provienen de un cambio en la lista de funciones (tools), recursos (resources) o prompts.
  • Manejadores de logs: configuración de la forma en la que los servidores enviarán mensajes de log a los clientes, pudiendo estos últimos controlar la verbosidad configurando los niveles de log.

Se permiten varios tipos de transporte:

  • I/O estándar (Stdio): activado por spring-ai-starter-mcp-client.
  • SSE HTTP: activado por spring-ai-starter-mcp-client.
  • SSE WebFlux: activado por spring-ai-starter-mcp-client-webflux.

También es posible la integración con el ciclo de ejecución de “tools” (Tool-Calling) de Spring AI que tiene que ser habilitado mediante la propiedad spring.ai.mcp.client.toolcallback.enabled=true.

MCP Server

El starter para el servidor de MCP que proporciona Spring AI ofrece la autoconfiguración para crear un servidor MCP en aplicaciones Spring Boot. El starter ofrece:

  • Configuración automática para los componentes del servidor MCP.
  • Soporte de los modos síncrono y asíncrono.
  • Varias opciones de capa de transporte.
  • Flexibilidad en la especificación de funciones (tools), recursos (resources) y prompts.
  • Notificaciones de cambios.

Starters

Se tendrá que seleccionar un starter en función de las necesidades a nivel de capa de transporte:

  • Estándar: soporta el transporte STDIO adecuado para herramientas de escritorio o línea de comando.
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-mcp-server-spring-boot-starter</artifactId>
</dependency>

El starter activa la clase McpServerAutoConfiguration ofreciendo:

  1. Configuración de los componentes básicos del servidor
  2. Manejo de las especificaciones de funciones (tools), recursos (resources) y prompts
  3. Gestión de las capacidades del servidor y notificaciones de cambio
  4. Implementaciones síncronas y asíncronas
  • WebMVC: soporta el transporte SSE basado en Spring MVC y opcionalmente el transporte STDIO.
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>

El starter activa las clases McpWebMvcServerAutoConfiguration y McpServerAutoConfiguration ofreciendo:

  1. Transporte HTTP usando Spring MVC
  2. Configuración automática de endpoints SSE
  3. Transporte STDIO habilitado mediante la propiedad spring.ai.mcp.server.stdio=true.
  • WebFlux: soporta el transporte SSE basado en Spring WebFlux y, opcionalmente, el transporte STDIO.
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>

El starter activa las clases McpWebFluxServerAutoConfiguration y McpServerAutoConfiguration ofreciendo:

  1. Transporte reactivo usando Spring WebFlux.
  2. Configuración automática de endpoints SSE.
  3. Transporte STDIO habilitado mediante la propiedad spring.ai.mcp.server.stdio=true

Properties

Algunas de las propiedades importantes para la configuración, y que se tienen que indicar con el prefijo spring.ai.mcp.server, son:

Property Descripción Default
stdio Habilitar/deshabilitar el transporte stdio false
type Tipo del servidor (SYNC/ASYNC) SYNC
resource-change-notification Habilitar las notificaciones de cambios de resources true
prompt-change-notification Habilitar las notificaciones de cambios de prompts true
tool-change-notification Habilitar las notificaciones de cambios de tools true

Tipos

Los servidores pueden ser de tipo síncrono o asíncrono:

  • Síncrono: el tipo por defecto implementado usando McpSyncServer y diseñado para patrones petición-respuesta. Se habilita con la propiedad spring.ai.mcp.server.type=SYNC.
  • Asíncrono: implementado con McpAsyncServer y optimizado para operaciones no bloqueantes. Se habilita con la propiedad spring.ai.mcp.server.type=ASYNC.

Funcionalidades

Este starter, entre otras cosas, permite a los servidores exponer funciones, recursos y prompts a los clientes. Convierte automáticamente los manejadores (handlers) registrados como beans de las funcionalidades mencionadas anteriormente en especificaciones sync/async según el tipo de servidor:

  • Funciones (Tools): para que los servidores expongan funciones que puedan ser invocadas por los modelos. En este aspecto el starter ofrece:
    • Notificaciones de cambio.
    • Las funciones son transformadas automáticamente a la especificación síncrona/asíncrona según el tipo de servidor usado.
    • Especificación a través de beans de Spring.
@Bean
ToolCallbackProvider userToolProvider(UserTools userTools) {
   return 
MethodToolCallbackProvider.builder().toolObjects(userTools).build();
}
  • Recursos (Resources): proporciona una forma para que los servidores expongan recursos a los clientes:
    • Especificaciones estáticas y dinámicas de los recursos.
    • Notificaciones de cambio.
    • Posibilidad de plantillas de recursos.
    • Transformación automática entre las especificaciones síncronas y asíncronas.
    • Especificación a través de beans de Spring.
@Bean
List<McpServerFeatures.SyncResourceSpecification> 
myResources(@Value("classpath:/static/trends.txt") Resource resource) {
    var systemInfoResource = new McpSchema.Resource("file://trends.txt", "Trends", "Fichero con 3 tendencias tecnologicas del año 2025", "text/plain", new Annotations(Arrays.asList(Role.USER), Double.valueOf("0")));
    var resourceSpecification = new 
McpServerFeatures.SyncResourceSpecification(systemInfoResource, (exchange, request) -> {
      try {
         String jsonContent = 
      resource.getContentAsString(StandardCharsets.UTF_8);
         return new McpSchema.ReadResourceResult(List.of(new 
      McpSchema.TextResourceContents(request.uri(), "text/plain", jsonContent)));
      } catch (Exception e) {
         throw new RuntimeException("Failed to generate system info", e);
      }
   });
   return List.of(resourceSpecification);
}
  • Prompts: la forma en la que los servidores presentan las plantillas de prompt:
    • Notificaciones de cambio.
    • Versionado de plantillas.
    • Transformación automática entre las especificaciones síncronas y asíncronas.
    • Especificación a través de beans de Spring.
@Bean
List<McpServerFeatures.SyncPromptSpecification> myPrompts() {
   var prompt = new McpSchema.Prompt("search-user-by-name", "Prompt to search a user by name", List.of(new McpSchema.PromptArgument("name", "The user's name", true)));

   var promptSpecification = new 
McpServerFeatures.SyncPromptSpecification(prompt, 
      (exchange, getPromptRequest) -> {
         String nameArgument = (String) 
      getPromptRequest.arguments().get("name");
         if (nameArgument == null) {nameArgument = "friend";}
         var userMessage = new PromptMessage(Role.USER, new 
      TextContent("Existe el usuario " + nameArgument + "?"));
         return new GetPromptResult("Prompt to search a user by name", List.of(userMessage));
      });        
      return List.of(promptSpecification);
}
  • Roots: cuando existe un cambio en un root, los clientes que lo soportan (listChanged) envían una notificación. En este caso, el servidor ofrece:
    • Monitorización de cambios de roots.
    • Transformación automática a consumidores asíncronos para aplicaciones reactivas.
    • Registro opcional a través de beans de Spring.
@Bean
BiConsumer<McpSyncServerExchange, List<McpSchema.Root>> rootsChangeHandler(){
    return (exchange, roots) -> {
        log.info("Cambio en los roots recibido: {}", roots);
    };
}

En referencia a los tipos de transporte, los expuestos por estos starters son:

  • I/O estándar (Stdio): activado por spring-ai-starter-mcp-server.
  • Spring MVC (SSE): activado por spring-ai-starter-mcp-server-webmvc.
  • Spring WebFlux (SSE reactivo): activado por spring-ai-starter-mcp-server-webflux.

Demo

Para comprobar el funcionamiento de ambos starters, creamos dos proyectos/aplicaciones, uno para el cliente y otro para el servidor:

  1. Cliente: mediante la la configuración de properties, indicamos dos servidores:
  • Stdio: servidor con funciones sobre el sistema de ficheros (en este caso la carpeta /projects/test dentro del Docker) ejecutado mediante un Docker. Será necesario definir correctamente la conexión de carpetas entre el host y el contenedor Docker, además de la carpeta sobre la que tiene permisos el servidor MCP.
  • SSE: servidor creado mediante Spring AI con una función sencilla de listar usuarios.
spring:
  ...
    mcp:
      client:
        toolcallback:
          enabled: true
        type: SYNC
        sse:
          connections:
            usuarios:
              url: http://localhost:8081
        stdio:
          connections:
            files:
              command: docker
              args:
                - 'run'
                - '-i'
                - '--rm'
                - '--mount'
                - 'type=bind,src=/home/user/test,dst=/projects/test'
                - 'mcp/filesystem'
                - '/projects/test'
  1. Servidor: ofrece una función (tool) para obtener una lista de usuarios, además de exponer recursos (resources) y prompts.

Para la ejecución de los endpoints será necesario levantar en primer lugar la aplicación del servidor MCP y después la del cliente MCP (el servidor de file-system se levanta con Docker automáticamente al levantar el cliente MCP).

En la app que actúa como cliente MCP se exponen los siguientes endpoints:

  • list-files: endpoint que lista los ficheros de un directorio dentro del Docker y que ejecuta el servidor MCP. El cliente proporciona el contexto con la información expuesta del servidor al LLM para que pueda responder correctamente.
Estás en el directorio '/projects/test'. Tienes los archivos 'README.md', 'code.txt' y 'hola' en esta carpeta.
Mismo listado de archivos por comando
  • list-users: endpoint que lista los usuarios que expone el servidor MCP implementado con Spring AI. El cliente proporciona el contexto con la información obtenida del servidor al LLM para que pueda responder correctamente.
Los usuarios disponibles son: Jose, Lucia, David, Marga. ¿Necesitas algo más?
  • tools: devuelve un listado de funciones ofrecidas por el servidor MCP implementado con Spring AI.
"name": "listUsers", "description": "devuelve una lista de usuarios", "inputSchema": { "type": "object", "properties": 13, "required": [], "additionalProperties": false }
  • resources: lista los resources expuestos a través del servidor MCP implementado con Spring AI.
"resources":  "uri": "file://trends.txt", "name": "Trends", "description": "Fichero con 3 tendencias tecnologicas del año 2025", "mimeType": "text/plain", "annotations": { "audience":  "user" . "priority": 0.0 } } }
  • resource: obtiene la información del recurso (trends.txt) expuesto por el servidor MCP implementado con Spring AI.
"contents":  "uri": "file://trends.txt", "mimeType": "text/plain", "text": "1- La voz y los videos en la IA\n2 - Accesibilidad: desarrollando aplicaciones inclusivas\n3 - De Digital Native a AI Native: la nueva era de la IA en los negocios"
  • prompts: listado con los ejemplos de prompts disponibles en el servidor MCP implementado con Spring AI.
"prompts":  "name": "search-user-by-name", "description": "Prompt to search a user by name", "arguments":  "name": "name", "description": "The user's name", "required": true 10
  • prompt: obtiene un ejemplo de prompt predefinido a usar con el servidor MCP implementado con Spring AI.
"role": "user", "content": { "type": "text", "text": "Existe el usuario Juan?" }
  • add-root: endpoint que permite añadir un root a los existentes del cliente (si el root a añadir existe, se producirá un error), informando al servidor del cambio en el listado de roots disponibles. Debido a que se ha definido un consumidor de cambios en los roots en el servidor MCP, se pueden ver en el log los roots disponibles.
Cambio en los roots recibido: Root uri=/home/simonrodriguez/springai, name=Spring AI foler, Root uri=/home/simonrodriguez/out, name=Out foler

En estos enlaces se encuentran los códigos de ejemplo del cliente y servidor MCP.

Conclusión

Hasta aquí el último capítulo de la serie de Spring AI. En este caso hemos explorado los conceptos importantes del MCP así como verificar qué funcionalidades ofrecen los starters de MCP con un ejemplo de implementación.

A lo largo de esta serie centrada en Spring AI hemos podido comprobar todas las funcionalidades para crear no solo aplicaciones de chat al uso, sino también otro tipo de aplicaciones que puedan contener diversos flujos de trabajo y tareas ayudándonos de un LLM para poder mantener una interfaz unificada de interacción a través de lenguaje natural.

A pesar de estar todavía en las primeras versiones, podemos comprobar la potencia del framework, siempre teniendo en cuenta las capacidades de los LLMs que usemos para crear nuevas aplicaciones centradas en la IA. Tampoco debemos olvidar que seguramente el equipo de Spring irá aumentando nuevas funcionalidades en este módulo a medida que los LLMs sigan evolucionando.

Es por todo esto que es importante prestar atención, más que nunca, a las últimas novedades para no quedarnos atrás en esta nueva etapa tecnológica, ya que todo lo relacionado con la IA avanza a un ritmo todavía más vertiginoso que antes. ¡Os leo en comentarios!

Referencias

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.