MCP Servers

A collection of Model Context Protocol servers, templates, tools and more.

Repositorio con el perfil ARDF subagent@v0.1 y un servidor MCP en Node/TypeScript que actúa como puente hacia agentes A2A.

Created 10/11/2025
Updated 2 months ago
Repository documentation and setup instructions

ARDF Subagent Profile + MCP ↔ A2A Bridge

CI Build codecov

npm npm downloads/month GitHub Release Tarball SHA256

Quality Gate Status Coverage Duplications

Repositorio con el perfil ARDF subagent@v0.1 y un servidor MCP en Node/TypeScript que actúa como puente hacia agentes A2A.

Índice

Contenido

  • schemas/subagent.v0.1.json: JSON Schema (draft 2020-12) que describe cómo publicar un subagente (type subagent) como recurso MCP.
  • examples/ventas-subagent.json: Manifiesto de ejemplo que enlaza con un Agent Card A2A y declara los tools MCP (subagent_start, subagent_send, subagent_cancel).
  • src/server.ts: Servidor MCP sobre HTTP streamable usando el SDK oficial; expone el recurso del subagente, materializa tareas a2a://task/{id} y reenvía las llamadas A2A.

Uso

npm install
npm run dev
# Cliente MCP -> http://localhost:3000/mcp

Flujo sugerido con un cliente MCP:

  1. Leer a2a-agent://ventas-core para obtener el manifiesto ARDF.
  2. Invocar subagent_start con un mensaje (text: "Hola"); la respuesta incluye un resource_link a a2a://task/{id} y structuredContent con { taskUri, taskId }.
  3. Suscríbase a ese recurso (resources/subscribe) y continúe con subagent_send o finalice con subagent_cancel.

Uso rápido

  • Arranque el servidor:
    npm install
    npm run dev
    # Servidor MCP en http://localhost:3000/mcp
    
  • Ejemplo de invocación directa a /mcp para subagent_start (usando curl):
    curl -s \
      -X POST http://localhost:3000/mcp \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{
        "type":"request",
        "method":"tools/call",
        "params":{
          "name":"subagent_start",
          "arguments":{"subagentUri":"a2a-agent://ventas-core","text":"Hola"}
        },
        "id":1
      }'
    
  • Respuesta esperada (forma general):
    {
      "type": "response",
      "result": {
        "content": [
          { "type": "resource_link", "uri": "a2a://task/abc-123" },
          { "type": "text", "text": "{\"taskUri\":\"a2a://task/abc-123\",\"taskId\":\"abc-123\"}" }
        ],
        "isError": false,
        "structuredContent": { "taskUri": "a2a://task/abc-123", "taskId": "abc-123" }
      },
      "id": 1
    }
    
  • Siguiente paso: suscríbase a a2a://task/{id} para recibir notifications/resources/updated y continúe con subagent_send o finalice con subagent_cancel.

Notas de implementación

  • El schema valida IDs, transporte preferido, mapping de skills A2A e identifica los tools MCP que orquestan la tarea.
  • server.ts usa ResourceTemplate para publicar a2a-agent://{id} y a2a://task/{taskId}, enviando notifications/resources/updated cuando el estado cambia.
  • Los tools consumen el Agent Card remoto y llaman a los endpoints JSON-RPC (message/send, tasks/cancel) del agente A2A, manteniendo un almacén en memoria de las tareas activas.
  • La enumeración de subagentes implementada con ResourceTemplate.list debe devolver un objeto { resources: [...] } (no un array directo).
  • Las URIs de subagente usan el esquema a2a-agent://{id}; el id se extrae del hostname de la URI (y se admite como pathname para compatibilidad).
  • El transporte HTTP streamable está configurado con enableJsonResponse: true y sin gestión de sesión (sessionIdGenerator: undefined):
    • El cliente DEBE incluir Accept: application/json, text/event-stream.
    • El encabezado Mcp-Protocol-Version se negocia automáticamente si no se envía; no se requiere Mcp-Session-Id por defecto.

