sql-mcp
v0.1.2
Published
MCP server for SQL databases with guardrails against destructive operations
Maintainers
Readme
sql-mcp
An MCP (Model Context Protocol) server for SQL databases with built-in guardrails against destructive operations.
AI coding agents can execute SQL queries autonomously — which means they can also accidentally run DELETE, DROP, or UPDATE on the wrong rows. sql-mcp sits between the agent and your database, intercepting guarded operations and requiring explicit confirmation before they execute.
Supported databases: Microsoft SQL Server, PostgreSQL
Installation
# Run directly without installing
npx sql-mcp
# Or install globally
npm install -g sql-mcp
sql-mcpConfiguration
sql-mcp is configured entirely via environment variables — either in your shell or in your MCP client's config.
Required
| Variable | Description |
|---|---|
| DB_TYPE | Database type: mssql or pgsql |
| DB_HOST | Database server hostname |
| DB_NAME | Database name |
| DB_USER | Database username |
| DB_PASSWORD | Database password |
Optional
| Variable | Default | Description |
|---|---|---|
| DB_PORT | 5432 (pgsql) / 1433 (mssql) | Database port |
| GUARD_MODE | confirm | confirm — hold guarded queries for approval. block — reject them outright |
| GUARDED_OPERATIONS | See below | Comma-separated SQL keywords to guard. NONE disables guarding. ALL uses the full default list |
| DB_TRUST_SERVER_CERTIFICATE | false | MSSQL only. Set to true for local/dev instances with self-signed certificates |
Default guarded operations: INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, EXEC, EXECUTE, MERGE, CREATE, GRANT, REVOKE, DENY
MCP Client Setup
Claude Code / Cursor / Windsurf
Add to your MCP settings (e.g. ~/.claude/settings.json or your client's equivalent):
PostgreSQL:
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["sql-mcp"],
"env": {
"DB_TYPE": "pgsql",
"DB_HOST": "localhost",
"DB_NAME": "mydb",
"DB_USER": "postgres",
"DB_PASSWORD": "secret"
}
}
}
}SQL Server:
{
"mcpServers": {
"database": {
"command": "npx",
"args": ["sql-mcp"],
"env": {
"DB_TYPE": "mssql",
"DB_HOST": "localhost",
"DB_NAME": "mydb",
"DB_USER": "sa",
"DB_PASSWORD": "secret",
"DB_TRUST_SERVER_CERTIFICATE": "true"
}
}
}
}Guard Modes
confirm (default)
When a guarded operation is detected, sql-mcp holds the query and returns a token. The agent must call confirm_query with that token to execute it. This gives you a chance to review and approve before anything runs.
Agent: run_query("DELETE FROM users WHERE inactive = true")
← { needs_confirmation: true, token: "uuid", query: "...", expires_in_minutes: 5 }
[You review and approve]
Agent: confirm_query("uuid")
← { success: true, rowsAffected: [47] }Tokens expire after 5 minutes. If a token expires, re-run the original query to get a new one.
Note for "run everything" / auto-approve agent modes: If your MCP client is configured to execute tool calls automatically without pausing for human review (e.g. Cursor's "run everything" mode), the agent may call
confirm_queryimmediately afterrun_querywithout any human checkpoint. In this case,confirmmode offers no real protection — useblockmode instead.
block
All guarded operations are rejected immediately with no option to confirm. Use this for production environments or any setup where the agent runs autonomously without human review.
Tools
list_tables
Lists all user tables in the connected database.
Returns: schema name, table name, row count, size (MB) for each table.
{
"success": true,
"table_count": 12,
"tables": [
{ "schema": "public", "table": "users", "full_name": "public.users", "row_count": 15234, "size_mb": 45.2 }
]
}describe_table
Returns detailed schema information for a table.
Parameters:
tableName— table name, optionally schema-prefixed (e.g."users"or"dbo.users")
Returns: columns (with types, nullability, defaults, PK/identity flags), foreign keys, indexes, row count, and sample rows.
{
"success": true,
"table": "public.users",
"statistics": { "row_count": 15234 },
"columns": [
{ "name": "id", "type": "integer", "nullable": false, "default": null, "primary_key": true, "identity": true },
{ "name": "email", "type": "character varying(255)", "nullable": false, "default": null, "primary_key": false, "identity": false }
],
"foreign_keys": [ ... ],
"indexes": [ ... ],
"sample_rows": [ ... ]
}run_query
Executes a SQL query.
Parameters:
sql— the SQL query to run
Safe (non-guarded) queries execute immediately and return results. Guarded queries are handled according to the configured GUARD_MODE.
Results are capped at 1000 rows. If a result set is truncated, the response includes "truncated": true.
confirm_query
Executes a previously held guarded query.
Parameters:
token— the UUID token returned byrun_query
Only works if the token exists and has not expired (5-minute TTL).
analyze_query_impact
Analyzes a destructive SQL query without executing it. Returns an estimate of how many rows will be affected, any CASCADE relationships that will trigger on child tables, warnings, and recommendations.
Parameters:
sql— the SQL query to analyze (DELETE,UPDATE,TRUNCATE, orDROP)
Returns: operation type, target table, estimated rows affected, cascade details, warnings, and recommendations.
{
"operation": "UPDATE",
"table": "public.orders",
"estimated_rows_affected": 3200,
"cascades": [],
"warnings": [],
"recommendations": [
"Large operation (3,200 rows). Consider batching with LIMIT to reduce lock duration."
]
}For DELETE, TRUNCATE, and DROP, the response also includes cascades — child tables that have an ON DELETE CASCADE foreign key relationship with the target table. Cascade row counts are best-effort and may be null for complex queries.
Note: In
confirmmode,run_queryautomatically includes impact analysis in its response when holding a guarded query — you do not need to callanalyze_query_impactseparately.
Requirements
- Node.js ≥ 18
- Network access to your database
License
MIT
