PlainQuery
Ask your database questions in plain English. Connects PostgreSQL databases to MCP clients and answers with real, structured data instead of guesses.
README
PlainQuery
Ask your database questions in plain English. PlainQuery connects PostgreSQL databases to Claude, Cursor, or VS Code — and answers with real, structured data instead of guesses.
<p align="center"> <img src="docs/PlainQuery_GIF.gif" alt="PlainQuery demo — querying a database in plain English from an MCP client" width="800"> </p>
What it does
You connect a database once. Then, from any MCP client, you ask questions like:
"How many orders did we ship in March?" "Which 5 customers spent the most last year?" "What's the average order value by month?"
PlainQuery introspects your schema, generates SQL with an LLM, validates it for safety, runs it, and returns structured JSON. If a query fails, it reads the error and retries automatically.
Why PlainQuery
- Natural language, real answers — no SQL required; results come from your actual data, not a hallucination.
- Read-only and safe by design — every query is checked before it runs: writes (
INSERT/UPDATE/DELETE/DDL) are blocked, dangerous functions and patterns are scanned out, and aLIMITis injected automatically. - Self-correcting — when a generated query errors, the agent feeds the error back to the LLM and retries (up to a configurable limit).
- Schema-aware — automatically introspects and caches your schema, so questions map to the right tables and columns.
- Works with any MCP client — Claude Desktop, Cursor, VS Code Copilot, or anything that speaks MCP.
- Secure multi-tenant hosting — database URLs and LLM keys are Fernet-encrypted at rest; user-supplied connection strings are SSRF-guarded; every request is tenant-scoped.
- Hosted or self-hosted — use the managed service, or run the whole stack yourself with Docker.
- Bring your own LLM — Anthropic Claude or Groq.
How it works
Your question
→ Schema introspection (reads tables/columns, cached)
→ SQL generation (schema + question → LLM → SQL)
→ Safety validation (blocks writes, scans dangerous patterns, injects LIMIT)
→ Execution (runs read-only, with a timeout)
→ Self-correction retry (on error: feed it back to the LLM, fix, re-run)
→ Structured JSON result
Architecture
<p align="center"> <img src="docs/diagram/plainquery-architecture.png" alt="PlainQuery architecture diagram showing clients, Next.js frontend, Starlette/FastAPI backend, FastMCP endpoint, query pipeline, LLM provider, and customer database" width="900"> </p>
Quick start
Option 1 — Use the hosted service (no install)
- Sign up at
https://plainquery.inand verify your email. - Connect your PostgreSQL database and create an API key.
- Add the server to your MCP client (see Connect your MCP client).
That's it — start asking questions.
Option 2 — Run it yourself
See Self-hosting & local development below.
Connect your MCP client
PlainQuery is published on the official MCP Registry as io.github.Jarvis-27/mcp-db-agent, so registry-aware clients can discover it directly.
To configure a client manually, point it at the MCP endpoint and pass your API key:
{
"mcpServers": {
"plainquery": {
"url": "https://plainquery.in/mcp",
"headers": { "X-API-Key": "mdbk_your_key_here" }
}
}
}
The backend can also generate ready-to-paste config for VS Code, Cursor, and generic HTTP clients — call POST /api/v1/account/setup-payloads (see Setup payloads). MCP clients can authenticate with an OAuth 2.1 bearer token or an API key, depending on MCP_AUTH_MODE.
Self-hosting & local development
Prerequisites
- Python 3.12+
uv- Node.js 20+ and
pnpmfor the frontend - At least one LLM API key (
ANTHROPIC_API_KEYorGROQ_API_KEY)
Backend
uv sync
cp .env.example .env
Edit .env with at least:
CREDENTIAL_ENCRYPTION_KEYSREGISTRATION_OPEN=true- one LLM provider key plus
LLM_PROVIDER
Run the backend:
uv run uvicorn src.app:app --reload --host 0.0.0.0 --port 8000
The backend mounts:
- REST API at
http://localhost:8000/api - MCP endpoint at
http://localhost:8000/mcp
Frontend
cd frontend
pnpm install
pnpm dev
Open http://localhost:3000, sign up, complete setup, link your OAuth identity via account settings, then connect an MCP client at http://localhost:8000/mcp. The client will complete the OAuth flow automatically, or you can use an API key in api_key_only / hybrid mode.
Runtime model
- Backend API: FastAPI/Starlette at
src.app:app - Frontend: Next.js app in
frontend/ - Auth: passwordless email verification and login links
- MCP auth: OAuth 2.1 bearer tokens (
oauth_only), API keys (api_key_only), or both (hybrid) — set viaMCP_AUTH_MODE - Setup payloads:
POST /api/v1/account/setup-payloads - Billing: Stripe Checkout, Customer Portal, and webhook-confirmed Free/Pro entitlements
The product model is single-account and user-scoped:
signup → verify email → connect database → link OAuth identity → use /mcp → upgrade with Stripe
Security
<p align="center"> <img src="docs/diagram/plainquery-security.png" alt="PlainQuery security diagram showing URL validation, SSRF protection, encrypted credential storage, MCP authentication, per-request isolation, SQL validation, rate limits, and controlled execution" width="900"> </p>
- User-supplied database URLs are validated against SSRF, path traversal, private IPs, and DNS rebinding before any connection attempt.
- Database URLs and LLM keys are Fernet-encrypted at rest, with support for key rotation.
- Generated SQL is validated before execution: single-statement guard, forbidden-function scan, dangerous-pattern scan, write/DDL block, table-existence check, and automatic
LIMITinjection. - Per-request context scoping prevents cross-tenant data leaks.
- Per-user rate limits and fallback-LLM quotas limit cost abuse.
Important environment variables
| Variable | Description |
|---|---|
AUTH_DATABASE_URL |
Auth/account database used by the hosted product |
CREDENTIAL_ENCRYPTION_KEYS |
Encrypts stored database URLs and other secrets |
REGISTRATION_OPEN |
Enables or disables public signup |
ANTHROPIC_API_KEY / GROQ_API_KEY |
LLM credentials for SQL generation |
LLM_PROVIDER |
Active provider name |
APP_BASE_URL |
Base URL used in setup payloads |
FRONTEND_BASE_URL |
Base URL used in email links |
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET |
Stripe API and webhook credentials |
STRIPE_PRO_PRICE_ID |
Stripe Price ID that maps to the Pro plan |
STRIPE_CHECKOUT_SUCCESS_URL / STRIPE_CHECKOUT_CANCEL_URL |
Optional Checkout redirect overrides |
STRIPE_CUSTOMER_PORTAL_RETURN_URL |
Optional Customer Portal return URL override |
See .env.example for the current full set.
API surface
Auth
POST /api/v1/auth/signupGET /api/v1/auth/verify-emailPOST /api/v1/auth/request-login-linkGET /api/v1/auth/exchange-login-linkPOST /api/v1/auth/logout
Account (session-authenticated)
All account routes use session token auth (x-session-token: <session-token> or Authorization: Bearer <session-token>):
GET /api/v1/accountGET /api/v1/account/statusPUT /api/v1/account/databaseGET /api/v1/account/api-keysPOST /api/v1/account/api-keysDELETE /api/v1/account/api-keys/{id}POST /api/v1/account/api-keys/{id}/rotatePOST /api/v1/account/setup-payloadsGET /api/v1/account/dashboardGET /api/v1/account/usage/recent
Billing
GET /api/v1/account/billingPOST /api/v1/account/billing/checkout-sessionPOST /api/v1/account/billing/portal-sessionPOST /api/v1/billing/webhook
Stripe webhooks are the source of truth for plan transitions. Checkout or
subscription activation moves a user to plan_code=pro; canceled, unpaid, or
past-due states restrict paid entitlements without deleting database setup.
OAuth MCP account linking (session-authenticated)
GET /api/v1/account/mcp-oauth/statusPOST /api/v1/account/mcp-oauth/startGET /api/v1/account/mcp-oauth/callbackDELETE /api/v1/account/mcp-oauth/link
MCP
POST /mcp- Auth: OAuth 2.1 bearer token, API key, or both — controlled by
MCP_AUTH_MODE
Setup payloads
POST /api/v1/account/setup-payloads returns client configuration material for VS Code, Cursor, generic HTTP MCP clients, and the current ChatGPT placeholder.
curl -X POST http://localhost:8000/api/v1/account/setup-payloads \
-H "Authorization: Bearer <session-token>" \
-H "Content-Type: application/json" \
-d '{"raw_api_key":"mdbk_..."}'
The backend never stores raw API keys after creation. A raw key is only embedded in setup payloads when you explicitly send it in the request.
Tests
uv run pytest tests/ -m "not integration"
uv run pytest tests/ -m integration
uv run ruff check .
uv run mypy src --ignore-missing-imports
Deployment smoke test
After each hosted deploy, verify the public MCP OAuth discovery surface:
uv run python scripts/smoke_mcp_deployment.py \
--mcp-url https://mcp.example.com/mcp \
--issuer-url https://YOUR_DOMAIN.auth0.com/
The smoke test checks the unauthenticated /mcp challenge, protected resource
metadata, ChatGPT/Claude well-known discovery routes, and issuer metadata. To
also verify authenticated MCP initialize, tools/list, and resources/list,
pass a linked user's access token with --access-token or
MCP_SMOKE_ACCESS_TOKEN.
Docker
Run the hosted HTTP stack:
docker compose up --build
The container image serves only the hosted HTTP runtime. The MCP endpoint remains http://localhost:8000/mcp.
License
MIT © 2026 PlainQuery
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。