Terminator CLI
The Terminator CLI is a powerful command-line tool for managing the Terminator project, including version management, releases, Azure VM deployment, and MCP server interaction.
Features
- 📦 Version Management: Bump and sync versions across all packages
- 🏷️ Release Automation: Tag and release with a single command
- ☁️ Azure VM Deployment: One-liner to deploy Windows VMs with MCP server
- 🤖 MCP Client: Chat with MCP servers over HTTP or stdio
- 🔄 Workflow Execution: Run automation workflows from YAML/JSON files
- 🔧 Tool Execution: Execute individual MCP tools directly
- 🔒 Secure by Default: Auto-generated passwords, configurable security rules
Installation
Windows (Recommended - npm wrapper):
# Run directly without installation
# Or install globally
macOS / Linux (Compile from Source):
⚠️ The npm package @mediar-ai/cli only includes Windows binaries. Other platforms must compile from source.
# From the workspace root
# Install globally (optional)
Quick Start
Version Management
# Bump version
# Sync all package versions
# Show current status
# Tag and push
# Full release (bump + tag + push)
MCP Workflow Execution
Execute automation workflows from YAML or JSON files:
# Execute a workflow file
# Execute with verbose logging
# Dry run (validate without executing)
# Use specific MCP server command
# Use HTTP MCP server
# Pass input values to workflow (available as env variables in scripts)
# Combine inputs with other options
Passing Input Values to Workflows
The --inputs parameter allows you to pass initial values to your workflow that can be accessed by JavaScript/Python scripts:
# Pass inputs as JSON
These inputs are accessible in your workflow scripts:
- In JavaScript: via
env.inputsobject or directly asenv.username,env.count, etc. - In Python: via
envdictionary - Inputs override any default values defined in the workflow file
Example workflow file (workflow.yml):
tool_name: execute_sequence
arguments:
steps:
- tool_name: navigate_browser
arguments:
url: "https://example.com"
- tool_name: click_element
arguments:
selector: "role:Button|name:Submit"
- tool_name: get_applications_and_windows_list
id: get_apps
- tool_name: run_command
engine: javascript
id: extract_pid
run: |
const apps = get_apps_result[0]?.applications || [];
const focused = apps.find(app => app.is_focused);
return { pid: focused?.pid || 0 };
- tool_name: get_window_tree
arguments:
pid: "{{extract_pid.pid}}"
id: capture_result
output_parser:
ui_tree_source_step_id: capture_result
javascript_code: |
// Extract all checkbox names
const results = [];
function findElementsRecursively(element) {
if (element.attributes && element.attributes.role === 'CheckBox') {
const item = {
name: element.attributes.name || ''
};
results.push(item);
}
if (element.children) {
for (const child of element.children) {
findElementsRecursively(child);
}
}
}
findElementsRecursively(tree);
return results;
JavaScript execution in workflows:
tool_name: execute_sequence
arguments:
steps:
- tool_name: run_command
arguments:
engine: "javascript"
run: |
// Access inputs passed from CLI via --inputs parameter
console.log(`Processing for user: ${env.username}`);
console.log(`Count value: ${env.count}`);
// Or access the entire inputs object
const allInputs = env.inputs;
console.log(`All inputs:`, JSON.stringify(allInputs));
// Use inputs in your logic
for (let i = 0; i < env.count; i++) {
console.log(`Processing item ${i + 1} for ${env.username}`);
}
return {
processed_by: env.username,
items_processed: env.count
};
Original example:
tool_name: execute_sequence
arguments:
steps:
- tool_name: run_command
arguments:
engine: "node"
script: |
// Access desktop automation APIs
const elements = await desktop.locator('role:button').all();
log(`Found ${elements.length} buttons`);
// Interact with UI elements
for (const element of elements) {
const name = await element.name();
if (name.includes('Submit')) {
await element.click();
break;
}
}
return {
buttons_found: elements.length,
action: 'clicked_submit'
};
MCP Tool Execution
Execute individual MCP tools directly:
# Execute a single tool
# Execute with arguments
# Use different MCP server
Interactive MCP Chat
Chat with MCP servers interactively:
# Start chat session (uses local MCP server by default)
# Chat with remote MCP server
# Chat with specific MCP server command
Control Remote Computer Through Chat
- Run the MCP server on your remote machine
- Open port or use ngrok
- Connect via CLI:
Advanced Usage
MCP Server Connection Options
The CLI supports multiple ways to connect to MCP servers:
# Local MCP server (default - uses @latest for compatibility)
# Specific version
# HTTP server
# Custom server command
Workflow File Formats
The CLI supports both YAML and JSON workflow files:
Direct workflow (workflow.yml):
steps:
- tool_name: navigate_browser
arguments:
url: "https://example.com"
stop_on_error: true
Tool call wrapper (workflow.json):
Error Handling
# Continue on errors
# Custom timeout
# Detailed error output
Code Execution in Workflows
The CLI supports executing code within workflows using the run_command tool in engine mode, providing access to desktop automation APIs:
Available Engines:
nodejs- Full Node.js runtime with desktop APIsquickjs- Lightweight JavaScript engine (default)
Desktop APIs Available:
// Element discovery
const elements = await desktop..;
const element = await desktop..;
// Element interaction
await element.;
await element.;
await element.;
// Property access
const name = await element.;
const bounds = await element.;
const isEnabled = await element.;
// Utilities
; // Logging
await ; // Delay in milliseconds
Example Use Cases:
# Conditional logic based on UI state
- tool_name: run_command
arguments:
engine: "node"
script: |
const submitButton = await desktop.locator('role:button|name:Submit').first();
const isEnabled = await submitButton.enabled();
if (isEnabled) {
await submitButton.click();
return { action: 'submitted' };
} else {
log('Submit button is disabled, checking form validation...');
return { action: 'validation_needed' };
}
# Bulk operations on multiple elements
- tool_name: run_command
arguments:
engine: "javascript"
script: |
const checkboxes = await desktop.locator('role:checkbox').all();
let enabledCount = 0;
for (const checkbox of checkboxes) {
await checkbox.setToggled(true);
enabledCount++;
await sleep(50); // Small delay between operations
}
return { total_enabled: enabledCount };
# Dynamic element discovery and interaction
- tool_name: run_command
arguments:
engine: "javascript"
script: |
// Find all buttons containing specific text
const buttons = await desktop.locator('role:button').all();
const targets = [];
for (const button of buttons) {
const name = await button.name();
if (name.toLowerCase().includes('download')) {
targets.push(name);
await button.click();
await sleep(1000);
}
}
return { downloaded_items: targets };
Configuration
Environment Variables
RUST_LOG: Set logging level (e.g.,debug,info,warn,error)MCP_SERVER_URL: Default MCP server URLMCP_SERVER_COMMAND: Default MCP server command
Default Behavior
- MCP Server: Uses
npx -y terminator-mcp-agent@latestby default - Logging: Info level by default, debug with
--verbose - Error Handling: Stops on first error by default
- Format: Auto-detects YAML/JSON from file extension
Troubleshooting
Version Mismatch Issues
If you encounter "missing field" errors, ensure you're using the latest MCP server:
# Force latest version
# Clear npm cache if needed
Connection Issues
# Test MCP server connectivity
# Use verbose logging for debugging
# Test with dry run first
JavaScript Execution Issues
# Test JavaScript execution capability via run_command (engine mode)
# Use node engine for full APIs
# Run Python with terminator.py
# Debug JavaScript errors with verbose logging
For more examples and advanced usage, see the Terminator MCP Agent documentation.