Ship tools and MCP servers
01A package can deliver programs: command-line tools built on install, or a server your agent talks to. This page declares both, builds them in the package's own folder, and explains why a server pins the exact version of the tools it serves.
In the current VibeVM project, create the tool package org.acme/notes-tools as an in-tree package with a small Rust crate crates/notes-check inside it, declare the crate as a binary named notes-check, install the package into the project, build the tool through vibe, and run it through vibe bin exec with --help to prove the dispatch works.
the vibevm skill installed for your agent; a package with a Cargo workspace at its root and a binary crate; the Rust toolchain on the PATH
the manifest carries a [[binary]] table; vibe bin list shows the tool; vibe bin build produced it in the package's own target folder; vibe bin exec notes-check -- --help prints the tool's help
vibe bin listvibe bin exec notes-check -- --help
What happens
03The agent scaffolds the package slot with vibe init package, puts the crate inside it, adds a [[binary]] table naming the tool and the crate directory, and installs the package into the project from the project's own registry. It runs vibe bin build, which asks for consent and runs a release build inside the package's own workspace. Then it runs vibe bin exec, which resolves the tool through the project's lock file to the artifact of the exact version installed and runs it. Consumers get the same: installing the package materialises its source, building on first use produces the tool beside it, and the artifact never enters the package's fingerprint.
04 MUST: a package declares its binaries; vibe builds and dispatches them.
05 Build output sits outside the shippable tree (PROP-024 §2.2), so content hashes never move.
Code in a package
06A package is a project made installable, so it may carry arbitrary code at its root beside vibevm/vibespecs/: a Cargo workspace, crates, tests. The shippable tree is the source minus build output; target/, node_modules/ and anything in .vibeignore never travel. Consumers receive the source and build it themselves, which keeps identity a property of what the author committed.
07 The package root holds arbitrary code (e.g.Cargo.toml+crates/) andvibe.toml, exactly as a project root does. Code is optional — a prompt-only package (e.g.discipline-core) simply has no code at its root.
08 Why. Identity is the source, never build artifacts: build output is non-deterministic (timestamps, host paths, incremental state) and may be gigabytes — hashing or copying it would make identity unstable and materialisation ruinous, the exact failure PROP-022 §1.1 names for "big in file count".
09A code-bearing package keeps its own workspace manifest, and a consumer that is itself a Rust project excludes the dependency tree from its own workspace, so the two builds never collide.
10 Decision. A code-bearing package carries its own workspace manifest (for Rust, a rootCargo.tomlwith[workspace]) — it is a standalone, independently-buildable project.
Binaries
11Each tool is one [[binary]] entry: a name, unique in the package, and a crate, a directory inside the shippable tree with a Cargo.toml. vibe bin list shows what the installed packages declare, vibe bin build builds the named tools or all of them, vibe bin path prints an artifact's location, and vibe bin exec <name> -- <args> runs it through the lock file. Building executes the package's build scripts, so it asks for consent the first time, like an install does.
12
A code-bearing package declares each shipped tool in its vibe.toml:
13 Successor to the historical first-build prompt. Building executes package build scripts and proc-macros, so installation plus explicit target/route selection is the authorisation and the engine narrates the exact provider and target before execution. Lifecycle build does not add an allow-list, first-run prompt or--allow-hooksanalogue; provider identity, artifact records and outcomes supply the audit trail. Existing directvibe bincompatibility remains routed through its declared package rather than silently choosing ambient code.
14The name is unique within the package and should be safe from collisions across packages, which the family prefix gives you for free. The crate names a folder inside the shippable tree with a Cargo package whose binary is called exactly name.
15
Constraints: name MUST be unique within the package and SHOULD be
globally collision-safe (the family-prefix convention, PROP-028 §2.4).
16crateMUST name a directory inside the shippable tree carrying a[[bin]]-bearing (or default-bin) Cargo package whose bin name equalsname.
MCP servers
18A package of kind mcp delivers a server an agent talks to: one or more [[mcp_server]] tables, each naming the binary that serves it and its arguments. The server is built like any binary and registered into the agents' configurations by vibe mcp install, beside vibe's own server. Once built, it runs without vibe: the agent launches the artifact directly.
19
An mcp package is one whose primary deliverable is one or more
Model Context Protocol servers.
20 An mcp package's servers run without vibe: the artifact is launched by the agent host directly from the slot path, links its vendored engines, and speaks stdio MCP.
21A server that serves another package's toolchain, the gates of a language discipline for example, must require that package with an exact pin, =X.Y.Z. The engines behind the agent's tools and the gates the consumer runs must resolve to one version set: one engine, one truth, enforced by the resolver rather than by the protocol.
22 The pin closes it: every[requires.packages]entry of anmcp-kind package MUST be an exact=X.Y.Zrequirement — the resolver holds the served engines and the consumer's gates to ONE version set; no runtime handshake exists or is needed.
23The kind promises a server: an mcp manifest without an [[mcp_server]] table is refused. The server is one of the package's own binaries, so binary must name a [[binary]] of the same manifest, and its delivery, consent and staleness follow the binary machinery. In args, the only substitution is {project_root}, resolved at registration; an unknown token is refused.
24 Anmcp-kind manifest that declares NO[[mcp_server]]is refused: the kind promises a server.
25 The server IS a PROP-025 binary: delivery, consent, staleness, and slot residence come from that machinery wholesale —binarymust resolve to a[[binary]]declared in the same manifest.
26argsmay carry substitution tokens ONLY from the closed set{project_root}(the absolute, verbatim-free root of the consuming project, resolved at registration time); unknown{…}tokens are refused at validation.
Install hooks
27A package may run a script when it is installed. [hooks] names a base path without extension, and the package ships <base>.sh, <base>.ps1 or both; the runner picks the one for the host. pre-install runs as soon as the package's folder is complete and before vibe uses it; post-install runs after the install is durable, with the lock written and the boot files regenerated. The working directory is the package's own folder in the dependency tree, and the environment names the package's group, name, version, kind and folder, and which of the two moments it is. A hook's edits to files vibe owns are ephemeral: a reinstall or an update restores those bytes and runs the hook again.
28 The value is a base path without extension; the runner resolves.sh/.ps1beside it per §2.2.
29 A package ships a phase script as<base>.sh(portable, POSIX shell) and/or<base>.ps1(PowerShell). The runner picks per host:
30pre-install— runs immediately after the package's slot is fully populated (content materialised, submodules fetched per PROP-021) and before vibevm uses the slot (before boot regeneration, before any latervibe skillprojection reads it). This is the "bring the tree into order" hook.
31
post-install — runs after the install run is durable for that package
(lockfile written, boot artefacts regenerated). For finalisation that needs
the package already registered.
32 The hook's working directory is the package's materialised slot; it sees exactly the tree vibevm will use.
33 The runner passes a documented environment:VIBE_PACKAGE_GROUP,VIBE_PACKAGE_NAME,VIBE_PACKAGE_VERSION,VIBE_PACKAGE_KIND,VIBE_PACKAGE_DIR(the slot, also CWD),VIBE_HOOK_PHASE. (PROP-024 §2.3 addsVIBE_PROJECT_ROOT, the workspace absolute root, so a build hook can target a gitignored build dir outside the slot; it lands with that work.)
34
Only a hook's edits
to materialiser-owned recorded payload are ephemeral: reinstall, update or
integrity repair restores those bytes per
PROP-022, then reruns hooks exactly
when that payload diff is nonempty. Hook-created unrecorded state is outside
.vibe-slot.toml ownership and survives by design; a rerun may compound it,
so hooks must be idempotent until a separate hook-output ownership contract
exists.
Edge cases and rules
35A tool's artifact belongs to the exact version installed; after an update the old artifact is not trusted, and the next vibe bin exec builds the new version first.
36 Current-slot existence alone is not trust. The selected artifact must belong to the current resolved provider root and pass the shared record's exact source/config/platform/provider/output-path/digest revalidation. A same-slot refresh preserves unrecorded build output; a version change moves current-root identity and cannot reuse the old slot's record.
37Building needs the language's own package sources, from crates.io for Rust, unless they are vendored; an offline build says so rather than pretending.
38
Cargo needs crates.io for third-party deps unless the
local cargo cache is warm: offline boxes get the same honest failure
cargo gives, plus the hint that cargo install --path <slot>/crates/…
(the documented degraded path, which stays valid indefinitely) has the
same network shape — there is no offline shortcut to a first build.
39The [[mcp_server]] table is legal only in packages of kind mcp; a tool package ships binaries but no servers.
40 The[[mcp_server]]table (§2.2) is legal only in this kind — the kind IS the taxonomy, enforced byManifest::validate, not advisory.
41A .vibeignore at the package root adds globs to the list of build output that never enters the shippable tree.
42
Optional .vibeignore at the package root — newline-delimited globs added
to the §2.2 build-output denylist.