FastAPI Database Server
БесплатноНе проверенProvides read-only SQL query access to Postgres and DuckDB databases via MCP tools, with extensive security hardening for public endpoints.
Описание
Provides read-only SQL query access to Postgres and DuckDB databases via MCP tools, with extensive security hardening for public endpoints.
README
Read-only SQL query API for Postgres and DuckDB, exposed as MCP tools for AI clients.
Live endpoint: db-mcp.tigzig.com
Security Hardening
This server is designed to run with a public, open MCP endpoint (/mcp) — no API key, no auth. A separate secured endpoint (/mcp-secure) uses Auth0 OAuth for controlled access. The open endpoint relies entirely on the defense stack below.
Defense Layers
- Edge rate limiting — Cloudflare WAF or equivalent, configured per your needs (recommended before traffic hits origin)
- Application rate limiting — per-IP and global rate limits via SlowAPI (configurable via env vars)
- Per-IP concurrency cap — limits simultaneous in-flight queries per IP (default: 4)
- Global concurrency cap — limits total simultaneous queries server-wide (default: 10)
- SQL prefix allowlist — only SELECT, WITH, SHOW, DESCRIBE, EXPLAIN allowed
- SQL keyword blocklist — INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE, EXEC, COPY, GRANT, REVOKE, and 20+ more blocked (hardcoded in
BLOCKED_KEYWORDS) - Resource exhaustion blocking — CROSS JOIN, REPEAT, REGEXP_REPLACE, MD5, SHA256 blocked to prevent CPU/bandwidth bombs
- SQL parser structural validation (sqlglot) — regex-based checks cannot understand SQL structure (CTEs, aliases, nested subqueries). The sqlglot parser builds an AST (Abstract Syntax Tree) and validates structurally: blocks multiple table sources in a single SELECT (catches cartesian products via
FROM a, b, CTE alias bypass likeWITH t AS (...) SELECT FROM t a, t b, and aliased self-joins), detects blocked functions even when nested or aliased. Supports both Postgres and DuckDB dialects. Falls back to regex-only mode gracefully if sqlglot is not installed. - SQL comment rejection —
--and/*blocked to prevent comment-based validation bypass - System catalog blocking — pg_catalog, information_schema, pg_stat, duckdb_tables(), and other system objects blocked (hardcoded in
BLOCKED_PG_SOURCESandBLOCKED_DUCKDB_SOURCES) - Auto-append LIMIT — queries without an outer LIMIT automatically get one (configurable via
MAX_JSON_ROWS/MAX_TSV_ROWS) - Response size limit — responses exceeding the byte limit are rejected with HTTP 413 (configurable via
MAX_RESPONSE_BYTES, default: 1MB) - ORDER BY validation — function calls in ORDER BY blocked except whitelisted aggregates; subqueries in ORDER BY blocked
- Subquery depth limit — max 3 SELECT keywords per query (hardcoded)
- Data-generating function blocking — GENERATE_SERIES, RANGE, UNNEST blocked (hardcoded)
- Query timeouts — configurable timeout on both Postgres and DuckDB, queries exceeding this are killed (default: 15s)
- DuckDB query interrupt — on timeout,
connection.interrupt()kills the C++ engine query, not just the Python coroutine - Postgres read-only mode —
default_transaction_read_only = on+ dedicated read-only database user - DuckDB read-only mode —
read_only=True+enable_external_access=false - DuckDB resource limits — memory and thread limits (configurable via env vars, defaults: 512MB, 2 threads)
- Container resource limits — Docker/container-level RAM and CPU caps (configured in your hosting platform)
- Error message sanitization — generic error messages returned, no internal details leaked
- Auth0 OAuth on
/mcp-secure— JWT verification (RS256), audience/issuer validation, email whitelist (optional, enabled via env vars) - Failed-auth rate limiter — in-memory counter blocks IPs after repeated failed JWT attempts on
/mcp-secure(configurable viaAUTH_FAIL_MAXandAUTH_FAIL_WINDOW)
What's Hardcoded vs Configurable
Hardcoded in code (edit app.py to change):
- SQL keyword blocklist (
BLOCKED_KEYWORDS) - System catalog blocklists (
BLOCKED_PG_SOURCES,BLOCKED_DUCKDB_SOURCES) - Allowed SQL prefixes (SELECT, WITH, SHOW, DESCRIBE, EXPLAIN)
- ORDER BY allowed functions (SUM, COUNT, AVG, MIN, MAX, COALESCE, NULLIF, CASE)
- Subquery depth limit (3)
- sqlglot structural checks (multi-table per SELECT, blocked functions via AST)
Configurable via environment variables (see table below):
- Rate limits, concurrency caps, query timeouts, row limits, response size limit, connection pool settings, Auth0 config
Database-Level Hardening (Postgres)
If your backend connects to a hosted Postgres (Supabase, Neon, etc.), you should also harden at the database level:
- Enable RLS on all tables
- Revoke default grants from anon/authenticated roles
- Use a dedicated read-only database user (SELECT-only grants on specific tables)
- Set
default_transaction_read_only = onon application roles - Set
statement_timeouton application roles (matches yourPG_STATEMENT_TIMEOUT_MS) - Add indexes on columns used in WHERE, GROUP BY, ORDER BY for large tables
DuckDB-Level Hardening
DuckDB runs in-process. The server opens it with read_only=True and enable_external_access=false, and sets memory_limit and threads to prevent a single query from consuming all resources. On timeout, connection.interrupt() + connection.close() kills the query at the C++ engine level.
What It Does
Two databases, two endpoints, one MCP server:
- Postgres (Supabase) — ~1M rows of ODI cricket ball-by-ball data (2013-2025)
- DuckDB — ~1M rows of T20 cricket ball-by-ball data (2013-2025)
Both tables have identical schemas (23 columns) covering match details, player info, runs, extras, and dismissals.
The endpoints are mounted as MCP tools via fastapi-mcp, so any MCP-compatible AI client (Claude Code, Claude Desktop, etc.) can connect and query directly.
Endpoints
| Method | Path | Description |
|---|---|---|
| POST | /api/query/postgres |
SQL query on ODI data (Supabase) |
| POST | /api/query/duckdb |
SQL query on T20 data (DuckDB) |
| GET | /health |
Health check with DB connectivity status |
| GET | /mcp |
MCP SSE endpoint for AI clients (open, no auth) |
| GET | /mcp-secure |
MCP SSE endpoint with Auth0 OAuth (secured) |
Query Format
{
"sql": "SELECT striker, SUM(runs_off_bat) as runs FROM ball_by_ball WHERE season = '2023' GROUP BY striker ORDER BY runs DESC LIMIT 10",
"format": "json"
}
Set "format": "tsv" for compact tab-delimited output (~70% fewer tokens).
Connecting as MCP Client
Claude Code
claude mcp add --transport sse db-mcp https://your-server.com/mcp
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"db-mcp": {
"type": "sse",
"url": "https://your-server.com/mcp"
}
}
}
Local Development
git clone https://github.com/amararun/shared-fastapi-database-mcp.git
cd shared-fastapi-database-mcp
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
cp .env.example .env
# Edit .env with your database connection strings
uvicorn app:app --host 0.0.0.0 --port 8000
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
SUPABASE_POSTGRES |
Yes | — | Postgres connection string (use a read-only user) |
DUCKDB_FILE |
Yes | ./data/t20_cricket.duckdb |
Path to .duckdb file |
RATE_LIMIT |
No | 30/minute |
Per-IP rate limit (SlowAPI format, e.g. 60/hour, 100/minute) |
GLOBAL_RATE_LIMIT |
No | 200/minute |
Global rate limit across all IPs |
PG_STATEMENT_TIMEOUT_MS |
No | 15000 |
Postgres query timeout in milliseconds |
DUCKDB_QUERY_TIMEOUT_MS |
No | 15000 |
DuckDB query timeout in milliseconds |
MAX_JSON_ROWS |
No | 1000 |
Max rows returned in JSON format |
MAX_TSV_ROWS |
No | 1000 |
Max rows returned in TSV format |
MAX_RESPONSE_BYTES |
No | 1048576 |
Max response size in bytes (default: 1MB) |
MAX_CONCURRENT_PER_IP |
No | 4 |
Max simultaneous queries per IP |
MAX_CONCURRENT_GLOBAL |
No | 10 |
Max simultaneous queries server-wide |
PG_POOL_MIN |
No | 3 |
Postgres connection pool minimum size |
PG_POOL_MAX |
No | 6 |
Postgres connection pool maximum size |
PG_POOL_ACQUIRE_TIMEOUT |
No | 15 |
Seconds to wait for a pool connection before returning 503 |
DUCKDB_MEMORY_LIMIT |
No | 512MB |
DuckDB memory limit |
DUCKDB_THREADS |
No | 2 |
DuckDB thread limit |
DUCKDB_TEMP_DIR |
No | /tmp/duckdb |
DuckDB temporary directory |
DUCKDB_MAX_TEMP_DIR_SIZE |
No | 2GB |
DuckDB temp directory size cap |
CORS_ALLOW_ORIGINS |
No | * |
Comma-separated allowed origins |
LOG_LEVEL |
No | INFO |
Logging level |
AUTH0_DOMAIN |
No | — | Auth0 tenant domain (enables /mcp-secure when set) |
AUTH0_AUDIENCE |
No | — | Auth0 API identifier |
AUTH0_CLIENT_ID |
No | — | Auth0 application client ID |
AUTH0_CLIENT_SECRET |
No | — | Auth0 application client secret |
AUTH_FAIL_MAX |
No | 5 |
Max failed auth attempts per IP before blocking |
AUTH_FAIL_WINDOW |
No | 86400 |
Failed auth tracking window in seconds (default: 24 hours) |
API_MONITOR_URL |
No | — | tigzig-api-monitor endpoint URL |
API_MONITOR_KEY |
No | — | tigzig-api-monitor API key |
RENDER_EXTERNAL_URL |
No | — | Base URL for MCP (auto-detected on Render) |
Auth0 OAuth (Secured Endpoint)
The /mcp-secure endpoint adds Auth0 OAuth on top of all existing security layers. It is optional — if AUTH0_DOMAIN is not set, only the open /mcp endpoint is mounted.
How It Works
- MCP client discovers OAuth metadata at
/.well-known/oauth-authorization-server - Client redirects user to Auth0 login
- Auth0 authenticates user, checks email whitelist, issues JWT
- Client sends JWT as Bearer token with every request
- Server validates JWT signature (RS256 via JWKS), audience, and issuer
Auth0 Setup
- Create an Auth0 API with your server URL as the identifier (audience)
- Create an Auth0 Application (Regular Web Application)
- Add your MCP client's callback URLs (e.g.,
https://claude.ai/api/mcp/auth_callback) - Create a post-login Action with an email whitelist
- Set the AUTH0 environment variables on the server
Connecting to the Secured Endpoint
Claude.ai (Web) — Settings > Connectors > Add custom connector:
- URL:
https://your-server.com/mcp-secure - OAuth Client ID: your Auth0 app's client ID
Claude Desktop:
{
"mcpServers": {
"db-mcp-secure": {
"command": "npx",
"args": ["mcp-remote", "https://your-server.com/mcp-secure", "8080"]
}
}
}
Demo Limitations
- JWKS cache does not auto-refresh (refresh on restart only)
- No token scope validation (any valid token grants access to all tools)
- Fake Dynamic Client Registration (returns pre-configured credentials for MCP spec compatibility)
- No token revocation handling (tokens accepted until expiry)
Stack
- FastAPI + uvicorn
- asyncpg (Postgres connection pool)
- DuckDB (read-only, thread-pool executor)
- fastapi-mcp v0.4.0 (MCP server mounting, OAuth support)
- sqlglot (SQL parser for structural validation)
- python-jose (JWT verification)
- SlowAPI (rate limiting)
Author
Built by Amar Harolikar
Explore 30+ open source AI tools for analytics, databases & automation at tigzig.com
License
MIT License
Установка FastAPI Database Server
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/amararun/shared-fastapi-database-mcpFAQ
FastAPI Database Server MCP бесплатный?
Да, FastAPI Database Server MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для FastAPI Database Server?
Нет, FastAPI Database Server работает без API-ключей и переменных окружения.
FastAPI Database Server — hosted или self-hosted?
Доступен hosted-вариант: Unyly запускает сервер в облаке, локальная установка не обязательна.
Как установить FastAPI Database Server в Claude Desktop, Claude Code или Cursor?
Открой FastAPI Database Server на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
wenb1n-dev/SmartDB_MCP
A universal database MCP server supporting simultaneous connections to multiple databases. It provides tools for database operations, health analysis, SQL optim
автор: wenb1n-devPostgres Server
This server enables interaction with PostgreSQL databases through the Model Context Protocol, optimized for the AWS Bedrock AgentCore Runtime. It provides tools
автор: madhurprashPostgres
Query your database in natural language
автор: AnthropicPostgreSQL
Read-only database access with schema inspection.
автор: modelcontextprotocolCompare FastAPI Database Server with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории data
