Source of truth: https://zed.dev/docs/extensions/mcp-extensions and the zed_extension_api crate.
A Zed extension is a Git repository with an extension.toml manifest. Procedural parts are written in Rust and compiled to WebAssembly via cargo build --target wasm32-wasip1. Extensions can ship languages, themes, icon themes, snippets, debuggers, slash commands, and MCP servers (a.k.a. context servers).
Rust must be installed via rustup. Homebrew Rust does not work for Zed dev extensions.
Minimum:
my-extension/
├── extension.toml
├── Cargo.toml
├── LICENSE # Required at extension root (or at the configured path)
└── src/lib.rs
A maximal extension can also include languages/, themes/, snippets/, debug_adapters/, icon_themes/, etc.
id = "my-extension"
name = "My Extension"
version = "0.0.1"
schema_version = 1
authors = ["Your Name <you@example.com>"]
description = "Example extension"
repository = "https://github.com/your-name/my-zed-extension"
[context_servers.my-context-server]
# Bind a context server id to this extension.
# Rust code must implement context_server_command for this id.Naming rules:
idmust not containzed,Zed, orextension.- IDs should hint at the purpose. Themes get
-theme, snippet packs get-snippets, etc.
[package]
name = "my-extension"
version = "0.0.1"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
zed_extension_api = "0.7.0" # Use the latest compatible version on crates.iostdout/stderr from the extension are forwarded to the Zed process. Launch Zed with zed --foreground to see dbg!/println! output.
use zed_extension_api as zed;
struct MyExtension;
impl zed::Extension for MyExtension {
fn new() -> Self { Self }
fn context_server_command(
&mut self,
_context_server_id: &zed::ContextServerId,
_project: &zed::Project,
) -> zed::Result<zed::Command> {
Ok(zed::Command {
command: "my-server-binary".to_string(),
args: vec![],
env: vec![],
})
}
}
zed::register_extension!(MyExtension);The extension API only supports spawn-based context servers — context_server_command returns a Command (binary + args + env) that Zed runs on the host. There is no extension-side API to register a remote URL the way ~/.config/zed/settings.json allows:
{
"context_servers": {
"latitude": { "url": "https://api.latitude.so/v1/mcp" }
}
}So for a remote OAuth MCP, the extension has to bridge — typically via npx mcp-remote <url>, which requires Node.js on the user's machine.
If you need to download the MCP server from GitHub Releases / npm / etc., the docs recommend doing it inside context_server_command (or the relevant API on first use). Extensions must not ship server binaries themselves.
As of 2025-10-01, extension repositories must include one of these licenses:
- Apache 2.0
- BSD 2-Clause / BSD 3-Clause
- CC BY 4.0
- GNU GPLv3 / LGPLv3
- MIT
- Unlicense
- zlib
File name must start with LICENCE or LICENSE (case-insensitive). The license requirement covers extension code only — bundled tools (language servers, etc.) keep their own licenses.
- From Zed's Extensions page, click Install Dev Extension (or run the
zed: install dev extensionaction). - Pick the extension folder. The published version, if any, is overridden during the dev session.
- To debug:
zed --foregroundfor verbose logs.zed: open logshowsZed.log.
- Fork
zed-industries/extensions. Forks to a personal account are easier for Zed staff to push fixes to than forks to org accounts. - Clone and run:
git clone <your-fork> cd extensions git submodule init git submodule update
- Add the extension as a Git submodule (must use HTTPS, public repo, on a branch — not a detached commit):
If the extension lives in a subdirectory of the submodule, also add
git submodule add https://github.com/<you>/<repo>.git extensions/<id> git add extensions/<id>
path = "subdir"to the entry. - Add an entry to the top-level
extensions.toml:Version must match what's in the extension's[<id>] submodule = "extensions/<id>" version = "0.0.1"
extension.tomlat that commit. - Run
pnpm sort-extensionsto keepextensions.tomland.gitmodulessorted. - Open the PR.
Once merged, Zed packages and publishes the extension to its registry.
- ID and name must not contain
zedorextension. - ID should hint at purpose (
-theme,-snippets, etc., when applicable). - Provides something not already in the marketplace (prefer contributing to an existing extension over forking).
- Does not ship a language server / MCP server binary — must download or use the host environment.
- Themes / icon themes are not bundled with feature extensions; ship separately.
Open another PR:
- Update the submodule to the new commit:
git submodule update --remote extensions/<id>
- Bump
versioninextensions.tomlto match the newextension.toml. - If the repo's license changed, ensure it's still one of the accepted ones.
A community GitHub Action exists to automate the version-bump PR.