ADR-026: CLI Execution Modes

Status

Proposed

Date

2025-12-07

Context

The idempiere-cli needs to support multiple execution modes for different deployment and usage scenarios:

  1. Local development - Developer runs commands on same machine
  2. CI/CD pipelines - Automated one-shot execution
  3. Container deployment - Long-running process in K8s/Docker
  4. Remote execution - Send commands from Computer A to Server B
  5. AI assistants - Claude Code/Desktop integration via MCP

Currently, only the MCP server mode (server mcp) stays running. All other commands exit after execution, which is problematic for container deployments and remote execution scenarios.

Research Summary

Key References

Resource URL Key Insight
CLI Guidelines https://clig.dev/ Comprehensive CLI design best practices
12 Factor CLI Apps https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46 Modern CLI principles
Unix Interface Design Patterns https://homepage.cs.uri.edu/~thenry/resources/unix_art/ch11s06.html Classic daemon/filter patterns
Quarkus Picocli + HTTP https://github.com/quarkusio/quarkus/discussions/43925 How to combine CLI with HTTP server
Docker Daemon Best Practices https://stackoverflow.com/questions/41494592 Don't daemonize inside container
Remote CLI via REST https://ganeshvelrajan.medium.com/execute-remote-commands-using-rest-apis-without-ssh-daba4a113608 REST API as SSH alternative
n8n CLI Architecture https://docs.n8n.io/hosting/cli-commands/ Multiple execution modes pattern

CLI Execution Mode Patterns

From Unix Interface Design Patterns:

Pattern Description Stays Running
Filter stdin → transform → stdout No
Cantrip Do something, exit No
Source Generate data → stdout No
Sink stdin → process No
Compiler file → transform → file No
Daemon/Server Background service, shared resource access Yes
Spooler Queue jobs for daemon processing Yes (daemon)
Interactive/REPL User prompts, shell environment Yes

Key Insight from Unix Art

> "A daemon is designed to mediate access to some sort of shared resource — a database, or a transaction stream. Another reason for such a daemon may be to avoid performing expensive startup actions each time the program is invoked."

This is exactly our use case: database connections, embedding model loading, and Quarkus startup are expensive operations.

Container Deployment Best Practice

From Docker best practices: > "Don't daemonize a process inside a container. Run the process in foreground as PID 1. The container will exit when PID 1 exits."

This means our CLI must have a mode that runs in foreground and stays alive.

Remote Execution Pattern

From REST API remote execution research: > "SSH is OK for one-off accesses but not suited for repeated tasks/jobs and not easy to automate. REST API provides secure access via HTTPS/TLS and ability to automate using scripts."

Decision

Implement three execution modes for idempiere-cli:

1. One-Shot Mode (Default) - EXISTS

Standard CLI behavior - execute command and exit.

idempiere-cli dict registry list --pattern "C_Order%"
idempiere-cli dev doctor
idempiere-cli gen model XX_MyTable

Use cases: Local development, CI/CD pipelines, shell scripts

2. Server Mode - PARTIAL (MCP only)

Long-running HTTP server exposing CLI functionality.

# MCP Server (EXISTS)
idempiere-cli server mcp
# Listens on http://localhost:8765/mcp

# REST API Server (PROPOSED)
idempiere-cli server api
# Listens on http://localhost:8765/api
# POST /api/dict/registry/list
# POST /api/dev/doctor

Use cases: Container deployment, remote execution, n8n integration

3. Interactive Shell Mode - PROPOSED

REPL for interactive sessions.

idempiere-cli shell

idempiere> dict registry list --pattern "C_Order%"
┌────────────┬─────────────────────┬────────────┐
│ Table Name │ Description         │ Entity Type│
├────────────┼─────────────────────┼────────────┤
│ C_Order    │ Sales Order         │ D          │
│ C_OrderLine│ Sales Order Line    │ D          │
└────────────┴─────────────────────┴────────────┘

idempiere> dev doctor
✓ Java 21.0.1
✓ Maven 3.9.6
✓ Database connected

idempiere> exit

