Agent Lab MCP Server
Agent Lab
Aplicación educativa para inspeccionar el flujo técnico de agentes de IA sobre datos empresariales. El proyecto muestra contratos, protocolos, llamadas a herramientas, resultados estructurados y trazas sanitizadas.
Estado
Etapas 1 a 11 — Fundación, MCP, Agent Runtime, Auth/RBAC, A2A, evaluaciones, cloud, CI/CD, contratos deterministas, resiliencia y robustez semántica:
frontend React + Vite;
backend Node.js + Express;
esquema y migraciones PostgreSQL;
usuarios de aplicación
adminyviewer;períodos de calendario deterministas;
contrato técnico
TraceEvent;tests unitarios y shell visual del Agent Lab;
servidor MCP oficial sobre transporte
stdio;siete herramientas MCP de solo lectura con respuestas estructuradas;
consultas PostgreSQL parametrizadas mediante un rol de mínimo privilegio.
Ollama con
qwen3:8bpara inferencia local y tool calling sin costo por token;OpenAI Responses API conservada como proveedor opcional;
MCP Client local con descubrimiento y ejecución de herramientas;
orquestador grounded y endpoint
POST /api/agent/query;interfaz de consulta con respuesta y traza técnica real.
autenticación con sesiones opacas persistidas en PostgreSQL;
cookie
HttpOnly,SameSite=Stricty expiración configurable;autorización RBAC con perfiles
adminyviewer;alta auditada de usuarios y borrado transaccional de datos de RR. HH.
dos agentes publicando Agent Cards A2A 1.0;
delegación HR → Finanzas mediante JSON-RPC
SendMessage;tarea financiera con ciclo de vida y Artifact estructurado;
reporte de pérdidas por ausencias consultadas mediante MCP.
suite de evaluaciones de comportamiento con assertions deterministas;
casos de referencia, resultado vacío y frescura de PostgreSQL;
fixture dinámica aislada con limpieza garantizada y verificación residual.
timeout presupuestado, retry transitorio acotado y circuit breaker por instancia caliente;
degradación segura que conserva el
answerPayloadgrounded si falla sólo la narrativa;evaluación de resiliencia con inyección controlada de fallos.
interpretación de español neutro, informal y rioplatense mediante propuesta semántica del LLM;
validación backend de capability, schema, período, polaridad y límites antes de MCP;
decisiones tipadas de aclaración y consulta no soportada sin acceso a PostgreSQL;
benchmark lingüístico versionado con baseline before/after y estabilidad entre ejecuciones repetidas.
La aplicación está desplegada con frontend estático en Render, backend serverless en Vercel y PostgreSQL en Neon. GitHub Actions aplica quality gates y smoke tests contra producción.
Related MCP server: Employee Management MCP Server
Estructura
frontend/ React, inspector técnico y system index
backend/ API, dominio, migraciones, acceso PostgreSQL y trazasRequisitos
Node.js 22 o superior.
npm 10 o superior.
Ollama 0.32 o superior y el modelo local
qwen3:8b.PostgreSQL cloud con tres credenciales separadas cuando el proveedor lo permita.
Instalación
npm --prefix backend install
npm --prefix frontend installCopiar backend/.env.example a backend/.env y completar las URLs del proveedor PostgreSQL. Nunca utilizar la credencial propietaria en DATABASE_READONLY_URL o DATABASE_ADMIN_URL.
El proveedor predeterminado es Ollama local:
LLM_PROVIDER=ollama
OLLAMA_HOST=http://127.0.0.1:11434
OLLAMA_MODEL=qwen3:8bInstalar Ollama y descargar el modelo una vez con ollama pull qwen3:8b. La inferencia usa CPU/GPU y almacenamiento locales, sin consumo de una API paga.
OpenAI continúa disponible como alternativa configurando LLM_PROVIDER=openai, OPENAI_API_KEY y OPENAI_MODEL. La clave sólo pertenece a backend/.env, que está ignorado por Git. Nunca debe enviarse al frontend ni incluirse en trazas.
Base de datos
Crear la base PostgreSQL en el proveedor cloud.
Crear o configurar los roles siguiendo
backend/ops/database-roles.example.sql.Configurar las variables de entorno.
Ejecutar:
npm --prefix backend run db:migrate
npm --prefix backend run db:seed
npm --prefix backend run db:smoke
npm --prefix backend run db:verify-permissionsdb:smoke usa exclusivamente DATABASE_READONLY_URL y consulta la vista hr_late_arrivals con parámetros de fecha.
El seed exige SEED_ADMIN_PASSWORD y SEED_VIEWER_PASSWORD, ambas con al menos 12 caracteres. No existen contraseñas predeterminadas en el repositorio.
Desarrollo
En dos terminales:
npm run dev:backend
npm run dev:frontendFrontend:
http://localhost:5173Backend:
http://localhost:3000
Verificación sin base cloud
npm test
npm run typecheck
npm run buildLos tests de calendario verifican:
mes corriente desde el día 1 hasta hoy inclusive;
mes calendario anterior completo;
febrero de año bisiesto;
últimos 30 días de calendario.
Intervalos temporales
La interfaz habla de fechas inclusivas, pero internamente se utilizan intervalos semiabiertos:
startInclusive <= timestamp < endExclusiveEsto evita depender de 23:59:59 y conserva correctamente la precisión de PostgreSQL.
Trazabilidad
El frontend muestra eventos técnicos con:
nombre del evento;
tecnología;
componente;
categoría;
conceptos;
input y output sanitizados;
duración y estado.
No se mostrarán credenciales, tokens de sesión ni razonamiento interno del modelo.
MCP Server
El servidor utiliza el SDK oficial de Model Context Protocol para exponer:
count_employees: cuenta empleados totales, activos e inactivos;list_employees: lista el directorio completo con legajo, nombre, departamento y estado;find_employee: busca por nombre o número de empleado;summarize_employee_delays: agrega las tardanzas históricas de una persona por nombre o legajo;list_late_arrivals: lista llegadas tarde por período y empleado opcional;list_employees_without_late_arrivals: calcula en PostgreSQL qué empleados activos no tuvieron llegadas tarde durante el período;list_absences: lista ausencias por período y empleado opcional.
Las herramientas declaran readOnlyHint, validan entrada y salida con Zod y devuelven tanto contenido textual como structuredContent. Los resultados incluyen fuente, fecha de consulta, período aplicado, cantidad total y señal de truncamiento. Cada llamada consulta PostgreSQL nuevamente; no hay caché en esta etapa.
Para iniciar el servidor MCP local:
npm run mcp:serverPara verificar descubrimiento, llamadas reales, datos sembrados y resultado vacío:
npm run mcp:smokestdout queda reservado al protocolo MCP; los errores operativos se envían a stderr y la respuesta al cliente se sanitiza.
Agent Runtime
Flujo implementado:
React → POST /api/agent/query → HrAgentOrchestrator
→ Ollama local + qwen3:8b (tool calling)
→ MCP Client → MCP Server → PostgreSQL
→ tool result → Ollama → respuesta + TraceEvent[]El MCP Client descubre las herramientas disponibles, pero el router entrega al modelo una única definición de la allowlist controlada por ejecución. Los esquemas de function calling son estrictos y las llamadas paralelas están desactivadas para que cada ejecución sea simple de inspeccionar.
grounded: true significa que el orquestador verificó una llamada a una herramienta aprobada y recibió structuredContent antes de solicitar la respuesta final. No significa que exista una garantía matemática sobre cada token producido por el modelo; esa calidad debe medirse con evaluaciones.
El system prompt exige que los datos empresariales provengan exclusivamente de las herramientas, que los resultados vacíos se informen explícitamente y que el contenido recibido sea tratado como datos, no como instrucciones.
Prueba de integración determinista, sin consumir API:
npm run agent:smokePrueba real con Ollama local, MCP y Neon:
npm run agent:smoke:ollamaPrueba real con Groq, MCP y Neon:
npm run agent:smoke:groqPrueba opcional con OpenAI, MCP y Neon:
npm run agent:smoke:openaiEndpoint:
POST /api/agent/query
Content-Type: application/json
{"question":"¿Qué empleados llegaron tarde durante el último mes?"}La respuesta contiene answer, model, grounded, toolsUsed y una secuencia de eventos técnicos sanitizados. No incluye tokens, credenciales ni razonamiento interno.
Robustez semántica, routing validado y presentación determinista
El LLM recibe las siete capabilities controladas y propone exactamente una decisión. El backend no confía en esa propuesta: valida allowlist, schema Zod, período expresado por el usuario, polaridad y límites de negocio antes de permitir una llamada MCP:
LLM propone → backend valida → MCP ejecuta → PostgreSQL → payload deterministaCapacidad | Herramienta |
contar empleados |
|
listar el directorio |
|
buscar una persona |
|
resumir demoras históricas |
|
consultar llegadas tarde por período |
|
consultar quién no tuvo llegadas tarde |
|
consultar ausencias por período |
|
Además de las siete tools MCP, la planificación dispone de dos decisiones internas que nunca llegan a MCP: request_clarification y reject_unsupported_query. La primera devuelve agent_clarification_required cuando falta un período o existe ambigüedad; la segunda devuelve unsupported_agent_query cuando el pedido exige una capability, ranking, frecuencia o filtro inexistente. El catálogo público se consulta en GET /api/agent/capabilities y también aparece en el System index.
Las expresiones informales se interpretan por significado. Por ejemplo, llegar, entrar, caer, fichar o marcar tarde pueden referirse a late_arrivals; “sin tardanzas” y “siempre puntual” se interpretan como cero eventos sólo dentro de un período explícito. Expresiones como “banda”, “una bocha”, “siempre” o “seguido” nunca se convierten en cantidades inventadas.
Después de ejecutar MCP, AnswerPresentation valida el structuredContent mediante una unión discriminada de Zod. La API devuelve dos superficies separadas:
presentation:answerPayloadtipado, determinista y renderizado por un componente React específico;answer: narrativa grounded generada por el LLM, visible en un panel secundario identificado como no determinista.
Las cantidades, tablas, fechas, estados vacíos y metadatos de fuente se muestran desde presentation; no se extraen del texto del modelo. La Etapa 11 no modifica este contrato de la Etapa 9. La traza incorpora llm.semantic_proposal.completed, agent.semantic_decision.validated y presentation.payload.validated para separar propuesta, validación y representación determinista.
La consulta negada se implementa como diferencia de conjuntos: empleados activos menos empleados con al menos una llegada tarde dentro del período. PostgreSQL ejecuta esa semántica mediante NOT EXISTS; el LLM no calcula el complemento. Si Groq devuelve una respuesta final vacía o intenta una segunda tool call durante la finalización, el adaptador realiza un único reintento textual con los mismos datos grounded. El evento llm.grounded_response.completed informa recovery=not_required, el tipo de retry aplicado o el fallback a la presentación determinista.
Resiliencia del proveedor LLM
La Etapa 10 contiene fallos externos mediante cuatro mecanismos explícitos:
Técnica | Política de demostración | Resultado |
timeout budget | 12 segundos por intento | aborta una llamada que excede el presupuesto |
bounded retry | 1 retry transitorio | reintenta |
circuit breaker | abre con 3 fallos; prueba half-open a los 30 segundos | evita insistir contra un proveedor que permanece caído |
graceful degradation | sólo después de una consulta MCP correcta | conserva |
El endpoint público GET /api/resilience expone la política y el estado sanitizado del circuito, nunca credenciales. El agente se reutiliza dentro de cada instancia caliente para que el circuit breaker conserve estado entre requests. En Vercel cada instancia posee su propio circuito; coordinarlo globalmente exigiría un store distribuido y no se justifica para este laboratorio.
Si falla la planificación inicial, todavía no existe una llamada MCP ni datos grounded y la API devuelve un error tipado (llm_timeout, llm_rate_limited, llm_provider_unavailable o llm_circuit_open). Si falla únicamente la redacción final, la API responde exitosamente con la tabla determinista y emite llm.grounded_response.degraded.
Autenticación y autorización
Las credenciales se validan contra hashes bcrypt en app_users. Al autenticar, el backend crea un token aleatorio, guarda únicamente su hash SHA-256 en app_sessions y entrega el token mediante una cookie HttpOnly. El frontend nunca accede al token.
La duración se configura con SESSION_TTL_HOURS=8.
Permisos de aplicación:
viewer: puede consultar al agente y ver el índice técnico;admin: incluye las capacidades de consulta, alta de usuarios y borrado controlado de datos operativos.
El borrado administrativo no ejecuta DROP DATABASE. Elimina attendance_records, employees y departments dentro de una única transacción; conserva esquema, usuarios, sesiones y audit_events. Requiere la confirmación literal DELETE HR DATA y registra el resultado en auditoría.
El rol PostgreSQL app_admin no tiene DROP, CREATE DATABASE, superusuario ni membresía neon_superuser. Esta separación demuestra que RBAC de aplicación y privilegios de base de datos son capas distintas.
Prueba real de ambos usuarios y del ciclo completo de sesión:
npm run auth:smokeAgentes y A2A
El proyecto implementa A2A Protocol 1.0 con el SDK oficial @a2a-js/sdk:
HR Grounding Agent: consultas de empleados y asistencia grounded mediante MCP;Absence Finance Agent: análisis económico determinista de ausencias.
Agent Cards:
/.well-known/agent-card.json
/.well-known/hr-agent-card.json
/.well-known/finance-agent-card.jsonFlujo financiero:
Usuario → HR Agent / A2A Client
→ descubre Finance Agent Card
→ JSON-RPC SendMessage
→ Finance Agent Task: submitted → working
→ MCP list_absences → PostgreSQL
→ calculadora determinista
→ A2A Artifact application/json
→ Task completed → reporte + TraceEvent[]Los endpoints A2A usan un bearer token interno aleatorio. La Agent Card describe el esquema de seguridad pero nunca contiene la credencial.
La base no contiene salarios. Por eso el reporte exige parámetros explícitos: moneda, costo diario, prima de reemplazo e impacto de productividad. La fórmula es:
días × costo diario × (1 + prima de reemplazo + impacto de productividad)El LLM no realiza la aritmética. Una función TypeScript determinista calcula importes redondeados a dos decimales. Si MCP indica que el resultado fue truncado, el agente rechaza el cálculo para evitar un reporte incompleto.
La implementación utiliza tareas A2A en memoria porque el flujo es breve y síncrono. Para múltiples instancias o tareas largas, el TaskStore deberá migrarse a almacenamiento persistente.
Prueba real de Agent Card, A2A, MCP y Neon:
npm run a2a:smokeEvaluaciones del agente
Los tests unitarios validan funciones y contratos con dependencias controladas. La suite de evaluaciones mide el comportamiento completo del agente real con el modelo configurado, MCP y PostgreSQL.
Casos implementados:
employee-count: comprueba que una pregunta de cantidad se enruta exclusivamente acount_employees;employee-directory: comprueba que una solicitud de nombres se enruta alist_employeesy recupera el directorio;employee-delay-summary: comprueba la agregación determinista de demoras de Bruno Silva mediantesummarize_employee_delays;employees-without-late-arrivals: comprueba routing de negación, diferencia de conjuntos y el resultado esperadoEMP-003;known-late-arrivals: compara herramienta, grounding y cantidad contra el dataset sembrado;unknown-employee: exige resultado PostgreSQL vacío y una respuesta explícita sin datos inventados;source-of-truth-freshness: inserta un empleado temporal único y una llegada tarde, consulta el registro recién creado y comprueba que el agente observa la actualización.finalization-failure-degradation: inyecta un fallo controlado después de MCP y comprueba que el payload PostgreSQL continúa disponible.semantic-robustness-v1: ejecuta 80 formulaciones neutrales, formales, informales, rioplatenses y de frontera; mide intención, decisión, argumentos, temporalidad, ambigüedad y estabilidad.
La fixture dinámica usa el rol administrativo sólo durante la preparación y limpieza. La consulta del agente continúa usando el rol read-only. Un bloque finally elimina por UUID y número de empleado exactos; al finalizar, una consulta adicional exige que no existan empleados EVAL-% ni asistencias con fuente agent-evaluation.
Ejecución real con el proveedor LLM configurado, MCP y Neon:
npm run evals:run
npm run resilience:eval
npm run semantic:eval
npm run semantic:stabilitysemantic:eval recorre una vez los 80 casos y semantic:stability repite cinco veces el conjunto crítico. Ambos informan validDecisionRate, intentRecognitionRate, toolSelectionRate, argumentExtractionRate, temporalInterpretationRate, exactOutcomeRate, stabilityRate, ambiguityPassRate y unsupportedPassRate. Por defecto esperan 30 segundos entre llamadas para respetar el presupuesto gratuito de tokens de Groq y separar límites del proveedor de inestabilidad semántica. El baseline Stage 10 se conserva en backend/evals/baselines/ y los resultados Stage 11 en backend/evals/results/.
Los demás comandos devuelven JSON reproducible con passRate, duración, checks esperados/reales y evidencia grounded por caso. Finalizan con código distinto de cero si falla una evaluación o si queda alguna fixture temporal. El caso de referencia presupone que el seed de demostración está presente.
Deployment cloud
El repositorio conserva frontend/ y backend/ separados, con dos superficies de despliegue:
agent-lab-ignac: frontend Vite como Render Static Site;agent-lab-api-ignac: backend Express como una Vercel Function con Fluid Compute.
URLs de producción:
aplicación:
https://agent-lab-ignac.onrender.com;API:
https://agent-lab-api-ignac.vercel.app;health check directo:
https://agent-lab-api-ignac.vercel.app/api/health.
render.yaml sólo administra el frontend y reescribe /api/* hacia https://agent-lab-api-ignac.vercel.app. Para el navegador, autenticación y cookies continúan bajo el origen del frontend; el token de sesión permanece HttpOnly y no se expone a React.
backend/vercel.json declara Express, un máximo de 300 segundos y la región gru1 (São Paulo), cercana a la base Neon. Vercel detecta el handler lazy exportado por src/app.ts; la aplicación y sus pools se inicializan al recibir la primera request de una instancia. src/server.ts conserva app.listen() para desarrollo local.
El transporte MCP se selecciona mediante MCP_TRANSPORT:
stdio: desarrollo local; el cliente inicia un proceso MCP independiente;in_process: Vercel; cliente y servidor MCP se conectan con un par de transportes en memoria, sin perder el protocolo, contratos, validación ni tool discovery.
En desarrollo local, LLM_PROVIDER=ollama conserva qwen3:8b. En Vercel, LLM_PROVIDER=groq usa openai/gpt-oss-20b, que soporta function calling. El adaptador Groq fuerza al menos una herramienta y devuelve su resultado al modelo para producir la respuesta grounded.
Variables de producción requeridas en el proyecto Vercel:
NODE_ENV=production
FRONTEND_ORIGIN=https://agent-lab-ignac.onrender.com
APP_TIMEZONE=America/Argentina/Buenos_Aires
SESSION_TTL_HOURS=8
PUBLIC_BASE_URL=https://agent-lab-api-ignac.vercel.app
MCP_TRANSPORT=in_process
LLM_PROVIDER=groq
GROQ_MODEL=openai/gpt-oss-20b
GROQ_API_KEY=<secret>
LLM_TIMEOUT_MS=12000
LLM_TRANSIENT_RETRIES=1
LLM_CIRCUIT_FAILURE_THRESHOLD=3
LLM_CIRCUIT_RESET_MS=30000
DATABASE_READONLY_URL=<secret>
DATABASE_ADMIN_URL=<secret>
A2A_INTERNAL_TOKEN=<secret-aleatorio-de-32-o-mas-caracteres>El backend público añade headers con Helmet, rate limits, manejo explícito de errores y GET /api/health. Los límites en memoria son demostrativos y operan por instancia caliente; una aplicación productiva distribuida usaría un store compartido. PostgreSQL conserva usuarios, sesiones y datos, por lo que el filesystem serverless permanece descartable.
CI/CD y quality gates
Cada push a main y cada pull request ejecutan .github/workflows/ci.yml. Backend y frontend se validan en jobs independientes y reproducibles sobre Node.js 22:
checkout → npm ci → typecheck → build → tests → audit de dependencias productivasnpm ci instala exactamente el árbol fijado por cada package-lock.json. Los jobs sólo poseen permiso de lectura del repositorio, tienen timeout y cancelan ejecuciones anteriores de la misma rama. Ninguna credencial de producción se entrega al workflow de CI.
Vercel está conectado al repositorio con backend/ como Root Directory; un commit aceptado en main genera el despliegue serverless. Render mantiene el frontend estático desde frontend/. Esta separación distingue dos controles:
quality gate previo al runtime: tipos, compilación, tests y auditoría;
smoke test posterior al deployment: contrato HTTP público realmente desplegado.
.github/workflows/production-smoke.yml escucha estados exitosos de deployment y también permite ejecución manual. scripts/production-smoke.mjs comprueba:
health directo del backend Vercel;
contrato de
/api/systemy etapa vigente;contrato público de
/api/resilience;proxy
/api/*servido bajo el origen Render;disponibilidad del documento HTML del frontend.
Ejecución local del mismo contrato de producción:
node scripts/production-smoke.mjsThis server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables interaction with a PostgreSQL database through MCP tools for employee management. Supports listing and adding employees via natural language chat interface with LLM integration.
- AlicenseNot gradedqualityDmaintenanceEnables managing employee records by providing tools to list directories, retrieve detailed profiles, and search for staff by department. It integrates with Claude Desktop to allow users to interact with employee data through natural language commands.MIT
- AlicenseBqualityCmaintenanceEnables Claude, Cursor, and other MCP clients to query PeopleForce HRIS data (employees, time-off, recruitment) via 27 read-only tools.283MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to query a PostgreSQL database through a small set of controlled, read-only tools for schema inspection, row lookup, and aggregate statistics.1MIT
Related MCP Connectors
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.
See, price, and control every tool call your AI agents make: policy checks, cost, and audit tools.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/TatooCollado/agent-lab'
If you have feedback or need assistance with the MCP directory API, please join our Discord server