Core Concepts

Addons

Wrap an external MCP server in a small manifest and install it into lean-ctx's gateway with one command — community extensions, no fork.

An addon packages an external MCP server behind a small lean-ctx-addon.toml manifest, so a third-party tool plugs into lean-ctx's MCP gateway with a single lean-ctx addon add — no fork, no recompile. Addons are user-global and reuse the gateway trust model: [gateway] is global-only and opt-in, and every install discloses exactly what it will run before wiring anything.

Install & manage addons

The lean-ctx addon command browses the registry and manages what is wired into your gateway.

# Browse
lean-ctx addon list              # installed addons + the registry
lean-ctx addon search markdown   # search the registry (empty = list all)
lean-ctx addon info lean-md      # an addon's details + MCP wiring

# Install / remove (both ask for confirmation)
lean-ctx addon add lean-md                  # from the registry, by name
lean-ctx addon add ./lean-ctx-addon.toml    # from a local manifest
lean-ctx addon remove lean-md               # unwinds exactly what it wired

# Scripts / CI
lean-ctx addon add lean-md --yes # skip the prompt

add appends a [[gateway.servers]] entry to your global config via the safe Config::update_global path and records it in <data_dir>/addons/installed.json. remove reverses precisely that — it never touches a server you wired by hand.

Installable vs. listed

Every registry entry is in one of two states, decided purely by whether it ships a runnable [mcp] block:

StateHas [mcp]?What happens
Installable Yes lean-ctx addon add <name> wires it into the gateway.
Listed No Shown as a directory entry that links to its homepage. Never installed with fabricated wiring.

Install on add — runners & the [install] block

There are two ways addon add makes a tool runnable, both pinned and disclosed before anything runs:

  • Ephemeral runner — when the [mcp] command is npx (Node) or uvx (Python/uv), the package is fetched and executed lazily on the first tool call, then cached. add only writes the [[gateway.servers]] entry; adding is installing, as long as the runner is on your PATH.
  • [install] block (Phase 2) — for tools that need a one-time bootstrap before a runnable command exists, the manifest declares a pinned package-manager install. On add lean-ctx runs it idempotently; on remove it uninstalls it.
[install]
manager = "uv"               # uv | pip | cargo | npm | brew
package = "headroom-ai[mcp]"  # the package spec the manager understands
version = "0.27.0"           # mandatory exact pin (no ranges / latest)
bin     = "headroom"          # binary the [mcp] command needs (PATH idempotency)

Install matrix — what each ecosystem tool needs

Not every tool is a one-line runner. The registry is honest about which integrations install on add today, which still need a manual bootstrap, and which need secrets to function:

ToolAdd = install?Wiring / bootstrapSecrets
repomixYesnpx -y repomix@1.15.0 --mcp (lazy runner)
serenaYesuvx --from serena-agent==1.5.3 serena start-mcp-server
sequential-thinkingYesnpx -y @modelcontextprotocol/server-sequential-thinking@…
everythingYesnpx -y @modelcontextprotocol/server-everything@…
headroomYes[install]: uv tool install headroom-ai[mcp]==0.27.0headroom mcp serve
graphifyListedPackage installs cleanly, but its MCP server needs a pre-built graph.json (no out-of-the-box server)
cogneeListedMCP server needs a repo clone + uv sync (upstream #1815); no pinned one-liner
lettaListednpm i -g letta-mcp-server + a running Letta backendLETTA_API_KEY
mem0ListedOfficial MCP server (hosted)MEM0_API_KEY
claude-contextListednpx @zilliz/claude-context-mcpOPENAI_API_KEY + Milvus
rtkListedPrimarily a shell-output hook; reaches MCP via the rtk-mcp bridge
lean-mdListedMarkdown directive layer — no MCP endpoint

headroom is the flagship [install] integration: addon add headroom runs the pinned uv tool install, wires headroom mcp serve, and addon remove uninstalls it. Tools marked Listed either need secrets/a backend or don't ship a clean, pinned, out-of-the-box MCP server yet — each flips to install-on-add with a one-line registry change the moment upstream ships one. See the bootstrap-engine design.

The manifest: lean-ctx-addon.toml

The manifest is the contract an addon author writes. The same shape is reused as a registry entry, so a curated catalog and a hand-written manifest deserialize into one type.

The [addon] table — metadata

KeyTypeNotes
namestringStable slug [a-z0-9-]; becomes the gateway server name.
display_namestringHuman-friendly name for UIs (falls back to name).
versionstringAuthor-declared version (free-form).
descriptionstringOne-line summary shown in addon list and on the website.
authorstringMaintainer or org.
homepagestringProject or repository URL.
licensestringSPDX id, e.g. Apache-2.0.
categoriesstring[]Coarse buckets for browsing, e.g. plans, workflow.
integrationstringTyped L4 adapter for the output pipeline: codebase-pack, code-graph, code-symbols, memory, compression or none. Empty derives from categories.
keywordsstring[]Free-form search keywords.
min_lean_ctxstringMinimum lean-ctx version the addon targets (informational).
verifiedboolRegistry-controlled trust tier. true only for maintainer-audited entries — setting it yourself is meaningless.

The [mcp] table — wiring

The [mcp] block mirrors a gateway server, so installation is a direct translation. Omit it and the entry is listed; include it and the entry is installable.

# stdio — lean-ctx spawns a local binary and speaks MCP over stdin/stdout
[addon]
name = "my-addon"
display_name = "My Addon"
version = "0.1.0"
description = "What it does, in one line."
author = "you"
homepage = "https://github.com/you/my-addon"
license = "Apache-2.0"
categories = ["workflow"]
keywords = ["example"]
min_lean_ctx = "3.8.0"

[mcp]
transport = "stdio"          # spawn a child process
command = "my-addon-mcp"     # executable to run (uvx/npx works too)
args = ["serve"]
# env = { MY_ADDON_HOME = "~/.my-addon" }

For a remote server, swap the transport for HTTP:

[mcp]
transport = "http"
url = "https://my-addon.example.com/mcp"
# headers = { Authorization = "Bearer ..." }

Deep integration: L4 typed adapters

A passthrough addon's output is opaque text. A typed integration folds that output into the matching lean-ctx store or retrieval surface, so it shows up in the same ctx_* tools your agent already uses — no second mental model. The owning [[gateway.servers]] entry carries an integration slug (set automatically from the addon's category, or by hand), which the proxy reads on the hot path with no catalog lookup.

