securedblink
MCP server that gives LLM agents read access to any database, with built-in gates for writes.
Installation
Mac / Linux
curl -fsSL https://raw.githubusercontent.com/paulushcgcj/securedblink/main/install.sh | bash
Windows (PowerShell)
irm https://raw.githubusercontent.com/paulushcgcj/securedblink/main/install.ps1 | iex
Via pip / uv (all platforms)
pip install securedblink
# or
uv tool install securedblink
macOS note: If you see a security warning on first run, clear the quarantine flag once:
xattr -d com.apple.quarantine /usr/local/bin/securedblink
Why securedblink
LLMs handle read-only database work: schema exploration, query writing, data analysis. A stray DELETE or DROP from an agent can wipe production data. securedblink draws a hard line: reads go through, writes require the agent to show you exactly what it plans to do and wait for your explicit approval.
Features
- Read queries run immediately:
SELECT,EXPLAIN,SHOW,DESCRIBE, andWITH(when safe). - Write and destructive queries go through a two-step confirmation: the agent previews the change, you approve it.
- Token-bound execution. Approval tokens encode the exact SQL and connection. Swap the query or target a different database, and the server rejects it.
- Named connections via
DB_<NAME>=<url>environment variables. No config files to manage. - Credential vault for secure storage. Register connections with aliases, and the MCP stores credentials in the system's secure credential manager. No credentials in env vars, logs, or tool responses.
- Any SQLAlchemy-compatible database: PostgreSQL, SQLite, Oracle, MySQL, SQL Server, Snowflake, or whatever you install the driver for.
Supported databases
| Database | URL format | Extra install |
|---|---|---|
| PostgreSQL | postgresql://user:pass@host:5432/db |
included |
| SQLite | sqlite:///./path/to/file.db |
included (built-in) |
| Oracle | oracle+oracledb://user:pass@host:1521/service |
[oracle] |
| MySQL | mysql+pymysql://user:pass@host:3306/db |
[mysql] |
| SQL Server | mssql+pyodbc://user:pass@host/db?driver=... |
[mssql] |
| Snowflake | snowflake://user:pass@account/db/schema |
snowflake-sqlalchemy |
Any other database works too. Install the right SQLAlchemy driver and use its URL format.
Quick start
# Clone the repo
git clone git@github.com:paulushcgcj/securedblink.git
cd securedblink
# Run with a local SQLite database
DB_LOCAL=sqlite:///./test.db ./run.sh
The server starts and your LLM tool can list tables, describe schemas, and run read queries against local.
Configure your connections
Set DB_<NAME>=<url> environment variables. The part after DB_ (lowercased) is the name you reference in prompts.
DB_PROD=postgresql://user:pass@db.internal:5432/production
DB_LOCAL=sqlite:///./dev.db
DB_MAX_ROWS=1000 # optional, default 500
Put these in a .env file if you run the server manually.
Run the server
run.sh is the recommended launcher. It syncs dependencies, detects which database drivers your DB_* env vars need, installs them if missing, and hands off to the MCP server.
# Run manually
DB_LOCAL=sqlite:///./test.db ./run.sh
# Or with a .env file
./run.sh
When your IDE launches securedblink, point it at run.sh instead of calling uv run securedblink directly. The script handles driver installation so you don't have to pip install extras like [oracle] or [mysql] by hand.
Connect your IDE
VS Code Copilot
Add to .vscode/mcp.json (workspace) or ~/.vscode/mcp.json (global):
{
"servers": {
"securedblink": {
"type": "stdio",
"command": "/absolute/path/to/securedblink/run.sh",
"env": {
"DB_PROD": "postgresql://user:pass@host:5432/mydb",
"DB_LOCAL": "sqlite:///./local.db",
"DB_MAX_ROWS": "500"
}
}
}
}
OpenCode
Add to ~/.config/opencode/opencode.jsonc or .opencode.json in your project:
{
"mcp": {
"securedblink": {
"type": "local",
"command": ["/absolute/path/to/securedblink/run.sh"],
"environment": {
"DB_PROD": "postgresql://user:pass@host:5432/mydb",
"DB_LOCAL": "sqlite:///./local.db",
"DB_MAX_ROWS": "500"
}
}
}
}
Tools
| Tool | Description |
|---|---|
list_connections |
List all configured DB connections (env + vault) |
list_tables |
List tables and views in a connection |
describe_table |
Show columns, PK, FKs, indexes for a table |
query |
Execute read-only SQL (SELECT, EXPLAIN, etc.) |
preview_mutation |
Preview a write/destructive query, get a confirmation token |
execute_mutation |
Execute after you confirm (requires token from above) |
vault_register_connection |
Register a connection in the credential vault |
vault_register_from_path |
Register a connection from a config file |
vault_list |
List all registered vault aliases |
vault_revoke |
Remove a connection from the vault |
How writes get approved
Every write or destructive query follows the same flow:
Agent calls preview_mutation(connection, sql)
→ Server returns a preview of the change + a one-time token (5-minute TTL)
Agent shows you the preview:
"This will DELETE 42 rows from orders. Do you confirm?"
You say yes.
Agent calls execute_mutation(connection, sql, token)
→ Server validates the token, executes, and consumes it.
The token binds the connection name and the exact SQL to the approval. If the agent tries to run different SQL or target a different connection, the server rejects the token. Tokens expire after 5 minutes and can only be used once.
Custom drivers
run.sh detects the URL scheme and installs the driver for you. For example, setting DB_SNOW=snowflake://... causes the script to install snowflake-sqlalchemy on first launch.
If you prefer to install manually:
uv pip install snowflake-sqlalchemy
The SQLAlchemy dialect registry resolves the driver from the URL prefix.
Environment reference
| Variable | Default | Description |
|---|---|---|
DB_<NAME> |
— | Connection URL for a named database |
DB_MAX_ROWS |
500 |
Max rows returned per query call |
SECUREDBLINK_ALLOWED_ROOTS |
— | Colon-separated list of directories for vault file registration |
Credential Vault
The credential vault lets you store database credentials securely using your system's credential manager (macOS Keychain, Linux Secret Service, Windows Credential Manager). Once stored, credentials are never visible to the agent or in tool responses, logs, or traces.
Why use the vault
- Security: Credentials are stored using the OS credential manager, not in plaintext files or environment variables
- Isolation: The MCP server is the only component that ever holds plaintext credentials
- Clean separation: Use descriptive aliases instead of exposing connection strings in agent conversations
Vault tools
| Tool | Description |
|---|---|
vault_register_connection |
Register a connection by providing JDBC URL, username, and password directly |
vault_register_from_path |
Register a connection by reading a config file (.env, .properties, .yml) |
vault_list |
List all registered vault aliases with metadata |
vault_revoke |
Remove a connection from the vault |
Using the vault
Register a connection directly
# The agent calls:
vault_register_connection(
alias="prod",
jdbc_url="postgresql://user:password@host:5432/mydb",
username="user",
password="password"
)
# Returns: {"alias": "prod", "status": "registered"}
# The credentials are now stored securely and never echoed back
Then use the alias with any query tool:
query(connection_name="prod", sql="SELECT * FROM users")
Register from a config file
First, configure the allowed directories:
export SECUREDBLINK_ALLOWED_ROOTS="/path/to/configs:/another/path"
Then register:
# Agent calls:
vault_register_from_path(
alias="prod",
file_path="/path/to/configs/prod.env"
)
Supported file formats:
- .env:
DB_URL=postgresql://...,DB_USERNAME=...,DB_PASSWORD=... - .properties:
jdbc.url=postgresql://...,jdbc.username=...,jdbc.password=... - .yml/.yaml: Spring Boot style with
spring.datasource.url/username/password
List and revoke
# List all vault aliases
vault_list()
# Returns: {"aliases": [{"name": "prod", "created_at": "...", "source": "direct"}, ...]}
# Remove a connection
vault_revoke(alias="prod")
# Returns: {"alias": "prod", "status": "revoked", "existed": true}
Command-line interface
You can also register connections from the terminal without going through the agent. Use the securedblink command with a subcommand (or python -m securedblink.server if you run from a source checkout):
# Register a connection directly (prompts-free, useful for scripting)
securedblink register \
--alias prod \
--jdbc-url "postgresql://user:password@host:5432/mydb" \
--username user \
--password password \
--driver org.postgresql.Driver
# Overwrite an existing alias
securedblink register --alias prod --jdbc-url "..." --overwrite
# Register from a config file (requires SECUREDBLINK_ALLOWED_ROOTS)
export SECUREDBLINK_ALLOWED_ROOTS="/path/to/configs"
securedblink register-from-path --alias prod --file-path /path/to/configs/prod.env
# List registered aliases
securedblink list
Running securedblink with no subcommand starts the MCP server.
Note (macOS):
SECUREDBLINK_ALLOWED_ROOTSis compared against resolved paths./tmpresolves to/private/tmp, so useSECUREDBLINK_ALLOWED_ROOTS="/private/tmp"if your config files live in/tmp.
Security requirements
- The
vault_register_from_pathtool requiresSECUREDBLINK_ALLOWED_ROOTSto be set - Paths outside the allow-listed roots are rejected — no exceptions
- All logging and exception messages are redacted to prevent credential leaks
- No tool returns plaintext credentials under any circumstance
Contributing
See CONTRIBUTING.md for development setup, code style, and PR guidelines.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file securedblink-0.6.0.tar.gz.
File metadata
- Download URL: securedblink-0.6.0.tar.gz
- Upload date:
- Size: 162.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5155f528c510c0dff1ab9be47d7ff3cbcefeab30e766e70ac6a22ba869915100
|
|
| MD5 |
b8e733d094b2e7df40014ad1e493d145
|
|
| BLAKE2b-256 |
479a2b5b8f19c0e5a572204387ec417aa12245627478691a6245adc6679fbcb0
|
File details
Details for the file securedblink-0.6.0-py3-none-any.whl.
File metadata
- Download URL: securedblink-0.6.0-py3-none-any.whl
- Upload date:
- Size: 44.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6e1d8691a9ef9bdb7108d73d6516fceb5ee029f1e2d179236cf295a1c7e7c91a
|
|
| MD5 |
262ebe98467c3d383caa34989ab1c34b
|
|
| BLAKE2b-256 |
5c78eb4d1316b49fc97577030cc5a55dd7429702353a1ff5e16cd05c075d1591
|