Personalización

  • Sustituye examples/ventas-subagent.json por tus propios subagentes o carga varios en SUBAGENTS.
  • Añade autenticación según security (OAuth2, API key, mTLS) ajustando jsonRpc.
  • Si el Agent Card anuncia message/stream, incorpora un listener SSE y utiliza notifyTaskUpdated para reflejar eventos en tiempo real.

Visión general

  • Propósito: Puente entre MCP (Model Context Protocol) y agentes A2A, exponiendo subagentes como recursos MCP y orquestando tareas mediante tools.
  • Contexto: MCP ofrece un contrato uniforme para recursos y herramientas; A2A define Agent Cards y endpoints JSON-RPC para mensajería y tareas.
  • Resultado: Clientes MCP pueden descubrir, iniciar y manejar conversaciones/tareas con agentes A2A sin conocer los detalles internos.

Arquitectura

  • Componentes principales:
    • Servidor MCP HTTP streamable en src/server.ts.
    • Recursos:
      • a2a-agent://{id}: manifiesto ARDF del subagente.
      • a2a://task/{taskId}: estado de la tarea/conversación.
    • Tools:
      • subagent_start: inicia una tarea con un mensaje inicial.
      • subagent_send: envía mensajes adicionales a una tarea existente.
      • subagent_cancel: solicita cancelar una tarea.
  • Flujo de datos:
    • Descubrimiento → resources/list y resources/read del subagente.
    • Inicio → tools/call subagent_start → creación y publicación de a2a://task/{taskId}.
    • Continuación/Cancelación → subagent_send/subagent_cancel → actualizaciones y notificaciones.
  • Notificaciones: El servidor envía notifications/resources/updated cuando cambian los recursos a2a://task/{taskId}.

Configuración

  • Subagentes:
    • Archivo de ejemplo: examples/ventas-subagent.json.
    • Puedes cargar múltiples manifiestos en una estructura SUBAGENTS en src/server.ts.
  • Seguridad (a nivel de Agent Card):
    • Ajusta encabezados Authorization para OAuth2 o API keys, o configura mTLS según el security del manifest.
  • Transporte MCP:
    • enableJsonResponse: true y sin sesión por defecto.
    • Requisitos del cliente: Accept: application/json, text/event-stream.
    • Mcp-Protocol-Version se negocia si no se envía; Mcp-Session-Id no requerido por defecto.
  • Streaming SSE (message/stream):
    • Variables de entorno:
      • SSE_IDLE_TIMEOUT_MS (por defecto 30000) tiempo de inactividad antes de abortar.
      • SSE_RETRY_BASE_MS (por defecto 1000) base del backoff exponencial.
      • SSE_RETRY_MAX_MS (por defecto 30000) máximo del backoff.
      • SSE_MAX_ATTEMPTS (por defecto , en test 1) límite de reconexiones del listener SSE.
    • En modo test, el listener limita reconexiones para evitar bucles.
  • Persistencia y directorio de datos:
    • DATA_DIR: ruta para almacenar tasks.json. Por defecto ./data/.
    • Escritura atómica de tareas y limpieza periódica configurable: TASK_TTL_MS (TTL, por defecto 24h), TASK_CLEANUP_INTERVAL_MS (intervalo de compactación, por defecto 5min).
  • Endpoint de salud:
    • GET /healthz devuelve { ok, activeSse, tasks, uptime } útil para probes.
  • En modo test, el listener limita reconexiones para evitar bucles.

