Skip to main content
Glama
TatooCollado

Agent Lab MCP Server

by TatooCollado

Agent Lab

CI Production Smoke

Образовательное приложение для изучения технического потока ИИ-агентов, работающих с корпоративными данными. Проект демонстрирует контракты, протоколы, вызовы инструментов, структурированные результаты и санитизированные трассы.

Статус

Этапы 1–11 — Фундамент, MCP, Agent Runtime, Auth/RBAC, A2A, оценки, облако, CI/CD, детерминированные контракты, отказоустойчивость и семантическая устойчивость:

  • фронтенд React + Vite;

  • бэкенд Node.js + Express;

  • схема и миграции PostgreSQL;

  • пользователи приложения admin и viewer;

  • детерминированные календарные периоды;

  • технический контракт TraceEvent;

  • модульные тесты и визуальная оболочка Agent Lab;

  • официальный MCP-сервер поверх транспорта stdio;

  • семь MCP-инструментов только для чтения со структурированными ответами;

  • параметризованные PostgreSQL-запросы через роль с минимальными привилегиями.

  • Ollama с qwen3:8b для локального вывода и tool calling без затрат на токены;

  • OpenAI Responses API сохранён как опциональный провайдер;

  • локальный MCP Client с обнаружением и выполнением инструментов;

  • grounded-оркестратор и конечная точка POST /api/agent/query;

  • интерфейс запросов с ответом и реальной технической трассой.

  • аутентификация с непрозрачными сессиями, хранящимися в PostgreSQL;

  • cookie HttpOnly, SameSite=Strict и настраиваемый срок действия;

  • авторизация RBAC с профилями admin и viewer;

  • аудируемое создание пользователей и транзакционное удаление данных HR;

  • два агента, публикующие Agent Cards A2A 1.0;

  • делегирование HR → Finance через JSON-RPC SendMessage;

  • финансовая задача с жизненным циклом и структурированным Artifact;

  • отчёт о потерях из-за отсутствий, полученный через MCP.

  • набор поведенческих оценок с детерминированными assertions;

  • эталонные случаи, пустой результат и свежесть PostgreSQL;

  • изолированная динамическая фикстура с гарантированной очисткой и проверкой остатков.

  • бюджетный timeout, ограниченный транзиентный retry и circuit breaker для горячего экземпляра;

  • безопасная деградация, сохраняющая grounded answerPayload, если падает только нарратив;

  • оценка отказоустойчивости с контролируемой инъекцией сбоев.

  • интерпретация нейтрального, неформального и риоплатского испанского через семантическое предложение LLM;

  • серверная валидация capability, схемы, периода, полярности и лимитов до MCP;

  • типизированные решения о уточнении и неподдерживаемом запросе без доступа к PostgreSQL;

  • версионированный лингвистический бенчмарк с baseline before/after и стабильностью между повторными запусками.

Приложение развёрнуто со статическим фронтендом на Render, serverless-бэкендом на Vercel и PostgreSQL на Neon. GitHub Actions применяет quality gates и smoke-тесты против продакшена.

Related MCP server: mcp-data-gateway

Структура

frontend/   React, inspector técnico y system index
backend/    API, dominio, migraciones, acceso PostgreSQL y trazas

Требования

  • Node.js 22 или выше.

  • npm 10 или выше.

  • Ollama 0.32 или выше и локальная модель qwen3:8b.

  • Облачный PostgreSQL с тремя отдельными учётными данными, если провайдер это позволяет.

Установка

npm --prefix backend install
npm --prefix frontend install

Скопируйте backend/.env.example в backend/.env и заполните URL-адреса провайдера PostgreSQL. Никогда не используйте владельческие учётные данные в DATABASE_READONLY_URL или DATABASE_ADMIN_URL.

Провайдер по умолчанию — локальный Ollama:

LLM_PROVIDER=ollama
OLLAMA_HOST=http://127.0.0.1:11434
OLLAMA_MODEL=qwen3:8b

Установите Ollama и один раз загрузите модель командой ollama pull qwen3:8b. Вывод использует локальные CPU/GPU и хранилище, без затрат на платный API.

OpenAI по-прежнему доступен как альтернатива: настройте LLM_PROVIDER=openai, OPENAI_API_KEY и OPENAI_MODEL. Ключ принадлежит только backend/.env, который игнорируется Git. Его никогда нельзя отправлять на фронтенд или включать в трассы.

