npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcp-bridge-a2a

v0.1.2

Published

MCP ↔ A2A bridge (ARDF subagent profile) template

Downloads

15

Readme

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 [email protected] 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.