Quickstart
This walks through one PostgreSQL database end to end. The other providers differ only in the connection string and the SQL that creates the login; see Read-only logins for those.
1. Create a read-only login
Section titled “1. Create a read-only login”sql-mcp refuses a database whose login can write (see How queries stay read-only), so start with a login that can only read:
CREATE ROLE agent LOGIN PASSWORD 'choose-a-password';GRANT USAGE ON SCHEMA app TO agent;GRANT SELECT ON ALL TABLES IN SCHEMA app TO agent;Grant SELECT only on what agents should see. Whatever this login can read, an agent can read.
2. Configure the database
Section titled “2. Configure the database”Replace the sample Databases in appsettings.json with yours:
{ "Databases": [ { "Name": "prod", "Provider": "Postgres", "ConnectionString": "Host=db.example.com;Database=myapp;Username=agent;Password={password};", "PasswordEnvVar": "DB_PROD_PASSWORD" } ]}{password} is filled in from the environment variable named by PasswordEnvVar, so the file can
be committed. Name is what agents pass as dbName. Every setting is in the
configuration reference.
3. Start it
Section titled “3. Start it”export DB_PROD_PASSWORD='choose-a-password'dotnet runCheck that the database connects and its login passes the audit:
curl -s http://localhost:8080/health/ready{ "status": "ready", "databases": { "prod": "ok" } }If the login can write, the entry says so instead, and the server log lists exactly which privileges it found.
4. Connect an agent
Section titled “4. Connect an agent”With Claude Code:
claude mcp add --transport http sql http://localhost:8080/mcpWithout agent tokens the server only listens on localhost. To serve other machines, give each agent its own token; Connect a client covers tokens, stdio, and other clients such as anchor.
5. Ask a question
Section titled “5. Ask a question”Ask something like “How many orders were placed last week, by status?”. A capable agent usually:
- calls
list_databasesand learnsprodis Postgres, - calls
list_tables(withsearchon a large schema) to find the orders table, - calls
describe_tablefor its columns, comments and foreign keys, - maybe calls
explain_queryif the table is large, - and runs one
SELECTwithquery_database.
Each of those calls appears in the server log under the agent’s name. The tools reference shows exactly what each one returns.