Inicio / Artículos

Enterprise MCP en Bedrock AgentCore con Okta SSO y Trino

Cerebro Lambda OAuth, relé PKCE, gateway y entorno de ejecución AgentCore, además de conexiones Trino por usuario sin fugas de tokens compartidos.

Editor: REACTAPP.TOP13 min de lectura2982 palabras

Fuentes: Building Enterprise MCP on AWS Bedrock AgentCore: Okta SSO + Trino

Esta guía reconstruye el proceso desde las materias primas hasta un sistema funcional para: crear MCP empresarial en AWS Bedrock AgentCore: Okta SSO + Trino. Se enfoca en pasos operativos, verificaciones explícitas y código que se puede insertar directamente en un repositorio sin necesidad de adivinar su propósito. Para tener una visión general, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo de tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de una demostración a entornos compartidos.

Resumen

En resumen, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación; los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema. Exponga herramientas con esquemas limitados y etiquetas explícitas sobre efectos secundarios. Los administradores necesitan saber qué llamadas modifican el estado antes de aprobarlas automáticamente.

MCP Client → Lambda OAuth Proxy → Bedrock AgentCore Gateway → AgentCore Runtime → Trino

Por qué entregar un token sin procesar a una herramienta hace fallar las verificaciones empresariales

Para comprender por qué entregar un token sin procesar a una herramienta hace que fallen las verificaciones empresariales, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Exponga las herramientas con esquemas limitados y etiquetas explícitas sobre efectos secundarios. Los administradores necesitan saber qué llamadas modifican el estado antes de aprobarlas automáticamente.

Arquitectura a primera vista

Para una visión general de la arquitectura, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Exponga herramientas con esquemas limitados y etiquetas explícitas sobre efectos secundarios. Los administradores necesitan saber qué llamadas modifican el estado antes de aprobarlas automáticamente. Para una visión general de la arquitectura, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de la versión de demostración a entornos compartidos.

1. El cliente MCP

Para el cliente MCP, se deben definir las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido, sin tener que adivinar el estado oculto. La configuración debe mantenerse fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben estar en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema.

2. El proxy Lambda MCP (el cerebro de OAuth)

Para el punto 2: el proxy Lambda MCP (el cerebro de OAuth) requiere que se definan las entradas, el propietario de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido, sin tener que adivinar el estado oculto. Se debe documentar tanto la ruta normal como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. La autenticación se realiza en la pasarela y la reautorización en el plano de datos; un token portador por sí solo no constituye un límite entre tenencias.

3. La pasarela AWS Bedrock AgentCore

Para el punto 3, el AWS Bedrock AgentCore Gateway: defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una tarea falla, el error debe indicar una única responsabilidad y no un proceso complicado. Autentíquese en la pasarela y vuelva a autorizarse en el plano de datos. Un token de portador por sí solo no constituye un límite entre tenencias. Para el punto 3, el AWS Bedrock AgentCore Gateway: defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo de tokens o consultas junto con los resultados funcionales. Tener visibilidad temprana del costo evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos.

4. El entorno de ejecución AWS Bedrock AgentCore

En el caso del entorno de ejecución AWS Bedrock AgentCore 4, se deben definir las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar un paso a partir de un punto de control conocido, sin tener que adivinar el estado oculto. La configuración debe mantenerse fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un único lugar que los operadores puedan auditar sin necesidad de leer todo el sistema. Se debe registrar el nombre de la herramienta, el hash de los argumentos, la latencia y el resultado de cada llamada. Depurar bucles del agente sin ese registro desperdicia horas.

El flujo de autenticación de extremo a extremo

Para el flujo de autenticación integral, defina las entradas, el responsable de cada paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Registre el nombre de la herramienta, el hash de los argumentos, la latencia y el resultado de cada llamada. Depurar ciclos del agente sin esa información desperdicia horas.

┌─────────────────────┐
│    MCP Client       │
│ Claude / Claude Code│
└──────────┬──────────┘
           │
           │ OAuth 2.1 + PKCE
           ▼
