Laboratory Create your first MCP Server with AWS architecture
Este es un laboratorio avanzado de nivel arquitectónico. Configurar el Model Context Protocol (MCP) con Amazon Bedrock AgentCore Gateway (el componente que conecta agentes con servidores MCP externos a través de endpoints seguros) requiere una alineación perfecta entre Java, Docker, AWS (EKS/ECR) y Kubernetes.
Para que no te falte absolutamente nada y mantengas la agilidad que necesitas en tus laboratorios, he estructurado todo en una guía integral paso a paso de principio a fin, incluyendo la herramienta local necesaria (kubectl y aws-cli) para desplegar desde tu máquina.
Elegiremos 2 herramientas (Tools) lógicas para el agente: get_exchange_rate (tasas de cambio para Palma/Polonia) y get_city_safety_index (datos de seguridad/consejos).
mcp-server/
├── src/main/java/com/lab/mcp/
│ ├── domain/ # Reglas de negocio puras (Modelos y contratos)
│ │ ├── model/ToolRequest.java
│ │ └── repository/CityToolRepository.java
│ ├── infrastructure/ # Spring AI, WebFlux, Controladores (Detalles técnicos)
│ │ ├── controller/McpHttpController.java
│ │ └── repository/MockCityToolRepository.java
│ └── McpServerApplication.java
├── pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!-- Agregamos Spring AI para estructurar el formato de tools si se requiere,
pero para cumplir el estándar estricto de MCP sobre HTTP de Bedrock: -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
// domain/model/ToolResponse.java
package com.lab.mcp.domain.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ToolResponse {
private String result;
}Bedrock AgentCore Gateway invoca los servidores MCP remotos utilizando el protocolo estandarizado MCP sobre transporte HTTP (POST /tools/call).
// infrastructure/controller/McpHttpController.java
package com.lab.mcp.infrastructure.controller;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import java.util.Map;
@RestController
@RequestMapping("/mcp")
public class McpHttpController {
// endpoint 1: Listar las herramientas disponibles (Requerido por MCP)
@GetMapping("/tools")
public Mono<Map<String, Object>> listTools() {
return Mono.just(Map.of(
"tools", java.util.List.of(
Map.of(
"name", "get_exchange_rate",
"description", "Get the current exchange rate for a target currency compared to EUR.",
"inputSchema", Map.of(
"type", "object",
"properties", Map.of("currency", Map.of("type", "string", "description", "e.g., PLN, USD"))
)
),
Map.of(
"name", "get_city_safety_index",
"description", "Get safety index and tips for a specific city.",
"inputSchema", Map.of(
"type", "object",
"properties", Map.of("city", Map.of("type", "string"))
)
)
)
));
}
// endpoint 2: Ejecutar la herramienta (Call Tool)
@PostMapping("/tools/call")
public Mono<Map<String, Object>> callTool(@RequestBody Map<String, Object> request) {
String toolName = (String) request.get("name");
Map<String, Object> arguments = (Map<String, Object>) request.get("arguments");
if ("get_exchange_rate".equals(toolName)) {
String curr = (String) arguments.getOrDefault("currency", "PLN");
return Mono.just(Map.of("content", java.util.List.of(Map.of("type", "text", "text", "Current rate: 1 EUR = 4.32 " + curr))));
} else if ("get_city_safety_index".equals(toolName)) {
String city = (String) arguments.getOrDefault("city", "Palma");
return Mono.just(Map.of("content", java.util.List.of(Map.of("type", "text", "text", city + " is very safe (Index: 85/100). Normal precautions apply."))));
}
return Mono.just(Map.of("isError", true, "content", java.util.List.of(Map.of("type", "text", "text", "Tool not found"))));
}
}Este template creará el repositorio ECR, la red VPC, el clúster EKS y un grupo de nodos administrados. Guardar como infra-eks.yaml.
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Infraestructura base para MCP: ECR, VPC y clúster EKS básico.'
Resources:
McpRepository:
Type: AWS::ECR::Repository
Properties:
RepositoryName: mcp-server-repo
McpVPC:
Type: AWS::ECR::Repository # (Nota simplificada para el lab: puedes usar la vpc default o un wizard de EKS)
# Para agilidad en laboratorios, crearemos el EKS usando eksctl directamente o referenciando recursos existentes.
💡 Nota de Agilidad para Labs: Crear un EKS completo por CloudFormation puro toma más de 500 líneas de código JSON/YAML de redes. La herramienta oficial y estándar de la industria para laboratorios rápidos es
eksctl. Ejecuta esto en tu terminal en lugar del CFN pesado para crear el clúster en 10 minutos:eksctl create cluster --name lab-mcp-cluster --region us-east-2 --nodegroup-name standard-nodes --node-type t3.medium --nodes 2
Optimizado con Multi-stage building para reducir el tamaño de la imagen final usando la distro oficial de Java:
# Stage 1: Build
FROM maven:3.9.6-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests
# Stage 2: Run
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
Debes tener instalados: aws-cli, docker y kubectl.
- Autentica tu Docker local con tu ECR de AWS (reemplaza
123456789012y la región):
aws ecr get-login-password --region us-east-2 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-2.amazonaws.com
- Construye tu imagen Docker:
docker build -t mcp-server-repo .
- Taggea la imagen apuntando a tu repositorio de AWS:
docker tag mcp-server-repo:latest 123456789012.dkr.ecr.us-east-2.amazonaws.com/mcp-server-repo:latest
- Sube la imagen a la nube (Push):
docker push 123456789012.dkr.ecr.us-east-2.amazonaws.com/mcp-server-repo:latest
Para que tu comando kubectl local sepa a dónde enviar los recursos:
aws eks update-kubeconfig --region us-east-2 --name lab-mcp-cluster
Para exponer de forma ágil el servicio, utilizaremos el AWS Load Balancer Controller o, de manera aún más nativa para laboratorios sin instalar controladores externos, un Service de tipo LoadBalancer (creará un Classic o Network Load Balancer automáticamente en tu cuenta de AWS).
Guarda este archivo único como k8s-mcp-deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server-deployment
labels:
app: mcp-server
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: mcp-server
template:
metadata:
labels:
app: mcp-server
spec:
containers:
- name: mcp-server
image: 123456789012.dkr.ecr.us-east-2.amazonaws.com/mcp-server-repo:latest
ports:
- containerPort: 8080
imagePullPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: mcp-server-service
namespace: default
spec:
type: LoadBalancer # Crea el componente de red público de AWS de inmediato
ports:
- port: 80
targetPort: 8080
protocol: TCP
selector:
app: mcp-server
kubectl apply -f k8s-mcp-deployment.yaml
Para obtener la URL pública asignada por AWS que utilizaremos en el Gateway:
kubectl get svc mcp-server-service -w
Espera a que la columna EXTERNAL-IP cambie de <pending> a un DNS largo de AWS (ej: a123bc...us-east-2.elb.amazonaws.com). Tu URL MCP para el Gateway será: http://<TU-EXTERNAL-IP>/mcp.
El AgentCore Gateway permite registrar este endpoint HTTP externo como un Endpoint MCP compatible dentro de tu entorno de agentes de Bedrock.
- Ingresa a Amazon Bedrock usando el Rol que creamos previamente.
- En el panel de navegación izquierdo, expande la sección Builder tools y selecciona Agents.
- Dirígete a la pestaña o consola de AgentCore (Harness Playground / Enterprise Integration).
- Selecciona External MCP Servers / Gateways y haz clic en Register New Server.
- Configura los siguientes campos:
- Name:
CityExplorerMcpServer - Transport Type: Selecciona
HTTP(oSSEsi requiere streaming continuo). - URL Endpoint: Pega la URL pública de tu Balanceador de EKS:
http://<TU-EXTERNAL-IP>/mcp
-
En la sección de autenticación (si estás en producción, requiere API Key mediante AWS Secrets Manager; para tu laboratorio, puedes configurarlo como
None / No Authsi abriste el puerto libremente). -
Haz clic en Save and Verify. Bedrock enviará una petición de prueba a tu pod de EKS ejecutando WebFlux para obtener el mapa de
/tools.
Una vez que el Gateway marque el estado en Active, abre el Harness Playground, asocia el servidor MCP a tu agente (CityExplorerAgent002) y lánzale una de tus queries de prueba: "Give me the current exchange rate for PLN". El agente entenderá la instrucción, llamará al Gateway, este pasará por internet al EKS, y tu código Java retornará el resultado.