M
Matthew Diakonov
12 min read

The free desktop automation tool that lets the assistant compile-check its own script before the first click

The category page for free desktop automation tools rarely asks the question that decides whether an AI-written workflow runs at all. Not the license, not the recorder, not the seat count. The question is whether the assistant can find out the file does not compile before the file gets executed against the operating system. Terminator answers it with a single MCP tool, typecheck_workflow, that runs tsc --noEmit against the workflow directory and returns a structured list of TypeError records with file, line, column, error code, message, and a seven-line code context window with a -> arrow on the offending line. Everything below walks through where that tool lives, what its parser regex looks like, and how the loop runs end-to-end.

5.0from MIT, MCP-native, structured pre-flight type-check
typecheck_workflow MCP tool runs bun tsc --noEmit, falls back to npx
Errors return as JSON: file, line, column, TS code, message, context
Seven-line context window with a -> arrow on the failing line

What every other roundup leaves out

The same dozen products show up on every list. AutoHotkey, AutoIt, SikuliX, Pywinauto, Robot Framework, Winium, WinAppDriver, Ui.Vision, Power Automate Desktop, UiPath Community Edition. The comparison axes are always license type, supported recorder, image matching vs. accessibility-based, Windows vs. cross-platform. None of those axes change once the operator stops being a person at the keyboard. When the operator is an AI coding assistant, the axis that matters is whether it can find out the workflow does not even compile before the workflow opens a window. Almost none of the tools in the category expose that signal at all. None of them expose it as a structured tool an MCP client can call.

AutoHotkey|no static type system, no compile checkAutoIt|Au3Check exists but is not MCP-exposedSikuliX|runtime-only Jython tracebacksPywinauto|no built-in pre-flight checkRobot Framework|--dryrun is a CLI flag, not an MCP toolWinium|WebDriver runtime errors onlyWinAppDriver|Appium errors onlyPower Automate Desktop|vendor designer, not exposed to MCPUiPath Community|Studio compiler, not exposed to MCPUi.Vision RPA|image-recognition runtime errors onlyTerminator|typecheck_workflow returns structured TypeError records

The anchor: one file, one regex, one fallback chain

The implementation lives in crates/terminator-mcp-agent/src/tools/typecheck.rs. It does four things in order. It validates that the path the assistant passed exists and that there is a tsconfig.json inside it. It picks a runner with a command_exists check that prefers bun and falls back to npx. It runs tsc --noEmit with the workflow directory as the working directory and captures both stdout and stderr. It parses the combined output with the regex ^(.+?)\((\d+),(\d+)\):\s*error\s+(TS\d+):\s*(.+)$ and, on failure only, enriches each error with a seven-line context window. The output is JSON the assistant reads on the same MCP turn that requested the check.

The function the MCP agent runs

This is the entire entry point. Validation, runner detection, spawn, parse, optional context enrichment, structured result. The return shape is what gets serialized back to the assistant as the tool result.

crates/terminator-mcp-agent/src/tools/typecheck.rs

The regex that turns one tsc line into a TypeError

Tsc emits errors in a single line format: path(line,col): error TSXXXX: message. The regex extracts the five capture groups, each one becomes a field on the TypeError struct, and lines that do not match are dropped silently. That last detail matters: a real tsc dump is full of noise (warnings, summaries, header lines), and the parser ignores everything that is not shaped like an error.

parse_tsc_output (typecheck.rs)

How the seven-line context window is built

The context is the bit a model needs most. A line and column number on its own is hard to act on. The function reads the file, slices three lines above and three below the failing line, and renders each one with a four-character marker: " -> " on the offending line and four spaces on the others. Line numbers are right-aligned in a four-wide gutter so the output stays a clean rectangle in the assistant's prompt.

get_error_context (typecheck.rs)

What the assistant sees on the wire

Here is one real failure shape. The workflow tries to call desktop.openApplication(7), which is wrong because openApplication takes a string. The compile check returns a single error with the exact line, the exact column, the exact error code, and the four lines around the failure rendered with the arrow marker.

