Universal Database MCP
Allows querying a MariaDB database safely with read-only access, role-based permissions, and data masking.
Allows querying a MySQL database safely with read-only access, role-based permissions, and data masking.
Allows querying a PostgreSQL database safely with read-only access, role-based permissions, and data masking.
Allows querying a SQLite database safely with read-only access, role-based permissions, and data masking.
Click on "Install 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., "@Universal Database MCPShow me the schema of the users table in the prod database."
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.
Anvaya Labs — Universal Database MCP (Simple Version)
A small MCP server that lets an LLM query PostgreSQL, MySQL, MariaDB, SQL Server, or SQLite databases safely — read-only, role-restricted, and with sensitive data blacked out.
Files (4 files, ~650 lines total, no classes/decorators/async)
config.py— plain dictionaries: who can see what, which columns get masked. Edit this file to change any rule.security.py— 4 plain functions: check it's read-only, check the role is allowed, add tenant filtering, mask sensitive values.db_engine.py— 3 plain functions: get schema, run query, explain query. Talks to the actual database.server.py— the 4 tools the LLM can call, each just a try/except around the functions above, with a log line either way.
Related MCP server: mcp-multi-db
Keeping database passwords out of chat
db_uri no longer has to be a raw connection string typed into the
conversation. Instead:
Copy
.env.exampleto.envand fill in real connection strings there.In chat, refer to a database by its short name — e.g. "query the
proddatabase" — and passdb_uri="prod". The server looks upANVAYA_DB_PRODin the environment and uses that.Leaving
db_uriempty ("") usesANVAYA_DEFAULT_DB_URIinstead.
A full connection string (containing ://) still works directly if you
pass one — useful for quick local testing — but the recommended pattern
is to never type a real password into the chat at all.
Authentication (for remote/multi-team deployments)
Since other people connect to this server, role can no longer be
trusted as something the LLM just tells the server — anyone could type
role="admin". Instead:
Generate a secret once and put it in
.env:python -c "import secrets; print(secrets.token_hex(32))"ANVAYA_JWT_SECRET=<paste it here> ANVAYA_MCP_TRANSPORT=httpWhenever someone needs access, mint them a signed token:
uv run python issue_token.py --name alice --role analyst --tenant acmeGive that token to them. Their MCP client connects with it as a Bearer token. The server verifies the signature on every call and uses the role/tenant from the token — any
role/tenant_idthey try to pass as arguments is ignored.
If ANVAYA_JWT_SECRET isn't set, the server runs with no auth at all
(fine for local stdio testing on your own machine, not for anything
reachable by other people).
Transport: this uses Streamable HTTP (transport="http"), the current
MCP standard for remote servers — SSE is deprecated as of the 2025-11-25
MCP spec revision.
Deploying for free (Render) so claude.ai (web) can reach it
claude.ai connects to remote MCP servers from Anthropic's cloud, not
from your browser — so this needs a real public HTTPS URL. Local
network / VPN-only hosting won't work for the web client (Claude
Desktop's local stdio config is different and stays working as-is).
Render gives you that for free, with auto-deploy
on every git push.
1. Push this project to a GitHub repo
git init
git add .
git commit -m "Anvaya Universal Database MCP"
git remote add origin https://github.com/YOUR_USERNAME/anvaya-labs-mcp.git
git push -u origin main2. Create the Render service
Go to render.com → sign up (no credit card needed for the free tier) → New → Web Service
Connect your GitHub repo
Settings:
Runtime: Python 3
Build Command:
pip install -r requirements.txtStart Command:
python server.pyInstance Type: Free
3. Add environment variables (Render dashboard → Environment)
ANVAYA_JWT_SECRET=<your generated secret>
ANVAYA_MCP_TRANSPORT=http
ANVAYA_DEFAULT_DB_URI=sqlite:///test.db(test.db is committed in this repo as a small demo database — Render's
free tier has no persistent disk, so a database that's part of the
codebase is what survives redeploys. For real data, point
ANVAYA_DEFAULT_DB_URI at an externally hosted database instead —
e.g. a free Postgres from Neon — rather than SQLite.)
4. Deploy
Render builds and deploys automatically. You'll get a URL like
https://anvaya-labs-mcp.onrender.com. Every future git push to
this repo redeploys automatically — no extra steps needed.
Two honest limitations of the free tier:
The service sleeps after 15 minutes of no traffic, and the first request after that takes 30-50 seconds to wake up — the very first tool call after idling may feel slow or briefly time out.
No persistent disk, as noted above — anything written to disk at runtime disappears on the next restart or deploy.
5. Issue a token and add the connector in claude.ai
uv run python issue_token.py --name alice --role analyst --tenant acmeThen in claude.ai: Settings → Connectors → Add custom connector
URL:
https://anvaya-labs-mcp.onrender.com/mcpOpen Request headers (this is a beta feature — if you don't see it, it may not be rolled out to your account yet)
Header name:
authorizationHeader value:
Bearer <the token you issued>
Testing with realistic data across all roles
generate_test_data.py builds a bigger test.db — 4 tables, 30 rows
each, spread across 3 tenants (acme, globex, initech) — designed
to exercise every rule in ROLE_PERMISSIONS:
uv run python generate_test_data.pyThen run the full role test battery:
uv run python test_all_roles.pyThis checks things like: admin can reach every table but still can't
write; analyst gets auto-scoped to one tenant and can't see
password_hash; support can't see payments at all; readonly_guest
can only ever see products. One thing worth knowing: masking is
unconditional — even admin sees email/card_number redacted, since
masking isn't role-aware in this version, only RBAC table/column access
is.
If you change test.db, remember to git push so Render picks up the
new version on its next deploy (it has no persistent disk, so the
committed file is what it actually serves).
Before shipping to a client
Two things worth doing every time, before a client's database is connected for real:
1. Error messages no longer leak internals. Unexpected errors (a bad connection string, a driver failure, anything not deliberately raised by our own RBAC/validation code) now return only a generic message plus a short reference code — the real detail goes only to the server-side log (stderr), tagged with that same code. Our own deliberate messages (like "Role 'analyst' is not allowed to access column 'x'") are unaffected and still show clearly, since those are safe by design.
2. Check your RBAC config against the real database before go-live:
uv run python validate_config.py <db_uri or connection name>This catches table/column name mismatches between config.py's
ROLE_PERMISSIONS and whatever database you actually point it at —
e.g. a table you renamed but forgot to update in the config, or a
table nobody remembered to explicitly allow or deny for a given role.
It exits non-zero if it finds a real mismatch, so it's safe to wire
into a pre-deploy check if you want.
OAuth mode (for claude.ai web, which only supports OAuth connectors)
Bearer tokens (above) work great for Claude Desktop and Claude Code, but claude.ai in a browser currently only offers OAuth as a connector auth option (a "Request headers" beta exists but isn't available to every account yet). For that, this project includes a full, self-hosted OAuth 2.1 authorization server — a real login page, not just a token check.
Read this before using it in production: building your own OAuth
server is something FastMCP's own documentation says most people
shouldn't do — it's included here because claude.ai web specifically
requires it and no external identity provider was wanted. The
security-critical cryptography (PKCE verification, redirect URI
validation) is handled by the underlying MCP SDK, not custom code here
— but you're still responsible for everything else (the login page,
code/token bookkeeping, who's allowed to log in). See the limitations
listed at the top of oauth_provider.py.
1. Turn it on
ANVAYA_JWT_SECRET=<your secret>
ANVAYA_AUTH_MODE=oauth
ANVAYA_MCP_TRANSPORT=http
ANVAYA_PUBLIC_BASE_URL=https://your-real-public-url.onrender.com2. Add people who are allowed to log in
uv run python manage_oauth_users.py add --username alice --role analyst --tenant acme
uv run python manage_oauth_users.py list
uv run python manage_oauth_users.py remove --username aliceYou'll be prompted for a password (not shown in your terminal history).
This creates oauth_users.json locally — never commit this file (it's
already in .gitignore).
3. Add the connector in claude.ai
Settings → Connectors → Add custom connector — just the URL:
https://your-real-public-url.onrender.com/mcpclaude.ai discovers everything else automatically (it registers itself as an OAuth client, then redirects you to your server's login page). When you connect, you'll see the login form built into this project — sign in with a username/password from step 2.
What you get, and what you don't
✅ Real login, on claude.ai web, with role/tenant decided by who logged in — not a header anyone could paste in.
✅ PKCE, redirect URI validation, and authorization-code replay protection all verified working (see the test files this was built and checked against).
❌ No refresh tokens — when the 24-hour access token expires, the person just logs in again.
❌ Registered OAuth clients and issued codes are in-memory — a server restart clears them (claude.ai will just re-register/re-login automatically next time it connects).
Setup
uv sync
uv run anvaya-mcpThe 4 tools
get_database_schema(db_uri, role)execute_safe_query(db_uri, sql_query, role, tenant_id)explain_sql_query(db_uri, sql_query)export_results_format(db_uri, sql_query, role, format_type, tenant_id)
Roles
Defined in config.py under ROLE_PERMISSIONS: admin, analyst,
support, readonly_guest. Edit that dictionary to add roles, change
which tables/columns they can see, or turn tenant filtering on/off.
What got simplified from the first version
Sync database calls instead of async (easier to read top-to-bottom)
No decorators, dataclasses, or Enums — just dicts and functions
Row-level tenant filtering only supports a single
tenant_idcolumn (not multiple candidate column names)Masking is column-name-based only (no scanning cell contents for patterns like card numbers)
Connections and schema are cached (see db_engine.py — ENGINE_CACHE
and SCHEMA_CACHE, two plain dictionaries), so repeated calls reuse the
same connection instead of opening a new one every time, and don't
re-fetch the schema on every call. The schema cache refreshes itself
every 5 minutes, or immediately if you pass force_refresh=True.
Everything from your original feature list is still here — schema discovery, relationship mapping, read-only enforcement, EXPLAIN, table/column RBAC, row-level filtering, data masking, CSV/Excel/JSON export, and audit logging — just written as plainly as possible.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-qualityBmaintenanceA read-only MCP server for MS SQL Server, written in Python. Lets AI agents query your database safely — no writes, no stored procedures, no shell access.Last updated
- AlicenseAqualityAmaintenanceRead-only MCP server for querying PostgreSQL, MySQL, and SQLite from AI agents — multi-database, safe by default.Last updated4181ISC
- Flicense-qualityFmaintenanceA read-only MCP server that enables AI agents to explore database schemas and execute safe queries on PostgreSQL and MySQL.Last updated
- Alicense-qualityCmaintenanceA read-only MCP server that enables LLMs to safely explore and query any SQLite database via natural language. It exposes tools for listing tables, describing schemas, and executing SELECT/WITH queries with built-in safety guards like write prevention and row limits.Last updatedMIT
Related MCP Connectors
GibsonAI MCP server: manage your databases with natural language
Query PostgreSQL databases in plain English — LLM-generated, safety-validated SQL.
Analytical memory for AI agents: a real Postgres queried in plain English over MCP. One command.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/jaynasriwala/Universal-Database-MCP'
If you have feedback or need assistance with the MCP directory API, please join our Discord server