База данных

  1. Создайте базу PostgreSQL у облачного провайдера.

  2. Создайте или настройте роли по backend/ops/database-roles.example.sql.

  3. Настройте переменные окружения.

  4. Выполните:

npm --prefix backend run db:migrate
npm --prefix backend run db:seed
npm --prefix backend run db:smoke
npm --prefix backend run db:verify-permissions

db:smoke использует исключительно DATABASE_READONLY_URL и запрашивает представление hr_late_arrivals с параметрами дат.

Seed требует SEED_ADMIN_PASSWORD и SEED_VIEWER_PASSWORD, обе — не менее 12 символов. В репозитории нет паролей по умолчанию.

Разработка

В двух терминалах:

npm run dev:backend
npm run dev:frontend
  • Фронтенд: http://localhost:5173

  • Бэкенд: http://localhost:3000

Проверка без облачной базы

npm test
npm run typecheck
npm run build

Тесты календаря проверяют:

  • текущий месяц с 1-го числа по сегодняшний день включительно;

  • полный предыдущий календарный месяц;

  • февраль високосного года;

  • последние 30 календарных дней.

Временные интервалы

Интерфейс говорит об инклюзивных датах, но внутри используются полуоткрытые интервалы:

startInclusive <= timestamp < endExclusive

Это позволяет не зависеть от 23:59:59 и корректно сохраняет точность PostgreSQL.

Трассируемость

Фронтенд показывает технические события с:

  • названием события;

  • технологией;

  • компонентом;

  • категорией;

  • концепциями;

  • санитизированными входом и выходом;

  • длительностью и статусом.

Учётные данные, токены сессий и внутренние рассуждения модели не отображаются.

MCP Server

Сервер использует официальный SDK Model Context Protocol для предоставления:

  • count_employees: подсчитывает общее количество сотрудников, активных и неактивных;

  • list_employees: выводит полный справочник с табельным номером, именем, отделом и статусом;

  • find_employee: ищет по имени или номеру сотрудника;

  • summarize_employee_delays: агрегирует исторические опоздания человека по имени или табельному номеру;

  • list_late_arrivals: выводит опоздания за период и по опциональному сотруднику;

  • list_employees_without_late_arrivals: вычисляет в PostgreSQL, какие активные сотрудники не имели опозданий за период;

  • list_absences: выводит отсутствия за период и по опциональному сотруднику.

Инструменты объявляют readOnlyHint, валидируют вход и выход с помощью Zod и возвращают как текстовое содержимое, так и structuredContent. Результаты включают источник, дату запроса, применённый период, общее количество и признак усечения. Каждый вызов заново обращается к PostgreSQL; на этом этапе кэша нет.

Для запуска локального MCP-сервера:

npm run mcp:server

Для проверки обнаружения, реальных вызовов, посеянных данных и пустого результата:

npm run mcp:smoke

stdout зарезервирован под протокол MCP; операционные ошибки отправляются в stderr, а ответ клиенту санитизируется.

Agent Runtime

Реализованный поток:

React → POST /api/agent/query → HrAgentOrchestrator
      → Ollama local + qwen3:8b (tool calling)
      → MCP Client → MCP Server → PostgreSQL
      → tool result → Ollama → respuesta + TraceEvent[]

MCP Client обнаруживает доступные инструменты, но роутер передаёт модели единственное определение allowlist, управляемой исполнением. Схемы function calling строгие, а параллельные вызовы отключены, чтобы каждое выполнение было легко инспектировать.

grounded: true означает, что оркестратор проверил вызов одобренного инструмента и получил structuredContent перед запросом финального ответа. Это не означает математической гарантии по каждому токену, произведённому моделью; это качество должно измеряться оценками.

System prompt требует, чтобы корпоративные данные поступали исключительно из инструментов, чтобы пустые результаты сообщались явно, а полученное содержимое трактовалось как данные, а не как инструкции.

Детерминированный интеграционный тест без потребления API:

npm run agent:smoke

Реальный тест с локальным Ollama, MCP и Neon:

npm run agent:smoke:ollama

Реальный тест с Groq, MCP и Neon:

npm run agent:smoke:groq

Опциональный тест с OpenAI, MCP и Neon:

npm run agent:smoke:openai

Конечная точка:

POST /api/agent/query
Content-Type: application/json

{"question":"¿Qué empleados llegaron tarde durante el último mes?"}

Ответ содержит answer, model, grounded, toolsUsed и последовательность санитизированных технических событий. Он не включает токены, учётные данные или внутренние рассуждения.