Autenticación

  • Recomendaciones generales:
    • No commitees secretos en manifiestos; usa variables de entorno y mecanismos seguros de inyección.
    • Si tus manifests usan placeholders (${VAR}), sustitúyelos antes de cargar el archivo o genera el manifest a partir de plantillas.
  • OAuth2 (Bearer):
    • Define VENTAS_TOKEN (u otro nombre según tu agente) y referencia en encabezados Authorization: Bearer ${VENTAS_TOKEN} dentro del manifest.
    • Renueva el token fuera del bridge (cron, CI/CD) y recarga el manifest si cambia.
    • Estrategia de refresh: mantén un job que obtenga tokens de corta duración y actualice la variable de entorno; evita tokens perpetuos.
  • API Key:
    • Define FINANZAS_API_KEY y referencia en encabezados X-API-Key: ${FINANZAS_API_KEY}.
    • Rotación: usa mecanismos de rotación segura y métricas para detectar uso indebido.
  • mTLS:
    • Evita incluir PEMs en el repositorio; usa inyección por archivo/volumen y variables como CLIENT_CERT_PEM, CLIENT_KEY_PEM.
    • Opción recomendada: Reverse proxy (Nginx/Envoy) con client cert del lado del proxy; el bridge habla HTTP(s) interno sin manejar certificados.
    • Si necesitas mTLS desde el bridge, usa una librería HTTP que soporte client cert (axios/node-fetch) y configura cert/key a partir de secretos; valida que tu runtime soporte esta opción.
    • Validación: verifica fecha de expiración y subject del cert al inicio y registra errores claros si faltan o son inválidos.

Despliegue Docker/Cloud

  • Variables de entorno típicas:
    • SSE_IDLE_TIMEOUT_MS, SSE_RETRY_BASE_MS, SSE_RETRY_MAX_MS.
    • Credenciales: VENTAS_TOKEN, FINANZAS_API_KEY, CLIENT_CERT_PEM, CLIENT_KEY_PEM.
  • Docker (ejemplo de comandos):
    # Construir imagen
    docker build -t mcp-a2a-bridge .
    
    # Ejecutar con variables de entorno y volumen para manifests
    docker run --rm -p 3000:3000 \
      -e SSE_IDLE_TIMEOUT_MS=30000 \
      -e SSE_RETRY_BASE_MS=1000 \
      -e SSE_RETRY_MAX_MS=30000 \
      -e VENTAS_TOKEN=REDACTED \
      -v %cd%/examples:/app/examples \
      mcp-a2a-bridge
    
  • Docker Compose (idea general):
    services:
      bridge:
        image: mcp-a2a-bridge:latest
        ports:
          - "3000:3000"
        environment:
          SSE_IDLE_TIMEOUT_MS: 30000
          SSE_RETRY_BASE_MS: 1000
          SSE_RETRY_MAX_MS: 30000
          VENTAS_TOKEN: ${VENTAS_TOKEN}
        volumes:
          - ./examples:/app/examples
    
  • Cloud (lineamientos):
    • Usa secretos gestionados (Render, Fly.io, Railway, etc.) y mapea a variables de entorno.
    • Expone el puerto 3000 y configura health checks básicos en / si tu plataforma lo requiere.
    • Monta examples/ como volumen o empaqueta manifests en la imagen si son públicos.

Publicación en npm

Pasos típicos:

  1. Inicia sesión: npm login
  2. Dry run opcional: npm publish --dry-run (ejecuta automáticamente prepacknpm run build)
  3. Publica: npm publish
  4. Uso rápido vía npx:
    • npx mcp-bridge-a2a@latest (si el paquete expone un script de arranque) o instala global:
    • npm i -g mcp-bridge-a2a y luego mcp-a2a-bridge
    1. Uso rápido vía npx:
    • npx mcp-a2a-bridge (ejecuta el bin del paquete)
    • o instala global: npm i -g mcp-bridge-a2a y luego mcp-a2a-bridge

Notas:

  • El bin del paquete es mcp-a2a-bridge → ejecuta dist/server.js.
  • Requiere Node.js >= 18.