┌─────────────────────┐
│   Lambda MCP Proxy  │◄──────────────► Okta
│ OAuth / Identity    │   Login + JWT
└──────────┬──────────┘
           │
           │ Bearer JWT
           ▼
┌─────────────────────────────┐
│ Bedrock AgentCore Gateway   │
│ CUSTOM_JWT validation       │
└────────────┬────────────────┘
             │ SigV4
             ▼
┌─────────────────────────────┐
│ Bedrock AgentCore Runtime   │
│ FastMCP Server              │
│                             │
│ list_catalogs               │
│ describe_table              │
│ execute_query               │
└────────────┬────────────────┘
             │
             │ User JWT
             ▼
┌─────────────────────────────┐
│ Enterprise Query Engine     │
│ Per-user authorization      │
└─────────────────────────────┘

Análisis en profundidad 1: Enseñar al proxy Lambda a utilizar OAuth 2.1

Para el análisis en profundidad 1: Enseñar al proxy Lambda a utilizar OAuth 2.1, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una tarea falla, el error debe indicar una única responsabilidad y no un proceso complicado. Registre el nombre de la herramienta, el hash de los argumentos, la latencia y el resultado de cada llamada. Depurar bucles de agentes sin esa información desperdicia horas. Para el análisis en profundidad 1: Enseñar al proxy Lambda a utilizar OAuth 2.1, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo de tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando se sigue un camino determinado.

pasa de entornos de demostración a entornos compartidos.

# GET /.well-known/protected-resource
{
  "resource": f"{BASE_URL}/mcp",
  "authorization_servers": [BASE_URL],
}
# GET /.well-known/oauth-authorization-server
{
  "issuer": BASE_URL,
  "authorization_endpoint": f"{BASE_URL}/authorize",
  "token_endpoint":         f"{BASE_URL}/token",
  "registration_endpoint":  f"{BASE_URL}/register",
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "code_challenge_methods_supported": ["S256"],
  "token_endpoint_auth_methods_supported": ["none"],
}

Reenvío de PKCE sin interrumpirlo

Para reenviar PKCE sin interrumpirlo, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar que los operadores puedan auditar sin tener que leer todo el sistema. Exponga herramientas con esquemas limitados y etiquetas explícitas de efectos secundarios. Los hosts necesitan saber qué llamadas modifican el estado antes de aprobarlas automáticamente.

relay_payload = json.dumps({"u": original_redirect, "s": original_state}).encode()
relay_state   = base64.urlsafe_b64encode(relay_payload).decode().rstrip("=")
params = {
    "client_id":             OKTA_CLIENT_ID,
    "response_type":         "code",
    "redirect_uri":          f"{BASE_URL}/callback",   # <- proxy's callback
    "state":                 relay_state,               # <- client's state, wrapped
    "code_challenge":        qs["code_challenge"],      # <- untouched
    "code_challenge_method": "S256",
}

Registro dinámico de clientes

Para el registro dinámico de clientes, defina las entradas, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Ofrezca herramientas con esquemas limitados y etiquetas explícitas sobre los efectos secundarios. Los administradores necesitan saber qué llamadas modifican el estado antes de aprobarlas automáticamente.

def handle_register(event):
    req = json.loads(event.get("body") or "{}")
    return _json_resp(201, {
        "client_id": OKTA_CLIENT_ID,
        "redirect_uris": req.get("redirect_uris", []),
        "grant_types":   req.get("grant_types", ["authorization_code"]),
        "response_types": req.get("response_types", ["code"]),
        "token_endpoint_auth_method": "none",
    })

Análisis en profundidad 2: Problemas observados cuando FastMCP estaba detrás de AgentCore

Para el análisis en profundidad 2: Se observaron fallos cuando FastMCP se encontraba detrás de AgentCore; antes de modificar el código, hay que definir las entradas, el responsable del paso y los criterios de finalización. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Es preferible utilizar unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Exponer herramientas con esquemas limitados y etiquetas explícitas sobre efectos secundarios. Los hosts necesitan saber qué llamadas modifican el estado antes de aprobarlas automáticamente. Para el análisis en profundidad 2: Se observaron fallos cuando FastMCP se encontraba detrás de AgentCore; antes de modificar el código, hay que definir las entradas, el responsable del paso y los criterios de finalización. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registrar los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana del costo previene...

Existen facturas sorpresa cuando el camino pasa de entornos de demostración a entornos compartidos.

Riesgo agudo n.º 1: Ningún encabezado HTTP personalizado llega al contenedor

En cuanto al Riesgo agudo n.º 1: Ningún encabezado HTTP personalizado llega al contenedor, se deben definir las entradas, el propietario de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Se debe mantener la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar que los operadores puedan auditar sin tener que leer todo el sistema. Autenticar en la pasarela y volver a autorizar en el plano de datos. Un token portador por sí solo no constituye un límite entre tenencias.

meta = payload.setdefault("params", {}).setdefault("_meta", {})
meta["okta_token"]   = token         # the validated user identity
meta["trino_target"] = routing_hint  # optional routing metadata
class CredentialExtractMiddleware:
    async def __call__(self, scope, receive, send):
        # buffer the whole body
        body = await _read_full_body(receive)
        # pull identity out of params._meta
        meta = json.loads(body).get("params", {}).get("_meta", {})
        token = meta.get("okta_token")        # AgentCore also injects the validated caller token as a header —
        # prefer that when present, fall back to _meta / Authorization otherwise
        token = token or _caller_token_from_headers(scope)        _req_okta_token.set(token)        # replay the buffered body so the MCP app can read it too
        await self.app(scope, _replay(body, receive), send)

Riesgo agudo n.º 2: AgentCore elimina el encabezado Accept

Para el caso de borde afilado n.º 2: AgentCore elimina la cabecera Accept, define las entradas, el propietario de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa a partir de un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Autentique en la pasarela y vuelva a autorizar en el plano de datos. Un token portador por sí solo no constituye un límite entre tenencias.

class InjectAcceptMiddleware:
    async def __call__(self, scope, receive, send):
        if scope["type"] == "http":
            headers = [(k, v) for k, v in scope["headers"] if k != b"accept"]
            headers.append((b"accept", b"application/json, text/event-stream"))
            scope = {**scope, "headers": headers}
        await self.app(scope, receive, send)

Análisis en profundidad 3: Asociación de tokens de acceso de Okta con sesiones aisladas de Trino

Para el Análisis Profundo 3: Asociar tokens de acceso de Okta a sesiones aisladas de Trino, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso desde un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el error debe indicar una única responsabilidad y no un proceso complicado. Autentique en la pasarela y vuelva a autorizarlo en el plano de datos. Un token portador por sí solo no constituye un límite entre tenencias. Para el Análisis Profundo 3: Asociar tokens de acceso de Okta a sesiones aisladas de Trino, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso desde un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo de los tokens o consultas junto con los resultados funcionales. Tener visibilidad temprana del costo evita facturas inesperadas.

Cuando la ruta pasa de entornos de demostración a entornos compartidos.

def get_connection():
    token = _req_okta_token.get()
    user  = _jwt_email(token)          # e.g. "alice@company.com"
    return backend.connect(
        host=BACKEND_HOST,
        user=user,                      # identity for attribution/authorization
        auth=JWTAuthentication(token),  # the backend re-validates this itself
        http_scheme="https",
    )

La autorización y la aplicación de permisos de solo lectura pertenecen en última instancia a la capa de datos.

Para que la autorización y la aplicación de permisos de solo lectura pertenezcan en última instancia a la capa de datos, defina las entradas, el propietario del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Mantenga la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben estar en un lugar que los operadores puedan auditar sin tener que leer todo el grafo. Registre el nombre de la herramienta, el hash de los argumentos, la latencia y el resultado de cada llamada. Depurar agentes en bucles sin esa información desperdicia horas.

forbidden = ("INSERT", "UPDATE", "DELETE", "DROP", "CREATE",
             "ALTER", "TRUNCATE", "MERGE", "REPLACE", "GRANT", "REVOKE")
