docs/dev-tools/tool-stubs.md
Tool stubs allow you to create executable files with embedded TOML configuration for tool execution. They provide a convenient way to define tool versions, backends, and execution parameters directly within executable scripts. They are also a good way to have some tools in mise lazy-load since the tools are only fetched when called and not when calling something like mise install.
This feature is inspired by dotslash, which pioneered the concept of executable files with embedded configuration for portable tool execution.
A tool stub is an executable file that begins with a shebang line pointing to mise tool-stub and contains TOML configuration specifying which tool to execute and how to execute it. When the stub is run, mise automatically installs the specified tool version (if needed) and executes it with the provided arguments.
Tool stubs can use any mise backend but because they default to http—and http backend tools have things like urls and don't require a version—the http stubs look a bit different than non-http stubs.
::: tip Tool stubs are particularly useful for adding less-commonly used tools to your mise setup. Since tools are only installed when their stub is first executed, you can define many tools without the overhead of installing them all upfront. This is perfect for specialized tools, testing utilities, or project-specific binaries that you might not use every day. :::
#!/usr/bin/env -S mise tool-stub
# Optional comment describing the tool
version = "1.0.0"
tool = "python"
bin = "python"
::: info Why use env -S?
The -S flag tells env to split the command line on spaces, allowing multiple arguments to be passed to the interpreter. This is necessary because shebangs on Unix systems traditionally only support a single argument after the interpreter path. Using env -S mise tool-stub allows the shebang to work correctly by splitting it into env → mise → tool-stub.
:::
Tool stub configuration is essentially a subset of what can be done in mise.toml [tools] sections, with the addition of a tool field to specify which tool to use. All the same options available for tool configuration in mise.toml are supported in tool stubs.
tool - Explicit tool name or backend specification (e.g., "python", "github:cli/cli"). This is the only field unique to tool stubs - it specifies which tool entry from the configuration to use. If omitted and a url field is present, defaults to the HTTP backend.version - The version of the tool to usebin - The binary name to execute within the tool (defaults to the stub filename)For multi-platform tarballs:
#!/usr/bin/env -S mise tool-stub
url = "https://example.com/releases/1.0.0/tool-linux-x64.tar.gz"
For platform-specific tarballs:
#!/usr/bin/env -S mise tool-stub
[platforms.linux-x64]
url = "https://example.com/releases/1.0.0/tool-linux-x64.tar.gz"
[platforms.darwin-arm64]
url = "https://example.com/releases/1.0.0/tool-macos-arm64.tar.gz"
Different platforms may have different binary structures or names. You can specify platform-specific bin fields when the binary path differs between platforms:
#!/usr/bin/env -S mise tool-stub
# Global bin field used when platforms have the same structure
bin = "bin/tool"
[platforms.linux-x64]
url = "https://example.com/tool-linux.tar.gz"
# Uses global bin field: "bin/tool"
[platforms.windows-x64]
url = "https://example.com/tool-windows.zip"
bin = "tool.exe" # Platform-specific binary for Windows
The tool stub generator automatically detects when platforms have different binary paths and will generate platform-specific bin fields when needed, or use a global bin field when all platforms have the same binary structure.
::: tip
tool stubs default to the HTTP backend if no tool field is specified and a url field is present.
See the HTTP backend documentation for full details on configuring HTTP-based tools.
:::
While you can manually create tool stubs with TOML configuration, mise provides a mise generate tool-stub command to automatically create stubs for HTTP-based tools.
::: tip Incremental Building When using platform-specific URLs, the tool stub generator will append new platforms to existing stub files rather than overwriting them. This allows you to incrementally build cross-platform tool stubs by running the command multiple times with different platforms. :::
Generate a tool stub for a tool distributed via HTTP:
mise generate tool-stub ./bin/gh --url "https://github.com/cli/cli/releases/download/v2.336.0/gh_2.336.0_linux_amd64.tar.gz"
This will:
For tools with different URLs per platform, you can generate all platforms at once:
mise generate tool-stub ./bin/rg \
--platform-url linux-x64:https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-x86_64-unknown-linux-musl.tar.gz \
--platform-url darwin-arm64:https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-aarch64-apple-darwin.tar.gz
Auto-Platform Detection: If the URL contains platform information, you can omit the platform prefix and let mise auto-detect it:
# Auto-detect platform from URL (detects as 'macos-arm64')
mise generate tool-stub ./bin/node \
--platform-url https://nodejs.org/dist/v22.17.1/node-v22.17.1-darwin-arm64.tar.gz
# Auto-detect platform from URL (detects as 'linux-x64')
mise generate tool-stub ./bin/node \
--platform-url https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-x86_64-unknown-linux-musl.tar.gz
Or build them incrementally by adding platforms one at a time:
# Start with Linux support (explicit platform)
mise generate tool-stub ./bin/rg \
--platform-url linux-x64:https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-x86_64-unknown-linux-musl.tar.gz
# Later, add macOS support using auto-detection (appends to existing file)
mise generate tool-stub ./bin/rg \
--platform-url https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-aarch64-apple-darwin.tar.gz
# Add Windows support using auto-detection (appends to existing file)
mise generate tool-stub ./bin/rg \
--platform-url https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-x86_64-pc-windows-msvc.zip
The generator will preserve existing configuration and merge new platforms into the [platforms] table. If you specify a platform that already exists, its URL will be updated.
--version VERSION - Specify tool version (defaults to "latest").--bin PATH - Override auto-detected binary path--platform-url PLATFORM:URL - Add platform-specific URL (can be used multiple times)--platform-url URL - Add platform-specific URL with auto-detected platform from URL filename--platform-bin PLATFORM:PATH - Set platform-specific binary path--skip-download - Skip downloading for faster generation (no checksums or binary detection)--lock - Resolve and embed lockfile data (pinned version + platform URLs/checksums) into an existing stubThe generator automatically detects and extracts various archive formats:
.tar.gz / .tgz (gzip compressed tarballs).tar.xz / .txz (xz compressed tarballs).tar.bz2 / .tbz2 (bzip2 compressed tarballs).tar.zst / .tzst (zstd compressed tarballs).zip (zip archives).7z (7-zip archives, Windows only)Running the generation command produces an executable stub like:
#!/usr/bin/env -S mise tool-stub
version = "latest"
bin = "bin/gh"
url = "https://github.com/cli/cli/releases/download/v2.336.0/gh_2.336.0_linux_amd64.tar.gz"
checksum = "blake3:a1b2c3d4e5f6..."
size = 12345678
The generator automatically:
#!/usr/bin/env -S mise tool-stub
# Node.js v20 tool stub
tool = "node"
version = "20.0.0"
bin = "node"
#!/usr/bin/env -S mise tool-stub
# Python tool accessible as 'py'
tool = "python"
version = "3.11"
bin = "python"
#!/usr/bin/env -S mise tool-stub
# GitHub CLI tool
tool = "github:cli/cli"
version = "latest"
#!/usr/bin/env -S mise tool-stub
tool = "node"
version = "20.18.1"
bin = "node"
[lock.platforms.linux-x64]
url = "https://nodejs.org/dist/v20.18.1/node-v20.18.1-linux-x64.tar.xz"
checksum = "sha256:abc123..."
[lock.platforms.macos-arm64]
url = "https://nodejs.org/dist/v20.18.1/node-v20.18.1-darwin-arm64.tar.gz"
checksum = "sha256:def456..."
The [lock] section is generated by mise generate tool-stub --lock and provides
reproducible downloads with checksum verification. The tool/version fields are still
used for backend resolution, while lock data provides the download shortcuts.
::: tip
Locking is especially useful for avoiding GitHub API rate limits when users don't have a GITHUB_TOKEN set. With locked stubs, tools can be installed without any API calls at runtime.
:::
# Create a stub with a fuzzy version
mise generate tool-stub ./bin/node --version 20
# Lock it to pin the exact version and add platform URLs/checksums
mise generate tool-stub ./bin/node --lock
This resolves the version, fetches URLs for all common platforms (linux-x64, linux-arm64, macos-x64, macos-arm64, windows-x64), and writes them into a [lock] section in the stub.
To bump the version of a locked stub, pass --version along with --lock:
# Bump to the latest node 22.x and re-lock
mise generate tool-stub ./bin/node --lock --version 22
#!/usr/bin/env -S mise tool-stub
# Custom HTTP tool with platform-specific downloads
version = "1.0.0"
[platforms.linux-x64]
url = "https://releases.example.com/v{{version}}/tool-linux-x64.tar.gz"
[platforms.darwin-arm64]
url = "https://releases.example.com/v{{version}}/tool-macos-arm64.tar.gz"
Make the stub executable and run it directly:
chmod +x ./bin/my-tool
./bin/my-tool --version
Execute using the mise tool-stub command—useful for testing if something isn't working right:
mise tool-stub ./bin/my-tool --version
Tool stubs implement intelligent caching which reduces the overhead mise has when running stubs:
Cached stubs have ~4ms of overhead.
mise xFor basic use cases, you can quickly create simple tool stubs using the mise x command as an alternative to writing TOML configuration manually:
# Create bin directory
mkdir -p ./bin
# Create a simple Node.js stub
cat > ./bin/node << 'EOF'
#!/usr/bin/env bash
exec mise x node@20 -- "$@"
EOF
chmod +x ./bin/node
# Create a Python stub with specific version
cat > ./bin/python << 'EOF'
#!/usr/bin/env bash
exec mise x [email protected] -- "$@"
EOF
chmod +x ./bin/python
This approach is ideal for simple tool execution without the need for custom options, environment variables, or platform-specific settings. For more complex configurations, use the full TOML configuration format described above.