@priyanshumit/macos-terminal-mcp 0.5.2 → 0.6.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/README.md +28 -1
- package/dist/safety/audit.js +31 -1
- package/dist/safety/audit.js.map +1 -1
- package/dist/tools/audit.js +33 -0
- package/dist/tools/audit.js.map +1 -0
- package/dist/tools/close_tab.js +134 -0
- package/dist/tools/close_tab.js.map +1 -0
- package/dist/tools/new_tab.js +31 -0
- package/dist/tools/new_tab.js.map +1 -1
- package/dist/tools/register.js +10 -0
- package/dist/tools/register.js.map +1 -1
- package/dist/tools/wait_for_idle.js +91 -0
- package/dist/tools/wait_for_idle.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ A local MCP server that lets AI agents inspect and drive your macOS Terminal.app
|
|
|
14
14
|
|
|
15
15
|
## What it does
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Fifteen MCP tools across four categories:
|
|
18
18
|
|
|
19
19
|
### Terminal interaction
|
|
20
20
|
|
|
@@ -25,6 +25,8 @@ Twelve MCP tools across three categories:
|
|
|
25
25
|
| `terminal_execute` | **write** | Type a command into a specific tab and press Enter. Refuses if the target tab is busy unless `force=true`. `dry_run=true` returns the safety verdict without any side effects. |
|
|
26
26
|
| `terminal_clear` | **write** | Wipe scrollback of a specific tab via Cmd+K. Briefly steals focus. |
|
|
27
27
|
| `terminal_new_tab` | **write** | Open a new empty tab in Terminal.app and return its tty for follow-up calls. No dialog — low blast radius (user can close the tab). |
|
|
28
|
+
| `terminal_close_tab` | **write** | Close a specific tab by tty. Refuses busy tabs unless `force=true`. No dialog. |
|
|
29
|
+
| `terminal_wait_for_idle` | read | Block until the target tab is no longer busy, or until `timeout_seconds` (default 60, max 600). Polls every 250ms inside a single JXA call. |
|
|
28
30
|
|
|
29
31
|
### Safety policy management
|
|
30
32
|
|
|
@@ -43,6 +45,12 @@ Twelve MCP tools across three categories:
|
|
|
43
45
|
| `pending_approve` | **write** | Approve a queued command by id. Triggers its own confirmation dialog. |
|
|
44
46
|
| `pending_deny` | **write** | Deny a queued command. |
|
|
45
47
|
|
|
48
|
+
### Observability
|
|
49
|
+
|
|
50
|
+
| Tool | Read/Write | Description |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `audit_log_tail` | read | Read the last N entries from the audit log. Default count 20, max 1000. Returns parsed JSON array of `{timestamp, tool, outcome, ...}` entries. |
|
|
53
|
+
|
|
46
54
|
Write tools are **off by default**. Set `WRITE_TOOLS_ENABLED=1` in the server's environment to enable them. Even when enabled, every non-`safe` operation triggers a native macOS confirmation dialog.
|
|
47
55
|
|
|
48
56
|
## Prerequisites
|
|
@@ -193,6 +201,25 @@ Whichever path resolves first wins. For solo desktop use, the dialog is the cano
|
|
|
193
201
|
|
|
194
202
|
Pending entries auto-expire after 10 minutes. The queue is in-memory only — a server restart drops all pending entries.
|
|
195
203
|
|
|
204
|
+
## Privacy & data
|
|
205
|
+
|
|
206
|
+
**No network, no telemetry.** The server speaks MCP over stdio to your local Claude client. It makes zero outbound network calls — no analytics, no crash reporting, no auto-update pings.
|
|
207
|
+
|
|
208
|
+
**What is stored on disk:**
|
|
209
|
+
|
|
210
|
+
| File | Path | Contents | Permissions |
|
|
211
|
+
|---|---|---|---|
|
|
212
|
+
| Audit log | `~/.local/state/macos-terminal-mcp/audit.log` | One JSON line per write-tool call: tool name, tty, **full command text**, safety level, matched pattern, outcome, timestamp | `0o600` (owner read/write only); parent dir `0o700` |
|
|
213
|
+
| Safety config | `~/.config/macos-terminal-mcp/safety.json` | User-added regex patterns with their levels and descriptions | default umask |
|
|
214
|
+
|
|
215
|
+
**Important caveat for secrets handling**: `terminal_execute` writes the **full command text** to the audit log. If a command contains a password, API key, or other secret, that secret ends up on disk in plaintext (owner-readable only). Mitigations:
|
|
216
|
+
|
|
217
|
+
- Delete the log at any time: `rm ~/.local/state/macos-terminal-mcp/audit.log`
|
|
218
|
+
- Default forbidden patterns already block common secret-leak vectors (`>\s*/etc/`, `\bcurl\b[^|;]*\|`, `\|\s*(bash|sh|zsh)\b`, `~/.ssh`, `/etc/passwd`, `/etc/shadow`)
|
|
219
|
+
- Add your own forbidden patterns for company-specific secret formats via `safety_add`
|
|
220
|
+
|
|
221
|
+
**Where the audit log helps**: post-hoc review of what an AI agent ran on your machine, debugging unexpected command refusals, and satisfying audit requirements in regulated environments.
|
|
222
|
+
|
|
196
223
|
## Usage examples
|
|
197
224
|
|
|
198
225
|
Once registered, from Claude Code:
|
package/dist/safety/audit.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { appendFile, chmod, mkdir } from "node:fs/promises";
|
|
1
|
+
import { appendFile, chmod, mkdir, readFile } from "node:fs/promises";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
function resolveDefaultPath() {
|
|
@@ -41,4 +41,34 @@ export async function appendAudit(entry, path = AUDIT_LOG_PATH) {
|
|
|
41
41
|
process.stderr.write(`[macos-terminal-mcp] audit log write failed: ${err.message}\n`);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Read the last N JSONL entries from the audit log. Returns [] if the file
|
|
46
|
+
* does not exist (no write tool calls have happened yet).
|
|
47
|
+
*
|
|
48
|
+
* Malformed lines are silently skipped — the audit log is best-effort writes
|
|
49
|
+
* and a corrupt entry shouldn't break the read path.
|
|
50
|
+
*/
|
|
51
|
+
export async function readAuditTail(count, path = AUDIT_LOG_PATH) {
|
|
52
|
+
let content;
|
|
53
|
+
try {
|
|
54
|
+
content = await readFile(path, "utf8");
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
if (err.code === "ENOENT")
|
|
58
|
+
return [];
|
|
59
|
+
throw err;
|
|
60
|
+
}
|
|
61
|
+
const lines = content.split("\n").filter((l) => l.length > 0);
|
|
62
|
+
const tail = lines.slice(-count);
|
|
63
|
+
const entries = [];
|
|
64
|
+
for (const line of tail) {
|
|
65
|
+
try {
|
|
66
|
+
entries.push(JSON.parse(line));
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Defensive: skip malformed lines.
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return entries;
|
|
73
|
+
}
|
|
44
74
|
//# sourceMappingURL=audit.js.map
|
package/dist/safety/audit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/safety/audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/safety/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { readAuditTail } from "../safety/audit.js";
|
|
3
|
+
export async function auditTailHandler({ count = 20 }) {
|
|
4
|
+
try {
|
|
5
|
+
const entries = await readAuditTail(count);
|
|
6
|
+
return {
|
|
7
|
+
content: [{ type: "text", text: JSON.stringify(entries, null, 2) }],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
catch (err) {
|
|
11
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
12
|
+
return {
|
|
13
|
+
content: [{ type: "text", text: `audit_log_tail failed: ${message}` }],
|
|
14
|
+
isError: true,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function register(server) {
|
|
19
|
+
server.registerTool("audit_log_tail", {
|
|
20
|
+
description: "Read the last N entries from the audit log (~/.local/state/macos-terminal-mcp/audit.log, or $XDG_STATE_HOME equivalent). Each entry is a JSON object with {timestamp, tool, outcome, tty?, command?, level?, matchedPattern?, source?, errorMessage?, details?}. Read-only — does not require WRITE_TOOLS_ENABLED and is not itself logged. Returns [] if the log file does not exist yet.",
|
|
21
|
+
inputSchema: {
|
|
22
|
+
count: z
|
|
23
|
+
.number()
|
|
24
|
+
.int()
|
|
25
|
+
.min(1)
|
|
26
|
+
.max(1000)
|
|
27
|
+
.optional()
|
|
28
|
+
.describe("Number of entries to return from the tail of the log. Default 20, max 1000."),
|
|
29
|
+
},
|
|
30
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
31
|
+
}, auditTailHandler);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/tools/audit.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAMnD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAE,KAAK,GAAG,EAAE,EAAkB;IACnE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3C,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SACpE,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,OAAO,EAAE,EAAE,CAAC;YACtE,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,WAAW,EACT,4XAA4X;QAC9X,WAAW,EAAE;YACX,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,IAAI,CAAC;iBACT,QAAQ,EAAE;iBACV,QAAQ,CAAC,6EAA6E,CAAC;SAC3F;QACD,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;KAC1D,EACD,gBAAgB,CACjB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { OsascriptError, runJxa } from "../applescript.js";
|
|
3
|
+
import { appendAudit } from "../safety/audit.js";
|
|
4
|
+
import { isWriteToolsEnabled, writeToolsDisabledMessage } from "../safety/confirm.js";
|
|
5
|
+
export function buildCloseTabScript(tty, force) {
|
|
6
|
+
return `
|
|
7
|
+
function safe(fn) { try { return fn(); } catch (e) { return null; } }
|
|
8
|
+
(function closeTab(targetTty, force) {
|
|
9
|
+
const terminal = Application("Terminal");
|
|
10
|
+
const wins = terminal.windows();
|
|
11
|
+
for (let wi = 0; wi < wins.length; wi++) {
|
|
12
|
+
const w = wins[wi];
|
|
13
|
+
const tabs = w.tabs();
|
|
14
|
+
for (let ti = 0; ti < tabs.length; ti++) {
|
|
15
|
+
const t = tabs[ti];
|
|
16
|
+
if (safe(function () { return t.tty(); }) === targetTty) {
|
|
17
|
+
const busy = safe(function () { return t.busy(); }) === true;
|
|
18
|
+
if (busy && !force) {
|
|
19
|
+
return JSON.stringify({ status: "busy", tty: targetTty });
|
|
20
|
+
}
|
|
21
|
+
// Terminal.app's AppleScript dictionary does NOT expose "close" on tab
|
|
22
|
+
// objects — only on window objects. Each tab is enumerated as its own
|
|
23
|
+
// "window" entry though (a quirk of the dictionary), so closing the
|
|
24
|
+
// enclosing "w" reference closes just this tab in the physical window.
|
|
25
|
+
w.close();
|
|
26
|
+
return JSON.stringify({ status: "closed", tty: targetTty, killedRunningCommand: busy });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return JSON.stringify({ status: "missing", tty: targetTty });
|
|
31
|
+
})(${JSON.stringify(tty)}, ${JSON.stringify(force)});
|
|
32
|
+
`;
|
|
33
|
+
}
|
|
34
|
+
export async function closeTabHandler({ tty, force = false, }) {
|
|
35
|
+
if (!isWriteToolsEnabled()) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: "text", text: writeToolsDisabledMessage("terminal_close_tab") }],
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
let raw;
|
|
42
|
+
try {
|
|
43
|
+
raw = await runJxa(buildCloseTabScript(tty, force));
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
47
|
+
const hint = err instanceof OsascriptError && /not authorized/i.test(err.stderr)
|
|
48
|
+
? "\nAutomation permission missing — System Settings → Privacy & Security → Automation."
|
|
49
|
+
: "";
|
|
50
|
+
await appendAudit({
|
|
51
|
+
tool: "terminal_close_tab",
|
|
52
|
+
outcome: "error",
|
|
53
|
+
tty,
|
|
54
|
+
errorMessage: message,
|
|
55
|
+
});
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: `terminal_close_tab failed: ${message}${hint}` }],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
let result;
|
|
62
|
+
try {
|
|
63
|
+
result = JSON.parse(raw);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
text: `terminal_close_tab: unexpected JXA response: ${err.message}`,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (result.status === "missing") {
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: "text", text: `No Terminal.app tab found with tty ${tty}.` }],
|
|
79
|
+
isError: true,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (result.status === "busy") {
|
|
83
|
+
await appendAudit({
|
|
84
|
+
tool: "terminal_close_tab",
|
|
85
|
+
outcome: "refused",
|
|
86
|
+
tty,
|
|
87
|
+
details: { reason: "target tab is busy" },
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: "text",
|
|
93
|
+
text: `Refused: target tab ${tty} is busy with a running command. Closing it would kill the process. ` +
|
|
94
|
+
`Pass force=true to close anyway, or wait for the command to finish (see terminal_wait_for_idle).`,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
isError: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
await appendAudit({
|
|
101
|
+
tool: "terminal_close_tab",
|
|
102
|
+
outcome: "success",
|
|
103
|
+
tty,
|
|
104
|
+
...(result.killedRunningCommand
|
|
105
|
+
? { details: { killedRunningCommand: true, force: true } }
|
|
106
|
+
: {}),
|
|
107
|
+
});
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: "text",
|
|
112
|
+
text: result.killedRunningCommand
|
|
113
|
+
? `Closed ${tty} [force=true, killed running command].`
|
|
114
|
+
: `Closed ${tty}.`,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function register(server) {
|
|
120
|
+
server.registerTool("terminal_close_tab", {
|
|
121
|
+
description: "Close a specific Terminal.app tab by tty. Useful for cleaning up scratch tabs spawned via terminal_new_tab. If the tab has a running foreground command, the call refuses by default — pass force=true to close anyway (the running command is killed). Requires WRITE_TOOLS_ENABLED=1 but no confirmation dialog (low blast radius for idle tabs; force=true scenarios are deliberate).",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
tty: z
|
|
124
|
+
.string()
|
|
125
|
+
.regex(/^\/dev\/ttys[0-9]+$/)
|
|
126
|
+
.describe('Target tab tty, e.g. "/dev/ttys003"'),
|
|
127
|
+
force: z
|
|
128
|
+
.boolean()
|
|
129
|
+
.optional()
|
|
130
|
+
.describe("If true, close even when a foreground command is running (killing it). Default: false."),
|
|
131
|
+
},
|
|
132
|
+
}, closeTabHandler);
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=close_tab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"close_tab.js","sourceRoot":"","sources":["../../src/tools/close_tab.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/new_tab.js
CHANGED
|
@@ -22,7 +22,38 @@ function snapshotTabs(terminal) {
|
|
|
22
22
|
(function newTab() {
|
|
23
23
|
const terminal = Application("Terminal");
|
|
24
24
|
const systemEvents = Application("System Events");
|
|
25
|
+
// Detect cold start: if Terminal isn't running yet, activate() has to LAUNCH
|
|
26
|
+
// it, which takes much longer than the warm-path 150ms delay. We then wait
|
|
27
|
+
// for the new process to be ready before sending the keystroke.
|
|
28
|
+
const wasRunning = (function () { try { return terminal.running(); } catch (e) { return true; } })();
|
|
25
29
|
terminal.activate();
|
|
30
|
+
// activate() returns before the window server actually makes Terminal frontmost,
|
|
31
|
+
// and macOS sometimes refuses to honor activate() when another app is in use
|
|
32
|
+
// by the user. System Events accessibility-layer "frontmost = true" is a more
|
|
33
|
+
// forceful way to make Terminal the key app — required for Cmd+T to land on it.
|
|
34
|
+
try { Application("System Events").applicationProcesses["Terminal"].frontmost = true; } catch (e) { /* best-effort */ }
|
|
35
|
+
// Cold start: poll up to 3s for Terminal to come up. Warm start: short settle.
|
|
36
|
+
if (!wasRunning) {
|
|
37
|
+
for (let i = 0; i < 30; i++) {
|
|
38
|
+
delay(0.1);
|
|
39
|
+
try { if (terminal.running()) break; } catch (e) { /* keep waiting */ }
|
|
40
|
+
}
|
|
41
|
+
delay(0.5);
|
|
42
|
+
// Cold-start: Terminal almost always auto-opens a default window per the
|
|
43
|
+
// user's "When Terminal Starts" preference. Since Terminal wasn't running
|
|
44
|
+
// before, ANY tab that exists now is the new one we owe the caller — return
|
|
45
|
+
// it directly. Don't ALSO send Cmd+T (which would create a second tab the
|
|
46
|
+
// caller didn't ask for; reported by user during v0.6.1 verification).
|
|
47
|
+
const afterLaunch = snapshotTabs(terminal);
|
|
48
|
+
const launchedKeys = Object.keys(afterLaunch);
|
|
49
|
+
if (launchedKeys.length > 0) {
|
|
50
|
+
return JSON.stringify({ tty: launchedKeys[0], windowId: afterLaunch[launchedKeys[0]] });
|
|
51
|
+
}
|
|
52
|
+
// Cold launch produced no default window (user disabled it in prefs) — fall
|
|
53
|
+
// through to the keystroke path so Cmd+N creates one.
|
|
54
|
+
} else {
|
|
55
|
+
delay(0.3);
|
|
56
|
+
}
|
|
26
57
|
const before = snapshotTabs(terminal);
|
|
27
58
|
const wasEmpty = Object.keys(before).length === 0;
|
|
28
59
|
// Cmd+T opens a new tab in the front window; Cmd+N opens a new window when
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new_tab.js","sourceRoot":"","sources":["../../src/tools/new_tab.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,MAAM,CAAC,MAAM,cAAc,GAAG
|
|
1
|
+
{"version":3,"file":"new_tab.js","sourceRoot":"","sources":["../../src/tools/new_tab.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+E7B,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC,mBAAmB,EAAE,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAChF,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;QAC1C,MAAM,WAAW,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACpE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IACrD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,GACR,GAAG,YAAY,cAAc,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;YACjE,CAAC,CAAC,sFAAsF;YACxF,CAAC,CAAC,EAAE,CAAC;QACT,MAAM,WAAW,CAAC;YAChB,IAAI,EAAE,kBAAkB;YACxB,OAAO,EAAE,OAAO;YAChB,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QACH,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,4BAA4B,OAAO,GAAG,IAAI,EAAE,EAAE,CAAC;YAC/E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,WAAW,EACT,2nBAA2nB;KAC9nB,EACD,aAAa,CACd,CAAC;AACJ,CAAC"}
|
package/dist/tools/register.js
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
|
+
import * as audit from "./audit.js";
|
|
1
2
|
import * as clear from "./clear.js";
|
|
3
|
+
import * as closeTab from "./close_tab.js";
|
|
2
4
|
import * as execute from "./execute.js";
|
|
3
5
|
import * as list from "./list.js";
|
|
4
6
|
import * as newTab from "./new_tab.js";
|
|
5
7
|
import * as pending from "./pending.js";
|
|
6
8
|
import * as read from "./read.js";
|
|
7
9
|
import * as safety from "./safety.js";
|
|
10
|
+
import * as waitForIdle from "./wait_for_idle.js";
|
|
8
11
|
export function registerAll(server) {
|
|
12
|
+
// Terminal interaction
|
|
9
13
|
list.register(server);
|
|
10
14
|
read.register(server);
|
|
11
15
|
execute.register(server);
|
|
12
16
|
clear.register(server);
|
|
13
17
|
newTab.register(server);
|
|
18
|
+
closeTab.register(server);
|
|
19
|
+
waitForIdle.register(server);
|
|
20
|
+
// Safety policy
|
|
14
21
|
safety.register(server);
|
|
22
|
+
// Async approval queue
|
|
15
23
|
pending.register(server);
|
|
24
|
+
// Observability
|
|
25
|
+
audit.register(server);
|
|
16
26
|
}
|
|
17
27
|
//# sourceMappingURL=register.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/tools/register.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,KAAK,WAAW,MAAM,oBAAoB,CAAC;AAElD,MAAM,UAAU,WAAW,CAAC,MAAiB;IAC3C,uBAAuB;IACvB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7B,gBAAgB;IAChB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,uBAAuB;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzB,gBAAgB;IAChB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { runJxa } from "../applescript.js";
|
|
3
|
+
const DEFAULT_TIMEOUT_SEC = 60;
|
|
4
|
+
const MAX_TIMEOUT_SEC = 600;
|
|
5
|
+
const POLL_INTERVAL_SEC = 0.25;
|
|
6
|
+
export function buildWaitForIdleScript(tty, timeoutSec) {
|
|
7
|
+
return `
|
|
8
|
+
function safe(fn) { try { return fn(); } catch (e) { return null; } }
|
|
9
|
+
var app = Application.currentApplication();
|
|
10
|
+
app.includeStandardAdditions = true;
|
|
11
|
+
(function waitForIdle(targetTty, timeoutSec) {
|
|
12
|
+
const terminal = Application("Terminal");
|
|
13
|
+
const startMs = Date.now();
|
|
14
|
+
const deadlineMs = startMs + timeoutSec * 1000;
|
|
15
|
+
|
|
16
|
+
function probe() {
|
|
17
|
+
const wins = terminal.windows();
|
|
18
|
+
for (let wi = 0; wi < wins.length; wi++) {
|
|
19
|
+
const w = wins[wi];
|
|
20
|
+
const tabs = w.tabs();
|
|
21
|
+
for (let ti = 0; ti < tabs.length; ti++) {
|
|
22
|
+
const t = tabs[ti];
|
|
23
|
+
if (safe(function () { return t.tty(); }) === targetTty) {
|
|
24
|
+
return { found: true, busy: safe(function () { return t.busy(); }) === true };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { found: false, busy: false };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let state = probe();
|
|
32
|
+
if (!state.found) {
|
|
33
|
+
return JSON.stringify({ tty: targetTty, missing: true, waited_ms: 0 });
|
|
34
|
+
}
|
|
35
|
+
if (!state.busy) {
|
|
36
|
+
return JSON.stringify({ tty: targetTty, idle: true, waited_ms: 0 });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
while (Date.now() < deadlineMs) {
|
|
40
|
+
delay(${POLL_INTERVAL_SEC});
|
|
41
|
+
state = probe();
|
|
42
|
+
if (!state.found) {
|
|
43
|
+
return JSON.stringify({ tty: targetTty, missing: true, waited_ms: Date.now() - startMs });
|
|
44
|
+
}
|
|
45
|
+
if (!state.busy) {
|
|
46
|
+
return JSON.stringify({ tty: targetTty, idle: true, waited_ms: Date.now() - startMs });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return JSON.stringify({ tty: targetTty, timed_out: true, waited_ms: Date.now() - startMs });
|
|
51
|
+
})(${JSON.stringify(tty)}, ${timeoutSec});
|
|
52
|
+
`;
|
|
53
|
+
}
|
|
54
|
+
export async function waitForIdleHandler({ tty, timeout_seconds = DEFAULT_TIMEOUT_SEC, }) {
|
|
55
|
+
const timeoutSec = Math.min(Math.max(timeout_seconds, 1), MAX_TIMEOUT_SEC);
|
|
56
|
+
try {
|
|
57
|
+
// Outer runJxa timeout is the inner poll deadline + 5s buffer to let the
|
|
58
|
+
// JXA finish writing its "timed_out" result before SIGKILL engages.
|
|
59
|
+
const raw = await runJxa(buildWaitForIdleScript(tty, timeoutSec), {
|
|
60
|
+
timeoutMs: (timeoutSec + 5) * 1000,
|
|
61
|
+
});
|
|
62
|
+
return { content: [{ type: "text", text: raw }] };
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: "text", text: `terminal_wait_for_idle failed: ${message}` }],
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
export function register(server) {
|
|
73
|
+
server.registerTool("terminal_wait_for_idle", {
|
|
74
|
+
description: "Block until the target Terminal.app tab is no longer busy (no foreground command running), or until timeout. Polls every 250ms inside a single osascript invocation. Useful for sequential workflows like 'run npm install, then run npm test'. Returns one of {idle: true, waited_ms}, {timed_out: true, waited_ms}, or {missing: true, waited_ms}. Read-only — no WRITE_TOOLS_ENABLED gate.",
|
|
75
|
+
inputSchema: {
|
|
76
|
+
tty: z
|
|
77
|
+
.string()
|
|
78
|
+
.regex(/^\/dev\/ttys[0-9]+$/)
|
|
79
|
+
.describe('Target tab tty, e.g. "/dev/ttys003"'),
|
|
80
|
+
timeout_seconds: z
|
|
81
|
+
.number()
|
|
82
|
+
.int()
|
|
83
|
+
.min(1)
|
|
84
|
+
.max(MAX_TIMEOUT_SEC)
|
|
85
|
+
.optional()
|
|
86
|
+
.describe(`Maximum seconds to wait before giving up. Default ${DEFAULT_TIMEOUT_SEC}, max ${MAX_TIMEOUT_SEC}.`),
|
|
87
|
+
},
|
|
88
|
+
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
89
|
+
}, waitForIdleHandler);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=wait_for_idle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wait_for_idle.js","sourceRoot":"","sources":["../../src/tools/wait_for_idle.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,MAAM,UAAU,sBAAsB,CAAC,GAAW,EAAE,UAAkB;IACpE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAiCG,iBAAiB;;;;;;;;;;;KAWxB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,UAAU;CACtC,CAAC;AACF,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EACvC,GAAG,EACH,eAAe,GAAG,mBAAmB,GACpB;IACjB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IAC3E,IAAI,CAAC;QACH,yEAAyE;QACzE,oEAAoE;QACpE,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,GAAG,EAAE,UAAU,CAAC,EAAE;YAChE,SAAS,EAAE,CAAC,UAAU,GAAG,CAAC,CAAC,GAAG,IAAI;SACnC,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kCAAkC,OAAO,EAAE,EAAE,CAAC;YAC9E,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,MAAiB;IACxC,MAAM,CAAC,YAAY,CACjB,wBAAwB,EACxB;QACE,WAAW,EACT,+XAA+X;QACjY,WAAW,EAAE;YACX,GAAG,EAAE,CAAC;iBACH,MAAM,EAAE;iBACR,KAAK,CAAC,qBAAqB,CAAC;iBAC5B,QAAQ,CAAC,qCAAqC,CAAC;YAClD,eAAe,EAAE,CAAC;iBACf,MAAM,EAAE;iBACR,GAAG,EAAE;iBACL,GAAG,CAAC,CAAC,CAAC;iBACN,GAAG,CAAC,eAAe,CAAC;iBACpB,QAAQ,EAAE;iBACV,QAAQ,CACP,qDAAqD,mBAAmB,SAAS,eAAe,GAAG,CACpG;SACJ;QACD,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE;KAC1D,EACD,kBAAkB,CACnB,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@priyanshumit/macos-terminal-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "MCP server exposing macOS Terminal.app to AI agents via AppleScript / JXA. List windows, read scrollback, execute commands, clear buffers — with three-tier safety patterns and confirmation dialogs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|