---
title: Portable Rust Plugin
description: Create one portable Rust Plugin and ship a verified Bundle.
---

Use this path for an ordinary installable Agent Tool. The Rust scaffold keeps
business behavior in one source file and lets the SDK derive its Capability
descriptor, Wasm lowering, Process entrypoint, and package metadata.

## 1. Create the project

```sh
# Portable Wasm plus a trusted native Process from the same source
lenso plugin new example.echo

# Wasm only
lenso plugin new example.echo --runtime wasm

# Trusted Process only
lenso plugin new example.echo --runtime process
```

The default multi-output project is useful when portability and native process
compatibility both matter. Process Plugins have normal local process authority;
they are not sandboxes.

```text
example.echo/
├── Cargo.toml
├── Cargo.lock
└── src/
    ├── lib.rs     # business behavior
    └── main.rs    # generated Process entrypoint for multi/process projects
```

The scaffold is intentionally narrow: it creates an Agent Tool Provider. It is
not a general generator for arbitrary Capability providers, stateful Plugins,
Web UI, or Bun projects.

## 2. Implement behavior

The initial scaffold uses the public macros rather than hand-written Adapter
protocol code:

```rust title="src/lib.rs"
use lenso_agent_tool_sdk::prelude::*;
use schemars::JsonSchema;

#[derive(Debug, serde::Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
struct Arguments {
    text: String,
}

#[lenso::plugin]
#[derive(Clone, Copy, Debug, Default)]
struct Plugin;

#[lenso_agent_tool_sdk::tool_provider]
impl Plugin {
    #[tool(name = "example.echo", description = "Echo text.")]
    fn execute(arguments: Arguments) -> Result<ExecuteResponse, ExecuteError> {
        Ok(ExecuteResponse {
            content: arguments.text,
            content_type: ContentType::Text,
            metadata_json: "{}".try_into().unwrap(),
        })
    }
}
```

The `Arguments` type becomes the Tool's JSON Schema. Expected bad input returns
`ExecuteError`; a panic or broken runtime boundary remains a Runtime Failure.

## 3. Run the development loop

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

`check` builds every declared implementation and checks descriptor agreement.
`dev` crosses a real Adapter boundary; it does not call the Rust function as a
test shortcut. `--watch` rebuilds and reruns the same request after changes.

Use `--implementation wasm`, `process`, or `all` when you need to compare the
declared outputs explicitly.

## 4. Package the Release

```sh
lenso plugin pack
```

`pack` builds release artifacts, requires every implementation to expose the
same Contract, writes one immutable `.lenso-plugin` archive under `dist/`, and
reopens the published bytes through the installation verifier.

Add the resulting Bundle through [Add Plugins to an App](/docs/core/plugin-composition).
For another Capability role, define its contract first and use the
[Linked Rust Plugin](/docs/core/linked-rust-plugin) path or a supported typed SDK.

For independent providers of the same Capability, stateful construction, and
cleanup, continue with [named dependencies](/docs/core/named-dependencies) and the
[complete document-sync example](/docs/core/document-sync).
