Skip to main content

Opinionated Markdown formatter and linter

Project description

mdlint

CI

An opinionated Markdown formatter and linter, written in Rust.

What ruff did for Python and gofmt did for Go, mdlint aims to do for Markdown: enforce a single, consistent canonical style so that style debates disappear and diffs stay meaningful. As AI coding agents increasingly read and write Markdown, well-structured files matter more than ever. Run mdlint format and stop thinking about it.

Project Status: Active development, in between my day job.

Features

  • Formatter first: mdlint format rewrites files to a canonical style — no configuration required
  • Linter second: mdlint check reports violations; most are auto-fixable by the formatter
  • Fast: written in Rust for performance
  • Portable: single, small, 0-dependency binary (Linux x86_64/ARM64, macOS Intel/Apple Silicon, Windows)
  • Git-aware: respects .gitignore files by default

Installation

pip install markdownlint-rs

Or with uv:

uv tool install markdownlint-rs

How it works

pip install markdownlint-rs downloads a platform-specific wheel that bundles the correct pre-built mdlint binary for your OS and architecture. The mdlint command is a thin Python wrapper that locates and execs that binary. No Rust toolchain is required.

Platform Architecture Wheel tag
Linux (glibc) x86_64 manylinux_2_17_x86_64
Linux (glibc) aarch64 manylinux_2_17_aarch64
Linux (musl) x86_64 musllinux_1_2_x86_64
Linux (musl) aarch64 musllinux_1_2_aarch64
macOS x86_64 macosx_10_12_x86_64
macOS arm64 (Apple Silicon) macosx_11_0_arm64
Windows x86_64 win_amd64

Usage

Basic Usage

Check Markdown files for issues:

# check all Markdown files (auto-detected)
mdlint check

# check specific files or directories
mdlint check README.md docs/

# check and apply auto-fixes
mdlint check --fix

Format Markdown files (opinionated, fixes everything):

# format all Markdown files
mdlint format

# verify formatting without modifying files
mdlint format --check

# format specific files or directories
mdlint format README.md docs/

Command-Line Options

mdlint check

Lint Markdown files and report issues.

Usage: mdlint check [OPTIONS] [FILES]...

Arguments:
  [FILES]...              Files or directories to check (defaults to current directory)

Options:
      --fix               Apply auto-fixes where possible
      --format <FORMAT>   Output format: default or json [default: default]
      --exclude <PATH>    Exclude files or directories
      --config <CONFIG>   Path to configuration file
  -v, --verbose           Print each file name as it is checked
      --color <COLOR>     Color output: auto, always, never [default: auto]
  -h, --help              Print help

mdlint format

Format Markdown files with opinionated fixes.

Usage: mdlint format [OPTIONS] [FILES]...

Arguments:
  [FILES]...              Files or directories to format (defaults to current directory)

Options:
      --check             Only verify formatting, don't modify files
      --exclude <PATH>    Exclude files or directories
      --config <CONFIG>   Path to configuration file
      --color <COLOR>     Color output: auto, always, never [default: auto]
  -h, --help              Print help

Examples

Check with auto-fix:

mdlint check --fix

Check with custom config file:

mdlint check --config mdlint.toml

Check with JSON output:

mdlint check --format json

Check specific files:

mdlint check README.md CONTRIBUTING.md docs/

Format all files:

mdlint format

Verify formatting in CI:

mdlint format --check

Disable color output:

mdlint check --color never

Show each file as it is checked:

mdlint check --verbose

Configuration

mdlint uses TOML configuration files, similar to how ruff uses ruff.toml. The tool automatically discovers configuration files by searching up from the current directory.

Configuration File Locations

The tool searches for these files in order (first found wins per directory level):

  1. mdlint.toml
  2. .mdlint.toml

Configuration File Format

Create a mdlint.toml file in your project root:

# Enable all rules by default
default_enabled = true

# Respect .gitignore files when discovering files
gitignore = true

# Disable inline configuration comments
no_inline_config = false

# Rule-specific configuration
[rules.MD013]
line_length = 120
heading_line_length = 80
code_blocks = false

[rules.MD003]
style = "atx"

[rules.MD004]
style = "asterisk"

[rules.MD007]
indent = 2

# Disable specific rules
[rules.MD034]
enabled = false

Configuration Options

See mdlint.default.toml for every option with its default value and a description of what it does. The global options are summarised below.

Global Options

  • default_enabled (boolean): When true, all rules are enabled unless explicitly disabled. Default: false
  • gitignore (boolean): Respect .gitignore files when discovering markdown files. Default: true
  • no_inline_config (boolean): Disable inline configuration via HTML comments. Default: false
  • exclude (array): Paths to exclude from file discovery; merged with any --exclude CLI flags. Default: []
  • custom_rules (array): Paths to custom rule modules (future feature). Default: []
  • front_matter (string): Pattern for front matter detection. Default: auto-detects YAML (---) and TOML (+++)
  • fix (boolean): When true, mdlint check automatically applies all auto-fixable violations, equivalent to passing --fix on the command line. Default: true

Rule Configuration

