Warden
Scans, deletes, anonymizes, and restores subject records such as sessions and activity logs in MongoDB as part of a multi-system data-rights fan-out.
Optional GPT-4o-mini enrichment that appends a plain-English narrative to deterministic impact reports for human reviewers; gracefully disabled if not configured and never blocks execution.
Scans, deletes, anonymizes, and restores subject records (users, subscriptions, profiles) in PostgreSQL, and stores Warden's central governance state (approvals, snapshots, execution steps, scans, impact reports, and hash-chained audit log).
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Wardenscan all records for user jane@example.com and show the impact"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
🛡 Warden
Governed Data-Rights Execution Agent — MCP server for auditable, reversible, human-gated data deletion.
Warden is a Model Context Protocol server that finds, proves, and executes data-deletion or anonymization rights across every connected system in an organization — but never acts without showing the blast radius first, and every action it takes can be undone and independently audited.
The governance layer is the product.
Table of Contents
Related MCP server: Conarium
Problem
AI agents are being given direct access to production databases to "handle" data requests. The risks are real:
Gap | Consequence |
No blast-radius preview | Unknown what breaks before you delete |
No approval gate | Agent acts autonomously — no human checkpoint |
No audit proof | Cannot prove what was deleted, when, or by whom |
No rollback | Once gone, it is gone |
Regulations (GDPR, DPDP, CCPA) mandate auditable, reversible erasure workflows. Warden enforces this at the architecture level.
Solution
Warden inserts a governance layer between the AI client and your data:
AI requests data deletion
|
scan_subject -> discovers ALL records across every connected system
generate_impact_report -> blast-radius: what breaks downstream if we delete?
request_execution -> creates a pending action token -- NO mutation yet
|
Human reviews in Ops Dashboard -> Approve / Deny
|
execute_approved_action -> snapshots data first, then deletes/anonymizes
rollback_action -> restores from snapshot on demand
|
Audit log -> every step SHA-256 hash-chained, tamper-evidentNo irreversible action executes without an explicit human decision. Every action — taken or undone — is independently auditable.
Key Features
Feature | Implementation |
5 MCP tools |
|
Multi-system fan-out | Parallel scan across Postgres, MongoDB, and payment ledger via uniform |
SHA-256 hash-chained audit log | Every entry's hash depends on the previous — tamper one entry and all subsequent hashes break |
Pre-execution snapshots | Full data snapshots taken before any mutation — rollback always available |
Persistent scan state | Scan results and impact reports persisted to Postgres — server restarts do not lose mid-flow approval state |
Blast-radius impact report | Structured risk assessment: |
Human approval gate |
|
Optional LLM enrichment | OpenAI GPT-4o-mini generates a plain-English narrative for impact reports (gracefully disabled if not configured) |
Ops Dashboard | Next.js console for reviewing pending requests, approving/denying, rolling back, and verifying hash-chain integrity |
Health endpoint |
|
Architecture
+-------------------------------------------------------------+
| MCP Client (Claude / MCP Inspector / any) |
+--------------------------|----------------------------------+
| stdio transport
+--------------------------V----------------------------------+
| Warden MCP Server |
| src/index.ts (Node.js) |
| |
| scan_subject . generate_impact_report . request_execution |
| execute_approved_action . rollback_action |
| |
| HTTP /health (Railway / Render / Docker monitoring) |
+------|------------------------------------|------------------+
| |
+------V--------+ +--------------------V-----------------+
| Data Adapters| | Ops Dashboard (Next.js) |
| | | |
| . Postgres | | Pending requests . Blast radius |
| . MongoDB | | Approve / Deny . Rollback |
| . Ledger stub | | Live audit log feed |
+-------V-------+ | Verify Integrity (hash chain walk) |
| +--------------------------------------+
+-------V---------------------------------------------------------+
| PostgreSQL -- Central State Store |
| |
| approvals . snapshots . execution_steps . audit_log |
| scans . impact_reports |
+-----------------------------------------------------------------+
+-------V---------------------------------------------------------+
| Audit Log -- SHA-256 Hash Chain |
| |
| entry1: hash = SHA256(data1 + "GENESIS") |
| entry2: hash = SHA256(data2 + entry1.hash) |
| entryN: hash = SHA256(dataN + entryN-1.hash) |
| |
| Tamper entry2 -> entry3 through entryN all fail verify |
+-----------------------------------------------------------------+Request Flow
scan_subject(email)
-> PostgresAdapter.findByIdentifier() (users, subscriptions, profiles)
-> MongoDBAdapter.findByIdentifier() (sessions, activity_logs)
-> PaymentAdapter.findByIdentifier() (in-memory ledger stub)
-> ScanResult persisted to Postgres scans table
-> AuditLogger.appendLog(SCAN)
generate_impact_report(scan_id)
-> Fetches ScanResult from cache / Postgres
-> Computes risk: orphan_risk -> CRITICAL | financial -> HIGH | multi-pii -> MEDIUM
-> Optionally enriches with OpenAI GPT-4o-mini narrative
-> ImpactReport persisted to Postgres impact_reports table
request_execution(scan_id, action)
-> Runs impact report
-> Creates pending approval token in Postgres approvals table
-> Returns token -- NO data changed
[Human approves via dashboard]
execute_approved_action(token)
-> Validates token status === "approved"
-> Phase A: snapshotRecords() across all adapters -> Postgres snapshots table
-> Phase B: deleteRecords() / anonymizeRecords() across all adapters
-> On failure: automatic rollback initiated
-> AuditLogger.appendLog(EXECUTION_COMPLETED)
rollback_action(execution_id)
-> Validates not already rolled back
-> adapter.restoreRecords(executionId) across all adapters
-> Updates approvals.status = "rolled_back"
-> AuditLogger.appendLog(ROLLBACK_COMPLETED)Tech Stack
Layer | Choice | Version |
Language | TypeScript | 7.x |
Runtime | Node.js | 22 LTS |
MCP SDK |
| ^1.29.0 |
Input validation |
| ^4.4.3 |
Primary DB | PostgreSQL ( | ^8.22.0 |
Secondary DB | MongoDB ( | ^7.5.0 |
Payment stub | In-memory JSON ledger | — |
Crypto | Node.js | built-in |
Dashboard | Next.js (App Router) | latest |
Dashboard styling | Tailwind CSS | v4 |
Dashboard data fetching | React Query (TanStack) | — |
MCP transport | stdio | — |
CI | GitHub Actions | — |
Containerization | Docker (multi-stage) | — |
AI Architecture
Optional LLM Narrative Enrichment
Warden's impact report engine is deterministic by default — it always produces a structured risk assessment with no external dependencies. The optional LLM layer adds a plain-English summary for human reviewers.
generate_impact_report(scan_id)
|
V
computeRiskLevel(hits) <- pure function, always runs
| orphan_risk -> CRITICAL
| financial/vol>50 -> HIGH
| multi-pii -> MEDIUM
| else -> LOW
V
ImpactReport (deterministic)
|
V
enrichWithNarrative(report) <- optional, gracefully skipped
| if LLM_ENABLED=true AND OPENAI_API_KEY is set:
| POST https://api.openai.com/v1/chat/completions
| model: gpt-4o-mini
| prompt: structured summary of risk, records, dependencies
| -> appends narrative field to report
| else (or on error):
| returns report unchanged
V
ImpactReport (with or without narrative)Design principle: LLM failure never blocks execution. The governance gate works without AI — AI is an enhancement, not a dependency.
Database Schema
PostgreSQL (primary state)
Table | Purpose |
| Pending / approved / denied / executed / rolled_back actions with approval token |
| Pre-execution data snapshots (JSONB) indexed by |
| Step-by-step log of each execution phase (SNAPSHOT / MUTATE / ROLLBACK) |
| SHA-256 hash-chained immutable audit trail; unique index on |
| Persisted scan results (JSONB) — survives server restarts |
| Persisted impact reports (JSONB) — referenced by |
Audit log chain integrity is enforced by a WITH last_log AS (...) CTE that checks prev_hash matches inside a single atomic INSERT ... WHERE — concurrent appends that race are rejected with a 23505 unique constraint violation.
MongoDB (sessions and behavioral logs)
Collections scanned:
sessions— user session recordsactivity_logs— behavioral event logs
MongoDB is used by the MongoDB adapter for discovery, snapshot, delete/anonymize, and restore operations.
Payment Ledger (in-memory stub)
An in-memory JSON array loaded at startup (seeded by npm run seed). Demonstrates the multi-system adapter pattern without requiring a real payment API. Snapshot/restore operates directly on the in-memory array.
MCP Tools API
All tools are exposed over stdio transport using @modelcontextprotocol/sdk.
scan_subject
Discover all records for a subject across every connected data system.
Parameter | Type | Required | Description |
|
| Yes | Email address or user ID to scan |
Returns: ScanResult — scan ID, list of data hits per system/table with sensitivity tags (pii, financial, behavioral, system), total record count.
generate_impact_report
Produce a blast-radius assessment for a previous scan.
Parameter | Type | Required | Description |
|
| Yes | Scan ID from a previous |
Returns: ImpactReport — risk level (LOW/MEDIUM/HIGH/CRITICAL), data found section, downstream dependency risks, recommended action, optional LLM narrative.
request_execution
Request human approval for a delete or anonymize operation. No data is modified.
Parameter | Type | Required | Description |
|
| Yes | Scan ID to operate on |
|
| Yes | Operation type |
Returns: Pending action record with token UUID. Direct the reviewer to the Ops Dashboard to approve or deny.
execute_approved_action
Execute a human-approved action. Snapshots data first, then mutates across all systems. Automatically rolls back on partial failure.
Parameter | Type | Required | Description |
|
| Yes | Approval token from |
Returns: execution_id UUID. Fails with error if token status is not approved.
rollback_action
Restore all data from the pre-execution snapshot.
Parameter | Type | Required | Description |
|
| Yes | Execution ID from |
Returns: Success confirmation. Idempotency guard prevents double rollback.
Dashboard API Routes
Internal Next.js API routes. All mutating routes require X-Dashboard-Secret header.
Route | Method | Auth | Description |
|
| None | Dashboard health check |
|
| Required | List all approval requests |
|
| Required | Approve or deny a pending action |
|
| Required | Trigger rollback for an executed action |
|
| Required | Paginated live audit log feed |
|
| Required | Walk the full hash chain and report first break |
|
| Required | Trigger demo tamper for hash chain demonstration |
Local Setup
Prerequisites
Docker Desktop (for Postgres + MongoDB)
1. Clone and install
git clone https://github.com/hashmessi/Warden-MCP.git
cd Warden-MCP
npm install
cd dashboard; npm install; cd ..2. Configure environment
# MCP server
Copy-Item .env.example .env
# Dashboard
Copy-Item dashboard\.env.local.example dashboard\.env.localEdit dashboard/.env.local and set DASHBOARD_SECRET to any string for local dev.
3. Start databases
docker-compose up -d
# Wait ~15s for health checks to pass4. Seed synthetic data
npm run seed
# Seeds users, subscriptions, sessions, payment records5. Start services (three separate terminals)
Terminal 1 — Ops Dashboard:
cd dashboard
npm run dev
# -> http://localhost:3000Terminal 2 — MCP Inspector (interactive tool testing):
npx @modelcontextprotocol/inspector tsx src/index.ts
# -> MCP Inspector: http://localhost:6274
# -> Health check: http://localhost:3001/healthTerminal 3 — MCP server (direct stdio, optional):
npm run devFull Demo Flow
1. scan_subject("jane.doe@email.com")
-> Returns hits from Postgres, MongoDB, payment ledger
2. generate_impact_report("<scan_id>")
-> Risk level, dependency warnings, recommended action
3. request_execution("<scan_id>", "delete")
-> Returns approval token -- no data changed yet
4. Open http://localhost:3000, review blast radius, click Approve
5. execute_approved_action("<token>")
-> Snapshots all data, then deletes across all systems
6. rollback_action("<execution_id>")
-> Restores from pre-execution snapshot
7. npm run demo:tamper
-> Corrupts one audit entry in Postgres
-> Click "Verify Integrity" in dashboard -- chain break detectedEnvironment Variables
MCP Server (.env)
Variable | Required | Default | Description |
| Yes | — | PostgreSQL connection string |
| Yes | — | MongoDB connection string |
| Yes |
| MongoDB database name |
| No |
| HTTP health endpoint port |
| No |
| Runtime environment |
| No |
| Enable OpenAI narrative enrichment |
| No | — | Required only if |
Dashboard (dashboard/.env.local)
Variable | Required | Description |
| Yes | Same Postgres instance as MCP server |
| Yes | Same MongoDB instance as MCP server |
| Yes | Same database name |
| Yes | Shared secret for |
See .env.production.example for production values and source instructions.
Deployment
Cloud: Railway (MCP server) + Vercel (dashboard)
Step 1 — Provision managed databases
PostgreSQL: supabase.com -> Project -> Settings -> Database -> Connection string
MongoDB: cloud.mongodb.com -> free M0 cluster -> Connect -> Drivers -> connection string
Step 2 — Deploy MCP server to Railway
Connect your GitHub repo to Railway. Railway auto-detects railway.toml and builds the Dockerfile. Set these environment variables in the Railway dashboard:
POSTGRES_URL=<supabase-url>
MONGODB_URL=<atlas-url>
MONGODB_DB=warden_db
PORT=3001
NODE_ENV=productionStep 3 — Deploy dashboard to Vercel
Connect your GitHub repo to Vercel, set Root Directory to dashboard, and add these environment variables in Vercel project settings:
POSTGRES_URL=<supabase-url>
MONGODB_URL=<atlas-url>
MONGODB_DB=warden_db
DASHBOARD_SECRET=<openssl rand -hex 32>Step 4 — Seed production data
$env:POSTGRES_URL="<prod-url>"; $env:MONGODB_URL="<prod-url>"; npm run seedDocker (self-hosted)
# Build image
docker build -t warden .
# Run with env file
docker run -p 3001:3001 --env-file .env warden
# Full local stack with docker-compose
docker-compose up -d
# Optional: seed via Docker
docker-compose --profile seed up seedThe Dockerfile is a two-stage build: TypeScript compiled in Stage 1 (node:22-alpine), only production node_modules and compiled dist/ copied to Stage 2. Includes a Docker HEALTHCHECK that polls GET /health.
Testing
CI Pipeline (GitHub Actions)
Three parallel jobs on every push to main/master and every pull request:
Job | Commands |
Backend |
|
Dashboard |
|
Lint | ESLint on dashboard ( |
End-to-End Verification Script
Validates the full scan -> approve -> execute -> rollback loop against live databases:
# Requires running Postgres + MongoDB
docker-compose up -d
npm run seed
npm run test:e2eThe script (src/scripts/verify-e2e.ts) asserts:
scan_subjectfinds recordsPending action created and approved
executeActionreturns an execution IDSecond scan confirms records are deleted (count = 0)
rollbackActionrestores dataThird scan confirms count matches original
Exit 0 = pass, exit 1 = fail with specific error message.
Manual Verification
# Health endpoint
Invoke-WebRequest http://localhost:3001/health | ConvertFrom-Json
# Interactive MCP tool calls via browser UI
npx @modelcontextprotocol/inspector tsx src/index.ts
# -> http://localhost:6274
# Tamper demo
npm run demo:tamper
# Then click "Verify Integrity" in the dashboardKnown Limitations
Limitation | Detail |
Single-approver model | Any request with the correct |
Audit log race condition | Concurrent appends are serialized by a |
Payment ledger is in-memory | Data is lost on server restart unless |
No real-time push | Dashboard uses React Query polling. No WebSocket or SSE — updates require a poll interval or manual refresh. |
LLM enrichment is best-effort | If OpenAI is unreachable or the key is invalid, narrative enrichment is silently skipped. The structured report is always returned. |
Tamper-evident, not tamper-proof | The SHA-256 hash chain detects stored-entry modifications, but a Postgres admin with write access could reconstruct the chain. Suitable for audit and detection, not custody proof. |
Shared-secret dashboard auth |
|
No pagination on scan results |
|
Future Improvements
Improvement | Value |
Per-user auth (NextAuth.js + OAuth) | Named identities in audit log; revocable sessions |
Approval comments | Approvers attach a reason to approve/deny decisions |
Field-level anonymization config | Configurable strategies per field type rather than placeholder replacement |
Additional adapters | Stripe API, Salesforce, S3 object storage, Elasticsearch |
Scheduled compliance scans | Cron-triggered scanning for GDPR Article 17 erasure requests |
Audit log WORM export | Export hash chain to S3 + Object Lock for regulatory retention |
MCP Resources/Prompts | Expose scan results and audit log as MCP Resources for non-tool access |
Multi-approver quorum | N-of-M approval requirement for CRITICAL risk operations |
Streaming scan progress | MCP progress notifications for real-time status on large scans |
Project Structure
warden/
├── src/
│ ├── index.ts # MCP server (5 tools) + HTTP /health endpoint
│ ├── scanner.ts # Multi-system parallel scan fan-out (Postgres-persisted)
│ ├── config.ts # Typed config from environment variables
│ ├── adapters/
│ │ ├── types.ts # DataAdapter interface
│ │ ├── postgres.adapter.ts
│ │ ├── mongodb.adapter.ts
│ │ ├── payment.adapter.ts # In-memory ledger stub
│ │ └── index.ts # createAdapterRegistry()
│ ├── audit/
│ │ └── logger.ts # SHA-256 hash chain (AuditLogger.appendLog)
│ ├── execution/
│ │ └── engine.ts # executeAction() + rollbackAction()
│ ├── approval/
│ │ └── store.ts # Approval CRUD (Postgres-backed)
│ ├── impact/
│ │ ├── report.ts # ImpactReport generation + risk computation
│ │ ├── llm.ts # Optional OpenAI narrative enrichment
│ │ └── types.ts
│ ├── db/
│ │ ├── postgres.ts # Pool, initDb(), query()
│ │ ├── mongodb.ts # MongoClient singleton + pingMongo()
│ │ └── ledger.json # Payment ledger seed data
│ ├── utils/
│ │ └── crypto.ts # hashIdentifier(), computeAuditHash()
│ ├── seed/
│ │ └── index.ts # Synthetic data seeder (Faker.js)
│ └── scripts/
│ ├── tamper.ts # Demo: corrupt one audit log entry
│ └── verify-e2e.ts # End-to-end verification script
├── dashboard/
│ ├── app/
│ │ ├── page.tsx # Ops console UI
│ │ ├── layout.tsx
│ │ ├── globals.css
│ │ ├── providers.tsx # React Query provider
│ │ ├── types.ts
│ │ ├── lib/auth.ts # Dashboard API auth helper
│ │ └── api/
│ │ ├── health/
│ │ ├── approvals/[token]/
│ │ │ └── rollback/
│ │ ├── audit-log/
│ │ ├── verify-integrity/
│ │ └── demo/
│ ├── vercel.json
│ └── package.json
├── .github/workflows/ci.yml # Typecheck + build + lint
├── Dockerfile # Multi-stage Node.js 22 build
├── docker-compose.yml # Local: Postgres 16 + MongoDB 7
├── railway.toml # Railway deployment config
├── .env.example # Local dev template
└── .env.production.example # Production templateSecurity Notes
Dashboard approve/deny/rollback routes require
X-Dashboard-SecretheaderAudit log subject identifiers are SHA-256 hashed — raw PII is never stored in the audit log
Scan results store identifiers in the
scanstable; restrict Postgres access in productionProduction auth path: Replace
DASHBOARD_SECRETwith an IdP integration (Okta, Auth0) + NextAuth.js
License
Built as a portfolio-grade demonstration of production governance patterns for AI agent ecosystems. Every irreversible action requires a human decision. Every action is independently auditable with cryptographic proof.
This server cannot be deployed
Maintenance
Related MCP Connectors
Runtime AI governance: decision gates, human approval, hash-chained audit, compliance mapping.
Register every AI agent, log every action, prove it. EU AI Act compliance built in.
Sanctions screening, KYB, identifier validation, jurisdiction risk & secret scanning for AI agents
Pre-action allow/deny for AI agents. 24 statutes, 13 jurisdictions: EU AI Act, GDPR, DPDP.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables AI agents to sign decisions with post-quantum cryptographic proofs and maintain secure audit trails for compliance. It provides tools for stamping events, verifying chain integrity, and exporting audit data across industries like finance and healthcare.432 npmMIT
- AlicenseAqualityAmaintenanceSelf-hosted governance layer between an AI assistant and your data: allow/deny policy, deterministic PII masking, row caps, and a hash-chained audit log with an Ed25519-signed receipt for every access, verifiable offline.4434 npm3MIT
- AlicenseNot gradedqualityCmaintenanceTamper-evident audit trail MCP server for EU AI Act and GDPR compliance. It enables logging events with automatic PII scanning, compliance checks, and right-to-erasure support.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceProvides permission gates and tamper-evident audit logging for AI agent tool executions, with declarative policies, consent ladders, and hash-chained verification.MIT