Doramagic Project Pack · Human Manual

mova-flat-runner

Related topics: Core MCP Tools, Installation and Setup, System Architecture

Project Overview

Related topics: Core MCP Tools, Installation and Setup, System Architecture

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Entry Point: dist/index.js

Continue reading this section for the full explanation and source context.

Section Main Server Implementation: src/index.ts

Continue reading this section for the full explanation and source context.

Section HTTP Transport: movaRequest()

Continue reading this section for the full explanation and source context.

Related topics: Core MCP Tools, Installation and Setup, System Architecture

Project Overview

Introduction

mova-flat-runner (@leryk1981/mova-flat-runner) is an MCP (Model Context Protocol) server that provides governed AI workflows with human approval gates and audit trails. It enables AI-powered contract execution with built-in compliance controls for workflows such as invoice OCR, AML (Anti-Money Laundering) triage, credit review, and custom contract management. Sources: package.json:1-15

The project serves as the authoritative native MCP connection for the MOVA Operator platform, allowing MCP clients like Claude Desktop and Codex to interact with governed AI workflows through a standardized interface. Sources: README.md:1-30

High-Level Architecture

The mova-flat-runner implements a layered architecture that bridges MCP clients with the MOVA API backend, providing both remote execution through the MOVA cloud and local execution capabilities.

graph TD
    subgraph "MCP Clients"
        Claude["Claude Desktop"]
        Codex["Codex IDE"]
    end

    subgraph "Mova Flat Runner"
        MCPServer["MCP Server Entry Point<br/>dist/index.js"]
        ToolExecutor["Tool Executor<br/>executeTool()"]
        RemoteAPI["Remote API Transport<br/>movaRequest()"]
        LocalSeamBridge["Local Seam Bridge<br/>local_seam_bridge.ts"]
        StepModeGuard["Step Mode Guard<br/>step_mode_guard.ts"]
    end

    subgraph "MOVA Backend"
        API["MOVA API<br/>api.mova-lab.eu"]
        ContractDB["Contract Registry"]
        RunEngine["Run Engine"]
    end

    Claude --> MCPServer
    Codex --> MCPServer
    MCPServer --> ToolExecutor
    ToolExecutor --> RemoteAPI
    ToolExecutor --> LocalSeamBridge
    RemoteAPI --> API
    LocalSeamBridge --> RunEngine
    API --> ContractDB

Project Structure

DirectoryPurpose
cmd/Application entry points (publisher CLI)
internal/api/HTTP handlers and routing
internal/auth/Authentication (GitHub OAuth, JWT, namespace blocking)
internal/config/Configuration management
internal/database/Data persistence (PostgreSQL)
internal/service/Business logic layer
internal/telemetry/Metrics and monitoring
internal/validators/Input validation
pkg/Public packages
src/Main application source code

Sources: README.md:30-50

Core Source Files

Entry Point: `dist/index.js`

The binary entry point is defined in package.json:

{
  "bin": {
    "mova-mcp": "dist/index.js"
  }
}

Sources: package.json:22-24

The server is started using npx with the npm package @leryk1981/mova-flat-runner:

npx -y @leryk1981/[email protected]

Sources: README.md:15-20

Main Server Implementation: `src/index.ts`

The core implementation handles tool execution through a switch-based executeTool() function that routes requests to the appropriate handler. Sources: src/index.ts:200-300

#### Tool Categories

The server exposes the following tool categories:

Tool PrefixPurpose
mova_runExecute built-in contract workflows
mova_contractRegister and manage custom contracts
mova_queryQuery contract status and audit trails
mova_decideHandle decision points
mova_connectorExternal system integration
mova_healthHealth check operations

Sources: README.md:55-65

#### Key Tool Handlers

mova_run Handler (src/index.ts)

  • Validates contract type against built-in manifests
  • Bridges to either remote API or local execution
  • Returns structured JSON responses

mova_contract Actions:

  • register: POST /api/v1/contracts/register
  • run: POST /run/{contract_id}
  • run_status: GET /run/{run_id}/status

Sources: src/index.ts:100-200

mova_query Views:

  • status: Returns bridged run status
  • audit: Returns audit trail (backend-dependent for custom contracts)
  • audit_compact: Returns structured unavailability for custom IDs

Sources: tasks/task061.md:1-30

HTTP Transport: `movaRequest()`

All API communication uses a unified transport layer:

export const movaGet = (config, path) => movaRequest(config, "GET", path);
export const movaPut = (config, path, body) => movaRequest(config, "PUT", path, body);
export const movaDelete = (config, path) => movaRequest(config, "DELETE", path);
export const movaPost = (config, path, body) => movaRequest(config, "POST", path, body);

Sources: dist-test/src/transports/remote_api.js:80-85

Local Seam Bridge: `src/transports/local_seam_bridge.ts`

The local seam bridge enables local execution of contract steps without requiring remote API calls. It handles:

  • Execution Modes: AI_ATOMIC, HUMAN_GATE, DETERMINISTIC, CONTRACT_CALL
  • State Management: Bridge state references and terminal outcomes
  • Verification: Pass/fail verification payloads with invariant checks
const producedOutput =
  request.step.execution_mode === "AI_ATOMIC"
    ? CANONICAL_STRATEGY
    : request.stepResult ?? null;

Sources: src/transports/local_seam_bridge.ts:1-50

#### Bridge Response Structure

FieldDescription
status"completed", "advanced", or "human_gate_required"
execution_modeStep execution mode
produced_outputOutput data for AI_ATOMIC steps
gate_requiredBoolean indicating human approval needed
next_phase"EXECUTION" or "WAIT_HUMAN"
verification_payloadPass/fail verification checks

Sources: src/transports/local_seam_bridge.ts:15-30

Security: `src/security/step_mode_guard.ts`

The step mode guard validates that contract step definitions conform to their declared execution modes:

Violation TypeCondition
deterministic_with_modelDETERMINISTIC step has a model field
ai_atomic_without_modelAI_ATOMIC step missing model field
contract_call_without_contract_idCONTRACT_CALL step missing contract_id
human_gate_without_decisionsHUMAN_GATE step missing decision_options
export function findStepModeViolations(flow): Violation[] {
  // Validates each step's execution_mode against its required fields
}

export function assertStepModesValid(flow, requestId): FlatRunnerResult | null {
  // Returns error result if violations found
}

Sources: src/security/step_mode_guard.ts:1-80

Package Support: `src/package_support.ts`

Contract packages are validated for structural integrity before execution:

function validateGlobalShape(value: unknown): string | null {
  // Validates global file structure:
  // - schema_id: "contract_package_global_v1"
  // - version: required string
  // - semantic_roles: non-empty array
  // - non_authority_rules: non-empty array
}

function validatePackageManifestShape(value: unknown): string | null {
  // Validates manifest structure:
  // - Allowed keys: schema_id, package_id, version, flow_ref, etc.
  // - Required strings: package_id, version, flow_ref
}

Sources: src/package_support.ts:1-50

Configuration

Required Environment Variables

VariableDescriptionDefault
MOVA_API_URLMOVA API base URLhttps://api.mova-lab.eu
MOVA_API_KEYMOVA API authentication key
LLM_KEYOpenRouter API key for LLM analysis
LLM_MODELOpenRouter model IDopenai/gpt-4o-mini

Optional Local HTTP Mode Variables

VariableDescriptionDefault
MOVA_API_TIMEOUT_MSAPI request timeout30000
MOVA_HTTP_PORTLocal HTTP server port3796
MOVA_INVOKE_TOKENLocal invoke authentication token

Local Sandbox Variables

VariableDescriptionDefault
MOVA_SANDBOX_PACKAGE_PATHContract package locationD:\Projects_MOVA\mova-intent\contracts\dockerfile-nodejs-v1
MOVA_SANDBOX_PROJECT_PATHProject directory(required)
MOVA_SANDBOX_STATE_FILEState persistence fileSystem temp directory

Sources: README.md:35-60

MCP Server Configuration

Claude Desktop Example