Семантическая устойчивость, валидированная маршрутизация и детерминированное представление

LLM получает семь управляемых capabilities и предлагает ровно одно решение. Бэкенд не доверяет этому предложению: он валидирует allowlist, схему Zod, выраженный пользователем период, полярность и бизнес-лимиты перед разрешением MCP-вызова:

LLM propone → backend valida → MCP ejecuta → PostgreSQL → payload determinista

Capability

Инструмент

подсчёт сотрудников

count_employees

вывод справочника

list_employees

поиск человека

find_employee

сводка исторических опозданий

summarize_employee_delays

запрос опозданий за период

list_late_arrivals

запрос тех, у кого не было опозданий

list_employees_without_late_arrivals

запрос отсутствий за период

list_absences

Помимо семи MCP-инструментов, планировщик имеет два внутренних решения, которые никогда не доходят до MCP: request_clarification и reject_unsupported_query. Первое возвращает agent_clarification_required, когда отсутствует период или есть неоднозначность; второе возвращает unsupported_agent_query, когда запрос требует несуществующей capability, ранжирования, частоты или фильтра. Публичный каталог доступен через GET /api/agent/capabilities и также присутствует в System index.

Неформальные выражения интерпретируются по смыслу. Например, llegar, entrar, caer, fichar или marcar tarde могут относиться к late_arrivals; «sin tardanzas» и «siempre puntual» интерпретируются как ноль событий только в пределах явного периода. Выражения вроде «banda», «una bocha», «siempre» или «seguido» никогда не превращаются в выдуманные количества.

После выполнения MCP AnswerPresentation валидирует structuredContent через дискриминируемое объединение Zod. API возвращает две отдельные поверхности:

  • presentation: типизированный, детерминированный answerPayload, отображаемый конкретным React-компонентом;

  • answer: grounded-нарратив, сгенерированный LLM, видимый на вторичной панели, помеченной как недетерминированный.

Количества, таблицы, даты, пустые состояния и метаданные источника отображаются из presentation; они не извлекаются из текста модели. Этап 11 не изменяет этот контракт Этапа 9. Трасса включает llm.semantic_proposal.completed, agent.semantic_decision.validated и presentation.payload.validated для разделения предложения, валидации и детерминированного представления.

Отрицательный запрос реализуется как разность множеств: активные сотрудники минус сотрудники хотя бы с одним опозданием в пределах периода. PostgreSQL выполняет эту семантику через NOT EXISTS; LLM не вычисляет дополнение. Если Groq возвращает пустой финальный ответ или пытается выполнить второй tool call во время завершения, адаптер выполняет единственный текстовый повтор с теми же grounded-данными. Событие llm.grounded_response.completed сообщает recovery=not_required, тип применённого retry или откат к детерминированному представлению.

Отказоустойчивость LLM-провайдера

Этап 10 обрабатывает внешние сбои с помощью четырёх явных механизмов:

Техника

Политика демонстрации

Результат

timeout budget

12 секунд на попытку

прерывает вызов, превышающий бюджет

bounded retry

1 транзиентный повтор

повторяет 429, таймаут, сеть или 5xx; не повторяет функциональные ошибки

circuit breaker

размыкается при 3 сбоях; пробный half-open через 30 секунд

позволяет не давить на провайдера, который остаётся недоступным

graceful degradation

только после успешного MCP-запроса

сохраняет presentation grounded, даже если LLM-нарратив отсутствует

Публичный эндпоинт GET /api/resilience раскрывает политику и санитизированное состояние контура, но никогда — учётные данные. Агент переиспользуется внутри каждого горячего инстанса, чтобы circuit breaker сохранял состояние между запросами. В Vercel каждый инстанс имеет собственный контур; глобальная координация потребовала бы распределённого хранилища и не оправдана для этой лаборатории.

Если падает начальное планирование, MCP-вызова и grounded-данных ещё нет, и API возвращает типизированную ошибку (llm_timeout, llm_rate_limited, llm_provider_unavailable или llm_circuit_open). Если падает только финальная формулировка, API отвечает успешно с детерминированной таблицей и выдаёт llm.grounded_response.degraded.

Аутентификация и авторизация

Учётные данные проверяются по bcrypt-хешам в app_users. При аутентификации бэкенд создаёт случайный токен, сохраняет только его SHA-256-хеш в app_sessions и передаёт токен через cookie HttpOnly. Фронтенд никогда не обращается к токену.

Длительность настраивается через SESSION_TTL_HOURS=8.

