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:
-
@EazeAiComponenton 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 -
-parameterscompiler flag in build.gradle → parameter names preserved -
@PluginLifeCycle(phase)on lifecycle methods → auto-discovered -
@PluginSettingsDiscovery(...)on a class → settings metadata registered -
BaseSettingsHandlersubclass instantiated inON_LOAD→ settings UI served -
Resource folder name matches
PLUGIN_NAME→ resources resolve correctly -
eaze_editor_code_sdk.jariscompileOnly→ 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: