Skip to main content
Glama
bhargavlukka

SecureAgentServer

by bhargavlukka

Sistema de agente seguro basado en MCP

Un servidor y cliente MCP para tickets de soporte y cuentas de cliente, construido y asegurado de extremo a extremo: autenticación mediante token firmado, dos salvaguardas independientes contra la inyección de prompts y el envenenamiento de herramientas, un modelo de amenazas documentado y un control de supervisión humana para la única llamada a una herramienta destructiva. Construido para el laboratorio "Secure an MCP-Based Agent System".

Arquitectura

flowchart TD
    HOST["Host application"] --> CLIENT["client.py\n(fastmcp.Client + elicitation_handler)"]
    CLIENT <-->|"Streamable HTTP\nAuthorization: Bearer <signed JWT>"| SERVER

    subgraph Server["server.py — FastMCP('SecureAgentServer')"]
        AUTH["JWTVerifier (HS256)\nissuer + audience + signature checked"]
        G1["Guardrail 1: sanitize_untrusted_text()\napplied to ticket body/subject"]
        G2["Guardrail 2: verify_tool_manifest()\nchecked at startup, refuses to start on mismatch"]
        TOOLS["Tools: search_tickets, lookup_customer_account,\nclose_ticket (elicitation-gated)"]
        AUTH --> TOOLS
        TOOLS --> G1
    end
    G2 -.->|startup check| SERVER

    TOOLS --> TICKETS[("data/tickets.json\n(untrusted customer text)")]
    TOOLS --> CUSTOMERS[("data/customers.json\n('internal API')")]

Related MCP server: enterprise-agent-lab

Configuración

pip install -r requirements.txt

# 1. Set the JWT signing secret (never commit the real value; see .env.example)
export MCP_JWT_SECRET="a-long-random-secret-at-least-32-characters"

# 2. Generate the pinned tool-integrity manifest (a deliberate, manual step —
#    see docs/threat-model.md Risk #2)
python generate_manifest.py

# 3. Run the server
python server.py

# 4. In another terminal (same MCP_JWT_SECRET exported)
python client.py --auto-confirm   # non-interactive demo
python client.py                  # interactive: real yes/no confirmation prompts

Reproducción de los controles de seguridad

Control

Cómo verificarlo

Autenticación con JWT firmado

Los dos últimos demos de client.py: un token con alcance de solo lectura se rechaza en close_ticket, y un token falsificado/sin firmar se rechaza con 401 antes de que se ejecute herramienta alguna — ambos casos quedan capturados en demo/session_log.txt.

Salvaguarda 1: saneamiento de inyección de prompts

Ejecuta el cliente y busca en la salida de search_tickets el TICKET-2002 (su cuerpo contiene una carga útil insertada con el texto "IGNORE ALL PREVIOUS INSTRUCTIONS...") — el texto devuelto se envuelve y la frase desencadenante queda oculta. El stdout del propio servidor registra una línea [SECURITY] cuando esto se activa.

Salvaguarda 2: detección de envenenamiento de herramientas

python demo/verify_tampering_detection.py manipula en memoria la descripción de close_ticket y muestra cómo verify_tool_manifest() detecta la discrepancia; consulta demo/tampering_detection_log.txt para ver una ejecución capturada.

Supervisión humana en acciones destructivas

close_ticket en el registro de la demo solo se completa tras [ELICITATION] ... -> accepting; ejecuta client.py sin --auto-confirm para ver una comprobación de confirmación interactiva real.

Asignación de privilegio mínimo

add_ticket_note / close_ticket comprueban explícitamente write:tickets vía get_access_token() (server.py::_require_scope), no solo un requisito a nivel de conexión — demostrado por el rechazo del token de solo lectura anterior.

Modelo de amenazas

Documento completo, incluidos 5 riesgos identificados con sus mitigaciones y notas de riesgo residual, expuestos explícitamente y no ocultados: docs/threat-model.md.

Lo que queda deliberadamente fuera del alcance, dicho sin rodeos

  • mint_token.py sustituye a un proveedor de identidades OAuth 2.1 real; un despliegue de producción necesita emisión/rotación/revocación real de tokens, no un script de acuñación local.

  • No hay limitación de velocidad ni endurecimiento a nivel de red (terminación de TLS, WAF): esto es una demostración de seguridad a nivel de aplicación, no una guía completa de endurecimiento de despliegue.

  • La mitad basada en regex de la Salvaguarda 1 es explícitamente una capa secundaria de mejor esfuerzo — para saber por qué el envoltorio delimitador es el control realmente usado, ver docs/threat-model.md, Riesgo #1.

Sin secretos en el repositorio

MCP_JWT_SECRET se lee del entorno (consulta .env.example) y nunca se fija en el código ni se incluye en el repositorio. tool_manifest.json sí se incorpora deliberadamente al repositorio: es un manifiesto de hashes fijado (como un lockfile), no un secreto.

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables controlled AI-agent access to enterprise-shaped tools with a deny-by-default gated write path, human approval, dry-run execution, and append-only audit logging.
    1
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enforces authenticated identity on every tool call and SSE frame, rotates vaulted credentials in place, and restricts tools via allowlists.
    71 npm
    MIT