policymesh
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., "@policymeshShow me total revenue by region while masking customer PII"
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.
PolicyMesh — Governed Data Intelligence
Every query is SQL-validated, access-controlled by role, PII-masked, and audit-logged before a single row is returned. Governance lives at the MCP tool boundary — enforced in code, not in the prompt.
What it does
PolicyMesh is a governed agentic data API. A user asks a natural-language question; an LLM agent decides which tool to call; every tool call passes a server-side enforcement pipeline before touching data.
User question
│
▼
POST /ask ──► Agent loop (LLaMA 3.3 70B via Groq)
│
▼ picks a tool
┌───────────────────────────────────────┐
│ MCP Tool Boundary │
│ │
│ query_sql ──► parse (SQLGlot) │
│ allowlist check │
│ RBAC check │
│ EXPLAIN dry-run │
│ read-only execute │
│ PII mask │
│ audit log ─────────┼──► audit_log table
│ │
│ search_docs ──► embed query │
│ pgvector cosine │
│ build_context [N] │
│ audit log ─────────┼──► audit_log table
│ │
│ lookup_metadata ► role-filtered │
│ schema discovery │
│ audit log ─────────┼──► audit_log table
└───────────────────────────────────────┘
│
▼
Grounded, cited answerRelated MCP server: RunContext
System design
graph TD
U([User]) -->|POST /ask| API[FastAPI]
API -->|run_agent| LOOP[Agent Loop\nLLaMA 3.3 via Groq\nMAX_STEPS=5]
LOOP -->|tool_call| BOUNDARY
subgraph BOUNDARY [MCP Tool Boundary — app/policy.py + app/sql_validator.py]
P[check_access\nrole → tables] --> V[validate_and_run\nparse → allowlist → EXPLAIN → read-only]
V --> M[mask_rows\nPII sentinel ***]
S[search_raw\npgvector cosine] --> C[build_context\nnumbered citations]
META[get_metadata\ninformation_schema + pg_class]
end
M -->|rows| AUDIT[audit.log_call\nfinally block]
C -->|context| AUDIT
META -->|schema| AUDIT
AUDIT -->|INSERT| DB[(audit_log\nappend-only)]
V -->|SELECT| PG[(Neon Postgres\n5 tables)]
S -->|<=> cosine| PG
META -->|system tables| PG
LOOP -->|answer| API
API -->|JSON| UWhy this design
Governance at the boundary, not the prompt. Prompt rules can be jailbroken, ignored, or forgotten across model upgrades. Every access-control decision in PolicyMesh is enforced server-side in app/policy.py — the model never sees data it shouldn't.
Four SQL guards, cheapest first.
parse()— SQLGlot AST; must be exactly one SELECT. Rejects DDL/DML before any network call.check_allowlist()— everyexp.Tableandexp.Columnnode must be in the schema allowlist. Includes scope-alias fix soORDER BY total_revenuepasses whentotal_revenueis a SELECT alias.dry_run()—EXPLAINagainst live DB catches type mismatches the AST walk misses.execute_readonly()—SET TRANSACTION READ ONLYblocks data-modifying CTEs at the engine level.
Why a custom agent loop, not LangChain/LangGraph.
The loop is 80 lines with explicit termination conditions, state as a flat messages list, and a hard step cap. LangGraph adds ~400 lines of framework surface area, a new graph DSL to explain, and hides the termination logic inside abstractions. For a governed system, the control flow must be auditable — a while True is auditable; a graph runtime is not.
Two eval axes (per the CLAUDE.md spec), pass thresholds enforced in tests/test_eval.py:
Tool-selection accuracy — deterministic check, threshold 83% (5/6)
Groundedness — LLM-as-judge via Groq, threshold 80%
Both hit a live LLM against a free-tier key, so the exact score varies run-to-run and dips if the day's Groq/Gemini free-tier quota is already partly spent (e.g. from repeated manual testing, or firing scripts/stress_test.py right before). Run pytest tests/test_eval.py -v -m eval -s for a current read rather than trusting a cached number.
Tech stack
Layer | Choice | Why |
API | FastAPI (async) | Non-blocking I/O for DB + embedding calls |
LLM agent | LLaMA 3.3 70B (Groq), auto-fallback to Gemini 2.5 Flash | Free tier, tool calling; both speak the OpenAI-compatible chat API, so the fallback is a drop-in swap on a 429 ( |
Embeddings | Gemini embedding-001 | Free tier, 3072-dim, strong semantic quality |
Database | Neon (serverless Postgres) | pgvector built-in, free tier, matches local dev |
Vector search | pgvector | Native Postgres, no separate vector DB needed |
Tool protocol | MCP 2.0 (SSE transport) | Industry standard for AI tool exposure |
SQL parsing | SQLGlot | Typed AST, handles aliases, CTEs, subqueries |
Tests | pytest + pytest-asyncio | 63 unit/integration tests + 2-axis live eval |
Running locally
# Clone and install
git clone https://github.com/deepakmeena61/policymesh
cd policymesh
python -m venv .venv && .venv/bin/pip install -r requirements.txt
# Configure (get free keys at neon.tech, console.groq.com, aistudio.google.com)
cp .env.example .env
# Fill in DATABASE_URL, GROQ_API_KEY, GOOGLE_API_KEY
# Seed database
.venv/bin/python -m app.seed # 5 tables, 25 customers, 67 orders
.venv/bin/python -m app.seed_docs # 6 knowledge-base documents with embeddings
# Start
.venv/bin/uvicorn app.main:app --port 8000 --reload
# Open http://localhost:8000Running tests
# Unit + integration tests (no API keys needed for unit tests)
.venv/bin/pytest tests/test_policy.py tests/test_sql_validator.py -v
# Audit integration tests (needs DATABASE_URL)
.venv/bin/pytest tests/test_audit.py -v
# Live evals (needs GROQ_API_KEY + GOOGLE_API_KEY)
.venv/bin/pytest tests/test_eval.py -v -m eval -sProject structure
app/
├── main.py # FastAPI app — /ask, /explore, /audit, /health, env validation
├── agent.py # Agent loop: tool selection, MAX_STEPS cap, messages state, LLM retry
├── mcp_server.py # MCP 2.0 SSE server — 3 tools: query_sql, search_docs, lookup_metadata
├── policy.py # ← ALL governance: ROLE_POLICY, check_access(), mask_rows(), ABAC sketch
├── sql_validator.py # 4-guard SQL pipeline + scope-alias fix for ORDER BY/HAVING/CTE aliases
├── audit.py # Append-only audit log — log_call() context manager fires in finally
├── metadata.py # Role-filtered schema discovery (information_schema + pg_class)
├── docs.py # pgvector cosine search + build_context() with [N] citation markers
├── embed.py # Google Gemini embedding wrapper (asyncio.to_thread, singleton client)
├── explore.py # Data exploration API — same policy masking as MCP tools
├── seed.py # 5 tables, 25 customers, 67 orders, 12k events, 28 tickets (--reset flag)
└── seed_docs.py # 6 KB docs with Gemini embeddings (--reset flag)
tests/
├── test_policy.py # 15 unit tests — RBAC + PII masking
├── test_sql_validator.py # 23 unit tests — 4 guards + scope-alias edge cases
├── test_audit.py # 4 integration tests — hits real Neon DB
└── test_eval.py # 2-axis eval: tool-selection accuracy + groundedness (LLM-as-judge)Demo scenarios
Role | Query | What to observe |
| "Who are our top customers by spend?" |
|
| "List all customers" | MCP boundary: |
| "What SLA do enterprise customers get?" |
|
| "What data do I have access to?" |
|
| "Open critical support tickets?" |
|
any | Click Explore tab | Role-filtered schema + sample rows + stats per column |
any | Click Audited pill | Live feed of every tool call with role, latency, row count |
Known limitations / production delta
Gap | Current state | Production fix |
Auth |
| Extract role from verified JWT at FastAPI middleware; |
ABAC | RBAC only (role → tables/columns) | Extend to ABAC via a policy DB table with subject attributes + resource sensitivity tags — sketch in |
Alias PII bypass |
| Column-level |
Dynamic roles |
| Move to DB-backed policy table; make |
Data lineage | Not tracked | Extend |
Warehouse scale | Neon Postgres only |
|
LLM provider quota | Groq free tier (100k tokens/day) with Gemini fallback on 429 ( | A paid tier or self-hosted model removes the ceiling entirely; governance is LLM-agnostic via |
Column enforcement timing |
| Column-level |
Governance boundary duplication | The same enforcement sequence ( | Have the agent loop call the MCP tool functions directly instead of re-implementing them, so there is exactly one enforcement path instead of two kept in sync by hand |
Built as a portfolio project demonstrating MCP, governed data access, agentic retrieval, and two-axis eval — the core skills for AI platform engineering on data-mesh infrastructure.
This server cannot be deployed
Maintenance
Related MCP Connectors
The grounded data layer for any LLM: governed SQL, metrics, lineage and catalog over your data.
Query your warehouse or a CSV with Claude/ChatGPT over MCP, governed by table-level ACL + audit.
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
Related MCP Servers
AlicenseNot gradedqualityAmaintenanceToolMesh is an Apache-2.0, self-hosted MCP gateway written in Go that sits between AI agents and backend systems. It enforces a fail-closed pipeline on every tool call, including per-tool and per-user authorization, server-side credential injection, structured audit logging, and output policies. APIs are declared in YAML with DADL, turning REST endpoints into MCP tools without writing a custom MCP6Apache 2.0- AlicenseNot gradedqualityCmaintenanceEnables AI agents to understand and query your database safely by providing a semantic layer of metadata, with tools to search, explain, validate, and generate safe SQL.2MIT
- AlicenseAqualityFmaintenanceA governed SQL gateway that exposes typed tools to AI agents, compiling safe read-only queries from a semantic layer while blocking PII before execution, supporting SQL Server, Postgres, and SQLite.9MIT
- AlicenseNot gradedqualityAmaintenanceA governed SQL gateway for untrusted AI agents, providing controlled access to PostgreSQL, MySQL, and OceanBase with RBAC, field ACLs, row policies, and cost controls.MIT