> ## Documentation Index
> Fetch the complete documentation index at: https://docs.plungeai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugins — bundles of skills, MCP servers, and scripts injected as one unit

> A plugin is a Claude-Code-format bundle stored on the platform: a directory tree containing any number of SKILL.md skills, an optional .mcp.json (or manifest mcpServers) documenting the MCP servers its skills were written for, and executable Python scripts.


<!-- sources-of-truth: core/core-base/pack-enrich.ts (expandPlugin), orchestration/cnl-engine/schema-types.ts | last-synced: 2026-09-24 -->
A **plugin** is a Claude-Code-format bundle stored on the platform: a directory tree
containing any number of `SKILL.md` skills, an optional `.mcp.json` (or manifest
`mcpServers`) documenting the MCP servers its skills were written for, and
executable Python scripts. Declaring ONE plugin on a mission indexes the whole
bundle's skills and scripts in a single id — its documented MCP servers are NOT
auto-connected (below).

Real plugins are big (a serious finance bundle ships a dozen skills, several
scripts, and references to multiple MCP servers), which drives the design below:
plugins are **indexed, never dumped**.

## Declaring a plugin

```yaml
- type: harness
  goal: "Build a valuation snapshot for {input}"
  mission: |
    You are a buy-side analyst.
  plugins: [finance-bundle]
```

Also accepted flat on the task alongside `skills`/`experts`/`mcp` (see
`plungeai-missions` for the full authoring contract), and stored pre-built agent
cards may declare plugins that merge into every run referencing the card.

## What expansion actually injects

When the run starts, each declared plugin expands into two injected things
(skills, scripts) plus one thing it deliberately does NOT do (auto-connect MCP
servers):

### 1. A skill INDEX — progressive disclosure, deliberately

Every bundled `SKILL.md` is indexed by **name + description only** (read from its
frontmatter). The agent's prompt gets:

```
## PLUGIN CAPABILITIES

These skills come from the plugins in this mission. Only their names and
descriptions are loaded. When one is relevant, read its full instructions FIRST with
load_skill {type: "plugin_skill", plugin: "<plugin>", path: "<path>"} — then follow them.
- **dcf-modeling** — Build a discounted-cash-flow model from …
  (plugin: finance-bundle · path: skills/dcf-modeling/SKILL.md)
- …
```

Why an index and not the bodies: injecting bodies would burn the run's entire eager
prompt budget (24 KB shared — see `skills.md`) on text the agent may never need, and
an arbitrary-order "first five" cutoff would leave the rest unreachable. With the
index, **every** bundled skill is discoverable and the agent pays for a body only
when it decides it is relevant. There is no cap on index size.

Consequence: a plugin skill influences the run ONLY if the agent loads it. If a
particular bundled procedure MUST apply, say so in the mission text ("follow the
dcf-modeling skill from the finance bundle") — the agent will then load it first.

### 2. MCP servers are NOT auto-connected — declare them yourself

A plugin's `.mcp.json` / manifest `mcpServers` are the connectors its bundled
skills were WRITTEN FOR, not an instruction to connect them: `expandPlugin`
deliberately ignores them. An earlier version folded them into the run and
silently connected 7–12 paid SaaS servers per plugin for runners who never
authorized them (a wall of 401/`needs_connection` steps before the first turn) —
so declaring a plugin injects only its skill index and script index (below);
the run's live MCP set is the mission's own `mcp:` field alone. If a bundled
skill needs a server, declare that server id yourself in `mcp:` alongside the
plugin (see the skill's own text/card for which id to use — `plungeai-tools-connectors`
for discovering connector ids).

### 3. A script index for `run_python`

Every bundled `.py` file is indexed (never injected as text):

```
## PLUGIN SCRIPTS

Executable scripts from the plugins in this mission (run with run_python,
referencing plugin + path):
- finance-bundle: scripts/wacc.py
```

The agent executes them with its `run_python` tool, referencing plugin + path. This
is how a plugin ships deterministic computation alongside instructions — prefer a
bundled script over asking the model to do arithmetic.

## Failure semantics

Plugin resolution never kills a run. Degradations surface as warnings:

| Warning | Meaning | Fix |
|---|---|---|
| `plugin "<id>" not found in plugin-memory` | Bad id or unpublished bundle | Verify the id; publish the bundle |
| `plugin "<id>" could not be expanded` | Retrieval failed mid-expand | Transient — re-run; check platform status |
| `plugins declared but PLUGIN_MEMORY_SERVICE is not bound` | The executing surface has no plugin store | Run the mission on the standard engine surface |

A run that "should have had the toolkit" but behaved bare almost always has one of
these warnings in its detail — check `plungeai-results-traces` for where to read them.

Because resolution degrades silently, look ids up before declaring them: MCP server
ids for `mcp:` lists come from `plungeai_list_agents {kind: "connectors", search:
"…"}` (see SKILL.md "Discovery first" for the other injectable kinds); plugin bundle
ids live in plugin-memory — verify one cheaply by declaring it on a `quick` mission
and checking the run's warnings.

## Plugins vs skills vs MCP — which to declare

| You have | Declare |
|---|---|
| One instruction pack | `skills: [id]` — cheaper, eagerly injected |
| An MCP server the agent should call | `mcp: [server-id]` directly — plugins never add this for you |
| A coherent domain toolkit (several skills + scripts, maintained together; documented MCP servers to declare alongside it) | `plugins: [id]` — one id, whole skill/script kit, versioned as a unit |

Do not unbundle a plugin into individual `skills:` entries to force eager injection
— you lose the script index and blow the budget. State the must-follow skill in the
mission text instead. If the bundle's skills call out an MCP server, add that
server's id to `mcp:` yourself — declaring the plugin alone will not connect it.
