A terminal SQL client for developers who live in the terminal. First-class MS SQL Server support, plus PostgreSQL and SQLite. Keyboard-driven, fast, works over SSH and inside tmux.
This is a agentic AI coded piece of software. It is an experiment in doing everything with an AI agent and working out the best ways to develop software this way. While the version is less that 1.0.0 the software is likely to have a bunch of rough edges but my plans is to dogfood it (use it myself for real work) and have replaced using DataGrip. It is actually going really well and I find it a lot more useful than DataGrip (it is tailored to my workflows after all!). Once I think it is reasonably well polished and feature complete I will do a v1.
Ctrl+K switcher (with Ctrl+D to delete); Ctrl+N add-connection modal with connection-string or form-builder modeCtrl+P for app actions (including snippets), Ctrl+H for recent SQLCtrl+E runs the logical statement under the cursor; F5 runs the full bufferCtrl+R popup: alias tables, expand SELECT *, convert SELECT↔UPDATE, IDENTITY_INSERT wrapping, tab renameCtrl+Shift+FCtrl+G (editor and vim :42 syntax)Ctrl+B/F2; fuzzy filter, column selection for JOIN-aware SELECT, action menu (a), row counts (r)s), row numbers (#), stacked column filters (f), configurable row limit (L), poll/auto-refresh (P), zoom modes (z/Z)Enter opens a vertical column/value overlay; navigate rows with h/lSpace tags rows; V range-tag; Ctrl+A tag all/clear; tagged rows highlighted; export respects selection| toggles a column (purple header); \ opens a fuzzy column picker with multi-select; t toggles SELECTED ONLY view showing only tagged rows and/or selected columnsp pins the current result; re-running the same query shows added/removed/changed rowse in the results grid or row detail view opens an inline cell editor (with vim mode); on confirm an UPDATE preview panel appears showing the generated SQL — execute it with Ctrl+E, copy with y, close with Esc; results refresh automatically on success; Ctrl+D sets the value to NULLX in results: CSV, Markdown, JSON, SQL INSERT, or WHERE IN list — honours row tags and column selection; to clipboard or fileF10 captures the current view to clipboard or file--mcp starts a background JSON-RPC server so Claude Code can drive the TUI as an agentCtrl+N new tab, Ctrl+W close, Ctrl+PgDn/Ctrl+PgUp switchF1 shows keybindings, state, and loaded config values%APPDATA%\sql\config.lua (Windows) / ~/.config/sql/config.luaGrab a pre-built binary from the Releases page and put it on your PATH.
Requires Go 1.21+.
go install github.com/sqltui/sql/cmd/sql@latest
Or clone and build:
git clone https://github.com/sqltui/sql
cd sql
go build -o sql ./cmd/sql/
# Connect with a connection string
sql "server=1.2.3.4;user id=sa;password=...;database=mydb"
# List saved connections
sql --list
# Save a named connection
sql --add "server=1.2.3.4;user id=sa;password=...;database=mydb" --name prod
# Connect using a saved connection
sql prod
# SQLite
sql ./mydb.sqlite
# PostgreSQL
sql "postgres://user:pass@localhost/mydb"
# Start TUI with MCP socket server on port 45678
sql --mcp
sql --mcp prod
# Relay an MCP client (e.g. Claude Code) to a running TUI
sql --mcp-relay
sql --mcp-relay --mcp-port 9999
Once connected, the last-used connection is saved and restored on next launch.
| Database | Format |
|---|---|
| SQL Server | server=host;user id=user;password=pass;database=db |
| SQL Server | sqlserver://user:pass@host?database=db |
| PostgreSQL | postgres://user:pass@host/db |
| PostgreSQL | host=localhost user=postgres dbname=mydb sslmode=disable |
| SQLite | ./path/to/file.db or file:path/to/file.db |
Passwords are never displayed in the UI.
| Key | Action |
|---|---|
F1 | Help / settings overlay |
Ctrl+P | Command palette |
Ctrl+H | Query history palette |
Ctrl+K | Connection switcher |
Ctrl+E | Execute block under cursor |
Ctrl+Shift+F / Ctrl+F | Format active block |
F5 | Execute full buffer |
F3 / Alt+1 | Focus editor |
F4 / Alt+2 | Focus results |
Ctrl+B / F2 | Toggle schema browser |
Ctrl+Q | Quit (saves session) |
| Key | Action |
|---|---|
Ctrl+N | New tab |
Ctrl+W | Close tab |
Ctrl+PgDn / Alt+L | Next tab |
Ctrl+PgUp / Alt+H | Previous tab |
Alt+Up / Alt+Down | Previous / next query block |
Ctrl+G | Goto line |
Ctrl+R | Refactor popup |
Ctrl+\ | Toggle comment |
Tab | Insert 4 spaces |
Ctrl+Alt+V | Toggle vim mode |
Refactor popup (Ctrl+R):
| Key | Action |
|---|---|
N | Name/alias current table |
E | Expand SELECT * |
u / U | Convert SELECT → UPDATE (or append UPDATE) |
s / S | Convert UPDATE → SELECT (or append SELECT) |
i | Wrap INSERT with IDENTITY_INSERT ON/OFF |
T | Rename current tab |
Command palette (Ctrl+P) also exposes:
| Key | Action |
|---|---|
↑ / ↓ or j / k | Scroll rows |
PgUp / PgDn | Scroll a page |
Home / End | Jump to top / bottom row |
← / → or h / l | Scroll columns |
0 / $ | Jump to first / last column |
Alt+PgUp / Alt+PgDn | Previous / next result set |
Enter | Row detail view (vertical) |
s | Sort by cursor column (cycles: none → ▲ → ▼) |
f | Filter by cursor column (regex, stacks — add multiple) |
F | Clear all filters |
# | Toggle row numbers |
L | Change row limit |
p | Pin result as diff baseline (re-run to see diff) |
P | Toggle poll / auto-refresh |
Space | Tag / untag current row (advances cursor) |
V | Range-tag rows |
Ctrl+A | Tag all rows / clear all tags |
| | Toggle current column selection (purple header) |
\ | Column picker — fuzzy search columns; ↓/Tab enters list, Space tags, Enter applies |
Ctrl+\ | Clear all column selections |
t | Toggle SELECTED ONLY view — show only tagged rows and/or selected columns |
e | Edit cell → UPDATE preview panel |
E | Row edit form (multi-column UPDATE) |
X | Export (CSV / Markdown / JSON / SQL INSERT / WHERE IN → clipboard or file) |
v | Open cell value viewer |
y | Copy current cell to clipboard |
z | Zoom: shrink editor to active query block |
Z | Zoom: results fullscreen (hides editor) |
F10 | Screenshot to clipboard / file |
| Key | Action |
|---|---|
j / k or ↑ / ↓ | Scroll fields |
h / ← | Previous row |
l / → | Next row |
y | Copy focused field to clipboard |
e | Edit focused cell → UPDATE preview panel |
Esc | Close |
| Key | Action |
|---|---|
| Type | Edit value (vim mode supported) |
Ctrl+S | Confirm and open UPDATE preview |
Ctrl+D | Set value to NULL and open UPDATE preview |
Esc | Cancel |
| Key | Action |
|---|---|
Ctrl+E / Enter | Execute UPDATE |
y / Ctrl+C | Copy SQL to clipboard |
↑ / ↓ / j / k | Scroll |
Esc | Close |
| Key | Action |
|---|---|
| Type | Filter tables |
↓ / Enter | Move to table list |
j / k | Navigate tables |
Enter | Paste SELECT into editor |
Tab | Select / deselect column |
r | Fetch row count for selected table |
a | Action menu (copy name, DDL, indexes, row count) |
/ | Return to search input |
Esc | Close browser |
Alt+← / Alt+→ | Resize panel |
Config file location:
%APPDATA%\sql\config.lua~/.config/sql/config.lua-- config.lua
editor = {
result_limit = 500, -- default rows fetched per query
vim_mode = false, -- start in vim mode
}
theme = {
line_number = "#555555",
cursor_line_number = "#aaaaaa",
}
--mcp enables MCP (Model Context Protocol) support. There are two modes depending on how the process is launched:
When stdin is a pipe (i.e. launched by an MCP client), sql --mcp runs as a headless JSON-RPC 2.0 server over stdio with no TUI. This is the mode used by .claude/mcp.json:
{
"mcpServers": {
"sqltui": {
"command": "sql",
"args": ["--mcp", "myconnection"]
}
}
}
Replace myconnection with a saved connection name or a raw DSN. The agent can then query the database directly.
When launched in a terminal, --mcp starts the TUI normally and also opens a TCP socket server on port 45678 (default) so an agent can drive the live TUI — reading/writing the editor, executing queries, and seeing results.
sql --mcp # TUI + socket on default port 45678
sql --mcp myconn # connect and open socket
sql --mcp --mcp-port 9999 myconn # custom port
Press F1 to see the socket address. The agent writes queries into a new editor tab and executes them so you can watch the results appear live.
--mcp-relay connects to a running TUI's socket and bridges it over stdio, allowing Claude Code to drive the live TUI via .claude/mcp.json:
{
"mcpServers": {
"sqltui": {
"command": "sql",
"args": ["--mcp", "myconnection"],
"description": "Headless SQL client. Use for running queries and exploring the database."
},
"sqltui-live": {
"command": "sql",
"args": ["--mcp-relay"],
"description": "Live SQL TUI. Use when the user wants queries written into their editor. Requires sql --mcp to be running in a terminal."
}
}
}
sqltui when you want Claude to query the database independently.sqltui-live (or ask Claude to "write it in the editor") when you want to see the query built live in your terminal.| Tool | Description |
|---|---|
read_editor | Return current editor content |
write_editor(sql, mode) | Write SQL — new_tab (default), replace, append |
list_tabs | List open tab names |
switch_tab(name) | Switch to a named tab |
execute_query(sql) | Execute SQL and return results as JSON |
get_results | Return the current results grid |
get_schema | Return the introspected schema as JSON |
go build ./... # build all packages
go test ./... # run all tests
go test -cover ./... # with coverage
Go
100.0%
A terminal SQL client for developers who live in the terminal. First-class MS SQL Server support, plus PostgreSQL and SQLite. Keyboard-driven, fast, works over SSH and inside tmux.
This is a agentic AI coded piece of software. It is an experiment in doing everything with an AI agent and working out the best ways to develop software this way. While the version is less that 1.0.0 the software is likely to have a bunch of rough edges but my plans is to dogfood it (use it myself for real work) and have replaced using DataGrip. It is actually going really well and I find it a lot more useful than DataGrip (it is tailored to my workflows after all!). Once I think it is reasonably well polished and feature complete I will do a v1.
Ctrl+K switcher (with Ctrl+D to delete); Ctrl+N add-connection modal with connection-string or form-builder modeCtrl+P for app actions (including snippets), Ctrl+H for recent SQLCtrl+E runs the logical statement under the cursor; F5 runs the full bufferCtrl+R popup: alias tables, expand SELECT *, convert SELECT↔UPDATE, IDENTITY_INSERT wrapping, tab renameCtrl+Shift+FCtrl+G (editor and vim :42 syntax)Ctrl+B/F2; fuzzy filter, column selection for JOIN-aware SELECT, action menu (a), row counts (r)s), row numbers (#), stacked column filters (f), configurable row limit (L), poll/auto-refresh (P), zoom modes (z/Z)Enter opens a vertical column/value overlay; navigate rows with h/lSpace tags rows; V range-tag; Ctrl+A tag all/clear; tagged rows highlighted; export respects selection| toggles a column (purple header); \ opens a fuzzy column picker with multi-select; t toggles SELECTED ONLY view showing only tagged rows and/or selected columnsp pins the current result; re-running the same query shows added/removed/changed rowse in the results grid or row detail view opens an inline cell editor (with vim mode); on confirm an UPDATE preview panel appears showing the generated SQL — execute it with Ctrl+E, copy with y, close with Esc; results refresh automatically on success; Ctrl+D sets the value to NULLX in results: CSV, Markdown, JSON, SQL INSERT, or WHERE IN list — honours row tags and column selection; to clipboard or fileF10 captures the current view to clipboard or file--mcp starts a background JSON-RPC server so Claude Code can drive the TUI as an agentCtrl+N new tab, Ctrl+W close, Ctrl+PgDn/Ctrl+PgUp switchF1 shows keybindings, state, and loaded config values%APPDATA%\sql\config.lua (Windows) / ~/.config/sql/config.luaGrab a pre-built binary from the Releases page and put it on your PATH.
Requires Go 1.21+.
go install github.com/sqltui/sql/cmd/sql@latest
Or clone and build:
git clone https://github.com/sqltui/sql
cd sql
go build -o sql ./cmd/sql/
# Connect with a connection string
sql "server=1.2.3.4;user id=sa;password=...;database=mydb"
# List saved connections
sql --list
# Save a named connection
sql --add "server=1.2.3.4;user id=sa;password=...;database=mydb" --name prod
# Connect using a saved connection
sql prod
# SQLite
sql ./mydb.sqlite
# PostgreSQL
sql "postgres://user:pass@localhost/mydb"
# Start TUI with MCP socket server on port 45678
sql --mcp
sql --mcp prod
# Relay an MCP client (e.g. Claude Code) to a running TUI
sql --mcp-relay
sql --mcp-relay --mcp-port 9999
Once connected, the last-used connection is saved and restored on next launch.
| Database | Format |
|---|---|
| SQL Server | server=host;user id=user;password=pass;database=db |
| SQL Server | sqlserver://user:pass@host?database=db |
| PostgreSQL | postgres://user:pass@host/db |
| PostgreSQL | host=localhost user=postgres dbname=mydb sslmode=disable |
| SQLite | ./path/to/file.db or file:path/to/file.db |
Passwords are never displayed in the UI.
| Key | Action |
|---|---|
F1 | Help / settings overlay |
Ctrl+P | Command palette |
Ctrl+H | Query history palette |
Ctrl+K | Connection switcher |
Ctrl+E | Execute block under cursor |
Ctrl+Shift+F / Ctrl+F | Format active block |
F5 | Execute full buffer |
F3 / Alt+1 | Focus editor |
F4 / Alt+2 | Focus results |
Ctrl+B / F2 | Toggle schema browser |
Ctrl+Q | Quit (saves session) |
| Key | Action |
|---|---|
Ctrl+N | New tab |
Ctrl+W | Close tab |
Ctrl+PgDn / Alt+L | Next tab |
Ctrl+PgUp / Alt+H | Previous tab |
Alt+Up / Alt+Down | Previous / next query block |
Ctrl+G | Goto line |
Ctrl+R | Refactor popup |
Ctrl+\ | Toggle comment |
Tab | Insert 4 spaces |
Ctrl+Alt+V | Toggle vim mode |
Refactor popup (Ctrl+R):
| Key | Action |
|---|---|
N | Name/alias current table |
E | Expand SELECT * |
u / U | Convert SELECT → UPDATE (or append UPDATE) |
s / S | Convert UPDATE → SELECT (or append SELECT) |
i | Wrap INSERT with IDENTITY_INSERT ON/OFF |
T | Rename current tab |
Command palette (Ctrl+P) also exposes:
| Key | Action |
|---|---|
↑ / ↓ or j / k | Scroll rows |
PgUp / PgDn | Scroll a page |
Home / End | Jump to top / bottom row |
← / → or h / l | Scroll columns |
0 / $ | Jump to first / last column |
Alt+PgUp / Alt+PgDn | Previous / next result set |
Enter | Row detail view (vertical) |
s | Sort by cursor column (cycles: none → ▲ → ▼) |
f | Filter by cursor column (regex, stacks — add multiple) |
F | Clear all filters |
# | Toggle row numbers |
L | Change row limit |
p | Pin result as diff baseline (re-run to see diff) |
P | Toggle poll / auto-refresh |
Space | Tag / untag current row (advances cursor) |
V | Range-tag rows |
Ctrl+A | Tag all rows / clear all tags |
| | Toggle current column selection (purple header) |
\ | Column picker — fuzzy search columns; ↓/Tab enters list, Space tags, Enter applies |
Ctrl+\ | Clear all column selections |
t | Toggle SELECTED ONLY view — show only tagged rows and/or selected columns |
e | Edit cell → UPDATE preview panel |
E | Row edit form (multi-column UPDATE) |
X | Export (CSV / Markdown / JSON / SQL INSERT / WHERE IN → clipboard or file) |
v | Open cell value viewer |
y | Copy current cell to clipboard |
z | Zoom: shrink editor to active query block |
Z | Zoom: results fullscreen (hides editor) |
F10 | Screenshot to clipboard / file |
| Key | Action |
|---|---|
j / k or ↑ / ↓ | Scroll fields |
h / ← | Previous row |
l / → | Next row |
y | Copy focused field to clipboard |
e | Edit focused cell → UPDATE preview panel |
Esc | Close |
| Key | Action |
|---|---|
| Type | Edit value (vim mode supported) |
Ctrl+S | Confirm and open UPDATE preview |
Ctrl+D | Set value to NULL and open UPDATE preview |
Esc | Cancel |
| Key | Action |
|---|---|
Ctrl+E / Enter | Execute UPDATE |
y / Ctrl+C | Copy SQL to clipboard |
↑ / ↓ / j / k | Scroll |
Esc | Close |
| Key | Action |
|---|---|
| Type | Filter tables |
↓ / Enter | Move to table list |
j / k | Navigate tables |
Enter | Paste SELECT into editor |
Tab | Select / deselect column |
r | Fetch row count for selected table |
a | Action menu (copy name, DDL, indexes, row count) |
/ | Return to search input |
Esc | Close browser |
Alt+← / Alt+→ | Resize panel |
Config file location:
%APPDATA%\sql\config.lua~/.config/sql/config.lua-- config.lua
editor = {
result_limit = 500, -- default rows fetched per query
vim_mode = false, -- start in vim mode
}
theme = {
line_number = "#555555",
cursor_line_number = "#aaaaaa",
}
--mcp enables MCP (Model Context Protocol) support. There are two modes depending on how the process is launched:
When stdin is a pipe (i.e. launched by an MCP client), sql --mcp runs as a headless JSON-RPC 2.0 server over stdio with no TUI. This is the mode used by .claude/mcp.json:
{
"mcpServers": {
"sqltui": {
"command": "sql",
"args": ["--mcp", "myconnection"]
}
}
}
Replace myconnection with a saved connection name or a raw DSN. The agent can then query the database directly.
When launched in a terminal, --mcp starts the TUI normally and also opens a TCP socket server on port 45678 (default) so an agent can drive the live TUI — reading/writing the editor, executing queries, and seeing results.
sql --mcp # TUI + socket on default port 45678
sql --mcp myconn # connect and open socket
sql --mcp --mcp-port 9999 myconn # custom port
Press F1 to see the socket address. The agent writes queries into a new editor tab and executes them so you can watch the results appear live.
--mcp-relay connects to a running TUI's socket and bridges it over stdio, allowing Claude Code to drive the live TUI via .claude/mcp.json:
{
"mcpServers": {
"sqltui": {
"command": "sql",
"args": ["--mcp", "myconnection"],
"description": "Headless SQL client. Use for running queries and exploring the database."
},
"sqltui-live": {
"command": "sql",
"args": ["--mcp-relay"],
"description": "Live SQL TUI. Use when the user wants queries written into their editor. Requires sql --mcp to be running in a terminal."
}
}
}
sqltui when you want Claude to query the database independently.sqltui-live (or ask Claude to "write it in the editor") when you want to see the query built live in your terminal.| Tool | Description |
|---|---|
read_editor | Return current editor content |
write_editor(sql, mode) | Write SQL — new_tab (default), replace, append |
list_tabs | List open tab names |
switch_tab(name) | Switch to a named tab |
execute_query(sql) | Execute SQL and return results as JSON |
get_results | Return the current results grid |
get_schema | Return the introspected schema as JSON |
go build ./... # build all packages
go test ./... # run all tests
go test -cover ./... # with coverage
Go
100.0%