Ejemplos de payloads

  • Petición resources/list (cliente):
{
  "type": "request",
  "method": "resources/list",
  "params": {},
  "id": 1
}
  • Respuesta resources/list (servidor):
{
  "type": "response",
  "result": {
    "resources": [
      { "uri": "a2a-agent://ventas-core", "name": "Ventas Core", "mimeType": "application/json" }
    ]
  },
  "id": 1
}
  • Petición resources/read de subagente:
{
  "type": "request",
  "method": "resources/read",
  "params": { "uri": "a2a-agent://ventas-core" },
  "id": 2
}
  • Respuesta resources/read de subagente:
{
  "type": "response",
  "result": {
    "contents": [
      { "type": "text", "text": "Manifiesto ARDF", "mimeType": "application/json" },
      { "type": "json", "json": { /* contenido del manifest */ } }
    ]
  },
  "id": 2
}
  • Petición tools/call subagent_start:
{
  "type": "request",
  "method": "tools/call",
  "params": {
    "name": "subagent_start",
    "arguments": { "subagentUri": "a2a-agent://ventas-core", "text": "Hola" }
  },
  "id": 3
}
  • Respuesta tools/call subagent_start:
{
  "type": "response",
  "result": {
    "content": [
      { "type": "resource_link", "uri": "a2a://task/abc-123" },
      { "type": "text", "text": "{\"taskUri\":\"a2a://task/abc-123\",\"taskId\":\"abc-123\"}" }
    ],
    "isError": false,
    "structuredContent": { "taskUri": "a2a://task/abc-123", "taskId": "abc-123" }
  },
  "id": 3
}
  • Petición tools/call subagent_send:
{
  "type": "request",
  "method": "tools/call",
  "params": {
    "name": "subagent_send",
    "arguments": { "taskId": "abc-123", "text": "Siguiente mensaje" }
  },
  "id": 4
}
  • Respuesta tools/call subagent_send:
{
  "type": "response",
  "result": {
    "content": [ { "type": "text", "text": "Mensaje enviado" } ],
    "isError": false,
    "structuredContent": { "ok": true }
  },
  "id": 4
}
  • Petición tools/call subagent_cancel:
{
  "type": "request",
  "method": "tools/call",
  "params": {
    "name": "subagent_cancel",
    "arguments": { "taskId": "abc-123" }
  },
  "id": 5
}
  • Respuesta tools/call subagent_cancel:
{
  "type": "response",
  "result": {
    "content": [ { "type": "text", "text": "Tarea cancelada" } ],
    "isError": false,
    "structuredContent": { "canceled": true }
  },
  "id": 5
}
  • Petición resources/read de tarea:
{
  "type": "request",
  "method": "resources/read",
  "params": { "uri": "a2a://task/abc-123" },
  "id": 6
}
  • Respuesta resources/read de tarea (ejemplo):
{
  "type": "response",
  "result": {
    "contents": [
      { "type": "json", "json": { "taskId": "abc-123", "status": "running", "messages": [] } }
    ]
  },
  "id": 6
}

Diagrama de notificaciones (SVG)

Para un diagrama detallado del flujo de suscripciones y eventos SSE, ver docs/notifications.svg.

Validación de esquemas (JSON Schema)

  • El esquema de subagentes está en schemas/subagent.v0.1.json.
  • Recomendación: valida tus manifiestos con una librería de JSON Schema (p. ej., Ajv) antes de cargarlos en el bridge.
  • Puntos clave a validar:
    • type debe ser subagent.
    • id único y estable.
    • a2a.agentCardUri accesible y válido.
    • security acorde (OAuth2, API Key, mTLS) y sin secretos hardcodeados.
    • mcpBridge.tools declara al menos subagent_start (y opcionalmente subagent_send, subagent_cancel).
    • taskResourceTemplate con el patrón a2a://task/{taskId}.
  • Ejemplo de validación conceptual:
    • Instala una librería de validación en tu pipeline y falla el build si el manifest no cumple el schema.

Manifest avanzado (API Key + mTLS)

Ejemplo ilustrativo de un subagente que usa API Key y mTLS para llamadas JSON-RPC:

