Skip to content

Plugin API Reference

A complete guide to building powerful extensions and AI tools for EazeEditor using the Java-based Plugin SDK. Based on the Autonomous Shell Plugin reference implementation.

Project Structure

Every EazeEditor plugin follows a standard Java project layout. Below is the recommended directory structure:

my-plugin/
├── build.gradle
├── sdks/
│   ├── eaze_editor_code_sdk.jar          # Host SDK (compileOnly)
│   └── my-dependency.jar                 # Bundled dependencies
├── src/main/java/com/example/plugin/
│   ├── Main.java                         # Entry point (optional)
│   ├── mcp/
│   │   └── MyMCP.java                    # AI tool definitions
│   ├── pluginlifecycle/
│   │   └── LifeCycleObserver.java        # Lifecycle hooks
│   ├── setting/
│   │   ├── SettingDiscovery.java         # Settings metadata
│   │   └── SettingsHandler.java          # Settings HTTP handler
│   └── util/
│       └── Vars.java                     # Constants
└── src/main/resources/my-plugin/         # Static web resources
    ├── settings.html
    ├── script.js
    └── about.html

1. build.gradle Setup

Configure your Gradle build to treat the host SDK as compileOnly (it is provided at runtime by EazeEditor), bundle all other dependencies into the output JAR, and enable the -parameters compiler flag so that @P annotations on tool methods work correctly.

plugins {
    id 'java'
}

group = 'com.example.plugin'
version = '1.0.0'

java {
    sourceCompatibility = JavaVersion.VERSION_25
    targetCompatibility = JavaVersion.VERSION_25
}

repositories {
    mavenCentral()
}

dependencies {
    // Host SDK — provided at runtime, do NOT bundle
    compileOnly files('sdks/eaze_editor_code_sdk.jar')
    testImplementation files('sdks/eaze_editor_code_sdk.jar')

    // Plugin-specific dependencies — bundled into JAR
    implementation files('sdks/my-dependency.jar')
}

jar {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    // Bundle implementation deps into plugin JAR
    from(zipTree('sdks/my-dependency.jar'))
    // Do NOT include eaze_editor_code_sdk.jar
}

// Required for @Tool parameter names to be preserved
tasks.withType(JavaCompile).configureEach {
    options.compilerArgs << "-parameters"
}

Key point: eaze_editor_code_sdk.jar is compileOnly — the host provides it at runtime. All other dependencies must be bundled via from(zipTree(...)) in the jar task. The -parameters compiler flag is required for @P annotations on tool methods to work.

2. Constants (Vars.java)

Centralize all plugin identifiers, setting keys, and default values in a single constants class. The PLUGIN_NAME value must match the resource folder name under src/main/resources/.

public class Vars {
    // Plugin identity
    public static final String PLUGIN_NAME = "my-plugin";               // Unique ID, must match resource folder name
    public static final String PLUGIN_DISPLAY_NAME = "My Plugin";
    public static final String PLUGIN_DESCRIPTION = "Description of my plugin.";
    public static final String PLUGIN_HREF = ""; // Leaving blank intentionally to use Plugin generated Seting Page URL. IF you are dont want to use plugin settings API, set custom link.

    // Setting keys
    public static final String SETTING_MY_OPTION = "my_option";

    // Defaults
    public static final boolean DEFAULT_MY_OPTION = true;
}

3. Register MCP Tools (AI Tools)

Annotate a class with @EazeAiComponent to have it auto-discovered by the host as an AI component. Each public method annotated with @Tool is exposed to the AI agent, and @P describes each parameter so the AI knows what to pass.

import com.eazeeditor.core.pluginmodule.annotation.EazeAiComponent;
import com.eazeeditor.core.pluginmodule.api.PluginApi;
import com.neel.eazewebitcore.coremcp.plugins.library.settingapi.SettingAPI;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;

@EazeAiComponent   // Auto-discovered by the host as an AI component
public class MyMCP {

    @Tool("Short description of what this tool does. The AI reads this to decide when to call it.")
    public String myTool(
            @P("Description of this parameter for the AI") String param1,
            @P("Another parameter description") boolean flag) {

        // Read a setting value at runtime
        String val = SettingAPI.getSettingValue(Vars.PLUGIN_NAME, Vars.SETTING_MY_OPTION);

        // Log to host
        PluginApi.info("myTool called with: " + param1, MyMCP.class);

        // Send real-time status update to the user
        PluginApi.notifyHost("Processing...", MyMCP.class);

        return "Result string returned to the AI";
    }
}