integrationExample addonsWhat the adapter doesSurfaces in
codebase-packRepomixRepo pack → retrievable archive handlectx_expand
code-graphGraphifyNodes/edges → property graphctx_callgraph
code-symbolsSerenaReferences → property-graph call edgesctx_callgraph
memoryMem0 · Cognee · LettaMemories → knowledge factsctx_knowledge
compressionHeadroom · RTKRegistered as a named lean-ctx compressorcompressor pipeline
nonedefaultGeneric L1–L3 (compress · spill · index) only

Set it explicitly when the category isn't a giveaway:

[addon]
# …
categories = ["code-intelligence"]
integration = "codebase-pack"   # codebase-pack | code-graph | code-symbols | memory | compression | none

Build & publish your own

  1. Expose your tool as an MCP server — a stdio binary or an HTTP endpoint. This is the runtime extension lean-ctx runs or connects to.
  2. Add a lean-ctx-addon.toml to your repo with the tables above.
  3. Test it live, locally: lean-ctx addon add ./lean-ctx-addon.toml runs the full install flow against your own gateway. Remove it with lean-ctx addon remove <name>.
  4. Get listed: open a merge request adding your entry to rust/data/addon_registry.json. Once merged, anyone can install it by name and it appears on the Addons page.

Security model

An addon is executable trust: a stdio addon spawns a child process with your privileges, an http addon sends your context to a remote endpoint, and its tool output flows into the model (a prompt-injection surface). lean-ctx defends this in depth.

Baseline (always on)

  • Full disclosure. add prints the exact transport, command, args and env it will wire before doing anything.
  • Explicit confirmation. It asks for a yes/no; non-interactive shells refuse without --yes.
  • Global-only, opt-in. Wiring goes through the same trust model as [gateway]; nothing is enabled implicitly. The registry is curated and compiled into the binary — no live fetch.
  • Reversible. remove unwinds exactly what add wired, leaving hand-authored servers untouched.

Trust tier & risk review

Entries are verified (maintainer-audited) or community (installable, unaudited), shown in addon list/info and the install preview. Before install, lean-ctx statically reviews the [mcp] wiring and flags risk signals — remote endpoints, non-HTTPS urls, inline shells (sh -c), fetch-and-exec (curl), unpinned upstreams, secret-bearing env — at info/warn/danger. The same checks run in CI over the bundled registry.

Install policy floor — [addons]

A global-only config block (never merged from a project-local file). Permissive by default:

KeyValuesEffect
policyopen · verified_only · allowlist · lockedWhat may be installed.
allowliststring[]Permitted slugs when policy = allowlist.
require_signatureboolHonour a user-override registry only if signed by a trusted org key.
sandboxoff · auto · strictSandbox spawned stdio servers.
block_riskyboolRefuse to install an addon with a danger finding.
lean-ctx config set addons.policy verified_only   # or allowlist / locked
lean-ctx config set addons.sandbox strict          # macOS sandbox-exec / Linux bwrap
lean-ctx config set addons.block_risky true
lean-ctx config set addons.require_signature true
lean-ctx config set addons.allow_bootstrap false   # forbid [install] package-manager execution
lean-ctx config schema addons                      # inspect every key

Sandboxing & runtime

  • Sandbox. addons.sandbox = auto blocks outbound network for spawned stdio servers; strict also makes the filesystem read-only and refuses to spawn if no launcher (sandbox-exec/bwrap) exists. Off by default.
  • Untrusted output. Downstream tool output is scrubbed for secrets and audit-tagged as untrusted before it reaches the model.

Teams: distribute [addons] via MDM, or pin it through the signed org-policy floor to make it un-bypassable.

Reference