A T-SQL formatter that runs in both text editors and CI, understands the whole language, and checks its own work.
- One static binary.
- No runtime to install.
- The formatter is free and MIT licensed, all of it.
maxdop query.sql # formatted SQL to stdout
maxdop --write src/ # format every .sql under src/, in place
maxdop --check src/ # fail the build if anything would changeWindows, macOS, Linux and Alpine; x64 and arm64. ~3.5 ms cold start. The binary in your editor is the same one your pipeline runs.
Style lives in a .maxdop.json at your repo root, so the team agrees once instead of per editor.
Most SQL formatters never parse your SQL. They split it into tokens and guess. That works until the shape of the code matters. These are real outputs:
| you wrote | sql-formatter 15.8.2, tsql | maxdop |
|---|---|---|
if @a = 1
begin
if @b = 2
begin
select 1;
end
end |
-- nesting lost
if @a = 1 begin if @b = 2 begin
select
1;
end end |
IF @a = 1
BEGIN
IF @b = 2
BEGIN
SELECT 1;
END
END |
SELECT 1 << 1 >> 1;
SELECT 'a' || 'b'; |
-- no longer valid SQL
SELECT
1 < < 1 > > 1;
SELECT
'a' | | 'b'; |
SELECT 1 << 1 >> 1;
SELECT 'a' || 'b'; |
maxdop is built on ScriptDom, Microsoft's own T-SQL
parser. It includes twelve grammars, SQL Server 2000 to 2025
plus Fabric DW. It reads stored procedures, GO batches and syntax the way the server does.
Microsoft's own formatters use ScriptDom, so they have the potential to understand and format your code equally well. However, there formatters are bundled in tightly into their products:
flowchart TD
SD["<b>Microsoft ScriptDom</b> · MIT<br/>12 grammars · SQL Server 2000-2025 + Fabric DW"]
SD --> MX["<b>maxdop</b>"]
SD --> MS["<b>mssql</b> extension<br/>for VS Code"]
SD --> SS["<b>SSMS 22 formatter</b><br/>preview"]
MX --> MXS["wrapped in<br/><b>one native binary</b><br/>no runtime · ~3.5 ms cold start"]
MS --> MSS["wrapped in a<br/><b>292 MB .NET service</b><br/>requires .NET 10"]
SS --> SSS["wrapped in<br/><b>SSMS itself</b><br/>Windows only"]
MXS --> MXU["<b>CLI</b> · --check in CI, pre-commit<br/><b>Editors</b> · VS Code, Neovim"]
MSS --> MSU["<b>CLI</b> · none — JSON-RPC only<br/><b>Editors</b> · VS Code only"]
SSS --> SSU["<b>CLI</b> · none<br/><b>Editors</b> · SSMS only"]
classDef mine fill:#0b6b53,stroke:#0b6b53,color:#ffffff;
class MX,MXS,MXU mine;
A formatter with no command line cannot gate a pull request. Style only becomes the team's style when something fails the build.
maxdop re-parses what it produced and compares its significant token stream against your input's, token by token, with a second check for the comments. Identical tokens mean an identical parse tree, so this delivers tree equivalence rather than approximating it. If anything differs you get your original file back, untouched, and a distinct exit code.
maxdop has been measured against 2,215 real-world files including AdventureWorks, WideWorldImporters, the First Responder Kit, Ola Hallengren's Maintenance Solution, sp_WhoIsActive, and ScriptDom's own test suite (formatted once per configuration option).
| Test Results | |
|---|---|
| Refused, crashed, or non-idempotent | 0, in every variant |
| Comments lost | 0 of 11,385 |
| Token coverage, corpus-wide | 98.6% |
More about Safety and Validation →
Maxdop is compiled as one static executable and works across multiple platforms. No additional runtimes or platform support (.NET, Node, etc.) are needed. It can run inside a text editor as a formatter, as a standalone CLI tool, and it can run in a CI pipeline as a quality gate.
The following editors are tested and supported for formatting T-SQL.
-
VS Code extension - packaged as an extension which bundles the binary for your platform.
-
Neovim - through conform.nvim.
-
Helix
-
Other Editors - Any editor that can pipe a buffer through a command works too — the whole interface is stdin in, stdout out, exit code back.
| Homebrew (macOS, Linux) | brew install pagebrooks/tap/maxdop |
| Scoop (Windows) | scoop bucket add maxdop https://github.com/pagebrooks/scoop-maxdopscoop install maxdop |
| pip / uv (any platform) | pip install maxdopuvx maxdop --check src/ |
The PyPI package is the same static binary in a wheel, no Python runs when you format a file.
Note: Support for more package mamanagers are on the way. If you maintain a package for a manager not listed here, an issue or a PR to
packaging/ is welcome.
Pick your platform from the latest release — linux-x64, linux-arm64,
linux-musl-x64 (Alpine), osx-x64, osx-arm64, win-x64 or win-arm64.
macOS and Linux
V=0.1.2; RID=linux-x64 # or linux-arm64, linux-musl-x64, osx-x64, osx-arm64
curl -fsSLO "https://github.com/pagebrooks/maxdop/releases/download/v$V/maxdop-$V-$RID.tar.gz"
tar -xzf "maxdop-$V-$RID.tar.gz"
sudo install "maxdop-$V-$RID/maxdop" /usr/local/bin/Windows
Download maxdop-<version>-win-x64.zip (or win-arm64), extract it, and put
maxdop.exe somewhere on your PATH.
maxdop query.sql # stdout
maxdop --write src/ # a file or a directory, searched to the bottom
maxdop --check src/ # exit 1 if anything would change
maxdop --parser-version 2016 # pin the grammar
cat query.sql | maxdop # stdin to stdout, how editors call it
git diff --name-only --diff-filter=ACM -z | maxdop --check --files-from - # only what changed
maxdop --write-baseline src/ # adopt on a codebase already writtenExit codes and CLI reference →
repos:
- repo: https://github.com/pagebrooks/maxdop
rev: v0.1.2
hooks:
- id: maxdop # rewrites files, then fails so you re-stage
- id: maxdop-check # fails without touching anythingpre-commit installs the binary itself, so nobody on the team has to have maxdop already. Exclusions
still come from .maxdop.json, not from the hook.
One .maxdop.json at the repo root, committed next to the code it formats. The nearest one at or above the file being formatted wins.
{
"maxWidth": 100,
"indentSize": 4,
"useTabs": false,
"keywordCase": "upper",
"leadingCommas": false,
"recaseBuiltInFunctions": true,
"alwaysBreakSelectList": false,
"alwaysBreakWhere": false,
"maxBlankLines": 1,
"parserVersion": "2022",
"initialQuotedIdentifiers": false,
"exclude": ["db/generated/**", "*.gen.sql"]
}recaseBuiltInFunctions gives built-in function names and global variables the configured keyword
case, so getdate() becomes GETDATE() and @@rowcount becomes @@ROWCOUNT. This is the one
casing rule maxdop applies from a list of names rather than from the parse tree, so it is the one you
can switch off — everywhere else, a word is recased only where the grammar proves it cannot be a
name.
Even on, it is narrow. Only an unqualified, undelimited call is touched: dbo.MyFunc(...),
dbo.Len(...) and [len](...) keep the casing you wrote, because SQL Server needs at least a
two-part name to reach a function of yours. And only a documented global variable is touched — if you
have a DECLARE @@MyVar INT (legal T-SQL, and indistinguishable from @@ROWCOUNT to the parser), it
keeps every character you wrote.
There are no editor-level formatting settings on purpose. A repo formats the same way whoever opens it.
| Safety | Safety guarantees made by maxdop |
| Comparison | Comparison of maxdop against other formatters |
| CLI | Flags, exit codes, grammar versioning, migration scripts |
| CI | Gating a pull request with --check, on GitHub Actions and elsewhere |
| Editors | VS Code, Neovim, plain Vim, Helix |
| Development | Repo layout and building from source |
MIT see LICENSE. ScriptDom is MIT and used as a NuGet dependency.