typecheck_workflow result (one error)

With the loop vs. without the loop

Two ways to discover the same bug. One ends with the assistant reading a runtime stack trace after the workflow already opened a window or clicked into the wrong app. The other ends with the assistant patching one line in one file before the OS sees a single tool call.

Pre-flight type-check vs. find-out-at-runtime

The assistant writes the script, calls execute_sequence, the runtime crashes mid-step. The error surfaces as a stack trace inside a runtime log. The assistant has to parse the trace, guess the source file, and figure out the fix from a runtime error message that does not include the original line of code.

  • Workflow opens windows or clicks before the bug surfaces
  • Stack trace is the only signal
  • Source line is not in the error
  • Fix turn is reactive, not preventive

The exchange between assistant, agent, and tsc

Three actors, eleven messages, two compile passes. The MCP agent sits between the assistant and the local tsc binary. It is the piece that knows how to spawn the right runner, parse the output into structured errors, and merge in the code context. Every byte the assistant sees is shaped by it.

Assistant -> terminator-mcp-agent -> tsc

Assistantterminator-mcp-agenttsc (bun or npx)write_file('flows/x/src/workflow.ts', ...)ok, 1.2 kBtypecheck_workflow(workflow_path: 'flows/x')spawn bun tsc --noEmit (cwd: flows/x)stderr: workflow.ts(14,19): error TS2345 ...parse_tsc_output + enrich with 7-line context{ success: false, errors: [{...}], error_count: 1 }edit_file then typecheck_workflow againspawn bun tsc --noEmit (cwd: flows/x)exit 0, no errors{ success: true, errors: [], error_count: 0 }

What a real session looks like on stdio

One file write, one failed type-check, one fix, one passing type-check, one execute. The agent prints to stderr so you can watch the loop resolve in real time, even when the assistant runs it from a UI that hides the protocol.

claude_desktop -> terminator-mcp-agent (stderr tail)

One agent, every assistant, two possible runners

The MCP agent is a single binary. Every major AI coding assistant speaks the protocol. On the other side, the agent picks one of three outcomes per call: bun if it is in PATH, npx if it is not, an explicit error string if neither is installed. Nothing else is consulted, no env var lookup, no config file, no version negotiation.

typecheck_workflow as a hub between assistants and the local toolchain

Claude Code
Cursor
Codex
Windsurf
typecheck_workflow
bun tsc --noEmit
npx tsc --noEmit
Err: install bun or Node

The four steps that turn this on for any MCP client

There is no separate package, no settings panel, no opt-in flag. The type-check tool is registered in the same tool_router as click_element and get_window_tree. Once the agent is wired up, the assistant can call it in the same conversation it writes the workflow.

1

Install the MCP agent

Add one block to your assistant's MCP config that runs npx -y terminator-mcp-agent@latest. typecheck_workflow comes with it. No extra dependency, no separate install, no service.

2

Have the assistant scaffold a workflow

Ask your assistant to write a TypeScript workflow under a directory it picks. It will create a tsconfig.json (the type-checker requires one), a package.json that depends on @mediar-ai/terminator and @mediar-ai/workflow, and an src/workflow.ts file.

3

Have it call typecheck_workflow

Pass workflow_path: './path/to/the/dir'. The agent runs bun tsc --noEmit if bun is installed, else npx tsc --noEmit. The result is JSON: success, error_count, an array of TypeErrors with code context, and raw_output if anything failed.

4

Let the assistant fix and re-check

If the result has error_count > 0, the assistant patches the file and re-calls typecheck_workflow. Once success is true, it can call execute_sequence to actually run the workflow against the OS. The first click happens after the compile check passes, not before.

claude_desktop_config.json

The comparison row that belongs on every roundup

License is a price column. Recorder type is a feature column. Pre-flight compile-check, exposed as an MCP tool, callable by the same assistant that wrote the script, is the column that decides whether an AI-written workflow has any chance of running cleanly on the first try.