Права приложения:

  • viewer: может обращаться к агенту и просматривать технический индекс;

  • admin: включает возможности запросов, создание пользователей и контролируемое удаление операционных данных.

Административное удаление не выполняет DROP DATABASE. Оно удаляет attendance_records, employees и departments в рамках одной транзакции; сохраняет схему, пользователей, сессии и audit_events. Требуется буквальное подтверждение DELETE HR DATA, результат фиксируется в аудите.

Роль PostgreSQL app_admin не имеет DROP, CREATE DATABASE, суперпользователя или членства neon_superuser. Это разделение демонстрирует, что RBAC приложения и привилегии базы данных — разные слои.

Реальный тест обоих пользователей и полного цикла сессии:

npm run auth:smoke

Агенты и A2A

Проект реализует A2A Protocol 1.0 с официальным SDK @a2a-js/sdk:

  • HR Grounding Agent: запросы сотрудников и посещаемости, grounded через MCP;

  • Absence Finance Agent: детерминированный экономический анализ отсутствий.

Agent Cards:

/.well-known/agent-card.json
/.well-known/hr-agent-card.json
/.well-known/finance-agent-card.json

Финансовый поток:

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[]

Эндпоинты A2A используют внутренний случайный bearer-токен. Agent Card описывает схему безопасности, но никогда не содержит учётных данных.

В базе нет зарплат. Поэтому отчёт требует явных параметров: валюта, дневная стоимость, премия за замену и влияние на производительность. Формула:

días × costo diario × (1 + prima de reemplazo + impacto de productividad)

LLM не выполняет арифметику. Детерминированная TypeScript-функция вычисляет суммы с округлением до двух знаков. Если MCP сообщает, что результат был усечён, агент отклоняет расчёт, чтобы не выдать неполный отчёт.

Реализация использует A2A-задачи в памяти, потому что поток короткий и синхронный. Для нескольких инстансов или длинных задач TaskStore придётся перенести в постоянное хранилище.

Реальный тест Agent Card, A2A, MCP и Neon:

npm run a2a:smoke

Оценки агента

Юнит-тесты проверяют функции и контракты с контролируемыми зависимостями. Набор оценок измеряет полное поведение реального агента с настроенной моделью, MCP и PostgreSQL.

Реализованные случаи:

  • employee-count: проверяет, что вопрос о количестве направляется исключительно в count_employees;

  • employee-directory: проверяет, что запрос имён направляется в list_employees и получает справочник;

  • employee-delay-summary: проверяет детерминированную агрегацию задержек Bruno Silva через summarize_employee_delays;

  • employees-without-late-arrivals: проверяет маршрутизацию отрицания, разность множеств и ожидаемый результат EMP-003;

  • known-late-arrivals: сравнивает инструмент, grounding и количество с посеянным набором данных;

  • unknown-employee: требует пустого результата PostgreSQL и явного ответа без выдуманных данных;

  • source-of-truth-freshness: вставляет временного уникального сотрудника и опоздание, запрашивает только что созданную запись и проверяет, что агент видит обновление.

  • finalization-failure-degradation: внедряет контролируемый сбой после MCP и проверяет, что PostgreSQL-полезная нагрузка остаётся доступной.

  • semantic-robustness-v1: выполняет 80 нейтральных, формальных, неформальных, риоплатенских и пограничных формулировок; измеряет намерение, решение, аргументы, темпоральность, неоднозначность и стабильность.

Динамическая фикстура использует административную роль только во время подготовки и очистки. Запрос агента продолжает использовать роль только для чтения. Блок finally удаляет по UUID и точному номеру сотрудника; в конце дополнительный запрос требует, чтобы не осталось сотрудников EVAL-% или посещений с источником agent-evaluation.

Реальный запуск с настроенным LLM-провайдером, MCP и Neon:

npm run evals:run
npm run resilience:eval
npm run semantic:eval
npm run semantic:stability

semantic:eval один раз проходит 80 случаев, а semantic:stability повторяет критический набор пять раз. Оба сообщают validDecisionRate, intentRecognitionRate, toolSelectionRate, argumentExtractionRate, temporalInterpretationRate, exactOutcomeRate, stabilityRate, ambiguityPassRate и unsupportedPassRate. По умолчанию они ждут 30 секунд между вызовами, чтобы уважать бесплатный бюджет токенов Groq и отделять лимиты провайдера от семантической нестабильности. Базовый уровень Stage 10 сохраняется в backend/evals/baselines/, а результаты Stage 11 — в backend/evals/results/.

