# How to Create a Custom Modly Extension: Complete Guide to Manifest Fields and Required Structure

> Learn how to create a custom Modly extension. This guide covers the essential manifest fields id name type version author description entry and nodes plus specific model fields for successful development.

- Repository: [lightningpixel/modly](https://github.com/lightningpixel/modly)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Creating a custom Modly extension requires a properly configured [`manifest.json`](https://github.com/lightningpixel/modly/blob/main/manifest.json) file with eight mandatory fields—`id`, `name`, `type`, `version`, `author`, `description`, `entry`, and `nodes`—plus model-specific fields when building model extensions.**

Modly extensions are self-contained packages that extend the platform's workflow capabilities through custom processing nodes or AI model integrations. The extension system, as implemented in `lightningpixel/modly`, uses a strict manifest validation process that runs at installation time to ensure compatibility and security.

## Overview of the Extension Creation Process

Extensions in Modly follow a predictable lifecycle: folder creation → manifest definition → entry point implementation → local testing → optional publication. The core validation logic resides in [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts), specifically within the `parseExtensionManifest()` function (lines 911–923), where each manifest undergoes schema verification before the extension becomes available in the UI.

Extensions can be loaded from two sources:
- Local folders under `~/.modly/extensions/` or the app's `extensions/` directory
- Remote GitHub repositories via the **Install from GitHub** dialog

## Step 1: Create the Extension Folder Structure

Every Modly extension lives as a discrete folder containing at minimum a [`manifest.json`](https://github.com/lightningpixel/modly/blob/main/manifest.json) file and an entry point script.

```bash

# Create local extension directory

mkdir -p ~/.modly/extensions/my-awesome-ext
cd ~/.modly/extensions/my-awesome-ext

# Required files

touch manifest.json
touch processor.js  # or your preferred entry filename

```

Modly scans registered extension directories at startup, parsing each [`manifest.json`](https://github.com/lightningpixel/modly/blob/main/manifest.json) found in immediate subdirectories.

## Step 2: Define Required Manifest Fields

The [`manifest.json`](https://github.com/lightningpixel/modly/blob/main/manifest.json) file serves as the single source of truth for extension identity and capabilities. Based on the `ParsedManifest` type definition in [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts), these eight fields are mandatory for all extension types:

| Field | Type | Description |
|-------|------|-------------|
| `id` | string | Unique identifier, conventionally `author/extension-name` |
| `name` | string | Human-readable display name shown in the UI |
| `type` | `"process"` \| `"model"` | Determines runtime behavior and validation rules |
| `version` | string | Semantic version (e.g., `"1.0.0"`) |
| `author` | string | Creator name or organization |
| `description` | string | Short summary for extension browser |
| `entry` | string | Relative path to main JavaScript file |
| `nodes` | array | Workflow node definitions provided by this extension |

### Complete Base Manifest Example

```json
{
  "id": "acme/text-processor",
  "name": "Text Utilities",
  "type": "process",
  "version": "1.0.0",
  "author": "Acme Corp",
  "description": "Common text transformation operations",
  "entry": "processor.js",
  "nodes": [
    {
      "id": "uppercase",
      "label": "To Uppercase",
      "description": "Converts input text to uppercase",
      "inputSchema": {
        "type": "object",
        "properties": {
          "text": { "type": "string" }
        }
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "text": { "type": "string" }
        }
      }
    }
  ]
}

```

## Step 3: Implement Model-Specific Fields (When Applicable)

Model extensions require two additional fields beyond the base manifest. The [`extension-install-utils.ts`](https://github.com/lightningpixel/modly/blob/main/extension-install-utils.ts) file (line 37) explicitly validates these and throws an installation error if `generator_class` is absent:

- **`generator_class`**: String name of the model generator class exported by the entry file
- **`nodeClass`** (optional): Custom node class identifier for specialized UI rendering

```json
{
  "id": "acme/custom-llm",
  "name": "Custom Language Model",
  "type": "model",
  "version": "1.0.0",
  "author": "Acme AI Lab",
  "description": "Fine-tuned model for domain-specific tasks",
  "entry": "generator.js",
  "generator_class": "DomainSpecificLLM",
  "nodes": [
    {
      "id": "generate",
      "label": "Generate",
      "description": "Run inference with custom model"
    }
  ]
}

```

## Step 4: Write the Entry Point Script

The file referenced by `entry` must export the appropriate interface based on extension type.

### Process Extension Entry Point

For `type: "process"`, export an async `run` function receiving `{ input, params }`:

```javascript
// processor.js
export async function run({ input, params }) {
  const { text = '' } = input || {};
  const { addPrefix = false, prefix = '' } = params || {};
  
  const processed = addPrefix ? `${prefix}${text}` : text;
  
  return {
    text: processed,
    charCount: processed.length,
    wordCount: processed.split(/\s+/).filter(Boolean).length
  };
}

```

The runtime execution path flows through [`workflowRunStore.ts`](https://github.com/lightningpixel/modly/blob/main/workflowRunStore.ts) (lines 428–440), where `window.electron.extensions.runProcess()` ultimately invokes this function.

### Model Extension Entry Point

For `type: "model"`, export the generator class matching `generator_class`:

```javascript
// generator.js
export class DomainSpecificLLM {
  constructor(config) {
    this.config = config;
  }
  
  async generate(prompt, options = {}) {
    // Model inference implementation
    return {
      text: `Generated response for: ${prompt}`,
      tokensUsed: prompt.length / 4
    };
  }
  
  async *stream(prompt, options = {}) {
    // Streaming implementation for real-time output
    yield { chunk: "Partial " };
    yield { chunk: "response" };
  }
}

```

## Step 5: Define Node Specifications

Each object in the `nodes` array requires at minimum `id` and `label`. Full node schema enables rich workflow editor integration:

```json
{
  "nodes": [
    {
      "id": "transform",
      "label": "Transform Text",
      "description": "Applies configurable text transformations",
      "inputSchema": {
        "type": "object",
        "required": ["text"],
        "properties": {
          "text": { "type": "string", "description": "Input text" }
        }
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "text": { "type": "string" },
          "stats": { "type": "object" }
        }
      },
      "paramSchema": {
        "type": "object",
        "properties": {
          "operation": {
            "type": "string",
            "enum": ["uppercase", "lowercase", "reverse"],
            "default": "uppercase"
          }
        }
      }
    }
  ]
}

```

## Step 6: Test and Validate Locally

Install your extension through Modly's UI to verify manifest parsing:

1. Open **Extensions → Install from Local**
2. Select your extension folder
3. Check for `manifestError` indicators in the UI
4. Create a test workflow using your extension's nodes

Validation failures surface as error toasts with line references to `parseExtensionManifest()` in [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts).

## Step 7: Publish via GitHub (Optional)

For distribution, push to a public repository and install via **Install from GitHub**. The installer ([`extension-install-utils.ts`](https://github.com/lightningpixel/modly/blob/main/extension-install-utils.ts), lines 18–41) performs identical validation before registration:

```bash

# Repository structure for GitHub installation

my-modly-extension/
├── manifest.json
├── processor.js
└── README.md

```

## Manifest Validation in Source Code

The validation pipeline enforces field presence through explicit checks:

| File | Function | Lines | Purpose |
|------|----------|-------|---------|
| [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts) | `parseExtensionManifest()` | 911–923 | Schema validation, required field checks |
| [`electron/main/extension-install-utils.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/extension-install-utils.ts) | `installExtension()` | 18–41 | Installation orchestration, model field verification |
| [`electron/main/extension-install-utils.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/extension-install-utils.ts) | Model loader | 37 | `generator_class` presence check |

## Summary

- **Eight fields are mandatory** in every Modly extension manifest: `id`, `name`, `type`, `version`, `author`, `description`, `entry`, and `nodes`
- **Model extensions require** `generator_class` in addition to base fields
- **Process extensions** export an async `run({ input, params })` function
- **Model extensions** export a class matching the `generator_class` name
- **Validation occurs** in `parseExtensionManifest()` before any extension becomes available
- **Local testing** uses **Install from Local**; distribution uses **Install from GitHub**

## Frequently Asked Questions

### What happens if I omit a required manifest field?

Modly rejects the extension during installation with a validation error. The `parseExtensionManifest()` function in [`electron/main/ipc-handlers.ts`](https://github.com/lightningpixel/modly/blob/main/electron/main/ipc-handlers.ts) checks for all mandatory fields and returns a structured error object that the UI displays as a `manifestError` notification with specific field references.

### Can I use a different entry filename than [`processor.js`](https://github.com/lightningpixel/modly/blob/main/processor.js)?

Yes. The `entry` field accepts any relative path within your extension folder. Process extensions default to [`processor.js`](https://github.com/lightningpixel/modly/blob/main/processor.js) only when `entry` is omitted, but explicit declaration is recommended for clarity. The runtime resolves this path relative to the extension's root directory.

### How do I debug a failing extension installation?

Check the DevTools console for validation errors from `parseExtensionManifest()`. Enable verbose logging in Modly's settings to see full manifest parsing traces. Common failures include malformed JSON, missing `generator_class` for model types, or node definitions lacking required `id` and `label` properties.