mcp-ide 0.1.0 → 0.1.1
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/.claude-plugin/skills/start/SKILL.md +58 -85
- package/README.md +16 -19
- package/dist/index.js +118 -410
- package/package.json +1 -1
|
@@ -8,43 +8,63 @@ allowed-tools: mcp__plugin_ide_ide__*
|
|
|
8
8
|
|
|
9
9
|
Start and manage your development environment with terminals, processes, interactive Ink components, and dashboards.
|
|
10
10
|
|
|
11
|
+
## Tools (8 total)
|
|
12
|
+
|
|
13
|
+
### Process Management (require mide.yaml)
|
|
14
|
+
|
|
15
|
+
| Tool | Description |
|
|
16
|
+
|------|-------------|
|
|
17
|
+
| `list_processes` | List all processes with status, port, URL, health |
|
|
18
|
+
| `manage_process(name, op)` | Start, stop, or restart a process |
|
|
19
|
+
| `get_logs(name, tail?)` | Get process logs |
|
|
20
|
+
|
|
21
|
+
### Pane Management
|
|
22
|
+
|
|
23
|
+
| Tool | Description |
|
|
24
|
+
|------|-------------|
|
|
25
|
+
| `create_pane(name, command)` | Create a terminal pane |
|
|
26
|
+
| `create_interaction(schema?, ink_file?)` | Show interactive Ink form/component |
|
|
27
|
+
| `remove_pane(name)` | Remove a pane |
|
|
28
|
+
| `capture_pane(name, lines?)` | Capture terminal output |
|
|
29
|
+
|
|
30
|
+
### Status
|
|
31
|
+
|
|
32
|
+
| Tool | Description |
|
|
33
|
+
|------|-------------|
|
|
34
|
+
| `set_status(status, message?)` | Update window title/status |
|
|
35
|
+
|
|
11
36
|
## Starting the Environment
|
|
12
37
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
38
|
+
```
|
|
39
|
+
list_processes() // Initializes tmux session, shows all processes
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Managing Processes
|
|
17
43
|
|
|
18
44
|
```
|
|
19
|
-
|
|
45
|
+
manage_process(name: "api", op: "start")
|
|
46
|
+
manage_process(name: "api", op: "stop")
|
|
47
|
+
manage_process(name: "api", op: "restart")
|
|
20
48
|
```
|
|
21
49
|
|
|
22
|
-
## Terminal
|
|
50
|
+
## Creating Terminal Panes
|
|
23
51
|
|
|
24
|
-
### `create_pane`
|
|
25
|
-
Create a terminal pane running any command. Use for dev servers, build commands, or any shell process.
|
|
26
52
|
```
|
|
27
53
|
create_pane(name: "dev-server", command: "npm run dev")
|
|
28
|
-
create_pane(name: "tests", command: "npm test --watch"
|
|
54
|
+
create_pane(name: "tests", command: "npm test --watch")
|
|
29
55
|
```
|
|
30
56
|
|
|
31
|
-
### `remove_pane`
|
|
32
|
-
Remove a terminal pane by name.
|
|
33
|
-
|
|
34
57
|
## Interactive Ink Components
|
|
35
58
|
|
|
36
|
-
|
|
37
|
-
Show interactive Ink components for user input, TUI dashboards, or any terminal UI.
|
|
38
|
-
|
|
39
|
-
**Schema mode** - Define forms inline (no file needed):
|
|
59
|
+
**Schema mode** - Define forms inline:
|
|
40
60
|
```
|
|
41
|
-
|
|
61
|
+
create_interaction(
|
|
42
62
|
schema: {
|
|
43
63
|
questions: [
|
|
44
64
|
{ question: "What's your name?", header: "Name", inputType: "text" },
|
|
45
65
|
{ question: "Select role", header: "Role", options: [
|
|
46
|
-
{ label: "Developer"
|
|
47
|
-
{ label: "Designer"
|
|
66
|
+
{ label: "Developer" },
|
|
67
|
+
{ label: "Designer" }
|
|
48
68
|
]}
|
|
49
69
|
]
|
|
50
70
|
},
|
|
@@ -52,103 +72,56 @@ show_interaction(
|
|
|
52
72
|
)
|
|
53
73
|
```
|
|
54
74
|
|
|
55
|
-
**File mode** - Run custom Ink components
|
|
75
|
+
**File mode** - Run custom Ink components:
|
|
56
76
|
```
|
|
57
|
-
|
|
58
|
-
show_interaction(ink_file: "dashboard.tsx")
|
|
77
|
+
create_interaction(ink_file: "color-picker.tsx", title: "Pick a Color")
|
|
59
78
|
```
|
|
60
79
|
|
|
61
|
-
File resolution
|
|
62
|
-
1. Absolute paths used as-is
|
|
63
|
-
2. Project `.mide/interactive/` directory
|
|
64
|
-
3. Global `~/.mide/interactive/` directory
|
|
65
|
-
|
|
66
|
-
### `get_interaction_result`
|
|
67
|
-
Get result from a non-blocking interaction (when `block: false`).
|
|
68
|
-
|
|
69
|
-
### `cancel_interaction`
|
|
70
|
-
Cancel a pending interaction.
|
|
80
|
+
File resolution: `.mide/interactive/` → `~/.mide/interactive/`
|
|
71
81
|
|
|
72
|
-
## Writing
|
|
82
|
+
## Writing Ink Components
|
|
73
83
|
|
|
74
|
-
Create `.tsx` files in `.mide/interactive
|
|
84
|
+
Create `.tsx` files in `.mide/interactive/`:
|
|
75
85
|
|
|
76
86
|
```tsx
|
|
77
87
|
import { Box, Text, useInput, useApp } from 'ink';
|
|
78
88
|
import { useState } from 'react';
|
|
79
89
|
|
|
80
|
-
// onComplete is injected globally - call it to return data
|
|
81
90
|
declare const onComplete: (result: unknown) => void;
|
|
82
91
|
|
|
83
92
|
function MyComponent() {
|
|
84
93
|
const { exit } = useApp();
|
|
85
|
-
const [value, setValue] = useState('');
|
|
86
94
|
|
|
87
95
|
useInput((input, key) => {
|
|
88
96
|
if (key.return) {
|
|
89
|
-
onComplete({
|
|
90
|
-
exit(); // Close the component
|
|
91
|
-
}
|
|
92
|
-
if (key.escape) {
|
|
93
|
-
onComplete({ cancelled: true });
|
|
97
|
+
onComplete({ value: "done" });
|
|
94
98
|
exit();
|
|
95
99
|
}
|
|
96
100
|
});
|
|
97
101
|
|
|
98
|
-
return
|
|
99
|
-
<Box flexDirection="column">
|
|
100
|
-
<Text bold>My Interactive Component</Text>
|
|
101
|
-
<Text>Press Enter to confirm, Escape to cancel</Text>
|
|
102
|
-
</Box>
|
|
103
|
-
);
|
|
102
|
+
return <Text>Press Enter to confirm</Text>;
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
export default MyComponent;
|
|
105
|
+
export default MyComponent;
|
|
107
106
|
```
|
|
108
107
|
|
|
109
|
-
**Available imports:**
|
|
110
|
-
- `ink` - Box, Text, useInput, useApp, useFocus, Newline, Spacer, Static, Transform
|
|
111
|
-
- `ink-text-input` - TextInput component
|
|
112
|
-
- `ink-select-input` - SelectInput component
|
|
113
|
-
- `react` - useState, useEffect, useMemo, useCallback, etc.
|
|
114
|
-
|
|
115
|
-
**Key patterns:**
|
|
116
|
-
- `useInput((input, key) => {...})` - Handle keyboard input
|
|
117
|
-
- `useApp().exit()` - Close the component
|
|
118
|
-
- `onComplete(data)` - Return result to Claude (global function)
|
|
119
|
-
- `useState` - Manage component state
|
|
108
|
+
**Available imports:** `ink`, `ink-text-input`, `ink-select-input`, `react`
|
|
120
109
|
|
|
121
|
-
|
|
122
|
-
Update the terminal window title/status indicator.
|
|
110
|
+
## Capturing Terminal Output
|
|
123
111
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
- `get_logs` - Get stdout/stderr logs
|
|
129
|
-
- `get_url` - Get the preview URL for a process
|
|
130
|
-
- `start_process` - Start a stopped process
|
|
131
|
-
- `stop_process` - Stop a running process
|
|
132
|
-
- `restart_process` - Restart a process
|
|
112
|
+
```
|
|
113
|
+
capture_pane(name: "dev-server", lines: 50)
|
|
114
|
+
// Returns last 50 lines of terminal output
|
|
115
|
+
```
|
|
133
116
|
|
|
134
117
|
## When to Use
|
|
135
118
|
|
|
136
119
|
| User Intent | Tool |
|
|
137
120
|
|-------------|------|
|
|
138
121
|
| "start dev environment" | `list_processes` |
|
|
139
|
-
| "run a command
|
|
140
|
-
| "ask user a question" | `
|
|
141
|
-
| "show a
|
|
142
|
-
| "
|
|
143
|
-
| "
|
|
122
|
+
| "run a command" | `create_pane` |
|
|
123
|
+
| "ask user a question" | `create_interaction` with schema |
|
|
124
|
+
| "show a picker" | `create_interaction` with ink_file |
|
|
125
|
+
| "what's in the terminal" | `capture_pane` |
|
|
126
|
+
| "restart the API" | `manage_process(op: "restart")` |
|
|
144
127
|
| "show me the logs" | `get_logs` |
|
|
145
|
-
| "restart the API" | `restart_process` |
|
|
146
|
-
|
|
147
|
-
## Best Practices
|
|
148
|
-
|
|
149
|
-
1. Use `list_processes` first to initialize the environment
|
|
150
|
-
2. Use `show_interaction` for structured user input instead of asking in chat
|
|
151
|
-
3. For simple questions, use schema mode (no file needed)
|
|
152
|
-
4. For complex UIs, create Ink components in `.mide/interactive/`
|
|
153
|
-
5. Use `create_pane` for long-running processes you want visible
|
|
154
|
-
6. Check process status before suggesting restarts
|
package/README.md
CHANGED
|
@@ -50,39 +50,36 @@ processes:
|
|
|
50
50
|
> "restart the frontend"
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
## MCP Tools
|
|
53
|
+
## MCP Tools (8 total)
|
|
54
54
|
|
|
55
55
|
### Process Management
|
|
56
56
|
|
|
57
57
|
| Tool | Description |
|
|
58
58
|
|------|-------------|
|
|
59
|
-
| `list_processes` | List all processes with status |
|
|
60
|
-
| `
|
|
61
|
-
| `stop_process(name)` | Stop a process |
|
|
62
|
-
| `restart_process(name)` | Restart a process |
|
|
63
|
-
| `get_status(name)` | Get detailed status |
|
|
59
|
+
| `list_processes` | List all processes with status, port, URL, health |
|
|
60
|
+
| `manage_process(name, op)` | Start, stop, or restart a process |
|
|
64
61
|
| `get_logs(name, tail?)` | Get process logs |
|
|
65
|
-
| `get_url(name)` | Get process URL |
|
|
66
62
|
|
|
67
|
-
###
|
|
63
|
+
### Panes
|
|
68
64
|
|
|
69
65
|
| Tool | Description |
|
|
70
66
|
|------|-------------|
|
|
71
|
-
| `create_pane(name, command
|
|
72
|
-
| `
|
|
73
|
-
| `
|
|
67
|
+
| `create_pane(name, command)` | Create a terminal pane |
|
|
68
|
+
| `create_interaction(schema?, ink_file?)` | Show interactive Ink form/component |
|
|
69
|
+
| `remove_pane(name)` | Remove a pane |
|
|
70
|
+
| `capture_pane(name, lines?)` | Capture terminal output |
|
|
74
71
|
|
|
75
|
-
###
|
|
72
|
+
### Status
|
|
76
73
|
|
|
77
74
|
| Tool | Description |
|
|
78
75
|
|------|-------------|
|
|
79
|
-
| `
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
| `set_status(status, message?)` | Update window title/status |
|
|
77
|
+
|
|
78
|
+
## Interactive Ink Components
|
|
82
79
|
|
|
83
80
|
**Schema mode** - Define forms inline:
|
|
84
81
|
```typescript
|
|
85
|
-
|
|
82
|
+
create_interaction({
|
|
86
83
|
schema: {
|
|
87
84
|
questions: [
|
|
88
85
|
{ question: "What's your name?", header: "Name", inputType: "text" },
|
|
@@ -94,14 +91,14 @@ show_interaction({
|
|
|
94
91
|
},
|
|
95
92
|
title: "User Setup"
|
|
96
93
|
})
|
|
94
|
+
// Returns: { action: "accept", answers: { Name: "John", Role: "Developer" } }
|
|
97
95
|
```
|
|
98
96
|
|
|
99
97
|
**File mode** - Run custom Ink components:
|
|
100
98
|
```typescript
|
|
101
|
-
|
|
99
|
+
create_interaction({
|
|
102
100
|
ink_file: "color-picker.tsx", // Relative to .mide/interactive/
|
|
103
|
-
title: "Pick a Color"
|
|
104
|
-
block: true
|
|
101
|
+
title: "Pick a Color"
|
|
105
102
|
})
|
|
106
103
|
// Returns: { action: "accept", result: { color: "blue" } }
|
|
107
104
|
```
|
package/dist/index.js
CHANGED
|
@@ -174,29 +174,18 @@ function formatAge(date) {
|
|
|
174
174
|
return `${days}d`;
|
|
175
175
|
}
|
|
176
176
|
// Process tool schemas
|
|
177
|
-
const
|
|
177
|
+
const ManageProcessSchema = z.object({
|
|
178
178
|
name: z.string().describe("Process name from mide.yaml"),
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
const StopProcessSchema = z.object({
|
|
183
|
-
name: z.string().describe("Process name to stop"),
|
|
184
|
-
});
|
|
185
|
-
const RestartProcessSchema = z.object({
|
|
186
|
-
name: z.string().describe("Process name to restart"),
|
|
187
|
-
});
|
|
188
|
-
const GetStatusSchema = z.object({
|
|
189
|
-
name: z.string().describe("Process name"),
|
|
179
|
+
op: z.enum(["start", "stop", "restart"]).describe("Operation to perform"),
|
|
180
|
+
args: z.string().optional().describe("Additional arguments (for start)"),
|
|
181
|
+
force: z.boolean().optional().describe("Kill any process using the port (for start)"),
|
|
190
182
|
});
|
|
191
183
|
const GetLogsSchema = z.object({
|
|
192
184
|
name: z.string().describe("Process name"),
|
|
193
185
|
stream: z.enum(["stdout", "stderr", "combined"]).optional().describe("Log stream (default: combined)"),
|
|
194
186
|
tail: z.number().optional().describe("Number of lines to return (default: 100)"),
|
|
195
187
|
});
|
|
196
|
-
|
|
197
|
-
name: z.string().describe("Process name"),
|
|
198
|
-
});
|
|
199
|
-
// Dynamic terminal schemas
|
|
188
|
+
// Pane tool schemas
|
|
200
189
|
const CreatePaneSchema = z.object({
|
|
201
190
|
name: z.string().describe("Unique name for the pane"),
|
|
202
191
|
command: z.string().describe("Command to run in the pane"),
|
|
@@ -206,10 +195,9 @@ const CreatePaneSchema = z.object({
|
|
|
206
195
|
const RemovePaneSchema = z.object({
|
|
207
196
|
name: z.string().describe("Name of the pane to remove"),
|
|
208
197
|
});
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
heartbeat_interval_ms: z.number().optional().describe("Progress heartbeat interval in ms (default: 25000)"),
|
|
198
|
+
const CapturePaneSchema = z.object({
|
|
199
|
+
name: z.string().describe("Name of the pane to capture"),
|
|
200
|
+
lines: z.number().optional().describe("Number of lines to capture (default: 100)"),
|
|
213
201
|
});
|
|
214
202
|
// Interaction tool schemas
|
|
215
203
|
const FormQuestionSchema = z.object({
|
|
@@ -227,30 +215,28 @@ const FormQuestionSchema = z.object({
|
|
|
227
215
|
const FormSchemaSchema = z.object({
|
|
228
216
|
questions: z.array(FormQuestionSchema).min(1).describe("Questions to ask"),
|
|
229
217
|
});
|
|
230
|
-
const
|
|
218
|
+
const CreateInteractionSchema = z.object({
|
|
231
219
|
schema: FormSchemaSchema.optional().describe("Form schema (AskUserQuestion-compatible)"),
|
|
232
220
|
ink_file: z.string().optional().describe("Path to custom Ink component file (.tsx/.jsx)"),
|
|
233
221
|
title: z.string().optional().describe("Form title"),
|
|
234
222
|
group: z.string().optional().describe("tmux layout group"),
|
|
235
|
-
timeout_ms: z.number().optional().describe("Auto-cancel after N ms"),
|
|
236
|
-
block: z.boolean().optional().describe("Block until done (default: true)"),
|
|
237
|
-
});
|
|
238
|
-
const GetInteractionResultSchema = z.object({
|
|
239
|
-
interaction_id: z.string().describe("Interaction ID from non-blocking show_interaction"),
|
|
240
|
-
block: z.boolean().optional().describe("Wait for result (default: false)"),
|
|
241
|
-
});
|
|
242
|
-
const CancelInteractionSchema = z.object({
|
|
243
|
-
interaction_id: z.string().describe("Interaction ID to cancel"),
|
|
223
|
+
timeout_ms: z.number().optional().describe("Auto-cancel after N ms (default: blocks indefinitely)"),
|
|
244
224
|
});
|
|
245
225
|
const SetStatusSchema = z.object({
|
|
246
226
|
status: z.enum(["pending", "running", "completed", "failed"]),
|
|
247
227
|
message: z.string().optional(),
|
|
248
228
|
});
|
|
249
|
-
//
|
|
250
|
-
const
|
|
229
|
+
// Test blocking tool schema (for internal testing)
|
|
230
|
+
const TestBlockingSchema = z.object({
|
|
231
|
+
duration_seconds: z.number().describe("How long to block in seconds"),
|
|
232
|
+
heartbeat_interval_ms: z.number().optional().describe("Progress heartbeat interval in ms (default: 25000)"),
|
|
233
|
+
});
|
|
234
|
+
// MCP Tools - Simplified API (8 tools)
|
|
235
|
+
const MCP_TOOLS = [
|
|
236
|
+
// Process tools (require mide.yaml)
|
|
251
237
|
{
|
|
252
238
|
name: "list_processes",
|
|
253
|
-
description: "List all processes
|
|
239
|
+
description: "List all processes from mide.yaml with full status, port, URL, and health info",
|
|
254
240
|
inputSchema: {
|
|
255
241
|
type: "object",
|
|
256
242
|
properties: {},
|
|
@@ -258,49 +244,17 @@ const PROCESS_TOOLS = [
|
|
|
258
244
|
},
|
|
259
245
|
},
|
|
260
246
|
{
|
|
261
|
-
name: "
|
|
262
|
-
description: "Start a process defined in mide.yaml",
|
|
247
|
+
name: "manage_process",
|
|
248
|
+
description: "Start, stop, or restart a process defined in mide.yaml",
|
|
263
249
|
inputSchema: {
|
|
264
250
|
type: "object",
|
|
265
251
|
properties: {
|
|
266
252
|
name: { type: "string", description: "Process name from mide.yaml" },
|
|
267
|
-
|
|
268
|
-
|
|
253
|
+
op: { type: "string", enum: ["start", "stop", "restart"], description: "Operation to perform" },
|
|
254
|
+
args: { type: "string", description: "Additional arguments (for start)" },
|
|
255
|
+
force: { type: "boolean", description: "Kill any process using the port (for start)" },
|
|
269
256
|
},
|
|
270
|
-
required: ["name"],
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
{
|
|
274
|
-
name: "stop_process",
|
|
275
|
-
description: "Stop a running process",
|
|
276
|
-
inputSchema: {
|
|
277
|
-
type: "object",
|
|
278
|
-
properties: {
|
|
279
|
-
name: { type: "string", description: "Process name to stop" },
|
|
280
|
-
},
|
|
281
|
-
required: ["name"],
|
|
282
|
-
},
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
name: "restart_process",
|
|
286
|
-
description: "Restart a process",
|
|
287
|
-
inputSchema: {
|
|
288
|
-
type: "object",
|
|
289
|
-
properties: {
|
|
290
|
-
name: { type: "string", description: "Process name to restart" },
|
|
291
|
-
},
|
|
292
|
-
required: ["name"],
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
name: "get_status",
|
|
297
|
-
description: "Get detailed status of a process",
|
|
298
|
-
inputSchema: {
|
|
299
|
-
type: "object",
|
|
300
|
-
properties: {
|
|
301
|
-
name: { type: "string", description: "Process name" },
|
|
302
|
-
},
|
|
303
|
-
required: ["name"],
|
|
257
|
+
required: ["name", "op"],
|
|
304
258
|
},
|
|
305
259
|
},
|
|
306
260
|
{
|
|
@@ -310,137 +264,91 @@ const PROCESS_TOOLS = [
|
|
|
310
264
|
type: "object",
|
|
311
265
|
properties: {
|
|
312
266
|
name: { type: "string", description: "Process name" },
|
|
313
|
-
stream: { type: "string", enum: ["stdout", "stderr", "combined"], description: "Log stream (default: combined)" },
|
|
314
267
|
tail: { type: "number", description: "Number of lines to return (default: 100)" },
|
|
315
268
|
},
|
|
316
269
|
required: ["name"],
|
|
317
270
|
},
|
|
318
271
|
},
|
|
319
|
-
|
|
320
|
-
name: "get_url",
|
|
321
|
-
description: "Get the URL for a process (if it has a port)",
|
|
322
|
-
inputSchema: {
|
|
323
|
-
type: "object",
|
|
324
|
-
properties: {
|
|
325
|
-
name: { type: "string", description: "Process name" },
|
|
326
|
-
},
|
|
327
|
-
required: ["name"],
|
|
328
|
-
},
|
|
329
|
-
},
|
|
272
|
+
// Pane tools
|
|
330
273
|
{
|
|
331
274
|
name: "create_pane",
|
|
332
|
-
description: "Create a terminal
|
|
275
|
+
description: "Create a terminal pane running a command",
|
|
333
276
|
inputSchema: {
|
|
334
277
|
type: "object",
|
|
335
278
|
properties: {
|
|
336
279
|
name: { type: "string", description: "Unique name for the pane" },
|
|
337
|
-
command: { type: "string", description: "Command to run (e.g., 'npm run dev'
|
|
338
|
-
group: { type: "string", description: "
|
|
339
|
-
mode: { type: "string", enum: ["embedded", "standalone"], description: "Tmux mode: embedded (current session) or standalone (separate IDE session)" },
|
|
280
|
+
command: { type: "string", description: "Command to run (e.g., 'npm run dev')" },
|
|
281
|
+
group: { type: "string", description: "Layout group (standalone mode only)" },
|
|
340
282
|
},
|
|
341
283
|
required: ["name", "command"],
|
|
342
284
|
},
|
|
343
285
|
},
|
|
344
286
|
{
|
|
345
|
-
name: "
|
|
346
|
-
description: "
|
|
347
|
-
inputSchema: {
|
|
348
|
-
type: "object",
|
|
349
|
-
properties: {
|
|
350
|
-
name: { type: "string", description: "Name of the pane to remove" },
|
|
351
|
-
},
|
|
352
|
-
required: ["name"],
|
|
353
|
-
},
|
|
354
|
-
},
|
|
355
|
-
{
|
|
356
|
-
name: "set_status",
|
|
357
|
-
description: "Update the IDE terminal window title to show current status",
|
|
358
|
-
inputSchema: {
|
|
359
|
-
type: "object",
|
|
360
|
-
properties: {
|
|
361
|
-
status: {
|
|
362
|
-
type: "string",
|
|
363
|
-
enum: ["pending", "running", "completed", "failed"],
|
|
364
|
-
description: "Current status indicator (⏳ pending, 🔄 running, ✅ completed, ❌ failed)",
|
|
365
|
-
},
|
|
366
|
-
message: {
|
|
367
|
-
type: "string",
|
|
368
|
-
description: "Custom message (e.g., 'Building project...', '3 tests failed')",
|
|
369
|
-
},
|
|
370
|
-
},
|
|
371
|
-
required: ["status"],
|
|
372
|
-
},
|
|
373
|
-
},
|
|
374
|
-
{
|
|
375
|
-
name: "test_blocking",
|
|
376
|
-
description: "Test tool that blocks for a specified duration while sending progress heartbeats. Used to validate timeout handling.",
|
|
377
|
-
inputSchema: {
|
|
378
|
-
type: "object",
|
|
379
|
-
properties: {
|
|
380
|
-
duration_seconds: { type: "number", description: "How long to block in seconds" },
|
|
381
|
-
heartbeat_interval_ms: { type: "number", description: "Progress heartbeat interval in ms (default: 25000)" },
|
|
382
|
-
},
|
|
383
|
-
required: ["duration_seconds"],
|
|
384
|
-
},
|
|
385
|
-
},
|
|
386
|
-
{
|
|
387
|
-
name: "show_interaction",
|
|
388
|
-
description: "Show an interactive form or custom Ink component in a tmux pane and collect user input. By default blocks until user completes the form.",
|
|
287
|
+
name: "create_interaction",
|
|
288
|
+
description: "Create an interactive Ink component or form in a pane. Blocks until user completes.",
|
|
389
289
|
inputSchema: {
|
|
390
290
|
type: "object",
|
|
391
291
|
properties: {
|
|
392
292
|
schema: {
|
|
393
293
|
type: "object",
|
|
394
|
-
description: "Form schema with questions
|
|
294
|
+
description: "Form schema with questions",
|
|
395
295
|
properties: {
|
|
396
296
|
questions: {
|
|
397
297
|
type: "array",
|
|
398
298
|
items: {
|
|
399
299
|
type: "object",
|
|
400
300
|
properties: {
|
|
401
|
-
question: { type: "string"
|
|
402
|
-
header: { type: "string"
|
|
403
|
-
options: { type: "array"
|
|
404
|
-
multiSelect: { type: "boolean"
|
|
301
|
+
question: { type: "string" },
|
|
302
|
+
header: { type: "string" },
|
|
303
|
+
options: { type: "array" },
|
|
304
|
+
multiSelect: { type: "boolean" },
|
|
405
305
|
inputType: { type: "string", enum: ["text", "textarea", "password"] },
|
|
406
|
-
placeholder: { type: "string" },
|
|
407
|
-
validation: { type: "string", description: "Regex pattern" },
|
|
408
306
|
},
|
|
409
307
|
required: ["question", "header"],
|
|
410
308
|
},
|
|
411
309
|
},
|
|
412
310
|
},
|
|
413
|
-
required: ["questions"],
|
|
414
311
|
},
|
|
415
|
-
ink_file: { type: "string", description: "Path to
|
|
416
|
-
title: { type: "string", description: "
|
|
417
|
-
group: { type: "string", description: "tmux layout group" },
|
|
312
|
+
ink_file: { type: "string", description: "Path to Ink component (.tsx) - resolves from .mide/interactive/" },
|
|
313
|
+
title: { type: "string", description: "Title" },
|
|
418
314
|
timeout_ms: { type: "number", description: "Auto-cancel timeout in ms" },
|
|
419
|
-
block: { type: "boolean", description: "Block until done (default: true)" },
|
|
420
315
|
},
|
|
421
316
|
},
|
|
422
317
|
},
|
|
423
318
|
{
|
|
424
|
-
name: "
|
|
425
|
-
description: "
|
|
319
|
+
name: "remove_pane",
|
|
320
|
+
description: "Remove a terminal or interaction pane by name",
|
|
321
|
+
inputSchema: {
|
|
322
|
+
type: "object",
|
|
323
|
+
properties: {
|
|
324
|
+
name: { type: "string", description: "Name of the pane to remove" },
|
|
325
|
+
},
|
|
326
|
+
required: ["name"],
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
name: "capture_pane",
|
|
331
|
+
description: "Capture terminal output from a pane",
|
|
426
332
|
inputSchema: {
|
|
427
333
|
type: "object",
|
|
428
334
|
properties: {
|
|
429
|
-
|
|
430
|
-
|
|
335
|
+
name: { type: "string", description: "Name of the pane" },
|
|
336
|
+
lines: { type: "number", description: "Number of lines to capture (default: 100)" },
|
|
431
337
|
},
|
|
432
|
-
required: ["
|
|
338
|
+
required: ["name"],
|
|
433
339
|
},
|
|
434
340
|
},
|
|
341
|
+
// Status tool
|
|
435
342
|
{
|
|
436
|
-
name: "
|
|
437
|
-
description: "
|
|
343
|
+
name: "set_status",
|
|
344
|
+
description: "Update the terminal window title/status indicator",
|
|
438
345
|
inputSchema: {
|
|
439
346
|
type: "object",
|
|
440
347
|
properties: {
|
|
441
|
-
|
|
348
|
+
status: { type: "string", enum: ["pending", "running", "completed", "failed"] },
|
|
349
|
+
message: { type: "string", description: "Custom message" },
|
|
442
350
|
},
|
|
443
|
-
required: ["
|
|
351
|
+
required: ["status"],
|
|
444
352
|
},
|
|
445
353
|
},
|
|
446
354
|
];
|
|
@@ -555,12 +463,19 @@ async function main() {
|
|
|
555
463
|
content: [{ type: "text", text: "No processes defined in mide.yaml" }],
|
|
556
464
|
};
|
|
557
465
|
}
|
|
466
|
+
// Return full status including URL
|
|
558
467
|
const formatted = processes.map((p) => {
|
|
468
|
+
const proc = processManager.getProcess(p.name);
|
|
469
|
+
const state = proc?.getState();
|
|
559
470
|
const parts = [`${p.name}: ${p.status}`];
|
|
560
471
|
if (p.port)
|
|
561
472
|
parts.push(`port=${p.port}`);
|
|
473
|
+
if (state?.url)
|
|
474
|
+
parts.push(`url=${state.url}`);
|
|
562
475
|
if (p.healthy !== undefined)
|
|
563
476
|
parts.push(`healthy=${p.healthy}`);
|
|
477
|
+
if (state?.pid)
|
|
478
|
+
parts.push(`pid=${state.pid}`);
|
|
564
479
|
if (p.error)
|
|
565
480
|
parts.push(`error=${p.error}`);
|
|
566
481
|
return parts.join(" | ");
|
|
@@ -569,68 +484,32 @@ async function main() {
|
|
|
569
484
|
content: [{ type: "text", text: formatted.join("\n") }],
|
|
570
485
|
};
|
|
571
486
|
}
|
|
572
|
-
case "
|
|
573
|
-
if (!processManager) {
|
|
574
|
-
return formatToolError("No mide.yaml found - process management not available");
|
|
575
|
-
}
|
|
576
|
-
const parsed = StartProcessSchema.parse(args);
|
|
577
|
-
await processManager.startProcess(parsed.name, {
|
|
578
|
-
args: parsed.args,
|
|
579
|
-
force: parsed.force,
|
|
580
|
-
});
|
|
581
|
-
return {
|
|
582
|
-
content: [{ type: "text", text: `Process "${parsed.name}" started` }],
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
case "stop_process": {
|
|
586
|
-
if (!processManager) {
|
|
587
|
-
return formatToolError("No mide.yaml found - process management not available");
|
|
588
|
-
}
|
|
589
|
-
const parsed = StopProcessSchema.parse(args);
|
|
590
|
-
await processManager.stopProcess(parsed.name);
|
|
591
|
-
return {
|
|
592
|
-
content: [{ type: "text", text: `Process "${parsed.name}" stopped` }],
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
case "restart_process": {
|
|
596
|
-
if (!processManager) {
|
|
597
|
-
return formatToolError("No mide.yaml found - process management not available");
|
|
598
|
-
}
|
|
599
|
-
const parsed = RestartProcessSchema.parse(args);
|
|
600
|
-
await processManager.restartProcess(parsed.name);
|
|
601
|
-
return {
|
|
602
|
-
content: [{ type: "text", text: `Process "${parsed.name}" restarted` }],
|
|
603
|
-
};
|
|
604
|
-
}
|
|
605
|
-
case "get_status": {
|
|
487
|
+
case "manage_process": {
|
|
606
488
|
if (!processManager) {
|
|
607
489
|
return formatToolError("No mide.yaml found - process management not available");
|
|
608
490
|
}
|
|
609
|
-
const parsed =
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
491
|
+
const parsed = ManageProcessSchema.parse(args);
|
|
492
|
+
switch (parsed.op) {
|
|
493
|
+
case "start":
|
|
494
|
+
await processManager.startProcess(parsed.name, {
|
|
495
|
+
args: parsed.args,
|
|
496
|
+
force: parsed.force,
|
|
497
|
+
});
|
|
498
|
+
return {
|
|
499
|
+
content: [{ type: "text", text: `Process "${parsed.name}" started` }],
|
|
500
|
+
};
|
|
501
|
+
case "stop":
|
|
502
|
+
await processManager.stopProcess(parsed.name);
|
|
503
|
+
return {
|
|
504
|
+
content: [{ type: "text", text: `Process "${parsed.name}" stopped` }],
|
|
505
|
+
};
|
|
506
|
+
case "restart":
|
|
507
|
+
await processManager.restartProcess(parsed.name);
|
|
508
|
+
return {
|
|
509
|
+
content: [{ type: "text", text: `Process "${parsed.name}" restarted` }],
|
|
510
|
+
};
|
|
613
511
|
}
|
|
614
|
-
|
|
615
|
-
const lines = [
|
|
616
|
-
`Name: ${state.name}`,
|
|
617
|
-
`Status: ${state.status}`,
|
|
618
|
-
];
|
|
619
|
-
if (state.pid)
|
|
620
|
-
lines.push(`PID: ${state.pid}`);
|
|
621
|
-
if (state.port)
|
|
622
|
-
lines.push(`Port: ${state.port}`);
|
|
623
|
-
if (state.url)
|
|
624
|
-
lines.push(`URL: ${state.url}`);
|
|
625
|
-
if (state.healthy !== undefined)
|
|
626
|
-
lines.push(`Healthy: ${state.healthy}`);
|
|
627
|
-
if (state.restartCount > 0)
|
|
628
|
-
lines.push(`Restart Count: ${state.restartCount}`);
|
|
629
|
-
if (state.error)
|
|
630
|
-
lines.push(`Error: ${state.error}`);
|
|
631
|
-
return {
|
|
632
|
-
content: [{ type: "text", text: lines.join("\n") }],
|
|
633
|
-
};
|
|
512
|
+
break;
|
|
634
513
|
}
|
|
635
514
|
case "get_logs": {
|
|
636
515
|
if (!processManager) {
|
|
@@ -648,21 +527,6 @@ async function main() {
|
|
|
648
527
|
content: [{ type: "text", text: content || "(no logs yet)" }],
|
|
649
528
|
};
|
|
650
529
|
}
|
|
651
|
-
case "get_url": {
|
|
652
|
-
if (!processManager) {
|
|
653
|
-
return formatToolError("No mide.yaml found - process management not available");
|
|
654
|
-
}
|
|
655
|
-
const parsed = GetUrlSchema.parse(args);
|
|
656
|
-
const url = processManager.getUrl(parsed.name);
|
|
657
|
-
if (!url) {
|
|
658
|
-
return {
|
|
659
|
-
content: [{ type: "text", text: `Process "${parsed.name}" has no URL (no port configured or detected)` }],
|
|
660
|
-
};
|
|
661
|
-
}
|
|
662
|
-
return {
|
|
663
|
-
content: [{ type: "text", text: url }],
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
530
|
case "create_pane": {
|
|
667
531
|
const parsed = CreatePaneSchema.parse(args);
|
|
668
532
|
// Determine effective mode: tool param > config > auto-detect
|
|
@@ -723,6 +587,25 @@ async function main() {
|
|
|
723
587
|
content: [{ type: "text", text: `Removed pane "${parsed.name}"` }],
|
|
724
588
|
};
|
|
725
589
|
}
|
|
590
|
+
case "capture_pane": {
|
|
591
|
+
const parsed = CapturePaneSchema.parse(args);
|
|
592
|
+
const lines = parsed.lines ?? 100;
|
|
593
|
+
// Try embedded manager first
|
|
594
|
+
if (embeddedTmuxManager?.hasPane(parsed.name)) {
|
|
595
|
+
const content = await embeddedTmuxManager.capturePane(parsed.name, lines);
|
|
596
|
+
return {
|
|
597
|
+
content: [{ type: "text", text: content || "(no output)" }],
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
// Try standalone tmux manager
|
|
601
|
+
if (tmuxManager) {
|
|
602
|
+
const content = await tmuxManager.capturePane(parsed.name, lines);
|
|
603
|
+
return {
|
|
604
|
+
content: [{ type: "text", text: content || "(no output)" }],
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
return formatToolError("Pane not found or no tmux session active");
|
|
608
|
+
}
|
|
726
609
|
case "set_status": {
|
|
727
610
|
const parsed = SetStatusSchema.parse(args);
|
|
728
611
|
// Use embedded manager if available, otherwise standalone
|
|
@@ -739,7 +622,6 @@ async function main() {
|
|
|
739
622
|
content: [{ type: "text", text: `Status: ${parsed.status}${parsed.message ? ` - ${parsed.message}` : ""}` }],
|
|
740
623
|
};
|
|
741
624
|
}
|
|
742
|
-
// test_blocking is handled separately in the request handler (needs server access)
|
|
743
625
|
default:
|
|
744
626
|
return formatToolError(`Unknown tool: ${name}`);
|
|
745
627
|
}
|
|
@@ -756,134 +638,15 @@ async function main() {
|
|
|
756
638
|
// Handle tool listing
|
|
757
639
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
758
640
|
let tools = [];
|
|
759
|
-
//
|
|
641
|
+
// Full mode: all tools available (with mide.yaml)
|
|
760
642
|
if (processManager) {
|
|
761
|
-
tools = [...
|
|
643
|
+
tools = [...MCP_TOOLS];
|
|
762
644
|
}
|
|
763
645
|
else if (embeddedTmuxManager) {
|
|
764
|
-
// Embedded mode:
|
|
765
|
-
tools.
|
|
766
|
-
name: "create_pane",
|
|
767
|
-
description: "Create a terminal/process pane in the current tmux session. Use for dev servers, build commands, or any shell process.",
|
|
768
|
-
inputSchema: {
|
|
769
|
-
type: "object",
|
|
770
|
-
properties: {
|
|
771
|
-
name: { type: "string", description: "Unique name for the pane" },
|
|
772
|
-
command: { type: "string", description: "Command to run (e.g., 'npm run dev', 'python server.py')" },
|
|
773
|
-
},
|
|
774
|
-
required: ["name", "command"],
|
|
775
|
-
},
|
|
776
|
-
}, {
|
|
777
|
-
name: "remove_pane",
|
|
778
|
-
description: "Remove a terminal/process pane by name",
|
|
779
|
-
inputSchema: {
|
|
780
|
-
type: "object",
|
|
781
|
-
properties: {
|
|
782
|
-
name: { type: "string", description: "Name of the pane to remove" },
|
|
783
|
-
},
|
|
784
|
-
required: ["name"],
|
|
785
|
-
},
|
|
786
|
-
}, {
|
|
787
|
-
name: "set_status",
|
|
788
|
-
description: "Update the terminal window title to show current status",
|
|
789
|
-
inputSchema: {
|
|
790
|
-
type: "object",
|
|
791
|
-
properties: {
|
|
792
|
-
status: {
|
|
793
|
-
type: "string",
|
|
794
|
-
enum: ["pending", "running", "completed", "failed"],
|
|
795
|
-
description: "Current status indicator",
|
|
796
|
-
},
|
|
797
|
-
message: { type: "string", description: "Custom message" },
|
|
798
|
-
},
|
|
799
|
-
required: ["status"],
|
|
800
|
-
},
|
|
801
|
-
}, {
|
|
802
|
-
name: "test_blocking",
|
|
803
|
-
description: "Test tool that blocks for a specified duration while sending progress heartbeats.",
|
|
804
|
-
inputSchema: {
|
|
805
|
-
type: "object",
|
|
806
|
-
properties: {
|
|
807
|
-
duration_seconds: { type: "number", description: "How long to block in seconds" },
|
|
808
|
-
heartbeat_interval_ms: { type: "number", description: "Progress heartbeat interval in ms (default: 25000)" },
|
|
809
|
-
},
|
|
810
|
-
required: ["duration_seconds"],
|
|
811
|
-
},
|
|
812
|
-
},
|
|
813
|
-
// Interaction tools (ink-runner based)
|
|
814
|
-
{
|
|
815
|
-
name: "show_interaction",
|
|
816
|
-
description: "Show an interactive form or custom Ink component in a tmux pane and collect user input. By default blocks until user completes the form.",
|
|
817
|
-
inputSchema: {
|
|
818
|
-
type: "object",
|
|
819
|
-
properties: {
|
|
820
|
-
schema: {
|
|
821
|
-
type: "object",
|
|
822
|
-
description: "Form schema with questions (AskUserQuestion-compatible)",
|
|
823
|
-
properties: {
|
|
824
|
-
questions: {
|
|
825
|
-
type: "array",
|
|
826
|
-
items: {
|
|
827
|
-
type: "object",
|
|
828
|
-
properties: {
|
|
829
|
-
question: { type: "string", description: "The question to ask" },
|
|
830
|
-
header: { type: "string", description: "Short label (max 12 chars)" },
|
|
831
|
-
options: { type: "array", description: "Selection options (omit for text input)" },
|
|
832
|
-
multiSelect: { type: "boolean", description: "Allow multiple selections" },
|
|
833
|
-
inputType: { type: "string", enum: ["text", "textarea", "password"] },
|
|
834
|
-
placeholder: { type: "string" },
|
|
835
|
-
validation: { type: "string", description: "Regex pattern" },
|
|
836
|
-
},
|
|
837
|
-
required: ["question", "header"],
|
|
838
|
-
},
|
|
839
|
-
},
|
|
840
|
-
},
|
|
841
|
-
required: ["questions"],
|
|
842
|
-
},
|
|
843
|
-
ink_file: { type: "string", description: "Path to custom Ink component file (.tsx/.jsx) - resolves from ~/.mide/interactive/ or .mide/interactive/" },
|
|
844
|
-
title: { type: "string", description: "Form title" },
|
|
845
|
-
timeout_ms: { type: "number", description: "Auto-cancel timeout in ms" },
|
|
846
|
-
block: { type: "boolean", description: "Block until done (default: true)" },
|
|
847
|
-
},
|
|
848
|
-
},
|
|
849
|
-
}, {
|
|
850
|
-
name: "get_interaction_result",
|
|
851
|
-
description: "Get the result of a non-blocking interaction",
|
|
852
|
-
inputSchema: {
|
|
853
|
-
type: "object",
|
|
854
|
-
properties: {
|
|
855
|
-
interaction_id: { type: "string", description: "Interaction ID from show_interaction" },
|
|
856
|
-
block: { type: "boolean", description: "Wait for result (default: false)" },
|
|
857
|
-
},
|
|
858
|
-
required: ["interaction_id"],
|
|
859
|
-
},
|
|
860
|
-
}, {
|
|
861
|
-
name: "cancel_interaction",
|
|
862
|
-
description: "Cancel an active interaction",
|
|
863
|
-
inputSchema: {
|
|
864
|
-
type: "object",
|
|
865
|
-
properties: {
|
|
866
|
-
interaction_id: { type: "string", description: "Interaction ID to cancel" },
|
|
867
|
-
},
|
|
868
|
-
required: ["interaction_id"],
|
|
869
|
-
},
|
|
870
|
-
});
|
|
871
|
-
}
|
|
872
|
-
else {
|
|
873
|
-
// Minimal mode: just test_blocking
|
|
874
|
-
tools.push({
|
|
875
|
-
name: "test_blocking",
|
|
876
|
-
description: "Test tool that blocks for a specified duration while sending progress heartbeats. Used to validate timeout handling.",
|
|
877
|
-
inputSchema: {
|
|
878
|
-
type: "object",
|
|
879
|
-
properties: {
|
|
880
|
-
duration_seconds: { type: "number", description: "How long to block in seconds" },
|
|
881
|
-
heartbeat_interval_ms: { type: "number", description: "Progress heartbeat interval in ms (default: 25000)" },
|
|
882
|
-
},
|
|
883
|
-
required: ["duration_seconds"],
|
|
884
|
-
},
|
|
885
|
-
});
|
|
646
|
+
// Embedded mode: pane tools only (no process management)
|
|
647
|
+
tools = MCP_TOOLS.filter(t => ["create_pane", "create_interaction", "remove_pane", "capture_pane", "set_status"].includes(t.name));
|
|
886
648
|
}
|
|
649
|
+
// Minimal mode: no tools (tmux not available)
|
|
887
650
|
return { tools };
|
|
888
651
|
});
|
|
889
652
|
// Handle tool calls
|
|
@@ -936,12 +699,12 @@ async function main() {
|
|
|
936
699
|
}],
|
|
937
700
|
};
|
|
938
701
|
}
|
|
939
|
-
// Handle
|
|
940
|
-
if (name === "
|
|
702
|
+
// Handle create_interaction (needs server access for progress notifications)
|
|
703
|
+
if (name === "create_interaction") {
|
|
941
704
|
if (!interactionManager) {
|
|
942
705
|
return formatToolError("Interaction tools not available - tmux required");
|
|
943
706
|
}
|
|
944
|
-
const parsed =
|
|
707
|
+
const parsed = CreateInteractionSchema.parse(args);
|
|
945
708
|
if (!parsed.schema && !parsed.ink_file) {
|
|
946
709
|
return formatToolError("Either schema or ink_file is required");
|
|
947
710
|
}
|
|
@@ -956,21 +719,12 @@ async function main() {
|
|
|
956
719
|
if (tmuxManager && config?.settings?.autoAttachTerminal !== false && !isInsideTmux()) {
|
|
957
720
|
await tmuxManager.openTerminal(config?.settings?.terminalApp, configDir);
|
|
958
721
|
}
|
|
959
|
-
//
|
|
960
|
-
if (parsed.block === false) {
|
|
961
|
-
return {
|
|
962
|
-
content: [{
|
|
963
|
-
type: "text",
|
|
964
|
-
text: JSON.stringify({ interaction_id: interactionId, status: "pending" })
|
|
965
|
-
}],
|
|
966
|
-
};
|
|
967
|
-
}
|
|
968
|
-
// Blocking mode with progress heartbeats
|
|
722
|
+
// Blocking mode with progress heartbeats (always blocks now)
|
|
969
723
|
const progressToken = request.params._meta?.progressToken;
|
|
970
724
|
const heartbeatIntervalMs = 25000;
|
|
971
725
|
const startTime = Date.now();
|
|
972
726
|
let heartbeatCount = 0;
|
|
973
|
-
console.error(`[mide]
|
|
727
|
+
console.error(`[mide] create_interaction: id=${interactionId}, progressToken=${progressToken}`);
|
|
974
728
|
while (true) {
|
|
975
729
|
// Wait for result with short timeout
|
|
976
730
|
const result = await interactionManager.waitForResult(interactionId, heartbeatIntervalMs);
|
|
@@ -1025,52 +779,6 @@ async function main() {
|
|
|
1025
779
|
}
|
|
1026
780
|
}
|
|
1027
781
|
}
|
|
1028
|
-
// Handle get_interaction_result
|
|
1029
|
-
if (name === "get_interaction_result") {
|
|
1030
|
-
if (!interactionManager) {
|
|
1031
|
-
return formatToolError("Interaction tools not available - tmux required");
|
|
1032
|
-
}
|
|
1033
|
-
const parsed = GetInteractionResultSchema.parse(args);
|
|
1034
|
-
const state = interactionManager.getState(parsed.interaction_id);
|
|
1035
|
-
if (!state) {
|
|
1036
|
-
return formatToolError(`Interaction "${parsed.interaction_id}" not found`);
|
|
1037
|
-
}
|
|
1038
|
-
// If blocking requested and still pending, wait
|
|
1039
|
-
if (parsed.block && state.status === "pending") {
|
|
1040
|
-
const result = await interactionManager.waitForResult(parsed.interaction_id, 30000);
|
|
1041
|
-
if (result) {
|
|
1042
|
-
return {
|
|
1043
|
-
content: [{
|
|
1044
|
-
type: "text",
|
|
1045
|
-
text: JSON.stringify({ status: "completed", result })
|
|
1046
|
-
}],
|
|
1047
|
-
};
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
return {
|
|
1051
|
-
content: [{
|
|
1052
|
-
type: "text",
|
|
1053
|
-
text: JSON.stringify({
|
|
1054
|
-
status: state.status,
|
|
1055
|
-
result: state.result
|
|
1056
|
-
})
|
|
1057
|
-
}],
|
|
1058
|
-
};
|
|
1059
|
-
}
|
|
1060
|
-
// Handle cancel_interaction
|
|
1061
|
-
if (name === "cancel_interaction") {
|
|
1062
|
-
if (!interactionManager) {
|
|
1063
|
-
return formatToolError("Interaction tools not available - tmux required");
|
|
1064
|
-
}
|
|
1065
|
-
const parsed = CancelInteractionSchema.parse(args);
|
|
1066
|
-
const success = await interactionManager.cancel(parsed.interaction_id);
|
|
1067
|
-
return {
|
|
1068
|
-
content: [{
|
|
1069
|
-
type: "text",
|
|
1070
|
-
text: JSON.stringify({ success })
|
|
1071
|
-
}],
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
782
|
return await handleToolCall(name, args);
|
|
1075
783
|
}
|
|
1076
784
|
catch (err) {
|