{
  "type": "subagent",
  "id": "finanzas-core",
  "name": "Finanzas Core",
  "a2a": {
    "agentCardUri": "https://finanzas.example.com/.well-known/agent-card.json"
  },
  "security": {
    "auth": {
      "type": "apiKey",
      "in": "header",
      "name": "X-API-Key",
      "value": "${FINANZAS_API_KEY}"
    },
    "mtls": {
      "enabled": true,
      "clientCert": "${CLIENT_CERT_PEM}",
      "clientKey": "${CLIENT_KEY_PEM}"
    }
  },
  "mcpBridge": {
    "tools": [
      { "name": "subagent_start" },
      { "name": "subagent_send" },
      { "name": "subagent_cancel" }
    ]
  },
  "taskResourceTemplate": "a2a://task/{taskId}",
  "capabilities": {
    "message": {
      "send": {
        "endpoint": "https://finanzas.example.com/json-rpc/message/send",
        "headers": { "X-API-Key": "${FINANZAS_API_KEY}" },
        "mtls": true
      }
    },
    "tasks": {
      "get": {
        "endpoint": "https://finanzas.example.com/json-rpc/tasks/get",
        "headers": { "X-API-Key": "${FINANZAS_API_KEY}" },
        "mtls": true
      },
      "cancel": {
        "endpoint": "https://finanzas.example.com/json-rpc/tasks/cancel",
        "headers": { "X-API-Key": "${FINANZAS_API_KEY}" },
        "mtls": true
      }
    }
  }
}

Notas:

  • Usa variables de entorno para credenciales y claves mTLS; no las incluyas en el repositorio.
  • Verifica certificados y claves en tiempo de arranque y reporta errores claros si faltan.

FAQ (Preguntas frecuentes)

  • ¿Por qué no veo recursos en resources/list?
    • Asegúrate de que ResourceTemplate.list devuelve { resources: [...] } y no un array directo.
    • Confirma que hay manifiestos cargados en SUBAGENTS y que sus URIs a2a-agent://{id} se publican.
  • ¿Recibo error de protocolo o encabezados inválidos?
    • Incluye Accept: application/json, text/event-stream y permite que el servidor negocie Mcp-Protocol-Version si falta.
  • ¿subagent_start no devuelve resource_link?
    • Revisa que el agente A2A devuelva taskId; el bridge construye a2a://task/{taskId} a partir de ese valor.
  • ¿Se parsea mal subagentUri?
    • Para URIs como a2a-agent://ventas-core, el ID se extrae del hostname. Se admite pathname por compatibilidad.
  • ¿No recibo actualizaciones de la tarea?
    • Suscríbete al recurso a2a://task/{id} con resources/subscribe y mantén SSE activo para notifications/resources/updated.

Pruebas

  • Ejecutar: npm test.
  • El archivo test/mcp.spec.ts cubre:
    • resources/list + resources/read de subagentes.
    • tools/call para subagent_start, subagent_send, subagent_cancel.
  • Mock de fetch para simular respuestas A2A.
  • Validaciones clave:
    • ResourceTemplate.list devuelve { resources: [...] }.
    • Accept incluye application/json, text/event-stream.
    • resource_link.uri correcto en subagent_start.

Troubleshooting

  • Error: expected undefined to be an instance of Array en resources/list.
    • Causa: El callback de ResourceTemplate.list devuelve un array en lugar de { resources }.
    • Solución: Devolver { resources: [...] }.
  • tools/call subagent_start no devuelve resource_link.
    • Causa: Falta taskId en la respuesta del agente A2A.
    • Solución: Asegurar que la respuesta A2A incluya taskId y construir a2a://task/{taskId}.
  • Identificador de subagente incorrecto.
    • Causa: Extracción desde pathname cuando la URI es a2a-agent://{id}.
    • Solución: Extraer desde hostname y usar pathname solo por compatibilidad.
  • Protocolo/encabezados.
    • Causa: Accept incorrecto o versión MCP no negociada.
    • Solución: Incluir Accept: application/json, text/event-stream; permitir negociación de Mcp-Protocol-Version.

Glosario

  • ARDF: Esquema para describir recursos y capacidades de agentes.
  • MCP: Protocolo para herramientas y recursos de contexto.
  • A2A: Protocolo para agentes y sus interacciones JSON-RPC.
  • Agent Card: Documento que describe endpoints y capacidades de un agente A2A.
  • ResourceTemplate: Utilidad del SDK MCP para publicar familias de URIs.

Diagrama de arquitectura (ASCII)