Key APIs Inside Tools

API Purpose
PluginApi.info(msg, clazz) Log info to the host console
PluginApi.error(msg, clazz, throwable) Log an error to the host console
PluginApi.notifyHost(msg, clazz) Send a real-time status update to the user UI
SettingAPI.getSettingValue(pluginName, key) Read a persisted setting (returns String or null)
SettingAPI.putSettingValue(pluginName, key, value) Write a persisted setting

4. Plugin Lifecycle Hooks

Use @PluginLifeCycle to react to plugin load, close, and error events. The host auto-discovers lifecycle methods via annotation scanning — no registration call is needed.

import com.eazeeditor.core.pluginmodule.annotation.PluginLifeCycle;
import com.eazeeditor.core.pluginmodule.api.LifecyclePhase;
import com.eazeeditor.core.pluginmodule.api.PluginApi;

public class LifeCycleObserver {

    @PluginLifeCycle(LifecyclePhase.ON_LOAD)
    public void onPluginStart() {
        PluginApi.info("Plugin loaded!", LifeCycleObserver.class);
        // Initialize settings handler here
        new MySettingsHandler();
    }

    @PluginLifeCycle(LifecyclePhase.ON_CLOSE)
    public void onPluginStop() {
        // Cleanup resources
    }

    @PluginLifeCycle(LifecyclePhase.ON_ERROR)
    public void onPluginError(String message, Throwable throwable) {
        PluginApi.error("Error: " + message, LifeCycleObserver.class, throwable);
    }
}

5. Register a Settings Page

5a. Settings Discovery (Metadata)

Annotate a class with @PluginSettingsDiscovery to register the plugin's settings metadata with the host.

import com.eazeeditor.core.pluginmodule.annotation.PluginSettingsDiscovery;

@PluginSettingsDiscovery(
    pluginName    = Vars.PLUGIN_NAME,
    description   = Vars.PLUGIN_DESCRIPTION,
    displayName   = Vars.PLUGIN_DISPLAY_NAME,
    href          = Vars.PLUGIN_HREF
)
public class MySettingDiscovery {}

5b. Settings Handler (Serves UI + API)

Extend BaseSettingsHandler to serve HTML/JS resources and handle settings CRUD operations via HTTP endpoints.

import com.eazeeditor.core.pluginmodule.api.PluginApi;
import com.eazeeditor.core.pluginmodule.api.PluginSettingRegistration;
import com.eazeeditor.core.pluginmodule.core.settingssdk.server.BaseSettingsHandler;
import com.neel.eazewebitcore.coremcp.plugins.library.settingapi.SettingAPI;
import com.sun.net.httpserver.HttpExchange;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

public class MySettingsHandler extends BaseSettingsHandler {

    public MySettingsHandler() {
        super(Vars.PLUGIN_NAME);  // Must match resource folder name

        // Register with the host
        PluginApi.registerSettingsHandler(Vars.PLUGIN_NAME, this);
        PluginApi.registerSettings(new PluginSettingRegistration(
            Vars.PLUGIN_NAME,
            Vars.PLUGIN_DESCRIPTION,
            Vars.PLUGIN_DISPLAY_NAME,
            null  // Let SDK auto-generate the URL
        ));
    }

    @Override
    protected void handleCustomRoute(HttpExchange exchange, String method, String path)
            throws IOException {
        switch (path) {
            case "/settings" -> handleResourceRequest(exchange, "settings.html");
            case "/script.js" -> handleResourceRequest(exchange, "script.js");
            case "/api/get-settings" -> handleGetSettings(exchange);
            case "/api/save-settings" -> handleSaveSettings(exchange);
            default -> sendJsonResponse(exchange, 404, Map.of("error", "Not Found"));
        }
    }

    private void handleGetSettings(HttpExchange exchange) throws IOException {
        Map<String, Object> settings = new HashMap<>();
        String val = SettingAPI.getSettingValue(
            Vars.PLUGIN_NAME, Vars.SETTING_MY_OPTION);
        settings.put("my_option",
            val != null ? Boolean.parseBoolean(val) : Vars.DEFAULT_MY_OPTION);
        sendJsonResponse(exchange, 200, settings);
    }

