ADR-074: Workflow Tool Architecture (Wf Prefix Convention)
Status: Proposed Date: 2025-12-30 Related: ADR-051 (Application Generator), ADR-054 (AI Tool Architecture), ADR-055 (Tool Ecosystem Review)
Context and Problem Statement
The iDempiere Hub has evolved into a multi-layered tool ecosystem with three AI integration patterns (MCP, LangChain4j, Chat API). As documented in ADR-054 and ADR-055, we have:
- Individual Tools (Layer 2) - Single operations like
createTable(),generate2Pack() - Implicit Workflows - ADR-051 Application Generator is a workflow but not formalized
Problem: There's no clear architectural distinction between:
- Individual tools (single operations)
- Workflows (orchestrations of multiple tools)
This leads to:
- Confusion about tool scope and responsibility
- No naming convention to distinguish workflows from tools
- Unclear patterns for creating new workflows
Decision Drivers
- Clarity: Clear distinction between tools and workflows
- Discoverability: AI agents can easily identify workflow-level operations
- Consistency: Uniform naming convention across MCP, LangChain4j, Chat API
- Extensibility: Pattern for adding new workflows
- ADR-051 Alignment: Formalize the implicit workflow pattern from Application Generator
Decision
Introduce a four-layer tool architecture with explicit "Wf" prefix for workflow tools.
Layer Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ Layer 3: WORKFLOWS (Wf prefix) │
│ ─────────────────────────────────────────────────────────────── │
│ │
│ Orchestration of multiple tools for business-level operations │
│ │
│ Examples: │
│ • WfDeployTable() - createTable → createWindow → 2Pack → packIn│
│ • WfDeployApplication()- Full app generation (ADR-051) │
│ • WfMigrateSchema() - Generate migration → apply → verify │
│ │
│ Characteristics: │
│ • Orchestrates 2+ Layer 2 tools │
│ • Business-level abstraction │
│ • May be stateful (track progress) │
│ • Human-readable goal descriptions │
├─────────────────────────────────────────────────────────────────────┤
│ Layer 2: TOOLS (no prefix) │
│ ─────────────────────────────────────────────────────────────── │
│ │
│ Individual operations exposed via MCP/LangChain4j │
│ │
│ Examples: │
│ • createTable() - Create AD table + columns │
│ • createWindow() - Create AD window + tabs + fields │
│ • generate2Pack() - Generate 2Pack ZIP │
│ • packin2Pack() - Deploy via REST API (NEW) │
│ • generateMigration() - Generate SQL migration script │
│ │
│ Characteristics: │
│ • Single responsibility │
│ • Stateless │
│ • Direct service invocation │
├─────────────────────────────────────────────────────────────────────┤
│ Layer 1: SERVICES (business logic) │
│ ─────────────────────────────────────────────────────────────── │
│ │
│ Business logic used by both tools and workflows │
│ │
│ Examples: │
│ • TableService - Table CRUD operations │
│ • WindowService - Window CRUD operations │
│ • PackOutService - 2Pack generation logic │
│ • MigrationService - Migration script generation │
│ │
│ Characteristics: │
│ • No AI/MCP awareness │
│ • Reusable by CLI, API, tools, workflows │
│ • Transaction-safe │
├─────────────────────────────────────────────────────────────────────┤
│ Layer 0: DATA │
│ ─────────────────────────────────────────────────────────────── │
│ │
│ • PostgreSQL (Application Dictionary) │
│ • iDempiere REST API │
│ • File System (2Pack, migrations) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Naming Convention
| Layer | Prefix | Example Class | Example Method |
|---|---|---|---|
| Workflow | Wf |
WfDeployToolLogic |
wfDeployTable() |
| Tool | (none) | TableToolLogic |
createTable() |
| Service | (none) | TableService |
create() |
MCP Tool Naming
// Layer 2: Individual Tools (no prefix)
@Tool(description = "Create a table in Application Dictionary")
ToolResponse createTable(...);
@Tool(description = "Generate 2Pack ZIP file")
ToolResponse generate2Pack(...);
@Tool(description = "Deploy 2Pack via REST API PackIn")
ToolResponse packin2Pack(...);
// Layer 3: Workflows (Wf prefix)
@Tool(description = "Deploy complete table: AD entry + window + 2Pack + PackIn")
ToolResponse wfDeployTable(...);
@Tool(description = "Deploy complete application from requirements")
ToolResponse wfDeployApplication(...);
Workflow Implementation Pattern
Class Structure
src/main/java/org/idempiere/cli/
├── ai/shared/ # Shared logic (Layer 1-3)
│ ├── ToolResult.java # Unified result
│ ├── ToolGuide.java # AI guidance
│ │
│ ├── TableToolLogic.java # Layer 2: Table tools
│ ├── WindowToolLogic.java # Layer 2: Window tools
│ ├── PackOutToolLogic.java # Layer 2: 2Pack tools
│ ├── MigrationToolLogic.java # Layer 2: Migration tools
│ │
│ └── workflow/ # Layer 3: Workflows
│ ├── WfDeployToolLogic.java # Deployment workflows
│ ├── WfMigrateToolLogic.java # Migration workflows
│ └── WfApplicationToolLogic.java# Application workflows (ADR-051)
│
├── mcp/tools/ # MCP Server wrappers
│ ├── McpTableTools.java # Layer 2 exposure
│ ├── McpWindowTools.java # Layer 2 exposure
│ ├── McpPackOutTools.java # Layer 2 exposure
│ └── McpWfTools.java # Layer 3 exposure (Wf* methods)
│
└── services/ # Layer 1: Business logic
├── TableService.java
├── WindowService.java
└── PackOutService.java
Workflow Logic Example
@ApplicationScoped
public class WfDeployToolLogic {
@Inject TableToolLogic tableLogic;
@Inject WindowToolLogic windowLogic;
@Inject PackOutToolLogic packOutLogic;
@Inject PackInToolLogic packInLogic; // NEW: REST API deployment
/**
* Workflow: Deploy a complete table with window and 2Pack.
*
* Steps:
* 1. Create table in Application Dictionary
* 2. Create window for the table
* 3. Generate 2Pack ZIP
* 4. Deploy via REST API PackIn
*
* @param tableName table name (e.g., "XX_MyTable")
* @param columns column definitions
* @param windowName optional window name
* @param deploy if true, deploy to iDempiere via REST API
* @return workflow result with all step outcomes
*/
public ToolResult wfDeployTable(String tableName, String columns,
String windowName, boolean deploy) {
WorkflowContext ctx = new WorkflowContext("wfDeployTable");
try {
// Step 1: Create table
ctx.startStep("createTable");
ToolResult tableResult = tableLogic.createTable(
tableName, null, columns, "U", "3", false);
ctx.completeStep(tableResult);
if (!tableResult.isSuccess()) {
return ctx.failed("Table creation failed: " + tableResult.getMessage());
}
// Step 2: Create window
ctx.startStep("createWindow");
String winName = windowName != null ? windowName : tableName;
ToolResult windowResult = windowLogic.createWindow(
tableName, winName, "M", "U", false);
ctx.completeStep(windowResult);
if (!windowResult.isSuccess()) {
return ctx.failed("Window creation failed: " + windowResult.getMessage());
}
// Step 3: Generate 2Pack
ctx.startStep("generate2Pack");
ToolResult packResult = packOutLogic.generate2Pack(
"com.cloudempiere." + tableName.toLowerCase(),
"1.0.0",
"Auto-generated package for " + tableName,
tableName,
winName,
null, null, null, null);
ctx.completeStep(packResult);
if (!packResult.isSuccess()) {
return ctx.failed("2Pack generation failed: " + packResult.getMessage());
}
// Step 4: Deploy via PackIn (optional)
if (deploy) {
ctx.startStep("packin2Pack");
String zipPath = (String) packResult.getData().get("packOut.outputPath");
ToolResult packInResult = packInLogic.packin2Pack(zipPath);
ctx.completeStep(packInResult);
if (!packInResult.isSuccess()) {
return ctx.partialSuccess(
"Table and 2Pack created, but deployment failed: " + packInResult.getMessage(),
ctx.getStepResults());
}
}
return ctx.success(String.format(
"Deployed table %s with window %s%s",
tableName,
winName,
deploy ? " (imported via PackIn)" : " (2Pack ready for import)"));
} catch (Exception e) {
return ctx.failed("Workflow error: " + e.getMessage());
}
}
}
MCP Workflow Exposure
@ApplicationScoped
public class McpWfTools {
@Inject WfDeployToolLogic deployLogic;
@Inject WfMigrateToolLogic migrateLogic;
@Tool(description = """
WORKFLOW: Deploy a complete table with window and 2Pack.
This is a multi-step workflow that:
1. Creates the table in Application Dictionary
2. Creates a window with tabs and fields
3. Generates a 2Pack ZIP file
4. Optionally deploys via REST API PackIn
Use this instead of individual tools when you want the full deployment cycle.
""")
ToolResponse wfDeployTable(
@ToolArg(description = "Table name (e.g., 'XX_MyTable')") String tableName,
@ToolArg(description = "Column definitions (e.g., 'S#Name,Q#Qty')") String columns,
@ToolArg(description = "Optional window name") String windowName,
@ToolArg(description = "If true, deploy to iDempiere via REST API") Boolean deploy) {
ToolResult result = deployLogic.wfDeployTable(
tableName, columns, windowName,
deploy != null ? deploy : false);
return toToolResponse(result);
}
@Tool(description = """
WORKFLOW: Deploy complete application from requirements.
This is a comprehensive workflow that:
1. Analyzes requirements (AI-assisted)
2. Designs database schema
3. Creates all tables and columns
4. Creates windows for each table
5. Generates business logic (processes, callouts)
6. Generates complete plugin structure
7. Generates 2Pack and documentation
See ADR-051 for full details.
""")
ToolResponse wfDeployApplication(
@ToolArg(description = "Application name") String name,
@ToolArg(description = "Requirements in markdown or YAML") String requirements,
@ToolArg(description = "Entity type prefix (e.g., 'CE_WMS')") String entityType) {
// Delegate to ADR-051 Application Generator
// ...
}
}
Workflow vs Tool Decision Matrix
| Scenario | Use Tool | Use Workflow |
|---|---|---|
| Create single table | createTable() |
- |
| Create table with window + deploy | - | wfDeployTable() |
| Generate 2Pack only | generate2Pack() |
- |
| Full application from requirements | - | wfDeployApplication() |
| Generate single migration script | generateMigration() |
- |
| Generate + apply + verify migration | - | wfMigrateSchema() |
AI Agent Guidance
Workflows should include AI guidance in their tool descriptions:
@Tool(description = """
WORKFLOW: Deploy a complete table to iDempiere.
WHEN TO USE:
- User wants to create a "complete" or "full" table
- User mentions "deploy" or "production-ready"
- User asks to create table "with window" or "with UI"
WHEN NOT TO USE:
- User only wants AD_Table entry (use createTable)
- User only wants 2Pack file (use generate2Pack)
- User is debugging/testing (use individual tools)
STEPS PERFORMED:
1. createTable() - AD_Table + AD_Column entries
2. createWindow() - AD_Window + AD_Tab + AD_Field entries
3. generate2Pack() - Export to portable ZIP
4. packin2Pack() - Import to target iDempiere (if deploy=true)
""")
ToolResponse wfDeployTable(...);
Required New Tools
Before implementing workflows, these Layer 2 tools are needed:
1. packin2Pack() - REST API Deployment
@Tool(description = "Deploy a 2Pack ZIP file to iDempiere via REST API PackIn")
ToolResponse packin2Pack(
@ToolArg(description = "Path to 2Pack ZIP file") String zipPath);
Uses the REST API flow documented in docs/guides/2pack.md:
- POST
/api/v1/models/AD_Package_Imp- Create PackIn record - POST
/api/v1/uploads- Initiate upload - PUT
/api/v1/uploads/{id}/chunks/1- Upload file - POST
/api/v1/uploads/{id}/copy- Attach to record - PUT
/api/v1/models/AD_Package_Imp/{id}- Set Processing=Y
Consequences
Positive
- Clear Abstraction - Obvious distinction between tools and workflows
- AI Discoverability - "Wf" prefix signals orchestration to AI agents
- Reusability - Tools compose into workflows without duplication
- Consistency - Uniform pattern across MCP, LangChain4j, Chat API
- ADR-051 Alignment - Formalizes the Application Generator pattern
Negative
- Naming Discipline - Team must follow convention consistently
- Overhead - Workflows add orchestration complexity
- Testing - Workflows require integration tests, not just unit tests
Mitigation
- Code review enforces naming convention
- Workflow base class reduces boilerplate
- Integration test templates provided
Implementation Roadmap
Phase 1: Foundation (This ADR)
- [x] Create ADR-074 documenting workflow architecture
- [ ] Review and approve
Phase 2: packin2Pack Tool
- [ ] Create
PackInToolLogic.java- REST API PackIn logic - [ ] Create
McpPackInTools.java- MCP exposure - [ ] Add to McpPackOutTools or separate class
Phase 3: First Workflow
- [ ] Create
workflow/package - [ ] Create
WfDeployToolLogic.java - [ ] Create
McpWfTools.java - [ ] Implement
wfDeployTable()
Phase 4: Application Generator Integration
- [ ] Migrate ADR-051 to workflow pattern
- [ ] Expose as
wfDeployApplication()
References
- ADR-051: AI-Driven Application Generator - Implicit workflow pattern
- ADR-054: AI Tool Architecture Clarity - Tool layer documentation
- ADR-055: Tool Ecosystem Architectural Review - Layer architecture
- docs/guides/2pack.md - REST API PackIn flow
Document Status: Proposed Next Step: Review and approve, then implement Phase 2 (packin2Pack)