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

# Create a Runtime Plugin

> Author the Runtime Plugin manifest, singleton, typed invocation context, and dependency contract.

Create a root-owned folder under `plugins/`. The folder name is the plugin identity and must be a valid Python identifier.

```text plugins/warehouse/ theme={null}
plugins/warehouse/
├── plugin.yaml
├── plugin.py
├── pyproject.toml       optional SDK dependencies
├── lib/                 private plugin helpers
└── extensions/          declared Harnest lifecycle contributions
```

## Declare the plugin

<CodeGroup>
  ```yaml plugins/warehouse/plugin.yaml theme={null}
  apiVersion: harnest.dev/v1alpha1
  kind: RuntimePlugin
  metadata:
    name: warehouse
    version: 1.0.0
  runtime:
    entrypoint: plugin:plugin
  ```

  ```toml plugins/warehouse/pyproject.toml theme={null}
  [project]
  name = "warehouse"
  version = "1.0.0"
  requires-python = ">=3.11,<3.14"
  dependencies = ["warehouse-sdk>=2,<3"]
  ```
</CodeGroup>

`metadata.name` must match the folder. The optional PEP 621 project name and version must match `plugin.yaml`. Its static dependencies join the root environment solve.

## Export the application-owned singleton

`plugin.py` exports one public `Plugin` subclass and the singleton named `plugin`. Keep SDK clients on that singleton, and expose a bounded invocation view through `PluginContext`:

```python plugins/warehouse/plugin.py theme={null}
import os

from harnest.plugins import Plugin, PluginContext
from warehouse_sdk import WarehouseClient


class WarehouseContext(PluginContext):
    """Expose bounded warehouse operations during one invocation."""

    __slots__ = ("_owner",)

    def __init__(self, plugin_name: str, owner: "Warehouse") -> None:
        """Bind the revocable context to its application-owned plugin."""

        super().__init__(plugin_name)
        self._owner = owner

    async def query(self, statement: str) -> list[dict]:
        """Run one query while the Harnest invocation remains active."""

        self._require_active()
        return await self._owner._query(statement)


class Warehouse(Plugin[WarehouseContext]):
    """Own one warehouse SDK client for the application lifetime."""

    def __init__(self) -> None:
        """Defer all external connection work until runtime startup."""

        self._client = None

    async def start(self, _context) -> None:
        """Connect after dependency-ordered application startup begins."""

        self._client = await WarehouseClient.connect(
            endpoint=os.environ["WAREHOUSE_ENDPOINT"],
        )

    async def stop(self) -> None:
        """Release the application-owned SDK client."""

        # Shutdown must also be safe after a partial startup failure.
        if self._client is not None:
            await self._client.close()
            self._client = None

    def create_context(self, base: PluginContext) -> WarehouseContext:
        """Create one typed, revocable view for the active invocation."""

        return WarehouseContext(base.plugin_name, self)

    async def query(self, statement: str) -> list[dict]:
        """Route public calls through the current invocation context."""

        return await self.context.query(statement)

    async def _query(self, statement: str) -> list[dict]:
        """Cross the private SDK boundary after startup validation."""

        # Calls fail closed if they escape the managed application lifetime.
        if self._client is None:
            raise RuntimeError("Warehouse plugin is not started")
        return await self._client.query(statement)


plugin = Warehouse()
```

Harnest imports plugin modules during compilation but does not call `start()` or connect to services. Runtime startup activates plugins in dependency order and calls `stop()` in reverse order.

## Add optional plugin content

| Path                 | Purpose                       | Required manifest capability           |
| -------------------- | ----------------------------- | -------------------------------------- |
| `lib/**/*.py`        | Private plugin implementation | None                                   |
| `extensions/**/*.py` | Lifecycle hooks and factories | The capability matching each decorator |
| `tools/*.py`         | Managed Agent Tools           | `content.tools`                        |
| `mcp/*.py`           | Managed MCP clients           | `content.mcp`                          |
| `skills/*/SKILL.md`  | Managed Agent Skills          | `content.skills`                       |
| `subagents/`         | Managed SubAgents             | `content.subagents`                    |

Managed mode composes declared content into the owning agent. Advanced mode accepts declared lifecycle extensions but rejects populated managed-content directories because the native target owns its own composition.

## Add plugin dependencies

Use `requires.plugins` when this plugin depends on another local Runtime Plugin:

```yaml plugins/warehouse/plugin.yaml theme={null}
requires:
  plugins: [credentials]
```

Harnest rejects missing dependencies and cycles. Plugin Python dependencies share one solve with the root project; incompatible constraints fail `harnest env sync` instead of creating isolated environments.

```bash theme={null}
harnest env sync support-agent
harnest test support-agent
```

<Card title="Add lifecycle behavior" icon="arrows-rotate" href="/harnest/build/runtime-plugins/lifecycle">
  Declare exactly which Harnest surfaces the plugin contributes.
</Card>
