A Model Context Protocol server for the i3 (and Sway) window manager, with tool definitions tuned for high tool-definition-quality scores.
Strong tool definitions with comprehensive descriptions, proper parameter typing, and clear risk annotations. All 13 tools have detailed descriptions (avg ~180 chars) explaining WHAT, WHEN, and consequences. Input schemas are complete with types, constraints (min/max, enums), and per-parameter descriptions. Tool names follow verb_noun pattern (exec_application, focus_window, kill_window). Risk annotations (DESTRUCTIVE, DESTRUCTIVE_OPEN, MUTATE_IDEMPOTENT, READ_ONLY) are explicit. Output schemas are documented in descriptions. Main gaps: no per-tool error recovery guidance, no batch variants for common multi-call patterns, and output field documentation could be more structured (e.g., explicit JSON schema for responses).
Launch an external program via i3's `exec`. DESTRUCTIVE / open-world: runs an arbitrary command line on the user's machine with their privileges — never pass untrusted input. Set `no_startup_id=true` for programs without startup-notification support (avoids a lingering busy cursor). For built-in i3 verbs use the dedicated tools instead — `focus_window`, `move_window`, `set_layout` — not this.
Move keyboard focus, selected by exactly one of: a direction; a specific window (class/title/instance/mark/con_id criteria); the parent/child container; the floating/tiling layer; or an output. Reversible. Supplying none or several selectors is rejected. To *move* the focused window instead of focusing, use `move_window`.
Inspect i3/Sway configuration state in one call: version, whether the compositor is Sway, the loaded config path and (optionally) its full text, plus configured and active binding modes. Read-only. Set `include_config_text=false` to skip the potentially large config body. This is the only config-introspection tool; live layout comes from `get_tree`.
Get the single currently-focused window's details (class/app_id, title, marks, geometry, workspace, output). Read-only, no parameters. Use this instead of scanning `get_tree` when you only need the active window; returns `focused: null` when nothing holds focus, e.g. on an empty workspace.
Output schemas not formally documented in JSON Schema format. Descriptions mention return types (e.g., 'returns matched leaf windows with their ids, marks and location') but no explicit response schema is visible in tool registration. LLMs cannot reliably parse unstructured output descriptions.
No error recovery guidance in tool descriptions. When a tool fails (e.g., 'window not found', 'invalid criteria'), descriptions do not tell the LLM what to do next. E.g., focus_window should suggest 'If window not found, call get_tree() to list available windows.'
No batch variants for multi-window operations. Agents calling focus_window or kill_window in a loop waste tokens and latency. A batch_focus_windows(window_ids: [int]) or batch_kill_windows(criteria: [Criteria]) would enable efficient multi-window operations.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | 2025-06-18+ | v2 |
List application windows from the i3/Sway layout tree, optionally filtered by class, title, instance, role, workspace, or floating/urgent state. Read-only. Returns matched leaf windows with their ids, marks and location; use a returned `id` as `con_id` in focus_window/move_window/kill_window. For only the active window use `get_focused`; for a flat workspace summary use `list_workspaces`; for monitors use `list_outputs`. Capped at 200 windows (`truncated` flags the cap).
Close a window — the focused one, or one matched by class/title/instance/mark/con_id. DESTRUCTIVE: the target application is asked to quit and may discard unsaved work; there is no undo, and matching several windows closes all of them. To move a window out of the way instead of closing it, use `move_window` (e.g. to the scratchpad).
List display outputs (monitors): name, active/primary state, current workspace and geometry. Read-only. Use before moving windows or workspaces between monitors with `move_window` or `manage_workspace`; for the workspaces themselves use `list_workspaces`.
List all active workspaces with number, name, visibility, focus, urgency and owning output. Read-only. Use this for navigation decisions; for the windows on a workspace use `get_tree` with a `workspace` filter, and for monitors use `list_outputs`.
Act on workspaces: `switch` to one, `move_container_to` one (optionally following), `rename` one, or `navigate` next/prev/back_and_forth. Reversible. `action` selects the operation and determines which of `name`/`new_name`/`direction` are required. To move a single window rather than switch, use `move_window`; to list workspaces use `list_workspaces`.
Move the focused container (or one matched by class/title/instance/mark/con_id) to exactly one destination: a direction (with pixel amount), a workspace, an output, the scratchpad, or a screen position (center). Reversible; returns per-command success. To change *focus* rather than move, use `focus_window`; to move whole workspaces between monitors, use `manage_workspace`.
Run a raw i3/Sway command string — the escape hatch for operations without a dedicated tool: marks (`mark`/`unmark`), i3-gaps (`gaps ...`), i3bar (`bar ...`), scratchpad show/hide, and `reload`/`restart`. DESTRUCTIVE / open-world: the payload is unrestricted and CAN include `kill`, `restart`, `reload`, or `exit` (which logs the user out), so validate before sending. Prefer the typed tools (`focus_window`, `move_window`, `set_layout`, ...) whenever one fits.
Set how the focused container arranges children: container `layout` (stacking/tabbed/split*), the `split` orientation for the next window, and/or the `border` style. Reversible. Provide at least one of layout/split/border; `border_width` applies only to the `pixel` border. For floating/fullscreen/sticky state use `toggle_window_state`.
Enable, disable, or toggle one boolean window state — `floating`, `fullscreen`, or `sticky` — on the focused window. Reversible. Omit `enable` to toggle, or set it true/false to force. `fullscreen_scope` picks per-output ('normal') vs across-all ('global'). For layout/border changes use `set_layout`.
Mutual exclusivity constraints documented in descriptions but not enforced at schema level. focus_window and move_window state 'Supplying none or several selectors is rejected' but JSON Schema does not express this (no oneOf/anyOf constraint). LLMs may pass multiple conflicting parameters.
get_tree result capped at 200 windows with truncated flag, but no pagination parameters (limit, offset, cursor). Large desktop environments may exceed 200 windows; agents cannot retrieve the full tree.