{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/[email protected]"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}

Codex Config Example (TOML)

[mcp_servers.mova]
command = "npx"
args = ["-y", "@leryk1981/[email protected]"]
startup_timeout_sec = 90.0

[mcp_servers.mova.env]
MOVA_API_URL = "https://api.mova-lab.eu"
MOVA_API_KEY = "__SET_MOVA_API_KEY__"
LLM_KEY = "__SET_LLM_KEY__"
LLM_MODEL = "openai/gpt-4o-mini"

Sources: README.md:60-100

Build System

Available Make Targets

TargetPurpose
make publisherBuild the MCP publisher CLI
make checkRun lint, unit tests, and integration tests
make helpDisplay all available commands

Sources: README.md:10-20

Build Scripts (package.json)

ScriptDescription
npm run buildCompile TypeScript and add shebang
npm run test:buildValidate built output
npm run smoke:custom-bridgeSmoke test for custom contract bridge

Sources: package.json:25-30

Server Registry Configuration

The server.json file defines the MCP server schema for the Model Context Protocol registry:

{
  "name": "io.github.mova-compact/mova-flat-runner",
  "version": "2.0.6",
  "description": "Governed AI workflows with human approval gates and audit trails"
}

Sources: server.json:1-15

Workflow Execution Modes

The system supports four execution modes for contract steps:

graph LR
    subgraph "Execution Modes"
        AI["AI_ATOMIC<br/>LLM-driven step"]
        DET["DETERMINISTIC<br/>Local JS validation"]
        CALL["CONTRACT_CALL<br/>Cross-contract invocation"]
        GATE["HUMAN_GATE<br/>Human approval required"]
    end

    AI --> Output1["Produces structured output"]
    DET --> Output2["Validation result"]
    CALL --> Output3["Sub-contract result"]
    GATE --> Output4["Awaiting decision"]
ModeModel FieldAdditional FieldsUse Case
AI_ATOMICRequiredOutput schema pathLLM analysis and decision-making
DETERMINISTICForbiddenLocal validation logic
CONTRACT_CALLOptionalcontract_idInvoke another contract
HUMAN_GATEOptionaldecision_optionsManual approval checkpoints

Sources: src/security/step_mode_guard.ts:30-70

Custom Contract Bridge

The mova-flat-runner implements a Custom Query Bridge to handle custom contract visibility across different API namespaces:

sequenceDiagram
    participant Client as MCP Client
    participant MFR as mova-flat-runner
    participant Bridge as Custom Run Bridge
    participant API as MOVA API

    Note over Client,MFR: Contract Registration & Run
    Client->>MFR: mova_contract register
    MFR->>API: POST /api/v1/contracts/register
    API-->>MFR: contract_id
    MFR->>MFR: rememberCustomRun(contract_id, run_id)

    Note over Client,MFR: Custom Query Flow
    Client->>MFR: mova_query (404 from API)
    MFR->>Bridge: Check CUSTOM_RUN_BRIDGE
    Bridge-->>MFR: run_id mapping exists
    MFR->>API: GET /api/v1/contracts/my
    API-->>MFR: contract metadata
    MFR-->>Client: Bridged status response

Bridge Behavior:

  • On mova_contract run and run_status: captures contract_id -> run_id mapping
  • On mova_query status (404): probes /api/v1/contracts/my and returns bridged status
  • On mova_query audit (404): returns honest AUDIT_UNAVAILABLE envelope

Sources: tasks/task061.md:1-60

Security Considerations

API Key Management

Security Note: Do not commit real MOVA_API_KEY, LLM_KEY, or MOVA_INVOKE_TOKEN. Sources: README.md:70-75

Step Mode Validation

The step mode guard enforces strict validation before execution:

  • Prevents DETERMINISTIC steps from using LLM models
  • Ensures AI_ATOMIC steps have model configuration
  • Validates HUMAN_GATE steps have decision options

Human Gate Protection

const gateGuard = await assertNotHumanGate(config, run_id, step_id, requestId);
if (gateGuard) return gateGuard;

Human gate steps cannot be completed via generic step_complete — they require the dedicated gate_approve / gate_reject path. Sources: src/index.ts:200-250

References

  • Repository: https://github.com/mova-compact/mova-flat-runner
  • npm Package: @leryk1981/mova-flat-runner
  • Current Version: 3.3.4 Sources: package.json:3
  • MCP Registry: https://registry.modelcontextprotocol.io
  • API Health Check: https://api.mova-lab.eu/health

Sources: README.md:30-50

Core MCP Tools

Related topics: Project Overview, Transport Layer, System Architecture

Section Related Pages

Continue reading this section for the full explanation and source context.

Section movarun

Continue reading this section for the full explanation and source context.

Section movacontract

Continue reading this section for the full explanation and source context.

Section movaquery

Continue reading this section for the full explanation and source context.

Related topics: Project Overview, Transport Layer, System Architecture

Core MCP Tools

Overview

The MOVA Flat Runner is an MCP (Model Context Protocol) server that provides governed AI workflow execution for Claude and MCP-compatible clients. The Core MCP Tools are the primary interface through which clients interact with the system to execute business workflows such as invoice OCR, AML triage, credit review, and custom contracts.

The tool executor is implemented in executeTool() within src/index.ts and handles all incoming tool requests via the MCP protocol. Each tool maps to a specific workflow action or API operation, with built-in validation, error handling, and security guards.

Sources: src/index.ts:1-50

Architecture Overview

graph TD
    subgraph "MCP Client"
        A[Claude / Cursor / Codex]
    end
    
    subgraph "MOVA Flat Runner"
        B[executeTool dispatcher]
        C[mova_run]
        D[mova_contract]
        E[mova_query]
        F[mova_health]
        G[mova_decide]
        H[mova_connector]
        I[mova_calibrate_intent]
        J[step_complete / gate_approve / gate_reject]
    end
    
    subgraph "Backend Services"
        K[MOVA API]
        L[OpenRouter LLM]
        M[Local Sandbox]
    end
    
    A --> B
    B --> C
    B --> D
    B --> E
    B --> F
    B --> G
    B --> H
    B --> I
    B --> J
    
    C --> K
    D --> K
    E --> K
    C --> L
    I --> L
    J --> K
    I --> M
    
    style B fill:#f9f,stroke:#333

Available Tools Summary

ToolPurposePrimary API Endpoint
mova_runExecute built-in contract workflowsPOST /api/v1/contracts/{id}/run
mova_contractRegister and run custom contractsPOST /api/v1/contracts/register
mova_queryQuery contract status, audit, and audit_compactGET /api/v1/contracts/{id}
mova_healthHealth check for MOVA APIGET /health
mova_decideSubmit human decision at approval gatePOST /run/{run_id}/decision
mova_connectorConnect to external data sourcesInternal service routing
mova_calibrate_intentConvert natural language to contractLLM + local sandbox
step_completeMark a step as completePOST /run/{run_id}/step/{step_id}/complete
gate_approveApprove human gatePOST /run/{run_id}/gate/approve
gate_rejectReject human gatePOST /run/{run_id}/gate/reject
run_statusCheck run statusGET /run/{run_id}/status

Sources: src/index.ts:180-220

Tool Execution Flow

sequenceDiagram
    participant Client
    participant MCP as MOVA Flat Runner
    participant API as MOVA Backend API
    participant LLM as OpenRouter LLM
    participant Sandbox as Local Sandbox

    Client->>MCP: executeTool(name, args)
    MCP->>MCP: validateArgs(args)
    
    alt Built-in Contract
        MCP->>MCP: resolveManifest(contract_type)
        MCP->>API: movaRunStepsRemote()
        API-->>MCP: step results
    end
    
    alt Custom Contract
        MCP->>API: mova_contract register/run
        API-->>MCP: run_id
        MCP->>MCP: rememberCustomRun()
    end
    
    alt Human Gate Required
        MCP-->>Client: {status: "waiting_human", options: [...]}
        Client->>MCP: mova_decide / gate_approve / gate_reject
        MCP->>API: POST /run/{run_id}/decision
    end
    
    alt AI Atomic Step
        MCP->>LLM: analyze(inputs)
        LLM-->>MCP: analysis result
    end
    
    MCP-->>Client: final result

Core Tools Detail

mova_run

Executes built-in contract workflows using predefined manifests. Built-in contracts include invoice OCR, AML triage, credit review, and supplier screening.

Parameters:

ParameterTypeRequiredDescription
contract_typestringYesThe type of contract to execute (e.g., "invoice_ocr", "aml_triage")
inputsobjectYesInitial inputs for the contract workflow

Workflow:

  1. Resolves the contract manifest from CONTRACT_MANIFESTS
  2. Initializes the contract via POST /api/v1/contracts/{id}/run
  3. Executes steps remotely via movaRunStepsRemote()
  4. Returns structured response with status and outputs

Security Validation:

The step_mode_guard.ts validates that:

  • AI_ATOMIC steps have a model field defined
  • DETERMINISTIC steps do NOT have a model field
  • CONTRACT_CALL steps have a contract_id field
  • HUMAN_GATE steps have decision_options array

Sources: src/index.ts:200-280

mova_contract

Handles custom contract registration and execution. Custom contracts can be created via natural language using mova_calibrate_intent or by registering existing contract definitions.

Actions:

ActionDescriptionAPI Endpoint
registerRegister a new contract definitionPOST /api/v1/contracts/register
runExecute a custom contractPOST /run/{contract_id}
run_statusCheck custom contract run statusGET /run/{run_id}/status

Custom Run Bridge:

When a custom contract is run, the system captures and persists a contract_id -> run_id mapping in CUSTOM_RUN_BRIDGE:

const ref = { run_id: runId, updated_at: new Date().toISOString() };
if (typeof sourceUrl === "string" && sourceUrl.length > 0)
    ref.source_url = sourceUrl;
CUSTOM_RUN_BRIDGE.set(contractId, ref);

This bridge enables mova_query to return status for custom contracts that may not exist in the standard contracts API namespace.

Sources: src/index.ts:280-350

mova_query

Queries contract status, audit records, and compact audit information. This tool provides visibility into contract execution state and historical audit trails.

Views:

ViewDescriptionResponse Format
statusCurrent contract/run statusBridged status with bridge_mode=custom_contract_run_namespace_bridge_v1 for custom contracts
auditFull audit recordReturns AUDIT_UNAVAILABLE envelope when backend namespace is absent
audit_compactCompact audit summaryStructured unavailable object with ok=false, status=424

On 404 Response:

When the API returns 404, mova_query probes /api/v1/contracts/my to check if the contract exists in the user's contract list. If found via CUSTOM_RUN_BRIDGE, it returns bridged status rather than a 404 error.

Sources: src/index.ts:350-420

mova_health

Performs a health check on the MOVA API backend.

Endpoint: GET /health

Response: Returns health status of the MOVA API service.

Sources: server.json:1-50

mova_decide

Submits human decision at an approval gate. This is the primary mechanism for human-in-the-loop (HITL) interactions.

Parameters:

ParameterTypeRequiredDescription
run_idstringYesThe run identifier
option_idstringYesThe selected decision option

Flow:

  1. Fetches decision point from GET /api/v1/contracts/{contractId}/decision
  2. Presents question and options to the human
  3. Human selects an option_id
  4. Submits via POST /run/{run_id}/decision

Sources: src/transports/remote_api.ts:1-80

mova_calibrate_intent

Converts natural language descriptions into structured contract definitions using LLM processing and local sandbox validation.

Parameters:

ParameterTypeRequiredDescription
descriptionstringYesNatural language description of the business process
package_pathstringNoPath to contract package for sandbox execution
project_pathstringNoPath to project files

Process:

  1. Sends description to OpenRouter LLM for structured extraction
  2. Validates the generated contract in local sandbox via resolveLocalSeamLocator()
  3. Returns validated contract definition ready for registration

Sources: src/transports/local_seam_bridge.ts:1-100

step_complete, gate_approve, gate_reject

Management tools for contract execution state.

step_complete:

ParameterTypeRequiredDescription
run_idstringYesThe run identifier
step_idstringYesThe step to complete
outcomestringNoOutcome identifier (default: "default")
outputobjectNoStep output data

Security Guard: assertNotHumanGate() prevents generic step_complete from being used on HUMAN_GATE steps. Human gates must use the dedicated gate_approve / gate_reject paths.

gate_approve:

ParameterTypeRequiredDescription
run_idstringYesThe run identifier
step_idstringYesThe gate step
notesstringNoApproval notes

gate_reject:

ParameterTypeRequiredDescription
run_idstringYesThe run identifier
step_idstringYesThe gate step
reasonstringNoRejection reason

Sources: src/index.ts:420-500

Step Execution Modes

The MOVA system supports multiple step execution modes for different workflow requirements:

ModeDescriptionRequired Fields
AI_ATOMICSingle LLM call that must complete atomicallymodel, prompt
DETERMINISTICLocal JavaScript evaluation, no LLMNo model field
HUMAN_GATERequires human decision before proceedingdecision_options
CONTRACT_CALLCalls another contractcontract_id

Sources: src/security/step_mode_guard.ts:1-80

Local Seam Bridge

For workflows requiring local execution, the local_seam_bridge.ts provides an interface to sandboxed contract execution:

async function resolveLocalSeamLocator(initialInputs) {
    const packagePath = process.env.MOVA_SANDBOX_PACKAGE_PATH 
        ?? "D:\\Projects_MOVA\\mova-intent\\contracts\\dockerfile-nodejs-v1";
    const projectPath = process.env.MOVA_SANDBOX_PROJECT_PATH ?? "";
    // ...
}

The bridge produces standardized output conforming to the canonical strategy pattern:

return {
    ok: true,
    bridge: {
        ok: true,
        bridge_source: "mova_flat_runner_canonical_bridge",
        status: "completed" | "advanced" | "human_gate_required",
        execution_mode: request.step.execution_mode,
        next_phase: { phase: status === "human_gate_required" ? "WAIT_HUMAN" : "EXECUTION" },
        verification_payload: { status: "PASS", checks: [...] }
    }
};

Sources: src/transports/local_seam_bridge.ts:100-180

Remote API Transport

The remote API transport (remote_api.ts) handles communication with the MOVA backend:

export const movaGet = (config, path) => movaRequest(config, "GET", path);
export const movaPut = (config, path, body) => movaRequest(config, "PUT", path, body);
export const movaDelete = (config, path) => movaRequest(config, "DELETE", path);

Key endpoints:

EndpointMethodPurpose
/api/v1/contracts/{contractId}/stepPOSTExecute a step
/api/v1/contracts/{contractId}/steps/{stepId}/outputGETGet step output
/api/v1/contracts/{contractId}/decisionGETGet decision point
/run/{runId}/decisionPOSTSubmit decision
/run/{runId}/statusGETGet run status

Sources: src/transports/remote_api.ts:80-150

Error Handling

All tools return structured error responses using flatErr():

function flatErr(
    code: string,
    message: string,
    context?: Record<string, unknown>,
    retryable?: boolean,
    requestId?: string
): FlatRunnerResult

Error Codes:

CodeDescription
UNKNOWN_CONTRACT_TYPEContract type not found in manifests
API_REQUEST_FAILEDBackend API request failed
STEP_MODE_FIELD_MISMATCHStep execution mode doesn't match required fields
LOCAL_VALIDATION_FAILEDLocal input validation failed

Sources: src/index.ts:500-560

Configuration

Required Environment Variables

VariableDescriptionDefault
MOVA_API_KEYMOVA API authentication keyRequired
LLM_KEYOpenRouter API key for LLM stepsRequired
LLM_MODELOpenRouter model IDopenai/gpt-4o-mini
MOVA_API_URLOverride API base URLhttps://api.mova-lab.eu

Optional Local HTTP Mode Variables

VariableDescriptionDefault
MOVA_API_TIMEOUT_MSAPI request timeout30000
MOVA_HTTP_PORTLocal HTTP server port3796
MOVA_INVOKE_TOKENLocal invoke authenticationRequired for HTTP mode

Sources: server.json:1-50

Resource Schema

The MCP server exposes resources via mova:// URI scheme:

URIDescription
mova://registryLists all available contract manifests
mova://schemas/envelopesEnvelope schema definitions
mova://contracts/{type}/manifestIndividual contract manifest

Sources: src/schemas.ts:1-50

Security Considerations

Step Mode Validation

The step_mode_guard.ts enforces strict validation on step execution modes:

  • Prevents AI_ATOMIC steps without a defined model
  • Prevents DETERMINISTIC steps from declaring a model
  • Prevents HUMAN_GATE steps without decision options
  • Prevents CONTRACT_CALL steps without a contract_id

Human Gate Protection

The assertNotHumanGate() guard prevents generic step completion from bypassing human approval requirements:

// SECURITY (CFV-3): HUMAN_GATE cannot be completed by generic step completion.
// Human confirmation requires the dedicated gate path (gate_approve / gate_reject).

Output Sanitization

The sanitizePublicShape() function filters internal bridge metadata from public responses, excluding fields like bridge_anchors, last_terminal_bridge, trace, outputs, and context.

Sources: src/transports/local_seam_bridge.ts:60-90

Quick Reference

Execute Built-in Contract

tool: mova_run
args: { contract_type: "invoice_ocr", inputs: { document: "..." } }

Register and Run Custom Contract

tool: mova_contract
args: { action: "register", contract_def: {...} }

tool: mova_contract
args: { action: "run", contract_id: "custom-123", inputs: {...} }

Query Contract Status

tool: mova_query
args: { contract_id: "ctr-abc", view: "status" }

Handle Human Approval

tool: mova_decide
args: { run_id: "run-xyz", option_id: "approve" }

Sources: src/index.ts:1-50

Installation and Setup

Related topics: Project Overview, Deployment and Operations

Section Related Pages

Continue reading this section for the full explanation and source context.

Section System Requirements

Continue reading this section for the full explanation and source context.

Section Required API Keys

Continue reading this section for the full explanation and source context.

Section Method 1: Direct npx Execution

Continue reading this section for the full explanation and source context.

Related topics: Project Overview, Deployment and Operations

Installation and Setup

Overview

The mova-flat-runner is an MCP (Model Context Protocol) server that provides governed AI workflow execution with human approval gates and audit trails. It enables clients like Claude Desktop, Cursor, and Codex to interact with MOVA's contract execution engine for invoice OCR, AML triage, credit review, supplier screening, and custom contract workflows.

Sources: README.md:1-20

Package Information

PropertyValue
npm Package@leryk1981/mova-flat-runner
Package Version3.3.4
MCP Server Nameio.github.mova-compact/mova-flat-runner
Server Registry Version2.0.6
Binarymova-mcp
LicenseMIT-0
RuntimeNode.js (ES Module)

Sources: package.json:2-12, server.json:2-8

Prerequisites

System Requirements

  • Node.js: Required for running the MCP server
  • npm or npx: For package installation and execution
  • Network access to api.mova-lab.eu (or custom MOVA API endpoint)

Required API Keys

Before setup, obtain the following credentials:

Environment VariableRequiredDescription
MOVA_API_KEYYesMOVA API key for authentication. Use test-key-001 for initial testing without registration.
LLM_KEYYesOpenRouter API key (sk-or-v1-...) for LLM analysis steps
LLM_MODELNoOpenRouter model ID (default: openai/gpt-4o-mini)

Sources: smithery.yaml:35-45, server.json:15-28, README.md:35-42

Installation Methods

Method 1: Direct npx Execution

The simplest installation method uses npx to run the package directly without global installation:

npx -y @leryk1981/[email protected]

This downloads and executes the specified version on-demand.

Sources: README.md:19-20

Method 2: Global npm Installation

Install the package globally for persistent access:

npm install -g @leryk1981/mova-flat-runner

After installation, the mova-mcp command becomes available system-wide.

Sources: package.json:13-14

Method 3: Building from Source

For development or custom modifications:

# Clone the repository
git clone https://github.com/mova-compact/mova-flat-runner.git
cd mova-flat-runner

# Install dependencies
npm install

# Build the TypeScript source
npm run build

The build process compiles TypeScript and adds the shebang header to the output binary:

#!/usr/bin/env node

Sources: package.json:16-21

Client Configuration

Claude Desktop Configuration

Add the MOVA MCP server to your Claude Desktop configuration file:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/[email protected]"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}

Sources: README.md:57-70

Codex Configuration

For Codex (Cursor) integration, add to your configuration:

[mcp_servers.mova]
command = "npx"
args = ["-y", "@leryk1981/[email protected]"]
startup_timeout_sec = 90.0

[mcp_servers.mova.env]
MOVA_API_URL = "https://api.mova-lab.eu"
MOVA_API_KEY = "__SET_MOVA_API_KEY__"
LLM_KEY = "__SET_LLM_KEY__"
LLM_MODEL = "openai/gpt-4o-mini"

Sources: README.md:81-93

Environment Variables Reference

Core Configuration

VariableRequiredDefaultDescription
MOVA_API_URLNohttps://api.mova-lab.euBase URL for MOVA API
MOVA_API_KEYYesAPI authentication key
LLM_KEYYesOpenRouter API key
LLM_MODELNoopenai/gpt-4o-miniLLM model identifier

Sources: README.md:35-48, server.json:19-32

Local HTTP Mode (Optional)

For local execution mode without external API calls:

VariableRequiredDefaultDescription
MOVA_API_TIMEOUT_MSNo30000HTTP request timeout in milliseconds
MOVA_HTTP_PORTNo3796Local HTTP server port
MOVA_INVOKE_TOKENConditionalSecurity token for local invocation

Sources: README.md:44-48

Local Seam Sandbox (Advanced)

For local seam bridge execution:

VariableRequiredDefaultDescription
MOVA_SANDBOX_PACKAGE_PATHNo(project-specific)Path to sandbox package
MOVA_SANDBOX_PROJECT_PATHNoPath to project directory
MOVA_SANDBOX_STATE_FILENo(temp file)Path to state file

Sources: src/transports/local_seam_bridge.ts:1-20

Health Check

Verify the MOVA API connectivity before running workflows:

curl -sS https://api.mova-lab.eu/health

Sources: README.md:52-53

Available MCP Tools

Once installed, the following tools are available:

ToolDescription
mova_healthCheck API connectivity
mova_registryList available contract types
mova_runExecute built-in contract workflows
mova_queryQuery contract status, audit, or audit_compact
mova_decideSubmit human decisions at approval gates
mova_connectorConnect external data sources
mova_contractRegister and run custom contracts

Sources: README.md:54-55

Build and Test Commands

For development, the following commands are available:

# Run lint, unit tests, and integration tests
make check

# View all available make targets
make help

# Build the publisher CLI
make publisher

# Run smoke tests
npm run smoke:custom-bridge

Sources: README.md:22-29

Security Notes

Important: Never commit real API keys to version control.
  • Replace placeholders (__SET_MOVA_API_KEY__, __SET_LLM_KEY__, __SET_MOVA_INVOKE_TOKEN__) with actual credentials only in local configuration files
  • The MCP registry provides server listings but does not transmit secrets
  • Local invocation tokens should be used for secure local HTTP mode

Sources: README.md:49-51, README.md:56-57

Architecture Overview

graph TD
    A[Claude Desktop / Codex] -->|MCP Protocol| B[mova-mcp Server]
    B --> C{Execution Mode}
    C -->|Remote API| D[MOVA API]
    C -->|Local HTTP| E[Local Seam Bridge]
    D --> F[Contract Execution Engine]
    E --> G[Local Sandbox]
    F --> H[(PostgreSQL)]
    G --> H
    D --> I[OpenRouter LLM]
    E --> I

Troubleshooting

Common Issues

IssueSolution
404 contract not foundUse mova_contract with action=run for custom contracts instead of mova_run
LLM analysis failsVerify LLM_KEY is set to a valid OpenRouter key
Connection timeoutIncrease MOVA_API_TIMEOUT_MS or check network connectivity
Human gate not advancingUse mova_decide tool to submit decisions at approval gates

Sources: tasks/task061.md:1-30, src/index.ts:150-180

Next Steps

After installation:

  1. Verify connectivity with mova_health
  2. Explore available contracts using mova_registry
  3. Run a built-in contract: mova_run complaint
  4. Create custom contracts with mova_contract register

Sources: README.md:1-20

System Architecture

Related topics: Project Overview, Security Guards, Transport Layer

Section Related Pages

Continue reading this section for the full explanation and source context.

Section MCP Server Entry Point

Continue reading this section for the full explanation and source context.

Section Tool Executor (executeTool)

Continue reading this section for the full explanation and source context.

Section Contract Manifests (CONTRACTMANIFESTS)

Continue reading this section for the full explanation and source context.

Related topics: Project Overview, Security Guards, Transport Layer

System Architecture

Overview

The mova-flat-runner is a Model Context Protocol (MCP) server that provides governed AI workflow execution with human approval gates and audit trails. It acts as an intermediary layer between MCP clients (such as Claude Desktop, Codex, or other compatible tools) and the MOVA backend API.

Sources: README.md

The architecture follows a transport-agnostic design that supports both local seam execution and remote API integration, enabling flexible deployment scenarios from local development to production environments.

High-Level Architecture

graph TD
    subgraph "MCP Client Layer"
        A[Claude Desktop] --> B[MCP Protocol]
        C[Codex] --> B
        D[Other MCP Clients] --> B
    end

    subgraph "mova-flat-runner"
        E[MCP Server Entry Point<br/>dist/index.js] --> F[Tool Executor<br/>executeTool]
        F --> G[Built-in Contracts<br/>CONTRACT_MANIFESTS]
        F --> H[Custom Contract Bridge<br/>CUSTOM_RUN_BRIDGE]
        F --> I[Security Layer<br/>step_mode_guard]
    end

    subgraph "Transport Layer"
        J[Local Seam Bridge<br/>local_seam_bridge.ts] 
        K[Remote API Transport<br/>remote_api.ts]
    end

    subgraph "External Services"
        L[MOVA API<br/>api.mova-lab.eu]
        M[OpenRouter LLM<br/>gpt-4o-mini]
    end

    E --> J
    E --> K
    J --> M
    K --> L
    L --> M

Core Components

MCP Server Entry Point

The MCP server is implemented as a stdio-based transport server that conforms to the Model Context Protocol specification. The server binary is located at dist/index.js and is invoked via the mova-mcp command.

Sources: package.json

Tool Executor (`executeTool`)

The central dispatch mechanism routes incoming tool requests to appropriate handlers based on the tool name. The executor supports the following tool categories:

Tool CategoryToolsPurpose
Workflow Executionmova_run, mova_contractLaunch contract execution
Query & Statusmova_query, run_statusRetrieve status and audit
Human Gatesgate_approve, gate_rejectHuman decision handling
Systemmova_health, mova_registryHealth checks and registry

Sources: src/index.ts:168-450

The executor follows a request-response pattern:

async function executeTool(name: string, args: Args): Promise<string> {
  const requestId = shortId();
  switch (name) {
    case "mova_run": {
      const contractType = args.contract_type as string;
      const manifest = CONTRACT_MANIFESTS[contractType];
      // ... dispatch logic
    }
    // ... other cases
  }
}

Contract Manifests (`CONTRACT_MANIFESTS`)

Built-in contracts are registered as manifests containing metadata such as:

  • contract_type: Unique identifier
  • version: Contract version
  • execution_mode: Runtime behavior (AI_ATOMIC, DETERMINISTIC, HUMAN_GATE, CONTRACT_CALL)
  • dataspec: Input field definitions
  • decision_options: Available choices for HUMAN_GATE steps

Sources: src/index.ts:450-550

Transport Layer Architecture

Remote API Transport

The remote transport handles communication with the MOVA backend API at https://api.mova-lab.eu. It provides HTTP methods for all contract operations.

export const movaGet  = (config, path) => movaRequest(config, "GET", path);
export const movaPost = (config, path, body) => movaRequest(config, "POST", path, body);
export const movaPut  = (config, path, body) => movaRequest(config, "PUT", path, body);
export const movaDelete = (config, path) => movaRequest(config, "DELETE", path);

Sources: src/transports/remote_api.ts

The transport layer performs step execution through a sequential pipeline:

graph LR
    A[Start Contract] --> B[analyze step]
    B --> C[verify step]
    C --> D[decide step]
    D --> E[Execute Validators]
    E --> F[Return Result]

Local Seam Bridge

The local seam bridge enables execution of contracts in a local sandbox environment without requiring remote API connectivity. It constructs a canonical bridge response format.

Key features include:

  • Local project path resolution from environment variables
  • State file management for persistent execution state
  • Sanitization of public shape to filter sensitive fields
function sanitizePublicShape(value: unknown): boolean {
  const excludedKeys = ["bridge_anchors", "last_terminal_bridge", 
    "terminal_commit_count", "_state15_bridge", "trace", "outputs", "context"];
  // ... sanitization logic
}

Sources: src/transports/local_seam_bridge.ts:80-95

Security Architecture

Step Mode Guard (`step_mode_guard.ts`)

The security layer validates that step execution modes are consistent with their declared content fields. This prevents misconfiguration that could lead to security issues.

Execution ModeRequired FieldViolation If Missing
AI_ATOMICmodelLLM step without model configuration
DETERMINISTIC- (no model allowed)Model field present in deterministic step
CONTRACT_CALLcontract_idCall to undefined contract
HUMAN_GATEdecision_optionsGate without user choices

Sources: src/security/step_mode_guard.ts

The validation function assertStepModesValid returns a structured error when violations are detected:

export function assertStepModesValid(flow: unknown, requestId: string): FlatRunnerResult | null {
  const violations = findStepModeViolations(flow);
  if (violations.length === 0) return null;
  return flatErr(ERR.STEP_MODE_FIELD_MISMATCH, message, { violations, http_status_equivalent: 400 });
}

Human Gate Protection

The step_complete tool includes a guard that prevents completion of HUMAN_GATE steps through the generic completion path, enforcing the dedicated gate approval/rejection flow.

Sources: src/index.ts:220-235

Custom Contract Bridge

A specialized bridge mechanism handles custom contract execution when the contract ID may not exist in the standard query namespace. This resolves the "custom contract audit identity gap."

graph TD
    A[mova_contract register] --> B[POST /api/v1/contracts/register]
    B --> C[mova_contract run]
    C --> D[POST /run/{contract_id}]
    D --> E[rememberCustomRun<br/>Map<contractId, runRef>]
    E --> F[run_status]
    F --> G[GET /run/{run_id}/status]
    G --> H[CUSTOM_RUN_BRIDGE<br/>In-Memory Map]
    H --> I[mova_query on 404]
    I --> J{Contract in Bridge?}
    J -->|Yes| K[Return bridged status]
    J -->|No| L[Return 404]

The bridge uses an in-memory CUSTOM_RUN_BRIDGE Map to track the relationship between custom contract IDs and their corresponding run IDs.

Sources: src/index.ts:100-140

Configuration

Environment Variables

VariableRequiredDefaultPurpose
MOVA_API_URLYeshttps://api.mova-lab.euBackend API endpoint
MOVA_API_KEYYes-Authentication key
LLM_KEYYes-OpenRouter API key
LLM_MODELNoopenai/gpt-4o-miniLLM model identifier
MOVA_API_TIMEOUT_MSNo30000HTTP request timeout
MOVA_HTTP_PORTNo3796Local HTTP mode port
MOVA_INVOKE_TOKENNo-Local invoke authentication

Sources: README.md

Configuration Object (`MovaConfig`)

The runtime configuration aggregates environment variables and validates required fields:

interface MovaConfig {
  apiUrl: string;
  apiKey: string;
  llmKey: string;
  llmModel: string;
  invokeToken?: string;
}

Data Flow

Contract Execution Flow

sequenceDiagram
    participant Client as MCP Client
    participant Server as mova-flat-runner
    participant API as MOVA API
    participant LLM as OpenRouter

    Client->>Server: mova_run(contract_type, inputs)
    Server->>API: POST /run/{contract_id}
    API->>LLM: Analysis request
    LLM-->>API: Analysis result
    API-->>Server: Step result
    alt AI_ATOMIC step
        Server->>API: GET /steps/{step_id}/output
        API-->>Server: Validated output
    end
    alt HUMAN_GATE step
        Server-->>Client: waiting_human status
        Client->>Server: gate_approve/gate_reject
        Server->>API: POST /decision
    end
    Server-->>Client: Final result

Local Seam Execution Flow

For local execution without remote API:

  1. Resolve package_path and project_path from inputs or environment
  2. Execute steps in the local sandbox environment
  3. Apply output sanitization to remove internal fields
  4. Construct canonical bridge response with verification payload

Sources: src/transports/local_seam_bridge.ts:95-150

Package Support and Validation

The package_support.ts module handles contract package validation including:

  • Global file structure validation
  • Semantic roles verification
  • Non-authority rules checking
  • Schema path validation

Sources: src/package_support.ts

Build and Deployment

The project uses TypeScript with a custom build script that:

  1. Compiles TypeScript to JavaScript
  2. Injects a shebang for Unix execution
  3. Sets executable permissions on the output binary
npm run build

The server is published as an npm package @leryk1981/mova-flat-runner version 3.3.4 and can be invoked via npx mova-mcp.

Sources: package.json

Sources: README.md

Security Guards

Related topics: System Architecture, Domain Validators

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Core Principles

Continue reading this section for the full explanation and source context.

Section Guard Registry

Continue reading this section for the full explanation and source context.

Section Purpose

Continue reading this section for the full explanation and source context.

Related topics: System Architecture, Domain Validators

Security Guards

Overview

Security Guards are a defensive layer in the mova-flat-runner that enforce strict validation rules on MOVA flows before they enter execution. They operate on the principle of deny-by-default: any flow containing unknown or potentially dangerous fields is rejected at registration time, preventing malicious or malformed data from persisting into runtime state.

The guard system addresses specific vulnerability categories identified through security audits (CFV-9, CFV-10, and others), ensuring that privilege-elevation hints, inline class definitions, and execution mode mismatches cannot be smuggled into the system. Sources: src/security/flow_schema_guard.ts:1-15

graph TD
    A[Flow Registration] --> B[Security Guards]
    B --> C{flow_schema_guard<br/>CFV-9}
    B --> D{class_definition_guard<br/>CFV-10}
    B --> E{step_mode_guard}
    B --> F{gate_guard}
    B --> G{graph_guard}
    B --> H{determinism_guard}
    B --> I{system_contract_guard}
    C --> J{All Guards Pass?}
    D --> J
    E --> J
    F --> J
    G --> J
    H --> J
    I --> J
    J -->|Yes| K[Flow Accepted]
    J -->|No| L[Rejection with<br/>FlatRunnerResult]

Guard Architecture

Core Principles

PrincipleDescription
Strict-by-defaultAny unknown field causes rejection
Side-effect free at importGuards can be imported without triggering validation
CompositableMultiple guards run in sequence, each checking a specific concern
Audit trailRejections return structured error payloads with remediation hints

Guard Registry

Each guard exports two key functions:

  1. Finder function — Scans the flow and returns a list of violations (returns string[] or violation objects)
  2. Assert function — Calls the finder, and if violations exist, returns a FlatRunnerResult error; otherwise returns null
// Standard guard interface pattern
export function findXXXViolations(flow: unknown): Violation[] // Finder
export function assertXXXValid(flow: unknown, requestId: string): FlatRunnerResult | null // Assert

Flow Schema Guard (CFV-9)

File: src/security/flow_schema_guard.ts

Purpose

The flow schema guard enforces a strict allow-list of top-level keys in MOVA flow definitions. During a security audit, flows containing __admin_override, __privilege_grant, and __debug_mode were accepted at registration. If these fields were persisted—even silently—an attacker could smuggle privilege-elevation hints into the runtime for future code paths to read. Sources: src/security/flow_schema_guard.ts:1-20

Allowed Top-Level Keys

KeyPurpose
versionFlow schema version
descriptionHuman-readable description
entryEntry point step reference
stepsStep definitions map
parallel_stepsParallel step definitions
notesDeveloper notes
audit_modeAudit configuration
audit_mode_noteAudit mode explanation
class_definition_refReference to registry class
CONTRACT_CALL_instructionsContract invocation instructions
input_schemaInput validation schema
output_schemaOutput validation schema
metadataAdditional metadata

Implementation

export const ALLOWED_FLOW_TOP_LEVEL_KEYS: ReadonlySet<string> = new Set([
    "version",
    "description",
    "entry",
    "steps",
    "parallel_steps",
    "notes",
    "audit_mode",
    "audit_mode_note",
    "class_definition_ref",
    "CONTRACT_CALL_instructions",
    "input_schema",
    "output_schema",
    "metadata",
]);

export function findUnknownFlowFields(flow: unknown): string[] {
    if (!flow || typeof flow !== "object" || Array.isArray(flow)) return [];
    const f = flow as Record<string, unknown>;
    const out: string[] = [];
    for (const key of Object.keys(f)) {
        if (!ALLOWED_FLOW_TOP_LEVEL_KEYS.has(key))
            out.push(key);
    }
    return out;
}

Rejection Response

When unknown fields are detected:

{
    "ok": false,
    "code": "FLOW_SCHEMA_VIOLATION",
    "message": "Flow body contains unknown top-level key(s): __admin_override, __debug_mode",
    "details": {
        "unknown_fields": ["__admin_override", "__debug_mode"],
        "http_status_equivalent": 400
    }
}

Class Definition Guard (CFV-10)

File: src/security/class_definition_guard.ts

Purpose

The class definition guard prevents inline class definitions within flow bodies. Class definitions define severity bands and noise control parameters that must be resolved from the registry by class_id. Embedding them inline creates a vector for policy tampering and bypasses the centralized authority on these security-sensitive values. Sources: src/security/class_definition_guard.ts:1-35

Forbidden Keys

const FORBIDDEN_FLOW_KEYS: readonly string[] = Object.freeze([
    "class_definition",
    "class_definition_ref",
    "class_definition_inline",
    "class_def_override",
    "class_def",
]);

Implementation

export function findInlineClassDefinitionFields(flow: unknown): string[] {
    if (!flow || typeof flow !== "object") return [];
    const f = flow as Record<string, unknown>;
    return FORBIDDEN_FLOW_KEYS.filter((k) => Object.prototype.hasOwnProperty.call(f, k));
}

export function assertNoInlineClassDefinition(
    flow: unknown,
    requestId: string,
): FlatRunnerResult | null {
    const found = findInlineClassDefinitionFields(flow);
    if (found.length === 0) return null;
    return flatErr(
        ERR.INLINE_CLASS_DEFINITION_FORBIDDEN,
        `Flow body contains inline class-definition field(s): ${found.join(", ")}. ` +
        "Class definitions must be resolved from the registry by class_id.",
        {
            forbidden_fields: found,
            remediation: "Remove these fields and reference the class via class_id",
            http_status_equivalent: 400,
        },
        false,
        requestId,
    );
}

Defense in Depth

The flow schema guard (CFV-9) catches any class_definition* keys not yet covered by this stricter rule, providing layered protection:

CFV-10's class_definition* keys are blocked specifically by class_definition_guard.ts; this guard catches everything else not yet covered by a stricter rule. Sources: src/security/flow_schema_guard.ts:17-20

Step Mode Guard

File: src/security/step_mode_guard.ts

Purpose

The step mode guard validates that each step's execution_mode agrees with the fields it contains. MOVA supports multiple execution modes, and each mode has specific field requirements. Mismatches can indicate configuration errors or attempts to bypass intended behavior.

Execution Mode Requirements

ModeRequired FieldForbidden Field
DETERMINISTICmodel
AI_ATOMICmodel
CONTRACT_CALLcontract_id
HUMAN_GATEdecision_options array

Violation Types

interface StepModeViolation {
    kind: string;
    step_id: string | undefined;
    execution_mode: string;
    message: string;
}
  1. deterministic_with_model — DETERMINISTIC steps run local JS checks, not LLMs
  2. ai_atomic_without_model — AI_ATOMIC requires a model field
  3. contract_call_without_contract_id — CONTRACT_CALL requires contract_id
  4. human_gate_without_decisions — HUMAN_GATE requires decision_options array

Implementation

export function findStepModeViolations(flow: unknown): StepModeViolation[] {
    const f = flow as Flow;
    if (!f?.steps) return [];
    const out: StepModeViolation[] = [];

    for (const [id, step] of Object.entries(f.steps)) {
        const mode = step.execution_mode ?? "DETERMINISTIC";

        if (mode === "DETERMINISTIC" && step.model !== undefined) {
            out.push({
                kind: "deterministic_with_model",
                step_id: id,
                execution_mode: mode,
                message: `step '${id}' is DETERMINISTIC but declares a 'model' field`,
            });
        }

        if (mode === "AI_ATOMIC" && asStr(step.model) === null) {
            out.push({
                kind: "ai_atomic_without_model",
                step_id: id,
                execution_mode: mode,
                message: `step '${id}' is AI_ATOMIC but has no 'model' field`,
            });
        }

        if (mode === "CONTRACT_CALL" && asStr(step.contract_id) === null) {
            out.push({
                kind: "contract_call_without_contract_id",
                step_id: id,
                execution_mode: mode,
                message: `step '${id}' is CONTRACT_CALL but has no 'contract_id'`,
            });
        }

        if (mode === "HUMAN_GATE" && !Array.isArray(step.decision_options)) {
            out.push({
                kind: "human_gate_without_decisions",
                step_id: id,
                execution_mode: mode,
                message: `step '${id}' is HUMAN_GATE but has no 'decision_options' array`,
            });
        }
    }
    return out;
}

Additional Guards

Gate Guard

File: src/security/gate_guard.ts

The gate guard enforces human gate access control, ensuring that human confirmation requires the dedicated gate path. It prevents generic step completion from bypassing human authorization requirements.

Determinism Guard

File: src/security/determinism_guard.ts

The determinism guard ensures deterministic steps produce consistent results and cannot introduce non-deterministic behavior that could compromise reproducibility.

Graph Guard

File: src/security/graph_guard.ts

The graph guard validates the structural integrity of the flow graph, ensuring all step references and dependencies are properly defined and no cycles or invalid references exist.

System Contract Guard

File: src/security/system_contract_guard.ts

The system contract guard monitors system contract usage, preventing unauthorized access to privileged operations.

Execution Flow

sequenceDiagram
    participant Client
    participant MCP as MCP Server
    participant Guards as Security Guards
    participant API as MOVA API

    Client->>MCP: mova_contract register(flow)
    MCP->>Guards: validate(flow)
    Guards->>Guards: flow_schema_guard
    Guards->>Guards: class_definition_guard
    Guards->>Guards: step_mode_guard
    Guards->>Guards: gate_guard
    Guards->>Guards: graph_guard
    Guards->>Guards: determinism_guard
    Guards->>Guards: system_contract_guard
    
    alt All Guards Pass
        Guards-->>MCP: null (valid)
        MCP->>API: POST /api/v1/contracts/register
        API-->>MCP: registration response
        MCP-->>Client: success
    else Guard Violation
        Guards-->>MCP: FlatRunnerResult (error)
        MCP-->>Client: rejection with details
    end

Error Codes

CodeGuardDescription
FLOW_SCHEMA_VIOLATIONflow_schema_guardUnknown top-level keys
INLINE_CLASS_DEFINITION_FORBIDDENclass_definition_guardInline class definition present
STEP_MODE_FIELD_MISMATCHstep_mode_guardExecution mode doesn't match fields
GATE_ACCESS_DENIEDgate_guardUnauthorized gate access attempt
DETERMINISM_VIOLATIONdeterminism_guardNon-deterministic behavior detected
GRAPH_STRUCTURE_INVALIDgraph_guardInvalid flow graph structure
SYSTEM_CONTRACT_VIOLATIONsystem_contract_guardUnauthorized system contract use

Integration Points

Local Seam Bridge

The local seam bridge integrates security guards into the transport layer:

SECURITY (CFV-3): HUMAN_GATE cannot be completed by generic step completion. Human confirmation requires the dedicated gate path (gate_approve / gate_reject). Guard runs BEFORE forwarding so run state is not advanced on rejection. Sources: src/index.ts:1-25

Custom Contract Bridge

Security considerations are applied when bridging custom contract runs to ensure audit integrity:

Safety: Built-in path untouched. No fake audit records introduced. Bridge only activates on 404 in mova_query. Sources: tasks/task061.md:1-40

Testing

Security guards are validated through:

# Run all security checks
make check

# Smoke test for custom contract bridge
npm run smoke:custom-bridge

# Build validation
npm run build
npm run test:build

Best Practices

  1. Never disable guards — Guards exist to prevent exploitation
  2. Keep allow-lists updated — Review ALLOWED_FLOW_TOP_LEVEL_KEYS when adding new features
  3. Report unknown fields — Fields that legitimately need to pass through should be explicitly allow-listed
  4. Maintain separation — Built-in and custom contract paths should have distinct security requirements
  5. Audit on change — Any schema change should trigger a security review

Source: https://github.com/mova-compact/mova-flat-runner / Human Manual

Transport Layer

Related topics: System Architecture, Core MCP Tools, Deployment and Operations

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Core Functions

Continue reading this section for the full explanation and source context.

Section Request Configuration

Continue reading this section for the full explanation and source context.

Section API Endpoints

Continue reading this section for the full explanation and source context.

Related topics: System Architecture, Core MCP Tools, Deployment and Operations

Transport Layer

The Transport Layer in mova-flat-runner provides the communication infrastructure for executing contract workflows across different execution environments. It abstracts the underlying HTTP communication with the MOVA backend API and provides a local seam bridge for sandboxed contract execution.

Overview

The transport layer handles two primary communication patterns:

Transport ModePurposeUse Case
Remote APIHTTP-based communication with api.mova-lab.euProduction deployments, remote contract execution
Local Seam BridgeIn-process contract execution via local sandboxDevelopment, testing, isolated execution

The layer is responsible for:

  • Managing HTTP requests to the MOVA backend
  • Routing contract step execution to appropriate handlers
  • Bridging between local execution context and remote API namespace
  • Sanitizing data shapes for public consumption
  • Handling execution modes (AI_ATOMIC, HUMAN_GATE, DETERMINISTIC, CONTRACT_CALL)

Sources: src/transports/local_seam_bridge.ts:1-50 Sources: src/transports/remote_api.ts:1-30

Architecture

graph TD
    subgraph "Client Layer"
        MCP[MCP Client]
        CLI[CLI Tools]
    end

    subgraph "Transport Layer"
        RemoteAPI[Remote API Transport]
        LocalBridge[Local Seam Bridge]
    end

    subgraph "Backend Services"
        MOVAAPI[MOVA API<br/>api.mova-lab.eu]
        LocalSandbox[Local Sandbox]
    end

    MCP --> |HTTP| RemoteAPI
    CLI --> |HTTP| RemoteAPI
    
    LocalBridge --> |Internal| LocalSandbox
    RemoteAPI --> |HTTP| MOVAAPI

    RemoteAPI -.-> |fallback| LocalBridge

Remote API Transport

The Remote API transport provides HTTP-based communication with the MOVA backend service.

Core Functions

FunctionMethodPurpose
movaRequestCoreBase HTTP request handler with error wrapping
movaGetGETRetrieve data from API endpoints
movaPostPOSTSubmit data to create or trigger actions
movaPutPUTUpdate existing resources
movaDeleteDELETERemove resources

Sources: src/transports/remote_api.ts:50-80

Request Configuration

The remote API uses a MovaConfig object for authentication and endpoint configuration:

interface MovaConfig {
  baseUrl: string;       // API base URL (default: api.mova-lab.eu)
  apiKey: string;        // API authentication key
  timeout?: number;       // Request timeout in milliseconds
  headers?: Record<string, string>;
}

API Endpoints

The transport layer integrates with the following MOVA API endpoints:

Endpoint PatternMethodUsage
/api/v1/contracts/{contractId}GETQuery contract metadata
/api/v1/contracts/{contractId}/stepPOSTExecute a contract step
/api/v1/contracts/{contractId}/steps/{stepId}/outputGETRetrieve step output
/api/v1/contracts/{contractId}/decisionGETGet human decision point
/api/v1/contracts/myGETList user's contracts
/run/{runId}/statusGETGet run execution status
/run/{contractId}POSTInitiate a contract run

Sources: dist-test/src/transports/remote_api.js:1-60

Local Seam Bridge

The Local Seam Bridge provides an internal bridge for sandboxed contract execution when the local seam backend is available.

Bridge Invoker

The createInternalBridgeInvoker() function creates a bridge invoker that handles step execution within the local environment:

function createInternalBridgeInvoker() {
  return async function bridgeInvoker(request) {
    bridgeSequence += 1;
    const suffix = `${request.step.id}:${bridgeSequence}`;
    
    const status =
      request.step.execution_mode === "HUMAN_GATE" && request.humanDecision == null
        ? "human_gate_required"
        : request.terminalOutcome
          ? "completed"
          : "advanced";

    return {
      ok: true,
      bridge: {
        ok: true,
        bridge_source: "mova_flat_runner_canonical_bridge",
        status,
        execution_mode: request.step.execution_mode,
        next_phase: { phase: status === "human_gate_required" ? "WAIT_HUMAN" : "EXECUTION" },
        // ... additional bridge fields
      },
    };
  };
}

Sources: src/transports/local_seam_bridge.ts:50-90

Execution Modes

The bridge handles different execution modes defined by the contract:

Execution ModeBehaviorBridge Output
AI_ATOMICLLM-powered atomic executionCanonical strategy output with full validation metadata
HUMAN_GATERequires human decisionReturns human_gate_required status, awaits confirmation
DETERMINISTICLocal JavaScript executionPasses through step result
CONTRACT_CALLCalls another contractForwards to referenced contract

Sources: dist-test/src/security/step_mode_guard.js:1-50

Status Handling

graph TD
    A[Step Execution Request] --> B{Execution Mode}
    
    B -->|AI_ATOMIC| C[Generate Canonical Output]
    B -->|HUMAN_GATE| D{Human Decision?}
    B -->|DETERMINISTIC| E[Pass Through Result]
    B -->|CONTRACT_CALL| F[Forward to Contract]
    
    D -->|No| G[Status: human_gate_required<br/>Phase: WAIT_HUMAN]
    D -->|Yes| H[Status: advanced<br/>Phase: EXECUTION]
    
    C --> I[Return Bridged Response]
    E --> I
    F --> I
    G --> I
    H --> I

Data Sanitization

The transport layer includes a sanitizePublicShape function to filter sensitive or internal fields from response data:

function sanitizePublicShape(value: unknown): boolean {
  if (Array.isArray(value)) {
    return value.every((item) => sanitizePublicShape(item));
  }
  
  const FORBIDDEN_KEYS = [
    "bridge_anchors",
    "last_terminal_bridge", 
    "terminal_commit_count",
    "_state15_bridge",
    "trace",
    "outputs",
    "context"
  ];

  for (const [key, child] of Object.entries(value)) {
    if (FORBIDDEN_KEYS.includes(key)) {
      return false;
    }
    if (!sanitizePublicShape(child)) {
      return false;
    }
  }
  return true;
}

Sources: src/transports/local_seam_bridge.ts:120-145

Package Locator Resolution

The Local Seam Bridge includes logic to resolve package references for contract execution:

async function resolveLocalSeamLocator(initialInputs) {
  const packagePath = initialInputs.package_path 
    ?? process.env.MOVA_SANDBOX_PACKAGE_PATH 
    ?? "D:\\Projects_MOVA\\mova-intent\\contracts\\dockerfile-nodejs-v1";
    
  const projectPath = initialInputs.project_path 
    ?? process.env.MOVA_SANDBOX_PROJECT_PATH 
    ?? "";
    
  if (!projectPath) {
    throw new Error("missing_local_seam_project_path");
  }
  // ... additional resolution logic
}

Sources: dist-test/src/transports/local_seam_bridge.js:80-100

Custom Contract Bridge

For custom contracts registered outside the standard /api/v1/contracts namespace, the transport layer provides a bridge mechanism:

Bridge Map

The system maintains an in-memory mapping of custom contracts:

CUSTOM_RUN_BRIDGE: Map<contract_id, {
  run_id: string,
  updated_at: string,
  source_url?: string
}>

Sources: src/index.ts:1-50

Custom Query Handling

When a contract is not found in the standard namespace, the bridge checks:

  1. Local CUSTOM_RUN_BRIDGE map for run history
  2. /api/v1/contracts/my for contract metadata
  3. /run/{runId}/status for execution status
graph LR
    A[mova_query Request] --> B{API 404?}
    
    B -->|Yes| C[Check CUSTOM_RUN_BRIDGE]
    B -->|No| Z[Normal Response]
    
    C --> D{Run Found?}
    D -->|Yes| E[Return Bridged Status]
    D -->|No| F[Query /contracts/my]
    
    F --> G{Record Found?}
    G -->|Yes| H[Return with Contract Record]
    G -->|No| I[Return Error]

Environment Variables

VariableDefaultPurpose
MOVA_API_URLhttps://api.mova-lab.euMOVA API base URL
MOVA_API_KEY-API authentication key
MOVA_API_TIMEOUT_MS30000Request timeout in milliseconds
MOVA_HTTP_PORT3796Local HTTP mode port
MOVA_INVOKE_TOKEN-Token for local HTTP invocation
MOVA_SANDBOX_PACKAGE_PATH-Package path for local seam
MOVA_SANDBOX_PROJECT_PATH-Project path for local seam
MOVA_SCHEMA_PATH-Additional schema lookup path

Sources: README.md

Remote Step Execution Flow

sequenceDiagram
    participant Client
    participant RemoteAPI
    participant MOVA as MOVA Backend
    
    Client->>RemoteAPI: movaRunStepsRemote(contractId)
    
    loop For each step: analyze, verify, decide
        RemoteAPI->>MOVA: POST /api/v1/contracts/{id}/step
        MOVA-->>RemoteAPI: Step result
        
        alt step === "analyze"
            RemoteAPI->>MOVA: GET /steps/analyze/output
            MOVA-->>RemoteAPI: Analysis output
            Note over RemoteAPI: Apply validators
        end
        
        alt status === "waiting_human"
            RemoteAPI->>MOVA: GET /decision
            MOVA-->>RemoteAPI: Decision point (question, options)
            Note over RemoteAPI: Return for human input
        end
    end
    
    RemoteAPI-->>Client: Aggregated results

Error Handling

The transport layer wraps errors with structured error codes:

Error CodeDescription
API_REQUEST_FAILEDHTTP request to backend failed
AUDIT_UNAVAILABLEAudit endpoint not accessible
CONTRACT_NOT_FOUNDContract ID not found in any namespace
LOCAL_VALIDATION_FAILEDLocal input validation failed
UNKNOWN_CONTRACT_TYPEContract type not in manifest registry

Sources: dist-test/src/transports/remote_api.js:40-80

Testing

The compiled test files verify transport behavior:

  • dist-test/src/transports/remote_api.js - Tests for HTTP transport functions
  • dist-test/src/transports/local_seam_bridge.js - Tests for local bridge execution
  • dist-test/src/package_support.js - Tests for package resolution

To run tests:

npm run build
npm run test:build
npm run smoke:custom-bridge

Sources: src/transports/local_seam_bridge.ts:1-50

Domain Validators

Related topics: Data Schemas and Types, Security Guards

Section Related Pages

Continue reading this section for the full explanation and source context.

Section Validator Function Type

Continue reading this section for the full explanation and source context.

Section Registry Structure

Continue reading this section for the full explanation and source context.

Section Error Handling

Continue reading this section for the full explanation and source context.

Related topics: Data Schemas and Types, Security Guards

Domain Validators

Domain Validators are the core validation layer in MOVA Flat Runner's contract execution engine. They provide domain-specific input validation for each workflow type, ensuring data integrity before contract execution proceeds through subsequent steps.

Overview

Domain Validators serve as a security boundary and data validation checkpoint within the MOVA contract execution model. Every validator function must be registered in the central VALIDATOR_REGISTRY before it can be invoked during contract execution.

Sources: src/validators/registry.ts:1-28

graph TD
    A[Contract Execution] --> B[ValidatorRef from Manifest]
    B --> C{Registry Lookup}
    C -->|Found| D[Execute ValidatorFn]
    C -->|Not Found| E[VALIDATOR_NOT_ALLOWED Error]
    D --> F[Return Validation Result]
    E --> G[Log Error to Analysis]
    F --> H[Proceed to Next Step]
    G --> H

Architecture

Validator Function Type

All domain validators conform to the ValidatorFn interface defined in the types module:

type ValidatorFn = (inputs: Record<string, unknown>) => {
    ok: boolean;
    value: Record<string, unknown>;
    step_id: string;
};

Sources: dist-test/src/types.d.ts:1-7

FieldTypeDescription
okbooleanIndicates whether validation passed
valueRecord<string, unknown>Validation results and derived fields
step_idstringIdentifier of the step being validated

Registry Structure

The registry is implemented as a Map<string, ValidatorFn> where keys are validator IDs in the format {domain}.{validator_name}_v{version}:

export const VALIDATOR_REGISTRY = new Map<string, ValidatorFn>(
  all.map(v => [v.id, v.fn])
);

Sources: src/validators/registry.ts:25-27

Registered Validator Domains

The validator registry aggregates validators from 10 domain-specific modules:

DomainModulePurpose
Invoiceinvoice.tsInvoice OCR and extraction validation
Purchase Orderpo.tsPO ID and approver validation
Tradetrade.tsTrade finance validation
AMLaml.tsAnti-money laundering screening
Complaintcomplaint.tsCustomer complaint processing
Compliancecompliance.tsRegulatory framework validation
Creditcredit.tsCredit review validation
Supply Chainsupply_chain.tsSupplier data validation
Churnchurn.tsCustomer churn prediction
Contract Gencontract_gen.tsContract generation validation

Sources: src/validators/registry.ts:3-13

Validator Invocation Flow

During contract execution, validators are invoked through the remote API transport layer when processing the analyze step:

const validatorContext = { ...initialInputs, ...analysis };
for (const validator of validators) {
    const fn = VALIDATOR_REGISTRY.get(validator.validator_id);
    if (!fn) {
        analysis[`${validator.step_id}_error`] = `VALIDATOR_NOT_ALLOWED: "${validator.validator_id}" not in registry`;
        continue;
    }
    try {
        const resultValue = fn(validatorContext);
        Object.assign(analysis, resultValue.value ?? {});
    } catch (error) {
        analysis[`${validator.step_id}_error`] = `VALIDATOR_FAILED: ${String(error)}`;
    }
}

Sources: dist-test/src/transports/remote_api.js:1-20

Error Handling

Error TypeConstantDescription
Validator not in registryVALIDATOR_NOT_ALLOWEDAttempted to call unregistered validator
Validator execution failedVALIDATOR_FAILEDValidator threw an exception
Local validation failedLOCAL_VALIDATION_FAILEDClient-side validation error

Sources: dist-test/src/types.d.ts:18-20

Example Validators

Supply Chain Validator

Validates supplier array input with country code format checking:

{
  id: "supply_chain.validate_inputs_v0",
  fn: (inputs) => {
    const suppliers = Array.isArray(inputs.suppliers) 
      ? inputs.suppliers as Record<string, unknown>[] 
      : [];
    const non_empty    = suppliers.length > 0;
    const valid_items  = suppliers.filter(s =>
      s &&
      typeof s === "object" &&
      String(s["id"]      || "").length > 0 &&
      String(s["name"]    || "").length > 0 &&
      /^[A-Z]{2}$/.test(String(s["country"] || ""))
    );
    const invalid_count = suppliers.length - valid_items.length;
    return {
      ok: true,
      value: {
        inputs_valid:          non_empty && invalid_count === 0,
        supplier_count:        suppliers.length,
        valid_supplier_count:  valid_items.length,
        invalid_supplier_count: invalid_count,
        has_suppliers:         non_empty,
      },
      step_id: "validate_inputs",
    };
  },
}

Sources: src/validators/supply_chain.ts:1-35

Purchase Order Validator

Validates PO ID and approver employee ID presence:

{
  id: "po.validate_inputs_v0",
  fn: (inputs) => {
    const po  = String(inputs.po_id                || "");
    const emp = String(inputs.approver_employee_id || "");
    const po_ok  = po.length  >= 3;
    const emp_ok = emp.length >= 3;
    return {
      ok: true,
      value: { 
        inputs_valid: po_ok && emp_ok, 
        po_id_present: po_ok, 
        approver_present: emp_ok 
      },
      step_id: "validate_inputs",
    };
  },
}

Sources: src/validators/po.ts:1-20

Compliance Framework Validator

Validates document URL protocol, framework type, and organization name:

const VALID_FRAMEWORKS = ["gdpr", "pci_dss", "iso_27001", "soc2"];

{
  id: "compliance.validate_inputs_v0",
  fn: (inputs) => {
    const url = String(inputs.document_url || "");
    const fw = String(inputs.framework || "");
    const url_ok = url.startsWith("https://");
    const fw_ok = VALID_FRAMEWORKS.includes(fw);
    const org_ok = String(inputs.org_name || "").trim().length >= 2;
    return {
      ok: true,
      value: {
        inputs_valid: url_ok && fw_ok && org_ok,
        url_ok,
        framework_ok: fw_ok,
        org_ok,
        document_url: url,
        framework: fw,
      },
      step_id: "validate_inputs",
    };
  },
}

Sources: dist-test/src/validators/compliance.js:1-30

Adding a New Validator

To add a new domain validator:

  1. Create a new file in src/validators/ (e.g., new_domain.ts)
  2. Export an array conforming to Array<{ id: string; fn: ValidatorFn }>
  3. Import and add to the aggregator in src/validators/registry.ts
  4. Use the naming convention: {domain}.{validator_name}_v{version}

Sources: src/validators/registry.ts:1-2

Naming Convention

ComponentFormatExample
Domain prefix{domain}invoice, supply_chain
Validator name{validator_name}validate_inputs, sanity_check
Version_v{version}_v0, _v1

Security Model

The validator registry enforces a strict allowlist:

  • No dynamic code execution: Validators must be pre-registered
  • No external function calls: Only functions in the registry may execute
  • No runtime injection: Validator IDs must match registry entries exactly
sequenceDiagram
    participant Contract as Contract Manifest
    participant Registry as VALIDATOR_REGISTRY
    participant Executor as Contract Executor
    
    Contract->>Executor: ValidatorRef{validator_id}
    Executor->>Registry: get(validator_id)
    alt ID exists in registry
        Registry-->>Executor: ValidatorFn
        Executor->>Executor: Call fn(inputs)
        Executor-->>Contract: result.value
    else ID not in registry
        Registry-->>Executor: undefined
        Executor-->>Contract: VALIDATOR_NOT_ALLOWED error
    end

This security boundary ensures that contract execution can only call explicitly approved validation logic, preventing arbitrary code execution during workflow runs.

Validation Result Usage

Validation results populate the analysis object in contract state:

Key PatternSourceDescription
inputs_validAll validatorsBoolean pass/fail indicator
{field}_okDomain validatorsIndividual field validation status
{step_id}_errorExecutorError messages on failure

These values are then available as validatorContext for subsequent validators in the same step, enabling cascading validation logic.

Sources: src/validators/registry.ts:1-28

Data Schemas and Types

Related topics: Domain Validators, Core MCP Tools

Section Related Pages

Continue reading this section for the full explanation and source context.

Section MovaConfig

Continue reading this section for the full explanation and source context.

Section FlatRunnerResult

Continue reading this section for the full explanation and source context.

Section ValidatorFn

Continue reading this section for the full explanation and source context.

Related topics: Domain Validators, Core MCP Tools

Data Schemas and Types

This document describes the core data schemas, type definitions, and validation mechanisms in the mova-flat-runner MCP server. The system uses TypeScript-first type definitions with JSON Schema validation for package manifests and runtime data validation.

Overview

The mova-flat-runner implements a governed AI workflow execution engine with multiple execution modes, contract-based state management, and human-in-the-loop approval gates. The type system is designed to enforce contract integrity across local and remote execution contexts.

graph TB
    subgraph "Type System Layers"
        JS[TypeScript Types]
        SCH[JSON Schemas]
        VAL[Validators]
    end
    
    subgraph "Runtime Contexts"
        LOCAL[Local Seam Bridge]
        REMOTE[Remote API Bridge]
    end
    
    subgraph "Execution Modes"
        AI[AI_ATOMIC]
        DET[DETERMINISTIC]
        HUM[HUMAN_GATE]
        CTR[CONTRACT_CALL]
    end
    
    JS --> SCH
    JS --> VAL
    SCH --> LOCAL
    SCH --> REMOTE

Core Type Definitions

MovaConfig

Configuration object for MOVA API connectivity. Required for all MCP tool executions.

FieldTypeRequiredDescription
apiUrlstringYesBase URL for MOVA API (default: https://api.mova-lab.eu)
apiKeystringYesAPI authentication key
llmKeystringNoOpenRouter API key for LLM analysis steps
llmModelstringNoOpenRouter model ID (default: openai/gpt-4o-mini)
timeoutMsnumberNoRequest timeout in milliseconds
invokeTokenstringNoLocal HTTP mode invocation token

Sources: src/index.ts:MovaConfig

FlatRunnerResult

Standard response envelope for all MCP tool operations.

FieldTypeDescription
okbooleanSuccess indicator
error_codestringError code on failure
error_messagestringHuman-readable error description
http_statusnumberHTTP status equivalent
interface FlatRunnerResult {
  ok: boolean;
  error_code?: string;
  error_message?: string;
  http_status?: number;
}

Sources: src/index.ts:FlatRunnerResult

ValidatorFn

Function signature for custom contract validators.

type ValidatorFn = (context: Record<string, unknown>) => {
  ok: boolean;
  value?: Record<string, unknown>;
  step_id?: string;
};

Sources: src/validators/supply_chain.ts:ValidatorFn

Execution Modes

The system defines four distinct execution modes for contract steps:

ModeDescriptionRequired FieldsValidation
AI_ATOMICLLM-powered single-step executionmodelStep must declare a model field
DETERMINISTICLocal JavaScript executionNoneCannot have model field
HUMAN_GATEHuman approval requireddecision_optionsMust have decision_options array
CONTRACT_CALLCross-contract invocationcontract_idMust specify target contract
stateDiagram-v2
    [*] --> DETERMINISTIC: Local check
    [*] --> AI_ATOMIC: LLM analysis
    [*] --> HUMAN_GATE: Approval required
    [*] --> CONTRACT_CALL: Cross-contract

    DETERMINISTIC --> [*]: Complete
    AI_ATOMIC --> [*]: Complete
    HUMAN_GATE --> APPROVED: Human approves
    HUMAN_GATE --> REJECTED: Human rejects
    CONTRACT_CALL --> [*]: Complete

Sources: src/security/step_mode_guard.ts:StepExecutionMode

Step Mode Validation

The step_mode_guard module enforces execution mode consistency through the assertStepModesValid function:

export function assertStepModesValid(
  flow: unknown,
  requestId: string,
): FlatRunnerResult | null {
  const violations = findStepModeViolations(flow);
  if (violations.length === 0) return null;
  return flatErr(
    ERR.STEP_MODE_FIELD_MISMATCH,
    `Step execution_mode does not agree with content fields`,
    { violations, http_status_equivalent: 400 }
  );
}

Violation Types:

KindCondition
ai_atomic_without_modelAI_ATOMIC mode without model field
deterministic_with_modelDETERMINISTIC mode with model field declared
contract_call_without_contract_idCONTRACT_CALL without target contract_id
human_gate_without_decisionsHUMAN_GATE without decision_options array

Sources: src/security/step_mode_guard.ts:findStepModeViolations

Package Manifest Schema

Global Package Shape

The validatePackageGlobalShape function enforces structure for contract package manifests:

export function validatePackageGlobalShape(value: unknown): string | null {
  if (!isObject(value)) return "global file must be a JSON object";
  const allowedKeys = new Set([
    "schema_id", "global_id", "version", "scope",
    "extends", "semantic_roles", "non_authority_rules"
  ]);
  // ... validation logic
}

Required Fields:

FieldTypeConstraint
schema_idstringNon-empty
global_idstringNon-empty
versionstringNon-empty
scopestringMust be "contract_package"
semantic_rolesarrayNon-empty array of role objects
non_authority_rulesarrayNon-empty array

Sources: src/package_support.ts:validatePackageGlobalShape

Semantic Role Structure

interface SemanticRole {
  id: string;
  role: string;
  authority: string;
  maturity: string;
  applies_to?: string[];
  allowed_use?: string[];
  forbidden_use?: string[];
  notes?: string;
}

Allowed Keys: id, role, authority, maturity, applies_to, allowed_use, forbidden_use, notes

Sources: src/package_support.ts:roleKeys

Output Schema Resolution

The resolveOutputSchema function locates JSON schemas for contract step outputs:

async function resolveOutputSchema(
  schemaRef: string,
  flowDir: string
): Promise<Record<string, unknown> | null> {
  const candidates = [
    path.join(flowDir, "_schemas", `${schemaRef}.json`),
    path.join(flowDir, "..", "_data-schemas", `${schemaRef}.json`),
    path.join(flowDir, "..", "..", "_data-schemas", `${schemaRef}.json`),
    ...(process.env.MOVA_SCHEMA_PATH 
      ? [path.join(process.env.MOVA_SCHEMA_PATH, `${schemaRef}.json`)]
      : []
    ),
  ];
  // ... search logic
}

Search Order (priority):

  1. {flowDir}/_schemas/{schemaRef}.json
  2. {flowDir}/../_data-schemas/{schemaRef}.json
  3. {flowDir}/../../_data-schemas/{schemaRef}.json
  4. $MOVA_SCHEMA_PATH/{schemaRef}.json (if set)

Sources: src/package_support.ts:resolveOutputSchema

Local Seam Bridge Types

BridgeRequest

interface BridgeRequest {
  contractRef: string;
  packagePath: string;
  step: {
    id: string;
    execution_mode: StepExecutionMode;
  };
  outputSchemaPath?: string;
  taskType?: string;
  stepResult?: unknown;
  terminalOutcome?: string;
  humanDecision?: string;
  routeKey?: string;
}

BridgeResponse

interface BridgeResponse {
  ok: boolean;
  bridge: {
    ok: boolean;
    bridge_source: string;
    status: "completed" | "advanced" | "human_gate_required";
    contract_ref: string;
    current_step_id: string;
    execution_mode: StepExecutionMode;
    produced_output: unknown;
    proof_ref: string;
    state_ref: string;
    next_state_ref: string;
    decision_kind: { kind: "Pass" | "Fail" };
    commit_effect: { kind: "Apply" | "Skip" };
    next_phase: { phase: "EXECUTION" | "WAIT_HUMAN" };
  };
}

Sources: src/transports/local_seam_bridge.ts:BridgeRequest

Contract Manifest

The CONTRACT_MANIFESTS registry defines built-in contract types:

interface ContractManifest {
  contract_type: string;
  title: string;
  version: string;
  execution_mode: StepExecutionMode;
  template_id?: string;
  policy_id?: string;
  dataspec: {
    inputs: Array<{
      key: string;
      label?: string;
      type: string;
      required?: boolean;
    }>;
  };
  decision_options?: Array<{
    id: string;
    label: string;
    description?: string;
  }>;
}

Sources: src/index.ts:CONTRACT_MANIFESTS

Validation Utilities

Required String Validation

function validateRequiredString(
  obj: Record<string, unknown>,
  key: string,
  scope: string
): string | null {
  if (typeof obj[key] !== "string" || (obj[key] as string).trim().length === 0) {
    return `${scope} is missing required string field "${key}"`;
  }
  return null;
}

Public Shape Sanitization

The sanitizePublicShape function filters internal bridge fields from public responses:

function sanitizePublicShape(value: unknown): boolean {
  const BLOCKED_KEYS = [
    "bridge_anchors",
    "last_terminal_bridge",
    "terminal_commit_count",
    "_state15_bridge",
    "trace",
    "outputs",
    "context"
  ];
  // Recursively validates and filters
}

Sources: src/transports/local_seam_bridge.ts:sanitizePublicShape

MCP Server Configuration Schema

Defined in server.json per Model Context Protocol specification:

{
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
  "name": "io.github.mova-compact/mova-flat-runner",
  "version": "2.0.6"
}

Environment Variables:

VariableRequiredDescription
MOVA_API_KEYYesAPI key for MOVA backend
LLM_KEYYesOpenRouter API key
LLM_MODELNoOpenRouter model ID
MOVA_API_URLNoOverride default API URL

Sources: server.json:environmentVariables

Error Types

Error CodeHTTP StatusDescription
UNKNOWN_CONTRACT_TYPE404Contract type not found in manifests
LOCAL_VALIDATION_FAILED400Input validation failed
STEP_MODE_FIELD_MISMATCH400Execution mode conflicts with fields
MOVA_API_ERROR502Upstream API failure
MOVA_API_404404Resource not found on backend

Sources: src/index.ts:ERR

Type Exports Summary

FilePrimary Exports
src/types.tsCore type interfaces (MovaConfig, FlatRunnerResult)
src/schemas.tsJSON Schema definitions for validation
src/validation/dataspec.tsData specification validators
src/package_support.tsPackage manifest validation
src/security/step_mode_guard.tsExecution mode validation

Sources: src/types.ts, src/schemas.ts, src/validation/dataspec.ts

Sources: src/index.ts:MovaConfig

Deployment and Operations

Related topics: Installation and Setup, Transport Layer, Extensibility and Customization

Section Related Pages

Continue reading this section for the full explanation and source context.

Section TypeScript Configuration

Continue reading this section for the full explanation and source context.

Section Build Pipeline

Continue reading this section for the full explanation and source context.

Section npm Scripts

Continue reading this section for the full explanation and source context.

Related topics: Installation and Setup, Transport Layer, Extensibility and Customization

Deployment and Operations

Overview

The MOVA Flat Runner is distributed as an MCP (Model Context Protocol) server package (@leryk1981/mova-flat-runner) that provides governed AI workflow capabilities including invoice OCR, AML triage, credit review, and custom contracts with human approval gates and audit trails.

This section covers the deployment architecture, build system, runtime configuration, publishing workflow, and operational considerations for running the MOVA Flat Runner in production and development environments.

Architecture Overview

graph TD
    subgraph "Client Layer"
        Claude["Claude Desktop"]
        Codex["Codex IDE"]
    end
    
    subgraph "MCP Transport"
        STDIO["STDIO Transport"]
        HTTP["Local HTTP Mode"]
    end
    
    subgraph "MOVA Flat Runner"
        Index["src/index.ts"]
        API["HTTP Handlers"]
        Auth["Authentication"]
        Service["Business Logic"]
    end
    
    subgraph "External Services"
        MOVA_API["MOVA API"]
        LLM["LLM Provider"]
    end
    
    Claude --> STDIO
    Codex --> HTTP
    STDIO --> Index
    HTTP --> Index
    Index --> API
    Index --> Auth
    Index --> Service
    Service --> MOVA_API
    Service --> LLM

Build System

TypeScript Configuration

The project uses TypeScript with separate configurations for production and testing builds.

Production Build (tsconfig.json)

SettingValuePurpose
targetES2022ECMAScript target version
moduleES2022 / NodeNextESM module system
outDirdistCompiled output directory
rootDirsrcSource root directory
stricttrueEnable strict type checking

Test Build (tsconfig.test.json)

SettingValuePurpose
extends./tsconfig.jsonInherits base config
outDirdist-testSeparate test output directory
rootDirsrcSource root directory

Build Pipeline

graph LR
    A["src/*.ts"] --> B["tsc compiler"]
    B --> C["dist/index.js"]
    C --> D["shebang injection"]
    D --> E["chmod +x"]
    
    F["src/*.ts"] --> G["tsc (test config)"]
    G --> H["dist-test/"]

npm Scripts

ScriptCommandPurpose
buildtsc && node -e "..."Compile TypeScript and make executable
checkmake checkRun lint, unit tests, and integration tests
test:buildtsc -p tsconfig.test.jsonBuild test artifacts

Sources: package.json:18-24

MCP Server Configuration

Server Metadata

{
  "name": "io.github.mova-compact/mova-flat-runner",
  "version": "2.0.6",
  "description": "Governed AI workflows with human approval gates and audit trails",
  "repository": {
    "url": "https://github.com/mova-compact/mova-flat-runner",
    "source": "github"
  }
}

Runtime Hints

PropertyValueNotes
runtimeHintnodeRequires Node.js runtime
transportstdioPrimary transport protocol

Sources: server.json:1-15

Environment Variables

Required Variables

VariableDescriptionExample
MOVA_API_KEYMOVA API authentication key__SET_MOVA_API_KEY__
LLM_KEYOpenRouter API key for LLM analysis__SET_LLM_KEY__

Optional Variables

VariableDefaultDescription
MOVA_API_URLhttps://api.mova-lab.euMOVA API base URL
LLM_MODELopenai/gpt-4o-miniOpenRouter model ID
MOVA_API_TIMEOUT_MS30000API request timeout
MOVA_HTTP_PORT3796Local HTTP mode port
MOVA_INVOKE_TOKENToken for local HTTP invocation

Sources: README.md:40-57

Publishing and Distribution

Publishing Workflow

The project includes a dedicated CLI tool for publishing servers to the MCP registry.

# Build the publisher CLI
make publisher

# Publish a server
./bin/mcp-publisher --help

npm Distribution

The package is published to npm as @leryk1981/mova-flat-runner:

PropertyValue
Package Name@leryk1981/mova-flat-runner
Current Version3.3.4
Binarymova-mcp
Entry Pointdist/index.js

Sources: package.json:3-6

Client Configuration Examples

Claude Desktop (JSON)

{
  "mcpServers": {
    "mova": {
      "command": "npx",
      "args": ["-y", "@leryk1981/[email protected]"],
      "env": {
        "MOVA_API_URL": "https://api.mova-lab.eu",
        "MOVA_API_KEY": "__SET_MOVA_API_KEY__",
        "LLM_KEY": "__SET_LLM_KEY__",
        "LLM_MODEL": "openai/gpt-4o-mini"
      }
    }
  }
}

Codex (TOML)

[mcp_servers.mova]
command = "npx"
args = ["-y", "@leryk1981/[email protected]"]
startup_timeout_sec = 90.0

[mcp_servers.mova.env]
MOVA_API_URL = "https://api.mova-lab.eu"
MOVA_API_KEY = "__SET_MOVA_API_KEY__"
LLM_KEY = "__SET_LLM_KEY__"
LLM_MODEL = "openai/gpt-4o-mini"

Sources: README.md:63-100

Deployment Configuration

Project Structure

mova-flat-runner/
├── cmd/                     # Application entry points
│   └── publisher/           # Server publishing tool
├── deploy/                  # Deployment configuration (Pulumi)
├── internal/                # Private application code
│   ├── api/                 # HTTP handlers and routing
│   ├── auth/                # Authentication (GitHub OAuth, JWT)
│   ├── config/              # Configuration management
│   ├── database/            # Data persistence (PostgreSQL)
│   ├── service/             # Business logic
│   ├── telemetry/           # Metrics and monitoring
│   └── validators/          # Input validation
├── pkg/                     # Public packages
└── tools/                   # Operational tools

Pulumi Deployment

Infrastructure is managed via Pulumi in the deploy/ directory. This enables repeatable, version-controlled infrastructure deployments.

Sources: README.md:12-29

Health Monitoring

Health Check Endpoint

curl -sS https://api.mova-lab.eu/health

Available Tools for Monitoring

ToolPurpose
mova_healthServer health status
mova_registryMCP registry information
mova_runContract execution
mova_queryQuery contract status
mova_decideHuman decision handling
mova_connectorExternal system integration
mova_contractContract management

Sources: README.md:58-60

Local HTTP Mode Operations

Overview

The flat runner supports an optional local HTTP transport mode for environments where STDIO is not suitable.

graph TD
    Client["MCP Client"] --> |HTTP Request| LocalHTTP["Local HTTP Bridge"]
    LocalHTTP --> |STDIO| MOVA_MCP["MOVA MCP Server"]
    MOVA_MCP --> |Response| LocalHTTP
    LocalHTTP --> |HTTP Response| Client
    
    subgraph "Local Environment"
        LocalHTTP
        MOVA_MCP
    end

Configuration

MOVA_HTTP_PORT=3796
MOVA_INVOKE_TOKEN=__SET_LOCAL_INVOKE_TOKEN__
MOVA_API_TIMEOUT_MS=30000

Security Considerations

  • MOVA_INVOKE_TOKEN must be set to authorize local HTTP requests
  • API timeouts prevent hanging requests (default: 30 seconds)
  • Health checks should be performed before heavy operations

Sources: README.md:50-55

Development Operations

Makefile Targets

TargetPurpose
make checkRun lint, unit tests, integration tests
make helpDisplay available commands
make publisherBuild the MCP publisher CLI

Validation Workflow

graph LR
    A["npm run build"] --> B["npm run test:build"]
    B --> C["npm run smoke:*"]
    C --> D["Validation Complete"]

Smoke Testing

Custom bridge validation:

npm run smoke:custom-bridge

This validates the custom contract query bridge for handling mova_query requests when the backend namespace is absent.

Sources: tasks/task061.md:1-50

Operational Data Flows

Local Seam Bridge Execution

sequenceDiagram
    participant MCP as MCP Client
    participant Bridge as Local Seam Bridge
    participant Machine as State Machine
    participant FS as File System
    
    MCP->>Bridge: Execute step with state
    Bridge->>Machine: Load machine module
    Machine->>FS: Read state file
    FS-->>Machine: State data
    Machine->>Machine: Execute step logic
    Machine->>FS: Write updated state
    Bridge->>MCP: Return result with proofs

Custom Contract Bridge

For custom contract IDs (prefixed with local-* or remote-*), the system maintains an in-memory bridge map:

CUSTOM_RUN_BRIDGE: Map<contract_id, {run_id, updated_at, source_url?}>

Sources: src/index.ts:1-100

Security Notes

Environment Variable Security

PracticeRequirement
Placeholders in docsUse __SET_*__ format
Real secretsNever commit to repository
API keysUse MOVA_API_KEY, LLM_KEY
Warning: Do not commit real MOVA_API_KEY, LLM_KEY, or MOVA_INVOKE_TOKEN to version control.

Sources: README.md:37-39

Step Mode Validation

The system validates step execution modes at runtime:

ModeValidation Rule
DETERMINISTICCannot have model field
AI_ATOMICMust have model field
CONTRACT_CALLMust have contract_id
HUMAN_GATEMust have decision_options array

Sources: dist-test/src/security/step_mode_guard.js:1-50

Deployment Environments

Build Variants

BranchPurposeBuild Output
mainProduction buildsdist/
main-<date>-<sha>Specific commit buildsdist/

Continuous Integration

main branch → Continuous build → Latest stable release

Sources: README.md:1-5

Troubleshooting

IssueResolution
Build failsRun npm run build
Test failuresRun make check
MCP connection issuesVerify MOVA_API_KEY and LLM_KEY
Custom contract 404Use mova_contract for custom IDs
Human gate timeoutCheck MOVA_API_TIMEOUT_MS setting

Sources: package.json:18-24

Extensibility and Customization

Related topics: Domain Validators, Security Guards, Deployment and Operations

Section Related Pages

Continue reading this section for the full explanation and source context.

Section 1.1 Architecture Overview

Continue reading this section for the full explanation and source context.

Section 1.2 Registry Implementation

Continue reading this section for the full explanation and source context.

Section 1.3 Registered Validator Modules

Continue reading this section for the full explanation and source context.

Related topics: Domain Validators, Security Guards, Deployment and Operations

Extensibility and Customization

The mova-flat-runner provides multiple layers of extensibility, allowing operators to add custom validators, define new contract types, extend execution modes, and integrate with external systems. This document describes the core extension points and customization mechanisms available in the MCP server.

1. Validator Registry System

1.1 Architecture Overview

The validator registry is the primary extension point for business logic validation. It implements a whitelist-based security model where only explicitly registered validator functions may be invoked during contract execution. Dynamic code execution is prohibited.

graph TD
    A[Contract Execution] --> B{ValidatorRef found?}
    B -->|Yes| C[Lookup in VALIDATOR_REGISTRY]
    B -->|No| D[Return VALIDATOR_NOT_ALLOWED error]
    C --> E{Validator registered?}
    E -->|Yes| F[Execute ValidatorFn]
    E -->|No| D
    F --> G[Return validation result]
    D --> H[Audit log: validator blocked]

1.2 Registry Implementation

The VALIDATOR_REGISTRY is a Map<string, ValidatorFn> that maps each validator's unique identifier to its implementation function. All validators must conform to the ValidatorFn type signature.

Type Signature:

type ValidatorFn = (inputs: Record<string, unknown>) => {
    ok: boolean;
    value: Record<string, unknown>;
    step_id: string;
};

1.3 Registered Validator Modules

ModuleSource FileValidators
Invoiceinvoice.tsinvoice validation validators
Purchase Orderpo.tsPO validation validators
Tradetrade.tstrade validation validators
AMLaml.tsAML triage validators
Complaintcomplaint.tscomplaint handling validators
Compliancecompliance.tscompliance check validators
Creditcredit.tscredit review validators
Supply Chainsupply_chain.tssupplier validation validators
Churnchurn.tschurn prediction validators
Contract Gencontract_gen.tscontract generation validators

Sources: src/validators/registry.ts:1-28

1.4 Adding a New Validator

To add a new validator:

  1. Implement the validator function in a contract-specific file following the ValidatorFn signature
  2. Export it as part of a validator array with a unique id
  3. Register it in src/validators/registry.ts

Example from supply_chain.ts:

export const supplyChainValidators: Array<{ id: string; fn: ValidatorFn }> = [
  {
    id: "supply_chain.validate_inputs_v0",
    fn: (inputs) => {
      const suppliers = Array.isArray(inputs.suppliers) ? inputs.suppliers : [];
      const valid_items = suppliers.filter(s =>
        s && typeof s === "object" &&
        String(s["id"] || "").length > 0 &&
        /^[A-Z]{2}$/.test(String(s["country"] || ""))
      );
      return {
        ok: true,
        value: {
          inputs_valid: valid_items.length === suppliers.length,
          supplier_count: suppliers.length,
        },
        step_id: "validate_inputs",
      };
    },
  },
];

Sources: src/validators/supply_chain.ts:1-35

1.5 Security Model

The registry enforces these security constraints:

ConstraintDescription
Whitelist-onlyOnly functions in the registry may execute
No dynamic codeArbitrary code execution is forbidden
Type enforcementAll validators must return {ok, value, step_id}
Step isolationValidators operate on sanitized input contexts

During contract execution, the runner checks each ValidatorRef against the registry before invocation:

const fn = VALIDATOR_REGISTRY.get(validator.validator_id);
if (!fn) {
    analysis[`${validator.step_id}_error`] = `VALIDATOR_NOT_ALLOWED: "${validator.validator_id}" not in registry`;
    continue;
}

Sources: dist-test/src/transports/remote_api.js:1-25

2. Contract Manifest System

2.1 Manifest Structure

Contract manifests define the metadata, inputs, and behavior of each contract type. Built-in manifests are stored in CONTRACT_MANIFESTS:

type ContractManifest = {
    contract_type: string;
    title: string;
    version: string;
    execution_mode: "AI_ATOMIC" | "HUMAN_GATE" | "LOCAL_SEAM";
    template_id: string;
    policy_id: string;
    dataspec: { inputs: InputSpec[] };
    decision_options?: DecisionOption[];
};

2.2 Manifest Registry Resources

The MCP server exposes manifests via the mova:// URI scheme:

Resource URIDescription
mova://registryLists all available contract types
mova://schemas/envelopesReturns envelope schema definition
mova://contracts/{type}/manifestReturns specific contract manifest
function readResource(uri: string): unknown {
  if (uri === "mova://registry") {
    return {
      schema_version: "1.0",
      contracts: Object.values(CONTRACT_MANIFESTS).map(m => ({
        contract_type: m.contract_type,
        title: m.title,
        version: m.version,
        manifest_resource: `mova://contracts/${m.contract_type}/manifest`,
      })),
    };
  }
  // ...
}

Sources: src/index.ts:300-350

3. Custom Contract Bridge

3.1 The Namespace Gap Problem

Custom contracts registered via mova_contract register use local-* or remote-* ID prefixes and execute in the /run/* namespace. However, mova_query operations query the /api/v1/contracts/* namespace, resulting in 404 errors for custom contracts.

3.2 Bridge Implementation

The custom contract bridge provides a mitigation layer:

graph LR
    A[register] --> B[rememberCustomRun]
    B --> C[CUSTOM_RUN_BRIDGE Map]
    C --> D[mova_query status]
    D --> E{404 from API?}
    E -->|Yes| F[getMyContractRecord]
    F --> G{Bridge entry found?}
    G -->|Yes| H[Return bridged status]
    G -->|No| I[Return error]

3.3 Bridge Components

ComponentTypePurpose
CUSTOM_RUN_BRIDGEMap<contract_id, {run_id, updated_at, source_url?}>In-memory ID mapping
rememberCustomRun()FunctionCaptures and persists contract→run mapping
getMyContractRecord()FunctionProbes /api/v1/contracts/my for metadata

Memory Bridge Map:

const CUSTOM_RUN_BRIDGE = new Map<string, CustomRunRef>();

function rememberCustomRun(contractId: string, runId: string, sourceUrl?: string): void {
    if (typeof contractId !== "string" || typeof runId !== "string") return;
    const ref = { run_id: runId, updated_at: new Date().toISOString() };
    if (sourceUrl) ref.source_url = sourceUrl;
    CUSTOM_RUN_BRIDGE.set(contractId, ref);
}

Sources: src/index.ts:400-420

3.4 Bridge Response Modes

View ModeBehavior
statusReturns structured bridged status with bridge_mode=custom_contract_run_namespace_bridge_v1
auditReturns AUDIT_UNAVAILABLE envelope (honest unavailability)
audit_compactReturns {ok=false, status=424} with JSON journal explaining bridge

Sources: tasks/task061.md:1-60

4. Local Seam Execution Mode

4.1 Overview

The LOCAL_SEAM execution mode allows contracts to run entirely within the MCP server process, bypassing the remote MOVA API. This is critical for offline development and testing scenarios.

4.2 Execution Flow

graph TD
    A[mova_run contract_type] --> B{Is LOCAL_SEAM?}
    B -->|No| C[Remote API execution]
    B -->|Yes| D[Resolve local locator]
    D --> E[Load sandbox package]
    E --> F[Execute steps locally]
    F --> G{Step type?}
    G -->|AI_ATOMIC| H[LLM invocation]
    G -->|HUMAN_GATE| I[Wait for gate decision]
    G -->|Standard| J[Apply step logic]
    H --> K[Validate output schema]
    I --> L[Bridge to human approval]
    J --> K
    K --> M{More steps?}
    M -->|Yes| F
    M -->|No| N[Return FlatRunnerResult]

4.3 Local Seam Locator Resolution

async function resolveLocalSeamLocator(initialInputs: Record<string, unknown>): Promise<SeamLocator> {
    const packagePath = initialInputs.package_path 
        ?? process.env.MOVA_SANDBOX_PACKAGE_PATH 
        ?? "default-contract-path";
    const projectPath = initialInputs.project_path 
        ?? process.env.MOVA_SANDBOX_PROJECT_PATH 
        ?? "";
    // ...
}

Sources: src/transports/local_seam_bridge.ts:50-80

4.4 Sandbox Environment Variables

VariableDescriptionDefault
MOVA_SANDBOX_PACKAGE_PATHPath to contract packageProject-specific
MOVA_SANDBOX_PROJECT_PATHPath to project directoryRequired
MOVA_SANDBOX_STATE_FILEState persistence fileSystem temp

5. Package Global Support

5.1 Package Manifest Validation

Custom contract packages must include a global file conforming to the contract_package scope. The validation enforces strict schema requirements:

Required Fields:

FieldTypeConstraint
global_idstringNon-empty
versionstringNon-empty
scopestringMust be contract_package
extendsarrayOptional, must be array if present
semantic_rolesarrayNon-empty, objects with allowed keys
non_authority_rulesarrayNon-empty

Validation Rules for semantic_roles:

const roleKeys = new Set([
    "id", "role", "authority", "maturity", 
    "applies_to", "allowed_use", "forbidden_use", "notes"
]);
for (const key of Object.keys(role)) {
    if (!roleKeys.has(key))
        return `global semantic_roles[${index}] contains unsupported field "${key}"`;
}

Sources: dist-test/src/package_support.js:1-30

5.2 Global File Schema

interface PackageGlobal {
    global_id: string;
    version: string;
    scope: "contract_package";
    extends?: string[];
    semantic_roles: Array<{
        id: string;
        role: string;
        authority: string;
        maturity: string;
        applies_to?: string;
        allowed_use?: string[];
        forbidden_use?: string[];
        notes?: string;
    }>;
    non_authority_rules: string[];
}

6. MCP Tool Extensibility

6.1 Available Tools

ToolPurposeExtensibility
mova_healthHealth checkFixed
mova_registryList contract typesFixed
mova_runExecute built-in contractsExtend via CONTRACT_MANIFESTS
mova_queryQuery contract status/auditBridge extensible
mova_decideSubmit human decisionsFixed
mova_connectorConnect to external systemsFixed
mova_contractCustom contract operationsCore extensibility interface

6.2 Tool Executor Pattern

Tools are registered in a switch-based executor that maps tool names to handler functions:

async function executeTool(name: string, args: Args): Promise<string> {
  const requestId = shortId();
  switch (name) {
    case "mova_run": {
      const manifest = CONTRACT_MANIFESTS[contractType];
      if (!manifest) {
        return JSON.stringify(flatErr(
          ERR.UNKNOWN_CONTRACT_TYPE,
          `Unknown contract_type "${contractType}". For custom contracts use mova_contract action=run.`,
          { available: Object.keys(CONTRACT_MANIFESTS) },
          false, requestId
        ));
      }
      // ...
    }
  }
}

Sources: src/index.ts:450-480

7. Configuration Extension Points

7.1 Environment Variables

VariableRequiredDescription
MOVA_API_URLYesMOVA API base URL
MOVA_API_KEYYesAPI authentication key
LLM_KEYConditionalOpenRouter API key for LLM steps
LLM_MODELNoModel identifier, default: openai/gpt-4o-mini
MOVA_API_TIMEOUT_MSNoHTTP timeout in milliseconds
MOVA_HTTP_PORTNoLocal HTTP mode port
MOVA_INVOKE_TOKENNoLocal invoke authentication

7.2 Config Merging

The configuration system supports runtime overrides through environment variables, with precedence: CLI args > env vars > defaults.

8. Extension Summary

graph TD
    subgraph Extension Points
        A[Validator Registry]
        B[Contract Manifests]
        C[Package Global]
        D[Local Seam Mode]
    end
    
    subgraph Security Layer
        E[Whitelist Enforcement]
        F[Type Validation]
        G[Schema Validation]
    end
    
    subgraph MCP Tools
        H[mova_contract]
        I[mova_run]
        J[mova_query]
    end
    
    A --> E
    B --> G
    C --> G
    D --> F
    H --> A
    H --> B
    I --> A
    J --> C
    J --> D

The mova-flat-runner's extensibility model is designed for operator-controlled customization while maintaining strict security boundaries. Validators, contracts, and execution modes can be extended without modifying core server code, enabling safe customization in regulated environments.

Sources: src/validators/registry.ts:1-28

Doramagic Pitfall Log

Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.

medium Configuration risk needs validation

Users may get misleading failures or incomplete behavior unless configuration is checked carefully.

medium v3.0.0 — Step Enforcement Runtime

Users may get misleading failures or incomplete behavior unless configuration is checked carefully.

medium README/documentation is current enough for a first validation pass.

The project should not be treated as fully validated until this signal is reviewed.

medium Maintainer activity is unknown

Users cannot judge support quality until recent activity, releases, and issue response are checked.

Doramagic Pitfall Log

Doramagic extracted 8 source-linked risk signals. Review them before installing or handing real data to the project.

1. Configuration risk: Configuration risk needs validation

  • Severity: medium
  • Finding: Configuration risk is backed by a source signal: Configuration risk needs validation. Treat it as a review item until the current version is checked.
  • User impact: Users may get misleading failures or incomplete behavior unless configuration is checked carefully.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: capability.host_targets | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | host_targets=mcp_host, claude, chatgpt

2. Configuration risk: v3.0.0 — Step Enforcement Runtime

  • Severity: medium
  • Finding: Configuration risk is backed by a source signal: v3.0.0 — Step Enforcement Runtime. Treat it as a review item until the current version is checked.
  • User impact: Users may get misleading failures or incomplete behavior unless configuration is checked carefully.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: Source-linked evidence: https://github.com/mova-compact/mova-flat-runner/releases/tag/v3.0.0

3. Capability assumption: README/documentation is current enough for a first validation pass.

  • Severity: medium
  • Finding: README/documentation is current enough for a first validation pass.
  • User impact: The project should not be treated as fully validated until this signal is reviewed.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: capability.assumptions | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | README/documentation is current enough for a first validation pass.

4. Maintenance risk: Maintainer activity is unknown

  • Severity: medium
  • Finding: Maintenance risk is backed by a source signal: Maintainer activity is unknown. Treat it as a review item until the current version is checked.
  • User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: evidence.maintainer_signals | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | last_activity_observed missing

5. Security or permission risk: no_demo

  • Severity: medium
  • Finding: no_demo
  • User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: downstream_validation.risk_items | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | no_demo; severity=medium

6. Security or permission risk: no_demo

  • Severity: medium
  • Finding: no_demo
  • User impact: The project may affect permissions, credentials, data exposure, or host boundaries.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: risks.scoring_risks | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | no_demo; severity=medium

7. Maintenance risk: issue_or_pr_quality=unknown

  • Severity: low
  • Finding: issue_or_pr_quality=unknown。
  • User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: evidence.maintainer_signals | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | issue_or_pr_quality=unknown

8. Maintenance risk: release_recency=unknown

  • Severity: low
  • Finding: release_recency=unknown。
  • User impact: Users cannot judge support quality until recent activity, releases, and issue response are checked.
  • Recommended check: Open the linked source, confirm whether it still applies to the current version, and keep the first run isolated.
  • Evidence: evidence.maintainer_signals | github_repo:1212840167 | https://github.com/mova-compact/mova-flat-runner | release_recency=unknown

Source: Doramagic discovery, validation, and Project Pack records

Community Discussion Evidence

These external discussion links are review inputs, not standalone proof that the project is production-ready.

Sources 2

Count of project-level external discussion links exposed on this manual page.

Use Review before install

Open the linked issues or discussions before treating the pack as ready for your environment.

Community Discussion Evidence

Doramagic exposes project-level community discussion separately from official documentation. Review these links before using mova-flat-runner with real data or production workflows.

Source: Project Pack community evidence and pitfall evidence