FeatureTypical free desktop automation toolTerminator
Compile-check the assistant's script before executionNot exposed; assistant has to run and crashtypecheck_workflow MCP tool, returns structured TypeError list
Error format the assistant reads backRuntime stack trace in stdout / stderrJSON: file, line, column, code (TS2345 etc.), message, 7-line context
Pre-flight tool runnerManual; the developer runs tsc / pylint / Au3Check by handBuilt into the same MCP session that wrote the workflow
Works from inside an AI coding assistantNo; tools predate MCP and the AI-author use caseYes; the tool is registered with #[tool(...)] in server.rs
Toolchain detectionHard-coded interpreter or nonecommand_exists check: bun first, npx fallback, error if neither
Cost of a single checkFree, but human-drivenFree, no API, runs local tsc, returns in one MCP call
LicenseMixed: MIT, GPL, Apache, vendor proprietary, dual-licensedMIT for runtime, MIT for MCP agent, MIT for typecheck tool

The numbers that describe the surface

The whole tool is small. 0 file (typecheck.rs), 0 regex capture groups (file, line, column, code, message), 0 lines of context per error (3 before, the offending line, 3 after), 0 possible runners (bun, npx), and 0 validation checks (path exists, tsconfig present, runner found, exit code success).

0 clicks before tsc

The first click of an AI-driven workflow should never be the moment you find out the script does not compile.

typecheck.rs, terminator-mcp-agent

Why this is the part the category page never covers

A roundup is graded on whether it lists the expected dozen products. A pre-flight tool the assistant calls before any side effect is outside that grade. But once you are installing a free desktop automation framework so an AI assistant can drive it, the ability to compile-check the assistant's own output is the difference between a workflow that runs and a workflow that crashes mid-step. typecheck_workflow is a 280-line file in a Rust crate and it does only one thing. That one thing is the gap most other tools have not noticed yet.

Install with claude mcp add terminator "npx -y terminator-mcp-agent@latest", ask your assistant to write a workflow, and ask it to call typecheck_workflow on the directory before it calls execute_sequence. The first response back from the agent will tell you whether the file compiles. The first click comes after that, not before.

Watch the compile-check loop run against your own workflow

Bring a TypeScript file (or none) and we will open a live MCP session, write a workflow together, run typecheck_workflow until it passes, and only then execute it. You will see exactly how the assistant reads the structured TypeError records back.

Frequently asked

Frequently asked questions

What does typecheck_workflow actually do?

It is an MCP tool exposed by terminator-mcp-agent. The model passes a workflow_path argument that points at a directory. The agent confirms the directory exists, confirms there is a tsconfig.json inside it, then shells out to either bun tsc --noEmit or npx tsc --noEmit (whichever exists in PATH). The combined stdout and stderr is parsed against the regex ^(.+?)\((\d+),(\d+)\):\s*error\s+(TS\d+):\s*(.+)$ and turned into a list of TypeError structs. Each error includes file, line, column, error code (TS2345, TS2304, etc.), and message. On failure the agent enriches each error with a code context window: three lines before the offending line, the offending line itself with a -> marker, and three lines after, with line numbers in the gutter. The whole thing returns as structured JSON to the assistant, which can read it on the same conversational turn it requested the check.

Why is this in the MCP agent at all? Why not just expect the developer to run tsc?

Because the developer is no longer the one writing the workflow. A coding assistant generates the TypeScript file based on a prompt. If the file does not compile, the assistant has to find that out somehow. Without typecheck_workflow, the only way it finds out is by trying to run the workflow, watching the runtime crash, parsing a stack trace, and guessing what to fix. With typecheck_workflow, the assistant gets the same feedback a developer would get from their editor: file, line, column, error code, message, surrounding code. It can fix the bug in one more turn instead of recovering from a runtime failure that may have already opened windows or clicked into the wrong app.

Where exactly does this live in the source tree?

The implementation file is crates/terminator-mcp-agent/src/tools/typecheck.rs. It is roughly 280 lines including its tests. The tool is registered in crates/terminator-mcp-agent/src/server.rs at the typecheck_workflow function definition (around line 9523). The arguments struct is TypecheckWorkflowArgs and takes one field, workflow_path. The result struct is TypecheckResult with success: bool, errors: Vec<TypeError>, error_count: usize, and raw_output: Option<String>. The raw_output is only included on failure so the assistant can read the full tsc dump when the parsed errors are not enough.