Остальные команды возвращают воспроизводимый JSON с passRate, длительностью, ожидаемыми/фактическими проверками и grounded-доказательствами по каждому случаю. Они завершаются ненулевым кодом, если оценка не удалась или осталась временная фикстура. Эталонный случай предполагает наличие демонстрационного сида.

Облачное развёртывание

Репозиторий хранит frontend/ и backend/ отдельно, с двумя поверхностями развёртывания:

  • agent-lab-ignac: фронтенд Vite как Render Static Site;

  • agent-lab-api-ignac: бэкенд Express как Vercel Function с Fluid Compute.

Продакшн-URL:

  • приложение: https://agent-lab-ignac.onrender.com;

  • API: https://agent-lab-api-ignac.vercel.app;

  • прямой health check: https://agent-lab-api-ignac.vercel.app/api/health.

render.yaml управляет только фронтендом и переписывает /api/* на https://agent-lab-api-ignac.vercel.app. Для браузера аутентификация и cookie остаются под origin фронтенда; токен сессии остаётся HttpOnly и не передаётся в React.

backend/vercel.json объявляет Express, максимум 300 секунд и регион gru1 (Сан-Паулу), близкий к базе Neon. Vercel обнаруживает ленивый handler, экспортируемый из src/app.ts; приложение и его пулы инициализируются при первом запросе к инстансу. src/server.ts сохраняет app.listen() для локальной разработки.

Транспорт MCP выбирается через MCP_TRANSPORT:

  • stdio: локальная разработка; клиент запускает отдельный MCP-процесс;

  • in_process: Vercel; клиент и сервер MCP соединяются парой транспортов в памяти, не теряя протокол, контракты, валидацию или обнаружение инструментов.

В локальной разработке LLM_PROVIDER=ollama сохраняет qwen3:8b. В Vercel LLM_PROVIDER=groq использует openai/gpt-oss-20b, поддерживающий function calling. Адаптер Groq принудительно включает хотя бы один инструмент и возвращает его результат модели для создания grounded-ответа.

Требуемые переменные продакшена в проекте 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>

Публичный бэкенд добавляет заголовки через Helmet, rate limits, явную обработку ошибок и GET /api/health. Лимиты в памяти демонстрационные и работают на горячий инстанс; распределённое продакшн-приложение использовало бы общее хранилище. PostgreSQL сохраняет пользователей, сессии и данные, поэтому serverless-файловая система остаётся одноразовой.

CI/CD и quality gates

Каждый push в main и каждый pull request запускают .github/workflows/ci.yml. Бэкенд и фронтенд проверяются в независимых и воспроизводимых jobs на Node.js 22:

checkout → npm ci → typecheck → build → tests → audit de dependencias productivas

npm ci устанавливает ровно дерево, зафиксированное каждым package-lock.json. Jobs имеют только права на чтение репозитория, имеют таймаут и отменяют предыдущие запуски той же ветки. Никакие продакшен-учётные данные не передаются в CI workflow.

Vercel подключён к репозиторию с backend/ как Root Directory; принятый коммит в main запускает serverless-развёртывание. Render поддерживает статический фронтенд из frontend/. Это разделение различает два контроля:

  • quality gate до рантайма: типы, компиляция, тесты и аудит;

  • smoke test после развёртывания: публичный HTTP-контракт, реально развёрнутый.

.github/workflows/production-smoke.yml слушает успешные статусы развёртывания и также позволяет ручной запуск. scripts/production-smoke.mjs проверяет:

  • прямой health бэкенда Vercel;

  • контракт /api/system и текущий этап;

  • публичный контракт /api/resilience;

  • прокси /api/*, обслуживаемый под origin Render;

  • доступность HTML-документа фронтенда.

Локальный запуск того же продакшен-контракта:

node scripts/production-smoke.mjs

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    B
    maintenance
    Enables Claude, Cursor, and other MCP clients to query PeopleForce HRIS data (employees, time-off, recruitment) via 27 read-only tools.
    28
    4
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to query a PostgreSQL database through a small set of controlled, read-only tools for schema inspection, row lookup, and aggregate statistics.
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables querying an HR analytics database (hr_db) using natural language, providing tools to get database schema and run read-only SQL queries for HR metrics like headcount, attrition, and payroll.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    Enables Claude Desktop to securely query a SQLite HR database, providing tools to check leave balances and list pending approvals through natural language.
    -