@priyanshumit/macos-terminal-mcp 0.5.0 → 0.5.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.
@@ -2,36 +2,52 @@ import { OsascriptError, runJxa } from "../applescript.js";
2
2
  import { appendAudit } from "../safety/audit.js";
3
3
  import { isWriteToolsEnabled, writeToolsDisabledMessage } from "../safety/confirm.js";
4
4
  export const NEW_TAB_SCRIPT = `
5
+ var app = Application.currentApplication();
6
+ app.includeStandardAdditions = true;
5
7
  function safe(fn) { try { return fn(); } catch (e) { return null; } }
8
+ function snapshotTabs(terminal) {
9
+ // Returns { tty -> windowId } for every tab in every window.
10
+ const out = {};
11
+ const wins = terminal.windows();
12
+ for (let wi = 0; wi < wins.length; wi++) {
13
+ const winId = safe(function () { return wins[wi].id(); });
14
+ const tabs = wins[wi].tabs();
15
+ for (let ti = 0; ti < tabs.length; ti++) {
16
+ const t = safe(function () { return tabs[ti].tty(); });
17
+ if (t) out[t] = winId;
18
+ }
19
+ }
20
+ return out;
21
+ }
6
22
  (function newTab() {
7
23
  const terminal = Application("Terminal");
24
+ const systemEvents = Application("System Events");
8
25
  terminal.activate();
9
- const wins = terminal.windows();
10
- // do script with empty command + no "in" → new window. With "in: front
11
- // window" new tab in that window. Prefer a new tab in the front window
12
- // when one exists, otherwise let it create a new window.
13
- let newTab;
14
- if (wins.length > 0) {
15
- newTab = terminal.doScript("", { in: wins[0] });
16
- } else {
17
- newTab = terminal.doScript("");
18
- }
19
- const tty = safe(function () { return newTab.tty(); }) || "";
20
- let windowId = null;
21
- try {
22
- const after = terminal.windows();
23
- for (let i = 0; i < after.length; i++) {
24
- const ts = after[i].tabs();
25
- for (let j = 0; j < ts.length; j++) {
26
- if (safe(function () { return ts[j].tty(); }) === tty) {
27
- windowId = safe(function () { return after[i].id(); });
28
- break;
29
- }
26
+ const before = snapshotTabs(terminal);
27
+ const wasEmpty = Object.keys(before).length === 0;
28
+ // Cmd+T opens a new tab in the front window; Cmd+N opens a new window when
29
+ // there are none.
30
+ systemEvents.keystroke(wasEmpty ? "n" : "t", { using: "command down" });
31
+ // Poll for the new tab's tty to appear. Each iteration waits 100ms, up to ~2s.
32
+ let newTty = null;
33
+ let newWindowId = null;
34
+ for (let attempt = 0; attempt < 20; attempt++) {
35
+ delay(0.1);
36
+ const after = snapshotTabs(terminal);
37
+ const keys = Object.keys(after);
38
+ for (let i = 0; i < keys.length; i++) {
39
+ if (!(keys[i] in before)) {
40
+ newTty = keys[i];
41
+ newWindowId = after[keys[i]];
42
+ break;
30
43
  }
31
- if (windowId !== null) break;
32
44
  }
33
- } catch (e) { /* best-effort */ }
34
- return JSON.stringify({ tty, windowId });
45
+ if (newTty !== null) break;
46
+ }
47
+ if (newTty === null) {
48
+ throw new Error("terminal_new_tab: no new tab appeared after Cmd+" + (wasEmpty ? "N" : "T") + ". Accessibility permission may be missing — grant via System Settings → Privacy & Security → Accessibility.");
49
+ }
50
+ return JSON.stringify({ tty: newTty, windowId: newWindowId });
35
51
  })();
36
52
  `;
37
53
  export async function newTabHandler() {
@@ -64,7 +80,7 @@ export async function newTabHandler() {
64
80
  }
65
81
  export function register(server) {
66
82
  server.registerTool("terminal_new_tab", {
67
- description: "Open a new empty tab in Terminal.app (in the frontmost window, or a new window if none are open). Returns {tty, windowId} for the new tab. Use the returned tty in subsequent terminal_read / terminal_execute calls. NOTE: this tool does NOT execute any command in the new tab — to run a command, call terminal_execute against the returned tty. Requires WRITE_TOOLS_ENABLED=1 but does NOT pop a confirmation dialog (low blast radius — the user can close an unwanted tab).",
83
+ description: "Open a new empty tab in Terminal.app (in the frontmost window, or a new window if none are open). Returns {tty, windowId} for the new tab. Use the returned tty in subsequent terminal_read / terminal_execute calls. NOTE: this tool does NOT execute any command in the new tab — to run a command, call terminal_execute against the returned tty. Requires WRITE_TOOLS_ENABLED=1 but does NOT pop a confirmation dialog (low blast radius — the user can close an unwanted tab). Briefly steals focus to Terminal.app to deliver a Cmd+T keystroke, so also requires Accessibility permission (System Settings → Privacy & Security → Accessibility).",
68
84
  }, newTabHandler);
69
85
  }
70
86
  //# sourceMappingURL=new_tab.js.map
@@ -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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC7B,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,sdAAsd;KACzd,EACD,aAAa,CACd,CAAC;AACJ,CAAC"}
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgD7B,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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@priyanshumit/macos-terminal-mcp",
3
- "version": "0.5.0",
3
+ "version": "0.5.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": {