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, both native desktop GUI packages (.deb / .AppImage) and standalone headless CLI binaries are available.
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
MPE Flow provides native desktop GUI packages as well as lightweight headless CLI binaries for Linux:
Option 1: Debian / Ubuntu (.deb)
Download the .deb package from the Changelog and install:
sudo dpkg -i mpe_*_amd64.deb || sudo apt-get install -f
Option 2: Portable AppImage (.AppImage)
Download the portable .AppImage from the Changelog, make it executable, and launch:
chmod +x MPE_*.AppImage
./MPE_*.AppImage
Option 3: Headless CLI (tar.gz)
For Docker containers, headless servers, and CI/CD pipelines:
# Download and extract CLI
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
# Move to PATH
chmod +x mpe
sudo mv mpe /usr/local/bin/
# Verify
mpe --help
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 ReAct Execution & Execution Limits
When solving complex workflows, the AI Copilot iteratively plans, acts, observes, and refines:
- Mandatory Verification by Default: Newly assembled pipelines automatically execute and verify outputs (
batch_create_pipeline defaults auto_run: true), ensuring workflows work out of the box.
- Configurable Iteration Limits (
aiMaxIterations): Customize the maximum ReAct reasoning iterations in AI Settings (clamped to 5–100, default 20, with quick presets for 10, 20, 40, and 60 rounds). If the model reaches the limit without completing, it pauses gracefully and delivers actionable next steps.
- 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 with physics animation.
- 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.
Slash Commands & Interactive Tools
MPE Flow enhances AI Copilot productivity with fast keyboard shortcuts and human-in-the-loop tool collaboration:
- Slash Commands (
/): Type / in the Copilot chat input to summon the autocomplete command overlay:
/fix — Auto-diagnose and apply a 1-click self-healing repair for the currently selected or latest failed node.
/explain — Analyze the topology, variable bindings, and edge transitions of the active flow or selected nodes.
/test — Generate assertion nodes and validation boundaries tailored to current upstream protocol responses.
- Interactive User Inquiries (
ask_user): When the model requires human decisions (e.g., selecting authentication scopes, providing tokens, choosing between branching topologies), it emits an interactive choice card with optional submission support. Tool execution suspends cleanly until you respond.
- Fast AI Provider Switching: Switch active AI providers directly from the chat input box, panel header, or Spotlight palette (
Ctrl+K / Cmd+K) without opening Settings.
- Upstream Variable Extractor Backtracking: When extracting variables, the engine automatically backtracks through incoming connections (skipping assertions or conditionals) to bind to the nearest data producer.
- Secure Sandboxed Terminal (
terminal_exec): The AI Copilot can execute shell commands against your linked workspace with strict command whitelisting (e.g., git status, npm test, cargo test), requiring explicit one-time confirmation before running high-risk commands.
- Full Trust Mode: Power users can enable Full Trust Mode in AI Settings to execute multi-step agentic workflows without repetitive manual confirmation cards.
- Reasoning Physics Animation: Displays real-time thinking token metrics and a physics droplet loading indicator while models generate internal thought traces.
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 serve |
Run a long-lived Ingress mock gateway daemon with Tokio HTTP dispatch and graceful SIGTERM teardown |
mpe serve -f ingress_flow.mpf -p 9527 |
mpe mcp serve |
Host native Model Context Protocol (MCP) server on port 9528 or per-flow slug route |
mpe mcp serve -f flow.mpf --transport stdio |
mpe flow convert |
Losslessly convert between declarative YAML DSL and standard .mpf format |
mpe flow convert flow.mpf flow.yaml |
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, markdown summary and standalone HTML export
mpe run -f flow.mpf -e staging --summary summary.md --export-html report.html
# Start a long-running Ingress mock daemon service
mpe serve -f webhook_mock.mpf -p 9527
# Publish flow as native MCP server over stdio for Claude Desktop / Cursor
mpe mcp serve -f search_tool.mpf --transport stdio
# Convert between standard .mpf and declarative human-readable YAML DSL
mpe flow convert flow.mpf declarative.yaml
mpe flow convert declarative.yaml flow.mpf
# 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 serve --help
mpe mcp serve --help
mpe env --help
CI/CD Integration (GitHub Actions)
mpe run provides first-class CI/CD support with standardized exit codes, 3-stage aligned JUnit XML suites ({flow} :: Setup, {flow} :: Main, {flow} :: Teardown), automatic Markdown summaries for $GITHUB_STEP_SUMMARY, and self-contained interactive HTML reports.
# Export JUnit XML and GitHub Actions step summary
mpe run -f flow.mpf -e staging \
--report junit --report-file results.xml \
--summary "$GITHUB_STEP_SUMMARY" \
--export-html report.html
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 junit --report-file results.xml \
--summary "$GITHUB_STEP_SUMMARY" \
--export-html report.html
- 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'
3-Stage Aligned Test Suites
The JUnit XML report partitions results into aligned test suites: {flow} :: Setup, {flow} :: Main, and {flow} :: Teardown. Node execution outputs and failure snapshots are embedded directly inside failure traces.
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: All executed nodes and assertions passed |
1 |
Test Failure: One or more protocol nodes or assertions failed |
2 |
Configuration Error: Invalid CLI arguments, missing flow file, or schema validation failure |
3 |
Infrastructure Failure: Network unreachable, connection refused, or step timeout |
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 comprising the Main business stage |
connections |
array |
Yes |
Directed connections between Main stage nodes |
setup |
object |
No |
Optional FlowSubGraph ({ nodes: [], connections: [] }) executed once before Main. Exports variables into Main context. |
teardown |
object |
No |
Optional FlowSubGraph executed once after Main for cleanup and notifications. Receives the read-only $report variable. |
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
Flow Lifecycle Stages (Setup / Main / Teardown)
MPE Flow treats workflow lifecycles as a first-class three-stage architecture. Instead of rigid callback hooks, every stage is a full visual sub-graph accepting the entire multi-protocol node suite:
Hooks Replaced by Teardown Subgraphs
Legacy Flow Hooks (EntryConfig.hooks, webhook/script triggers) were completely removed in MPE Flow 0.5.0-beta. The Teardown stage replaces them with native nodes (HTTP calls, assertion steps, scripts, database queries) featuring visual routing, variable interpolation, and debug inspection.
Three Lifecycle Stages
| Stage |
Field |
Execution Frequency |
Purpose & Variable Flow |
| Setup |
flow.setup |
Runs once before Main |
Authentication pre-warming, token retrieval, test data seeding. Any variables exported by Setup nodes merge directly into the Main stage variable context. |
| Main |
flow.nodes / flow.connections |
Single run / Looped per VU / Consumed per row |
The primary business flow under test. In stress testing, virtual users loop only the Main stage to keep QPS, P50, and P99 latency pure. |
| Teardown |
flow.teardown |
Runs once after Main |
Alert notifications, webhook web-posting, resource teardown. Teardown inherits Main variables and receives the read-only $report variable. |
The $report Variable in Teardown
The Teardown stage automatically receives the full Main stage execution report via the read-only reserved variable $report:
{{$report.status}} — Execution status ("success", "failed", "cancelled").
{{$report.duration_ms}} — Execution duration of the Main business flow in milliseconds.
{{$report.node_reports}} — Individual node execution records, outputs, and assertion statuses.
# Example: Slack / Discord notification in Teardown HTTP node payload
{
"channel": "#ci-alerts",
"text": "E2E Run Finished with status: {{$report.status}} in {{$report.duration_ms}}ms"
}
Stage Failure & Execution Semantics
- Setup Fails: If Setup encounters an unhandled error or a node routes to
false, execution skips Main (stages.main.status == "skipped") and jumps straight to Teardown to clean up initialized resources.
- Teardown Fails: If Main succeeds but Teardown fails, the overall flow execution is marked as failed, but
stages.main.status preserves its "success" state — allowing you to isolate operational teardown glitches from application regressions.
- Deterministic Root Order: In Setup and Teardown, unlinked root nodes (zero indegree) execute deterministically from top-to-bottom and left-to-right based on canvas
(y, x) coordinates. An amber pill warns if multiple unlinked roots exist.
Canvas Stage Tab Bar
The canvas features a segmented stage pill bar at the top-left (Setup | Main | Teardown). Clicking a stage switches the canvas viewport and isolates node operations, drag-and-drop additions, and undo/redo history to that specific stage.
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.
Static Exported Variable Inference
MPE Flow features an intelligent static analysis engine that inspects target child workflows to deduce all variables exported by downstream extractors and scripts. The Subflow configuration panel surfaces an Expected Exported Variables preview card with 1-click clipboard copying, softening canvas variable-missing warnings when dynamic parameters are reliably produced by subflows.
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 }
}
HTTP Ingress & Webhook Mock Services
MPE Flow enables local webhook simulation and embedded mock HTTP backends natively inside flows without running external web servers like Express or Nginx.
http:ingress — HTTP Server Listener
The http:ingress node spins up an embedded HTTP server listening on a dedicated port. It captures incoming webhook payloads, callback requests, and query parameters, prioritizing them as root execution triggers.
uuid: ingress-1
type: http:ingress
name: Webhook Receiver
data:
type: http:ingress
port: 8080
path: /api/v1/webhook/order
method: post
timeout_ms: 30000
| Field | Type | Default | Description |
port | number | 8080 | Local TCP port to bind the embedded listener |
path | string | / | URL route path to match (e.g. /webhook) |
method | string | any | Expected HTTP method (get, post, put, delete, any) |
timeout_ms | number | 30000 | Maximum duration to wait for an incoming HTTP request before timing out |
Ingress Node Output
Downstream nodes receive the parsed request details from last_node_output:
{{request_body}} — Parsed JSON or raw text body sent by the external client.
{{headers}} — Request headers (e.g. x-webhook-signature, authorization).
{{query}} — URL query parameters as key-value pairs.
{{connection_id}} — Opaque connection handle required by http:respond.
http:respond — HTTP Mock Response
The http:respond node pairs with http:ingress using the connection_id to return a custom HTTP response back to the waiting client.
uuid: respond-1
type: http:respond
name: Send 200 OK
data:
type: http:respond
connection_id: "{{connection_id}}"
status_code: 200
headers:
Content-Type: application/json
body: '{"status": "accepted", "order_id": "{{order_id}}"}'
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.
Interactive Flow Slice Execution & Human-in-the-Loop
MPE Flow allows executing partial workflow subgraphs or isolated nodes on demand without needing to run the full end-to-end pipeline from an Entry node.
Run Selected Nodes (run_slice)
Select one or more nodes on the canvas (using shift-click or box selection), right-click, and choose Run Selected Nodes:
- Zero-Indegree Root Discovery: The execution compiler automatically detects nodes within the selection that have no incoming edges from other selected nodes, treating them as execution roots. No Entry node is required.
- Clean Execution History: Slice runs execute ephemerally in-memory, avoiding cluttering persistent execution history with temporary exploratory runs.
- Targeted Retries: Test changes to downstream scripts, transformations, or assertions instantly without re-triggering slow upstream database queries or external API rate limits.
Human-in-the-Loop (HITL) Interactive Cards
Workflow steps or AI agents can request human intervention during runtime via the ask_human bridge:
- Execution Pause: When a HITL prompt is encountered, execution suspends cleanly and presents an interactive input card in the ExecutionPanel.
- Prompt Submissions: Users can enter textual input, select choices, or approve sensitive actions. Once submitted, the response is injected into downstream node variables and execution resumes automatically.
- Slice Badges & Controls: Running slices feature visual status badges and immediate abort controls.
Full Lifecycle Debugging & Stage Sync
The visual debug engine seamlessly orchestrates execution across all three lifecycle stages: Setup → Main → Teardown.
Automatic Stage Following
When stepping through a workflow (F10/F11) or resuming to a breakpoint located in Setup or Teardown:
- The canvas automatically transitions to the active stage of the paused node, eliminating manual tab switching.
- The canvas tab bar indicates real-time per-stage status (
completed, paused, waiting).
- If you navigate to another stage while paused, a persistent notification banner offers a 1-click jump back to the paused node's stage.
- During Teardown debugging, the
$report variable snapshot containing the completed Main stage execution report is immediately inspectable in the Variable Pool panel.
Native MCP Server Engine & Shared Gateway
MPE Flow features an embedded native Model Context Protocol (MCP) Server engine directly inside the host runtime (flow-engine/src/mcp_server). You can publish any multi-protocol workflow as an AI agent tool callable by LLM clients (Cursor, Claude Desktop, local agents) without authoring adapter code or installing external plugin binaries.
Native Host Architecture
Unlike third-party sidecars, MCP Server hosting is built directly into the MPE core runtime. It natively supports high-performance Streamable HTTP and stdio transports, zero-copy variable serialization, and automatic parameter schema derivation.
Port 9528 Shared Gateway
By default, published flows register onto a centralized shared gateway running on port 9528 (next to the Ingress listener 9527, configurable in Settings → Network):
- Unified Discovery (
/mcp): Connect once from your AI client. Any published workflow is automatically registered and discovered as an available tool. When you publish new flows or edit existing ones, they appear immediately without reconfiguring your AI client.
- Path-Isolated Routing (
/{flow-slug}/mcp): Access a specific workflow exclusively with dedicated slug routing and independent request isolation.
- Dedicated Port Mode: Assign an explicit port to run a specific workflow as an isolated standalone MCP server.
Automatic Tool Schema Derivation
MPE Flow automatically converts workflow metadata into typed JSON Schema tool definitions:
- Tool Name: Derived from
flow.name (slugified with deterministic name de-duplication).
- Tool Description: Derived from
flow.description.
- Input Schema: Automatically inferred from
flow.initial_variables. Each variable name becomes an input parameter with description, expected type, and default value.
- Tool Execution Output: The final node output (e.g. from
variable:extractor, http:request, or script) is returned as the tool call result directly to the LLM context.
Canvas Export & Service Governance
1-Click Publishing from Canvas
To publish a workflow as an MCP tool in the desktop app:
- Right-click the active workflow tab in the top canvas bar, or click the flow options menu.
- Select Publish as MCP Server to open the Export MCP Server dialog.
- Review the generated tool name, description, and transport mode (Shared Gateway 9528, Dedicated Port, or stdio).
- Click Start Server to activate, or copy the client configuration snippet directly to your clipboard.
Service Governance in Settings
Open Settings → MCP Services to monitor and manage all published instances:
- Grouped by Endpoint: View active gateways and running tools in a clean, unified dashboard.
- Instance Durability: Registered services persist across app restarts and reboots. If a process stops, its row remains intact with a stopped indicator.
- Hot Reload: Click Reload to refresh a tool's logic directly from the updated flow file on disk without restarting the gateway.
- Direct Canvas Navigation: Clicking any flow name in the governance table immediately navigates to and opens that flow on the visual canvas.
Cursor & Claude Desktop Integration
MPE Flow generates pre-configured client configuration snippets with one click:
Option 1: Stdio Transport (Recommended for Cursor & Claude Desktop)
Communicates over standard input/output without opening network ports:
// Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
// Cursor: .cursor/mcp.json
{
"mcpServers": {
"mpe-search-tool": {
"command": "mpe",
"args": [
"mcp",
"serve",
"-f",
"/path/to/search_tool.mpf",
"--transport",
"stdio"
]
}
}
}
Option 2: Streamable HTTP (Shared Gateway)
Connect multiple AI clients to the shared daemon gateway on port 9528:
{
"mcpServers": {
"mpe-gateway": {
"url": "http://127.0.0.1:9528/mcp"
}
}
}
CLI MCP Management (mpe mcp)
Manage and automate MCP server processes headlessly from the command line:
# Serve a single flow over stdio (zero-port, process-based)
mpe mcp serve -f ./flows/weather_api.mpf --transport stdio
# Start shared gateway on port 9528 in background
mpe mcp start --gateway --port 9528 --detach
# Publish a workflow to the shared gateway
mpe mcp start -f ./flows/database_query.mpf --shared
# List all registered and active MCP server instances
mpe mcp list --all
# Inspect client configuration snippet for a published flow
mpe mcp config-snippet <INSTANCE_ID_OR_NAME>
# Hot reload an instance after modifying the flow file
mpe mcp reload <INSTANCE_ID_OR_NAME>
# Stop or unpublish an instance
mpe mcp stop <INSTANCE_ID_OR_NAME>
mpe mcp remove <INSTANCE_ID_OR_NAME>
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 Ecosystem
Protocol plugins operate as independent open-source repositories under the multi-protocol-flow organization (such as Kafka, Redis, MongoDB, gRPC, DB, SMTP, etc.). In addition, MPE Flow natively embeds an MCP Server hosting engine directly into the application core (served on port 9528 or custom ports), while external MCP client tools can be orchestrated via plugin or standard node connectors. Plugins can be installed from the GUI Plugin Market or 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.