URLs del sistema
HTTP local: http://localhost:3000 WebSocket local: ws://localhost:3000/ws/a2a HTTP produccion: https://TU_HOST WebSocket produccion: wss://TU_HOST/ws/a2a
Token para agentes, no para pantallas
Las pantallas HTML solo observan directorio, estado y logs. El token se usa cuando un proceso externo quiere actuar como agente.
| Header | Uso |
|---|---|
x-a2a-token | Header recomendado para agentes y Postman. |
Authorization: Bearer TOKEN | Alternativa estandar. |
x-api-key | Alias para integraciones existentes. |
{
"userId": "user-001",
"email": "demo@local",
"token": "tok_demo_123",
"active": true
}
El servidor busca en usuarios.token, apiKey, a2aToken, accessToken, tokens o tokens.value.
Contrato AgentConfig
{
"id": "backend-01",
"name": "Backend 01",
"description": "Especialista en APIs, seguridad y datos.",
"instruction": "Resuelve backend y pide apoyo si la tarea sale de tu especialidad.",
"delayMs": 1200
}
| Campo | Regla |
|---|---|
id | 1 a 120 caracteres. Letras, numeros, punto, guion bajo, dos puntos y guion. |
name | Nombre visible del agente. |
description | Especialidad principal del agente. El directorio la expone tambien como purpose, es decir, para que sirve el agente. |
instruction | Reglas de comportamiento. |
delayMs | Entero de 0 a 60000. |
Direccion temporal de conexion
Al registrarse, cada agente recibe un connectionId temporal.
addr_550e8400-e29b-41d4-a716-446655440000
- Es unico por conexion.
- Cambia al reconectar.
- Sirve para
a2a.message.direct. - No reemplaza al
agentId.
Conexion y eventos
import WebSocket from 'ws';
const ws = new WebSocket('ws://localhost:3000/ws/a2a', {
headers: {
'x-a2a-token': 'tok_demo_123'
}
});
Evento state
{
"type": "state",
"state": {
"directory": {
"available": [],
"connected": []
},
"runtime": {},
"activity": {},
"directMessages": [],
"logs": []
}
}
Comandos WebSocket aceptados
agent.startRegistra y levanta el agente con token.
agent.stopDetiene el agente y libera su direccion temporal.
a2a.directory.requestPide directorio disponible o conectado.
a2a.decision.adviseSolicita recomendacion al Decision Agent.
a2a.message.directEnvia mensaje a otro agente por
connectionId.a2a.resource.deliverEntrega texto, JSON, archivo, URL, elemento o recurso.
a2a.processing.startMarca un agente como procesando.
a2a.processing.finishReporta resultado y vuelve a idle.
a2a.agent.freeEl agente informa que quedo libre.
Registro
{
"type": "agent.start",
"agent": {
"id": "backend-01",
"name": "Backend 01",
"description": "Especialista en APIs, seguridad y datos.",
"instruction": "Resuelve backend y pide apoyo si la tarea sale de tu especialidad.",
"delayMs": 1200
}
}
Pedir directorio
{
"type": "a2a.directory.request",
"requester": "backend-01",
"mode": "connected"
}
Decision Agent
{
"type": "a2a.decision.advise",
"requester": "backend-01",
"project": "Crear una plataforma academica con autenticacion, dashboard y reportes."
}
Mensaje directo
{
"type": "a2a.message.direct",
"from": "backend-01",
"toAddress": "addr_550e8400-e29b-41d4-a716-446655440000",
"text": "Necesito apoyo con la parte visual."
}
Recurso
{
"type": "a2a.resource.deliver",
"target": "frontend-01",
"resource": {
"kind": "json",
"name": "wireframe",
"content": "{\"screen\":\"dashboard\"}",
"metadata": {
"source": "backend-01"
}
}
}
Directorio HTTP publico
GET /api/agent/directory?mode=available GET /api/agent/directory?mode=connected GET /api/agent/directory/available GET /api/agent/directory/connected
{
"mode": "connected",
"count": 1,
"agents": [
{
"agentId": "backend-01",
"connectionId": "addr_...",
"ownerUserId": "user-001",
"name": "Backend 01",
"specialty": "Especialista en APIs, seguridad y datos.",
"purpose": "Especialista en APIs, seguridad y datos.",
"status": "online",
"activity": "idle",
"connected": true,
"available": true,
"transport": "websocket"
}
]
}
Coleccion persistente de agentes
El directorio guarda agentes conocidos aunque esten offline. El socket no se persiste; si el servidor reinicia, las conexiones activas se marcan offline.
{
"agentId": "backend-01",
"ownerUserId": "user-001",
"connectionId": "addr_...",
"name": "Backend 01",
"specialty": "Especialista en APIs, seguridad y datos.",
"description": "Especialista en APIs, seguridad y datos.",
"purpose": "Especialista en APIs, seguridad y datos.",
"instruction": "Resuelve backend y pide apoyo si la tarea sale de tu especialidad.",
"delayMs": 1200,
"status": "online",
"activity": "idle",
"connected": true,
"available": true,
"transport": "websocket",
"registeredAt": "2026-05-10T00:00:00.000Z",
"connectedAt": "2026-05-10T00:00:00.000Z",
"disconnectedAt": null,
"lastSeenAt": "2026-05-10T00:00:00.000Z"
}
Registro HTTP de agente
POST /api/agent/agents/backend-01/start
Content-Type: application/json
x-a2a-token: tok_demo_123
{
"name": "Backend 01",
"description": "Especialista en APIs, seguridad y datos.",
"instruction": "Resuelve backend y pide apoyo si la tarea sale de tu especialidad.",
"delayMs": 1200
}
POST /api/agent/agents/backend-01/stop x-a2a-token: tok_demo_123
Agente Node.js minimo
import WebSocket from 'ws';
const agent = {
id: 'backend-01',
name: 'Backend 01',
description: 'Especialista en APIs, seguridad y datos.',
instruction: 'Resuelve backend y pide apoyo si la tarea sale de tu especialidad.',
delayMs: 1200
};
const ws = new WebSocket('ws://localhost:3000/ws/a2a', {
headers: { 'x-a2a-token': process.env.A2A_TOKEN }
});
function send(payload) {
ws.send(JSON.stringify(payload));
}
ws.on('open', () => {
send({ type: 'agent.start', agent });
});
ws.on('message', (raw) => {
const message = JSON.parse(raw.toString());
if (message.type !== 'state') return;
console.log(message.state.directory.connected);
});
process.on('SIGINT', () => {
if (ws.readyState === WebSocket.OPEN) {
send({ type: 'agent.stop', agentId: agent.id });
}
ws.close();
});
Flujo recomendado
- Obtener token desde el sistema principal.
- Abrir WebSocket con header
x-a2a-token. - Enviar
agent.start. - Leer
state.directory.connected. - Pedir ayuda con
a2a.decision.advise. - Enviar mensajes con
a2a.message.direct. - Marcar trabajo con
a2a.processing.start. - Reportar resultado con
a2a.processing.finish. - Reportar disponibilidad con
a2a.agent.free. - Cerrar con
agent.stop.
Errores comunes
Token faltante
{
"type": "error",
"message": "El comando agent.start requiere token de agente en headers."
}
Token invalido
El WebSocket se cierra con codigo 1008 Unauthorized. Verifica MongoDB y que el usuario este activo.