Pumpkin is in early development and bugs can happen. If you find any, report them on our GitHub.

Build plugins
without the pain.

No NMS. No reflection. No version-specific hacks. Just clean, type-safe APIs.

Version-specific NMS
// Sending a title packet
Object packet = Class.forName("net.minecraft.server."
    + VERSION + ".PacketPlayOutTitle")
    .getConstructor(...).newInstance(...);

player.getClass().getMethod("getHandle")
    .invoke(player).getClass()
    .getField("playerConnection").get(...)
    .getClass().getMethod("sendPacket", ...)
    .invoke(connection, packet);

// Breaks every update. Good luck.
Pumpkin plugin API
// Rewriting chat in a plugin
context.register_event_handler::<PlayerChatEvent, _>(
    PrefixChat,
    EventPriority::Normal,
    true,
)?;

impl EventHandler<PlayerChatEvent> for PrefixChat {
    fn handle(&self, _server: Server, mut event: PlayerChatEventData) -> PlayerChatEventData {
        event.message = format!("[Pumpkin] {}", event.message);
        event
    }
}

Every Player, One Plugin

Native Java + Bedrock support. Multi-version built in. Write once, reach everyone.

Typed Commands & Events

Register commands and hook typed plugin events without reflection, NMS, or version-specific hacks.

Async by Default

Built on Tokio. No main thread bottleneck. Heavy tasks don't freeze the server.

Memory Safe

Rust guarantees. No null pointers, no memory leaks, no data races.

0 NMS Classes
0 Reflection Calls
100% Type Safe

Quick Start Guide

Creating a new project

Pumpkin plugins use the Cargo build system.

The complete code for this plugin can be found as a template on GitHub.

Installing the toolchain

Before we can compile a plugin, we have to have the wasm32-wasip2 target installed. You can install the target by running:

rustup target add wasm32-wasip2

Initializing a new crate

First we need to create a new project folder. You can do this by running this command in the folder you created:

cargo new <project-name> --lib

After adding this, we want to create a new directory called .cargo and add in a config.toml file with the following contents:

[build]
target = "wasm32-wasip2"

Altogether your new folder structure should look like this:

├── .cargo/
│   └── config.toml
├── src/
│   └── lib.rs
├── Cargo.toml
└── Cargo.lock

Configuring the crate

Since Pumpkin plugins are loaded at runtime as dynamic libraries, we need to tell Cargo to build this crate as one.

Cargo.toml

[package]
name = "hello-pumpkin-wasm"
version = "0.1.0"
edition = "2024"

[lib] 
crate-type = ["cdylib"]

[dependencies]

Next we need to add some basic dependencies. Since Pumpkin is still in early development, the internal crates aren't published to crates.io, so we need to tell Cargo to download the dependencies directly from GitHub.

Cargo.toml

[package]
name = "hello-pumpkin"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]

[dependencies]
# This is the api crate that makes creating plugins easier, and has wit definitions
pumpkin-plugin-api = { version = "0.1.0", git = "https://github.com/Pumpkin-MC/Pumpkin", package = "pumpkin-plugin-api" }
tracing = "0.1"

Refer to the documentation for more.

Installation

First, you need to install the pumpkin-api-py library:

pip install pumpkin-api-py

Creating your first plugin

Create a file named main.py and add the following content:

from pumpkin_api import (
    Plugin, PluginMetadata, register_plugin, 
    server, event, command, text, context
)

class MyPlugin(Plugin):
    def metadata(self) -> PluginMetadata:
        return PluginMetadata(
            name="my-plugin",
            version="0.1.0",
            authors=["you"],
            description="An example plugin."
        )

    def on_load(self, ctx: context.Context) -> None:
        print("Python plugin loaded!")
        
        # Register an event handler
        self.register_event(ctx, event.EventType.PLAYER_JOIN_EVENT, self.on_player_join)

    def on_player_join(self, srv: server.Server, evt: event.PlayerJoinEventData) -> event.PlayerJoinEventData:
        print(f"Player {evt.player.get_name()} joined!")
        return evt

register_plugin(MyPlugin)

Building the plugin

Build your plugin into a WebAssembly component using the provided build tool:

pumpkin-api-build main -o my_plugin.wasm

This will generate a my_plugin.wasm file that you can place in the plugins folder of your Pumpkin server.

Refer to the documentation for more.

Installation

You will need the .NET SDK and the WASI workload to compile C# plugins for Pumpkin.

dotnet workload install wasi-experimental

Creating your first plugin

Create a new class library and add the following code:

using Pumpkin.API;

public class MyPlugin : IPlugin {
    public PluginMetadata GetMetadata() => new() {
        Name = "my-csharp-plugin",
        Version = "0.1.0",
        Authors = new[] { "you" },
        Description = "A simple C# plugin for Pumpkin"
    };

    public void OnLoad(IContext ctx) {
        Console.WriteLine("C# plugin loaded!");
    }
}

Building the plugin

Compile the plugin to WebAssembly using the WASI runtime identifier:

dotnet build -c Release /p:RuntimeIdentifier=wasi-wasm

