Manifest (plugin.toml)

Every extension ships a plugin.toml at its root. It declares identity, transport, capabilities, runtime requirements, and any bundled MCP servers. The runtime parses and validates the manifest before spawning anything.

Source: crates/extensions/src/manifest.rs.

Minimal example

[plugin]
id = "weather"
version = "0.1.0"
name = "Weather"
description = "Fetch weather by city name."
min_agent_version = "0.1.0"
priority = 0

[capabilities]
tools = ["get_weather"]
hooks = []

[transport]
type = "stdio"
command = "./weather"
args = []

[requires]
bins = ["curl"]
env = ["WEATHER_API_KEY"]

[context]
passthrough = false

[meta]
author = "you"
license = "MIT OR Apache-2.0"

Sections

[plugin]

FieldRequiredPurpose
idUnique id. Regex ^[a-z][a-z0-9_-]*$, ≤ 64 chars. Must not be a reserved id (see below).
versionSemver.
nameHuman-readable label.
description≤ 512 UTF-8 chars.
min_agent_versionSemver. Checked against the running agent version at load time.
priorityi32, default 0. Lower fires first in hook chains.

Reserved ids: agent, browser, core, email, heartbeat, memory, telegram, whatsapp. The host may register more via register_reserved_ids().

[capabilities]

[capabilities]
tools = ["get_weather", "get_forecast"]
hooks = ["before_message", "after_tool_call"]
channels = []
providers = []

At least one capability list must be non-empty. Names match ^[a-z][a-z0-9_]*$, ≤ 64 chars, no duplicates.

[transport]

One of three forms:

# stdio — spawn a child process
[transport]
type = "stdio"
command = "./my-extension"
args = ["--verbose"]
# nats — talk over a NATS subject prefix
[transport]
type = "nats"
subject_prefix = "ext.myext"
# http — call over HTTP
[transport]
type = "http"
url = "https://localhost:8080"

Validation: command, subject_prefix, url non-empty; url must be http(s)://.

[requires]

[requires]
bins = ["ffmpeg", "imagemagick"]
env  = ["OPENAI_API_KEY"]

Declarative preconditions used for gating: when the runtime discovers the extension, it calls Requires::missing(). If any bins is not on $PATH or any env is unset, the extension is skipped (warn, not fail) and its tools are not registered.

See Stdio runtime — Gating.

[context]

[context]
passthrough = true

When true, every tool call sent to this extension has _meta = { agent_id, session_id } injected into the JSON args. Lets the extension tell calls apart per-agent without the runtime having to encode the split into every tool signature.

[mcp_servers] (phase 12.7)

Inline MCP server declarations bundled with the extension:

[mcp_servers.gmail]
type = "stdio"
command = "./gmail-mcp"
args = []

[mcp_servers.calendar]
type = "streamable_http"
url = "https://mcp.example.com/calendar"

Each server name must match ^[a-z][a-z0-9_-]*$, ≤ 32 chars. Alternatively, drop a sidecar .mcp.json next to plugin.toml if the manifest has no [mcp_servers] section.

Validation at a glance

flowchart TD
    READ[read plugin.toml] --> PARSE[parse TOML]
    PARSE --> ID{id valid?<br/>regex + length<br/>+ not reserved}
    ID --> VER{version<br/>valid semver?}
    VER --> MIN{min_agent_version<br/>satisfied?}
    MIN --> CAPS{at least one<br/>capability declared?}
    CAPS --> NAMES{capability names<br/>valid + unique?}
    NAMES --> TRANS{transport<br/>non-empty +<br/>http scheme valid?}
    TRANS --> MCP{mcp_server names<br/>valid?}
    MCP --> OK([Manifest accepted])
    ID --> FAIL([Diagnostic: Error])
    VER --> FAIL
    MIN --> FAIL
    CAPS --> FAIL
    NAMES --> FAIL
    TRANS --> FAIL
    MCP --> FAIL

Any failure produces a DiagnosticLevel::Error in the discovery report — the candidate is dropped but scanning continues so an operator sees every broken manifest at once.

Agent-version gating

[plugin]
min_agent_version = "0.2.0"

On load the runtime compares against the agent build version. A mismatch logs a diagnostic and drops the candidate. Useful for shipping a manifest that relies on a newer host API without crash-looping older deployments. The host can override the reported version for tests via set_agent_version().

Next

  • Discovery and NATS runtime — how the manifest drives spawn
  • CLIagent ext validate <path> checks a manifest without touching the registry
  • Templates — prebuilt skeletons to copy