Hay algo que nadie quiere decir en voz alta sobre los agentes de IA:
Cada agente de IA que desplegás empieza desde
cero absoluto,
en cada sesión.
Sabe SQL. Sabe estadística. Sabe razonar sobre datos. Pero no sabe que en tu empresa
los empleados se llaman "asociados". No sabe que la columna
VentasBrutas
ya excluye las devoluciones, entonces nunca hay que restarlas de nuevo. No sabe que tu CFO
siempre quiere las cifras en USD y en MXN, en columnas separadas, sin excepción.
El AI adivina. A veces bien, a veces mal. Y cada suposición incorrecta es una respuesta errónea entregada con plena confianza.
Resolvimos esto en PeopleworksGPT con un patrón que llamamos The Intelligence Handshake. Este artículo explica qué es, cómo funciona arquitectónicamente y cómo implementarlo en cualquier servidor MCP.
El Problema de la Amnesia
MCP (Model Context Protocol) es un estándar poderoso. Permite que clientes de IA — Claude, ChatGPT, Copilot, Gemini — llamen a los tools de tu servidor para consultar bases de datos, obtener datos, ejecutar lógica de negocio. El ecosistema está creciendo rápido.
Pero casi todas las implementaciones MCP comparten el mismo punto ciego arquitectónico: el contexto solo fluye en una dirección.
Cómo funciona la mayoría de los servidores MCP
ORDER BY Monto DESC
Con The Intelligence Handshake
VentasUSD, VentasMXN
FROM Asociados
ORDER BY VentasUSD DESC
La diferencia entre esas dos consultas no es inteligencia de IA — ambas son inteligentes. La diferencia es conocimiento del dominio. The Intelligence Handshake es el patrón arquitectónico que entrega ese conocimiento de forma confiable, en cada consulta, desde la primera interacción.
La Revelación: Dos Direcciones, Dos Canales
El patrón tiene una premisa engañosamente simple: el conocimiento debe fluir en ambas direcciones entre cliente y servidor. Esto nos da dos canales distintos para diseñar:
El servidor expone su inteligencia de dominio acumulada al cliente AI al inicio de cada sesión.
GetConversationContext()
El cliente AI pasa su foco conversacional actual, para que el servidor genere SQL relevante al análisis activo — no solo a la pregunta aislada.
ExecuteQueryAsync(clientContext: "...")
Reglas de personalización que persisten entre sesiones. El AI aprende una vez que este usuario siempre quiere output con doble moneda, y esa regla se aplica automáticamente a cada consulta para siempre — con detección de conflictos para prevenir contradicciones.
AddUserRule() · ListUserRules() · RemoveUserRule()
El Disparador Determinístico
Existe un problema arquitectónico sutil pero crítico: ¿cómo garantizás que el cliente AI
realmente llame a GetConversationContext
al inicio de cada sesión?
Podés escribir "Llama esto al INICIO de la sesión" en el description del tool. Los buenos LLMs como Claude y GPT-4o a menudo siguen esa instrucción. Pero "a menudo" no es suficiente para un sistema en producción. Necesitamos comportamiento determinístico, no probabilístico.
La solución es elegante: incrustar las instrucciones de priming directamente en la respuesta
de autenticación. El cliente debe llamar a
authenticate()
antes de hacer cualquier otra cosa. Lo que sea que devuelva esa respuesta, el AI lo procesará.
Entonces ponemos las instrucciones ahí.
// La idea clave: authenticate() es la primera llamada obligatoria.
// Lo que devuelva, el AI DEBE procesarlo. Entonces ponemos
// las instrucciones de priming adentro — haciendo el priming determinístico.
return JsonSerializer.Serialize(new
{
success = true,
session_token = sessionToken,
user_id = user.Id,
username = user.UserName,
expires_at = DateTime.UtcNow.AddMinutes(60).ToString("O"),
next_step = new
{
action = "prime_session_context",
instructions = new[]
{
"1. Llamar a ListConnections(sessionToken) para obtener las conexiones disponibles.",
"2. Llamar a GetConversationContext(sessionToken, connectionId) para la conexión elegida.",
"3. IMPORTANTE: Leer el campo 'client_priming_guide' y mantenerlo activo en tu"
+ " contexto para TODAS las consultas subsecuentes de esta sesión."
+ " Contiene hints del dominio de negocio, reglas de personalización del usuario"
+ " e historial de consultas recientes que harán tu SQL significativamente más preciso.",
"4. Ahora estás listo para ejecutar consultas domain-aware con ExecuteQueryAsync."
},
tip = "Pasá el client_priming_guide como 'clientContext' en ExecuteQueryAsync"
+ " para maximizar la precisión del SQL en cada consulta."
}
});
Por qué esto funciona mejor que instrucciones en el description del tool
Los descriptions de tools se leen una vez cuando el cliente se conecta, y su influencia
sobre el comportamiento se desvanece a medida que avanza la conversación. La respuesta
de autenticación, en cambio, es un tool result en vivo — una salida concreta
que el AI acaba de recibir y está procesando activamente. Los LLMs tratan los tool
results recientes con alta fidelidad. El campo next_step
no es una sugerencia enterrada en metadatos; es una instrucción directa en el context window activo.
Construyendo los Tres Pilares
GetConversationContext — El Primador de Sesión
Canal Servidor → Cliente
Este tool agrega todo el conocimiento del dominio en una sola llamada, minimizando los
round-trips. Crucialmente, produce un
client_priming_guide:
un bloque de texto listo para usar que el cliente AI puede referenciar durante toda la sesión.
clientContext — El Foco Conversacional
Canal Cliente → Servidor
Esta es la dirección inversa: el cliente AI comparte lo que sabe con el servidor. Cuando un usuario ha estado preguntando sobre el rendimiento del Q4 durante los últimos cinco mensajes, el cliente AI tiene ese contexto. El servidor no. Sin este canal, el servidor genera SQL que responde la pregunta aislada — ignorando el hilo analítico.
/// Ensambla el contexto enriquecido en tres capas ordenadas por prioridad:
/// 1. additionalContext — restricciones técnicas/multi-tenant (mayor prioridad técnica)
/// 2. clientContext — foco conversacional del cliente AI
/// 3. userRules — reglas de personalización persistidas (mayor prioridad semántica)
private async Task<string?> BuildEnrichedContextAsync(
long userId, long connectionId,
string? clientContext, string? additionalContext)
{
var parts = new List<string>();
if (!string.IsNullOrWhiteSpace(additionalContext))
parts.Add(additionalContext);
if (!string.IsNullOrWhiteSpace(clientContext))
parts.Add("[CONTEXTO CONVERSACIONAL DEL CLIENTE - Usá esto para entender el foco actual]:\n"
+ clientContext);
var userRules = await _context.UserConversationRules
.Where(r => r.UserId == userId && r.IsActive && !r.Deleted
&& (r.DatabaseConnectionSettingId == null
|| r.DatabaseConnectionSettingId == connectionId))
.Select(r => r.RuleText)
.ToListAsync();
if (userRules.Count > 0)
parts.Add("REGLAS DE PERSONALIZACIÓN (APLICAR SIEMPRE A LA RESPUESTA):\n"
+ string.Join("\n", userRules.Select(r => $"- {r}")));
return parts.Count > 0 ? string.Join("\n\n", parts) : null;
}
Reglas de Personalización — Inteligencia Persistente
Memoria que sobrevive a las sesiones
Las reglas se persisten por usuario en la base de datos y se inyectan automáticamente en cada ejecución de consulta. El desafío de ingeniería más interesante aquí es la detección de conflictos: antes de guardar una regla nueva, el sistema usa AI para verificar si contradice alguna regla existente.
- • "Mostrar en USD" + "Ordenar por fecha descendente"
- • "Incluir nombre completo" + "Siempre incluir departamento"
- • "Agrupar ventas por mes" + "Mostrar comparativa año a año"
- • "Mostrar solo USD" ↔ "Mostrar USD y MXN"
- • "Ordenar ascendente" ↔ "Ordenar descendente"
- • "Solo empleados activos" ↔ "Incluir todos los estados"
El Bug Oculto que Destruye el Contexto en Silencio
Mientras construíamos este sistema, descubrimos un bug crítico que probablemente existe en muchas implementaciones MCP. Es el tipo de bug que es invisible en los tests porque todo parece funcionar — hasta que trazás los datos a través del pipeline completo.
El método de servicio aceptaba additionalContext como parámetro —
y después nunca lo pasaba al cliente AI.
// QueryExecutionService.cs (con el bug)
var result = await _aiClient
.ExecuteNaturalLanguageQueryAsync(
connection.Id,
chatRequest,
question, // ← additionalContext nunca llega!
sql: null,
options: null,
securityFilter,
cancellationToken);
// QueryExecutionService.cs (corregido)
var questionWithContext = string.IsNullOrWhiteSpace(additionalContext)
? question
: $"{question}\n\n{additionalContext}";
var result = await _aiClient
.ExecuteNaturalLanguageQueryAsync(
connection.Id,
chatRequest,
questionWithContext, // ← contexto llega al AI
sql: null,
options: null,
securityFilter,
cancellationToken);
Revisá tu propio servidor MCP por este patrón
Si tenés un método de servicio que acepta parámetros de contexto y los pasa a un cliente AI, trazá cada parámetro hasta su destino. Es sorprendentemente común que los parámetros de contexto sean aceptados en una capa y descartados silenciosamente en la siguiente. El síntoma es sutil: las consultas funcionan, pero son genéricas — el AI nunca recibió el contexto que cuidadosamente construiste.
Antes vs. Después: El SQL Cuenta la Historia
Consulta del usuario: "Mostrame los mejores vendedores del trimestre pasado"
-- Genérico, estadísticamente correcto,
-- pero incorrecto para este negocio
SELECT TOP 10
empleado_id,
nombre_empleado,
SUM(monto_venta) AS total_ventas
FROM empleados -- incorrecto: debería ser 'asociados'
WHERE
fecha_venta >= '2025-10-01'
AND fecha_venta < '2026-01-01'
-- falta: filtro status = 'A'
-- falta: devoluciones ya incluidas (¡bug!)
GROUP BY empleado_id, nombre_empleado
ORDER BY total_ventas DESC
-- falta: columnas de doble moneda
-- falta: preferencia de fecha desc del usuario
-- Domain-aware, preciso para el negocio,
-- personalizado desde el primer intento
SELECT TOP 10
a.asociado_id,
a.nombre_completo,
a.departamento,
SUM(v.VentasBrutas) AS total_ventas_usd,
SUM(v.VentasBrutas * 17.15) AS total_ventas_mxn
FROM Asociados a -- ✓ nombre de tabla correcto
JOIN RegistrosVentas v
ON a.asociado_id = v.asociado_id
WHERE
a.Status = 'A' -- ✓ regla solo-activos aplicada
AND v.fecha_venta >= '2025-10-01'
AND v.fecha_venta < '2026-01-01'
GROUP BY a.asociado_id, a.nombre_completo, a.departamento
ORDER BY total_ventas_usd DESC -- ✓ preferencia del usuario
Ambas consultas fueron generadas por el mismo modelo de IA, con la misma pregunta del usuario. La única diferencia es el pipeline de contexto.
Este Patrón Es Más Grande que el SQL
Implementamos The Intelligence Handshake para un sistema de consultas de base de datos, pero el patrón es universal. Cualquier servidor MCP que opere en un dominio específico — y casi todos los útiles lo hacen — se beneficia de la misma arquitectura de contexto bidireccional:
Agentes CRM / ERP
Dominio: "Las oportunidades 'cerradas-ganadas' en Salesforce están como status 7 en el ERP legado. Nunca mostrarlas como registros separados."
Agentes de Análisis Financiero
Dominio: "El EBITDA aquí incluye depreciación de equipos pero no amortización de patentes. El CFO lo definió así en 2019."
Agentes DevOps / Infraestructura
Dominio: "prod-db-03 es la primaria. prod-db-04 es la réplica para reportes. Nunca ejecutar DELETE en réplicas de reportes."
The Intelligence Handshake
Los agentes de IA son tan inteligentes como el contexto que llevan. La pregunta nunca fue si el AI es suficientemente inteligente — lo es. La pregunta es si construiste la arquitectura que hace que esa inteligencia cuente.
The Intelligence Handshake es esa arquitectura. No es una configuración. No es un prompt. Es un protocolo — un intercambio deliberado y bidireccional de conocimiento entre el AI y tu sistema, diseñado para que cada consulta comience informada en lugar de desde cero.