+--------------------+            HTTP (JSON/SSE)            +-------------------------+
|    Cliente MCP     |-------------------------------------->|   Servidor MCP (bridge) |
| (Inspector, IDE)   |    resources/*, tools/call            |   src/server.ts         |
+--------------------+                                       +-----------+-------------+
                                                                     |  JSON-RPC
                                                                     v
                                                              +------+----------------+
                                                              |   Agente A2A remoto  |
                                                              | (Agent Card: send,   |
                                                              |  stream, tasks/*)    |
                                                              +----------------------+

Versión SVG (detallada): docs/architecture.svg

  • Descubrimiento: El cliente llama resources/list y resources/read.
  • Orquestación: tools/call ejecuta message/send y publica a2a://task/{id}.
  • Actualizaciones: El servidor envía notifications/resources/updated a suscriptores.

Manifest avanzado (OAuth2 + SSE)

Ejemplo ilustrativo de un subagente con autenticación Bearer y message/stream para SSE:

{
  "type": "subagent",
  "id": "ventas-core",
  "name": "Ventas Core",
  "a2a": {
    "agentCardUri": "https://ventas.example.com/.well-known/agent-card.json"
  },
  "security": {
    "auth": {
      "type": "oauth2",
      "token": "${VENTAS_TOKEN}",
      "scopes": ["message:send", "tasks:read", "tasks:cancel"]
    }
  },
  "mcpBridge": {
    "tools": [
      { "name": "subagent_start" },
      { "name": "subagent_send" },
      { "name": "subagent_cancel" }
    ]
  },
  "taskResourceTemplate": "a2a://task/{taskId}",
  "capabilities": {
    "message": {
      "send": {
        "endpoint": "https://ventas.example.com/json-rpc/message/send",
        "headers": { "Authorization": "Bearer ${VENTAS_TOKEN}" }
      },
      "stream": {
        "endpoint": "https://ventas.example.com/events/message/stream",
        "sse": true,
        "headers": { "Authorization": "Bearer ${VENTAS_TOKEN}" }
      }
    },
    "tasks": {
      "get": {
        "endpoint": "https://ventas.example.com/json-rpc/tasks/get",
        "headers": { "Authorization": "Bearer ${VENTAS_TOKEN}" }
      },
      "cancel": {
        "endpoint": "https://ventas.example.com/json-rpc/tasks/cancel",
        "headers": { "Authorization": "Bearer ${VENTAS_TOKEN}" }
      }
    }
  }
}

Notas:

  • Usa variables de entorno (${VENTAS_TOKEN}) para no commitear secretos.
  • Si message/stream está presente, el servidor puede abrir SSE y llamar a notifyTaskUpdated con cada evento.

Integración con clientes MCP (rápida)

  • Encabezados recomendados:
    • Accept: application/json, text/event-stream
    • Mcp-Protocol-Version: 2024-10-01 (opcional; el servidor negocia si falta)
  • Endpoints típicos (ejemplo de curl):
# resources/list
curl -s -X POST \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  http://localhost:3000/mcp \
  -d '{"type":"request","method":"resources/list","params":{},"id":1}'

# tools/call subagent_start
curl -s -X POST \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  http://localhost:3000/mcp \
  -d '{"type":"request","method":"tools/call","params":{"name":"subagent_start","arguments":{"subagentUri":"a2a-agent://ventas-core","text":"Hola"}},"id":2}'

# resources/read de tarea
curl -s -X POST \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  http://localhost:3000/mcp \
  -d '{"type":"request","method":"resources/read","params":{"uri":"a2a://task/abc-123"},"id":3}'

Sugerencias:

  • Suscríbete a a2a://task/{id} (resources/subscribe) para recibir resources/updated via SSE.
  • Valida siempre structuredContent contra outputSchema del tool.
Quick Setup
Installation guide for this server

Installation Command (package not published)

git clone https://github.com/MauricioPerera/ARDF-MCP-A2A
Manual Installation: Please check the README for detailed setup instructions and any additional dependencies required.

Cursor configuration (mcp.json)

{ "mcpServers": { "mauricioperera-ardf-mcp-a2a": { "command": "git", "args": [ "clone", "https://github.com/MauricioPerera/ARDF-MCP-A2A" ] } } }