if any(query.strip().upper().startswith(kw) for kw in forbidden):
    raise ValueError("Write operations are not allowed.")

Dos formas de conectar al cliente

Para las dos formas de conectar al cliente, defina los parámetros de entrada, el responsable de la etapa y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la etapa desde un punto de control conocido sin tener que adivinar el estado oculto. Documente tanto la ruta óptima como la ruta de recuperación. Las reintentos, los controles humanos y el manejo de mensajes no entregados forman parte del producto, no son mejoras posteriores. Registre el nombre de la herramienta, el hash de los argumentos, la latencia y el resultado de cada llamada. Depurar ciclos del agente sin esa información desperdicia horas.

{ "mcpServers": { "data": { "type": "http", "url": "https://<lambda-url>/mcp" } } }
{ "mcpServers": { "data": { "type": "http", "url": "http://localhost:8766/mcp" } } }

Lo que aprendimos

Según lo aprendido, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando una tarea falla, el error debe indicar una única responsabilidad y no un proceso complicado. Registre el nombre de la herramienta, el hash de los argumentos, la latencia y el resultado de cada llamada. Depurar procesos sin ese registro desperdicia horas. Según lo aprendido, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea desde un punto de control conocido sin tener que adivinar el estado oculto. Anote los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. Tener visibilidad del costo desde el principio evita facturas inesperadas cuando se pasa de entornos de demostración a entornos compartidos.

Conclusión

Para concluir, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de datos confidenciales y las banderas de funcionalidad deben encontrarse en un lugar donde los operadores puedan auditarlos sin necesidad de leer todo el sistema.

Lista de verificación operativa

Para la lista de verificación operativa, defina las entradas, el responsable de la tarea y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar la tarea a partir de un punto de control conocido sin tener que adivinar el estado oculto.

Considere esta etapa como un contrato entre las entradas y las salidas validadas. Asigne nombres a los artefactos, defina verificaciones de éxito y rechace las completaciones parciales silenciosas.

Autentíquese en la pasarela y vuelva a autorizarlo en el plano de datos. Un token portador por sí solo no constituye un límite entre tenencias.

Haga puntos de control después de los pasos costosos. La función de reanudación no debe volver a facturar la misma llamada al LLM cuando un operador intenta nuevamente un nodo posterior.

Transfiera la identidad desde el cliente al almacenamiento downstream sin reducir a todos a una cuenta de servicio.

Agregue una prueba de funcionamiento básica que ejecute la ruta crítica en CI con configuraciones fijas, y no con APIs pagadas en tiempo real, siempre que lo permitan los presupuestos.

Antes de promocionar el stack, congele las versiones, capture una transcripción de referencia para la ruta crítica y confirme los pasos de reversión. Los entornos compartidos requieren límites de velocidad, verificaciones de arrendatario y un responsable claro para la rotación de credenciales. Prefiera una fiabilidad sólida a demostraciones ingeniosas pero puntuales.

Nota de despliegue 1: fije las imágenes, establezca presupuestos de solicitudes y verifique la aislación de arrendatarios en un entorno canario antes de despliegues más amplios.

Nota de despliegue 2: fije las imágenes, establezca presupuestos de solicitudes y verifique la aislación de arrendatarios en un entorno canario antes de despliegues más amplios.

Nota de despliegue 3: fije las imágenes, establezca presupuestos de solicitudes y verifique la aislación de arrendatarios en un entorno canario antes de despliegues más amplios.

Nota de despliegue 4: fije las imágenes, establezca presupuestos de solicitudes y verifique la aislación de arrendatarios en un entorno canario antes de despliegues más amplios.

Nota de despliegue 5: fije las imágenes, establezca presupuestos de solicitudes y verifique la aislación de arrendatarios en un entorno canario antes de despliegues más amplios.

Nota de despliegue 6: fije las imágenes, establezca presupuestos de solicitud y verifique la aislación de los inquilinos en una versión canario antes de despliegues más amplios.

Lecturas relacionadas