Skip to content

Plugin Structure

Understanding the anatomy of a Noorle plugin.

Overview

Every Noorle plugin follows a consistent structure regardless of language:

tree
my-plugin/
├── Source Files         # Language-specific implementation
├── wit/world.wit        # API definition (WebAssembly Interface)
├── build.sh             # Build script
├── prepare.sh           # Dependency installer
├── noorle.yaml          # Plugin configuration (optional)
├── Config Files         # Language-specific (package.json, Cargo.toml, etc.)
└── dist/                # Build output
    ├── plugin.wasm      # Compiled WebAssembly component
    └── *.npack          # Deployment archive

Core Components

1. Source Files

Your plugin implementation in your chosen language:

LanguageMain FileDescription
Pythonapp.pyPython class implementing WIT interface
TypeScriptapp.tsTypeScript module with exported functions
JavaScriptapp.jsJavaScript module with exported functions
Rustsrc/lib.rsRust library with component macro
Gomain.goGo package with exported functions

2. WIT Interface (wit/world.wit)

Defines your plugin's API using WebAssembly Interface Types:

wit
package noorle:my-plugin;

world my-plugin-component {
    /// Process input data
    /// This comment becomes the tool description in MCP
    export process: func(input: string) -> result<string, string>;

    /// Calculate something
    export calculate: func(a: f64, b: f64) -> f64;
}

Key Points:

  • Package name identifies your plugin
  • World name defines the WebAssembly component
  • Comments become tool descriptions
  • Functions are auto-discovered as MCP tools

3. Build Script (build.sh)

Template-provided script that handles compilation:

bash
#!/bin/bash
# Checks dependencies
# Compiles source to WebAssembly
# Creates component from module
# Packages into .npack archive

Build Contract:

  • Input: Source files and WIT definition
  • Output: dist/plugin.wasm and .npack archive
  • Must be executable (chmod +x build.sh)

4. Prepare Script (prepare.sh)

Installs all required dependencies:

bash
#!/bin/bash
# Installs language toolchain
# Installs WebAssembly tools
# Installs project dependencies

Usage:

bash
./prepare.sh           # Interactive installation
./prepare.sh --check   # Check dependencies only
./prepare.sh --ci      # Non-interactive mode

5. Configuration (noorle.yaml)

Optional configuration for permissions and metadata:

yaml
schema_version: "1.0"  # Required when file exists

metadata:
  name: my-plugin      # Defaults to folder name
  description: "What this plugin does"
  author: "Your Name"
  tags:
    - utility
    - api

permissions:
  network:
    allow:
      - host: "api.example.com"
  environment:
    allow:
      - API_KEY
      - DEBUG_MODE

Note: This file is completely optional - plugins work without it!

Language-Specific Files

Python

tree
├── app.py              # Main implementation
├── pyproject.toml      # Dependencies (uv/pip)
└── test_app.py         # Unit tests (optional)

TypeScript/JavaScript

tree
├── app.ts/app.js       # Main implementation
├── package.json        # NPM dependencies
├── tsconfig.json       # TypeScript config (TS only)
└── wasi.d.ts           # WASI type declarations

Rust

tree
├── src/lib.rs          # Main implementation
├── Cargo.toml          # Rust dependencies
└── wkg.toml            # WIT package config

Go

tree
├── main.go             # Main implementation
├── go.mod              # Go module definition
├── wkg.toml            # WIT package config
└── gen/                # Generated bindings

Build Output

The dist/ Directory

Created during build, contains:

  1. plugin.wasm - The compiled WebAssembly component

    • Self-contained binary
    • Platform independent
    • Includes all code and metadata
  2. *.npack - Deployment archive

    • TAR archive of necessary files
    • Named: <plugin-name>-<hash>.npack
    • Used for deployment to Noorle

Build Process Flow

WIT Dependencies

The wit/deps/ Directory

Contains WASI interface definitions (pre-included):

tree
wit/deps/
├── wasi-cli/           # CLI arguments, environment
├── wasi-filesystem/    # File system access
├── wasi-http/          # HTTP client
├── wasi-sockets/       # Network sockets
├── wasi-clocks/        # Time functions
├── wasi-random/        # Random numbers
└── wasi-io/            # Basic I/O

Important: Only import what you need in world.wit:

wit
world my-plugin-component {
    // Import only required interfaces
    import wasi:http/outgoing-handler@0.2.0;
    import wasi:cli/environment@0.2.0;

    export my-function: func() -> string;
}

Auto-Discovery

How It Works

  1. Deploy your .npack to Noorle
  2. Platform introspects the WIT interface
  3. Functions discovered automatically
  4. MCP tools created with proper schemas
  5. Available immediately to AI agents

No Configuration Needed

Unlike traditional plugin systems:

  • ❌ No manifest files
  • ❌ No tool registration
  • ❌ No schema definitions
  • ✅ Just export functions!

Example

Your WIT:

wit
export analyze: func(data: string) -> result<analysis, string>;

Becomes MCP tool:

json
{
  "name": "analyze",
  "description": "From WIT comments",
  "inputSchema": {
    "type": "object",
    "properties": {
      "data": { "type": "string" }
    }
  }
}

Universal Plugins

Not Noorle-Specific

Your plugin compiles to a standard WebAssembly component that:

  • Works in any WASI runtime
  • Can be used outside Noorle
  • Follows open standards
  • Has no vendor lock-in

Reusability

Plugins can be:

  • Shared across projects
  • Composed with other plugins
  • Versioned using standard practices
  • Distributed through any channel

Best Practices

1. Clear WIT Documentation

wit
/// Calculate shipping cost based on weight and distance
///
/// Returns the cost in USD or an error message
export calculate-shipping: func(
    weight-kg: f64,
    distance-km: f64
) -> result<f64, string>;

2. Consistent Naming

  • Use kebab-case in WIT files
  • Follow language conventions in source
  • Keep names descriptive and clear

3. Error Handling

Always use Result types for fallible operations:

wit
export process: func(input: string) -> result<output, error-message>;

4. Minimal Permissions

Only request what you need:

yaml
permissions:
  network:
    allow:
      - host: "api.example.com"  # Only specific API

5. Version Control

Track these files:

  • Source code
  • wit/world.wit
  • Configuration files
  • build.sh and prepare.sh

Don't track:

  • dist/ directory
  • Generated bindings
  • Dependencies (node_modules, etc.)

Troubleshooting Structure Issues

Missing Files

bash
# Ensure all required files exist
ls -la wit/world.wit build.sh prepare.sh

# Make scripts executable
chmod +x build.sh prepare.sh

Build Output Issues

bash
# Clean and rebuild
rm -rf dist/
noorle plugin build

# Check output
ls -la dist/

WIT Parsing Errors

Common issues:

  • Missing package declaration
  • Invalid function signatures
  • Unmatched braces
  • Missing semicolons

Next Steps