uniquery
by yashdhingra0
README.md
# UniQuery
**Ask your database questions in plain English — from any MCP client, or from the web UI.**
UniQuery connects to a Postgres or MongoDB database in **read-only** mode and exposes it as a natural-language query agent. It ships two front doors onto the same engine: an **MCP server** that plugs into Claude Desktop, Claude Code, Cursor, or any other MCP client, and a **Next.js web app** for exploring results with charts.
The interesting part isn't the natural-language-to-SQL step — plenty of tools do that. It's what sits around it: PII masking before rows reach the model, hard clamps on row counts and query timeouts, read-only enforcement at the connector, and a semantic schema store so the agent gets better at your schema over time.
## Architecture
```
apps/
web/ Next.js 16 + React 19 + Recharts UI
packages/
core/ The engine — connectors, agent loop, guardrails, viz
connectors/ Postgres (Neon serverless / pg) and MongoDB, read-only
agent/ Tool-calling loop over Vercel AI Gateway / Google Gemini
guardrails/ PII masking + row/timeout clamps
semantic/ Schema knowledge store with SQL migrations
viz/ Result-shape classifier that picks a chart type
mcp-server/ MCP server exposing core over @modelcontextprotocol/sdk
```
## Guardrails
These are the reason this is more than a demo:
| Guardrail | Behavior |
|---|---|
| **Read-only access** | Connectors never issue writes, regardless of what the model generates |
| **PII masking** | Columns matching `email`, `ssn`, `phone`, `dob`, `credit_card`, `cvv`, `passport`, `api_key`, `password` and friends are redacted *inside the agent tools* — masked values never reach the LLM context. Extra columns can be tagged manually |
| **Row limit clamp** | Every request is clamped to `[1, 1000]` rows, whatever the agent or MCP client asks for |
| **Timeout clamp** | Every query is clamped to `[1s, 30s]` |
| **Zod-validated tools** | MCP tool inputs are schema-checked before they touch a connector |
## MCP server
The MCP server registers three tools:
- **`connectDatabase`** — register a Postgres or MongoDB connection under a `connectionId` you choose
- **`askQuestion`** — ask a natural-language question against a registered connection; returns a markdown table plus a suggested visualization
- plus a connection-listing tool for discovering what's registered
Results come back as markdown tables (capped at 50 rows for display) with an appended chart suggestion — `bar`, `line`, `table`, or `none` — derived from the result's shape rather than guessed by the model.
### Configure it in an MCP client
```jsonc
{
"mcpServers": {
"uniquery": {
"command": "node",
"args": ["/path/to/uniquery/packages/mcp-server/dist/index.js"]
}
}
}
```
Then, in the client:
> Connect to my Postgres at `postgres://…` as `analytics`, then tell me which five customers generated the most revenue last quarter.
## Getting started
Requires Node.js >= 20 and pnpm 11.
```bash
pnpm install
pnpm build
# web app
pnpm dev
# MCP server
node packages/mcp-server/dist/index.js
```
Copy `.env.example` to `.env.local` and fill in your database URL and an LLM key — either a Vercel AI Gateway key (default) or a Google Gemini key with `provider: "google"`.
## Stack
TypeScript · pnpm workspaces · Model Context Protocol SDK · Vercel AI SDK 6 · Google Gemini · Neon serverless Postgres · MongoDB · Zod · Next.js 16 · Recharts · Vitest
## Status
Working prototype. The engine, guardrails, and MCP server are functional; the web app covers the core query-and-chart flow.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues