sscli 0.1.11

Agent-friendly token efficient SQL Server CLI tool for database inspection
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# sscli

SQL Server CLI for AI coding agents.

One install. Your agents automatically know how to inspect SQL Server databases, run safe queries, and export results.

## Why sscli?

|                            |                                                                                |
| -------------------------- | ------------------------------------------------------------------------------ |
| **Token-efficient**        | Markdown output by default keeps agent context lean                            |
| **Read-only default**      | Blocks INSERT, UPDATE, DELETE unless overridden (--allow-write) — no accidents |
| **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**: `sqlcmd` output 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 like `sscli tables`, `sscli describe`, and `sscli columns`.
- **No safety guardrails**: `sqlcmd` will happily run destructive statements if an agent makes a mistake.
  `sscli sql` blocks writes by default and requires `--allow-write` for mutations.
- **More setup friction**: `sqlcmd` is 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 safe-by-default queries, fast schema
inspection, and output formats that are easy for agents to use.

## Quick Start (Agent Users)

**1. Install sscli**

```bash
# 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**

```bash
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 safe queries, 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 tables`, `sscli describe <Object>`, etc.  |
| Risk of accidental writes             | Read-only by default, explicit `--allow-write` override      |
| 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)

```bash
# 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

```bash
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 sql --file [path/to/file]           # Run long queries, execute bulk statements
sscli update                              # Check for new releases (alias: sscli upgrade)
```

## Installation

### Homebrew (macOS/Linux)

```bash
brew install jwcraig/tap/sscli
```

### Scoop (Windows)

```powershell
scoop bucket add jwcraig https://github.com/jwcraig/scoop-bucket
scoop install sscli
```

### Quick install script

```bash
curl -sSL https://raw.githubusercontent.com/jwcraig/sql-server-cli/main/install.sh | sh
```

### Cargo binstall (fast, no compilation)

```bash
cargo binstall sscli
```

### From source

```bash
cargo install sscli
```

### Prebuilt binaries

Download from [GitHub Releases](https://github.com/jwcraig/sql-server-cli/releases).

### Development build

```bash
git clone https://github.com/jwcraig/sql-server-cli sscli
cd sscli
cargo build --release
./target/release/sscli --help
```

### Updating

```bash
# 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.json` by default

Example `settings.json`:

```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]https://github.com/numman-ali/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, safe queries)
- Available commands and their purpose
- Output preferences (markdown for context efficiency, `--json` for structured data)
- Safety model (read-only default, `--allow-write` for mutations)

## Configuration

sscli supports three ways to configure a connection (highest priority wins; env vars are skipped if you pass `--profile`):

```bash
# 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):

```bash
sscli init
```

Or copy the example file in this repo:

```bash
mkdir -p .sql-server
cp config.example.yaml .sql-server/config.yaml
```

### Config discovery (where sscli looks)

1. `--config <PATH>`
2. `SQL_SERVER_CONFIG` / `SQLSERVER_CONFIG`
3. Walk up from CWD looking for `.sql-server/config.{yaml,yml,json}` or `.sqlserver/config.{yaml,yml,json}`
4. Global config: `$XDG_CONFIG_HOME/sql-server/config.{yaml,yml,json}` (platform-dependent)
5. Environment variables (only applied when no `--profile` is provided)
6. Hardcoded defaults

Run `sscli config` to confirm which config file is being used and what values are in effect.

### Example `config.yaml`

```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:** sscli automatically loads a `.env` file from the current directory if present, reading any of the supported variables listed below. Use `--env-file` to load a different file (e.g., `--env-file .env.dev`). This is useful for local development without polluting your shell environment.

| 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 read-only 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

`sscli sql` enforces read-only mode by default:

- **Allowed:** SELECT, WITH (CTEs), whitelisted stored procedures
- **Blocked:** INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, MERGE, etc.

Override with `--allow-write` when you intentionally need mutations.

## 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):

```json
{ "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 `--profile` or config default).
- `--source-connection/--left-connection`, `--target-connection/--right-connection`: override profile with a connection string (URL or ADO-style `Server=...;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; `--pretty` renders text; `--json` renders JSON.
- `--apply-script [path|-]`: generate SQL to align target to source; default path `db-apply-diff-YYYYMMDD-HHMMSS.sql` in 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/--target` expect those names.

Examples:

```bash
# 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

```bash
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:

```bash
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):

```bash
SSCLI_INTEGRATION_TESTS=1 SQL_SERVER_CONFIG=/path/to/config.yaml \
SQL_PASSWORD=... cargo test
```