Projects¶
A project is a purpose-built assistant captured in a heim.toml file: which
model to load, its system prompt, its MCP tools, and which libraries it reads. In a
project directory both heim chat and heim serve --ui bind to that definition —
so the app boots straight into the right model with its tools, no manual picking.
A project is portable and committable: it references models by name (never by absolute path) and can carry its own model files, so the whole directory moves to another machine and still runs.
Scaffold one¶
Two entry points, same wizard:
heim project init # scaffold heim.toml in the current directory
heim project new my-assistant # create a fresh directory and scaffold in it (like `cargo new`)
The wizard asks for a kind (chat/voice), a default model (a library model or a curated starter to pull), MCP tools from installed plugins, a system prompt, and a spoken-reply voice. Flags skip or change parts of it:
| Flag | Effect |
|---|---|
--model <name> |
Bind this model, skipping the model picker. |
--copy (--local) |
Copy the model into a project-local library/ (a fast local-disk copy if it's already in your shared library) — makes the project self-contained. |
--git |
git init the project and write a .gitignore that excludes library/ (the weights) and .env. |
--no-uv |
Skip the uv project (write only heim.toml, no pyproject.toml). |
--force |
Overwrite an existing heim.toml. |
A project is a uv project¶
By default the scaffolder also writes a pyproject.toml, making the project a
self-contained uv project. It pins heim and the project's
pip-installable MCP servers, so the project carries its own environment:
cd my-assistant
uv sync # build the project's .venv (heim + its MCP servers)
uv run heim serve --ui # runs this project's heim, not a global one
uv run heim chat
This is what makes a project truly portable: uv run heim uses the pinned heim and the
MCP servers installed into the project's .venv, instead of relying on a globally-installed
heim and runtime uvx fetches. Because the servers are real dependencies, their .mcp.json
commands drop the uvx runner. .venv/ is gitignored; uv.lock is committed for
reproducibility. (heim isn't on PyPI yet, so its pin is a local path source — replace it
with a version once heim publishes. Pass --no-uv for the plain heim.toml-only shape.)
For a complete worked example — a DHIS2 assistant with its model copied in, a project-local DHIS2 profile, and example prompts — see the DHIS2 assistant worked example.
Templates¶
--template <name> presets the whole wizard so a purpose-built assistant is reproducible in
one command — a model, a system prompt, tools, and example files:
| Template | Model | Tools |
|---|---|---|
dhis2 |
Ornith-1.0-9B (the benchmark winner) |
the DHIS2 bridge (read-only) + a profile template |
coder |
Qwen3-Coder-30B |
git + filesystem |
research |
gemma-4-12B |
search + fetch |
In a uv project the template's pip-installable tools are pinned in pyproject.toml
automatically; bundled heim servers and node (bunx) servers are left as-is. Override the
model with --model, and add more tools later with heim mcp add (also uv-aware).
What it writes¶
# heim.toml
libraries = ["library", "@shared"] # only when --copy is used; else just @shared
[project]
model = "unsloth/Qwen3.5-4B-GGUF"
system_prompt = "You are a concise, helpful assistant."
chat_voice = "kokoro:af_heart" # spoken-reply voice (Kokoro)
Tools are not in heim.toml — they live in a sibling .mcp.json (see below).
libraries— the libraries this project reads, in priority order (first match wins). With--copyit lists the project-locallibrary/first, then@shared(the machine default,HEIM_LIBRARY_ROOT). See The library.[project]— the bound model, system prompt, and spoken-reply voice..mcp.json— the project's MCP tool servers (standardmcpServersJSON).
A locked assistant¶
Binding [project].model makes the project a locked assistant: heim serve --ui
in that directory hides the model picker and serves only that model (the UI shows a
locked state); heim chat defaults to it. Outside a project, heim is free-play (pick
any model). Pass --model to override the binding.
Tools¶
Tools are the standard mcpServers JSON in ./.mcp.json (plus the machine-global
~/.config/heim/mcp.json, which merges in). Browse a curated catalog and add one with
heim mcp:
heim mcp list # curated servers + installed plugins (✓ = already in .mcp.json)
heim mcp add fetch # add to ./.mcp.json (--global for ~/.config/heim/mcp.json)
…or write the JSON by hand:
heim.toml's command is split like a shell line, so arguments work inline; there
is no env field, so pass environment variables with an env prefix when a
server needs them. See Tools (MCP) for the full picture, and the
DHIS2 assistant worked example for an end-to-end build (scaffold,
local model copy, external MCP server with a profile, and confirming the model calls
the tool).
Inspect¶
heim project show prints the resolved project: the bound model's detail card, the
system prompt, and the actual tools (it connects to the MCP servers and lists
what they expose, not just the server names).