@mindfullabai/onda-mcp 0.1.0
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/LICENSE +21 -0
- package/README.md +119 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +459 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mindfull AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# @mindfullabai/onda-mcp
|
|
2
|
+
|
|
3
|
+
MCP server to control [Onda](https://onda.dev) terminal from AI agents (Claude Code, Cursor, Windsurf, etc.)
|
|
4
|
+
|
|
5
|
+
21 tools across 5 categories let AI agents split panes, run commands, manage tabs and workspaces, and orchestrate multi-agent workflows -- all through the standard [Model Context Protocol](https://modelcontextprotocol.io/).
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- **Onda terminal** running with IPC server active (automatic since v1.6)
|
|
10
|
+
- Node.js 18+
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### Claude Code
|
|
15
|
+
|
|
16
|
+
Add to your project's `.mcp.json`:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"mcpServers": {
|
|
21
|
+
"onda": {
|
|
22
|
+
"command": "npx",
|
|
23
|
+
"args": ["-y", "@mindfullabai/onda-mcp"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Claude Desktop
|
|
30
|
+
|
|
31
|
+
Add to `claude_desktop_config.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"onda": {
|
|
37
|
+
"command": "npx",
|
|
38
|
+
"args": ["-y", "@mindfullabai/onda-mcp"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Cursor / Windsurf
|
|
45
|
+
|
|
46
|
+
Same MCP config format as above.
|
|
47
|
+
|
|
48
|
+
## Available Tools
|
|
49
|
+
|
|
50
|
+
### Pane Management
|
|
51
|
+
|
|
52
|
+
| Tool | Description |
|
|
53
|
+
|------|-------------|
|
|
54
|
+
| `onda_pane_split` | Split the active pane. Creates a new terminal beside (`right`) or below (`down`) the current one. |
|
|
55
|
+
| `onda_pane_list` | List all panes in the active tab. Returns pane IDs, terminal IDs, and content types. |
|
|
56
|
+
| `onda_pane_close` | Close a specific pane by ID. If no ID given, closes the active pane. |
|
|
57
|
+
| `onda_pane_focus` | Focus (activate) a specific pane by its ID. |
|
|
58
|
+
|
|
59
|
+
### Terminal Control
|
|
60
|
+
|
|
61
|
+
| Tool | Description |
|
|
62
|
+
|------|-------------|
|
|
63
|
+
| `onda_terminal_run` | Run a command in a specific terminal (sends command + newline). |
|
|
64
|
+
| `onda_terminal_send` | Send raw text to a terminal without appending a newline. Use for interactive input or key sequences. |
|
|
65
|
+
| `onda_terminal_list` | List all active terminals with their IDs, PIDs, working directories, and alive status. |
|
|
66
|
+
| `onda_terminal_kill` | Kill a terminal process by ID. |
|
|
67
|
+
|
|
68
|
+
### Tab Management
|
|
69
|
+
|
|
70
|
+
| Tool | Description |
|
|
71
|
+
|------|-------------|
|
|
72
|
+
| `onda_tab_new` | Create a new tab. Each tab has its own layout of panes. |
|
|
73
|
+
| `onda_tab_list` | List all tabs with their IDs, titles, active state, and workspace. |
|
|
74
|
+
| `onda_tab_close` | Close a tab by ID. If no ID given, closes the active tab. |
|
|
75
|
+
| `onda_tab_focus` | Switch to (focus) a specific tab by ID. |
|
|
76
|
+
|
|
77
|
+
### Workspace Management
|
|
78
|
+
|
|
79
|
+
| Tool | Description |
|
|
80
|
+
|------|-------------|
|
|
81
|
+
| `onda_workspace_list` | List all workspaces with their IDs, names, root paths, and which is active. |
|
|
82
|
+
| `onda_workspace_create` | Create a new workspace. Workspaces group tabs by project. |
|
|
83
|
+
| `onda_workspace_focus` | Switch to a workspace by ID. |
|
|
84
|
+
| `onda_workspace_add_terminal` | Add a new terminal pane to a workspace (tiled mode). |
|
|
85
|
+
| `onda_workspace_tile` | Set the workspace tiling layout: `single`, `split-h`, `split-v`, or `quad`. |
|
|
86
|
+
|
|
87
|
+
### System
|
|
88
|
+
|
|
89
|
+
| Tool | Description |
|
|
90
|
+
|------|-------------|
|
|
91
|
+
| `onda_context` | Get the Onda context for the current AI agent (paneId, tabId, workspaceId). Call this first. |
|
|
92
|
+
| `onda_status` | Get current session state: active workspace, tab, pane, and their details. |
|
|
93
|
+
| `onda_app_info` | Get Onda app info: version, process ID. |
|
|
94
|
+
| `onda_ping` | Health check -- verify Onda is running and responsive. |
|
|
95
|
+
|
|
96
|
+
## Environment Variables
|
|
97
|
+
|
|
98
|
+
| Variable | Description | Default |
|
|
99
|
+
|----------|-------------|---------|
|
|
100
|
+
| `ONDA_SOCKET` | Path to Onda IPC socket | `~/.config/onda/onda.sock` |
|
|
101
|
+
| `ONDA_PANE_ID` | Pane ID where agent is running | auto-detected |
|
|
102
|
+
| `ONDA_TAB_ID` | Tab ID where agent is running | auto-detected |
|
|
103
|
+
| `ONDA_WORKSPACE_ID` | Workspace ID | auto-detected |
|
|
104
|
+
| `ONDA_TERMINAL` | Set to `"1"` when inside Onda | auto-detected |
|
|
105
|
+
|
|
106
|
+
## Architecture
|
|
107
|
+
|
|
108
|
+
- Communicates with Onda via **Unix domain socket**
|
|
109
|
+
- Protocol: **JSON-RPC 2.0** over newline-delimited JSON
|
|
110
|
+
- Socket path: `~/.config/onda/onda.sock`
|
|
111
|
+
- Transport: **stdio** (standard MCP transport)
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
AI Agent <--stdio--> onda-mcp <--Unix socket--> Onda Terminal
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## License
|
|
118
|
+
|
|
119
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Onda MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Onda terminal emulator controls as MCP tools.
|
|
6
|
+
* Connects to Onda's Unix domain socket (JSON-RPC 2.0).
|
|
7
|
+
*
|
|
8
|
+
* AI agents (Claude Code, Cursor, etc.) can use these tools to:
|
|
9
|
+
* - Split panes and create tabs
|
|
10
|
+
* - Run commands in terminals
|
|
11
|
+
* - Orchestrate multi-agent workflows
|
|
12
|
+
* - Query terminal and workspace state
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Onda MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Onda terminal emulator controls as MCP tools.
|
|
6
|
+
* Connects to Onda's Unix domain socket (JSON-RPC 2.0).
|
|
7
|
+
*
|
|
8
|
+
* AI agents (Claude Code, Cursor, etc.) can use these tools to:
|
|
9
|
+
* - Split panes and create tabs
|
|
10
|
+
* - Run commands in terminals
|
|
11
|
+
* - Orchestrate multi-agent workflows
|
|
12
|
+
* - Query terminal and workspace state
|
|
13
|
+
*/
|
|
14
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
15
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
16
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
17
|
+
import { Socket } from 'net';
|
|
18
|
+
import { join } from 'path';
|
|
19
|
+
import { homedir } from 'os';
|
|
20
|
+
// ─── JSON-RPC Client ─────────────────────────────────────────
|
|
21
|
+
const DEFAULT_SOCKET = join(homedir(), '.config', 'onda', 'onda.sock');
|
|
22
|
+
function getSocketPath() {
|
|
23
|
+
return process.env.ONDA_SOCKET || DEFAULT_SOCKET;
|
|
24
|
+
}
|
|
25
|
+
// Context from the terminal where this MCP server was launched
|
|
26
|
+
const ONDA_CONTEXT = {
|
|
27
|
+
paneId: process.env.ONDA_PANE_ID || null,
|
|
28
|
+
tabId: process.env.ONDA_TAB_ID || null,
|
|
29
|
+
workspaceId: process.env.ONDA_WORKSPACE_ID || null,
|
|
30
|
+
isOnda: process.env.ONDA_TERMINAL === '1',
|
|
31
|
+
};
|
|
32
|
+
let requestId = 0;
|
|
33
|
+
async function callOnda(method, params) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const socket = new Socket();
|
|
36
|
+
let buffer = '';
|
|
37
|
+
let settled = false;
|
|
38
|
+
const id = ++requestId;
|
|
39
|
+
const settle = (fn) => {
|
|
40
|
+
if (settled)
|
|
41
|
+
return;
|
|
42
|
+
settled = true;
|
|
43
|
+
fn();
|
|
44
|
+
socket.destroy();
|
|
45
|
+
};
|
|
46
|
+
const request = {
|
|
47
|
+
jsonrpc: '2.0',
|
|
48
|
+
method,
|
|
49
|
+
params,
|
|
50
|
+
id,
|
|
51
|
+
};
|
|
52
|
+
socket.on('connect', () => {
|
|
53
|
+
socket.write(JSON.stringify(request) + '\n');
|
|
54
|
+
});
|
|
55
|
+
socket.on('data', (chunk) => {
|
|
56
|
+
buffer += chunk.toString('utf8');
|
|
57
|
+
const lines = buffer.split('\n');
|
|
58
|
+
buffer = lines.pop() || ''; // Keep incomplete last line
|
|
59
|
+
for (const line of lines) {
|
|
60
|
+
if (!line.trim())
|
|
61
|
+
continue;
|
|
62
|
+
try {
|
|
63
|
+
const response = JSON.parse(line);
|
|
64
|
+
if (response.id !== id)
|
|
65
|
+
continue; // Skip mismatched responses
|
|
66
|
+
settle(() => {
|
|
67
|
+
if (response.error) {
|
|
68
|
+
reject(new Error(response.error.message));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
resolve(response.result);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Incomplete JSON, wait for more data
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
socket.on('error', (err) => {
|
|
82
|
+
settle(() => {
|
|
83
|
+
if (err.code === 'ENOENT' || err.code === 'ECONNREFUSED') {
|
|
84
|
+
reject(new Error('Onda is not running. Start Onda terminal first.'));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
reject(err);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
socket.on('timeout', () => {
|
|
92
|
+
settle(() => reject(new Error('Connection timeout')));
|
|
93
|
+
});
|
|
94
|
+
socket.connect(getSocketPath());
|
|
95
|
+
socket.setTimeout(5000);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
// ─── Tool Definitions ────────────────────────────────────────
|
|
99
|
+
const TOOLS = [
|
|
100
|
+
// --- Pane ---
|
|
101
|
+
{
|
|
102
|
+
name: 'onda_pane_split',
|
|
103
|
+
description: 'Split the active pane in Onda terminal. Creates a new terminal beside or below the current one. Use this to set up parallel workspaces for multi-agent workflows.',
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
direction: {
|
|
108
|
+
type: 'string',
|
|
109
|
+
enum: ['right', 'down'],
|
|
110
|
+
description: 'Where to place the new pane. "right" = side-by-side columns. "down" = stacked rows.',
|
|
111
|
+
},
|
|
112
|
+
cwd: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Working directory for the new pane. Defaults to current directory.',
|
|
115
|
+
},
|
|
116
|
+
shell: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Shell to use (e.g., /bin/zsh, /bin/bash). Defaults to user default.',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: 'onda_pane_list',
|
|
125
|
+
description: 'List all panes in the active tab. Returns pane IDs, terminal IDs, and content types. Use this to discover available terminals before sending commands.',
|
|
126
|
+
inputSchema: {
|
|
127
|
+
type: 'object',
|
|
128
|
+
properties: {},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'onda_pane_close',
|
|
133
|
+
description: 'Close a specific pane by ID. If no ID given, closes the active pane.',
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: 'object',
|
|
136
|
+
properties: {
|
|
137
|
+
id: { type: 'string', description: 'Pane ID to close. Omit to close active pane.' },
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'onda_pane_focus',
|
|
143
|
+
description: 'Focus (activate) a specific pane by its ID.',
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: 'object',
|
|
146
|
+
properties: {
|
|
147
|
+
id: { type: 'string', description: 'Pane ID to focus.' },
|
|
148
|
+
},
|
|
149
|
+
required: ['id'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
// --- Terminal ---
|
|
153
|
+
{
|
|
154
|
+
name: 'onda_terminal_run',
|
|
155
|
+
description: 'Run a command in a specific terminal (sends command + newline). Use this to execute shell commands, launch processes, or start other AI agents in separate terminals.',
|
|
156
|
+
inputSchema: {
|
|
157
|
+
type: 'object',
|
|
158
|
+
properties: {
|
|
159
|
+
id: { type: 'string', description: 'Terminal ID to run the command in.' },
|
|
160
|
+
command: { type: 'string', description: 'Shell command to execute.' },
|
|
161
|
+
},
|
|
162
|
+
required: ['id', 'command'],
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'onda_terminal_send',
|
|
167
|
+
description: 'Send raw text to a terminal without appending a newline. Use for interactive input, partial commands, or key sequences.',
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: 'object',
|
|
170
|
+
properties: {
|
|
171
|
+
id: { type: 'string', description: 'Terminal ID to send text to.' },
|
|
172
|
+
text: { type: 'string', description: 'Text to send (no trailing newline added).' },
|
|
173
|
+
},
|
|
174
|
+
required: ['id', 'text'],
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: 'onda_terminal_list',
|
|
179
|
+
description: 'List all active terminals with their IDs, PIDs, working directories, and alive status. Essential for discovering which terminals exist before running commands.',
|
|
180
|
+
inputSchema: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'onda_terminal_kill',
|
|
187
|
+
description: 'Kill a terminal process by ID.',
|
|
188
|
+
inputSchema: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
id: { type: 'string', description: 'Terminal ID to kill.' },
|
|
192
|
+
},
|
|
193
|
+
required: ['id'],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
// --- Tab ---
|
|
197
|
+
{
|
|
198
|
+
name: 'onda_tab_new',
|
|
199
|
+
description: 'Create a new tab in Onda. Each tab has its own layout of panes. Use this to isolate different workstreams.',
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: 'object',
|
|
202
|
+
properties: {
|
|
203
|
+
cwd: { type: 'string', description: 'Working directory for the new tab.' },
|
|
204
|
+
shell: { type: 'string', description: 'Shell to use.' },
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'onda_tab_list',
|
|
210
|
+
description: 'List all tabs with their IDs, titles, active state, and workspace.',
|
|
211
|
+
inputSchema: {
|
|
212
|
+
type: 'object',
|
|
213
|
+
properties: {},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
name: 'onda_tab_close',
|
|
218
|
+
description: 'Close a tab by ID. If no ID given, closes the active tab.',
|
|
219
|
+
inputSchema: {
|
|
220
|
+
type: 'object',
|
|
221
|
+
properties: {
|
|
222
|
+
id: { type: 'string', description: 'Tab ID to close. Omit to close active tab.' },
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
name: 'onda_tab_focus',
|
|
228
|
+
description: 'Switch to (focus) a specific tab by ID.',
|
|
229
|
+
inputSchema: {
|
|
230
|
+
type: 'object',
|
|
231
|
+
properties: {
|
|
232
|
+
id: { type: 'string', description: 'Tab ID to focus.' },
|
|
233
|
+
},
|
|
234
|
+
required: ['id'],
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
// --- Workspace ---
|
|
238
|
+
{
|
|
239
|
+
name: 'onda_workspace_list',
|
|
240
|
+
description: 'List all workspaces with their IDs, names, root paths, and which is active.',
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: 'object',
|
|
243
|
+
properties: {},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: 'onda_workspace_create',
|
|
248
|
+
description: 'Create a new workspace. Workspaces group tabs by project.',
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: 'object',
|
|
251
|
+
properties: {
|
|
252
|
+
name: { type: 'string', description: 'Workspace name.' },
|
|
253
|
+
rootPath: { type: 'string', description: 'Root directory path for the workspace.' },
|
|
254
|
+
color: { type: 'string', description: 'Color hex code (e.g., #a78bfa).' },
|
|
255
|
+
},
|
|
256
|
+
required: ['name', 'rootPath'],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
name: 'onda_workspace_focus',
|
|
261
|
+
description: 'Switch to a workspace by ID.',
|
|
262
|
+
inputSchema: {
|
|
263
|
+
type: 'object',
|
|
264
|
+
properties: {
|
|
265
|
+
id: { type: 'string', description: 'Workspace ID to switch to.' },
|
|
266
|
+
},
|
|
267
|
+
required: ['id'],
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: 'onda_workspace_add_terminal',
|
|
272
|
+
description: 'Add a new terminal pane to a workspace. Works in tiled mode to create terminals within workspace layout.',
|
|
273
|
+
inputSchema: {
|
|
274
|
+
type: 'object',
|
|
275
|
+
properties: {
|
|
276
|
+
workspaceId: { type: 'string', description: 'Workspace ID. Omit to use active workspace.' },
|
|
277
|
+
cwd: { type: 'string', description: 'Working directory for the new terminal.' },
|
|
278
|
+
shell: { type: 'string', description: 'Shell to use (e.g., /bin/zsh).' },
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: 'onda_workspace_tile',
|
|
284
|
+
description: 'Set the workspace-level tiling layout. Controls how workspaces are arranged in the main window.',
|
|
285
|
+
inputSchema: {
|
|
286
|
+
type: 'object',
|
|
287
|
+
properties: {
|
|
288
|
+
workspaceId: { type: 'string', description: 'Workspace ID. Omit to use active workspace.' },
|
|
289
|
+
layout: {
|
|
290
|
+
type: 'string',
|
|
291
|
+
enum: ['single', 'split-h', 'split-v', 'quad'],
|
|
292
|
+
description: 'Layout mode: single (one workspace), split-h (horizontal split), split-v (vertical split), quad (four quadrants).',
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
required: ['layout'],
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
// --- System ---
|
|
299
|
+
{
|
|
300
|
+
name: 'onda_context',
|
|
301
|
+
description: 'Get the Onda context for the terminal where this AI agent is running. Returns paneId, tabId, workspaceId. Use this FIRST to know which pane/tab you are in before splitting or sending commands.',
|
|
302
|
+
inputSchema: {
|
|
303
|
+
type: 'object',
|
|
304
|
+
properties: {},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
name: 'onda_status',
|
|
309
|
+
description: 'Get current Onda session state: active workspace, active tab, active pane, and their details. Use this to understand the current context before taking actions.',
|
|
310
|
+
inputSchema: {
|
|
311
|
+
type: 'object',
|
|
312
|
+
properties: {},
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
name: 'onda_app_info',
|
|
317
|
+
description: 'Get Onda app info: version, process ID.',
|
|
318
|
+
inputSchema: {
|
|
319
|
+
type: 'object',
|
|
320
|
+
properties: {},
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: 'onda_ping',
|
|
325
|
+
description: 'Health check - verify Onda is running and responsive.',
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: 'object',
|
|
328
|
+
properties: {},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
// ─── Tool → Method mapping ───────────────────────────────────
|
|
333
|
+
const TOOL_MAP = {
|
|
334
|
+
// Pane
|
|
335
|
+
onda_pane_split: {
|
|
336
|
+
method: 'pane.split',
|
|
337
|
+
mapParams: (args) => ({
|
|
338
|
+
// Onda IPC uses inverted naming: "vertical" = stacked (down), "horizontal" = side-by-side (right)
|
|
339
|
+
direction: args.direction === 'down' ? 'vertical' : 'horizontal',
|
|
340
|
+
cwd: args.cwd,
|
|
341
|
+
shell: args.shell,
|
|
342
|
+
}),
|
|
343
|
+
},
|
|
344
|
+
onda_pane_list: { method: 'pane.list' },
|
|
345
|
+
onda_pane_close: { method: 'pane.close' },
|
|
346
|
+
onda_pane_focus: { method: 'pane.focus' },
|
|
347
|
+
// Terminal
|
|
348
|
+
onda_terminal_run: { method: 'terminal.run' },
|
|
349
|
+
onda_terminal_send: { method: 'terminal.send' },
|
|
350
|
+
onda_terminal_list: { method: 'terminal.list' },
|
|
351
|
+
onda_terminal_kill: { method: 'terminal.kill' },
|
|
352
|
+
// Tab
|
|
353
|
+
onda_tab_new: {
|
|
354
|
+
method: 'tab.new',
|
|
355
|
+
mapParams: (args) => ({
|
|
356
|
+
...args,
|
|
357
|
+
// Auto-inject workspaceId from agent context so tab is created
|
|
358
|
+
// in the agent's workspace, not the currently active one
|
|
359
|
+
workspaceId: args.workspaceId || ONDA_CONTEXT.workspaceId || undefined,
|
|
360
|
+
}),
|
|
361
|
+
},
|
|
362
|
+
onda_tab_list: { method: 'tab.list' },
|
|
363
|
+
onda_tab_close: { method: 'tab.close' },
|
|
364
|
+
onda_tab_focus: { method: 'tab.focus' },
|
|
365
|
+
// Workspace
|
|
366
|
+
onda_workspace_list: { method: 'workspace.list' },
|
|
367
|
+
onda_workspace_create: { method: 'workspace.create' },
|
|
368
|
+
onda_workspace_focus: { method: 'workspace.focus' },
|
|
369
|
+
onda_workspace_add_terminal: {
|
|
370
|
+
method: 'workspace.addTerminal',
|
|
371
|
+
mapParams: (args) => ({
|
|
372
|
+
workspaceId: args.workspaceId || ONDA_CONTEXT.workspaceId || undefined,
|
|
373
|
+
cwd: args.cwd,
|
|
374
|
+
shell: args.shell,
|
|
375
|
+
}),
|
|
376
|
+
},
|
|
377
|
+
onda_workspace_tile: { method: 'workspace.setLayout' },
|
|
378
|
+
// System (onda_context handled separately - it's local, not RPC)
|
|
379
|
+
onda_status: { method: 'session.current' },
|
|
380
|
+
onda_app_info: { method: 'app.info' },
|
|
381
|
+
onda_ping: { method: 'app.ping' },
|
|
382
|
+
};
|
|
383
|
+
// ─── MCP Server ──────────────────────────────────────────────
|
|
384
|
+
const server = new Server({
|
|
385
|
+
name: 'onda',
|
|
386
|
+
version: '0.1.0',
|
|
387
|
+
}, {
|
|
388
|
+
capabilities: {
|
|
389
|
+
tools: {},
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
393
|
+
tools: TOOLS,
|
|
394
|
+
}));
|
|
395
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
396
|
+
const { name, arguments: args } = request.params;
|
|
397
|
+
// onda_context: local env vars + live session info from app
|
|
398
|
+
if (name === 'onda_context') {
|
|
399
|
+
let sessionInfo = null;
|
|
400
|
+
try {
|
|
401
|
+
sessionInfo = await callOnda('session.current');
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// App may not be reachable, return env-only context
|
|
405
|
+
}
|
|
406
|
+
const context = {
|
|
407
|
+
...ONDA_CONTEXT,
|
|
408
|
+
uiMode: sessionInfo?.uiMode || process.env.ONDA_UI_MODE || null,
|
|
409
|
+
};
|
|
410
|
+
return {
|
|
411
|
+
content: [{
|
|
412
|
+
type: 'text',
|
|
413
|
+
text: JSON.stringify(context, null, 2),
|
|
414
|
+
}],
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
const mapping = TOOL_MAP[name];
|
|
418
|
+
if (!mapping) {
|
|
419
|
+
return {
|
|
420
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
421
|
+
isError: true,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
// Auto-focus our pane before split so it splits in the right place
|
|
426
|
+
if (name === 'onda_pane_split' && ONDA_CONTEXT.paneId) {
|
|
427
|
+
await callOnda('pane.focus', { id: ONDA_CONTEXT.paneId });
|
|
428
|
+
}
|
|
429
|
+
const params = mapping.mapParams
|
|
430
|
+
? mapping.mapParams((args || {}))
|
|
431
|
+
: (args || {});
|
|
432
|
+
const result = await callOnda(mapping.method, params);
|
|
433
|
+
return {
|
|
434
|
+
content: [
|
|
435
|
+
{
|
|
436
|
+
type: 'text',
|
|
437
|
+
text: JSON.stringify(result, null, 2),
|
|
438
|
+
},
|
|
439
|
+
],
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
444
|
+
return {
|
|
445
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
446
|
+
isError: true,
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
// ─── Start ───────────────────────────────────────────────────
|
|
451
|
+
async function main() {
|
|
452
|
+
const transport = new StdioServerTransport();
|
|
453
|
+
await server.connect(transport);
|
|
454
|
+
console.error(`[onda-mcp] Server started, socket: ${getSocketPath()}`);
|
|
455
|
+
}
|
|
456
|
+
main().catch((error) => {
|
|
457
|
+
console.error('[onda-mcp] Fatal error:', error);
|
|
458
|
+
process.exit(1);
|
|
459
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mindfullabai/onda-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for Onda terminal - control tabs, panes, terminals from AI agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"onda-mcp": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/mindfullabai/onda-mcp",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/mindfullabai/onda-mcp.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/mindfullabai/onda-mcp/issues"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mcp",
|
|
27
|
+
"onda",
|
|
28
|
+
"terminal",
|
|
29
|
+
"ai-agent",
|
|
30
|
+
"model-context-protocol",
|
|
31
|
+
"claude"
|
|
32
|
+
],
|
|
33
|
+
"author": "Mindfull AI <hello@mindfull.ai>",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"files": [
|
|
36
|
+
"dist"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
40
|
+
"dev": "tsx src/index.ts"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^25.2.3",
|
|
47
|
+
"tsx": "^4.19.0",
|
|
48
|
+
"typescript": "^5.7.0"
|
|
49
|
+
}
|
|
50
|
+
}
|