Bundles
A bundle is a single document that describes everything a working setup needs: the MCP servers, the skills, the agents wired to both, the channels that reach them, the schedules that run them, the policies that constrain them, and the values a user has to supply. Installing one turns that document into real workspace resources.The problem
A useful agent is never one object. It is an agent plus two MCP instances plus three skills plus a cron trigger plus a spend cap — and the agent references the MCP instances by id, and the trigger references the agent by id, and every id is workspace-local. Sharing that setup by writing instructions produces a twenty-step guide where step fourteen silently depends on step three. Sharing it as a database export produces something that cannot be installed anywhere else, because every reference is a UUID that means nothing in another workspace. And in both cases the person installing it finds out what credentials it needs by hitting an error part-way through, after resources have already been created.How AgentArea approaches it
One fully-inlined document, referenced by key
Schema version0.1.0. Every entity has a key — a stable in-document
identifier matching [a-zA-Z][a-zA-Z0-9_]* — and entities reference each other
by key, never by database id. An agent lists mcps: [github] and skills: [triage]; an automation names agent: triage_bot. The installer resolves keys
to real ids at install time, which is what makes the document portable.
Secrets never appear in the document
ASetupField declares a key, a label, a type, and whether it is required.
Everything else in the bundle references it as ${setup.<key>}:
env_schema entry as a secret based on
SetupField.type, rather than relying on the MCP service’s name-based
heuristic. The bundle already declared which inputs are credentials, so guessing
from the variable name is unnecessary and less accurate.
Analyze, then install
POST /v1/bundles/analyze parses the source and returns an ImportPreview —
every entity tagged CREATE, REUSE, or SKIPPED with a reason, plus a list
of issues. It writes nothing. This is where a missing setup field, an agent
pointing at a nonexistent MCP key, or a command runtime the platform cannot run
is surfaced.
POST /v1/bundles/install takes the canonical bundle and the setup values.
Missing required setup blocks the whole install before anything is written.
Analyze accepts exactly one of source (pasted text) or source_url (fetched
server-side behind an SSRF guard, capped at 5 MB).
Install order is dependency order
MCPs, then skills, then agents, then channels, then automations, then policies. Each step returns the key-to-id map the next one needs. An agent whose MCP was skipped is still created; an automation whose agent was not created is skipped with that reason recorded, rather than failing the install.Idempotency is by name
Every step checks whether the named entity already exists in the workspace. An MCP instance, skill, agent, or trigger that exists is markedREUSED and not
duplicated. Triggers are named {bundle_name}:{key} so they are unambiguous
across bundles. An InstalledBundle row is upserted by bundle name, recording
the canonical document and the full install result.
Re-running an install is therefore close to a no-op for what already exists.
Automations and channels install disabled
enabled defaults to false on both, and the installer explicitly disables a
cron trigger after creating it — trigger creation schedules unconditionally, so
without that step an imported automation would start running before the user had
confirmed its credentials. Connect, verify, then activate.
Bundles in the catalog
A registry of typebundles holds catalog items whose spec is the bundle
document, keyed by the bundle’s name. Nothing is materialized on sync; the
bundle is provisioned into a workspace on demand through the same install path.
Why not a repository or an archive
A directory layout or a tarball is the familiar packaging shape, and it gives up the property that matters most here: a bundle must be analyzable before anything is written. A single inlined document can be pasted into a form, fetched in one request, diffed in a pull request, reviewed by a human, and previewed against a live workspace. An archive means fetch, unpack, and then trust — and the fetch itself becomes an attack surface with a much larger blast radius than a size-capped text fetch behind an SSRF guard.Why not reference resources by id
Ids are workspace-local. A bundle that names them can be exported from the workspace that built it and installed nowhere else, including back into the same workspace after those resources were recreated. Keys cost an indirection at install time and buy portability, which is the entire point of the format.Why not install first and report failures after
The preview exists because a half-finished install is worse than a refused one. An MCP instance created with a credential, orphaned because the agent step then failed, is a live secret nobody is tracking. Blocking on missing required setup before the first write, and reporting unsupported entities asSKIPPED in a
preview, moves those failures to a point where nothing has been created yet.
Limits
- Only canonical YAML or JSON is parsed.
parse_bundleloads the document and validates it against theBundleschema. There is no adapter that converts a Claude plugin manifest, a repository layout, or any other external format — despite what the format module’s own docstring implies. - Install is not transactional. The steps run in order and a failure part-way through does not undo what earlier steps created. Existence-by-name makes a re-run mostly idempotent, which is the mitigation, not a rollback.
- Command MCPs are restricted to eight runtimes.
npx,uvx,uv,python,python3,node,bunx,deno. A command that is a local path or references${CLAUDE_PLUGIN_ROOT}is skipped with a reason recorded, not failed. Docker and URL MCPs are always accepted. - Channels are Telegram only and automations are cron only in
0.1.0. The schema is a closed literal in both cases, so anything else fails validation. - Skills support inline content and GitHub only. Zip and S3 import are not in
0.1.0. - There is no uninstall.
InstalledBundlerecords what was installed, and removing those resources is manual. The API surface is analyze and install. - Reuse is by name, not by identity. An existing agent named
Triage Botis reused whether or not it came from this bundle, and its configuration is not reconciled against the document. extra="forbid"everywhere. An unrecognized field anywhere in the document is a validation error, not a warning. A bundle authored against a newer schema version is rejected outright rather than partially understood.
Related
- Registry and catalog — how a bundle reaches a workspace as a catalog item.
- MCP — what the
mcpssection provisions.