sql-mcp
Provides read-only access to PostgreSQL databases, allowing exploration of schemas, tables, views, stored procedures, indexes, foreign keys, and execution of SELECT queries.
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., "@sql-mcpwhat tables are in the 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.
sql-mcp
A read-only MCP (Model Context Protocol) server for relational databases. Gives Claude and other MCP clients the ability to explore and query databases — schemas, tables, views, stored procedures, indexes, foreign keys, and arbitrary SELECT queries.
Supported databases: SQL Server · PostgreSQL
Extensible: the provider factory lets you add any database engine with a single npm run scaffold command.
Table of contents
Related MCP server: SQL Query Tools MCP Server
Requirements
Node.js 22+
Access to a SQL Server instance (on-prem, Azure SQL, or SQL Server in Docker), or a PostgreSQL instance (v13+)
A dedicated read-only database login (see Read-only enforcement)
Installation
git clone https://github.com/teghoz/sql-mcp.git
cd sql-mcp
npm install
npm run buildConfiguration
Copy .env.example to .env and fill in your connection details.
cp .env.example .envProvider selection
Set DB_PROVIDER to choose the database engine. Defaults to mssql.
DB_PROVIDER=mssql # SQL Server (default)
DB_PROVIDER=postgres # PostgreSQLSQL Server configuration
Option A — Connection string
Provide a single ADO.NET connection string. When SQL_CONNECTION_STRING is set, all individual SQL_* parameters are ignored.
SQL_CONNECTION_STRING=Server=myserver,1433;Database=mydb;User Id=myuser;Password=secret;Encrypt=true;TrustServerCertificate=false;Common connection string keywords:
Keyword | Example | Notes |
|
| Hostname; append port with a comma |
|
| Default database |
|
| SQL Server login |
|
| |
|
| Use |
|
| Set |
|
| Seconds |
.NET app-config style keys
Connection strings copied straight out of a .NET app.config / web.config also work —
the server normalises the following keys before handing them to the driver, so no editing
is needed:
.NET key | Normalised to |
|
|
|
|
|
|
|
|
|
|
These keys are recognised by .NET but not by the underlying mssql driver, and are
stripped rather than silently ignored: Persist Security Info,
MultipleActiveResultSets, Application Name.
Option B — Individual parameters
SQL_SERVER=localhost
SQL_PORT=1433 # default: 1433
SQL_DATABASE=master # default database for queries
SQL_USER=sa
SQL_PASSWORD=your_password
SQL_ENCRYPT=false # true for Azure SQL
SQL_TRUST_SERVER_CERTIFICATE=true # true for self-signed certs in dev
SQL_REQUEST_TIMEOUT=30000 # query timeout in ms (default: 30000)PostgreSQL configuration
Option A — Connection string
PG_CONNECTION_STRING=postgresql://myuser:secret@localhost:5432/mydbOption B — Individual parameters
PG_HOST=localhost
PG_PORT=5432 # default: 5432
PG_DATABASE=postgres # default database for queries
PG_USER=myuser
PG_PASSWORD=your_password
PG_SSL=false # true for SSL connections
PG_STATEMENT_TIMEOUT=30000 # query timeout in ms (default: 30000)Option C (optional) — Microsoft Entra (Azure AD) auth
For Azure Database for PostgreSQL with Entra-only authentication, where the
"password" is a short-lived access token rather than a static secret. Set
PG_AZURE_AD_AUTH=true and the server mints a token on demand per connection
via the Azure CLI credential — the equivalent of
az account get-access-token --resource https://ossrdbms-aad.database.windows.net —
caching and refreshing it automatically. No PG_PASSWORD, no stored secret, and no
external refresh process. SSL is forced on.
DB_PROVIDER=postgres
PG_AZURE_AD_AUTH=true
PG_HOST=myserver.postgres.database.azure.com
PG_PORT=5432
PG_DATABASE=mydb
PG_USER=my_entra_principal # the AAD user/group mapped as a PostgreSQL roleRequirements: an active az login in the environment the server runs in, and the
PG_USER principal must be mapped to a PostgreSQL role on the server. This mode is
opt-in — when PG_AZURE_AD_AUTH is unset, connection-string and password auth behave
exactly as before. Use individual parameters (not PG_CONNECTION_STRING) with this mode.
HTTP mode (optional)
Set PORT to start the server in HTTP mode instead of stdio. Required for Docker deployments.
PORT=3000Connecting to Claude Code
Add the server to your Claude Code MCP configuration. The location of the config file depends on your setup:
Project-level:
.claude/settings.jsonin your project rootGlobal:
~/.claude/settings.json
stdio mode (recommended for local use)
{
"mcpServers": {
"sql-mcp": {
"command": "node",
"args": ["C:/Projects/sql-mcp/dist/index.js"],
"env": {
"SQL_CONNECTION_STRING": "Server=myserver,1433;Database=mydb;User Id=myuser;Password=secret;Encrypt=false;TrustServerCertificate=true;"
}
}
}
}Or with individual parameters:
{
"mcpServers": {
"sql-mcp": {
"command": "node",
"args": ["C:/Projects/sql-mcp/dist/index.js"],
"env": {
"SQL_SERVER": "myserver",
"SQL_PORT": "1433",
"SQL_DATABASE": "mydb",
"SQL_USER": "myuser",
"SQL_PASSWORD": "secret",
"SQL_ENCRYPT": "false",
"SQL_TRUST_SERVER_CERTIFICATE": "true"
}
}
}
}Alternatively, place connection details in a .env file at C:/Projects/sql-mcp/.env and omit the env block — the server loads .env automatically on startup.
Using claude mcp add (Claude Code CLI)
The fastest way to register the server without editing JSON:
claude mcp add sql-mcp --scope user -e DB_PROVIDER=mssql -e SQL_SERVER=myserver -e SQL_DATABASE=mydb -e SQL_USER=myuser -e SQL_PASSWORD=secret -e SQL_ENCRYPT=false -e SQL_TRUST_SERVER_CERTIFICATE=true -- node "C:/Projects/sql-mcp/dist/index.js"Use --scope user to make it available across all projects, or --scope project to limit it to the current project.
Connecting to multiple databases
Same server, different databases — no extra configuration needed. Every tool accepts an optional database parameter. A single sql-mcp instance maintains a separate connection pool per database:
"Show me all tables in the
Reportingdatabase"
Claude callslist_tableswithdatabase: "Reporting"automatically.
Different servers — register the server twice under different names:
claude mcp add sql-mcp-prod --scope user -e SQL_SERVER=prod-server -e SQL_USER=reader -e SQL_PASSWORD=secret -- node "C:/Projects/sql-mcp/dist/index.js"claude mcp add sql-mcp-dev --scope user -e SQL_SERVER=dev-server -e SQL_USER=reader -e SQL_PASSWORD=secret -- node "C:/Projects/sql-mcp/dist/index.js"Each registration is an independent process. Claude sees them as distinct MCP servers and can query both in the same conversation.
HTTP mode
If you are running the server in HTTP mode (e.g. via Docker), use the url transport:
{
"mcpServers": {
"sql-mcp": {
"type": "http",
"url": "http://localhost:3000/mcp"
}
}
}Running in HTTP mode (Docker)
HTTP mode is useful when you want a shared server accessible from multiple clients or remote deployments.
Docker Compose
Create a .env file with your connection details (see Configuration), then:
docker compose up --buildThe MCP endpoint will be available at http://localhost:3000/mcp.
A health check endpoint is available at http://localhost:3000 and returns:
{ "name": "sql-mcp", "version": "1.0.0", "tools": 38 }Docker only
docker build -t sql-mcp .
docker run -p 3000:3000 \
-e PORT=3000 \
-e SQL_CONNECTION_STRING="Server=myserver,1433;Database=mydb;User Id=myuser;Password=secret;" \
sql-mcpAvailable tools — SQL Server
All tools return JSON. Most accept an optional database parameter — when omitted, queries run against the database specified in your connection configuration.
list_databases
Lists all online databases on the SQL Server instance.
Parameter | Required | Description |
— | No parameters |
list_schemas
Lists all schemas in a database with their owner.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
list_tables
Lists all base tables in a database.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
describe_table
Returns column definitions for a table: name, data type, nullability, max length, precision, scale, and default value.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Table name |
| No | Database name (defaults to configured default) |
get_table_indexes
Returns all indexes on a table, including type, uniqueness, primary key flag, and the list of key columns.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Table name |
| No | Database name (defaults to configured default) |
get_foreign_keys
Returns all foreign key constraints for a table: parent and referenced columns, and the delete/update referential actions.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Table name |
| No | Database name (defaults to configured default) |
list_views
Lists all views in a database.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
describe_view
Returns column definitions and the full SQL definition for a view.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | View name |
| No | Database name (defaults to configured default) |
list_stored_procedures
Lists all stored procedures in a database with their created and last-altered timestamps.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
get_stored_procedure_definition
Returns the full source definition of a stored procedure. Uses sys.sql_modules to avoid the 4000-character limit of INFORMATION_SCHEMA.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Stored procedure name |
| No | Database name (defaults to configured default) |
list_synonyms
Lists all synonyms in a database with the base object each one points to.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
get_synonym_definition
Returns the base object a synonym resolves to, along with created and modified dates.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Synonym name |
| No | Database name (defaults to configured default) |
list_functions
Lists all user-defined functions (scalar, inline table-valued, multi-statement table-valued, CLR) in a database.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
get_function_definition
Returns the full source definition of a user-defined function.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Function name |
| No | Database name (defaults to configured default) |
list_triggers
Lists all DML triggers in a database with their parent table, events (INSERT/UPDATE/DELETE), enabled state, and INSTEAD OF flag.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by parent table schema |
| No | Filter by parent table name |
get_trigger_definition
Returns the full source definition of a DML trigger.
Parameter | Required | Description |
| Yes | Schema of the parent table (e.g. |
| Yes | Trigger name |
| No | Database name (defaults to configured default) |
get_table_constraints
Returns all constraints defined on a table: PRIMARY KEY, UNIQUE, CHECK, and DEFAULT.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Table name |
| No | Database name (defaults to configured default) |
get_extended_properties
Returns all extended properties on a database object and its columns (commonly used to store MS_Description documentation).
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Object name (table, view, procedure, function, etc.) |
| No | Database name (defaults to configured default) |
Each row includes property_name, property_value, scope (object or column), and column_name.
list_sequences
Lists all sequences in a database with data type, range, increment, cycling, and current value.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
get_table_stats
Returns row count and disk space usage (total, used, unused in MB) for a table.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Table name |
| No | Database name (defaults to configured default) |
list_database_users
Lists all users in a database with their type, default schema, and mapped login.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
list_database_roles
Lists all database roles and their members. Includes fixed roles and roles with no members.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
get_object_permissions
Returns all explicit permissions granted on a database object (GRANT/DENY/REVOKE).
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Object name (table, view, procedure, etc.) |
| No | Database name (defaults to configured default) |
get_server_properties
Returns SQL Server instance metadata: version, edition, collation, clustering, and HA state. No parameters required.
list_linked_servers
Lists all linked servers configured on the instance. Requires VIEW ANY DEFINITION permission — see Permissions.
No parameters.
list_agent_jobs
Lists all SQL Server Agent jobs with enabled state, category, owner, and last run outcome. Requires SQLAgentUserRole in msdb — see Permissions.
No parameters.
get_job_history
Returns execution history for a SQL Server Agent job. Requires SQLAgentUserRole in msdb — see Permissions.
Parameter | Required | Description |
| Yes | Exact job name |
| No | Number of most-recent entries to return (default: 50, max: 500) |
list_partition_functions
Lists all partition functions in the database, including range type (LEFT/RIGHT), partition count, and boundary values.
No parameters.
list_partition_schemes
Lists all partition schemes with the function they use and the filegroups mapped to each partition.
No parameters.
list_fulltext_catalogs
Lists all full-text search catalogs in a database with item count, size in MB, and populate status.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
list_fulltext_indexes
Lists all full-text indexes with catalog name, indexed columns, and change-tracking state.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
list_service_queues
Lists all Service Broker queues with enqueue/receive/activation state and activation procedure.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
list_broker_services
Lists all Service Broker services and the queue each is bound to.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
list_user_types
Lists all user-defined scalar types and table types, including the underlying base type.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
get_table_type_columns
Returns the column definitions for a user-defined table type.
Parameter | Required | Description |
| Yes | Schema name (e.g. |
| Yes | Table type name |
| No | Database name (defaults to configured default) |
list_temporal_tables
Lists all system-versioned temporal tables in a database, showing the linked history table for each.
Parameter | Required | Description |
| No | Database name (defaults to configured default) |
| No | Filter by schema name (e.g. |
execute_query
Executes a read-only SELECT query and returns the result set.
Parameter | Required | Description |
| Yes | A |
| No | Database to run the query against |
| No | Row cap (default: 1000, max: 5000) |
Returns:
{
"rows": [ { "col1": "value", "col2": 42 } ],
"row_count": 1,
"truncated": false
}truncated is true when the result set exceeded max_rows. Use TOP or FETCH NEXT in your query for better performance on large tables.
Available tools — PostgreSQL
Set DB_PROVIDER=postgres to use these tools. PostgreSQL-specific introspection uses information_schema and pg_catalog.
Tool | Description |
| All non-template databases with size and encoding |
| User-defined schemas (excludes system schemas) |
| Base tables with optional schema filter and total size |
| Column definitions — type, nullability, default, comments |
| Indexes including type, uniqueness, and definition SQL |
| Foreign key constraints with update/delete rules |
| Views with updatability flags |
| Column definitions + view definition SQL |
| User-defined functions and procedures with signatures |
| Full source definition via |
| Triggers with optional schema/table filter |
| Trigger definition via |
| Sequences with start, min, max, increment, and last value |
| Row count, live/dead rows, table/index sizes, vacuum times |
| Version, current user/database, memory config, total size |
| Ad-hoc read-only SELECT query |
Permissions
Most tools work with db_datareader on the target database plus VIEW DEFINITION for source definitions. A few tools require elevated permissions:
Tool(s) | Required permission | Reason |
|
| Agent job tables live in |
|
|
|
|
| Required to read |
To grant SQLAgentUserRole in msdb:
USE msdb;
CREATE USER sql_mcp_reader FOR LOGIN sql_mcp_reader;
ALTER ROLE SQLAgentUserRole ADD MEMBER sql_mcp_reader;To grant VIEW ANY DEFINITION at the server level:
GRANT VIEW ANY DEFINITION TO sql_mcp_reader;Read-only enforcement
The server enforces read-only access at two layers:
Application layer —
execute_queryvalidates the query before it reaches SQL Server. It strips SQL comments, checks that the statement starts withSELECTorWITH, and blocks any query containing write keywords (INSERT,UPDATE,DELETE,DROP,CREATE,ALTER,TRUNCATE,EXEC,EXECUTE,MERGE,GRANT,REVOKE,DENY,BULK,OPENROWSET,OPENDATASOURCE). The schema exploration tools (list_tables,describe_table, etc.) use parameterised queries against read-only system views only.Database layer (recommended) — Create a dedicated SQL Server login with only
db_datareadermembership (andVIEW DEFINITIONif you need stored procedure source). This is the primary safeguard and ensures read-only access even if the application layer is bypassed.
-- Create a dedicated read-only login
CREATE LOGIN sql_mcp_reader WITH PASSWORD = 'strong_password_here';
-- In each database you want to expose:
USE [your_database];
CREATE USER sql_mcp_reader FOR LOGIN sql_mcp_reader;
ALTER ROLE db_datareader ADD MEMBER sql_mcp_reader;
-- Optional: allow reading stored procedure / view definitions
GRANT VIEW DEFINITION TO sql_mcp_reader;Note: Write access (
INSERT,UPDATE,DELETE) is not supported in the current version. TheSQL_ACCESS_LEVELenvironment variable is reserved for a future release that will make the access level configurable.
Policy / Data Privacy
Four environment variables let you restrict access and mask sensitive data without modifying SQL Server permissions.
Variable | Description |
| Comma-separated list of schemas the MCP may access. When unset, all schemas are accessible. |
| Comma-separated |
| Comma-separated |
| Hard cap on rows returned by any tool (default: |
Example:
SQL_ALLOWED_SCHEMAS=dbo,reporting
SQL_BLOCKED_TABLES=dbo.audit_log,hr.salaries
SQL_MASKED_COLUMNS=dbo.users.ssn,dbo.users.credit_card,dbo.employees.salary
SQL_MAX_ROWS=1000Behaviour:
Schema allowlist — any tool call that specifies a
schemaargument not in the allowlist is rejected before the query runs.Table blocklist — blocked tables are filtered from
list_tables,list_views, andlist_temporal_tablesresults, and direct access viadescribe_table,get_table_indexes, etc. is rejected.execute_querychecks the raw SQL text for blocked table references.Column masking — matched column values are replaced with
[MASKED]in the response. The original data is never sent to the client.Row cap —
execute_queryrespects the lower of the caller's requestedmax_rowsandSQL_MAX_ROWS.
These controls operate at the MCP layer. For defence in depth, combine them with database-level permissions (see Read-only enforcement).
Audit Logging
Every tool call is recorded — including blocked requests. Records are written to two sinks simultaneously:
File sink
A daily-rotating JSON log file is written to ${AUDIT_LOG_PATH}/audit-YYYY-MM-DD.log (UTC date). Each line is a JSON object:
{
"ts": "2026-04-13T14:23:01.456Z",
"tool": "describe_table",
"database_name": "MyApp",
"schema_name": "dbo",
"object_name": "users",
"query_hash": "e3b0c44298fc1c149afb...",
"row_count": 12,
"masked_cols": "[\"ssn\",\"credit_card\"]",
"blocked": false,
"block_reason": null,
"duration_ms": 42
}Set AUDIT_LOG_PATH to change the directory (default: logs/). The directory is created automatically.
SQL sink
Set AUDIT_CONNECTION_STRING (or AUDIT_DATABASE to reuse the main connection params with a different database) to also insert records into a SQL table.
Create the table before enabling the SQL sink:
CREATE SCHEMA audit;
CREATE TABLE audit.mcp_audit_log (
id BIGINT IDENTITY(1,1) PRIMARY KEY,
logged_at DATETIME2(3) NOT NULL DEFAULT SYSUTCDATETIME(),
tool NVARCHAR(100) NOT NULL,
database_name NVARCHAR(128) NULL,
schema_name NVARCHAR(128) NULL,
object_name NVARCHAR(128) NULL,
query_hash CHAR(64) NULL,
row_count INT NULL,
masked_cols NVARCHAR(MAX) NULL,
blocked BIT NOT NULL DEFAULT 0,
block_reason NVARCHAR(500) NULL,
duration_ms INT NULL
);The audit login needs only INSERT permission on audit.mcp_audit_log:
GRANT INSERT ON audit.mcp_audit_log TO sql_mcp_audit_writer;Fault tolerance: audit write failures (file permission errors, SQL connectivity issues) are logged to stderr and never propagate to the tool response. A failed audit write does not block the tool from returning its result.
Adding a new provider
The provider factory makes it straightforward to add support for any database engine (MySQL, SQLite, Oracle, etc.).
1. Scaffold the skeleton
npm run scaffold -- mysqlThis creates src/providers/mysql/ with a working skeleton: client.ts, index.ts, and stub tool files for databases, schemas, tables, and execute_query.
2. Install your driver
npm install mysql2
npm install --save-dev @types/mysql23. Implement sqlQuery() in client.ts
Wire up your driver's connection pool and query execution. The function signature is already in place — replace the throw new Error("Not implemented") stub with your driver code.
4. Fill in the tool queries
Each stub tool file has a /* TODO */ placeholder with example SQL for PostgreSQL and MySQL as reference. Replace them with queries for your engine's system catalog.
5. Register the provider in src/index.ts
if (providerName === "mysql") {
const m = await import("./providers/mysql/index.js");
return { provider: m.mysqlProvider, checkEnv: m.checkEnv };
}6. Build and test
npm run build
DB_PROVIDER=mysql node dist/index.jsDevelopment
# Run directly with tsx (no build step needed)
npm run dev
# Build TypeScript to dist/
npm run build
# Start the compiled server
npm start
# Open the MCP Inspector UI in a browser (useful for testing tools interactively)
npm run inspectThe server logs startup information and errors to stderr, keeping stdout clean for the MCP stdio transport.
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
- Alicense-qualityCmaintenanceRead-only SQL Server MCP server enabling safe database queries, table listing, and schema inspection with built-in security protections.MIT
- Flicense-qualityDmaintenanceMCP server for connecting to SQL Server in readonly mode. Allows any MCP client to explore the schema and run SELECT queries against a SQL Server database.
- Alicense-qualityBmaintenanceMCP server for querying and managing multiple databases (SQLite, PostgreSQL, MySQL) with read-only mode and schema inspection.MIT
- AlicenseAqualityCmaintenanceRead-only PostgreSQL database MCP server for safely exploring schema, tables, relationships, and sample data without modification.1048MIT
Related MCP Connectors
Read-only MCP server for ClassQuill, a tutoring-business-management platform.
MCP server for managing Prisma Postgres.
Read-only MCP server for wafergraph.com's semiconductor & AI supply-chain data: 30 tools, no auth.
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/teghoz/sql-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server