
YugabyteDB MCP Server
An MCP server for YugabyteDB and PostgreSQL โ lets LLMs (Claude Desktop, Cursor, Windsurf, etc.) summarize schemas, run read-only queries, and execute write statements behind a configurable guardrail layer.
Features
summarize_databaseโ list tables with columns and row counts for a schema (read-only)run_read_only_queryโ execute a SELECT underBEGIN READ ONLY; results returned as JSON (read-only)run_write_queryโ INSERT/UPDATE/DELETE/MERGE/TRUNCATE/DDL gated by a guardrail blocklist (destructive, disabled by default โ enable with--enable-write-queryorYB_MCP_ENABLE_WRITE_QUERY=true)
Defense in depth: the write tool is annotated destructiveHint: true, so Claude Desktop surfaces a confirmation prompt before every call even when the guardrails would let the statement through.
Optional OAuth (AWS Cognito) and Origin-header validation for self-hosted remote deployments.
Other MCP Clients
The same approach works with Cursor (Settings โ MCP โ Add a new global MCP server) and Windsurf (Settings โ Cascade โ MCP Servers โ Add custom server) โ use either the uvx form or the installed-script form from above.
For MCP Inspector against an HTTP-mode server:
YUGABYTEDB_URL="โฆ" yugabytedb-mcp --transport http
# in another shell:
npx @modelcontextprotocol/inspector
# In the GUI: URL http://localhost:8000/mcp, transport Streamable-HTTPTools
| Tool | Title | Hints | What it does |
|---|---|---|---|
summarize_database(schema='public') | "Summarize database schema and row counts" | readOnlyHint: true | Lists tables in schema with columns and row counts |
run_read_only_query(query) | "Run a read-only SQL query" | readOnlyHint: true | Wraps the query in BEGIN READ ONLY and returns rows as JSON |
run_write_query(query) | "Run a write SQL query (with guardrails)" | destructiveHint: true | Validates the query against the guardrail blocklist, then executes. Disabled by default โ requires --enable-write-query. |
Guardrails for run_write_query
The following statement classes are rejected before execution:
DROP DATABASE/SCHEMA,ALTER DATABASE,CREATE DATABASE- Role/privilege ops:
GRANT,REVOKE,CREATE/ALTER/DROP ROLE,CREATE/ALTER/DROP USER - Filesystem / code execution:
COPY TO/FROM,LOAD, anonymousDO $$ โฆ $$,CREATE EXTENSION - Server config:
ALTER SYSTEM,RESET ALL - Dangerous built-ins:
pg_sleep,pg_read_file,pg_write_file,lo_import,lo_export,dblink - Schema isolation:
SET search_path,CREATE SCHEMA - Multi-statement queries (anything with a separator semicolon)
psqlmeta-commands (\c,\d,\!)- INSERT โฆ VALUES over
YB_MCP_MAX_INSERT_ROWS - Optionally UPDATE / DELETE without a WHERE clause
This list is best-effort, not exhaustive. destructiveHint: true is the second line of defense.
Self-hosted remote mode
For multi-user or shared deployments, run the server as Streamable HTTP behind a reverse proxy with TLS, with Cognito OAuth gating access:
export MCP_AUTH_PROVIDER=cognito
export MCP_BASE_URL=https://mcp.example.com
export COGNITO_USER_POOL_ID=us-west-2_XXXXXXXX
export COGNITO_AWS_REGION=us-west-2
export COGNITO_CLIENT_ID=โฆ
export COGNITO_CLIENT_SECRET=โฆ
export YUGABYTEDB_URL=โฆ
export MCP_ALLOWED_ORIGINS=https://mcp.example.com,https://claude.ai
yugabytedb-mcp --transport http --stateless-httpBehavior:
- Requests to
/mcpwithout a valid Bearer token return 401. - Requests with a disallowed
Originheader return 403 (DNS-rebinding defense). /pingis unauthenticated and is suitable for liveness probes./auth/loginexposes a Cognito email+password โ token shortcut (see below).--stateless-httpis required for multi-replica deployments โ without it, MCP session state lives in process memory and round-robin load balancing breaks sessions.
Getting a token without a browser
The browser-based OAuth flow is what Claude Desktop / mcp-remote / claude.ai use, but for curl-based smoke tests, CI, or any scripted client, the HTTP transport also exposes a direct password endpoint when MCP_AUTH_PROVIDER=cognito:
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"user@example.com","password":"โฆ"}'
# โ
# {"access_token":"โฆ","id_token":"โฆ","refresh_token":"โฆ","expires_in":86400,"token_type":"Bearer"}Then:
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Accept: application/json,text/event-stream" \
-X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'The endpoint uses Cognito's USER_PASSWORD_AUTH flow under the hood โ the Cognito app client must have ALLOW_USER_PASSWORD_AUTH enabled (Cognito Console โ User pools โ App integration โ App client settings). MFA, password-reset, and other challenge flows are not handled by this endpoint โ they require the browser flow.
OIDC (MCP_AUTH_PROVIDER=oidc) wiring exists but is untested; please share findings if you exercise it.
Per-user database roles (SET ROLE)
When OIDC auth is enabled, the server can map each authenticated user to a YugabyteDB database role using SET ROLE. This enforces row-level security, schema-level grants, and per-user privilege boundaries โ the same connection pool is used, but each tool call runs under the caller's database role.
export YB_MCP_IDENTITY_CLAIM=email # JWT claim to extract (default)
export YB_MCP_IDENTITY_TRANSFORM=strip_domain # alice@example.com โ aliceHow it works:
- The server extracts the configured claim from the OIDC access token.
- An optional transform derives the DB role name (e.g., stripping
@domain). - Before executing tool SQL, the server issues
SET ROLE <role>on the pooled connection. - After execution,
RESET ROLErestores the connection to the pool user.
Requirements:
- The pool user (from
YUGABYTEDB_URL) must be a superuser or haveGRANT <target_role> TO <pool_user>for every role that can authenticate. - Target roles must already exist in the database โ the server does not create them.
- When auth is disabled (no
MCP_AUTH_PROVIDER), SET ROLE is not used and the pool operates with its configured credentials as before.
Example setup:
-- Create per-user roles matching the identity claim
CREATE ROLE alice LOGIN;
CREATE ROLE bob LOGIN;
-- Grant them to the pool user so SET ROLE works
GRANT alice TO mcp_admin;
GRANT bob TO mcp_admin;
-- Apply per-role permissions as needed
GRANT SELECT ON ALL TABLES IN SCHEMA public TO alice;
GRANT ALL ON ALL TABLES IN SCHEMA public TO bob;AWS Secrets Manager for TLS certificates
If your database TLS root certificate is stored in AWS Secrets Manager, the server can fetch and use it automatically. Plaintext PEM is supported; JSON-keyed bundles too (set YB_AWS_SSL_ROOT_CERT_KEY to pick one).
yugabytedb-mcp \
--yugabytedb-url "host=โฆ port=5433 dbname=โฆ user=โฆ password=โฆ sslmode=verify-full" \
--yb-aws-ssl-root-cert-secret-arn arn:aws:secretsmanager:us-east-1:โฆ:secret:my-cert \
--yb-aws-ssl-root-cert-secret-region us-east-1Docker
docker build -t mcp/yugabytedb .
docker run -p 8000:8000 -e YUGABYTEDB_URL="โฆ" mcp/yugabytedb yugabytedb-mcp --transport httpSecurity
- All SQL is run through parameterized queries; user input is never interpolated into statement strings.
- The write tool is disabled by default โ must be explicitly enabled with
--enable-write-query. - The write tool's guardrail list (above) blocks the highest-risk statement classes.
destructiveHint: trueensures Claude Desktop surfaces a per-call confirmation for write operations.- When OIDC auth is active, per-user
SET ROLEenforces database-level privilege boundaries per caller. Role names are safely quoted withpsycopg.sql.Identifier. - HTTP transport requires a valid Bearer token when
MCP_AUTH_PROVIDERis configured. - HTTP transport validates the
Originheader againstMCP_ALLOWED_ORIGINS(defaults toMCP_BASE_URL). - HTTPS is the operator's responsibility โ terminate TLS at a reverse proxy (nginx, ALB, etc.) in front of the server.
- Run with a least-privilege database role (read-only role for
run_read_only_query-only deployments; otherwise a role scoped to the target schemas, no superuser).
Report security issues privately to support@yugabyte.com โ please do not open public GitHub issues for vulnerabilities.
Privacy Policy
Yugabyte's privacy policy applies: https://www.yugabyte.com/privacy-policy/
This MCP server does not transmit telemetry. All database access stays between Claude (your MCP client) and your YugabyteDB instance via the connection string you provide. The server logs locally to stderr (controlled by YB_LOG_LEVEL) โ no remote log aggregation is built in.
Development
git clone git@github.com:yugabyte/yugabytedb-mcp-server.git
cd yugabytedb-mcp-server
uv sync
uv run yugabytedb-mcp --helpNote: there is no longer a src/server.py you can run directly. The package layout was reorganized for PyPI distribution (entry point + namespace) so the modules now live under src/yugabytedb_mcp_server/. Always invoke via the yugabytedb-mcp console script (registered by uv sync / pip install) โ running the module file with python would skip the package import machinery and break the relative imports.
Equivalent commands:
uv run yugabytedb-mcp # uses the console script
uv run python -m yugabytedb_mcp_server # uses the __main__.py shimTesting the connector locally in Claude Desktop
Two paths, depending on how close to the production install experience you want to get:
Fastest โ no MCPB build, just point Claude Desktop at the local entry point. After uv sync, the yugabytedb-mcp script is on your $PATH (via the active venv). Add this to your claude_desktop_config.json:
{
"mcpServers": {
"yugabytedb-dev": {
"command": "/absolute/path/to/repo/.venv/bin/yugabytedb-mcp",
"env": {
"YUGABYTEDB_URL": "host=localhost port=5433 dbname=yugabyte user=yugabyte password=yugabyte",
"YB_LOG_LEVEL": "DEBUG"
}
}
}
}Restart Claude Desktop. Use ~/Library/Logs/Claude/mcp-server-yugabytedb-dev.log (macOS) to inspect debug output. This skips the MCPB bundling entirely and is the right loop for iterating on tool code.
Closer to production โ build a .mcpb and drag it into Claude Desktop. Requires the MCPB CLI:
npm install -g @modelcontextprotocol/mcpb-cli # one-time
mcpb validate manifest.json # static check
mcpb pack . # produces yugabytedb-mcp-server-<version>.mcpbDrag the resulting .mcpb into Claude Desktop โ the connector installer UI takes it from there, prompting for the user_config values defined in manifest.json. The .mcpb route is closest to what reviewers will exercise. Note: the manifest's mcp_config runs uvx yugabytedb-mcp-server, which fetches the package from PyPI on first launch. Make sure the version referenced by your .mcpb is published before sharing the bundle.
Testing
# unit tests (no DB, no network)
uv run pytest tests/test_guardrails.py tests/test_auth.py tests/test_identity_mapping.py
# integration tests (require a reachable Postgres-compatible DB)
YUGABYTEDB_URL="host=โฆ port=โฆ โฆ" uv run pytest tests/See tests/README.md for the coverage table and the manual Cognito smoke recipe.
# uvx โ no install at all; fetches and runs on demand. Handy for one-off use
# and also the form the MCPB Desktop extension uses internally.
uvx yugabytedb-mcp-server --help
# pipx โ installs to an isolated venv, puts the script on $PATH.
pipx install yugabytedb-mcp-server
# uv tool โ same idea, uv-managed.
uv tool install yugabytedb-mcp-server
# pip โ system-level or current-venv install.
pip install yugabytedb-mcp-serverBefore it works, you'll need: YUGABYTEDB_URL
Prerequisites
- Python 3.10+
- uv (recommended) or pip
- A reachable YugabyteDB or PostgreSQL database
- An MCP client (Claude Desktop, Cursor, Windsurf, etc.)
Installation
Three install options, in roughly the order of how end users will reach for them:
# uvx โ no install at all; fetches and runs on demand. Handy for one-off use
# and also the form the MCPB Desktop extension uses internally.
uvx yugabytedb-mcp-server --help
# pipx โ installs to an isolated venv, puts the script on $PATH.
pipx install yugabytedb-mcp-server
# uv tool โ same idea, uv-managed.
uv tool install yugabytedb-mcp-server
# pip โ system-level or current-venv install.
pip install yugabytedb-mcp-serverAfter any of the persistent installs (pipx / uv tool / pip), verify with:
yugabytedb-mcp --help
# or, equivalently:
yugabytedb-mcp-server --helpBoth console scripts are registered and point at the same entry point โ
yugabytedb-mcp is the short form, yugabytedb-mcp-server matches the
package name and is what uvx resolves to by default.
Pre-release note: while v2 is in release-candidate (e.g.
2.0.0rc2), default installs won't pick it up. For now, install with an explicit version (pipx install yugabytedb-mcp-server==2.0.0rc2) or with--pip-args='--pre'. This goes away once2.0.0stable is published.
For development from source, see Development below.
Configuration
| Environment Variable | CLI flag | Required | Description |
|---|---|---|---|
YUGABYTEDB_URL | --yugabytedb-url | Yes | libpq connection string (e.g. host=โฆ port=5433 dbname=โฆ user=โฆ password=โฆ). |
YB_MCP_TRANSPORT | --transport | No | stdio (default) or http. |
YB_MCP_STATELESS_HTTP | --stateless-http | No | true enables stateless Streamable-HTTP โ required for multi-replica self-hosted deployments. |
YB_MCP_MAX_INSERT_ROWS | --max-insert-rows | No | Reject INSERT โฆ VALUES with more rows than this. Default 1000. |
YB_MCP_REQUIRE_WHERE_ON_UPDATE | --require-where-on-update | No | Reject UPDATE without a WHERE clause. Default false. |
YB_MCP_REQUIRE_WHERE_ON_DELETE | --require-where-on-delete | No | Reject DELETE without a WHERE clause. Default false. |
YB_MCP_ENABLE_WRITE_QUERY | --enable-write-query | No | Enable the run_write_query tool. Default false (write tool disabled). |
MCP_AUTH_PROVIDER | --mcp-auth-provider | No | cognito (tested) or oidc (untested). Leave unset to disable auth. |
YB_MCP_IDENTITY_CLAIM | --identity-claim | No | JWT claim to use as the DB role identifier for per-user SET ROLE. Default email. |
YB_MCP_IDENTITY_TRANSFORM | --identity-transform | No | Transform applied to the claim before using as a DB role: none (default) or strip_domain (strips @โฆ from emails). |
MCP_BASE_URL | โ | When auth enabled | Public base URL the server is reachable at (e.g. https://mcp.example.com). |
MCP_ALLOWED_ORIGINS | โ | No | Comma-separated allowlist of Origin values for DNS-rebinding defense. Defaults to MCP_BASE_URL. |
YB_LOG_LEVEL | โ | No | Log level for the yugabytedb-mcp logger family (default INFO). |
YB_AWS_SSL_ROOT_CERT_SECRET_ARN | --yb-aws-ssl-root-cert-secret-arn | No | ARN of an AWS Secrets Manager secret holding the YugabyteDB TLS root certificate. |
YB_AWS_SSL_ROOT_CERT_KEY | --yb-aws-ssl-root-cert-key | No | JSON key inside the secret when it stores multiple certs. |
YB_AWS_SSL_ROOT_CERT_SECRET_REGION | --yb-aws-ssl-root-cert-secret-region | No | AWS region of the secret. |
YB_SSL_ROOT_CERT_PATH | --yb-ssl-root-cert-path | No | Where to write the fetched cert. Default /tmp/yb-root.crt. |
Cognito-specific env vars (only required when MCP_AUTH_PROVIDER=cognito):
| Variable | Description |
|---|---|
COGNITO_USER_POOL_ID | Cognito user-pool ID (e.g. us-west-2_XXXXXXXX) |
COGNITO_AWS_REGION | AWS region |
COGNITO_CLIENT_ID | App client ID |
COGNITO_CLIENT_SECRET | App client secret |
A starter template is in .env.example.
Quickstart โ Claude Desktop
Two ways to wire it up. The first uses uvx and requires no install at all
โ uv only. The second assumes you've already run pipx install (or
equivalent) and have the yugabytedb-mcp script on $PATH.
Option 1 โ via uvx (no install):
{
"mcpServers": {
"yugabytedb": {
"command": "uvx",
"args": ["yugabytedb-mcp-server"],
"env": {
"YUGABYTEDB_URL": "host=โฆ port=5433 dbname=โฆ user=โฆ password=โฆ"
}
}
}
}Option 2 โ via an installed script:
After pipx install yugabytedb-mcp-server (or uv tool install โฆ):
{
"mcpServers": {
"yugabytedb": {
"command": "yugabytedb-mcp",
"env": {
"YUGABYTEDB_URL": "host=โฆ port=5433 dbname=โฆ user=โฆ password=โฆ"
}
}
}
}Locations of claude_desktop_config.json:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Restart Claude Desktop. The three tools will appear with titles and hint badges (read-only icons on the read tools, a confirmation prompt before each run_write_query call).
While
2.0.0rc2is the only published version, theuvxsnippet needs["yugabytedb-mcp-server@2.0.0rc2"]in the args (or["--pre", "yugabytedb-mcp-server"]). Drop the explicit version once2.0.0stable is out.
Troubleshooting
spawn yugabytedb-mcp ENOENTfrom Claude Desktop โ ensure the install directory is on the PATH Claude Desktop sees;pipx ensurepathor symlink the entry point into/usr/local/bin.- Tools list is empty in the MCP client โ restart the client; check
YB_LOG_LEVEL=DEBUGoutput for connection errors during lifespan. - "Invalid or expired transaction" / "Client Not Registered" in HTTP+OAuth mode with multiple replicas โ see the self-hosted remote section;
--stateless-httpis mandatory for multi-replica.
Licensed under Apache-2.0โ you can use, modify, and redistribute it under that license's terms.