Overview
MPE Flow is a multi-protocol workflow orchestration engine. It runs as either a desktop GUI or a CLI tool — from the same binary.
Two Modes, One Binary
- No arguments → Opens the Tauri desktop GUI
- With arguments → Runs in CLI mode
On Linux, only the CLI binary is available — no GUI dependencies required.
Installation
Windows
Download the Windows installer (.exe installer or portable build) from the Changelog page and run it to set up MPE.
macOS
Download the Universal DMG package from the Changelog page (a single image natively supporting both Apple Silicon M-series and Intel processors). Open the DMG and drag the MPE icon to your Applications folder.
macOS First Run: "Unverified Developer" or "App is damaged" Workaround
Notice: Current open-source release builds are not signed or notarized with a paid Apple Developer certificate. On macOS, Gatekeeper's security mechanism will prevent opening untrusted binaries on first launch, displaying a warning such as "MPE cannot be opened because Apple cannot check it for malicious software" or "MPE is damaged and can't be opened. You should move it to the Trash." This is standard macOS behavior for unsigned software; MPE is fully open source and completely safe to run.
To launch MPE, use either of the following solutions:
Method 1: Clear quarantine attribute via Terminal (Recommended)
After moving MPE to your /Applications directory, open Terminal and execute the following command to remove the quarantine flag:
xattr -cr /Applications/MPE.app
Once cleared, double-click MPE.app from Launchpad or Finder. Gatekeeper will not prompt again.
Method 2: Right-click Open in Finder
- Open Finder, navigate to your Applications directory, and find MPE.
- Hold down the
Control key on your keyboard, right-click (or two-finger tap) the MPE icon, and click Open in the menu.
- In the dialog that appears, click the Open button to confirm. (This is only required once).
Linux (CLI)
Download the pre-built binary and add it to your PATH:
# Download and extract
curl -LO https://download.mpe.run/releases/latest/mpe-vX.Y.Z-linux-x86_64.tar.gz
tar xzf mpe-vX.Y.Z-linux-x86_64.tar.gz
# Make executable and move to PATH
chmod +x mpe
sudo mv mpe /usr/local/bin/
# Verify
mpe --help
Note
The Linux build is CLI-only — no GUI, no GTK/WebKit dependencies required.
Quick Example
Create a workflow file hello.mpf (defaults to clean YAML format):
_version: 1
flow:
uuid: example-flow
name: Hello World
nodes:
- uuid: entry-1
type: entry
name: Start
data:
type: entry
- uuid: http-1
type: http:request
name: Fetch Data
data:
type: http:request
url: https://httpbin.org/get
method: get
- uuid: end-1
type: end
name: End
data:
type: end
connections:
- id: conn-1
source_node_uuid: entry-1
target_node_uuid: http-1
source_port_id: out
target_port_id: in
- id: conn-2
source_node_uuid: http-1
target_node_uuid: end-1
source_port_id: "true"
target_port_id: in
Run it:
YAML by Default with Full JSON Compatibility
Starting in v0.3.0, MPE flow files (.mpf) use YAML by default for optimal readability and Git diff clarity. The engine, CLI, desktop app, and Web Playground automatically detect and support both YAML and JSON formats. Use mpe flow fmt to convert or format files anytime.
AI Copilot & Smart Orchestration
MPE Flow features a native, deep-integrated Agentic AI Copilot designed for natural language workflow generation, autonomous execution, canvas layout optimization, and one-click error self-healing.
Privacy & Data Sovereignty
The AI Copilot connects to any OpenAI-compatible provider (OpenAI, DeepSeek, Claude, or local Ollama/vLLM instances). All workflow files, tokens, and payloads stay on your machine. No mandatory accounts or cloud telemetry.
Configuring an AI Provider
Open Settings → AI Copilot to configure one or more model providers:
- Preset Quick-Fill: 1-click configuration for popular providers like DeepSeek, OpenAI, Claude, Kimi, and Ollama.
- Base URL & API Key: Connect to public cloud endpoints or private self-hosted models (e.g.,
http://localhost:11434/v1 for Ollama).
- Model Selection: Fetch model lists automatically or specify custom model identifiers (e.g.,
deepseek-chat, deepseek-reasoner, gpt-4o, claude-3-5-sonnet).
- Custom System Prompt: Inject domain rules, naming conventions, or company-specific API guidelines applied to every request.
Interaction Modes & Shortcuts
Access the AI Copilot across three integrated surfaces:
| Surface |
Shortcut |
Description |
| Sidebar Copilot |
Ctrl+I / Cmd+I |
Collapsible right-side chat panel with conversation history, multi-session management, and model switcher. |
| Detachable Floating Window |
Drag header or Undock button |
Undock the Copilot panel into a free-floating, draggable modal across multi-monitor setups. Drag back to the right to re-dock. |
| Spotlight Quick Commands |
Ctrl+K / Cmd+K |
Center command palette to run natural language instructions, batch pipeline commands, or layout operations without opening the sidebar. |
| CLI AI Bridge |
mpe ai "<prompt>" |
Execute AI commands directly from your terminal, seamlessly bridging with the running GUI or executing in headless mode. |
The Copilot does not just generate static text — it operates the visual canvas directly through strict typed tool calls:
- Natural Language to DAG: "Create an authentication flow that sends a POST to /api/login, extracts the bearer token, and pipes it to a WebSocket connection."
- Batch Pipeline Creation: Constructs complete multi-node flows with typed schemas and default connections in a single atomic operation.
- Automatic Layout & Adaptive Wrapping: Cleanly arranges complex branching DAGs. Supports standard horizontal/vertical layouts as well as the new Adaptive Wrap Auto-Layout for expansive multi-branch flows.
- Script Generation: Automatically writes JavaScript or Rhai snippets for data transformations, crypto signing, or custom assertions.
- Datasource Exploration: Searches connected API specifications and automatically imports target endpoints onto the canvas.
Autonomous Execution ("Run to Goal") & Midway Steering
When solving complex workflows, the AI Copilot can autonomously iterate through execution cycles:
- Auto-Execute to Goal: Enable the Auto-Execute toggle to let the Copilot run the workflow, observe execution outputs, and self-correct until the desired assertion or business goal passes.
- Midway Steering & Interruption: While the AI is actively running tools, you can type additional instructions in the input box (e.g., "Use port 8080 instead") without cancelling the task. The Copilot absorbs your correction in real-time.
- Thinking Chain Visibility: Full support for reasoning models (e.g. DeepSeek-R1). The stream parser extracts
<think> tags and provides an interactive collapsible thinking block.
- High-Risk Safety Confirmations: Destructive operations like clearing the canvas, deleting multiple nodes, or installing third-party marketplace plugins require explicit user approval before execution.
One-Click Error Diagnosis & Self-Healing
When a protocol node fails (HTTP 500, WebSocket handshake timeout, assertion mismatch):
- Click AI Diagnosis & Fix directly on the failed node's error banner or in the Execution History panel.
- The Copilot analyzes upstream inputs, output payload schemas, and underlying error chains.
- It identifies root causes (e.g., missing Authorization header, expired token, JSONPath extraction error) and suggests a 1-click automated fix on the canvas.
Local Project Workspace Awareness
Connect your local codebase repository (via Link Workspace) to empower the AI Copilot with repository context:
- Project Structure Scanning: Detects frameworks, OpenAPI / Swagger specs, and config files.
- Code Search & Snippet Reading: Discovers backend route signatures, payload DTOs, and environment contracts to accurately configure node URLs and request bodies.
- Multimodal Screenshot Upload: Paste or drag-and-drop screenshots of API documentation, Swagger UIs, or terminal error traces directly into the chat for instant visual comprehension.
CLI Commands
| Command |
Description |
Example |
mpe run |
Execute a flow file (.mpf) with optional environment and variable overrides |
mpe run -f flow.mpf -e staging --var TIMEOUT=5000 |
mpe validate |
Validate flow structure without executing |
mpe validate -f flow.mpf |
mpe debug |
Run with debug output (NDJSON protocol) and active environment |
mpe debug -f flow.mpf -e dev |
mpe stress |
Run multi-protocol stress test with environment resolution |
mpe stress run -f flow.mpf -e staging -u 50 -r 10 |
mpe env |
Manage global environments and local overrides (list, show, set, delete) |
mpe env list --format table |
mpe flow |
Manage and format flows (list, show, create, delete, fmt) |
mpe flow fmt -f flow.mpf --to yaml |
mpe report |
Manage reports (list, show, delete) |
mpe report list |
mpe run-node |
Execute a single protocol node for connectivity verification |
mpe run-node '{"type":"redis:connect","host":"127.0.0.1","port":6379}' |
mpe plugin |
Manage plugins (dir, list, install) |
mpe plugin list |
mpe ai |
Drive or query the AI Copilot from terminal (bridges with GUI or runs headless) |
mpe ai "Create a login flow that extracts JWT" |
mpe open |
Open a workflow file in the running GUI desktop app |
mpe open flow.mpf |
# View all commands
mpe --help
# Run flow with active environment and runtime variable overrides
mpe run -f flow.mpf -e staging --var TIMEOUT=5000 --var RETRIES=3
# Manage global environments
mpe env list --format table
mpe env show staging --source merged
mpe env set staging API_BASE=https://api.staging.com --local
mpe env delete staging --local
# Format flow file or convert between YAML and JSON
mpe flow fmt ./flows/hello.mpf --to yaml
mpe flow fmt --all --to yaml
# View command-specific help
mpe run --help
mpe env --help
CI/CD Integration (GitHub Actions)
mpe run can export execution results as standard JUnit XML, which is natively understood by Jenkins, GitHub Actions, and GitLab CI. Each node in the flow becomes a <testcase>; failed and skipped nodes map to <failure> and <skipped> elements.
# Export a JUnit XML report for CI consumers with staging environment
mpe run -f flow.mpf -e staging --report-format junit --report-file results.xml
# Or print the JUnit XML directly to stdout (no --report-file)
mpe run -f flow.mpf --report-format junit
Add a workflow step in your repository to run flows and publish the report on pull requests with dorny/test-reporter:
name: Flow Tests
on:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run MPE flows
run: |
mpe run -f tests/flows/api-smoke.mpf --report-format junit --report-file results.xml
- name: Publish test report
if: always()
uses: dorny/test-reporter@v1
with:
name: MPE Flow Results
path: results.xml
reporter: java-junit
fail-on-error: 'true'
Note
The JUnit report reflects node-level results: assertion, HTTP, WebSocket, and other protocol nodes map to individual test cases. A failed node produces a non-zero exit code, so plain mpe run --report-format junit --report-file results.xml also fails the job by default.
Output Format
All commands output JSON to stdout:
{
"success": true,
"data": null,
"error": null,
"execution_time": 156,
"node_reports": [
{
"node_uuid": "entry-001",
"node_type": "entry",
"node_name": "Start",
"status": "success",
"duration_ms": 1,
"used_port_id": "out"
},
{
"node_uuid": "http-001",
"node_type": "http:request",
"node_name": "Fetch Data",
"status": "success",
"duration_ms": 233,
"output_data": {
"success": true,
"status": 200,
"body": { ... },
"headers": { ... }
}
}
],
"flow_report": {
"flow_name": "Hello World",
"total_nodes": 3,
"executed_count": 3,
"status": "success",
"duration_ms": 234
}
}
Exit Codes
| Code |
Meaning |
0 |
Success |
1 |
Failure (error details in stderr and JSON output) |
2 |
Invalid arguments |
Flow File Structure
MPE flow files use the .mpf extension and follow the FlowFile wrapper format. Flow files default to YAML format (with seamless backwards compatibility for existing JSON files):
_version: 1
flow:
uuid: my-flow
name: My Flow
initial_variables:
api_base: https://api.example.com
token: abc123
nodes:
# Array of protocol and logic nodes ...
connections:
# Array of directed connections ...
Top-level Fields
| Field |
Type |
Required |
Description |
_version |
number |
No |
File format version (default: 1) |
flow |
object |
Yes |
Flow data container |
Flow Fields
| Field |
Type |
Required |
Description |
uuid |
string |
Yes |
Unique flow identifier |
name |
string |
Yes |
Human-readable flow name |
initial_variables |
object |
No |
Flow fallback variables available via {{key}} syntax. Serves as base layer in the resolution cascade. |
nodes |
array |
Yes |
List of nodes |
connections |
array |
Yes |
Connections between nodes |
Decoupled Environment Storage
Environment definitions are no longer embedded inside .mpf flow files. Environment files are managed independently as standalone YAML files under <flows_dir>/environments/ (e.g. dev.yaml, staging.yaml, prod.yaml). Any legacy embedded environment fields in older flow files are ignored upon loading.
Node Structure
uuid: node-uuid
type: http:request
name: My HTTP Request
data:
type: http:request
url: https://api.example.com
method: get
| Field |
Type |
Required |
Description |
uuid |
string |
Yes |
Unique node identifier |
type |
string |
Yes |
Node type (e.g. http, entry) |
name |
string |
Yes |
Display name |
data |
object |
Yes |
Node configuration. Must include "type" matching the node type |
on_error |
string |
No |
Error strategy: "route_to_false" (default), "ignore", "abort_flow" |
Important: data.type Field
Every data object must include "type": "<node_type>" for Rust deserialization. The value must match the node's top-level type field.
Connections
Connections define the execution flow between nodes.
id: conn-1
source_node_uuid: entry-1
target_node_uuid: http-1
source_port_id: out
target_port_id: in
| Field |
Description |
source_node_uuid |
UUID of the source node |
target_node_uuid |
UUID of the target node |
source_port_id |
Output port: out (single output), true/false (dual output) |
target_port_id |
Must be "in" for all nodes |
Critical Port Rules
target_port_id must always be "in" (not "input")
- Nodes with dual output (
true/false) should have both ports connected to prevent flow termination on failure
entry uses out, end has no output
Port Reference
| Node Type |
Input |
Output |
entry | none | out |
end | in | none |
http | in | true / false |
conditional | in | true / false |
script | in | true / false |
assertion | in | true / false |
variable:extractor | in | out |
ws_*, tcp_*, udp_*, sse:connect/sse:listen, graphql:connect/query/subscribe/introspect | in | true / false |
sse:disconnect, graphql:disconnect, proxy | in | out |
Variables
Variable Pool vs Node Output
{{var}} syntax can only access the Variable Pool. Protocol node outputs are stored separately in last_node_output and do NOT automatically become variables.
To use node output data with {{var}}, you must first extract it using a variable:extractor or script node.
Syntax
| Syntax |
Description |
{{variable_name}} |
Reference a variable from the pool |
{{obj.field}} |
Access nested field with dot notation |
Adding Variables to the Pool
- Initial variables: Define in
flow.initial_variables
- variable:extractor: Extract from
last_node_output using JSONPath
- script: Use
fn.variables.set(name, value)
// variable:extractor example
{
"type": "variable:extractor",
"data": {
"type": "variable:extractor",
"output_mappings": [
{ "source": "$.body.id", "target": "user_id" },
{ "source": "$.status", "target": "http_status" }
]
}
}
// Later use: {{user_id}}, {{http_status}}
Built-in Dynamic Variables
MPE Flow includes dynamic template variables evaluated at runtime on each step execution:
| Variable |
Description |
Example Output |
{{$timestamp}} |
Current Unix timestamp in milliseconds |
1772867200123 |
{{$isoTimestamp}} |
Current UTC timestamp in ISO 8601 format |
2026-09-07T12:00:00.000Z |
{{$guid}} / {{$uuid}} |
Random UUID v4 |
a3f89021-4b12-42fe-b58f-287517c2f829 |
{{$randomInt}} |
Random 6-digit integer (0-999999) |
482103 |
{{$randomAlphaNumeric}} |
Random 8-character alphanumeric string |
k9X2mQ8L |
Conditional / Assertion Paths
conditional and assertion nodes read directly from last_node_output:
| Node |
Path Format |
Example |
conditional.field_path |
Plain name, {{var}}, or $.path |
status, $.body.id |
assertion.target |
JSONPath with $. prefix (required) |
$.status, $.body.data.id |
Limitations
- Only dot notation:
{{obj.field.subfield}}
- No array indexing:
{{items[0]}} — not supported
- No function calls:
{{func()}} — not supported
Global Environments & Secret Layering
MPE Flow decouples environment variables from individual workflow files. Environments are stored as standalone YAML files under <flows_dir>/environments/ (default: ~/.app/flows/environments/, or configured via MPE_ENVIRONMENTS_DIR), allowing variable sets such as dev, staging, and prod to be shared across all workflows.
Two-File Secret Architecture
Every environment supports a two-file structure designed for version control safety:
- Shared Base File (
<name>.yaml): Contains team-wide non-sensitive configuration (base URLs, service ports, retry policies). Check this into Git along with workflows.
- Machine-Local Override (
<name>.local.yaml): Merged transparently on top of the shared file on the local machine. Ideal for developer-specific credentials, tokens, and private keys. Add *.local.yaml to your .gitignore to prevent accidental secret leakage.
# <flows_dir>/environments/staging.yaml (committed to Git)
name: staging
description: Staging Environment
variables:
API_URL: https://staging-api.example.com
TIMEOUT_MS: 5000
MOCK_MODE: false
# <flows_dir>/environments/staging.local.yaml (git-ignored on local machine)
name: staging
variables:
AUTH_TOKEN: dev_secret_bearer_token_xyz987
CLIENT_SECRET: sk_staging_99214a8f
4-Tier Variable Resolution Cascade
When executing, debugging, or stress-testing a flow, variables are resolved through a strict 4-tier cascading priority:
| Priority |
Layer |
Scope & Source |
Typical Usage |
| 1 (Lowest) |
Flow Fallback |
flow.initial_variables in .mpf |
Default baseline values so the flow remains runnable when no environment is active. |
| 2 |
Shared Environment |
<name>.yaml in environments directory |
Team-wide environment endpoints, common timeouts, and feature flags. |
| 3 |
Local Override |
<name>.local.yaml in environments directory |
Machine-private secrets, personal API tokens, or local proxy overrides. |
| 4 (Highest) |
Runtime Override |
CLI --var KEY=VALUE or explicit run input |
One-off dynamic overrides from CI/CD jobs or automation scripts. |
Variable Reference Syntax
Variables resolved from all layers are accessible directly via bare keys: {{API_URL}} or nested fields {{USER.id}}. Do NOT prefix with env. (e.g. {{env.API_URL}} is invalid and will not be found).
GUI Environment Controls
- Sidebar Environments Panel: A resizable section in the left sidebar displaying all discovered environments, active state indicators, and quick actions (activate, rename, duplicate, delete).
- Canvas Toolbar Selector: Switch the active environment directly from the top canvas bar without leaving your workflow.
- Environment Manager Dialog:
- Global Mode: Edit environment YAML directly with inline syntax highlighting, toggle local override layers, and manage secrets safely.
- Canvas Mode: Compare flow fallback variables side-by-side against the active environment in a Cascade Comparison Matrix, visually inspecting which layer wins for every key.
- Unified Debug & Stress Execution: Interactive single runs, step debugging (F10/F11), and high-concurrency stress tests all automatically inherit and merge the active environment.
- Flow Export with Environment Bake: When sharing a flow with external parties, export can optionally bake active environment variables into
flow.initial_variables. An integrated Credential Scanner automatically detects sensitive keywords (key, secret, token, password, auth, cred, cert, private) and prompts you to redact them before export.
Storage Configuration
The environments directory defaults to <flows_dir>/environments and can be configured through multiple methods:
- GUI: Settings → Storage → Environments Directory (
environmentsDir).
- Environment Variable:
MPE_ENVIRONMENTS_DIR=/custom/path/environments
- Configuration File: Set
storage.environments_dir in config.toml (located at $MPE_CONFIG or user data directory).
CLI Environment Management (mpe env)
Manage environments in headless CI/CD environments without opening the desktop GUI:
# List all environments with variable counts and local override flags
mpe env list --format table
# Inspect variables in an environment (merged, base, or local layer)
mpe env show staging --source merged
mpe env show staging --source base
mpe env show staging --source local
# Set variables in the shared base file
mpe env set staging API_URL=https://staging.example.com TIMEOUT_MS=6000
# Set sensitive credentials in the local private override (.local.yaml)
mpe env set staging AUTH_TOKEN=secret_token_12345 --local
# Delete a local override layer or the entire environment
mpe env delete staging --local
mpe env delete staging
Running Workflows with Environments
Pass -e, --env <NAME> to mpe run, mpe debug, or mpe stress run:
# Run flow against staging environment
mpe run -f flows/checkout.mpf -e staging
# Run with environment plus one-off runtime overrides
mpe run -f flows/checkout.mpf -e staging --var TIMEOUT_MS=10000 --var NOTIFY=true
# Run stress test against staging environment
mpe stress run -f flows/checkout.mpf -e staging -u 100 -r 15
Entry & End Nodes
Entry Node
Starting point of every flow. Exactly one required.
{
"uuid": "entry-1",
"type": "entry",
"name": "Start",
"data": { "type": "entry" }
}
End Node
Terminal node. Flow ends when reaching end.
{
"uuid": "end-1",
"type": "end",
"name": "End",
"data": { "type": "end" }
}
Subflow Node
Subflow nodes enable modular workflow reuse by embedding another workflow into the current flow. Child flows can be referenced by workspace flow ID or relative file path.
Canvas Drag & Drop
You can drag any workflow file directly from the Sidebar Flow List onto the canvas to instantly create a Subflow node pre-configured to target that flow. Double-clicking a Subflow node opens the target workflow in a new tab within the FlowTabBar without losing your active drafting or debugging context.
{
"uuid": "subflow-1",
"type": "flow:sub",
"name": "User Authentication Subflow",
"data": {
"type": "flow:sub",
"target_flow_id": "7b6c3d9a-1234-5678-abcd-ef0123456789",
"target_flow_path": "./subflows/auth.mpf",
"inherit_all_variables": true,
"variable_mappings": [
{ "source": "parent_username", "target": "login_user" },
{ "source": "parent_password", "target": "login_pass" }
],
"export_all_variables": true,
"output_mappings": [
{ "source": "access_token", "target": "auth_token" }
]
}
}
Configuration Fields
| Field |
Type |
Default |
Description |
target_flow_id | string (optional) | null | UUID of a saved workflow in the workspace (used in GUI mode). |
target_flow_path | string (optional) | null | Relative or absolute file path to a .mpf, .yaml, or .json workflow file (portable & CLI mode). |
inherit_all_variables | boolean | true | Whether the child subflow automatically inherits all parent scope variables. |
variable_mappings | array | [] | Explicit input parameter mappings (source expression/variable → target child variable). Overrides inherited variables. |
export_all_variables | boolean | true | Whether child workflow output variables are exported back into parent workflow scope after execution completes. |
output_mappings | array | [] | Explicit return variable mappings (source child variable → target parent variable). Used when selective export is desired. |
Cycle Detection & Safety
MPE Flow's DAG compiler validates subflow references before execution. Self-references and circular dependencies (A → B → A) are detected during pre-flight validation and rejected with descriptive error reports, preventing infinite recursion.
Nested Execution Reports
When a subflow executes, its child node execution metrics, step statuses, and duration are preserved in a hierarchical execution report tree viewable in the Execution History panel.
HTTP Node
Send HTTP requests. Supports GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.
{
"uuid": "http-1",
"type": "http:request",
"name": "API Call",
"data": {
"type": "http:request",
"url": "{{api_base}}/users",
"method": "post",
"headers": {
"Authorization": "Bearer {{token}}"
},
"body_type": "body",
"content_type": "json",
"body": "{\"name\": \"test\"}",
"timeout_ms": 30000
}
}
Core Fields
| Field |
Type |
Default |
Description |
url |
string |
— |
Request URL (supports {{var}}) |
method |
string |
get |
HTTP method |
headers |
object |
{} |
Request headers |
body_type |
string |
none |
none / body / form-data / x-www-form-urlencoded / binary |
content_type |
string |
json |
json / text / html / xml |
timeout_ms |
number |
30000 |
Timeout (1000–300000ms) |
HTTP Output
{
"success": true,
"status": 200,
"body": { "id": 1, "name": "test" },
"headers": { "content-type": "application/json" },
"timing": { "connect_ms": 45, "total_ms": 150 }
}
Conditional Node
Branch execution based on conditions. Routes to true or false port.
Simple Condition
{
"uuid": "cond-1",
"type": "conditional",
"name": "Check Status",
"data": {
"type": "conditional",
"condition_type": "simple",
"field_path": "status",
"operator": "equal",
"expected_value": 200
}
}
Advanced Expression
{
"data": {
"type": "conditional",
"condition_type": "advanced",
"advanced_expression": "status >= 200 && status < 300"
}
}
Operators
| Operator |
Description |
equal | Equals |
not_equal | Not equals |
greater | Greater than |
greater_or_equal | Greater or equal |
less | Less than |
less_or_equal | Less or equal |
contains | Contains substring |
starts_with | Starts with |
ends_with | Ends with |
exists | Field exists |
Script Node
Execute JavaScript code in a QuickJS runtime (ES2020 subset).
{
"uuid": "script-1",
"type": "script",
"name": "Process Data",
"data": {
"type": "script",
"script": "const data = JSON.parse(fn.response.raw); fn.variables.set('user_id', data.body.id);",
"timeout_ms": 5000
}
}
Script API
| API |
Description |
fn.variables.get(name) | Read variable from pool |
fn.variables.set(name, value) | Write variable to pool |
fn.response.raw | Full upstream node output (JSON string) |
fn.response.code | HTTP status code (HTTP nodes only) |
fn.console.log(msg) | Log to execution output |
fn.flow.stop(reason) | Stop flow execution |
fn.util.encodeBase64(str) | Base64 encode |
fn.util.md5(str) | MD5 hash |
fn.util.sha256(str) | SHA-256 hash |
fn.util.uuid() | Generate UUID v4 |
fn.util.sleep(ms) | Pause execution |
Assertion Node
Validate conditions with detailed pass/fail reporting.
{
"uuid": "assert-1",
"type": "assertion",
"name": "Validate Response",
"data": {
"type": "assertion",
"mode": "all",
"assertions": [
{ "id": "a1", "description": "Status is 200", "target": "$.status", "operator": "eq", "expected": 200 },
{ "id": "a2", "description": "Has data", "target": "$.body.data", "operator": "exists" }
]
}
}
Assertion Operators
| Category |
Operators |
| Comparison | eq, ne |
| Numeric | gt, gte, lt, lte |
| String | contains, not_contains, matches |
| Existence | exists, not_exists |
| Collection | in, not_in |
| Type | type_is |
| Range | between |
Target Path Format
Assertion target must use JSONPath with $. prefix (e.g. $.status, $.body.id). Do not add body. prefix for non-HTTP nodes.
WebSocket Nodes
Three nodes for WebSocket communication: connect, send/collect, close.
// Connect
{ "type": "ws:connect", "data": { "type": "ws:connect", "url": "wss://echo.example.com/ws" } }
// Send and collect response
{ "type": "ws:send_collect", "data": {
"type": "ws:send_collect",
"connection_id": "ws-connect-uuid",
"message": "{\"type\":\"ping\"}",
"collect_timeout_ms": 10000
}
}
// Close
{ "type": "ws:close", "data": { "type": "ws:close", "connection_id": "ws-connect-uuid" } }
Other Protocols
TCP
{ "type": "tcp:connect", "data": { "type": "tcp:connect", "host": "localhost", "port": 8080 } }
{ "type": "tcp:send", "data": { "type": "tcp:send", "connection_id": "uuid", "data": "Hello" } }
{ "type": "tcp:receive", "data": { "type": "tcp:receive", "connection_id": "uuid" } }
{ "type": "tcp:close", "data": { "type": "tcp:close", "connection_id": "uuid" } }
UDP
{ "type": "udp:bind", "data": { "type": "udp:bind", "local_addr": "0.0.0.0", "local_port": 8888 } }
{ "type": "udp:send_to", "data": { "type": "udp:send_to", "connection_id": "uuid", "data": "Hello", "target_addr": "192.168.1.100", "target_port": 9999 } }
{ "type": "udp:recv_from", "data": { "type": "udp:recv_from", "connection_id": "uuid" } }
{ "type": "udp:close", "data": { "type": "udp:close", "connection_id": "uuid" } }
SSE (Server-Sent Events)
{ "type": "sse:connect", "data": { "type": "sse:connect", "url": "https://api.example.com/events" } }
{ "type": "sse:listen", "data": { "type": "sse:listen", "connection_id": "uuid", "max_events": 100 } }
{ "type": "sse:disconnect", "data": { "type": "sse:disconnect", "connection_id": "uuid" } }
GraphQL
{ "type": "graphql:connect", "data": { "type": "graphql:connect", "endpoint": "https://api.example.com/graphql" } }
{ "type": "graphql:query", "data": { "type": "graphql:query", "connection_id": "uuid", "query": "query { users { id name } }" } }
{ "type": "graphql:subscribe", "data": { "type": "graphql:subscribe", "connection_id": "uuid", "query": "subscription { newUser { id } }" } }
{ "type": "graphql:introspect", "data": { "type": "graphql:introspect", "connection_id": "uuid" } }
{ "type": "graphql:disconnect", "data": { "type": "graphql:disconnect", "connection_id": "uuid" } }
Plugin Protocols
More protocols (Redis, MySQL/PostgreSQL, MongoDB, SMTP, gRPC, MCP) are available as installable plugins — see Plugin Development below.
Visual Debugging & Controls
MPE Flow provides an interactive visual step debugger built into both the desktop canvas and CLI. You can pause workflow execution, inspect node inputs/outputs, modify variable pools in flight, and bypass flaky endpoints.
Breakpoints
Click the red breakpoint dot on any canvas node to toggle a breakpoint. When debugging starts, execution proceeds until a breakpoint node is reached, pausing execution before the node runs.
Debug Actions & Global Shortcuts
| Action |
Shortcut |
Description |
| Continue | F5 / Resume | Resume execution until the next breakpoint or workflow completion. |
| Step Over | F10 | Execute the current node and pause at the immediate next node. For Subflow nodes, runs the child flow completely. |
| Step Into | F11 | Step inside a flow:sub node to pause at its first internal node. On regular nodes, behaves as Step Over. |
| Step Out | Shift + F11 | Execute the remainder of the current subflow and pause at the successor node in the parent flow. |
| Skip Node | — | Bypass current node execution. Choose an active output branch (e.g. true / false) and supply mock output data. |
| Run to End | — | Ignore all remaining breakpoints and finish the workflow execution. |
| Stop Debugging | — | Terminate the debug session and generate a partial execution report. |
Subflow Stepping & Call Stack Tracking
When orchestrating complex nested workflows, MPE Flow's debugger supports multi-level call frame tracking (DebugCallFrame) with hierarchical stepping.
Stepping into Subflows
When execution pauses at a Subflow node:
- Press Step Into (F11): The canvas seamlessly switches context to the child workflow, highlighting its entry step with a paused indicator.
- Press Step Over (F10): If you prefer treating the subflow as an atomic black box, Step Over runs the entire child flow and pauses at the parent flow's next node.
- Press Step Out (Shift + F11): If you are deep inside a subflow and have inspected the critical logic, Step Out completes the remaining child nodes and returns control to the parent caller.
Hierarchical Call Stack Navigation
The Execution Stack panel displays a live call frame hierarchy during nested subflow debugging:
#0 Root Flow (Order Ingestion) → Subflow Node: Process Payment
#1 Process Payment (Payment Pipeline) → Subflow Node: Auth Token
#2 Auth Token (OAuth2 Refresh) → HTTP Request: /oauth/v2/token [PAUSED]
Clicking any parent frame in the stack navigates the canvas to that frame's workflow and displays its local variable pool snapshot, allowing you to inspect parent and child state simultaneously.
Plugin Overview
Plugins extend MPE Flow with new protocol node types. A plugin runs as a sidecar process that communicates with the host over stdio using JSON-RPC 2.0 — one JSON message per line (LF-delimited, CRLF tolerated). At startup the host spawns each plugin process and performs a describe handshake, registering the declared node types into the shared NodeRegistry. Plugin nodes behave identically to built-in nodes in flows, the executor, and execution reports.
Installation
Place a plugin directory containing a plugin.json manifest into the plugin directory (default get_data_dir()/plugins; %APPDATA%/multi-protocol-flow-executor/plugins on Windows; override with the MPE_PLUGIN_DIR environment variable). The host scans synchronously at startup: invalid manifests are skipped without crashing, and a plugin whose describe fails 3 consecutive times is quarantined for the host's lifetime.
plugin.json Fields
| Field |
Description |
name | Plugin name (convention: directory name) |
version | Semantic version |
description | Human-readable description |
min_host_version | Optional version gate for host compatibility |
entry | command + args used to launch the plugin; interpreter plugins are first-class citizens (e.g. "python" + ["plugin.py"]) |
env | Environment variables injected into the plugin process |
permissions | Reserved; ignored in P0 |
capabilities.streaming | true = resident process; false = spawned on demand and recycled after 60s idle |
capabilities.single_node | Node-level capability; true = runnable standalone via mpe run-node for connectivity verification |
locales / default_locale | UI language declarations; the host injects the active locale via MPE_LOCALE |
Protocol Plugins in This Repository
The plugins/ directory ships protocol plugins for: amqp, db (SQLite/MySQL/PostgreSQL), grpc, imap, kafka, mcp, mongo, mqtt, redis, smtp. Install by building a plugin and placing its directory into the plugin directory, or install from the Plugins page.
SDK & Development
The SDK and wire contract live in the standalone public repository multi-protocol-flow/mpe-plugin-sdk. The Rust SDK is first-class; lightweight Python and Node templates are also provided. Wire types are shared from mpe-plugin-sdk::protocol, so host and plugin never drift.
Development Flow
- Clone the SDK repository
- Implement the
Plugin trait (describe + execute; optional flow_ended to release per-execution resources)
- Build the sidecar binary
- Place it into the plugin directory
Reference Docs
- SDK repository:
docs/plugin-guide.md (EN) / docs/plugin-guide.zh-CN.md (中文) and docs/plugin-protocol.md
- Host repository:
docs/plugin-architecture.md, docs/plugin-perf.md (visible under the multi-protocol-flow GitHub organization)
Marketplace
Plugins can be installed from the GUI settings panel's Plugin Market tab, or from the CLI:
mpe plugin dir
mpe plugin list
mpe plugin install <name> [--version <v>] [--registry <url>] [--dir <dir>] [--force]
Registry Contract
GET {base}/plugins returns the plugin list; each entry has a platform asset (zip) url and an optional sha256 — when provided the host enforces checksum validation and refuses mismatched installs.
- Platform keys:
windows-x64 | windows-arm64 | linux-x64 | linux-arm64 | macos-x64 | macos-arm64.
- The zip root must contain a single top-level directory named after the plugin, holding
plugin.json and the plugin files.
The registry base URL is configured via the MPE_PLUGIN_REGISTRY environment variable or the --registry flag. The public registry is served from GitHub Pages (multi-protocol-flow.github.io/mpe-plugin-registry) as a static JSON implementing this contract — browse and install plugins on the Plugins page.