limoni-mcp
Quick start
go get github.com/thebanri/limonipackage main
import "github.com/thebanri/limoni"
func main() {
limoni.Run(func(f *limoni.Frame, ev *limoni.Event) bool {
if ev != nil && ev.Type == limoni.EventKey && ev.Key.Type == limoni.KeyEsc {
return false // quit
}
f.RenderComponent(limoni.Border(
limoni.Center(limoni.Label("Hello from Limoni 🍋 (Esc quits)", limoni.Bold())),
limoni.SymbolsRounded,
limoni.Fg(limoni.Hex("#FFCC00")),
), f.Area())
return true
})
}Or generate a project that runs straight away, with a test already written:
go run github.com/thebanri/limoni/cmd/limoni@latest new myapp # -template counter|dashboard|form|chat|ssh
cd myapp && go mod tidy && go run .
go test ./... # a uitest test comes with every templateNext: Getting started · Widget gallery · Examples
Related MCP server: mcp-server-terminal
Why Limoni
1. Your TUI has a semantic tree, so tests and agents address widgets by name
Most terminal automation, such as termwright or mcp-tui-test, parses the rendered character grid, so a test breaks when the layout moves by one column. A Limoni app builds a semantic tree every frame (the same tree a screen reader uses), and the tools below work on that tree instead.
uitest: Playwright-style tests. Checks wait instead of sleeping, and a failure
prints the whole tree of the last frame.
page := uitest.Run(t, 80, 24, app.draw) // in process: no terminal needed
page.GetByRole("input", "New task").Type("Tag v1.0")
page.GetByRole("button", "Add task").Click()
page.Expect(page.GetByRole("list-item", "").Within(page.GetByRole("list", "Tasks"))).ToHaveCount(3)
page.Expect(page.GetByID("status")).ToContainLabel("Added")limoni-mcp: let an AI agent drive the app. It gives Claude Code, Cursor or any
MCP client eight tools (tree, click, type_text, wait_for…) that work on the
same tree. In one recorded run, Claude Code completed a release checklist in 17 tool
calls. That run included typing a deploy token the agent could not read back.
go run -tags limoni_debug ./examples/agent_checklist
claude mcp add limoni -- limoni-mcp -socket "$XDG_RUNTIME_DIR/limoni-checklist.sock"The automation socket only exists in -tags limoni_debug builds. It is closed by
default, only the same user can connect, and secret fields are never exposed.
→ Semantic automation, MCP and uitest
2. Fast where you can feel it
Zero heap allocations per frame for the renderer, every widget's drawing, and the click handling of the common input widgets, enforced in CI, so animations don't stutter from GC pauses. That includes widgets that drag or call your code — Table, Slider, Dialog, TreeView, CommandPalette — once their state is attached. The details.
Few bytes per frame. Blank runs become
ECH/ELand repeats becomeREP: a full-screen redraw is 377 bytes, and an idle app sends nothing. Bytes, not CPU, are what you feel over SSH.Virtual tables and lists. One million rows scroll at ~2.7 ms a frame, because only visible rows are touched.
Measured on a Ryzen 5 5600, Go 1.27.1 | Latency | Allocations |
Full-screen diff, 120×40, every cell changed | ~50 µs | 0 |
10% of the screen changed | ~11 µs | 0 |
Nothing changed | ~2 ns | 0 |
100 layered blocks, drawn and diffed | ~68 µs | 0 |
Absolute numbers depend on the machine. The comparisons against Ratatui 0.30.2, Ultraviolet and Bubble Tea v1.3.10, including which ratios are not meaningful, are in docs/benchmarks.md and the methodology. One earlier run showed a 4,700× lead; it came from a bug in the benchmark harness, and the methodology explains how it was caught.
3. Batteries that other TUI libraries leave to you
🕶️ 3D | A software rasteriser for OBJ/STL/PLY/GLB with Lambert and Gouraud shading, drawn in terminal cells |
🖼️ Images | Kitty, Sixel, iTerm2 and half-block fallback |
📊 Charts | Braille line charts, bar charts, pie charts, sparklines |
📝 Markdown | Headings, lists (nested, numbered, tasks), quotes, tables, links, and fenced code highlighted by language — including a block still streaming in |
♿ Accessibility | A semantic tree, a screen-reader line mode, |
🧬 Unicode | UAX #29 grapheme clusters (Unicode 17.0, all 766 conformance tests), so flags and emoji families take one cell |
🔗 Hyperlinks | OSC 8 links, in markdown or any style — and where the terminal cannot show them, the address is printed instead |
📃 Inline mode | Render in a band of the normal screen, like |
⏺️ Session replay | Record a session, replay it as a regression test (docs) |
🌐 Everywhere | Linux, macOS, BSD, Windows, WebAssembly in the browser, SSH sessions |
The only dependencies are golang.org/x/sys and golang.org/x/crypto.
Is Limoni the right choice?
Pick Limoni if you are writing a terminal app in Go and any of these matter:
You want to test the UI, or let an AI agent use it, by widget role and label rather than by screen coordinates (
uitest,limoni-mcp).It redraws a lot: dashboards, log viewers, monitoring, games, animation, anything over SSH. The draw path makes no garbage and sends few bytes.
It needs things other libraries leave to you: 3D models, images, charts, markdown, a million-row table, screen-reader support, WebAssembly in the browser.
You want both styles in one library: immediate mode (
limoni.Run) for dashboards, the Elm architecture (limoni.RunProgram) for forms and wizards.You want a small dependency tree:
golang.org/x/sysandgolang.org/x/crypto.
Pick something else if:
You need a stable 1.0 API today. Limoni is pre-1.0 (see Status).
You rely on the Charm ecosystem (Bubbles, Huh, Glamour, Wish) and its community. Bubble Tea is the larger and older project. If you already have a Bubble Tea app,
compat/bubbletearuns your models on Limoni (migration guide), so you can try it without a rewrite.You are writing Rust: use Ratatui.
The app is a one-shot prompt (a single question, a spinner): a small prompt library is less to learn.
A feature-by-feature table against Bubble Tea v1/v2 and Ratatui 0.30 is in docs/comparison.md.
Built with Limoni: zest
zest is a log viewer and Limoni's flagship app. It follows files and pipes, colours by level, and filters a million lines without stalling. A 67 MiB, 1,000,000-line log is on screen in about half a second.
go install github.com/thebanri/limoni/cmd/zest@latest
zest -demo 1000000 # or: zest app.log, kubectl logs -f pod | zestOr try it in the browser: the "Logs · zest" scene.
And: globe
globe is a world you can turn, search, zoom into and pin — and what a Limoni application looks like from the outside, since it is a module of its own that depends on a published Limoni and uses nothing but its public API.
go install github.com/thebanri/limoni/apps/globe@latest
globe -at Türkiyego install puts the binary in $(go env GOPATH)/bin — ~/go/bin unless you changed it —
which a fresh Go install does not add to your PATH. If globe is "command not found", add
that directory to PATH or run ~/go/bin/globe.
Everything on it is in the semantic tree, so "find Turkey on the world map" is something an agent
can do over MCP: type into search, click the row, read image#globe back as
value="39.3°N 34.5°E · zoom 2.6×". Nothing of it reaches you when you import Limoni — a
directory with its own go.mod is not part of the module around it.
And your shell: backdrop-shell
backdrop-shell puts a moving scene behind your shell, in any terminal — an aurora, a night city, a starfield, a synthwave sunset, a picture, or ASCII art of your own that blinks or drifts. The text in front stays sharp, selecting copies only what the shell wrote, a narrower window keeps every line, and the opacity or the scene changes at once in every open terminal. It costs about half a percent of one core, and nothing while the window is in the background.
curl -fsSL https://raw.githubusercontent.com/thebanri/limoni/main/apps/backdrop-shell/install.sh | sh
backdrop-shell opacity 0.3 # quieter, in every open terminal
backdrop-shell enable -scene synthwave # or -image wall.jpg, or -art my-art.txtDrawing and animating your own: docs/backdrop-art.md. The same scenes go
behind any Limoni application with limoni.WithBackdrop(backdrop.Aurora()).
And a game: Castle Lemonstein
Castle Lemonstein is a short first-person game. Squirt the rats and find ten lemons inside a banner-lined stone castle, and the gate to the lair lifts on Ratatui, king of the rats, who has a crown, a health bar and cheese to throw.
go install github.com/thebanri/limoni/apps/castle-lemonstein@latest
castle-lemonsteinOr play it in the browser, sound included.
It is a raycaster drawn two pixels to a cell in half blocks, with lit textures, pixel-art rats, 3D half lemons
ray-traced per pixel, and sound synthesised at start-up. A frame allocates nothing, through the diff too. It
holds keys down with WithKeyReleases, so walking is smooth in kitty, Ghostty and WezTerm. Runs are scored
on aim and time, onto a leaderboard kept in a file, or in the browser's storage.
Lemon Drop is falling blocks where a piece that lands crumbles into sand of its colour. A run of one colour that reaches from wall to wall is the line: it flashes and goes, and what rested on it falls, sometimes into the next one.
go install github.com/thebanri/limoni/apps/lemondrop@latest
lemondropOr play it in the browser, with sound and a world leaderboard.
Lemon Snake is a snake that eats lemons. Every five, a monster climbs out of the far
corner and hunts the head; spit the seeds your lemons bring at it, and where it steps on
the body it bites the rest off. Every level is faster, and so is its monster; fill the board to win. t switches between five
colour themes at any time. It glides in half-block pixels, plays soft synthesised sounds, and
sends finished runs to the same shared leaderboard as Lemon Drop.
go install github.com/thebanri/limoni/apps/lemonsnake@latest
lemonsnakeOr play it in the browser, with its sound and the world leaderboard.
pikap is a record player drawn in half blocks (or ASCII). It follows whatever the computer is playing (Spotify, a browser tab, mpv, anything on MPRIS): the cover turns on the record and the tonearm moves across it. Dragging the record or the arm moves the song back and forth. Given files, it plays them itself, and then pausing slows the music down with the platter and dragging the record scratches.
go install github.com/thebanri/limoni/apps/pikap@latest
pikap # follow what is playing
pikap ~/Music # or play filesTwo ways to write an app
Immediate mode | Declarative (Elm architecture) | |
Entry point |
|
|
State lives in | your closure | a |
Best for | dashboards, 3D, games, animation | forms, wizards, CRUD tools, async work |
Runtime gives you | a redraw on every event | commands, cancellation, panic recovery, session recording |
Both use the same renderer and widgets and are available from the root package.
examples/counter is a complete declarative app in under 80 lines.
Coming from Bubble Tea? See the migration guide.
Widgets
Category | Widgets |
Layout |
|
Data |
|
Charts |
|
Input |
|
Navigation |
|
Feedback |
|
Graphics |
|
Text |
|
Tooling |
|
→ Widget gallery · Widget reference
Examples
Example | What it shows |
The feature trailer: a 3D lemon in ASCII, Braille and half-blocks | |
A night city under an X-ray: every cell the diff sends lights up, with measured bytes against a full repaint ( | |
Tabs, forms, matrix rain, 3D, command palette, DevTools ( | |
OBJ/STL/PLY viewer with shading and orbit controls ( | |
Live CPU and memory sparklines, a process table, streaming logs | |
A one-million-row table | |
An app built to be driven by an AI agent, and tested with | |
A declarative todo app with tags, filters and fuzzy search | |
The smallest declarative app | |
Layout with | |
| Inputs, modals, file tree, charts |
Serving over SSH, running in the browser |
Run any of them with go run ./examples/<name>, or without cloning:
go run github.com/thebanri/limoni/examples/3d_viewer@latest.
All examples: docs/examples.md.
Documentation
Install, first app, both application models | |
The flat cell grid, the diff, why the draw path doesn't allocate | |
Reference | |
The automation socket, | |
Record and replay sessions as regression tests | |
Feature guides | |
Unix, Windows, WebAssembly, SSH | |
Against Bubble Tea v1/v2, Lip Gloss and Ratatui, with the caveats | |
Every measured number and how to reproduce it | |
Hairline gaps, recommended terminals, emoji | |
What may change before 1.0 |
Turkish documentation: docs/tr.
Status
Limoni is pre-1.0. Patch releases don't break the API; minor releases may, and
every break is listed in the changelog. The core renderer, layout and
widgets are settling; the automation, uitest and session packages are new and
experimental. See docs/stability.md for what has to happen
before v1.0.
Community and contributing
Questions and ideas: GitHub Discussions
Bugs: open an issue. The template asks for your terminal emulator, since most rendering bugs depend on it.
First contribution: issues labelled
good first issueare scoped to one file and say how to verify the change. Start with CONTRIBUTING.md.Built something? Add it to AWESOME.md.
AI assistants (Claude, Gemini) were used during development for scaffolding, tests and documentation drafts. The architecture was designed, profiled and benchmarked by the author, and the benchmark harness exists to check claims, from people or tools.
Security policy · Code of Conduct · Apache License 2.0
Available Tools
8 toolsclickClick a widgetADestructive
Clicks the centre of the one widget the selector matches, then returns the tree after the application redraws. Refused if the selector matches more than one widget. Clicking toggles a checkbox; to make a step safe to repeat, pass ensure: checked, unchecked or selected, and the widget is clicked only if it is not already in that state.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | The widget's ID, exactly as shown after # in the tree. | |
| nth | No | Which match to use when several qualify, 0-based; negative counts from the end. | |
| role | No | The semantic role. | |
| label | No | The label, matched exactly. | |
| value | No | The current value, matched exactly — for example the selected item of a list. | |
| ensure | No | Click only if the widget is not already in this state, then confirm it reached it. Makes the call idempotent. | |
| label_contains | No | A substring of the label, for labels that carry live data. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare destructiveHint=true and idempotentHint=false, and the description adds genuinely new behavior: the tree is returned after redraw, multi-match selectors are rejected rather than guessed, and clicking toggles checkboxes. The ensure parameter's conditional idempotency is a nuance the boolean annotation cannot express, and it complements rather than contradicts it. It does not say what side effects a click may trigger beyond toggling, so not a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the core action and outcome, then the refusal condition and the ensure pattern. Every clause carries information; the 'clicking toggles a checkbox' aside is the only part that could be trimmed. No preamble or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description does the right thing by stating the return value (the tree after redraw), and it covers the selector's single-match constraint and the idempotency escape hatch, which are the risky behaviors for a destructive click. Minor gaps remain — what 'selector' is composed of and what happens on refusal — but they are not blocking.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter — including the selector fields and the two enums — is already documented in the schema. The description's explanation of ensure largely restates what the schema says ('Click only if the widget is not already in this state... Makes the call idempotent'), adding only the concrete 'checked/unchecked/selected' framing. Baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
It states a specific verb and resource — clicking the centre of the single widget matched by a selector — and adds the post-condition (returns the tree after redraw). That is well beyond a tautology. It never names or contrasts with siblings such as press_key or type_text, so an agent gets no explicit routing help, which caps it at 4.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives two concrete usage rules: the call is refused when the selector matches more than one widget, and ensure should be passed when a step must be safe to repeat (click only if not already in the target state). That is real when-to-use guidance. It stops short of stating when to prefer this tool over press_key/type_text or what to do on refusal, so no exclusions are covered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
findFind widgetsARead-onlyIdempotent
Returns every widget matching a selector, with its subtree. An empty result is not an error.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | The widget's ID, exactly as shown after # in the tree. | |
| nth | No | Which match to use when several qualify, 0-based; negative counts from the end. | |
| role | No | The semantic role. | |
| label | No | The label, matched exactly. | |
| value | No | The current value, matched exactly — for example the selected item of a list. | |
| label_contains | No | A substring of the label, for labels that carry live data. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish read-only, non-destructive, idempotent, closed-world behavior, so the description is not burdened with safety disclosure. It still adds real non-obvious context: results include the matched widget's subtree, and no-match is a valid empty result rather than an error, which shapes how the agent interprets an empty response.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, zero waste, with the return scope front-loaded and the error-semantics caveat second. Nothing could be cut without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema the agent still lacks a picture of the returned structure ('with its subtree' is only gestured at), and for a six-parameter, all-optional query tool the description never says whether the selector fields are ANDed, whether nth applies on top of them, or what happens when nth is combined with a selector. It is minimally adequate for a filtered lookup but leaves real gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every one of the six selector fields (id, nth, role, label, value, label_contains) is documented in the schema with its matching semantics. The description adds nothing here beyond the vague word 'selector', which never explains how the fields combine or interact with nth — baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb (returns every widget matching a selector) and resource (widget, plus its subtree), which is clearly distinct from the sibling 'tree' or 'screen' that enumerate without a selector. It never names a sibling explicitly, so differentiation is implied rather than stated.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'An empty result is not an error' is a behavioral note, not usage guidance. Nothing tells the agent when to reach for find instead of tree, screen, or status, nor what prerequisites (e.g., an existing tree/snapshot) must hold before the selector can match.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
press_keyPress a keyADestructive
Sends one key press to the focused widget, then returns the tree after the application redraws. key is a single character or one of: enter, tab, esc, space, backspace, delete, insert, up, down, left, right, home, end, pgup, pgdn, f1–f12.
| Name | Required | Description | Default |
|---|---|---|---|
| alt | No | ||
| key | Yes | A single character, or a key name such as enter, tab, esc, up, down. | |
| ctrl | No | ||
| shift | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true, idempotentHint=false, and readOnlyHint=false, so the safety profile is covered. The description adds real behavioral context beyond that: the call synchronously returns the tree only after the application redraws, which tells the agent the response reflects post-keypress state.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, front-loaded with the action and side effect, then the key vocabulary. The long enumeration earns its place because the schema has no enum constraint.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description correctly explains the return (the redrawn tree) plus the redraw side effect, and it covers the required key parameter. It stops short of explaining modifier semantics, which is the remaining gap for a 4-parameter tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 25% and the schema defines no enums, so the description usefully supplies the full valid key vocabulary (enter, tab, esc, ... f1–f12) that the schema itself omits. It says nothing about the alt/ctrl/shift modifiers, leaving a quarter of the parameters undocumented in both places.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a precise verb+resource: 'Sends one key press to the focused widget,' which is clearly distinct from a string-typing sibling like type_text. However, it never names or contrasts against the closest siblings (type_text, click), so an agent must infer the boundary.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: 'to the focused widget' hints that a widget must be focused first, but there is no explicit when-to-use, when-not-to-use, or alternative (type_text/click/wait_for) mentioned.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
screenScreen textARead-onlyIdempotent
Returns the rendered character grid. Prefer tree; use this only for what the tree cannot express, such as chart contents or layout. Disabled unless the application's policy exposes the screen.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), so the lower bar applies; the description adds genuine behavioral context beyond them by disclosing an availability gate tied to the application's policy. It does not say whether an unavailable call errors or returns empty, which keeps it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with what the tool returns, then the routing rule, then the caveat. Every sentence earns its place with no redundancy against the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a parameterless read whose annotations carry the safety profile, the description supplies the return nature, sibling routing, and the availability gate — enough to call it correctly. Only the failure behavior when the screen is not exposed is left unstated, a minor gap rather than a blocking one.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline of 4 applies. No parameter claims are made or needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Returns the rendered character grid') and immediately contrasts itself with the sibling tool 'tree', so an agent can distinguish the two without opening a schema. The scope of what is returned is concrete rather than a restatement of the name.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly names the preferred alternative ('Prefer tree') and the narrow condition that selects this tool ('only for what the tree cannot express, such as chart contents or layout'). It also states a gating precondition ('Disabled unless the application's policy exposes the screen'), which is exactly the when-not guidance a caller needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
statusApplication statusARead-onlyIdempotent
Reports whether the application is reachable, its screen size, the focused widget, and what its automation policy allows: input, screen text, and input values.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so the safety profile is covered. The description goes further by disclosing the exact content surfaced, including the automation policy (input, screen text, input values), which tells the agent what capabilities it can expect without an output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single dense sentence with no wasted words, front-loading the most important fact (reachability) before the enumerated details. Appropriately sized for a zero-parameter probe.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema and no parameters, the description carries the burden of describing the return payload, and it does so across four dimensions including the automation policy. It is complete enough to call correctly, though it omits any note on result format or how policy restrictions manifest.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate and the baseline is 4. The description correctly does not invent parameter behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Uses a clear verb ("Reports") and enumerates the specific resource details returned: reachability, screen size, focused widget, and automation policy. This distinguishes it from general siblings like screen or tree by framing it as a status/policy probe, though it does not name those siblings explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Offers no when-to-use guidance or routing against alternatives such as screen, tree, or find, even though the sibling set clearly overlaps in returning application state. The agent must infer that this is the pre-flight reachability check.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
treeSemantic treeARead-onlyIdempotent
Returns the semantic tree of the current frame: every widget's role, ID, label, value, state and bounds. Start here.
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | outline (default) is one widget per line; json is structured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint/idempotentHint/no-destructive/no-open-world, so the safety profile is covered; the description adds behavioral value by disclosing the scope of the payload (current frame, every widget, and the exact per-widget fields returned). It does not mention size limits, truncation, or pagination for large trees, which is the remaining gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One substantive sentence followed by a two-word directive; zero filler and the most decision-relevant content (what it returns, that it is the starting point) is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the return-value burden and discharges it by listing the per-widget fields. The only parameter is fully described in schema, and the read-only nature is covered by annotations, leaving nothing an agent needs to call this correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single optional 'format' parameter is fully documented in the schema (100% coverage, enum values with meanings), so the schema already does the work. The description adds nothing about the format choice or the default, which is the baseline expectation when coverage is complete.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Returns the semantic tree of the current frame') and enumerates exactly what each node contains (role, ID, label, value, state, bounds). That enumeration distinguishes it from siblings like screen, find, and status, which an agent can rule out without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Start here.' is an explicit, unambiguous entry-point instruction that tells the agent when to reach for this tool rather than a sibling. It stops short of naming alternatives or when-not-to-use conditions, so it is clear context without full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
type_textType textADestructive
Types text into the focused widget as one key press per character, then returns the tree after the application redraws. Does not press enter.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | The text to type. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and non-idempotent, so safety is covered. The description adds real behavioral context beyond them: character-by-character keystroke emulation, the post-redraw tree return, and the explicit non-submission of Enter.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences, front-loaded with the action and mechanism, with the non-Enter caveat placed last. Every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-param tool with no output schema, the description supplies enough: it explains the return value ('returns the tree after the application redraws') and the key behavioral caveat about Enter. Minor gaps remain, such as what happens if no widget is focused.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Only one parameter and schema description coverage is 100%, so the schema already documents 'text' fully. The description adds no format details (e.g., newline handling or special characters) beyond what the schema provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('types text into the focused widget') and describes the mechanism ('one key press per character'). It implicitly contrasts with press_key via 'Does not press enter', but never names the sibling, so it stops short of full sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied rather than stated: writing to the 'focused widget' signals the caller must focus it first, and 'Does not press enter' rules out submission. No explicit when-to-use/when-not or reference to alternatives like press_key or click is given.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
wait_forWait for a widgetARead-onlyIdempotent
Waits until a widget matching the selector is on screen, and returns the matches. Use this instead of polling tree when something appears asynchronously.
| Name | Required | Description | Default |
|---|---|---|---|
| id | No | The widget's ID, exactly as shown after # in the tree. | |
| nth | No | Which match to use when several qualify, 0-based; negative counts from the end. | |
| role | No | The semantic role. | |
| label | No | The label, matched exactly. | |
| value | No | The current value, matched exactly — for example the selected item of a list. | |
| timeout_ms | No | How long to wait, in milliseconds. Defaults to 5000. | |
| label_contains | No | A substring of the label, for labels that carry live data. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, non-destructive and closed-world, so the safety profile is covered. The description adds the blocking/waiting semantics and that it returns matches, but omits what happens on timeout (empty result vs. error) and whether the wait is measured against a fixed or retrying poll — a meaningful gap for a wait tool.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, zero padding, with the behavioral outcome front-loaded and the routing advice second. Every clause carries information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description carries the burden of describing the return value; 'returns the matches' is suggestive but does not say what a match contains or how many are returned relative to 'nth'. Timeout failure behavior is also left unstated, leaving the definition adequate but incomplete for a 7-parameter wait primitive.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% across all 7 parameters, so the schema already explains id, nth, role, label, value, timeout_ms and label_contains. The description adds only the generic phrase 'matching the selector' and hints at multiple matches, which justifies the baseline 3 rather than more.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (waits), resource (widget matching the selector), and outcome (returns the matches). It also explicitly distinguishes itself from sibling tooling by naming 'polling tree' as the thing it replaces.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a clear selection condition: use this instead of polling tree for asynchronously appearing widgets. It names an alternative but offers no when-not guidance (e.g., when a synchronous lookup like 'find' is preferable because the widget should already be present).
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
8 tool updates
v0.1.0- First observed
click - First observed
find - First observed
press_key - First observed
screen - First observed
status - First observed
tree - First observed
type_text - First observed
wait_for
TDQS
Scored across 8 tools
Each tool has a clearly distinct role: tree/screen/find/status for observation, click/press_key/type_text for input, and wait_for for synchronization. The descriptions explicitly resolve overlap (prefer tree, use screen only as fallback) and state edge cases like click refusing multiple matches. An agent can select the right tool without ambiguity.
Names mix three conventions: single nouns (screen, status, tree), bare verbs (find, click), and verb_noun (press_key, type_text, wait_for). The grouping is semantically logical and readable, but there is no single predictable syntactic pattern. This fits 'mixed conventions but still readable' rather than a consistent verb_noun scheme.
Eight tools is well-scoped for a GUI automation server, with each tool earning its place across the observe-act-wait loop. There is no obvious redundancy or padding, and the surface is neither thin nor heavy.
The core lifecycle of inspecting the UI, performing input, and waiting for changes is covered. However, common GUI interactions such as scroll, drag, double-click, and right-click are absent; press_key partially compensates for scrolling but not for pointer gestures. These are minor gaps an agent can often work around.
Maintenance
Related MCP Connectors
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Persistent memory, hybrid search and a goal graph for AI agents, over stdio or remote HTTP.
Drive real devices from your AI Coding tool. Embed a client SDK (Unity, Godot, Flutter, iOS/macOS, Android, React Native, Web) in your app, then capture screenshots, traverse the UI tree, inject taps and key events, and run automated test tasks on the physical device over a secure relay.
Control real Android and iOS devices with LLM agents — tap, swipe, type, automate flows.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to fully interact with console applications, monitor output, detect errors, and automate terminal workflows across multiple sessions. Similar to how Playwright works for web browsers but for command-line interfaces.138 npm51MIT
- AlicenseNot gradedqualityBmaintenanceMCP server enabling AI agents to interact with terminal applications through structured Terminal State Tree representation. Works with any AI assistant that supports the Model Context Protocol.41 npm21MIT
- AlicenseAqualityBmaintenanceInteractive PTY sessions for AI agents — local shells, SSH with persistent sessions (ai-tmux daemon for attach/detach), and serial ports. Single Go binary, no tmux dependency.1619MIT
- AlicenseNot gradedqualityCmaintenanceProvides AI agents with fully interactive terminal sessions, including TUI support, keyboard control, and screen capture across Windows, Linux, and Mac.MIT