create-plugin
GitHub辅助创建符合TIS规范的插件,自动生成包含Describable接口、Descriptor类、属性注解及JSON描述文件的完整代码结构。
Trigger Scenarios
Install
npx skills add datavane/tis --skill create-plugin -g -y
SKILL.md
Frontmatter
{
"name": "create-plugin",
"description": "Create a TIS plugin conforming to TIS plugin specifications"
}
TIS Plugin Creator
This skill helps you create a well-structured TIS (Data Integration Service) plugin that conforms to all TIS plugin specifications.
What This Skill Does
When invoked, this skill will:
- Analyze the user-provided context to understand the plugin's functionality
- Design the plugin structure with appropriate properties (general or aggregated)
- Generate the complete plugin implementation including:
- Main plugin class implementing
Describable - Inner
Descriptorclass with@TISExtension - Properties with proper
@FormFieldannotations - Property validation methods
- Corresponding
.jsonproperty descriptor file - Optional
.mdhelp documentation
- Main plugin class implementing
TIS Plugin Specifications
Core Requirements
Every TIS plugin MUST satisfy:
-
Implement Describable Interface
- Plugin class must implement
com.qlangtech.tis.extension.Describable(directly or via parent)
- Plugin class must implement
-
Inner Descriptor Class
- Must have a
publicinner class extendingcom.qlangtech.tis.extension.Descriptor - Annotated with
@TISExtensionfromcom.qlangtech.tis.extension.TISExtension
- Must have a
-
Property Requirements
- All properties must be
public - Must be annotated with
@FormField
- All properties must be
-
Property Descriptor Files
- A
.jsonfile in the same package underresources/directory - Optional
.mdfile for rich markdown help content
- A
Property Types
General Properties
Basic Java types:
Boolean/booleanInteger/intStringLong/longjava.util.Datecom.qlangtech.tis.plugin.MemorySize- Duration types
Aggregated Properties
Properties that are themselves Describable plugins, providing polymorphic capability. Example: ClusterType in TISFlinkCDCStreamFactory.
IMPORTANT: Each concrete implementation class of an aggregated property is a full plugin and MUST have:
- Its own Java class with
@FormFieldproperties - Its own
@TISExtensionDescriptor - Descriptor MUST implement
DescriptorUseableShortCommentinterface (fromtis-plugin/src/main/java/com/qlangtech/tis/extension/DescriptorUseableShortComment.java)- Implement
shortComment()method returning a brief Chinese description of the plugin's function - Requirements for shortComment():
- Use Chinese language (中文)
- Maximum 10 characters (10个字以内)
- NO punctuation marks (no periods, commas, etc.)
- Will be displayed in the frontend UI to help users understand the plugin at a glance
- Example: "Hadoop文件系统管理" or "Hive元数据存储"
- Implement
- Its own
.jsondescriptor file in resources (same package path) - Optional
.mdhelp file if properties are complex
Parent Class Requirements for Aggregated Properties:
- If an aggregated property type (e.g.,
IcebergCatalog) will have multiple concrete implementations, the parent abstract class MUST define aBasicDescriptor:- Must be a protected abstract static inner class
- Must extend
Descriptor<ParentClassName> - All concrete implementation classes' Descriptors MUST extend this
BasicDescriptor - Example structure in parent class:
public abstract class IcebergCatalog implements Describable<IcebergCatalog> { // properties... protected abstract static class BasicDescriptor extends Descriptor<IcebergCatalog> { // common descriptor logic for all implementations } } - Example in implementation class:
public class HadoopCatalog extends IcebergCatalog { // properties... @TISExtension() public static class DefaultDescriptor extends BasicDescriptor implements DescriptorUseableShortComment { @Override public String shortComment() { return "基于HDFS文件系统"; } @Override public String getDisplayName() { return "Hadoop Catalog"; } } }
Example: If DataxIcebergWriter has an aggregated property IcebergCatalog catalog, and HadoopCatalog extends IcebergCatalog, then you need:
IcebergCatalog.java(abstract parent with protected abstract static BasicDescriptor inner class)HadoopCatalog.java(with its own @FormField properties, Descriptor extends BasicDescriptor)HadoopCatalog.json(describing HadoopCatalog's own properties)- Descriptor implements
DescriptorUseableShortCommentwithshortComment()returning Chinese text (≤10 chars, no punctuation) - Optional
HadoopCatalog.md(if properties are complex)
@FormField Annotation Attributes
- identity: Unique identifier for the plugin instance (acts as primary key)
- ordinal: Display order in UI form (lower = higher priority, semantically related fields should be adjacent)
- advance: Mark as advanced setting (must have default value, can be hidden)
- validate: Frontend validation rules (see Validator options below)
- type: Field type (see FormFieldType options below)
Validator Options
Reference: tis-plugin/src/main/java/com/qlangtech/tis/plugin/annotation/Validator.java
require: Required fielduser_name: Username format (letters, numbers, underscore, dot, dash)email: Email formatforbid_start_with_number: Cannot start with numberidentity: Primary key formatinteger: Integer formathost: Internet domain with optional port (e.g.,192.168.28.200:7070)hostWithoutPort: Domain without porturl: URL starting with http/httpsdb_col_name: Database column name formatrelative_path: File system relative pathabsolute_path: Unix absolute pathnone_blank: Non-empty content
FormFieldType Options
Reference: tis-plugin/src/main/java/com/qlangtech/tis/plugin/annotation/FormFieldType.java
MULTI_SELECTABLE: Multi-select, property typeList<IdentityName>orList<String>INPUTTEXT: Single-line text input, property typeStringSELECTABLE: Single-select dropdown (register options viaregisterSelectOptions()in Descriptor)PASSWORD: Password inputFILE: File upload (only one per form, plugin must implementITmpFileStore)TEXTAREA: Multi-line text input (for SQL scripts, XML, etc.)DATE: Date pickerJDBCColumn: JDBC column typeINT_NUMBER: Integer number inputENUM: Enumeration selectionDateTime: Date-time (UTC, property typelongorjava.util.Date)DECIMAL_NUMBER: Decimal numberDURATION_OF_SECOND: Duration in secondsDURATION_OF_MINUTE: Duration in minutesDURATION_OF_HOUR: Duration in hoursMEMORY_SIZE_OF_BYTE: Memory size in bytesMEMORY_SIZE_OF_KIBI: Memory size in KBMEMORY_SIZE_OF_MEGA: Memory size in MB
Validation Logic
Single Property Validation
Add a method in Descriptor with pattern validate{PropertyName}:
public boolean validateAge(IFieldErrorHandler msgHandler, Context context, String fieldName, String value) {
int age = Integer.parseInt(value);
if (age < MIN_AGE) {
msgHandler.addFieldError(context, fieldName, "Cannot be less than: " + MIN_AGE);
return false;
}
if (age > MAX_AGE) {
msgHandler.addFieldError(context, fieldName, "Cannot be greater than: " + MAX_AGE);
return false;
}
return true;
}
Multi-Property Joint Validation
Override validateAll in Descriptor:
@Override
protected boolean validateAll(IControlMsgHandler msgHandler, Context context, PostFormVals postFormVals) {
// Perform joint validation
if (!validateUserCredentials(postFormVals.newInstance())) {
msgHandler.addErrorMessage(context, "Validation failed");
return false;
}
return true;
}
Reference Implementations
Excellent examples in the codebase:
tis-plugin/src/main/java/com/qlangtech/tis/config/authtoken/impl/DefaultHiveUserToken.javatis-plugin/src/main/java/com/qlangtech/tis/manage/common/UserProfile.javatis-plugin/src/main/java/com/qlangtech/tis/plugin/alert/impl/LoginPlugin.java(multi-property validation)
Instructions for Claude
When the user invokes /create-plugin, follow this workflow:
Step 1: Analyze Context
- Ask the user to provide context about the plugin they want to create (or the user may provide it directly)
- Analyze the context to determine:
- Plugin's purpose and functionality
- What properties are needed (general vs aggregated)
- Parent class to extend (if any)
- Validation rules required
Step 2: Design Plugin Structure
- Determine property organization:
- Which properties should be general types?
- Which properties should be aggregated (polymorphic)?
- For each aggregated property:
- Will it have multiple concrete implementations?
- If yes, the parent abstract class MUST define a
BasicDescriptorinner class - List all planned implementation classes
- Group semantically related properties via ordinal ordering
- Plan validation logic:
- Which properties need business logic validation?
- Are there any multi-property joint validations?
Step 3: Confirm Design with User
Before implementation, present:
- Plugin class name and package
- List of properties with types, FormFieldType, and validators
- Any aggregated properties and their subtypes (each subtype will be a separate plugin with its own files)
- For each aggregated property subtype, list:
- Subtype class name and parent class
- Its own properties with types and validators
- Files to be generated (Java + JSON + optional MD)
- Validation methods to be implemented
- Wait for user approval
Step 4: Implement Plugin
Generate these files in the correct structure:
-
Plugin Java Class
- Package declaration
- Imports
- Plugin class implementing
Describable<PluginClassName> - Public properties with
@FormFieldannotations - Business logic methods
- Inner
Descriptorclass with:@TISExtensionannotationgetDisplayName()overridevalidate*methods for property validationvalidateAll()if needed for joint validationregisterSelectOptions()if using SELECTABLE fields
-
Property Descriptor JSON (
PluginClassName.json)- CRITICAL: Correct JSON Format
{ "propertyName1": { "label": "Display Label", "placeholder": "Input placeholder text", "help": "Short one-sentence help text", "dftVal": "default value" }, "propertyName2": { "label": "Another Property", "help": "Brief description" } } - WRONG Format (DO NOT USE):
{ "formFields": [ {"key": "propertyName1", "label": "...", "help": "..."} ] } - In
src/main/resources/{package_path}/ - Direct object-to-object mapping (propertyName → property descriptor)
- Each property descriptor contains:
label: Display label (required)placeholder: Input placeholder (optional, applicable for text inputs)help: Brief one-sentence help text (optional, for simple properties)dftVal: Default value (optional, for optional fields)
- CRITICAL: Correct JSON Format
-
Optional Markdown Help (
PluginClassName.md)- Create ONLY for properties that need detailed multi-line explanation
- Help Information Distribution Rule:
- Simple properties (username, password, port, simple flags): Only define
helpin JSON file with one sentence - Complex properties (catalog types, file formats, configuration objects, properties needing examples): Define detailed help in
.mdfile with## propertyNameheaders - DO NOT duplicate: If a property has detailed help in
.mdfile, keep JSONhelpbrief or omit it
- Simple properties (username, password, port, simple flags): Only define
- Decision criteria for .md file:
- Property requires multiple sentences to explain
- Property needs code examples or configuration samples
- Property has multiple options that need detailed comparison
- Property involves technical concepts requiring elaboration
- Use
## propertyNameheaders for each complex property that needs detailed documentation
-
For Aggregated Properties: Parent Abstract Class
- IMPORTANT: If the plugin has aggregated properties with multiple implementations, first create or verify the parent abstract class:
Parent Abstract Class (e.g.,
IcebergCatalog.java)- Implements
Describable<ParentClassName> - Has
@FormFieldproperties that are common to all implementations - MUST define a
BasicDescriptorinner class:- Declared as
protected abstract static class BasicDescriptor extends Descriptor<ParentClassName> - Contains common descriptor logic for all implementations
- May implement common validation methods
- Example:
public abstract class IcebergCatalog implements Describable<IcebergCatalog> { @FormField(ordinal = 1, validate = {Validator.require}) public String catalogName; @FormField(ordinal = 2) public String databaseName; // abstract methods... protected abstract static class BasicDescriptor extends Descriptor<IcebergCatalog> { // common descriptor logic @Override protected boolean verify(IFieldErrorHandler msgHandler, Context context, PostFormVals postFormVals) { return super.verify(msgHandler, context, postFormVals); } } }
- Declared as
-
For Each Aggregated Property Implementation Class
- IMPORTANT: If the plugin has aggregated properties (polymorphic plugin properties), EACH concrete implementation class is a separate plugin and needs:
Example: Main plugin
DataxIcebergWriterhas propertyIcebergCatalog catalog, and you're creatingHadoopCatalog extends IcebergCatalog:a. Implementation Java Class (
HadoopCatalog.java)- Extends the abstract parent class (e.g.,
IcebergCatalog) - Has its own
@FormFieldproperties (in addition to inherited ones) - Has its own
@TISExtensionDescriptor that:- MUST extend parent's BasicDescriptor
- MUST implement
DescriptorUseableShortCommentinterface - MUST override
shortComment()method with these requirements:- Return Chinese text (中文) describing the plugin function
- Maximum 10 characters (10个字以内)
- NO punctuation marks (不要标点符号)
- Be concise and clear
- Example for HadoopCatalog:
return "基于HDFS文件系统"; - Example for HiveMetastoreCatalog:
return "使用Hive元数据";
- Override
getDisplayName()if needed
- Same structure as main plugin
b. Implementation JSON Descriptor (
HadoopCatalog.json)- In
src/main/resources/{package_path}/(same package as HadoopCatalog.java) - Describes only HadoopCatalog's own properties (not inherited ones)
- Same format rules as main plugin JSON
- Example:
{ "warehouse": { "label": "Warehouse Path", "help": "HDFS or local filesystem path" }, "hadoopConfDir": { "label": "Hadoop Config Directory", "help": "Path to Hadoop configuration directory" } }
c. Optional Implementation MD (
HadoopCatalog.md)- Create if HadoopCatalog has complex properties
- Same rules as main plugin MD file
d. Repeat for ALL implementation classes
- If there's also
HiveMetastoreCatalog, create its Java + JSON + optional MD - If there's
GlueCatalog, create its Java + JSON + optional MD - Each implementation is a complete plugin with full file set
Step 5: Verify Implementation
- Check all requirements are satisfied:
- Implements
Describable - Has
@TISExtensioninner Descriptor - All properties are public with
@FormField - Ordinal values are properly sequenced
- Validation rules are appropriate
- JSON descriptor format is correct (object-to-object, NOT formFields array)
- All properties in JSON file exist as public fields in Java class
- Help information distribution is correct:
- Simple properties: only
helpin JSON (one sentence) - Complex properties: detailed help in
.mdfile, brief or nohelpin JSON - No duplicate help content between JSON and .md files
- Simple properties: only
- For aggregated properties:
- Parent abstract class requirements:
- Has a
protected abstract static class BasicDescriptor extends Descriptor<ParentClass> - BasicDescriptor is properly defined in the parent class
- Has a
- Each implementation class has its complete file set:
- Java class exists with @TISExtension Descriptor
- Descriptor extends parent's BasicDescriptor
- Descriptor implements
DescriptorUseableShortCommentinterface shortComment()method implemented correctly:- Returns Chinese text (中文)
- Length ≤ 10 characters
- No punctuation marks
- Clearly describes the plugin's function
- JSON descriptor exists in correct resources path
- JSON describes only the implementation's own properties (not inherited ones)
- Optional MD file if implementation has complex properties
- Parent abstract class requirements:
- Implements
- Suggest any improvements
Step 6: Provide Usage Instructions
- Explain where the plugin files were created
- How to build and test the plugin
- How to register the plugin in TIS (if special steps needed)
Important Considerations
-
JSON Format (CRITICAL): The property descriptor JSON file MUST use object-to-object format, NOT array format.
- ✅ Correct:
{"propertyName": {"label": "...", "help": "..."}} - ❌ Wrong:
{"formFields": [{"key": "propertyName", ...}]} - Wrong format will cause TIS backend to fail loading the plugin
- ✅ Correct:
-
Help Information Distribution: Avoid duplicating help content between JSON and .md files.
- Simple properties (username, password, port, boolean flags): Use only JSON
helpfield with one concise sentence - Complex properties (catalog types, configuration objects, properties with multiple options): Use .md file with detailed explanation, keep JSON
helpbrief or omit it - Rule of thumb: If explanation fits in one sentence, use JSON only. If it needs examples, bullet points, or multiple paragraphs, use .md file
- Let the AI judge the complexity: analyze each property's nature and decide the appropriate help placement
- Simple properties (username, password, port, boolean flags): Use only JSON
-
Ordinal Sequencing: Assign ordinal values carefully. Related properties (like username/password) should have consecutive ordinals to appear together in the UI.
-
Default Values: Advanced properties (with
advance = true) MUST have sensible default values. -
Validation: Always implement business logic validation for properties that need it. Don't rely solely on frontend validation.
-
Parent Classes: Check if there's an appropriate parent class to extend (like
AlertChannelfor alert plugins) to inherit common functionality. -
Package Organization: Place the plugin in the appropriate package that reflects its functionality (e.g., alert plugins in
*.plugin.alert.impl.*).
Example Interaction
User: /create-plugin
I want to create an email alert channel plugin for TIS. It should support:
- SMTP host and port
- Authentication (username/password)
- SSL support
- Sender email address
Version History
- 95bcb21 Current 2026-09-08 17:31


