Skip to main content
Glama
yashdhingra0

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