---
title: Bun Plugin
description: Build a typed request Plugin with the supported Bun SDK and CLI path.
---

Bun is a trusted child-process execution class, not a sandbox. Plugin business
code implements generated Capability Provider types; `@lenso/bun` and generated
entrypoints own startup, JSON-RPC, cancellation, and shutdown.

## 1. Create the project

```sh
lenso plugin new example.echo --runtime bun
cd example.echo
```

Creation stages the whole directory, installs the exact `bun.lock`, and runs the
TypeScript check before publishing the project. Use `--no-install` only for an
offline scaffold.

```text
example.echo/
├── package.json
├── bun.lock
├── tsconfig.json
└── src/
    ├── plugin.ts
    ├── lenso.bun.generated.ts
    ├── lenso.describe.generated.ts
    └── lenso.invoke.generated.ts
```

Edit only `src/plugin.ts`. The initial scaffold implements the generated
`lenso.agent.tool-provider@2` Provider and exposes one Tool. Expected business
outcomes return generated Domain Errors; thrown exceptions remain Runtime
Failures.

## 2. Implement the Provider

The authored file exports one Plugin definition. Generated files own the Bun
server, descriptor projection, and development invocation:

```ts title="src/plugin.ts"
import { definePlugin } from "@lenso/bun";
import {
  bindToolProviderProvider,
  type ToolProviderProvider,
} from "@lenso/bun/capabilities/agent-tool-provider";

const tool: ToolProviderProvider = {
  async catalog() {
    return { ok: true, value: { tools: [{
      name: "company.uppercase",
      description: "Convert text to uppercase.",
      input_schema_json: JSON.stringify({
        type: "object",
        additionalProperties: false,
        properties: { text: { type: "string", maxLength: 4096 } },
        required: ["text"],
      }),
    }] } };
  },
  async execute(_context, request) {
    if (request.name !== "company.uppercase") {
      return { ok: false, error: { kind: "domain", error: "not_found" } };
    }
    let input: unknown;
    try {
      input = JSON.parse(request.arguments_json);
    } catch {
      return { ok: false, error: { kind: "domain", error: "invalid_arguments" } };
    }
    if (typeof input !== "object" || input === null || !("text" in input)
      || typeof input.text !== "string") {
      return { ok: false, error: { kind: "domain", error: "invalid_arguments" } };
    }
    return { ok: true, value: {
      content: input.text.toUpperCase(),
      content_type: "text",
      metadata_json: "{}",
    } };
  },
};

export default definePlugin({ providers: [bindToolProviderProvider(tool)] });
```

Keep input validation and expected business rejection inside the generated
Domain Error union. Reserve thrown exceptions for broken implementation or
runtime conditions.

## 3. Check and develop

```sh
lenso plugin check
lenso plugin dev \
  --operation execute \
  --request-json '{"name":"company.uppercase","arguments_json":"{\"text\":\"hello\"}"}'
lenso plugin dev --watch \
  --operation execute \
  --request-json '{"name":"company.uppercase","arguments_json":"{\"text\":\"hello\"}"}'
```

`check` type-checks, builds the Bun implementation, derives its portable
Descriptor, materializes a standard Bundle, and verifies the closure. `dev`
invokes the same authored Provider without asking authors to implement a test
wire. The response for the request above is `HELLO`. Watch mode stays alive
after a failed rebuild.

## 4. Package and install

```sh
lenso plugin pack
lenso plugins add dist/example.echo-0.1.0.lenso-plugin --root "$HOME/.lenso/agent"
```

The archive selects `lenso.bun-process@1`. A compatible product Host must link
the Bun Adapter and the generated Rust codec for every provided Capability;
the Agent Host includes that integration. The machine running the Host must
have `bun` available.

Current Bun Authoring V2 supports Request, bidirectional Stream, and Event
Providers, plus generated outbound dependency clients for all three interactions.
`@lenso/bun 0.5.1` and `@lenso/bun-plugin 0.4.1` are recorded released versions;
the earlier Request-only limit no longer applies to that SDK cohort.

See [`@lenso/bun`](https://www.npmjs.com/package/@lenso/bun) for the typed SDK
and [`lenso-bun-adapter`](https://github.com/LioRael/lenso-bun-adapter) for the
process boundary.


## Author stateful Plugins with generated Capabilities

The `providers`/`bind*Provider` example above remains a compatibility entrypoint.
For new code, prefer generated Capability values, `provides`, and `create`.
This structural example assumes your project generated `Conversation` and
`Store` contracts with the corresponding `chat`/`get` operations:

```ts
import { definePlugin } from "@lenso/bun";
import { Conversation } from "./generated/conversation.ts";
import { Store } from "./generated/store.ts";

export default definePlugin({
  provides: [Conversation],
  dependencies: { store: Store.required() },
  async create({ dependencies }) {
    return {
      async *chat(_context, request) {
        const greeting = await dependencies.store.get(request.room);
        yield { text: greeting ?? "Hello" };
      },
    };
  },
});
```


Dependency keys default to stable requirement IDs. `required()`, `optional()`,
and `many()` work across all three interactions. `create` constructs each
Instance; managed shutdown calls `stop` at most once. Lifecycle-only Plugins
with no provided Capability are valid too.

Server-output Streams can return async generators. Use the generated
`StreamSession` for bidirectional messages, independent half-close, or terminal
domain errors. Event handlers return `void` or `Promise<void>`; publication
waits for completion and rejection becomes Runtime Failure. Outbound clients
use exact Plan-selected providers and preserve cancellation, ordering, and
bounded admission semantics.

See the [Bun SDK](https://github.com/LioRael/lenso-bun-adapter/blob/main/packages/lenso-bun-plugin/README.md)
for generated interfaces. Continue with [named dependencies](/docs/core/named-dependencies),
the [complete sync example](/docs/core/document-sync), or
[TypeScript Host authoring](/docs/core/typescript-host). The Host build profile has separate limits.