Rules can be configured in three ways:

  1. Enable/Disable a rule:

    [rules.MD013]
    enabled = false
    
  2. Configure rule parameters:

    [rules.MD013]
    line_length = 100
    code_blocks = false
    
  3. Use both (parameters implicitly enable the rule):

    [rules.MD003]
    style = "atx"
    

Configuration Hierarchy

Configurations are discovered by walking up the directory tree. When multiple configs are found, they are merged with the following precedence (highest to lowest):

  1. Command-line options (--config)
  2. Local directory config (mdlint.toml in current dir)
  3. Parent directory configs (walking up to root)
  4. Default configuration

Later configs override earlier ones for scalar values. When a rule is configured in multiple places, the most specific configuration wins.

See the markdownlint rules documentation for details on each rule and its configuration options.

Inline Configuration

Rules can be suppressed for specific lines using HTML comments, without modifying mdlint.toml:

<!-- mdlint-disable-next-line MD013 -->
This line may be longer than the configured limit.

<!-- mdlint-disable MD033 -->
<div>Raw HTML block that needs to stay as-is</div>
<!-- mdlint-enable MD033 -->
Comment Effect
<!-- mdlint-disable MD001 --> Disable rule from this line onward
<!-- mdlint-enable MD001 --> Re-enable rule from this line onward
<!-- mdlint-disable-next-line MD001 --> Disable rule for the next line only
<!-- mdlint-disable --> Disable all rules from this line onward
<!-- mdlint-enable --> Re-enable all rules

Multiple rules: <!-- mdlint-disable MD001 MD013 --> — space-separate rule codes. Set no_inline_config = true in mdlint.toml to ignore all inline comments.

Exit Codes

  • 0: Success - no linting errors found (or files successfully formatted with format)
  • 1: Linting errors found (or formatting issues found with format --check)
  • 2: Runtime error (invalid config, file not found, etc.)

Use exit codes in CI/CD pipelines:

# Fail build on linting errors
mdlint check || exit 1

# Fail build if files need formatting
mdlint format --check || exit 1

Supported Rules

mdlint implements the markdownlint rule set. Rules marked ✅ are enforced automatically by mdlint format; rules marked ❌ are reported by mdlint check only.

Rule Description Format fixes
MD001 Heading levels should only increment by one level at a time
MD003 Heading style
MD004 Unordered list style
MD005 Inconsistent indentation for list items at the same level
MD007 Unordered list indentation
MD009 Trailing spaces
MD010 Hard tabs
MD011 Reversed link syntax
MD012 Multiple consecutive blank lines
MD013 Line length
MD018 No space after hash on atx style heading
MD019 Multiple spaces after hash on atx style heading
MD022 Headings should be surrounded by blank lines
MD023 Headings must start at the beginning of the line
MD025 Multiple top-level headings in the same document
... See markdownlint rules ...

Pre-commit Hooks

Native git hook

Create .git/hooks/pre-commit (and make it executable with chmod +x):

#!/bin/sh
mdlint format --check

This causes git commit to fail if any staged Markdown file needs formatting.

pre-commit framework

Add to .pre-commit-config.yaml:

repos:
  - repo: https://github.com/swanysimon/mdlint
    rev: v0.3.16  # use the latest release tag
    hooks:
      - id: mdlint-format-check
        name: mdlint format --check
        language: system
        entry: mdlint format --check
        types: [markdown]
      - id: mdlint-check
        name: mdlint check
        language: system
        entry: mdlint check
        types: [markdown]

Or use mdlint check --fix to auto-fix and stage the result:

      - id: mdlint-fix
        name: mdlint check --fix
        language: system
        entry: mdlint check --fix
        types: [markdown]
        pass_filenames: false

GitHub Actions

- name: Check Markdown formatting
  run: mdlint format --check

- name: Lint Markdown
  run: mdlint check

Contributing

Contributions are welcome! See the main repository for development setup, code quality standards, and the pull request process.

Build a wheel locally

cd python

# Pure-Python wheel (no binary bundled — for metadata validation only)
uv build --wheel

# Platform-specific wheel with a binary
cp /path/to/mdlint-binary mdlint/mdlint
MDLINT_PLATFORM_TAG=macosx_11_0_arm64 uv build --wheel

MDLINT_PLATFORM_TAG is read by hatch_build.py to stamp the correct platform tag onto the wheel. Without it, the wheel is tagged py3-none-any and contains no binary — useful for metadata validation in CI but not for distribution.

Validate package metadata

