terminator-mcp-agent
v0.23.36
Published
Cross-platform Model Context Protocol (MCP) agent for Terminator
Readme
Terminator MCP Agent
A Model Context Protocol (MCP) server that provides desktop GUI automation capabilities using the Terminator library. This server enables LLMs and agentic clients to interact with Windows, macOS, and Linux applications through structured accessibility APIs—no vision models or screenshots required.
Quick Install
Claude Code
Install with a single command:
claude mcp add terminator "npx -y terminator-mcp-agent@latest" -s userCursor
Copy and paste this URL into your browser's address bar:
cursor://anysphere.cursor-deeplink/mcp/install?name=terminator-mcp-agent&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInRlcm1pbmF0b3ItbWNwLWFnZW50Il19Or install manually:
- Open Cursor Settings (
Cmd/Ctrl + ,) - Go to the MCP tab
- Add server with command:
npx -y terminator-mcp-agent
HTTP Endpoints (when running with -t http)
GET /health: Always returns 200 while the process is alive.GET /status: Busy-aware probe for load balancers. Returns JSON and appropriate status:- 200 when idle:
{ "busy": false, "activeRequests": 0, "maxConcurrent": 1, "lastActivity": "<ISO-8601>" } - 503 when busy:
{ "busy": true, "activeRequests": 1, "maxConcurrent": 1, "lastActivity": "<ISO-8601>" } - Content-Type is
application/json.
- 200 when idle:
POST /mcp: MCP execution endpoint. Enforces single-request concurrency per machine by default.
Concurrency is controlled by the MCP_MAX_CONCURRENT environment variable (default 1). Only accepted POST /mcp requests are counted toward activeRequests. If the server is at capacity, new POST /mcp requests return 503 immediately. This 503 behavior is intentional so an Azure Load Balancer probing GET /status can take a busy VM out of rotation and route traffic elsewhere.
Getting Started
The easiest way to get started is to use the one-click install buttons above for your specific editor (VS Code, Cursor, etc.).
Alternatively, you can install and configure the agent from your command line.
1. Install & Configure Automatically Run the following command and select your MCP client from the list:
npx -y terminator-mcp-agent@latest --add-to-app2. Manual Configuration If you prefer, you can add the following to your MCP client's settings file:
{
"mcpServers": {
"terminator-mcp-agent": {
"command": "npx",
"args": ["-y", "terminator-mcp-agent@latest"]
}
}
}Command Line Interface (CLI) Execution
For automation workflows and CI/CD pipelines, you can execute workflows directly from the command line using the Terminator CLI:
Quick Start:
# Execute a workflow file
terminator mcp run workflow.yml
# With verbose logging
terminator mcp run workflow.yml --verbose
# Dry run (validate without executing)
terminator mcp run workflow.yml --dry-run
# Use specific MCP server version
terminator mcp run workflow.yml --command "npx -y terminator-mcp-agent@latest"
# Run specific steps (requires step IDs in workflow)
terminator mcp run workflow.yml --start-from "step_12" --end-at "step_13"
# Run single step
terminator mcp run workflow.yml --start-from "read_json" --end-at "read_json"
# Execute jumps at end boundary (by default jumps are skipped at --end-at-step)
terminator mcp run workflow.yml --end-at "step_5" --execute-jumps-at-endWorkflow File Formats:
Direct workflow format (workflow.yml):
steps:
- tool_name: navigate_browser
arguments:
url: "https://example.com"
- tool_name: click_element
arguments:
selector: "role:Button && name:Submit"
stop_on_error: true
include_detailed_results: trueWith conditional jumps (workflow_with_jumps.yml):
steps:
- tool_name: validate_element
id: check_logged_in
arguments:
selector: "role:Button && name:Logout"
jumps:
- if: "check_logged_in_status == 'success'"
to_id: main_app
reason: "User already logged in - skipping authentication"
- tool_name: click_element
id: login_flow
arguments:
selector: "role:Button && name:Login"
# ... more login steps ...
- tool_name: click_element
id: main_app
arguments:
selector: "role:Button && name:Dashboard"Tool call wrapper format (workflow.json):
{
"tool_name": "execute_sequence",
"arguments": {
"steps": [
{
"tool_name": "navigate_browser",
"arguments": {
"url": "https://example.com"
}
}
]
}
}Code Execution in Workflows (engine mode):
Execute custom JavaScript or Python with access to desktop automation APIs via run_command.
Passing Data Between Workflow Steps:
When using engine mode, data automatically flows between steps:
steps:
# Step 1: Return data directly (NEW - simplified!)
- tool_name: run_command
arguments:
engine: "javascript"
run: |
// Get file info (example)
const filePath = 'C:\\data\\report.pdf';
const fileSize = 1024;
console.log(`Found file: ${filePath}`);
// Just return fields directly - they auto-merge into env
return {
status: 'success',
file_path: filePath, // Becomes env.file_path
file_size: fileSize // Becomes env.file_size
};
# Step 2: Access data automatically
- tool_name: run_command
arguments:
engine: "javascript"
run: |
// env is automatically available - no setup needed!
console.log(`Processing: ${env.file_path} (${env.file_size} bytes)`);
// Workflow variables also auto-available
console.log(`Config: ${variables.max_retries}`);
// NEW: Direct variable access also works!
console.log(`Processing: ${file_path} (${file_size} bytes)`);
console.log(`Config: ${max_retries}`);
// Continue with desktop automation
const elements = await desktop.locator('role:button').all();
// Return more data (auto-merges to env)
return {
status: 'success',
file_processed: env.file_path,
buttons_found: elements.length
};Important Notes on Data Passing:
- NEW:
envandvariablesare automatically injected into all scripts - NEW: Non-reserved fields in return values auto-merge into env (no
set_envwrapper needed) - NEW: Valid env fields are also available as individual variables (e.g.,
file_pathinstead ofenv.file_path) - Reserved fields that don't auto-merge:
status,error,logs,duration_ms,set_env - Data passing only works with
enginemode (JavaScript/Python), NOT with shell commands - Backward compatible: explicit
set_envstill works if needed - Individual variable names must be valid JavaScript identifiers (no spaces, special chars, or reserved keywords)
- Watch for backslash escaping issues in Windows paths (may need double escaping)
- Consider combining related operations in a single step if data passing becomes complex
For complete CLI documentation, see Terminator CLI README.
Tool Documentation
All tool documentation is maintained as source of truth in the codebase to ensure accuracy and prevent duplication.
Where to Find Tool Documentation
System Instructions (Strategic Guidance):
- File:
src/prompt.rs - What: High-level patterns, common pitfalls, environment variable safety, focus management
- Lines: 4-44
Tool Descriptions (Complete API Reference):
- File:
src/server.rs - What: Complete descriptions of all 35 tools including parameters, examples, error handling, and usage patterns
- Each tool has: Inline documentation in
#[tool(description = "...")]macros
Tool Index:
- See Tool Reference Guide for a complete index with line numbers
Quick Examples
Find a tool: Search server.rs for the tool name (e.g., click_element, run_command)
Example - Click Element (server.rs:1250):
- Comprehensive actionability validation documentation
- Error types explained (ElementNotVisible, NotEnabled, NotStable, etc.)
- When to use
invoke_elementinstead
Example - Run Command (server.rs:1659):
- Shell vs engine mode
- Optional element detection patterns
- Variable safety and data passing
Example - Execute Browser Script (server.rs:4789):
- Browser DOM access
- Variable injection patterns
- Script file loading
Why Source Code Documentation?
- Single Source of Truth: Documentation stays synchronized with implementation
- No Duplication: Update once, accurate everywhere
- Always Current: Code changes include documentation updates
- Type Safe: Parameter schemas embedded in code
Local Development
To build and test the agent from the source code:
# 1. Clone the entire Terminator repository
git clone https://github.com/mediar-ai/terminator
# 2. Navigate to the agent's directory
cd terminator/terminator-mcp-agent
# 3. Install Node.js dependencies
npm install
# 4. Build the Rust binary and Node.js wrapper
npm run build
# 5. To use your local build in your MCP client, link it globally
npm install --global .Now, when your MCP client runs terminator-mcp-agent, it will use your local build instead of the published npm version.
Troubleshooting
- Make sure you have Node.js installed (v16+ recommended).
- For VS Code/Insiders, ensure the CLI (
codeorcode-insiders) is available in your PATH. - If you encounter issues, try running with elevated permissions.
Version Compatibility Issues
Problem: "missing field items" or schema mismatch errors
Solution: Ensure you're using the latest MCP server version:
# Force latest version in CLI
terminator mcp run workflow.yml --command "npx -y terminator-mcp-agent@latest"
# Update MCP client configuration to use @latest
{
"mcpServers": {
"terminator-mcp-agent": {
"command": "npx",
"args": ["-y", "terminator-mcp-agent@latest"]
}
}
}
# Clear npm cache if needed
npm cache clean --forceCLI Integration Issues
Problem: CLI commands not working or connection errors
Solution: Test MCP connectivity step by step:
# Test basic connectivity
terminator mcp exec get_applications
# Test with verbose logging
terminator mcp run workflow.yml --verbose
# Test with dry run first
terminator mcp run workflow.yml --dry-run
# Use HTTP connection for debugging
terminator mcp run workflow.yml --url http://localhost:3000/mcpJavaScript Execution Issues
Problem: JavaScript code fails or can't access desktop APIs
Solution: Verify JavaScript execution and API access:
# Test basic JavaScript execution via run_command engine mode
terminator mcp exec run_command '{"engine": "javascript", "run": "return {test: true};"}'
# Test desktop API access with node engine
terminator mcp exec run_command '{"engine": "node", "run": "const elements = await desktop.locator(\\\"role:button\\\").all(); return {count: elements.length};"}'
# Test Python engine
terminator mcp exec run_command '{"engine": "python", "run": "return {\\\"py\\\": True}"}'
# Debug with verbose logging
terminator mcp run workflow.yml --verboseWorkflow File Issues
Problem: Workflow parsing errors or unexpected behavior
Solution: Validate workflow structure:
# Validate workflow syntax
terminator mcp run workflow.yml --dry-run
# Test with minimal workflow first
echo 'steps: [{tool_name: get_applications}]' > test.yml
terminator mcp run test.yml
# Check both YAML and JSON formats work
terminator mcp run workflow.yml # YAML
terminator mcp run workflow.json # JSONPlatform-Specific Issues
Windows:
- Ensure Windows UI Automation APIs are available
- Run with administrator privileges if accessibility features are restricted
- Check Windows Defender/antivirus isn't blocking automation
macOS:
- Grant accessibility permissions in System Preferences > Security & Privacy
- Ensure the terminal/IDE has accessibility access
- Check macOS version compatibility (10.14+ recommended)
Linux:
- Ensure AT-SPI (assistive technology) is enabled
- Install required packages:
sudo apt-get install at-spi2-core - Check desktop environment compatibility (GNOME, KDE, XFCE supported)
Virtual Display Support (Headless VMs)
Terminator MCP Agent includes virtual display support for running on headless VMs without requiring RDP connections. This enables scalable automation on cloud platforms like Azure, AWS, and GCP.
How It Works:
The agent automatically detects headless environments and initializes a virtual display context that Windows UI Automation APIs can interact with. This allows full UI automation capabilities even when no physical display or RDP session is active.
Activation:
Virtual display activates automatically when:
- Environment variable
TERMINATOR_HEADLESS=trueis set - No console window is available (common in VM/container scenarios)
- Running as a Windows service or scheduled task
Configuration:
# Enable virtual display mode
export TERMINATOR_HEADLESS=true
# Run the MCP agent
npx -y terminator-mcp-agentUse Cases:
- Running multiple automation agents on VMs without RDP overhead
- CI/CD pipelines in cloud environments
- Scalable automation farms on Azure/AWS/GCP
- Containerized automation workloads
Requirements:
- Windows Server 2016+ or Windows 10/11
- .NET Framework 4.7.2+
- UI Automation APIs available (included in Windows)
The virtual display manager creates a memory-based display context that satisfies Windows UI Automation requirements, enabling terminator to enumerate and interact with UI elements as if a physical display were present.
Performance Optimization
Large UI Trees:
- Use specific selectors instead of broad element searches
- Implement delays between rapid operations
- Consider using
include_tree_after_action: falsefor intermediate steps - For tree extraction tools, optimize with:
tree_max_depth: 30- Limit depth for large treestree_from_selector: "role:List"- Get subtree from specific elementtree_from_selector: "true"- Start from focused elementtree_output_format: "compact_yaml"- Readable format (default) or"verbose_json"for full data
JavaScript Performance:
- Use
quickjsengine for lightweight operations - Use
nodejsengine only when full APIs are needed - Implement
sleep()delays in loops to prevent overwhelming the UI
For additional help, see the Terminator CLI documentation or open an issue on GitHub.
📚 Full execute_sequence Reference & Sample Workflow
Why another example? The quick start above shows the concept, but many users asked for a fully-annotated workflow schema. The example below automates the Windows Calculator app—so it is 100% safe to share and does not reveal any private customer data. Feel free to copy-paste and adapt it to your own application.
1. Anatomy of an execute_sequence Call
{
"tool_name": "execute_sequence",
"arguments": {
"variables": {
// 1️⃣ Re-usable inputs with type metadata
"app_path": {
"type": "string",
"label": "Calculator EXE Path",
"default": "calc.exe"
},
"first_number": {
"type": "string",
"label": "First Number",
"default": "42"
},
"second_number": {
"type": "string",
"label": "Second Number",
"default": "8"
}
},
"inputs": {
// 2️⃣ Concrete values for *this run*
"app_path": "calc.exe",
"first_number": "42",
"second_number": "8"
},
"selectors": {
// 3️⃣ Human-readable element shortcuts
"calc_window": "role:Window && name:Calculator",
"btn_clear": "role:Button && name:Clear",
"btn_plus": "role:Button && name:Plus",
"btn_equals": "role:Button && name:Equals"
},
"steps": [
// 4️⃣ Ordered actions & control flow
{
"tool_name": "open_application",
"arguments": { "path": "${{app_path}}" }
},
{
"tool_name": "click_element", // 4a. Make sure the UI is reset
"arguments": { "selector": "${{selectors.btn_clear}}" },
"continue_on_error": true
},
{
"group_name": "Enter First Number", // 4b. Groups improve logs
"steps": [
{
"tool_name": "type_into_element",
"arguments": {
"selector": "${{selectors.calc_window}}",
"text_to_type": "${{first_number}}"
}
}
]
},
{
"tool_name": "click_element",
"arguments": { "selector": "${{selectors.btn_plus}}" }
},
{
"group_name": "Enter Second Number",
"steps": [
{
"tool_name": "type_into_element",
"arguments": {
"selector": "${{selectors.calc_window}}",
"text_to_type": "${{second_number}}"
}
}
]
},
{
"tool_name": "click_element",
"arguments": { "selector": "${{selectors.btn_equals}}" }
},
{
"tool_name": "wait_for_element", // 4c. Capture final UI tree
"arguments": {
"selector": "${{selectors.calc_window}}",
"condition": "exists",
"include_tree": true,
"timeout_ms": 2000
}
}
],
"output_parser": {
// 5️⃣ Turn the tree into clean JSON
"javascript_code": "// Extract calculator display value\nconst results = [];\n\nfunction findElementsRecursively(element) {\n if (element.attributes && element.attributes.role === 'Text') {\n const item = {\n displayValue: element.attributes.name || ''\n };\n results.push(item);\n }\n \n if (element.children) {\n for (const child of element.children) {\n findElementsRecursively(child);\n }\n }\n}\n\nfindElementsRecursively(tree);\nreturn results;"
}
}
}2. Key Concepts at a Glance
- Variables vs. Inputs – Declare once, override per-run. This is perfect for parameterizing CI pipelines or A/B test data.
- Selectors – Give every important UI element a nickname. It makes long workflows readable and easy to maintain.
- Templating –
${{ ... }}(GitHub Actions-style) or legacy{{ ... }}lets you reference any key insidevariables,inputs, orselectors. Both syntaxes are supported; the engine uses Mustache-style rendering. - Groups & Control Flow – Add
group_name,skippable,if, orcontinue_on_errorto any step for advanced branching. - Output Parsing – Always end with a step that includes the UI tree, then use the declarative JSON DSL to mine the data you need.
3. State Persistence & Partial Execution
The execute_sequence tool supports powerful features for workflow debugging and resumption:
Partial Execution with Step Ranges
You can run specific portions of a workflow using start_from_step and end_at_step parameters:
{
"tool_name": "execute_sequence",
"arguments": {
"url": "file://path/to/workflow.yml",
"start_from_step": "read_json_file", // Start from this step ID
"end_at_step": "fill_journal_entries", // Stop after this step (inclusive)
"follow_fallback": false, // Don't follow fallback_id beyond end_at_step (default: false)
"execute_jumps_at_end": false // Don't execute jumps at end_at_step boundary (default: false)
}
}Examples:
- Run single step: Set both
start_from_stepandend_at_stepto the same ID - Run step range: Set different IDs for start and end
- Run from step to end: Only set
start_from_step - Run from beginning to step: Only set
end_at_step - Debug without fallback: Use
follow_fallback: falseto prevent jumping to troubleshooting steps when a bounded step fails - Allow jumps at boundary: Use
execute_jumps_at_end: trueto execute jump conditions even at theend_at_stepboundary (by default, jumps are skipped at the boundary for predictable execution)
Automatic State Persistence
When using file:// URLs, the workflow state (environment variables) is automatically saved to a .mediar folder:
- State is saved after each step that modifies environment variables via
set_envor has a tool result with an ID - State is loaded when starting from a specific step
- Location:
.mediar/workflows/<workflow_name>/state.jsonin the workflow's directory - Tool results from all tools (not just scripts) are automatically stored as
{step_id}_resultand{step_id}_status
This enables:
- Debugging: Run steps individually to inspect state between executions
- Recovery: Resume failed workflows from the last successful step
- Testing: Test specific steps without re-running the entire workflow
Data Passing Between Steps
Steps can pass data using multiple methods:
1. Tool Result Storage (NEW)
ALL tools with an id field automatically store their results in the environment:
steps:
# Any tool with an ID stores its result
- id: check_apps
tool_name: get_applications
arguments:
include_tree_after_action: false
# Access the result in JavaScript
- tool_name: run_command
arguments:
engine: javascript
# Tree Parameter Examples - Performance Optimization
- tool_name: get_window_tree
arguments:
pid: 1234
tree_max_depth: 2 # Only get 2 levels deep
- tool_name: get_window_tree
arguments:
pid: 1234
tree_from_selector: "role:Dialog" # Start tree from first dialog
tree_max_depth: 3 # Limit depth from that point
# Backward compatible - still works
- tool_name: get_window_tree
arguments:
pid: 1234
include_tree_after_action: true # Simple boolean form
run: |
// Direct variable access - auto-injected!
const apps = check_apps_result || [];
const status = check_apps_status; // "success" or "error"
console.log(`Found ${apps[0]?.applications?.length} apps`);2. Script Return Values
Steps can pass data using the set_env mechanism in run_command with engine mode:
// Step 12: Read and process data
return {
set_env: {
file_path: "C:/data/input.json",
journal_entries: JSON.stringify(entries),
total_debit: "100.50",
},
};
// Step 13: Use the data (NEW - simplified access!)
const filePath = file_path; // Direct access, no {{env.}} needed!
const entries = JSON.parse(journal_entries);
const debit = total_debit;4. Running the Workflow
- Ensure the Terminator MCP agent is running (it will auto-start in supported editors).
- Send the JSON above as the body of an
execute_sequencetool call from your LLM or test harness. - Inspect the response: if parsing succeeds you'll see something like
Realtime events (SSE)
When running with the HTTP transport, you can subscribe to realtime workflow events at a separate endpoint outside /mcp:
- SSE endpoint:
/events - Emits JSON payloads for:
sequence(start/end),sequence_progress, andsequence_step(begin/end)
Example in Node.js:
import EventSource from "eventsource";
const es = new EventSource("http://127.0.0.1:3000/events");
es.onmessage = (e) => console.log("event", e.data);{
"parsed_output": {
"displayValue": "50" // 42 + 8
}
}5. Working with Tool Results
Every tool that has an id field automatically stores its result for use in later steps:
steps:
# Capture browser DOM
- id: capture_dom
tool_name: execute_browser_script
arguments:
selector: "role:Window"
script: "return document.documentElement.innerHTML;"
# Validate an element exists
- id: check_button
tool_name: validate_element
arguments:
selector: "role:Button && name:Submit"
# Use both results in script
- tool_name: run_command
arguments:
engine: javascript
run: |
// All tool results are auto-injected as variables
const dom = capture_dom_result?.content || '';
const buttonExists = check_button_status === 'success';
if (buttonExists) {
const button = check_button_result[0]?.element;
console.log(`Submit button at: ${button?.bounds?.x}, ${button?.bounds?.y}`);
}
return { dom_length: dom.length, has_button: buttonExists };Tool results are accessible as:
{step_id}_result: The tool's return value (content, element info, etc.){step_id}_status: Either "success" or "error"
6. Tips for Production Workflows
- Never hard-code credentials – use environment variables or your secret manager.
- Keep workflows short – <100 steps is ideal. Break large tasks into multiple sequences.
- Capture errors –
continue_on_erroris useful, but also check{step_id}_statusfor tool failures. - Version control – Store workflow JSON in a repo and use PR reviews just like regular code.
- Use step IDs – Give meaningful IDs to steps whose results you'll need later.
🔍 Troubleshooting & Debugging
Finding MCP Server Logs
MCP logs are saved to:
- Windows:
%LOCALAPPDATA%\claude-cli-nodejs\Cache\<encoded-project-path>\mcp-logs-terminator-mcp-agent\ - macOS/Linux:
~/.local/share/claude-cli-nodejs/Cache/<encoded-project-path>/mcp-logs-terminator-mcp-agent/
Where <encoded-project-path> is your project path with special chars replaced (e.g., C--Users-username-project).
Note: Logs are saved as .txt files, not .log files.
Read logs:
# Windows - Find and read latest logs (run in PowerShell)
Get-ChildItem (Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'claude-cli-nodejs\Cache\*\mcp-logs-terminator-mcp-agent\*.txt') | Sort-Object LastWriteTime -Descending | Select-Object -First 1 | Get-Content -Tail 50Enable Debug Logging
In your Claude MCP configuration (claude_desktop_config.json):
{
"mcpServers": {
"terminator-mcp-agent": {
"command": "path/to/terminator-mcp-agent",
"env": {
"LOG_LEVEL": "debug", // or "info", "warn", "error"
"RUST_BACKTRACE": "1" // for stack traces on errors
}
}
}
}Common Debug Scenarios
| Issue | What to Look For in Logs |
| --------------------- | ----------------------------------------------------------------- |
| Workflow failures | Search for fallback_id triggers and critical_error_occurred |
| Element not found | Look for selector resolution attempts, find_element timeouts |
| Browser script errors | Check for EVAL_ERROR, Promise rejections, JavaScript exceptions |
| Binary version issues | Startup logs show binary path and build timestamp |
| MCP connection lost | Check for panic messages, ensure binary path is correct |
Fallback Mechanism
Workflows support fallback_id to handle errors gracefully:
- If a step fails and has
fallback_id, it jumps to that step instead of stopping - Without
fallback_id, errors may setcritical_error_occurredand skip remaining steps - Use
troubleshooting:section for recovery steps only accessed via fallback
Need more help? Browse the examples under
examples/in this repo or open a discussion on GitHub.
Documentation
Workflow Development
- Workflow Output Structure: Detailed documentation on the expected output structure for workflows, including:
- How to structure
parsed_outputfor proper CLI rendering - Success/failure indicators and business logic validation
- Data extraction patterns and error handling
- Integration with CLI and backend systems
- How to structure
Additional Resources
- CLI Documentation: Command-line interface for executing workflows
- Examples: Sample workflows and use cases
- API Reference: Core Terminator library documentation
