GuideClaude CodeContext budgetexecute_sequence

The Claude Code MCP server that treats your context window as a budget, not a default.

Every tutorial for "Claude Code MCP server" ends at the install command. That is the easy part. The hard part is the minute your agent starts driving a real application. A 20-step desktop task via a naive MCP server eats the context window alive: twenty tool-call round-trips, twenty UI tree snapshots injected back into the conversation, twenty chances for the LLM to re-plan. Terminator ships a single tool, execute_sequence, that collapses that into one MCP call with server-side jumps, fallbacks, and state persistence. The YAML Claude Code authors today runs unattended on a headless VM tomorrow. This page walks through the exact code that makes that possible.

T
Terminator
13 min read
4.9from every 'Claude Code MCP server' top-10 article read
32 tools in one dispatch arm
execute_sequence collapses N steps into 1 round-trip
state.json persists between sessions and reboots
TERMINATOR_HEADLESS=true replays on a VM
claude mcp add terminator-s userstdio transportexecute_sequencestate.jsonrole:Window && name:press_key_globalTERMINATOR_HEADLESS=trueone MCP round-tripstop_executiontokio::select!Box::pin(execute_sequence_impl)

Why every Claude Code MCP guide stops at the install command

I read the top results for "claude code mcp server": the official docs, the Builder.io guide, Scott Spence's post, the Docker MCP Toolkit announcement, ksred's "Claude Code AS an MCP server" piece, and the three or four GitHub READMEs that rank with them. They all tell you the same three things. How transport types work (stdio vs HTTP). How claude mcp add writes a block to ~/.claude.json. And a list of popular servers, which at this point is an evergreen content format with no teeth.

None of them describe what happens on minute two. You connect an MCP server that can actually do something heavy (a browser driver, a desktop automator, a filesystem scraper), you ask Claude Code for a real task, and the context window starts filling with round-trip noise. By step fifteen you are either at the context limit or watching Claude Code politely lose the plot because earlier turns are getting summarised away.

1

Number of MCP round-trips needed to run an 8-step desktop workflow through Terminator's execute_sequence tool. The LLM does not see the intermediate UI trees unless it explicitly asks for them.

crates/terminator-mcp-agent/src/server.rs line 10234

The math behind the context problem

Before showing the fix, it is worth making the problem concrete. This is the arithmetic of a Claude Code session that does real desktop work through an MCP server that exposes one action per tool call.

1

Tool call shape

