Agent Lab MCP Server
Agent Lab
用于检查企业数据上 AI 代理技术流程的教育应用。该项目展示契约、协议、工具调用、结构化结果和脱敏后的追踪信息。
状态
第 1 至 11 阶段 — 基础、MCP、Agent Runtime、认证/RBAC、A2A、评估、云、CI/CD、确定性契约、弹性和语义鲁棒性:
前端 React + Vite;
后端 Node.js + Express;
PostgreSQL 模式和迁移;
应用用户
admin和viewer;确定性日历周期;
技术契约
TraceEvent;单元测试和 Agent Lab 的 shell 可视化;
基于
stdio传输的官方 MCP 服务器;七个只读 MCP 工具,带结构化响应;
通过最小权限角色执行的参数化 PostgreSQL 查询。
Ollama 搭配
qwen3:8b进行本地推理和工具调用,无按 token 计费;OpenAI Responses API 保留为可选提供商;
本地 MCP Client,支持工具发现和执行;
grounded 编排器和
POST /api/agent/query端点;带响应和真实技术追踪的查询界面。
使用持久化在 PostgreSQL 中的不透明会话进行认证;
HttpOnlycookie、SameSite=Strict和可配置的过期时间;基于 RBAC 的授权,包含
admin和viewer配置文件;可审计的用户创建和人力资源数据的事务性删除。
两个代理发布 A2A 1.0 Agent Cards;
通过 JSON-RPC
SendMessage实现 HR → 财务的委派;带生命周期和结构化 Artifact 的财务任务;
通过 MCP 查询的缺勤损失报告。
带确定性断言的行为评估套件;
参考用例、空结果和 PostgreSQL 新鲜度;
隔离的动态 fixture,保证清理和残留验证。
预算化超时、有界瞬时重试和按热实例的熔断器;
安全降级,如果仅叙述失败则保留 grounded 的
answerPayload;带受控故障注入的弹性评估。
通过 LLM 的语义提议解释中性西班牙语、非正式西班牙语和拉普拉塔河西班牙语;
在 MCP 之前对 capability、schema、周期、极性和限制进行后端验证;
类型化的澄清决策和不支持查询决策,无需访问 PostgreSQL;
版本化的语言基准,带 before/after 基线和重复执行间的稳定性。
该应用已部署,前端静态托管在 Render,后端 serverless 在 Vercel,PostgreSQL 在 Neon。GitHub Actions 对生产环境应用质量门禁和冒烟测试。
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 并填写 PostgreSQL 提供商的 URL。切勿在 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 忽略。绝不应发送到前端或包含在追踪信息中。
数据库
在云提供商处创建 PostgreSQL 数据库。
按照
backend/ops/database-roles.example.sql创建或配置角色。配置环境变量。
执行:
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 仅使用 DATABASE_READONLY_URL 并查询带日期参数的 hr_late_arrivals 视图。
种子数据要求 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
该服务器使用官方 Model Context Protocol SDK 来暴露:
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:smokestdout 保留给 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 的 schema 是严格的,并行调用已禁用,以便每次执行都易于检查。
grounded: true 表示编排器在请求最终响应之前验证了对已批准工具的调用并收到了 structuredContent。这并不意味着对模型产生的每个 token 都有数学保证;这种质量必须通过评估来衡量。
系统提示要求企业数据仅来自工具,空结果必须明确报告,接收到的内容应视为数据而非指令。
确定性集成测试,不消耗 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 并提议恰好一个决策。后端不信任该提议:在允许 MCP 调用之前,它会验证 allowlist、Zod schema、用户表达的周期、极性和业务限制:
LLM propone → backend valida → MCP ejecuta → PostgreSQL → payload determinista能力 | 工具 |
统计员工数量 |
|
列出目录 |
|
搜索某个人 |
|
汇总历史迟到 |
|
按周期查询迟到记录 |
|
查询谁没有迟到 |
|
按周期查询缺勤 |
|
除了七个 MCP 工具外,规划还有两个永远不会到达 MCP 的内部决策:request_clarification 和 reject_unsupported_query。前者在缺少周期或存在歧义时返回 agent_clarification_required;后者在请求要求不存在的 capability、排名、频率或过滤器时返回 unsupported_agent_query。公共目录可在 GET /api/agent/capabilities 查询,也出现在 System index 中。
非正式表达按含义解释。例如,迟到、进入、到达、打卡或标记迟到可能指 late_arrivals;“无迟到”和“总是准时”仅在明确周期内解释为零事件。像“一大笔”、“一大把”、“总是”或“经常”这样的表达永远不会被转换为虚构的数量。
在执行 MCP 之后,AnswerPresentation 通过 Zod 判别联合验证 structuredContent。API 返回两个独立的表面:
presentation:类型化的、确定性的answerPayload,由特定的 React 组件渲染;answer:由 LLM 生成的 grounded 叙述,显示在标记为非确定性的辅助面板中。
数量、表格、日期、空状态和来源元数据从 presentation 显示;不从模型文本中提取。第 11 阶段不修改第 9 阶段的此契约。追踪信息包含 llm.semantic_proposal.completed、agent.semantic_decision.validated 和 presentation.payload.validated,以区分提议、验证和确定性呈现。
被拒绝的查询作为集合差实现:在职员工减去周期内至少有一次迟到的员工。PostgreSQL 通过 NOT EXISTS 执行该语义;LLM 不计算补集。如果 Groq 返回空的最终响应或在完成期间尝试第二次工具调用,适配器会使用相同的 grounded 数据执行一次文本重试。事件 llm.grounded_response.completed 报告 recovery=not_required、应用的重试类型或回退到确定性呈现。
LLM 提供商的弹性
第 10 阶段通过四种显式机制包含外部故障:
技术 | 演示策略 | 结果 |
超时预算 | 每次尝试 12 秒 | 中止超过预算的调用 |
有界重试 | 1 次瞬时重试 | 重试 |
熔断器 | 3 次失败后打开;30 秒后尝试半开 | 避免对持续宕机的提供商反复请求 |
优雅降级 | 仅在一次成功的 MCP 查询之后 | 即使没有 LLM 叙述,也保留 grounded 的 |
公共端点 GET /api/resilience 公开策略和脱敏后的熔断器状态,绝不暴露凭据。代理在每个热实例内被复用,以便熔断器在请求之间保持状态。在 Vercel 上,每个实例拥有自己的熔断器;要全局协调需要分布式存储,对于本实验来说没有必要。
如果初始规划失败,此时尚不存在 MCP 调用或 grounded 数据,API 返回类型化错误(llm_timeout、llm_rate_limited、llm_provider_unavailable 或 llm_circuit_open)。如果仅最终文案生成失败,API 以确定性表格成功响应,并发出 llm.grounded_response.degraded。
身份验证与授权
凭据针对 app_users 中的 bcrypt 哈希进行验证。认证时,后端创建一个随机令牌,仅将其 SHA-256 哈希存储在 app_sessions 中,并通过 HttpOnly cookie 交付令牌。前端永远无法访问该令牌。
持续时间通过 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
该项目使用官方 SDK @a2a-js/sdk 实现 A2A Protocol 1.0:
HR Grounding Agent:通过 MCP 进行 grounded 的员工和考勤查询;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:验证通过summarize_employee_delays对 Bruno Silva 的迟到进行确定性聚合;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 种中性、正式、非正式、拉普拉塔河地区和边缘表述;衡量意图、决策、论据、时间性、歧义性和稳定性。
动态 fixture 仅在准备和清理阶段使用管理角色。代理查询继续使用只读角色。一个 finally 块按精确的 UUID 和员工编号删除;完成后,额外查询要求不存在 EVAL-% 员工或来源为 agent-evaluation 的考勤记录。
使用配置的 LLM 提供商、MCP 和 Neon 的真实执行:
npm run evals:run
npm run resilience:eval
npm run semantic:eval
npm run semantic:stabilitysemantic: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 证据。如果评估失败或残留任何临时 fixture,则以非零代码退出。参考用例假定演示种子数据存在。
云部署
仓库保持 frontend/ 和 backend/ 分离,具有两个部署面:
agent-lab-ignac:Vite 前端,作为 Render Static Site;agent-lab-api-ignac:Express 后端,作为带 Fluid Compute 的 Vercel Function。
生产 URL:
应用:
https://agent-lab-ignac.onrender.com;API:
https://agent-lab-api-ignac.vercel.app;直接健康检查:
https://agent-lab-api-ignac.vercel.app/api/health。
render.yaml 仅管理前端,并将 /api/* 重写至 https://agent-lab-api-ignac.vercel.app。对于浏览器,身份验证和 cookie 仍在前端源下;会话令牌保持 HttpOnly,不暴露给 React。
backend/vercel.json 声明 Express、300 秒最大值和 gru1(圣保罗)区域,靠近 Neon 数据库。Vercel 检测 src/app.ts 导出的惰性 handler;应用及其连接池在收到实例的第一个请求时初始化。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,支持函数调用。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 添加标头、速率限制、显式错误处理和 GET /api/health。内存限制是演示性的,按热实例运行;分布式生产应用将使用共享存储。PostgreSQL 保留用户、会话和数据,因此 serverless 文件系统保持可丢弃状态。
CI/CD 与质量门禁
每次推送到 main 和每个拉取请求都执行 .github/workflows/ci.yml。后端和前端在 Node.js 22 上的独立可复现作业中验证:
checkout → npm ci → typecheck → build → tests → audit de dependencias productivasnpm ci 精确安装每个 package-lock.json 固定的依赖树。作业仅具有仓库的读取权限,有超时,并取消同一分支的先前运行。没有生产凭据被交付给 CI 工作流。
Vercel 连接到仓库,backend/ 作为根目录;main 上接受的提交生成 serverless 部署。Render 从 frontend/ 维护静态前端。这种分离区分了两种控制:
运行时前的质量门禁: 类型、编译、测试和审计;
部署后的冒烟测试: 实际部署的公共 HTTP 契约。
.github/workflows/production-smoke.yml 监听成功的部署状态,也允许手动运行。scripts/production-smoke.mjs 检查:
Vercel 后端的直接健康检查;
/api/system契约和当前阶段;/api/resilience的公共契约;Render 源下提供的
/api/*代理;前端 HTML 文档的可用性。
本地执行相同的生产契约:
node scripts/production-smoke.mjsThis server cannot be deployed
Maintenance
Related MCP Connectors
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Safe, read-only Postgres and MySQL access for AI agents. Audit log + column-level controls.
- dataOAuthco.thinair
PostgreSQL, MySQL, and SQL Server in one session. 26 read-only MCP tools for AI agents.
Gateway between LLM agents and world data through eight tools and a bundled endpoint catalog.
Related MCP Servers
- AlicenseBqualityBmaintenanceEnables Claude, Cursor, and other MCP clients to query PeopleForce HRIS data (employees, time-off, recruitment) via 27 read-only tools.284MIT
- AlicenseNot gradedqualityDmaintenanceEnables 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
- FlicenseNot gradedqualityDmaintenanceEnables 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.-
- FlicenseNot gradedqualityDmaintenanceEnables Claude Desktop to securely query a SQLite HR database, providing tools to check leave balances and list pending approvals through natural language.-