Deployment
For more than one agent, run sql-mcp once over Streamable HTTP and give each agent a token. The same rules apply wherever it runs:
- Bind address.
App:Urlssets where it listens,http://localhost:8080by default. Separate several addresses with;. - Tokens. To listen beyond localhost, set at least one
App:AgentTokensentry, or setApp:AllowUnauthenticatedtotruewhen a proxy in front authenticates. Otherwise the server refuses to start. A blank token, or one shared by two agents, also stops it. - Passwords and tokens come from the environment, never from the file:
DB_<NAME>_PASSWORDfor whateverPasswordEnvVarnames, andApp__AgentTokens__<agent>. - TLS. sql-mcp serves plain HTTP. Beyond a single host, put it behind a reverse proxy or load balancer that terminates TLS, so tokens never cross the network in the clear.
Docker
Section titled “Docker”docker build -t sql-mcp .docker run -d --name sql-mcp -p 8080:8080 \ -e App__AgentTokens__reporting="$(openssl rand -hex 32)" \ -e DB_PROD_PASSWORD=secret \ -v /path/to/appsettings.json:/app/appsettings.json:ro \ sql-mcp- The image listens on all interfaces inside the container (
App__Urls=http://*:8080), so it won’t start without a token. - It runs as the base image’s unprivileged
appuser (uid 1654). A mountedappsettings.jsonmust be readable by that user. ASPNETCORE_ENVIRONMENT=Productionis set, so the production log settings apply (see Logging).- Its
HEALTHCHECKrunsdotnet sql-mcp.dll --healthcheck, which calls/healthon the configured port and exits 0 or 1. The image has no curl or wget, so use the same command for health checks elsewhere.
Linux: systemd
Section titled “Linux: systemd”Publish a self-contained binary (see Install), copy
it to /opt/sql-mcp, and create /etc/systemd/system/sql-mcp.service:
[Unit]Description=sql-mcpAfter=network.target
[Service]Type=notifyExecStart=/opt/sql-mcp/sql-mcpWorkingDirectory=/opt/sql-mcpRestart=alwaysEnvironment=ASPNETCORE_ENVIRONMENT=ProductionEnvironmentFile=/etc/sql-mcp/env
[Install]WantedBy=multi-user.targetKeep the secrets in /etc/sql-mcp/env, readable only by root:
DB_PROD_PASSWORD=secretApp__AgentTokens__reporting=<long random token>App__Urls=http://0.0.0.0:8080systemctl enable --now sql-mcpjournalctl -u sql-mcp -fWith Type=notify, sql-mcp tells systemd when it has started, so systemctl start waits until
it’s listening and reports a configuration error as a failed start.
Windows Service
Section titled “Windows Service”sc.exe create sql-mcp binPath= "C:\sql-mcp\sql-mcp.exe" start= autosc.exe description sql-mcp "sql-mcp MCP server"sc.exe start sql-mcpSet passwords and tokens as system environment variables, or in an appsettings.Production.json
next to the executable with ASPNETCORE_ENVIRONMENT=Production set for the service. Restrict that
file to the service’s account.
Health checks
Section titled “Health checks”| Endpoint | Token | Answers |
|---|---|---|
GET /health |
never | 200 {"status":"healthy"} while the process is up. Use it for liveness and load balancer probes. |
GET /health/ready |
when tokens are set | 200 when every database connects, passes the login audit and opens its read-only transaction, otherwise 503, with each database’s status. Use it for readiness. |
{ "status": "unavailable", "databases": { "prod": "ok", "reporting": "Database 'reporting' is disabled because its login can modify data. The server log lists why." } }/health/ready needs a token because it names the databases and why any are down. Its checks
aren’t written to the query log.
Limits worth tuning
Section titled “Limits worth tuning”The defaults suit one agent working interactively. For a shared server, look at:
App:MaxConcurrentQueries(8 per database). Further queries wait up toQueryTimeoutSecondsfor a slot, then fail with “Database ‘x’ is busy”.App:QueryTimeoutSeconds(30). A query that runs longer is cancelled.App:MaxRowsandApp:MaxResultBytes, which bound what one call returns.
All of them are in the configuration reference.