Every MCP tool call costs input tokens (the tool schema, the arguments, the system prompt reminder) and output tokens (the LLM's decision, the arguments to emit). Every response frame costs input tokens to inject back into the conversation.

2

Desktop tasks are long

A realistic desktop task is 15 to 30 steps: open app, wait, check state, click, type, validate, click, type, screenshot, ... . Times three if there are branches, retries, or vendor-specific prompts to dismiss.

3

Naive MCP servers explode the round-trip count

Each step becomes a separate tool call. Each step emits a full UI tree snapshot back into the conversation. Each step gives the LLM a chance to re-plan, and re-planning burns output tokens on top of the input cost. The context window becomes the bottleneck well before the desktop does.

4

execute_sequence moves the loop server-side

The LLM decides the workflow shape once, emits one tool call, and the sequence engine handles step-by-step execution, UI tree capture, selector resolution, variable interpolation, conditional jumps, and fallbacks. The LLM does not see the intermediate trees unless it asks for them.

5

State outlives the MCP call

Each step with an id writes an entry to state.json. If Claude Code closes, if the machine reboots, if a colleague picks up the task, the next run reads the same state file and continues. The context window is not the storage medium anymore.

0tools in one dispatch arm
0MCP call per full workflow
0desktop steps in the example YAML
0 OStargets with the same YAML

The dispatch arm that changes the shape of a Claude Code session

Here is the dispatch block every Claude Code tool call lands in. Most of the arms are shaped the same way: deserialise args, call a handler, return the result. The execute_sequence arm is the exception. It uses Box::pin to hold a recursive async future, so one tool call can contain an entire workflow, including nested sequences.

crates/terminator-mcp-agent/src/server.rs

One MCP call, N desktop actions

Claude Code
stdio transport
execute_sequence
open_application
type_into_element
click_element
wait_for_element
run_command
state.json write

The YAML Claude Code emits, in practice

This is a realistic slice of what Claude Code actually sends to Terminator for a common task: importing a PDF invoice into QuickBooks Desktop. Eight steps, one conditional jump, one set of selectors. Claude Code makes this decision once, ships one tool call, and the sequence engine runs the rest.

close-month-end.yml

What the round-trip actually looks like on the wire

The sequence diagram below shows the collapse in concrete terms. Left to right: Claude Code, the stdio transport, the dispatch function in server.rs, the sequence engine, and the underlying OS accessibility APIs. One request frame in, one response frame out, all step-level work contained between them.

A single execute_sequence call

Claude CodeMCP stdioserver.rsexecute_sequence_implDesktop (UIA / AX)call_tool name=execute_sequence (steps[1..8])JSON-RPC frame over stdioBox::pin(execute_sequence_impl(args))open_application, wait_for_element, ... (8x)step results + state.json write per idsingle condensed CallToolResultone JSON-RPC response frameone tool result injected into context

Cancellation: stop_execution actually stops the pointer

Batching only pays off if you can bail out safely. Every handler in Terminator awaits inside a tokio::select! against a cancellation token lifted off the request_context. When Claude Code disconnects mid-workflow (or when a second Claude Code tab explicitly calls stop_execution), the in-flight click is dropped at the next await point. The desktop pointer releases within a scheduler tick. Compare this with HTTP-only cancellation, where the connection closes but the handler runs to completion against a UI that no one is watching anymore.

crates/terminator-mcp-agent/src/server.rs

State.json: why the workflow survives Claude Code closing

Inside the sequence engine, every step that has an id or writes to set_env triggers a write to a per-workflow state file on disk. The path is OS-native and predictable. The consequence is that the MCP message history is not the storage medium: the YAML is the contract, and the state file is the resumable context.

crates/terminator-mcp-agent/src/server_sequence.rs

Verifying the install and the state file on your own box

If you just installed the server, these are the commands that confirm Claude Code can see it and that the state directory is where this page says it is. The last line is the one that matters: the state file is human-readable JSON you can cat and inspect.

Verifying install + state persistence

The payoff: the same YAML runs on a headless VM

The YAML Claude Code authored is not a transient artifact. Move it to a Windows VM, set TERMINATOR_HEADLESS=true, and run it with the Terminator CLI. The virtual display context satisfies Windows UI Automation without a logged-in RDP session, and the same selectors resolve because the accessibility tree is an OS-level structure, not a pixel artifact. This is how a Claude-Code-driven agent graduates from a desktop helper into a nightly job on a server.

Replay on a headless Windows VM

What else the 32 tools give Claude Code

execute_sequence is the one that matters for context budget, but it is sitting on top of 31 other tools. The full surface. The map below is the mental model: six capability clusters that every Claude Code tool call eventually lands in.

One install command

claude mcp add terminator "npx -y terminator-mcp-agent@latest" -s user. User scope means every Claude Code session on the box picks the server up, not just this repo.

32 tools in one dispatch

Browser (navigate_browser, execute_browser_script), native apps (open_application, click_element, type_into_element, press_key_global), filesystem (read_file, edit_file, glob_files, grep_files), and control flow (execute_sequence, stop_execution) all route through one match arm.

Selector syntax you can type

role:Button && name:Save, navigate to parent with >> .. >>, substring matching by default. No XPath, no CSS, no hand-rolled scraping.

One MCP call, N desktop steps

execute_sequence takes a YAML workflow with jumps, fallbacks, group_name, continue_on_error, and variable interpolation. Claude Code emits one tool call; the server runs the whole sequence.

Per-workflow state.json

Env variables, tool results, and the last step index are written to mediar/workflows/<folder>/state.json after every step with an id. You can resume a broken workflow from exactly where it stopped.

Headless VM replay

TERMINATOR_HEADLESS=true spins up a virtual display context. The same YAML Claude Code authored today runs unattended on a Windows VM tomorrow.

The install, end to end

Five steps, one of which only matters on macOS. If you are tempted to copy just the first command and move on, at least read step three: claude mcp list is how you know the server actually registered.

1

Install Claude Code if you have not already

npm install -g @anthropic-ai/claude-code. Claude Code is the CLI that speaks MCP; the Terminator server plugs into whichever Claude Code install you have on this machine.

2

Register the MCP server at user scope

claude mcp add terminator "npx -y terminator-mcp-agent@latest" -s user. User scope means every Claude Code session on this machine sees the server, without having to add it per repo.

3

Confirm Claude Code sees the 32 tools

claude mcp list should show terminator stdio 32 tools. If it shows 0 tools or the name in red, check that Node.js 16+ is on PATH and that npx can fetch the package.

4

Optional: grant accessibility permission on macOS

System Settings → Privacy & Security → Accessibility → add the terminal app that launches Claude Code (Terminal, iTerm, Warp). Without this, get_window_tree returns empty results on every window.

5

Tell Claude Code what you want

"Open QuickBooks, import invoice INV-4412.pdf, post it to Expense:Software, save and close." Claude Code picks execute_sequence, emits one tool call, and Terminator walks the real GUI. The conversation stays readable; the YAML it generates sits in .mediar/workflows/<name>/ and can be re-run unattended.

install-and-verify.sh

Terminator vs a generic Claude Code MCP server

The right-hand column is the composite behaviour of the other MCP servers that rank for this query. Nothing personal; the table is just a structural lens. If your server of choice has a batching primitive, a shared cancellation token, and a state file, you can treat the two columns as a quality check rather than a ranking.

FeatureGeneric Claude Code MCPTerminator MCP
Claude Code install flowHand-edit ~/.claude.json, restart Claude CodeOne line: claude mcp add terminator "npx -y terminator-mcp-agent@latest" -s user
Context cost of a 20-step task20 tool calls, 20 result frames, 20 ui-tree snapshots injected into the conversation1 execute_sequence tool call, 1 condensed result payload, optional per-step tree suppression
Mid-workflow control flowLLM re-plans after every step, loses state between tool callsYAML jumps, fallback_id, and group_name handle branching inside the sequence engine
Session survivalAgent history dies when Claude Code closesstate.json per workflow folder, env survives between runs and across reboots
CancellationClose connection; server handler finishes, desktop stays hungstop_execution flips a cancellation token inside every tokio::select; pointer releases within a scheduler tick
Re-run after Claude Code closesRe-prompt, rebuild the plan, hope the LLM remembersterminator mcp run workflow.yml --start-from step_5 against the same state.json
Headless executionNeeds RDP session or a real displayTERMINATOR_HEADLESS=true creates a virtual display context Windows UIA can still read
Vision fallbacks when the tree is wrongOne fixed strategyui_tree (default), OCR, Omniparser, browser DOM, or gemini_computer_use, picked per call

Checklist for evaluating any Claude Code MCP server

Not a marketing filter; a structural one. Steal this list and apply it to whichever MCP server you are considering. If it passes four out of six, Claude Code will handle long tasks. If it passes two, expect to babysit the context window.

Structural checklist

  • The server exposes a batching primitive (execute_sequence, run_script, workflow_run)
  • Tool handlers share one cancellation token so stop_execution actually stops work
  • There is a predictable on-disk location for workflow state between runs
  • The same MCP binary can run headless (no RDP, no attended session)
  • Selector syntax is visible, documented, and does not require vision models by default
  • The tool list the LLM sees is derived from the dispatch code, not drifted from it

The honest summary

Most "Claude Code MCP server" tutorials rank near the top of Google because the install command is memorable and the structure is replicable. They are not wrong; they are incomplete. The thing that separates an MCP server you can use in Claude Code for a weekend from one you can trust with a weekly job is whether the server treats the conversation as a transcript of decisions rather than the authoritative store of state. Terminator does. The YAML is the contract. The state file is the resumable context. The LLM's job is to decide the shape, not to relay every keystroke.

If you want to try it on the exact install command this page has been showing, run this:

one-command.sh

Wiring Terminator into a Claude Code workflow for a real deployment?

Bring the task you actually want automated. We will walk through the YAML, the state.json layout, and how to run it headless on your own VM. 20 minutes, live.

Frequently asked questions

What exactly is a 'Claude Code MCP server' and how is Terminator one of them?

Claude Code speaks the Model Context Protocol over stdio and HTTP. Any process that answers initialize, tools/list, and tools/call on one of those transports is a Claude Code MCP server. Terminator ships crates/terminator-mcp-agent as a Rust binary that speaks stdio by default (and HTTP with -t http). You install it in Claude Code with: claude mcp add terminator "npx -y terminator-mcp-agent@latest" -s user. After that, Claude Code sees 32 new tools it can call, all of which touch the real desktop through accessibility APIs (Windows UIA on Windows, AX on macOS) instead of screenshots.

Why does the context window matter here? Is this not just a performance micro-optimization?

No. On realistic desktop tasks it is the dominant cost. A 20-step workflow via a naive MCP server means 20 tool calls, 20 argument payloads, 20 return frames, and typically 20 UI tree snapshots injected back into the conversation. Tree snapshots are the heavy part: the basic tree of a single browser tab is already thousands of tokens. Twenty of them in one session is tens of thousands of tokens of context the LLM has to read to decide the next step. execute_sequence keeps the tree server-side (include_tree_after_action=false for intermediate steps) and returns one condensed result, which is the difference between the workflow fitting in context and Claude Code starting to lose earlier turns.

What is the 'one MCP call' I keep reading about in this page?

execute_sequence is a tool on the server. Claude Code emits a single JSON-RPC tools/call frame with tool_name=execute_sequence and arguments containing the full YAML workflow. The server routes it to the arm at crates/terminator-mcp-agent/src/server.rs line 10234, which calls Box::pin(self.execute_sequence_impl(...)) and runs every step in that workflow inside the same request context. One JSON-RPC request, one JSON-RPC response, N desktop actions in between. The alternative (one tool call per step) is what most MCP servers give you by default, and it is the context-window-explosion scenario.

How does the workflow actually survive after Claude Code closes?

Inside execute_sequence_impl, every step that has an id or calls set_env triggers save_workflow_state. That function is in crates/terminator-mcp-agent/src/server_sequence.rs around line 216 and it writes a file at <data_dir>/mediar/workflows/<folder>/state.json with the current env map, the last step id, the last step index, and an ISO-8601 timestamp. When you later run terminator mcp run workflow.yml --start-from some_step, the CLI reads that file and seeds env before executing. Nothing in the MCP message history is load-bearing; the workflow is the contract.

What does 'runs on a headless VM' mean in practice?

On Windows, the UI Automation API normally requires a display session. Terminator's agent detects headless environments (no console window, running as a service, or TERMINATOR_HEADLESS=true explicitly) and initializes a virtual display context that Windows UIA will read against. The effect is that you can take the same YAML Claude Code authored, scp it to a Windows VM with no RDP session, set TERMINATOR_HEADLESS=true, run terminator mcp run workflow.yml, and the automation fires as if a human were logged in. This is documented in crates/terminator-mcp-agent/README.md starting at line 419 under 'Virtual Display Support'.

What happens if I hit Ctrl+C in Claude Code while a long-running tool is in flight?

Claude Code triggers the MCP request_context cancellation, which flips request_context.ct.cancelled(). Every handler in crates/terminator-mcp-agent/src/server.rs wraps its work in tokio::select! against that token (starting at lines 9957-9966 for get_window_tree and continuing for click_element, type_into_element, wait_for_element, and the rest). When the token fires, the in-flight future is dropped at the next await point and the handler returns McpError code -32001. The desktop pointer releases within a scheduler tick. This is different from HTTP-only cancellation, where the connection closes but the server-side work runs to completion, often corrupting state. You can also call the stop_execution tool from a second Claude Code tab to force the same behaviour explicitly.

Is this better or worse than Claude's native computer-use tool?

It is a different trade-off. Claude computer use is vision-based: screenshots in, click coordinates out. It is model-agnostic about the target and works on anything rendered to pixels. Terminator reads the accessibility tree, which means selectors are readable (role:Button && name:Save), actions are deterministic (clicks dispatch through invoke patterns, not screen coordinates), and latency is lower (no image capture on every step). For most line-of-business desktop apps, especially Win32 and Electron apps with accessible trees, Terminator is faster and more reliable. For pixel-only surfaces (games, some legacy apps, remote desktop sessions rendered as video), computer-use is the right tool. The two compose: Terminator has a gemini_computer_use fallback arm for the cases where the accessibility tree is missing.

Does execute_sequence have error handling, or does the whole workflow fail on step 1?

It has four independent mechanisms. First, stop_on_error: true at the workflow level is the default strict mode. Second, continue_on_error: true on a single step lets it fail quietly. Third, fallback_id on a step jumps to a named troubleshooting step instead of stopping. Fourth, conditional jumps (jumps: [{ if: "status == 'success'", to_id: ... }]) let you branch based on prior step results. The full shape is documented in the README under 'Fallback Mechanism' and the example workflows in crates/terminator-mcp-agent/examples/. Between those, a real workflow can encode retry-then-escalate logic without forcing Claude Code to re-plan after every failure.

What is the minimal path to go from zero to 'Claude Code is driving my desktop'?

Three steps. One: install Claude Code (npm install -g @anthropic-ai/claude-code). Two: run claude mcp add terminator "npx -y terminator-mcp-agent@latest" -s user. Three: on macOS, grant accessibility permission to whatever terminal app launches Claude Code. Confirm with claude mcp list that terminator shows 32 tools. Then ask Claude Code something like "list the applications currently open on my desktop" (it will call get_applications_and_windows_list) or "open Calculator and compute 42 plus 8" (it will call execute_sequence with open_application, type_into_element, click_element, wait_for_element). No config files, no JSON patching.

Where do I look in the repo to verify what this page claims?

crates/terminator-mcp-agent/src/server.rs for the dispatch_tool block at line 9953, the execute_sequence arm at 10234 (Box::pin async recursion), and the tokio::select cancellation pattern at 9957-9966. crates/terminator-mcp-agent/src/server_sequence.rs for state persistence: get_state_file_path at line 193 and save_workflow_state at line 216. crates/terminator-mcp-agent/README.md lines 19 for the Claude Code install command, lines 419-433 for TERMINATOR_HEADLESS, and lines 600-650 for partial execution and state.json. crates/terminator-mcp-agent/build.rs line 31 for the compile-time tool-list extraction that keeps the LLM-visible tool names in sync with the dispatch arms.

terminatorDesktop automation SDK
© 2026 terminator. All rights reserved.