This will generate a .wasm file in your output directory that can be loaded by Pumpkin.

Refer to the documentation for more.

Installation

You will need the wasi-sdk to compile C/C++ plugins for Pumpkin. Download the latest release from the wasi-sdk repository.

The complete code for this plugin can be found in the pumpkin-api-c repository.

Creating your first plugin

Create a file named main.c and add the following content:

#include "pumpkin_api.h"
#include <stdio.h>

// Define the plugin's metadata
pumpkin_metadata_t get_meta(void) {
    static const char* authors[] = {"you"};
    return (pumpkin_metadata_t) {
        .name = "my-c-plugin",
        .version = "0.1.0",
        .authors = authors,
        .authors_count = 1,
        .description = "A simple C plugin for Pumpkin",
        .dependencies_count = 0
    };
}

// Callback executed when the plugin is loaded
void on_load(plugin_own_context_t ctx) {
    printf("C plugin loaded!\n");
}

// Register the plugin with the API
REGISTER_PUMPKIN_PLUGIN(((pumpkin_plugin_t){
    .get_metadata = get_meta,
    .on_load = on_load
}))

Building the plugin

To compile this plugin into a WebAssembly component, use clang from the wasi-sdk:

/path/to/wasi-sdk/bin/clang -O3 \
 -Iinclude -Isrc/gen \
 src/gen/plugin.c src/pumpkin_api.c main.c \
 -o my_plugin.wasm \
 -mexec-model=reactor

This will generate a my_plugin.wasm file that can be loaded by Pumpkin.

Refer to the GitHub repository for more.

Installation

You will need Go 1.23 or later and tinygo to compile Go plugins for Pumpkin.

brew tap tinygo-org/tools && brew install tinygo

Creating your first plugin

Create a file named main.go and add the following content:

package main

import (
    "fmt"
    "github.com/pumpkin-mc/pumpkin-api-go"
)

type MyPlugin struct{}

func (p *MyPlugin) Metadata() pumpkin.PluginMetadata {
    return pumpkin.PluginMetadata{
        Name:        "my-go-plugin",
        Version:     "0.1.0",
        Authors:     []string{"you"},
        Description: "A simple Go plugin for Pumpkin",
    }
}

func (p *MyPlugin) OnLoad(ctx pumpkin.Context) {
    fmt.Println("Go plugin loaded!")
}

func main() {
    pumpkin.RegisterPlugin(&MyPlugin{})
}

Building the plugin

Compile the plugin to WebAssembly using tinygo:

tinygo build -o my_plugin.wasm -target=wasi main.go

This will generate a my_plugin.wasm file that can be loaded by Pumpkin.

Refer to the documentation for more.

Installation

You will need a number of things installed.

Unlike most of the other APIs, this one is actually a template instead. Clone it and rename.

git clone --recurse-submodules https://github.com/Pumpkin-MC/pumpkin-api-kt
mv pumpkin-api-kt my_kotlin_plugin
cd my_kotlin_plugin

Then change the rootProject.name in settings.gradle.kts, AND the PROJECT_NAME in Makefile. They must both match. Whatever you name the project, is going to be the filename of the Wasm produced.

Creating your first plugin

The template already has some code prepared. src/wasmWasiMain/kotlin/plugin/Plugin.kt contains:

package plugin;

import pumpkin.*

class PluginRootFunctionsExportsImpl {
    companion object : PluginRootFunctions.Exports {
        override fun initPlugin() {
            Logging.log(Logging.Level.INFO, "Init Kotlin plugin!")
        }

        override fun onLoad(context: Context.Context): Result<Unit> {
            Logging.log(Logging.Level.INFO, "Load Kotlin plugin!")
            return Result.success(Unit)
        }

        override fun onUnload(context: Context.Context): Result<Unit> {
            Logging.log(Logging.Level.INFO, "Unload Kotlin plugin!")
            return Result.success(Unit)
        }

        override fun handleCommand(
            commandId: UInt,
            sender: Command.CommandSender,
            server: Server.Server,
            args: Command.ConsumedArgs
        ): Result<Int> {
            TODO("Not yet implemented")
        }

        override fun handleEvent(eventId: UInt, server: Server.Server, event: Event.Event): Event.Event {
            TODO("Not yet implemented")
        }

        override fun handleTask(handlerId: UInt, server: Server.Server) {
            TODO("Not yet implemented")
        }
    }
}

class MetadataImpl {
    companion object : Metadata {
        override fun getMetadata(): Metadata.PluginMetadata {
            return Metadata.PluginMetadata(
                name = "Example Kotlin Plugin",
                version = "0.1.0",
                authors = listOf("You"),
                description = "An example plugin written in Kotlin",
                dependencies = listOf(),
                permissions = listOf()
            );
        }
    }
}

Building the plugin

Compile the plugin to WebAssembly by running the Makefile

make

This will take a while the first time as it builds some Rust dependencies from source.

Once it is done, my_kotlin_plugin.wasm (or whatever your Gradle project name is) will appear in build, ready to load into Pumpkin.

Refer to the documentation for more.

Ready to build?