Appearance
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 archiveCore Components
1. Source Files
Your plugin implementation in your chosen language:
| Language | Main File | Description |
|---|---|---|
| Python | app.py | Python class implementing WIT interface |
| TypeScript | app.ts | TypeScript module with exported functions |
| JavaScript | app.js | JavaScript module with exported functions |
| Rust | src/lib.rs | Rust library with component macro |
| Go | main.go | Go 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 archiveBuild Contract:
- Input: Source files and WIT definition
- Output:
dist/plugin.wasmand.npackarchive - 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 dependenciesUsage:
bash
./prepare.sh # Interactive installation
./prepare.sh --check # Check dependencies only
./prepare.sh --ci # Non-interactive mode5. 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_MODENote: 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 declarationsRust
tree
├── src/lib.rs # Main implementation
├── Cargo.toml # Rust dependencies
└── wkg.toml # WIT package configGo
tree
├── main.go # Main implementation
├── go.mod # Go module definition
├── wkg.toml # WIT package config
└── gen/ # Generated bindingsBuild Output
The dist/ Directory
Created during build, contains:
plugin.wasm- The compiled WebAssembly component- Self-contained binary
- Platform independent
- Includes all code and metadata
*.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/OImportant: 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
- Deploy your
.npackto Noorle - Platform introspects the WIT interface
- Functions discovered automatically
- MCP tools created with proper schemas
- 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 API5. Version Control
Track these files:
- Source code
wit/world.wit- Configuration files
build.shandprepare.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.shBuild 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
- Configuration - Deep dive into noorle.yaml
- Language Guides - Language-specific details
- Deployment - Publishing your plugin