    private void handleSaveSettings(HttpExchange exchange) throws IOException {
        try (InputStream is = exchange.getRequestBody()) {
            String body = new String(is.readAllBytes(), StandardCharsets.UTF_8);
            if (body.contains("\"my_option\":true")
                    || body.contains("\"my_option\": true")) {
                SettingAPI.putSettingValue(
                    Vars.PLUGIN_NAME, Vars.SETTING_MY_OPTION, "true");
            } else {
                SettingAPI.putSettingValue(
                    Vars.PLUGIN_NAME, Vars.SETTING_MY_OPTION, "false");
            }
            sendJsonResponse(exchange, 200, Map.of("status", "success"));
        } catch (Exception e) {
            sendJsonResponse(exchange, 500,
                Map.of("status", "error", "message", e.getMessage()));
        }
    }
}

Inherited Methods from BaseSettingsHandler

Method Purpose
handleResourceRequest(exchange, filename) Serves a file from src/main/resources/<pluginName>/
sendJsonResponse(exchange, statusCode, map) Sends a JSON response with the given status code and data

6. Static Resources (Settings UI)

Place files under src/main/resources/<PLUGIN_NAME>/. The folder name must match Vars.PLUGIN_NAME. The settings UI fetches from relative paths (e.g., api/get-settings), which the BaseSettingsHandler routes to your handleCustomRoute.

settings.html (Minimal Example)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>My Plugin Settings</title></head>
<body>
    <label>
        <input type="checkbox" id="my_option"> Enable my option
    </label>
    <button id="save-btn">Save</button>
    <script src="script.js"></script>
</body>
</html>

script.js (Load + Save via Fetch)

async function loadSettings() {
    const res = await fetch('api/get-settings');
    const data = await res.json();
    document.getElementById('my_option').checked = data.my_option === true;
}

async function saveSettings() {
    const settings = {
        my_option: document.getElementById('my_option').checked
    };
    await fetch('api/save-settings', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(settings)
    });
}

document.addEventListener('DOMContentLoaded', () => {
    loadSettings();
    document.getElementById('save-btn').addEventListener('click', saveSettings);
});

7. Reading Settings in Tools at Runtime

Settings are persisted by the host. Use SettingAPI.putSettingValue(...) to write from your settings handler, and SettingAPI.getSettingValue(...) to read anywhere in your plugin — including inside @Tool methods.

// In any @Tool method or anywhere in your plugin:
String value = SettingAPI.getSettingValue(Vars.PLUGIN_NAME, "my_option");
boolean enabled = value != null ? Boolean.parseBoolean(value) : Vars.DEFAULT_MY_OPTION;

8. Build & Package

Run the Gradle build to produce your plugin JAR. The resulting JAR contains your compiled classes, bundled dependencies (from from(zipTree(...))), and resources from src/main/resources/. Drop this JAR into the EazeEditor plugins directory to install.

./gradlew jar
# Output: build/libs/my-plugin.jar

Quick Checklist

Use this checklist to verify your plugin is configured correctly before building:

  • @EazeAiComponent on your MCP class → tools auto-registered
  • @Tool("description") on each public method → exposed to AI
  • @P("description") on each parameter → AI knows what to pass
  • -parameters compiler flag in build.gradle → parameter names preserved
  • @PluginLifeCycle(phase) on lifecycle methods → auto-discovered
  • @PluginSettingsDiscovery(...) on a class → settings metadata registered
  • BaseSettingsHandler subclass instantiated in ON_LOAD → settings UI served
  • Resource folder name matches PLUGIN_NAME → resources resolve correctly
  • eaze_editor_code_sdk.jar is compileOnly → not bundled in output JAR
  • Implementation deps bundled via from(zipTree(...)) in jar task

Example Implementation

For a full, working reference implementation that covers all of the above concepts — including AI tool registration, lifecycle management, settings UI, and build configuration — check out the Autonomous Shell Plugin:

autonomous-shell-eaze-editor-plugin
A complete EazeEditor plugin example by nurujjamanpollob