Extism MoonBit PDK: This library can be used to write Extism Plug-ins in MoonBit.
moon add gmlewis/moonbit-pdkmoon new greet
cd greetmoon add gmlewis/moonbit-pdk
rm -rf libpub fn greet() -> Int {
let name = @host.input_string()
let greeting = "Hello, \{name}!"
@host.output_string(greeting)
0 // success
}
fn main {
}{
"import": [
"gmlewis/moonbit-pdk/pdk/host"
],
"link": {
"wasm": {
"exports": [
"greet"
],
"export-memory-name": "memory"
}
}
}moon build --target wasmextism call target/wasm/release/build/main/main.wasm greet --input "Benjamin" --wasi
# => Hello, Benjamin!Note: We also have a web-based, plug-in tester called the Extism Playground
pub fn greet() -> Int {
let name = @host.input_string()
if name == "Benjamin" {
@host.set_error("Sorry, we don't greet Benjamins!")
return 1 // failure
}
let greeting = "Hello, \{name}!"
@host.output_string(greeting)
0 // success
}moon build --target wasm
extism call target/wasm/release/build/main/main.wasm greet --input "Benjamin" --wasi
# => Error: Sorry, we don't greet Benjamins!
echo $? # print last status code
# => 1
extism call target/wasm/release/build/main/main.wasm greet --input "Zach" --wasi
# => Hello, Zach!
echo $?
# => 0struct Add {
a : Int
b : Int
}
pub fn Add::from_json(value : Json) -> Add? {
// From: https://github.com/moonbitlang/core/issues/892#issuecomment-2306068783
match value {
{ "a": Number(a), "b": Number(b) } => Some({ a: a.to_int(), b: b.to_int() })
_ => None
}
}
type! ParseError String derive(Show)
pub fn Add::parse(s : String) -> Add!ParseError {
match @json.parse?(s) {
Ok(jv) =>
match Add::from_json(jv) {
Some(value) => value
None => raise ParseError("unable to parse Add \{s}")
}
Err(e) => raise ParseError("unable to parse Add \{s}: \{e}")
}
}
struct Sum {
sum : Int
} derive(ToJson)
pub fn add() -> Int {
let input = @host.input_string()
let params = try {
Add::parse!(input)
} catch {
ParseError(e) => {
@host.set_error(e)
return 1
}
}
//
let sum = { sum: params.a + params.b }
let json_value = sum.to_json()
@host.output_json_value(json_value)
0 // success
}{
"import": [
"gmlewis/moonbit-pdk/pdk/host"
],
"link": {
"wasm": {
"exports": [
"add"
],
"export-memory-name": "memory"
}
}
}moon build --target wasm
extism call plugin.wasm add --input='{"a": 20, "b": 21}' --wasi
# => {"sum":41}pub fn greet() -> Int {
let user = match @config.get("user") {
Some(user) => user
None => {
@host.set_error("This plug-in requires a 'user' key in the config")
return 1 // failure
}
}
let greeting = "Hello, \{user}!"
@host.output_string(greeting)
0 // success
}{
"import": [
"gmlewis/moonbit-pdk/pdk/config",
"gmlewis/moonbit-pdk/pdk/host"
],
"link": {
"wasm": {
"exports": [
"greet"
],
"export-memory-name": "memory"
}
}
}moon build --target wasm
extism call target/wasm/release/build/main/main.wasm greet --config user=Benjamin
# => Hello, Benjamin!
extism call target/wasm/release/build/main/main.wasm greet
# => Error: This plug-in requires a 'user' key in the configpub fn count() -> Int {
let mut count = match @var.get_int("count") {
Some(v) => v
None => 0
}
count = count + 1
@var.set_int("count", count)
let s = count.to_string()
@host.output_string(s)
0 // success
}Note: Use the untyped variant @var.set_bytes to handle your own types.
{
"import": [
"gmlewis/moonbit-pdk/pdk/host",
"gmlewis/moonbit-pdk/pdk/var"
],
"link": {
"wasm": {
"exports": [
"count"
],
"export-memory-name": "memory"
}
}
}pub fn log_stuff() -> Int {
@host.log_info_str("An info log!")
@host.log_debug_str("A debug log!")
@host.log_warn_str("A warn log!")
@host.log_error_str("An error log!")
0 // success
}moon build --target wasm
extism call target/wasm/release/build/main/main.wasm log_stuff --wasi --log-level=trace
# => 2024/07/09 11:37:30 No runtime detected
# => 2024/07/09 11:37:30 Calling function : log_stuff
# => 2024/07/09 11:37:30 An info log!
# => 2024/07/09 11:37:30 A debug log!
# => 2024/07/09 11:37:30 A warn log!
# => 2024/07/09 11:37:30 An error log!Note: From the CLI you need to pass a level with --log-level. If you are running the plug-in in your own host using one of our SDKs, you need to make sure that you call set_log_file to "stdout" or some file location.
pub fn http_get() -> Int {
// create an HTTP Request (without relying on WASI), set headers as needed
let req = @http.new_request(
@http.Method::GET,
"https://jsonplaceholder.typicode.com/todos/1",
)
req.header.set("some-name", "some-value")
req.header.set("another", "again")
// send the request, get response back
let res = req.send()
// zero-copy send output to host
res.output()
0 // success
}extism call \
target/wasm/release/build/examples/http-get/http-get.wasm \
http_get \
--wasi \
--allow-host='*.typicode.com'
# => {
# => "userId": 1,
# => "id": 1,
# => "title": "delectus aut autem",
# => "completed": false
# => }pub fn a_python_func(offset : Int64) -> Int64 = "extism:host/user" "a_python_func"pub fn hello_from_python() -> Int {
let msg = "An argument to send to Python"
let mem = @host.allocate_string(msg)
let ptr = a_python_func(mem.offset)
mem.free()
let rmem = @host.find_memory(ptr)
let response = rmem.to_string()
@host.output_string(response)
return 0
}from extism import host_fn, Plugin
@host_fn()
def a_python_func(input: str) -> str:
# just printing this out to prove we're in Python land
print("Hello from Python!")
# let's just add "!" to the input string
# but you could imagine here we could add some
# applicaiton code like query or manipulate the database
# or our application APIs
return input + "!"manifest = {"wasm": [{"path": "target/wasm/release/build/main/main.wasm"}]}
plugin = Plugin(manifest, functions=[a_python_func], wasi=True)
result = plugin.call('hello_from_python', b'').decode('utf-8')
print(result)moon build --target wasm
python3 -m pip install extism
python3 app.py
# => Hello from Python!
# => An argument to send to Python!Note: This fails on my Mac M2 Max with some weird system error but works great on my Linux Mint Cinnamon box.
moon update && moon install
./build.sh./run.sh$ moon version --all
moon 0.1.20260713 (75c7e1f 2026-07-13) ~/.moon/bin/moon
moonc v0.10.4+2cc641edf (2026-07-15) ~/.moon/bin/moonc
moonrun 0.1.20260713 (75c7e1f 2026-07-13) ~/.moon/bin/moonrun
Extism MoonBit PDK: This library can be used to write Extism Plug-ins in MoonBit.