7 releases
Uses new Rust 2024
| 0.1.12 | Mar 27, 2026 |
|---|---|
| 0.1.11 | Feb 16, 2026 |
| 0.1.6 | Jan 8, 2026 |
#1027 in Database interfaces
425KB
11K
SLoC
sscli
SQL Server CLI for AI coding agents.
One install. Your agents automatically know how to inspect SQL Server databases, run SQL, and export results.
Command model direction
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.
Why sscli?
| 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 |
Why not sqlcmd
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.
Quick Start (Agent Users)
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 sscli
2. Teach your agents
sscli integrations skills add --global # Claude Code + Codex
sscli integrations gemini add --global # Gemini CLI
Done. Your agents now know how to browse schemas, run SQL, and export results.
What changes?
| 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 |
Manual Usage
For humans who want to use sscli directly.
1-minute setup (first run)
# 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 config
Common commands
sscli 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)
Installation
Homebrew (macOS/Linux)
brew install jwcraig/tap/sscli
Scoop (Windows)
scoop bucket add jwcraig https://github.com/jwcraig/scoop-bucket
scoop install sscli
Quick install script
curl -sSL https://raw.githubusercontent.com/jwcraig/sql-server-cli/main/install.sh | sh
The installer verifies the downloaded artifact against the release checksums-sha256.txt.
Cargo binstall (fast, no compilation)
cargo binstall sscli
From source
cargo install sscli
Prebuilt binaries
Download from GitHub Releases.
Development build
git clone https://github.com/jwcraig/sql-server-cli sscli
cd sscli
cargo build --release
./target/release/sscli --help
Updating
# Check if you're up to date (alias: `sscli upgrade`)
sscli update
# Homebrew
brew upgrade sscli
# Cargo
cargo install sscli --force
Automatic update notifications (optional)
By 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 Integration
Supported agents
| 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 |
Per-project vs global
| Flag | Installs to | Use case |
|---|---|---|
--global |
~/.claude/skills/ |
Available in all projects |
| (none) | ./.claude/skills/ |
Project-specific override |
What the skill teaches agents
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
Configuration
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 status
Creating a config file
Generate a commented template (writes ./.sql-server/config.yaml by default):
sscli init
Or copy the example file in this repo:
mkdir -p .sql-server
cp config.example.yaml .sql-server/config.yaml
Config discovery (where sscli looks)
--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.
Example config.yaml
defaultProfile: default
profiles:
default:
server: localhost
port: 1433
database: master
user: sa
passwordEnv: SQL_PASSWORD
encrypt: true
trustCert: true
For a fully commented example (including settings.output.*, timeout, and defaultSchemas), see config.example.yaml.
Environment variables
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 |
Commands
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.
Output Formats
| 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.
Safety
- 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.
JSON Contract (v1)
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" } }
compare (schema drift)
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" \
--summary
Exit codes: 0 = no drift, 3 = drift detected (summary/object/apply modes), 1 = error.
Testing
cargo test
Pre-push hook (local)
This 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
Dependencies
~33–56MB
~1M SLoC