Central library

Declare once.
Run anywhere.

Your skills and MCP servers live in one place — ~/.agentstack/lib. Each project is just a short file of names. agentstack renders the real config for every CLI, with secrets resolved per machine and never committed.

sources                 the library                 each project
local dirs ───┐
git skills ───┼──▶  ~/.agentstack/lib  ◀──── lib add · consolidate
catalog ──────┘     ├─ library.toml
                    ├─ skills/sql-review/
                    └─ servers/kibana.toml

# a project selects by NAME — nothing copied:
[profiles.backend]
servers = ["kibana"]
skills  = ["sql-review"]

# use --write renders the views the CLIs actually read:
.mcp.json · .claude/skills/ · ~/.codex/config.toml

Three ownership rules explain this whole page — and the digest lock makes the same names resolve to the same content on every machine, or fail loudly.

Mental model

Three ownership rules

agentstack owns definitions
Skills and MCP server definitions live once under ~/.agentstack/lib, indexed in library.toml.
projects own selection
A project profile names what it needs. It never copies the skill or server definition — an inline [servers.*]/[skills.*] table, when present, deliberately wins over the library.
providers receive views
Provider folders get symlinks or generated config entries only. agentstack never owns ~/.claude, ~/.codex, or any provider folder wholesale.

Choose your path

Start from where you are

You don't have to migrate everything at once.

Starting pointWhat to do
Existing project with inline [servers.*] / [skills.*] Keep it working. Run agentstack doctor, then centralize one capability at a time.
Skills scattered across provider folders agentstack consolidate — preview, then --write. Skills move into the library; originals become symlinks.
New project Add capabilities to the library first; the project manifest holds names only.
Many repos, no per-repo files agentstack connect --all --write once, then review + agentstack trust . per repo and open the smallest profile lease. See the recommended composition.
Team or second machine lock, doctor, and explain verify that the same names resolve to the same digests — or fail loudly. The gateway enforces the pins at serve time: a library definition that drifted from its lock is refused, not silently served.

Quick start

Library entry → name reference → running CLI

$ agentstack lib add-server kibana --file kibana.toml --write
$ agentstack lib add sql-review --path ./sql-review --write
 copied ./sql-review → ~/.agentstack/lib/skills/sql-review
  the library copy is now canonical — edits to the source have no effect

# the whole project manifest:
$ cat .agentstack/agentstack.toml
version = 1

[profiles.central]
servers = ["kibana"]
skills  = ["sql-review"]

[targets]
default = ["claude-code"]

$ agentstack use central --write   # render views + pin digests
$ agentstack lock                  # or: pin WITHOUT rendering anything
$ agentstack explain kibana        # origin, provenance, lock, secrets

agentstack lock is the clean-at-rest companion: it resolves every profile's name refs and pins them in agentstack.lock without materializing a single file. The lock is part of the trust digest, so re-pinning re-gates the repo — expect to re-run agentstack trust . after locking.

Existing projects

Centralize one capability at a time

before — repo-local

[servers.kibana]
type = "http"
url  = "https://kibana.example.com/mcp"

[servers.kibana.headers]
Authorization = "Bearer ${KIBANA_TOKEN}"

[skills.sql-review]
path = "./skills/sql-review"

[profiles.backend]
servers = ["kibana"]
skills  = ["sql-review"]

after — central names

[profiles.backend]
servers = ["kibana"]
skills  = ["sql-review"]

The profile lines don't change — the definitions just moved home. Migrate a skill with lib add <name> --path … --write, a server with lib add-server <name> --from-manifest --write, then delete the inline table.

Inline override is intentional: while a project still defines [servers.kibana], that local definition wins. Remove the inline table only when you want the central copy used.

Commands

The library toolbox

CommandWhat it does
lib add <name> --path|--gitCopy a skill into the library (--replace to update). From a git repo, --subpath <dir> (or --git <url>#<dir>) installs a skill living in a subdirectory — marketplace/monorepo layouts. Content-scanned before it lands; warns on temp-dir sources and skills over ~10 MiB.
lib add-server <name> --file|--from-manifestStore a reusable server definition — ${REF}s intact.
lib syncVersion the whole library as a git repo across machines (--init --remote to set up or clone, then commit + pull + push). The store cache stays local; a fail-closed gate blocks a literal secret in any server field — or in the outgoing commits — from travelling.
lib list / lib removeSee or drop entries; provenance and checksums included.
search / add from <id>Find shipped skills + registry servers and install them; the bundled catalog ships run-codex, sync-library, analyze-usage, route-by-cost, using-agentstack, and more.
consolidateSweep skills from every CLI's folder into the library, symlinking originals back. Preview first.
lib migrateCopy a legacy ~/.agentstack/skills/ home in — copy-first, reversible.
lock [--profile <p>]Pin every profile's name refs in the lockfile without rendering configs or materializing skills.
explain <name> / doctorProvenance, lock status, drift — with the exact fix command.
audit --calls / optimizeEvidence from the gateway call log; optimize --write applies only the provably-safe class.

Secrets & locking

Definitions, never values

safe central definition

[headers]
Authorization = "Bearer ${KIBANA_TOKEN}"

At render or gateway time the local resolver turns ${KIBANA_TOKEN} into that machine's value — env, varlock, OS keychain, or .env.

never in the library

[headers]
Authorization = "Bearer real-token-value"

lib add-server warns on literal secrets, and lib sync blocks a push that would carry one — in any field (headers, env, url, args), in a definition it can't parse (fail closed), or buried in an outgoing commit the working tree no longer shows. Skill content is scanned on every lib add before the copy becomes canonical — hidden Unicode blocks, injection heuristics warn; install/audit re-scan everything materialized.

ItemLockedNot locked
SkillContent checksum + source locatorGenerated provider copies
MCP serverDefinition checksum with ${REF}sResolved secret values, per-CLI render shape

Field guide

Every file you might encounter

Provider views

What the CLIs actually read

~/.claude.json after use --write

{
  "mcpServers": {
    "kibana": {
      "type": "http",
      "url": "https://kibana.example.com/mcp",
      "headers": {
        "Authorization": "Bearer local-secret"
      }
    }
  }
}

why that's fine

  • The provider view is generated output, backed up before every mutation.
  • Only agentstack-owned entries are changed or pruned — hand-added ones are guarded, and drift hints offer adopt before apply --write.
  • The resolved secret lives only in the machine-local view; the lock never contains it.
  • The library remains the single source of truth.

Prove it

The runnable sandbox

Every flow on this page is verified by a sandboxed demo that never touches your real provider folders:

cd agentstack-test
./demo-central-library.sh     # the central-library architecture
./demo-closed-loop.sh         # pack install → apply → firewall → receipts → upgrade
Animated terminal: install a versioned pack, apply everywhere, firewall a tool, watch the audited refusal, upgrade
The closed loop — a vendor pack installed at its tag, firewalled, audited, and upgraded.

Status

Implemented and next

Current implementation status — the releases page is the source of truth for shipped versions.