Does it actually fall back from bun to npx, or is one hard-coded?

It checks bun first. The function command_exists shells out to which (Unix) or where (Windows) and returns true only if the exit code is success. If bun exists, the tool runs bun tsc --noEmit. If bun does not exist, it tries the same check for npx and uses npx tsc --noEmit. If neither is in PATH the tool returns the error string 'Neither bun nor npx found. Install bun or Node.js.' The choice of bun first is intentional: bun is faster at booting tsc, which matters when the assistant is hitting this loop on every iteration of a workflow generation.

What does the seven-line context window look like in practice?

If the failure is on line 4 of src/test.ts, get_error_context reads the file, slices lines 1 through 7 (3 before, 1 offending, 3 after, clamped to file bounds), and renders each one as either ' {line_num:>4}: {line}' or ' -> {line_num:>4}: {line}' depending on whether it is the failing line. The output looks like ' 1: import { foo } from \'bar\';' on line 1 and ' -> 4: const x: string = 123;' on line 4. The model sees the failing line in context, not as a single message. That is what makes the next fix viable in one turn.

How is this different from running tsc in CI?

CI runs tsc once per push, after a human has already written the file. typecheck_workflow runs tsc inside an MCP session, on a file the assistant just wrote, before that file gets executed. The assistant calls it in the same conversation that produced the file. There is no commit, no push, no CI runner spin-up. It is a feedback turn, not a release gate. Most other free desktop automation tools have no analogue: their language is Python or AutoHotkey or .ahk script, which has no static type system to begin with, so 'compile-check' is not a thing the runtime can offer.

Will typecheck_workflow catch logic bugs, or only type errors?

Only the things tsc --noEmit catches. Wrong selector strings, missing element timeouts, race conditions, off-by-one indices: tsc cannot see those. What tsc does see is the shape of every desktop API call: did you pass a string where a number was required, did you call a method that does not exist on the Desktop class, did you import from a wrong path, did you forget to await a promise that returns a locator. Those are the errors that crash a workflow on startup, and those are the errors typecheck_workflow returns to the assistant before the workflow ever runs.

Do other free desktop automation tools have anything like this?

Not as a runtime feature. AutoHotkey is dynamically scoped and does not have a separate type checker. AutoIt has Au3Check, which catches some syntax issues, but it is a separate executable and is not exposed to an AI assistant over a structured protocol. SikuliX runs Jython and surfaces Python tracebacks at runtime, never at compile time. Pywinauto is plain Python with no built-in type-check tool. Robot Framework has --dryrun mode, which executes the workflow with all keywords stubbed; that is closer in spirit, but it is a CLI flag for a developer, not a tool an MCP client can call. Terminator is the only one in the category whose package.json includes both the runtime SDK and an MCP-exposed compile check that returns structured TypeError objects.

Is the whole thing free and MIT?

Yes. terminator-mcp-agent is on npm under the MIT license. The typecheck_workflow tool is part of the same npm package and ships with every install of npx -y terminator-mcp-agent@latest. There is no premium tier that unlocks the type checker. The only piece of the agent that touches a paid API is gemini_computer_use, which calls Gemini with your own key for last-resort visual recovery. Type-checking has no API call attached to it: it runs the local tsc binary that bun or node already has.

What is the quickest way to see typecheck_workflow run end-to-end?

Add the agent to your MCP client with claude mcp add terminator 'npx -y terminator-mcp-agent@latest'. Ask the assistant to write a TypeScript workflow that opens Notepad and types a string. Then ask it to call typecheck_workflow on the directory it just wrote the file into. The first call will either return success: true, errors: [], error_count: 0 or a structured list of errors with code context. The assistant will use whichever response it gets to decide whether to call execute_sequence next. That is the loop.

terminatorDesktop automation SDK
© 2026 terminator. All rights reserved.