cd python
uv build --wheel
uvx twine check dist/*.whl

Platform tags

Asset MDLINT_PLATFORM_TAG
mdlint-linux-x86_64 manylinux_2_17_x86_64.manylinux2014_x86_64
mdlint-linux-x86_64-musl musllinux_1_2_x86_64
mdlint-linux-aarch64 manylinux_2_17_aarch64.manylinux2014_aarch64
mdlint-linux-aarch64-musl musllinux_1_2_aarch64
mdlint-macos-x86_64 macosx_10_12_x86_64
mdlint-macos-aarch64 macosx_11_0_arm64
mdlint-windows-x86_64.exe win_amd64

Release

Releases are automated via .github/workflows/publish-python.yml. On a version tag push, the workflow downloads each pre-built binary from the GitHub release, builds a platform-specific wheel, and publishes it to PyPI via trusted publishing (OIDC, no token required).

License

The Unlicense - see LICENSE for details.

Acknowledgments

Resources

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

markdownlint_rs-0.3.16-py3-none-win_amd64.whl (1.7 MB view details)

Uploaded Python 3Windows x86-64

markdownlint_rs-0.3.16-py3-none-musllinux_1_2_x86_64.whl (1.8 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

markdownlint_rs-0.3.16-py3-none-musllinux_1_2_aarch64.whl (1.6 MB view details)

Uploaded Python 3musllinux: musl 1.2+ ARM64

markdownlint_rs-0.3.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.7 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

markdownlint_rs-0.3.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (1.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

markdownlint_rs-0.3.16-py3-none-macosx_11_0_arm64.whl (1.5 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

markdownlint_rs-0.3.16-py3-none-macosx_10_12_x86_64.whl (1.7 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file markdownlint_rs-0.3.16-py3-none-win_amd64.whl.

File metadata

  • Download URL: markdownlint_rs-0.3.16-py3-none-win_amd64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for markdownlint_rs-0.3.16-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 97d1e83dc3e8521618baf8ebb2b36854f4410a42d612ff2c04df4b9c9899262a
MD5 d552e20ec74e61920c48bdca8109b58b
BLAKE2b-256 8a52ba3cfb7b5e2905dcc353f196c308380633a7f6f25c1dddfe9655b58f741d

See more details on using hashes here.

File details

Details for the file markdownlint_rs-0.3.16-py3-none-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: markdownlint_rs-0.3.16-py3-none-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 1.8 MB
  • Tags: Python 3, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for markdownlint_rs-0.3.16-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 237b573c70d0c7e4b9a5d596331afe00404f1465fcd0a2791bbb4bccafa08b0a
MD5 1c0454bffa7a8fbcdba9f2de79b0d2b0
BLAKE2b-256 f297ae3c79ad6d1c1a0d18ffa65bdfde176d75c98670e55859f0c7faf0655824

See more details on using hashes here.

File details

Details for the file markdownlint_rs-0.3.16-py3-none-musllinux_1_2_aarch64.whl.

File metadata

  • Download URL: markdownlint_rs-0.3.16-py3-none-musllinux_1_2_aarch64.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: Python 3, musllinux: musl 1.2+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for markdownlint_rs-0.3.16-py3-none-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 95aa18bc7330ca7f5f677841cf7fa68cad610c926c3a6f1c90dcf4e670e9d2ee
MD5 d5dfca3d40fb6f3d8c5708897fe4eaa0
BLAKE2b-256 2ff3802d408b0983588143bd522d45a4f7fbe28ab76b90c11354fecd50bc512b

See more details on using hashes here.

File details

Details for the file markdownlint_rs-0.3.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

  • Download URL: markdownlint_rs-0.3.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: Python 3, manylinux: glibc 2.17+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for markdownlint_rs-0.3.16-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 234f570fe61fdf0e7fa496f4c7772d6c752034f56a6afc6d78e69b35ac2a48e4
MD5 f754cbb74239983762b7b1fa42460c30
BLAKE2b-256 6a8e0a4e258a88779b5f316f8bbd842dcd3e81e4520fe4a18275acf5fb331b07

See more details on using hashes here.

File details

Details for the file markdownlint_rs-0.3.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

  • Download URL: markdownlint_rs-0.3.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
  • Upload date:
  • Size: 1.6 MB
  • Tags: Python 3, manylinux: glibc 2.17+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for markdownlint_rs-0.3.16-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 04d8d4a69fd9c84ac313fd87c334b7758dc00a01349b83d943b09326cb672a46
MD5 1f06530d3e399376318aae37daba9753
BLAKE2b-256 91ec054335199ea2ea2571f31c7a8e72122be0e4979e4b5227a2f7933e89d7c6

See more details on using hashes here.

File details

Details for the file markdownlint_rs-0.3.16-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: markdownlint_rs-0.3.16-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 1.5 MB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for markdownlint_rs-0.3.16-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d73c3f9824ef767f8f841b273ed2c352d8a423e6c48fb1a25ef536f2755bd7cb
MD5 9e45de095df08c099ff042126ea51fda
BLAKE2b-256 99341da9e33497a69c824b6a61d089e57691fb10af995c79959c8ec60bf3ffa9

See more details on using hashes here.

File details

Details for the file markdownlint_rs-0.3.16-py3-none-macosx_10_12_x86_64.whl.

File metadata

  • Download URL: markdownlint_rs-0.3.16-py3-none-macosx_10_12_x86_64.whl
  • Upload date:
  • Size: 1.7 MB
  • Tags: Python 3, macOS 10.12+ x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for markdownlint_rs-0.3.16-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 5342bea91894c171abd83ad8be303c8aa4908af952ffa94dea13d3fc5b17e106
MD5 4699bfed4f9a9448a324822b6da69fdf
BLAKE2b-256 01facbe634a06fbf91ac782f376e3ebbb2323cda2193964e86bf1493bf77cc21

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page