Use cases: Interactive debugging, docker exec -it, SSH sessions

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                     idempiere-cli                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐           │
│  │  One-Shot    │  │   Server     │  │    Shell     │           │
│  │  Commands    │  │   Modes      │  │    (REPL)    │           │
│  │              │  │              │  │              │           │
│  │ dict, gen,   │  │ server mcp   │  │ shell        │           │
│  │ pack, dev,   │  │ server api   │  │              │           │
│  │ env, ide     │  │              │  │              │           │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘           │
│         │                 │                 │                    │
│         └─────────────────┼─────────────────┘                    │
│                           │                                      │
│                           ▼                                      │
│              ┌────────────────────────────┐                      │
│              │   Shared Tool Logic Layer  │                      │
│              │  (org.idempiere.cli.ai.shared)                    │
│              └────────────────────────────┘                      │
│                           │                                      │
│              ┌────────────┼────────────┐                         │
│              ▼            ▼            ▼                         │
│       ┌───────────┐ ┌───────────┐ ┌───────────┐                  │
│       │ iDempiere │ │ PostgreSQL│ │   Local   │                  │
│       │  REST API │ │  Database │ │   Files   │                  │
│       └───────────┘ └───────────┘ └───────────┘                  │
└─────────────────────────────────────────────────────────────────┘

Implementation

Server API Mode

Use Quarkus JAX-RS to expose CLI tools as REST endpoints:

@Path("/api")
@ApplicationScoped
public class CliApiResource {

    @Inject
    RegistryToolLogic registryLogic;

    @POST
    @Path("/dict/registry/list")
    @Produces(MediaType.APPLICATION_JSON)
    public Response listTables(
            @QueryParam("pattern") String pattern,
            @QueryParam("limit") Integer limit) {
        ToolResult result = registryLogic.listTables(pattern, false, limit, "en_US");
        return Response.ok(result.toJson()).build();
    }
}

Command implementation:

@Command(name = "api", description = "Start REST API server")
public class ApiServerCommand implements Callable<Integer> {

    @Override
    public Integer call() {
        System.out.println("REST API Server starting...");
        System.out.println("Endpoints: http://localhost:8765/api");
        System.out.println("Press Ctrl+C to stop");

        Quarkus.waitForExit();
        return 0;
    }
}

Shell Mode

Use JLine3 for interactive REPL:

@Command(name = "shell", description = "Start interactive shell")
public class ShellCommand implements Callable<Integer> {

    @Inject
    CommandLine.IFactory factory;

    @Override
    public Integer call() {
        Terminal terminal = TerminalBuilder.builder().build();
        LineReader reader = LineReaderBuilder.builder()
            .terminal(terminal)
            .completer(new PicocliCompleter())
            .build();

        while (true) {
            String line = reader.readLine("idempiere> ");
            if ("exit".equals(line.trim())) break;

            // Parse and execute command
            String[] args = line.split("\\s+");
            new CommandLine(IdempiereCli.class, factory).execute(args);
        }
        return 0;
    }
}

Usage Matrix

Scenario Mode Command
Local development One-shot idempiere-cli dict registry list
CI/CD pipeline One-shot idempiere-cli pack out --table XX_MyTable
Docker container Server idempiere-cli server api
Kubernetes pod Server idempiere-cli server api
Remote execution Server curl -X POST http://server:8765/api/dict/registry/list
AI assistant (Claude) Server idempiere-cli server mcp
Interactive debugging Shell idempiere-cli shell
Docker exec Shell docker exec -it cli idempiere-cli shell
SSH session Shell ssh server "idempiere-cli shell"

Consequences

Positive

Negative

Neutral

AI Connection Without MCP

Key Question: Can AI connect to CLI without MCP protocol?

Answer: YES - The server api command exposes standard REST/HTTP endpoints that ANY client can use:

Method Protocol AI Client Command
MCP MCP over HTTP/SSE Claude Desktop/Code only server mcp
REST API HTTP/JSON Any AI (n8n, OpenAI agents, custom) server api
LangChain4j Local (embedded) Built-in AI agent ai ask
┌─────────────────────────────────────────────────────────────────┐
│                    AI Connection Methods                         │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. MCP (Claude-specific)                                       │
│     Claude Desktop → MCP Protocol → server mcp → Tools          │
│                                                                  │
│  2. REST API (Universal) ← For any AI                           │
│     Any AI Agent → HTTP/JSON → server api → Tools               │
│     (OpenAI, n8n, custom agents, scripts)                       │
│                                                                  │
│  3. LangChain4j (Embedded)                                      │
│     User → ai ask "question" → LLM → Tools → Response           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Example: n8n calling CLI via REST:

HTTP Request Node → http://localhost:8765/api/registry/tables

Example: Custom Python AI agent:

import requests
response = requests.get("http://localhost:8765/api/registry/tables?pattern=C_Order%")

Security Considerations

For server api mode:

  1. Authentication: Require API token via header or query param
  2. HTTPS: Support TLS for production
  3. Rate limiting: Prevent abuse
  4. Read-only by default: Dangerous operations require explicit flag

References

Path: /docs/developers/architecture/idempiere-hub/026-cli-execution-modes