SQL Server CLI for AI coding agents.
One install. Your agents automatically know how to inspect SQL Server databases, run SQL, and export results.
sql is the canonical raw-SQL surface in this project.
You can run raw SQL either with the explicit subcommand:
sscli sql "SELECT 1"or with the top-level shorthand:
sscli "SELECT 1"For configuration, prefer explicit --env-file over relying on ambient .env
files in the working directory.
| Token-efficient | Markdown output by default keeps agent context lean |
| SQL-first workflows | Canonical sql command for raw SQL, plus schema/discovery helpers |
| Single binary | Fast startup, no runtime dependencies |
| CLI over MCP | No "tool bloat" from verbose tool descriptions for tools that are rarely used |
| Progressive disclosure | Core commands visible, advanced disclosed when needed |
sqlcmd is a great general-purpose SQL Server client, especially for interactive sessions and ad-hoc work.
For tool-calling agents, sqlcmd tends to be a poor fit because it's optimized for humans, not for
structured, repeatable automation:
- Output is hard to consume:
sqlcmdoutput is human-oriented text; agents usually want stable markdown tables or a single JSON object they can reliably parse. - Schema discovery is manual: you end up writing catalog queries (
sys.tables,INFORMATION_SCHEMA, etc.) instead of calling purpose-built primitives likesscli tables,sscli describe, andsscli columns. - Structured SQL workflows:
ssclikeeps raw SQL, schema discovery, and machine-readable output in one tool, so agents do not have to switch betweensqlcmdfor execution and a second tool for inspection. - More setup friction:
sqlcmdis typically installed via Microsoft tooling and may require ODBC drivers depending on platform/CI image; sscli is a single binary with config + env var discovery built in. - No agent integration: sscli can install a reusable skill/extension so agents "know the tool" without you pasting usage docs into every prompt.
Keep sqlcmd for interactive SQL. Reach for sscli when you want raw SQL plus fast schema inspection
and output formats that are easy for agents to use.
1. Install sscli
# macOS/Linux
brew install jwcraig/tap/sscli
# Windows (PowerShell)
scoop bucket add jwcraig https://github.com/jwcraig/scoop-bucket
scoop install sscli
# or with cargo (any platform)
cargo install sscli2. Teach your agents
sscli integrations skills add --global # Claude Code + Codex
sscli integrations gemini add --global # Gemini CLIDone. Your agents now know how to browse schemas, run SQL, and export results.
| Before | After |
|---|---|
| You paste schema context into prompts | Agent discovers schema on demand |
| Agent guesses at SQL Server commands | Agent knows sscli sql, sscli tables, sscli describe |
| Raw SQL needs a second tool | Canonical sql command plus schema/discovery helpers |
| Verbose output bloats context | Token-efficient markdown output by default, --json if needed |
For humans who want to use sscli directly.
# Create a starter config in ./.sql-server/config.yaml (safe defaults)
sscli init
# Set the password env var referenced by passwordEnv in your config. sscli also reads
export SQL_PASSWORD='...'
# Sanity-check connectivity + server metadata
sscli status
# See the effective settings + which config file was used
sscli configsscli status # Check connectivity
sscli tables # List tables
sscli tables --like "%User%" --describe # Describe all User-related tables
sscli describe Users # DDL, columns, indexes, triggers
sscli describe T_Users_Trig # Trigger definition (auto-detected)
sscli table-data equipment # Browse rows (schema auto-resolved; prompts on conflicts)
sscli sql "SELECT TOP 5 * FROM Users"
sscli "SELECT COUNT(*) FROM Users" # Top-level shorthand for inline SQL
sscli sql --file [path/to/file] # Run long queries, execute bulk statements
cat patch.sql | sscli sql --stdin # Pipe a script on stdin
sscli update # Check for new releases (alias: sscli upgrade)brew install jwcraig/tap/sscliscoop bucket add jwcraig https://github.com/jwcraig/scoop-bucket
scoop install ssclicurl -sSL https://raw.githubusercontent.com/jwcraig/sql-server-cli/main/install.sh | shThe installer verifies the downloaded artifact against the release checksums-sha256.txt.
cargo binstall ssclicargo install sscliDownload from GitHub Releases.
git clone https://github.com/jwcraig/sql-server-cli sscli
cd sscli
cargo build --release
./target/release/sscli --help# Check if you're up to date (alias: `sscli upgrade`)
sscli update
# Homebrew
brew upgrade sscli
# Cargo
cargo install sscli --forceBy default, sscli does not check for updates automatically.
To enable lightweight update notifications (stderr, TTY-only, cached), create:
~/.config/sscli/settings.json(Linux/XDG default)- macOS often uses
~/Library/Application Support/sscli/settings.jsonby default
Example settings.json:
{ "autoUpdate": true }| Agent | Command | What it installs |
|---|---|---|
| Claude Code | sscli integrations skills add --global |
~/.claude/skills/sscli/SKILL.md |
| Codex | (same command) | ~/.codex/skills/sscli/SKILL.md |
| Gemini CLI | sscli integrations gemini add --global |
~/.gemini/extensions/sscli/ |
| Other agent harnesses | Via OpenSkills | Bridge to installed skills |
| Flag | Installs to | Use case |
|---|---|---|
--global |
~/.claude/skills/ |
Available in all projects |
| (none) | ./.claude/skills/ |
Project-specific override |
The installed skill file tells agents:
- When to use sscli (database inspection, schema discovery, raw SQL execution)
- Available commands and their purpose
- Output preferences (markdown for context efficiency,
--jsonfor structured data) sqlas the main raw-SQL surface, with top-level shorthand for simple inline queries
sscli supports three ways to configure a connection (highest priority wins; env vars are skipped if you pass --profile):
# 1) CLI flags (one-off / scripts)
sscli status --server localhost --database master --user sa --password '...' # alias: --host
# 2) Environment variables (CI-friendly)
export SQL_SERVER=localhost SQL_DATABASE=master SQL_USER=sa SQL_PASSWORD='...'
sscli status
# 3) Config file (recommended for repeated use)
sscli init && export SQL_PASSWORD='...' && sscli statusGenerate a commented template (writes ./.sql-server/config.yaml by default):
sscli initOr copy the example file in this repo:
mkdir -p .sql-server
cp config.example.yaml .sql-server/config.yaml--config <PATH>SQL_SERVER_CONFIG/SQLSERVER_CONFIG- Walk up from CWD looking for
.sql-server/config.{yaml,yml,json}or.sqlserver/config.{yaml,yml,json} - Global config:
$XDG_CONFIG_HOME/sql-server/config.{yaml,yml,json}(platform-dependent) - Environment variables (only applied when no
--profileis provided) - Hardcoded defaults
Run sscli config to confirm which config file is being used and what values are in effect.
defaultProfile: default
profiles:
default:
server: localhost
port: 1433
database: master
user: sa
passwordEnv: SQL_PASSWORD
encrypt: true
trustCert: trueFor a fully commented example (including settings.output.*, timeout, and defaultSchemas), see config.example.yaml.
Environment variables override values from the config file when no explicit --profile was passed. If you pass --profile <name>, the profile values win over env vars (flags still win over both).
.env file support: use --env-file to load environment variables from a
specific file, for example --env-file .env.dev. sscli does not implicitly
load .env from the current working directory.
| Purpose | Environment variables (first match wins) |
|---|---|
| Config path | SQL_SERVER_CONFIG, SQLSERVER_CONFIG |
| Profile | SQL_SERVER_PROFILE, SQLSERVER_PROFILE |
| Connection URL | DATABASE_URL, DB_URL, SQLSERVER_URL |
| Server | SQL_SERVER, SQLSERVER_HOST, DB_HOST, MSSQL_HOST |
| Port | SQL_PORT, SQLSERVER_PORT, DB_PORT, MSSQL_PORT |
| Database | SQL_DATABASE, SQLSERVER_DB, DATABASE, DB_NAME, MSSQL_DATABASE |
| User | SQL_USER, SQLSERVER_USER, DB_USER, MSSQL_USER |
| Password | SQL_PASSWORD, SA_PASSWORD, MSSQL_SA_PASSWORD, SQLSERVER_PASSWORD, DB_PASSWORD, MSSQL_PASSWORD |
| Encrypt | SQL_ENCRYPT |
| Trust server certificate | SQL_TRUST_SERVER_CERTIFICATE |
| Connect timeout (ms) | SQL_CONNECT_TIMEOUT, DB_CONNECT_TIMEOUT |
sqlcmd compatibility: The following sqlcmd environment variables are also supported:
| Purpose | Variable |
|---|---|
| Server | SQLCMDSERVER |
| User | SQLCMDUSER |
| Password | SQLCMDPASSWORD |
| Database | SQLCMDDBNAME |
Core (shown in --help):
| Command | Purpose |
|---|---|
status |
Connectivity check |
databases |
List databases |
tables |
Browse tables and views (--describe for batch DDL) |
describe |
Any object: table, view, trigger, proc, function |
sql |
Execute SQL |
table-data |
Sample rows from a table |
columns |
Find columns across tables/views/procs (first result set) |
Advanced (shown in help --all):
| Command | Purpose |
|---|---|
indexes |
Index details with usage stats |
foreign-keys |
Table relationships |
stored-procs |
List and execute read-only procedures |
sessions |
Active database sessions |
query-stats |
Top cached queries by resource usage |
backups |
Recent backup history |
compare |
Schema drift detection between two connections |
integrations |
Install agent skills/extensions |
Note: sscli sessions filters by client host name using --client-host. --host is reserved as an alias for --server.
| Context | Default |
|---|---|
| Terminal (TTY) | Pretty tables |
| Piped / non-TTY | Markdown tables |
--json flag |
Stable JSON (v1 contract) |
--csv <file> |
CSV export |
JSON output emits exactly one object to stdout. Errors go to stderr.
- keep
sqlas the canonical raw-SQL command - support full SQL execution, including file/stdin-driven scripts
- keep the existing profile/config connection model
- prefer explicit
--env-fileover ambient cwd configuration - prefer lightweight safety rails and explicit target visibility over hidden parser restrictions
If you need a locked-down distribution, maintain a custom build or wrapper that strips write capability. The shipped tool is intended to be full-capability.
Each command returns a stable top-level object:
| Command | Shape |
|---|---|
status |
{ status, latencyMs, serverName, serverVersion, currentDatabase, timestamp, warnings } |
databases |
{ total, count, offset, limit, hasMore, nextOffset, databases: [...] } |
tables |
{ total, count, offset, limit, hasMore, nextOffset, tables: [...] } |
describe |
{ object: {schema, name, type}, columns, ddl?, indexes?, triggers?, foreignKeys?, constraints? } |
table-data |
{ table, columns, rows, total, offset, limit, hasMore, nextOffset } |
sql |
{ success, batches, resultSets, csvPaths? } |
compare |
{ modules, indexes, constraints, tables } when --summary; { source, target } snapshots with full metadata when --json without --summary |
Errors (stderr):
{ "error": { "message": "...", "kind": "Config|Connection|Query|Internal" } }Detects drift between two profiles or explicit connection strings.
Synopsis:
sscli compare --target <profile> [--source <profile>] [--schema web --schema dbo] \
[--summary|--json] [--ignore-whitespace] [--strip-comments] \
[--object dbo.ProcName] [--apply-script [path|-]] [--include-drops]
--target/--right(required): profile to treat as the environment you want to align.--source/--left: reference profile (defaults to global--profileor config default).--source-connection/--left-connection,--target-connection/--right-connection: override profile with a connection string (URL or ADO-styleServer=...;Database=...).--schema/--schemas: limit to specific schemas (repeatable or comma-separated).--object: emit unified diff for a single module (proc/view/function/trigger).--ignore-whitespace,--strip-comments: normalize noise before diffing definitions.--summary: compact drift counts;--prettyrenders text;--jsonrenders JSON.--apply-script [path|-]: generate SQL to align target to source; default pathdb-apply-diff-YYYYMMDD-HHMMSS.sqlin cwd; use-for stdout.--include-drops: include DROP statements (disabled by default).- Profiles are the names in your
.sql-server/config.*(e.g.,dev,stage,prod).--source/--targetexpect those names.
Examples:
# Summary with profile names
sscli compare --target prod --summary
# Object diff ignoring whitespace
sscli compare --target prod --object dbo.MyProc --ignore-whitespace
# Apply script to stdout
sscli compare --target prod --apply-script - --include-drops
# Using explicit connection strings instead of profiles
sscli compare --source-connection "Server=dev,1433;Database=app;User ID=sa;Password=..." \
--target-connection "sqlserver://user:pass@prod:1433/app" \
--summaryExit codes: 0 = no drift, 3 = drift detected (summary/object/apply modes), 1 = error.
cargo testThis repo ships a local pre-push hook that runs cargo fmt --check, cargo clippy -D warnings, and cargo test. It’s already enabled via core.hooksPath=.githooks. If you need to bypass temporarily:
HUSKY=0 git push # or
SKIP=1 git push # (any env; git ignores but hook can read if we add later)DB-backed integration tests (opt-in):
SSCLI_INTEGRATION_TESTS=1 SQL_SERVER_CONFIG=/path/to/config.yaml \
SQL_PASSWORD=... cargo test