Agent Lab MCP Server
Agent Lab
企業データに対するAIエージェントの技術的フローを検査するための教育用アプリケーション。このプロジェクトは、契約、プロトコル、ツール呼び出し、構造化された結果、およびサニタイズされたトレースを示します。
ステータス
ステージ1〜11 — 基盤、MCP、Agent Runtime、Auth/RBAC、A2A、評価、クラウド、CI/CD、決定論的契約、レジリエンス、意味的ロバスト性:
frontend React + Vite;
backend Node.js + Express;
PostgreSQLスキーマとマイグレーション;
アプリケーションユーザー
adminとviewer;決定論的カレンダー期間;
技術契約
TraceEvent;ユニットテストとAgent Labのシェルビジュアル;
stdioトランスポート上の公式MCPサーバー;構造化レスポンスを返す7つの読み取り専用MCPツール;
最小権限ロールによるパラメータ化されたPostgreSQLクエリ。
トークンコストなしのローカル推論とツール呼び出しのためのOllama +
qwen3:8b;オプションのプロバイダーとして維持されるOpenAI Responses API;
ツールのディスカバリと実行を備えたローカルMCP Client;
groundedオーケストレーターとエンドポイント
POST /api/agent/query;レスポンスと実際の技術トレースを備えたクエリインターフェース。
PostgreSQLに永続化された不透明セッションによる認証;
HttpOnly、SameSite=Strictクッキーと設定可能な有効期限;adminとviewerプロファイルによるRBAC認可;監査付きユーザー作成と人事データのトランザクション削除。
A2A 1.0 Agent Cardsを公開する2つのエージェント;
JSON-RPC
SendMessageによるHR → 財務への委任;ライフサイクルと構造化Artifactを備えた財務タスク;
MCP経由で照会された欠勤による損失レポート。
決定論的アサーションを備えた行動評価スイート;
参照ケース、空の結果、PostgreSQLの鮮度;
保証されたクリーンアップと残差検証を備えた分離された動的フィクスチャ。
予算化されたタイムアウト、制限付き一時リトライ、ホットインスタンスごとのサーキットブレーカー;
ナラティブのみが失敗した場合にgrounded
answerPayloadを保持する安全な劣化;制御された障害注入によるレジリエンス評価。
LLMの意味的提案による中立スペイン語、インフォーマル、リオプラテンセ方言の解釈;
MCP前のcapability、スキーマ、期間、極性、制限のバックエンド検証;
PostgreSQLにアクセスしない明確化と非対応クエリの型付き決定;
before/afterベースラインと繰り返し実行間の安定性を備えたバージョン管理された言語ベンチマーク。
アプリケーションは、Render上の静的フロントエンド、Vercel上のサーバーレスバックエンド、Neon上のPostgreSQLでデプロイされています。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。プロバイダーが許可する場合、3つの分離された認証情報を持つPostgreSQLクラウド。
インストール
npm --prefix backend install
npm --prefix frontend installbackend/.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:8bOllamaをインストールし、ollama pull qwen3:8b でモデルを一度ダウンロードします。推論はローカルのCPU/GPUとストレージを使用し、有料APIを消費しません。
OpenAIは、LLM_PROVIDER=openai、OPENAI_API_KEY、OPENAI_MODEL を設定することで代替として引き続き利用可能です。キーはGitで無視される backend/.env にのみ属します。フロントエンドに送信したり、トレースに含めたりしてはなりません。
データベース
クラウドプロバイダーで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文字以上である必要があります。リポジトリにはデフォルトのパスワードは存在しません。
開発
2つのターミナルで:
npm run dev:backend
npm run dev:frontendフロントエンド:
http://localhost:5173バックエンド:
http://localhost:3000
クラウドデータベースなしでの検証
npm test
npm run typecheck
npm run buildカレンダーテストは以下を検証します:
1日から今日までの当月;
前月全体;
うるう年の2月;
カレンダーの直近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スキーマは厳格で、各実行を簡単に検査できるように並列呼び出しは無効化されています。
grounded: true は、オーケストレーターが承認されたツールへの呼び出しを検証し、最終レスポンスを要求する前に structuredContent を受け取ったことを意味します。モデルによって生成されたすべてのトークンに対する数学的保証が存在するという意味ではありません。その品質は評価で測定する必要があります。
システムプロンプトは、企業データがツールからのみ取得されること、空の結果が明示的に報告されること、受信したコンテンツが命令ではなくデータとして扱われることを要求します。
APIを消費しない決定論的統合テスト:
npm run agent:smokeローカルOllama、MCP、Neonを使用した実際のテスト:
npm run agent:smoke:ollamaGroq、MCP、Neonを使用した実際のテスト:
npm run agent:smoke:groqOpenAI、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は7つの制御されたcapabilityを受け取り、正確に1つの決定を提案します。バックエンドはその提案を信頼しません。MCP呼び出しを許可する前に、allowlist、Zodスキーマ、ユーザーが表現した期間、極性、ビジネス制限を検証します:
LLM propone → backend valida → MCP ejecuta → PostgreSQL → payload determinista能力 | ツール |
従業員を数える |
|
ディレクトリを一覧表示する |
|
人物を検索する |
|
履歴遅刻を要約する |
|
期間別の遅刻を照会する |
|
遅刻がなかった人を照会する |
|
期間別の欠勤を照会する |
|
7つのMCPツールに加えて、プランニングにはMCPに到達しない2つの内部決定があります: 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は2つの分離されたサーフェスを返します:
presentation: 型付きで決定論的で、特定のReactコンポーネントによってレンダリングされるanswerPayload;answer: LLMによって生成されたgroundedナラティブで、非決定論的として識別されるセカンダリパネルに表示されます。
数量、テーブル、日付、空の状態、ソースメタデータは presentation から表示されます。モデルのテキストから抽出されることはありません。ステージ11はステージ9のこの契約を変更しません。トレースには、提案、検証、決定論的表現を分離するために llm.semantic_proposal.completed、agent.semantic_decision.validated、presentation.payload.validated が含まれます。
否定クエリは集合差として実装されます: アクティブな従業員から期間内に少なくとも1回の遅刻がある従業員を引いたもの。PostgreSQLは NOT EXISTS によってこのセマンティクスを実行します。LLMは補集合を計算しません。Groqが空の最終レスポンスを返すか、完了中に2回目のツール呼び出しを試みた場合、アダプターは同じgroundedデータで単一のテキスト再試行を実行します。イベント llm.grounded_response.completed は recovery=not_required、適用されたリトライタイプ、または決定論的プレゼンテーションへのフォールバックを報告します。
LLMプロバイダーのレジリエンス
ステージ10は、4つの明示的なメカニズムを通じて外部障害を含みます:
技術 | デモポリシー | 結果 |
timeout budget | 試行あたり12秒 | 予算を超える呼び出しを中断する |
bounded retry | 一時的リトライ1回 |
|
circuit breaker | 3回の失敗でオープン。30秒後にハーフオープンを試行 | 停止したままのプロバイダーへの再試行を回避する |
graceful degradation | MCPクエリ成功後にのみ | 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を発行する。
認証と認可
資格情報はapp_users内のbcryptハッシュに対して検証される。認証時、バックエンドはランダムなトークンを作成し、そのSHA-256ハッシュのみをapp_sessionsに保存し、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
このプロジェクトは、公式SDK@a2a-js/sdkを使用してA2A Protocol 1.0を実装している:
HR Grounding Agent:MCPを介したgroundedな従業員および勤怠クエリ。Absence Finance Agent:欠勤の決定論的な経済分析。
エージェントカード:
/.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エンドポイントは、内部のランダムなベアラートークンを使用する。エージェントカードはセキュリティスキームを説明するが、資格情報は決して含まない。
データベースには給与は含まれていない。そのため、レポートには明示的なパラメータ(通貨、日次コスト、代替要員プレミアム、生産性への影響)が必要である。計算式は:
días × costo diario × (1 + prima de reemplazo + impacto de productividad)LLMは算術演算を行わない。決定論的なTypeScript関数が、小数点以下2桁に丸めた金額を計算する。MCPが結果が切り詰められたことを示した場合、エージェントは不完全なレポートを避けるために計算を拒否する。
実装は、フローが短く同期的であるため、インメモリのA2Aタスクを使用する。複数のインスタンスや長時間のタスクの場合、TaskStoreは永続ストレージに移行する必要がある。
エージェントカード、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のニュートラル、フォーマル、インフォーマル、リオプラテンセ、境界的な表現を実行し、意図、決定、引数、時間性、曖昧さ、安定性を測定する。
動的フィクスチャは、準備とクリーンアップ中にのみ管理ロールを使用する。エージェントのクエリは、読み取り専用ロールを引き続き使用する。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ケースを1回実行し、semantic:stabilityは重要なセットを5回繰り返す。両方ともvalidDecisionRate、intentRecognitionRate、toolSelectionRate、argumentExtractionRate、temporalInterpretationRate、exactOutcomeRate、stabilityRate、ambiguityPassRate、unsupportedPassRateを報告する。デフォルトでは、Groqの無料トークン予算を尊重し、プロバイダーの制限と意味的不安定性を分離するために、呼び出し間に30秒待機する。Stage 10のベースラインはbackend/evals/baselines/に保持され、Stage 11の結果はbackend/evals/results/に保持される。
他のコマンドは、passRate、期間、期待される/実際のチェック、ケースごとのgroundedな証拠を含む再現可能なJSONを返す。評価が失敗した場合、または一時的なフィクスチャが残っている場合は、ゼロ以外のコードで終了する。参照ケースは、デモシードが存在することを前提としている。
クラウドデプロイメント
リポジトリはfrontend/とbackend/を分離して保持し、2つのデプロイメントサーフェスを持つ:
agent-lab-ignac:Render Static SiteとしてのViteフロントエンド。agent-lab-api-ignac:Fluid Computeを備えたVercel FunctionとしてのExpressバックエンド。
本番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に書き換える。ブラウザの場合、認証とクッキーはフロントエンドのオリジンに残り、セッショントークンはHttpOnlyのままでReactに公開されない。
backend/vercel.jsonはExpress、最大300秒、Neonデータベースに近いリージョンgru1(サンパウロ)を宣言する。Vercelは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はfunction callingをサポートするopenai/gpt-oss-20bを使用する。Groqアダプターは少なくとも1つのツールを強制し、その結果をモデルに返して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はユーザー、セッション、データを保持するため、サーバーレスファイルシステムは破棄可能なままである。
CI/CDと品質ゲート
mainへの各pushと各プルリクエストは、.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で受け入れられたコミットはサーバーレスデプロイメントを生成する。Renderはfrontend/から静的フロントエンドを維持する。この分離は2つの制御を区別する:
ランタイム前の品質ゲート: 型、コンパイル、テスト、監査。
デプロイメント後のスモークテスト: 実際にデプロイされた公開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 gradedqualityCmaintenanceEnables Claude Desktop to securely query a SQLite HR database, providing tools to check leave balances and list pending approvals through natural language.-