Adds a declarative blueprint.{yaml,json} manifest layer on top of the existing dynamic-module plugin loader. Each plugin can now ship as a self-contained package with a manifest describing name, version, author, type, entrypoint class, and capabilities. The legacy flat .py plugin format remains fully supported.
Manifest schema v1 with fields: schema_version, name (slug), version (semver), author, type (metadata_extractor), entrypoint.{module,class}, optional capabilities/tags/license/homepage/description/min_majoor_version. Path-traversal and module-name injection are rejected at parse time. YAML manifests require PyYAML (already shipped by ComfyUI core); JSON manifests need no extra deps.
Loader: PluginLoader.discover_plugins now treats subdirectories with a blueprint manifest as blueprint packages (taking precedence over plain Python-package scanning). New PluginLoader.blueprints property and get_stats[blueprints] field expose loaded manifests for listing endpoints.
Migrated bundled examples to blueprint layout: plugins/examples/wanvideo/, plugins/examples/custom_node_template/, plugins/examples/rgthree/ (YAML variant).
Tests: 11 new tests in tests/features/test_blueprint_plugins.py covering manifest parsing edge cases (bad name, bad semver, unknown type, path traversal, schema mismatch), YAML support, discovery skipping invalid manifests, and loader integration including a missing-entrypoint warning path.
Majoor Assets Manager - Plugin Examples
This directory contains example metadata extractor plugins for the Majoor Assets Manager.
Two plugin layouts are supported:
- Blueprint format (recommended) — a directory containing a declarative
blueprint.yaml(orblueprint.json) manifest plus the entrypoint module. - Flat module format (legacy) — a single
*.pyfile dropped into a plugin directory. Still supported; new plugins should use the blueprint format.
Blueprint Layout
my_plugin/
blueprint.yaml # or blueprint.json
my_extractor.py # entrypoint module
Manifest fields (v1):
| Field | Required | Notes |
|---|---|---|
schema_version |
yes | Currently 1 |
name |
yes | Lowercase slug [a-z][a-z0-9_]+ |
version |
yes | Semver, e.g. 1.2.0 |
author |
yes | Free text |
type |
yes | Only metadata_extractor for now |
entrypoint.module |
yes | Filename without .py, inside the package |
entrypoint.class |
yes | Class implementing MetadataExtractorPlugin |
description, license, homepage, min_majoor_version, tags, capabilities |
no | Informational |
See examples/wanvideo/blueprint.json and examples/rgthree/blueprint.yaml
for working manifests.
Available Examples
1. WanVideo Extractor (examples/wanvideo/)
Extracts WanVideo custom node metadata from PNG/WebP files. Priority 100.
2. Custom Node Template (examples/custom_node_template/)
Documented template for creating custom extractors. Priority 50.
3. rgthree Skeleton (examples/rgthree/)
Minimal blueprint demonstrating the YAML manifest variant. Priority 50.
Installation
Option 1: Copy to Global Plugin Directory
# Windows (PowerShell)
Copy-Item wanvideo_extractor.py ~/.comfyui/majoor_plugins/extractors/
# Linux/macOS
cp wanvideo_extractor.py ~/.comfyui/majoor_plugins/extractors/
Option 2: Copy to Local Plugin Directory
# Copy to ComfyUI output directory
cp wanvideo_extractor.py <ComfyUI>/output/_mjr_plugins/extractors/
Option 3: Use Bundled Plugins Directory
# Copy to extension's plugins directory
cp wanvideo_extractor.py <ComfyUI>/custom_nodes/ComfyUI-Majoor-AssetsManager/plugins/
Usage
After installing plugins:
-
Restart ComfyUI or reload plugins via API:
curl -X POST http://localhost:8188/mjr/am/plugins/reload -
List installed plugins:
curl http://localhost:8188/mjr/am/plugins/list -
Enable/Disable plugins via Assets Manager UI:
- Open Settings → Plugins tab
- Toggle plugins on/off
Creating Your Own Plugin
See custom_node_extractor.py for a complete template with documentation.
Quick Start
from mjr_am_backend.features.metadata.plugin_system.base import (
MetadataExtractorPlugin,
ExtractionResult,
)
class MyExtractor(MetadataExtractorPlugin):
@property
def name(self): return "my_extractor"
@property
def supported_extensions(self): return ['.png']
@property
def priority(self): return 50
async def extract(self, filepath: str) -> ExtractionResult:
# Your extraction logic here
data = {"key": "value"}
return self._create_success_result(data)
Troubleshooting
Plugin Not Loading
- Check logs for validation errors
- Verify file is in correct directory
- Ensure plugin inherits from
MetadataExtractorPlugin - Check for syntax errors
Extraction Fails
- Enable debug logging:
MJR_PLUGIN_LOG_LEVEL=DEBUG - Check file permissions
- Verify file format is supported
Performance Issues
- Reduce plugin priority to run later
- Implement caching in plugin
- Use
pre_extract()for quick rejections
Security Notes
Plugins are validated before loading. The following are blocked:
eval()/exec()usageos.system()/subprocesscalls- Network access (socket, requests)
- Unsafe deserialization (pickle, marshal)
See PLUGIN_SYSTEM_DESIGN.md for complete security model.
License
All example plugins are provided under the MIT license.
For complete documentation, see:
docs/PLUGIN_SYSTEM_DESIGN.md- Architecture and designdocs/PLUGIN_DEVELOPMENT_GUIDE.md- Development guide (TODO: create)