# How to Add a New Language Plugin to mise: A Complete Guide

> Learn how to add a new language plugin to mise. Follow this guide to create a metadata file and hooks, then register your plugin with mise plugins add for seamless integration.

- Repository: [jdx/mise](https://github.com/jdx/mise)
- Tags: how-to-guide
- Published: 2026-08-09

---

**To add a new language plugin to mise, create a directory with a [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) file and hooks under `~/.local/share/mise/plugins/<name>`, then register it using `mise plugins add <source>`.**

The mise tool manager (jdx/mise) supports extensible language plugins that allow you to manage versions of programming languages and tools. Whether you're adapting an existing asdf plugin or building a custom vfox-based integration, understanding how to add a new language plugin to mise requires familiarity with its plugin detection system and directory structure.

## Understanding mise Plugin Architecture

Mise stores plugins in the directory defined by `dirs::PLUGINS` (typically `~/.local/share/mise/plugins/`). According to the source code in [`src/plugins/mod.rs`](https://github.com/jdx/mise/blob/main/src/plugins/mod.rs), each plugin is classified by a specific **PluginType**—which can be `Asdf`, `Vfox`, `VfoxBackend`, or `Package`—determined by the files present in the plugin directory.

### Plugin Type Detection

Mise detects the plugin type using `PluginType::from_plugin_path` (lines 00222-00238 in [`src/plugins/mod.rs`](https://github.com/jdx/mise/blob/main/src/plugins/mod.rs)). This function inspects directory contents to distinguish between:

- **Vfox plugins**: Contain a [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) file
- **Asdf plugins**: Contain executable scripts like `bin/list-all`
- **Package plugins**: Special configuration-based definitions

For custom language plugins, you'll typically create a **Vfox-style plugin** by including the [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) marker file.

### The Core Plugin Files

The source code defines how plugins are instantiated via the `PluginType::plugin` method (lines 00441-00451), which constructs a concrete `PluginEnum` variant wrapping either `AsdfPlugin` (from [`src/plugins/asdf_plugin.rs`](https://github.com/jdx/mise/blob/main/src/plugins/asdf_plugin.rs)) or `VfoxPlugin` (from [`src/plugins/vfox_plugin.rs`](https://github.com/jdx/mise/blob/main/src/plugins/vfox_plugin.rs)).

## Step-by-Step: Creating a Language Plugin

To add a new language plugin to mise manually, prepare the plugin directory structure before registration.

### Step 1: Initialize the Plugin Directory

Create a named directory under the plugins root. The name becomes your plugin identifier:

```bash
PLUGIN_NAME=my-lang
mkdir -p ~/.local/share/mise/plugins/$PLUGIN_NAME/hooks

```

### Step 2: Create the metadata.lua File

Add a [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) file to mark this as a Vfox-style plugin:

```bash
touch ~/.local/share/mise/plugins/$PLUGIN_NAME/metadata.lua

```

This file serves as the type marker that `PluginType::from_plugin_path` checks when scanning the directory.

### Step 3: Implement Required Hooks

At minimum, create an [`available.lua`](https://github.com/jdx/mise/blob/main/available.lua) hook that tells mise when the tool can be used:

```lua
-- ~/.local/share/mise/plugins/my-lang/hooks/available.lua
return os.execute("command -v my-lang >/dev/null") == 0

```

Place this in the `hooks/` subdirectory. This script should return a truthy value when the language runtime is installed on the host system.

### Step 4: Add Installer Scripts (Optional)

If your plugin needs custom installation logic, add additional hook scripts:

```bash
cat > ~/.local/share/mise/plugins/$PLUGIN_NAME/hooks/backend_install.lua <<'EOF'
#!/usr/bin/env bash

# Download and install logic here

# Place binaries under $MISE_ROOT/tools/my-lang/<version>

EOF
chmod +x ~/.local/share/mise/plugins/$PLUGIN_NAME/hooks/backend_install.lua

```

Other optional hooks include [`install.lua`](https://github.com/jdx/mise/blob/main/install.lua) and [`uninstall.lua`](https://github.com/jdx/mise/blob/main/uninstall.lua), which the Vfox backend executes during tool lifecycle operations.

## Registering and Installing Your Plugin

Once your directory structure is ready, register the plugin with mise using the **PluginSource::parse** logic (lines 00404-00433 in [`src/plugins/mod.rs`](https://github.com/jdx/mise/blob/main/src/plugins/mod.rs)).

### From a Git Repository

```bash
mise plugins add git::https://github.com/you/my-lang-plugin.git

```

The `PluginSource::parse` function detects Git URLs and triggers `install_git_plugin_source` (lines 00776-00816), which clones the repository, checks out the requested ref, and symlinks the final directory into `dirs::PLUGINS`.

### From a Local Path

For local development, use a file path:

```bash
mise plugins add /path/to/my-lang-plugin

```

This invokes `install_local_plugin_source` (lines 00709-00784), which creates a symlink to your local directory.

### From a ZIP Archive

You can also distribute plugins as ZIP files. If the source URL ends with `.zip` (detected at lines 00420-00425), mise extracts the archive instead of cloning:

```bash
mise plugins add https://github.com/you/my-lang-plugin/releases/latest/download/plugin.zip

```

## How mise Resolves Plugin Types

When you run `mise plugins ls` or reference a tool, mise determines the backend implementation using two key methods:

1. **`PluginType::from_plugin_config`** (lines 00207-00216): Handles plugins declared in [`mise.toml`](https://github.com/jdx/mise/blob/main/mise.toml) configuration files
2. **`PluginType::from_plugin_path`** (lines 00222-00238): Scans the filesystem for [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) and hook directories to classify existing plugins

The [`src/toolset/install_state.rs`](https://github.com/jdx/mise/blob/main/src/toolset/install_state.rs) module maintains a map of installed plugins (`list_plugins`) that is consulted when resolving `plugin:tool` specifications.

## Using Your New Plugin

After installation, verify the plugin is recognized:

```bash
mise plugins ls | grep my-lang

```

Reference tools from your plugin using the `plugin:tool` syntax:

```bash
mise use my-lang:my-tool@latest

```

The Vfox backend (implemented in [`src/plugins/vfox_plugin.rs`](https://github.com/jdx/mise/blob/main/src/plugins/vfox_plugin.rs)) handles version resolution and installation, while the Asdf backend ([`src/plugins/asdf_plugin.rs`](https://github.com/jdx/mise/blob/main/src/plugins/asdf_plugin.rs)) supports legacy plugins that expose `bin/list-all` executables.

## Summary

- **Create the structure**: Build a directory under `~/.local/share/mise/plugins/<name>` containing [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) and required hooks
- **Mark the type**: The presence of [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) triggers `PluginType::from_plugin_path` (lines 00222-00238) to classify it as a Vfox plugin
- **Register via source**: Use `mise plugins add` with a Git URL, local path, or ZIP file; `PluginSource::parse` (lines 00404-00433) handles the source detection
- **Install logic**: Git sources use `install_git_plugin_source` (lines 00776-00816) while local paths use `install_local_plugin_source` (lines 00709-00784)
- **Integrate seamlessly**: Once installed, plugins work with `mise use` and version management through the `PluginEnum` abstraction defined in [`src/plugins/mod.rs`](https://github.com/jdx/mise/blob/main/src/plugins/mod.rs)

## Frequently Asked Questions

### What is the difference between Asdf and Vfox plugins in mise?

Asdf plugins rely on executable scripts in a `bin/` directory (such as `list-all` and `install`), while Vfox plugins use Lua-based hooks in a `hooks/` directory and require a [`metadata.lua`](https://github.com/jdx/mise/blob/main/metadata.lua) marker file. Mise detects the type automatically via `PluginType::from_plugin_path` based on which files are present.

### Can I add a plugin directly in mise.toml without using mise plugins add?

Yes. The configuration parser in [`src/config/mod.rs`](https://github.com/jdx/mise/blob/main/src/config/mod.rs) supports a `[plugins]` section where you can declare plugins inline. When parsing [`mise.toml`](https://github.com/jdx/mise/blob/main/mise.toml), mise calls `PluginType::from_plugin_config` (lines 00207-00216) to resolve these declarations without requiring a separate installation step.

### How does mise handle plugin updates?

When you run `mise plugins update`, mise re-invokes the installation logic for Git-based plugins. The `install_git_plugin_source` function (lines 00776-00816) fetches the latest changes from the remote repository and updates the local checkout in `dirs::PLUGINS`.

### Why does my plugin need an available.lua hook?

The [`available.lua`](https://github.com/jdx/mise/blob/main/available.lua) hook tells mise whether the underlying tool is already installed on the host system. This allows mise to skip unnecessary installations when the language runtime exists outside of mise's management, optimizing the tool resolution pipeline.