@mindfullabai/onda-mcp 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  MCP server to control [Onda](https://onda.dev) terminal from AI agents (Claude Code, Cursor, Windsurf, etc.)
4
4
 
5
- 28 tools across 7 categories let AI agents split panes, run commands, manage tabs and workspaces, coordinate across multiple windows, and orchestrate multi-agent workflows -- all through the standard [Model Context Protocol](https://modelcontextprotocol.io/).
5
+ 31 tools across 7 categories let AI agents split panes, run commands, manage tabs and workspaces, coordinate across multiple windows, and orchestrate multi-agent workflows -- all through the standard [Model Context Protocol](https://modelcontextprotocol.io/).
6
6
 
7
7
  Since v0.2.0 Onda MCP is **multi-window aware**: agents can discover windows, locate workspaces, address terminals unambiguously (each entry carries `windowId` + `workspaceId` + `paneId`), and launch full Claude/agent sessions in one atomic call with the `onda_launch_session` macro.
8
8
 
@@ -94,6 +94,9 @@ Same MCP config format as above.
94
94
  |------|-------------|
95
95
  | `onda_window_list` | List Onda main windows. Each entry: `{windowId, isFocused, title, workspaceIds[], activeWorkspaceId, uiMode}`. |
96
96
  | `onda_window_new` | Open a fresh empty main window. Returns `{windowId}`. |
97
+ | `onda_window_focus` | Bring a window to the foreground (restore + raise + focus). When `windowId` is omitted, focuses the primary window. |
98
+ | `onda_window_mount_workspace` | Mount a workspace in a specific window. Idempotent; orchestrates an atomic transfer if the workspace is currently owned by another window. Returns `{success, workspaceId, windowId, transferred}`. |
99
+ | `onda_workspace_unmount` | Remove a workspace from whichever window currently hosts it. The workspace continues to exist globally — only its on-screen mounting is dropped. Returns `{success, workspaceId, windowId, alreadyUnmounted}`. |
97
100
 
98
101
  ### Macro
99
102
 
package/dist/index.js CHANGED
@@ -487,6 +487,48 @@ const TOOLS = [
487
487
  description: 'Open a fresh empty main window (analogue of File > New Window). Returns { windowId }. Useful when an agent needs to host a workspace in a brand new window without contaminating existing ones.',
488
488
  inputSchema: { type: 'object', properties: {} },
489
489
  },
490
+ {
491
+ name: 'onda_window_focus',
492
+ description: 'Bring a specific main window to the foreground (restore if minimized, raise, focus). When windowId is omitted, focuses the primary window. Returns { success, windowId }. Use after mounting a workspace remotely, or to wake Onda from a background CLI subagent.',
493
+ inputSchema: {
494
+ type: 'object',
495
+ properties: {
496
+ windowId: { type: 'string', description: 'Target window id. Omit to focus the primary window.' },
497
+ },
498
+ },
499
+ },
500
+ {
501
+ name: 'onda_window_mount_workspace',
502
+ description: 'Mount a workspace in a specific window. Idempotent when the workspace is already there. If the workspace is currently mounted in another window, orchestrates an atomic transfer via the unmount-request flow (same path the in-app "Move workspace here?" dialog uses). Returns { success, workspaceId, windowId, transferred }. Pass `direction` to control how the new tile is spliced into the mosaic (mirrors the Cmd+P picker: Enter = down, Cmd+Enter = right).',
503
+ inputSchema: {
504
+ type: 'object',
505
+ properties: {
506
+ workspaceId: { type: 'string', description: 'Workspace to mount.' },
507
+ windowId: { type: 'string', description: 'Destination window id.' },
508
+ direction: {
509
+ type: 'string',
510
+ enum: ['down', 'right'],
511
+ description: 'Optional. Where to splice the new tile relative to the anchor workspace in the mosaic. Default: "down".',
512
+ },
513
+ anchorWorkspaceId: {
514
+ type: 'string',
515
+ description: 'Optional. Workspace id to anchor the split next to. Default: currently active workspace in the target window.',
516
+ },
517
+ },
518
+ required: ['workspaceId', 'windowId'],
519
+ },
520
+ },
521
+ {
522
+ name: 'onda_workspace_unmount',
523
+ description: 'Remove a workspace from whichever window currently hosts it (drops it from that window\'s tiled mosaic and releases the mount registry slot). The workspace continues to exist in the global list — only its on-screen mounting is dropped. Returns { success, workspaceId, windowId, alreadyUnmounted }.',
524
+ inputSchema: {
525
+ type: 'object',
526
+ properties: {
527
+ workspaceId: { type: 'string', description: 'Workspace to unmount.' },
528
+ },
529
+ required: ['workspaceId'],
530
+ },
531
+ },
490
532
  // --- Advanced terminal spawn ---
491
533
  {
492
534
  name: 'onda_terminal_spawn',
@@ -629,6 +671,9 @@ const TOOL_MAP = {
629
671
  workspaceId: args.workspaceId || ONDA_CONTEXT.workspaceId || undefined,
630
672
  cwd: args.cwd,
631
673
  shell: args.shell,
674
+ waitForReady: args.waitForReady,
675
+ direction: args.direction,
676
+ relativeToPaneId: args.relativeToPaneId,
632
677
  }),
633
678
  },
634
679
  onda_workspace_tile: { method: 'workspace.setLayout' },
@@ -636,6 +681,9 @@ const TOOL_MAP = {
636
681
  // Window
637
682
  onda_window_list: { method: 'window.list' },
638
683
  onda_window_new: { method: 'window.new' },
684
+ onda_window_focus: { method: 'window.focus' },
685
+ onda_window_mount_workspace: { method: 'window.mountWorkspace' },
686
+ onda_workspace_unmount: { method: 'workspace.unmount' },
639
687
  // Advanced spawn + macro
640
688
  onda_terminal_spawn: {
641
689
  method: 'terminal.spawnInPane',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindfullabai/onda-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "MCP server for Onda terminal - control tabs, panes, terminals from AI agents",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -2,13 +2,22 @@
2
2
  name: onda-mcp-usage
3
3
  description: Best practices for driving Onda (terminal emulator) from Claude Code via the `onda` MCP server (`mcp__onda__*`). Covers workspaces/windows/panes/terminals + buffer reading (read/subscribe/poll/wait_for/send_keys) + spatial awareness (layout/screenshot). Use whenever you call any `mcp__onda__*` tool or the user mentions Onda, pane, workspace, terminal listener, presence badge, inception loop (Claude controlling Onda from inside Onda). Triggers — onda mcp, drive onda, control onda terminal, show pane, workspace layout, onda screenshot, kai watcher, terminal listener.
4
4
  metadata:
5
- version: 0.2.0
5
+ version: 0.3.1
6
6
  source: '@mindfullabai/onda-mcp'
7
7
  ---
8
8
 
9
9
  # Onda MCP usage skill
10
10
 
11
- This skill tells you **how to correctly use the 38 `mcp__onda__*` tools** exposed by the `onda-mcp` server. It is installed alongside the MCP server. Without this guide you hit non-obvious pitfalls (subscribe AFTER run loses output, `down`/`right` mapping inverted pre-fix, UI cache refresh).
11
+ This skill tells you **how to correctly use the `mcp__onda__*` tools** exposed by the `onda-mcp` server (~40 tools as of 0.3.1). It is installed alongside the MCP server. Without this guide you miss non-obvious patterns (subscribe vs read, direction semantics, multi-window orchestration, inception loop).
12
+
13
+ ## Compatibility note
14
+
15
+ Some bug fixes shipped in `@mindfullabai/onda-mcp@0.3.1` + Onda 1.9.1+:
16
+ - `terminal_read` now returns the full buffer even on first call (eager tap attach at PTY spawn). Pre-0.3.1 a fresh terminal returned an empty buffer until `subscribe` was active.
17
+ - `workspace_add_terminal` now respects `direction='down'` (was silently rewritten to `'row'` whenever the anchor was already in a row split).
18
+ - `window_mount_workspace` now actually splices the workspace into the tiled mosaic (pre-fix the registry was updated but the UI tab/tile never appeared). Accepts `direction` ('down'|'right', default 'down') and `anchorWorkspaceId` mirroring the Cmd+P picker.
19
+
20
+ If you are talking to an older host, fall back to the legacy patterns: subscribe before run, avoid direction='down' on add_terminal, click-mount via the user.
12
21
 
13
22
  ## Mental model
14
23
 
@@ -30,6 +39,10 @@ Internal workspace layout = mosaic-component tree. Read it via `onda_workspace_l
30
39
  | "How is workspace X laid out" | `onda_workspace_layout` |
31
40
  | "I want to see what the user sees" | `onda_window_screenshot` |
32
41
  | "Open a new terminal right/below pane X" | `onda_workspace_add_terminal` with `direction` + `relativeToPaneId` |
42
+ | "Mount workspace W in window X as a new tile" | `onda_window_mount_workspace` with `windowId` + `direction` ('down' or 'right') |
43
+ | "Move workspace W from one window to another" | `onda_window_mount_workspace` with target `windowId` (atomic transfer, returns `transferred: true`) |
44
+ | "Drop workspace from its window without deleting it" | `onda_workspace_unmount` (registry slot released, PTYs survive) |
45
+ | "Bring a window to front" | `onda_window_focus` (omit `windowId` to focus primary) |
33
46
  | "Read what the terminal has printed so far" | `onda_terminal_read` (no subscribe needed) |
34
47
  | "I want to receive every new output chunk" | `subscribe` + `poll` loop |
35
48
  | "Wait until the build finishes" | `onda_terminal_wait_for` with regex |
@@ -40,16 +53,18 @@ Internal workspace layout = mosaic-component tree. Read it via `onda_workspace_l
40
53
 
41
54
  ## Pattern: working with terminal buffer (reads)
42
55
 
43
- **Anti-pattern**: call `terminal.run` and THEN `terminal.read`. The tap is created lazily on first `read`/`subscribe`, and PTY data emitted before the tap exists is **lost**.
56
+ Onda 1.9.1+ attaches the MCP tap **eagerly at PTY spawn**, so `terminal_read` and `subscribe.bufferSnapshot` return the full history regardless of when you first ask. The pre-0.3.1 anti-pattern (subscribe-before-run) is no longer required for correctness.
44
57
 
45
58
  ```
46
- run → read (you lose the run's output)
47
- read|subscribe → run → read|poll (captures everything)
59
+ run → read (run + check, works post-fix)
60
+ ✓ subscribe → run → poll loop (live streaming + history snapshot)
61
+ ✓ wait_for(pattern) (synchronize on a substring)
48
62
  ```
49
63
 
50
64
  Ring buffer size:
51
- - **200 KB** when no listener (`read`-only mode)
52
- - **1 MB** when at least one `subscribe` is active
65
+ - **200 KB** when no listener is attached (`read`-only mode)
66
+ - **1 MB** when at least one `subscribe` is active (auto-promoted)
67
+ - Shrinks back to 200 KB after the last `unsubscribe`, without losing the tap
53
68
 
54
69
  For high-throughput output (`find /`, `tail -F` on logs) the ring saturates. Use `wait_for` with a precise pattern instead of subscribe + scan.
55
70
 
@@ -106,6 +121,36 @@ new = workspace_add_terminal(workspaceId,
106
121
 
107
122
  Use `down` when the new pane will emit continuous output (logs, build). Use `right` for parallel work terminals.
108
123
 
124
+ ## Pattern: multi-window mosaic orchestration
125
+
126
+ A workspace lives in **at most one window at a time**. `window_mount_workspace` is idempotent and handles three cases:
127
+
128
+ ```
129
+ 1. Workspace unmounted → direct claim into target window (transferred: false)
130
+ 2. Already in target → no-op (transferred: false)
131
+ 3. Mounted elsewhere → atomic transfer via unmount-request/ack (transferred: true)
132
+ ```
133
+
134
+ ```
135
+ # Setup: workspace X visible as a tile under the current window
136
+ window_mount_workspace(workspaceId=X, windowId=current, direction="down")
137
+
138
+ # Move workspace X to a fresh window
139
+ new_id = window_new().windowId
140
+ window_mount_workspace(workspaceId=X, windowId=new_id, direction="down")
141
+ # returns { transferred: true }
142
+ window_focus(new_id) # bring the new window to front
143
+ ```
144
+
145
+ `direction` mirrors the Cmd+P picker semantics: `down` = stack below the anchor workspace (Enter), `right` = side-by-side (Cmd+Enter). `anchorWorkspaceId` lets you pick which existing tile to split next to; default is the target window's current active workspace.
146
+
147
+ To drop a workspace from its window without destroying it:
148
+ ```
149
+ workspace_unmount(workspaceId=X)
150
+ # registry slot freed, workspace still exists in global list
151
+ # PTYs inside it stay alive (orphaned terminals) until kill or app restart
152
+ ```
153
+
109
154
  ## Pattern: visual verification
110
155
 
111
156
  When you need to visually confirm an action succeeded (layout changed, badge appeared, modal opened) use `window_screenshot`:
@@ -257,7 +302,7 @@ Con: prompt cache miss every 15 min (cache threshold 5 min), non-zero token cost
257
302
  2. **`terminal_send` with raw bytes** (`"\x03"` for Ctrl+C): nope. Use `send_keys(["Ctrl+C"])`.
258
303
  3. **`workspace_add_terminal` without prior `workspace_layout`**: blind placement. Append-right quickly becomes unreadable.
259
304
  4. **Listener without unsubscribe**: leak for 30 min TTL. UI badge stays. Clean up.
260
- 5. **`window_new`** when the user just wants "one more workspace on the right": NO the user prefers **mounting the workspace as a tile in the existing window** (today the MCP tool for this is missing use manual click flow with `tell me when you clicked`).
305
+ 5. **`window_new`** when the user just wants "one more workspace on the right": NO. Use `window_mount_workspace(workspaceId, windowId=current, direction='right')` to splice it into the existing window as a side-by-side tile. Open a new window only when the user actually wants two separate windows.
261
306
  6. **Assuming `app_info.name === "Onda-dev"`** distinguishes dev vs prod: NO. Use `app_info.path` or an explicit flag.
262
307
  7. **`terminal_run` alone to submit a prompt to Claude TUI**: NO. The TUI treats `\n` as newline buffer. You must always follow with `send_keys(["Enter"])` to actually submit. (Validated 21 May 2026 spawning a dedicated Kai.)
263
308
  8. **Spawning Kai and then waiting for Mario to click and type**: anti-pattern. If you spawned the session, you drive it to "Kai is working" (pattern "drive a remote Claude Code session"). Mario shouldn't be a human postman in your agent team.