terminator-mcp-agent 0.5.6

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.
Files changed (3) hide show
  1. package/README.md +78 -0
  2. package/index.js +372 -0
  3. package/package.json +30 -0
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ ## Terminator MCP Agent
2
+
3
+ <!-- BADGES:START -->
4
+
5
+ [<img alt="Install in VS Code" src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20Server&color=0098FF">](https://insiders.vscode.dev/redirect?url=vscode%3Amcp%2Finstall%3F%257B%2522terminator-mcp-agent%2522%253A%257B%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522terminator-mcp-agent%2522%255D%257D%257D)
6
+ [<img alt="Install in VS Code Insiders" src="https://img.shields.io/badge/VS_Code_Insiders-VS_Code_Insiders?style=flat-square&label=Install%20Server&color=24bfa5">](https://insiders.vscode.dev/redirect?url=vscode-insiders%3Amcp%2Finstall%3F%257B%2522terminator-mcp-agent%2522%253A%257B%2522command%2522%253A%2522npx%2522%252C%2522args%2522%253A%255B%2522-y%2522%252C%2522terminator-mcp-agent%2522%255D%257D%257D)
7
+ [<img alt="Install in Cursor" src="https://img.shields.io/badge/Cursor-Cursor?style=flat-square&label=Install%20Server&color=22272e">](https://cursor.com/install-mcp?name=terminator-mcp-agent&config=eyJ0ZXJtaW5hdG9yLW1jcC1hZ2VudCI6eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsInRlcm1pbmF0b3ItbWNwLWFnZW50Il19fQ%3D%3D)
8
+
9
+ <!-- BADGES:END -->
10
+
11
+ A Model Context Protocol (MCP) server that provides desktop GUI automation capabilities using the [Terminator](https://github.com/mediar-ai/terminator) library. This server enables LLMs and agentic clients to interact with Windows, macOS, and Linux applications through structured accessibility APIs—no vision models or screenshots required.
12
+
13
+ ### Key Features
14
+
15
+ - **Fast and lightweight**. Uses OS-level accessibility APIs, not pixel-based input.
16
+ - **LLM/agent-friendly**. No vision models needed, operates purely on structured data.
17
+ - **Deterministic automation**. Avoids ambiguity common with screenshot-based approaches.
18
+ - **Multi-platform**. Supports Windows (full), macOS (partial), Linux (partial).
19
+
20
+ ### Requirements
21
+
22
+ - Node.js 16 or newer
23
+ - VS Code, Cursor, Windsurf, Claude Desktop, or any other MCP client
24
+
25
+ ### Getting started
26
+
27
+ First, install the Terminator MCP server with your client. A typical configuration looks like this:
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "terminator-mcp-agent": {
33
+ "command": "npx",
34
+ "args": ["-y", "terminator-mcp-agent"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ You can also use the CLI to configure your app automatically:
41
+
42
+ ```sh
43
+ npx -y terminator-mcp-agent --add-to-app [app]
44
+ ```
45
+
46
+ Replace `[app]` with one of: `cursor`, `claude`, `vscode`, `insiders`, `windsurf`.
47
+
48
+ ---
49
+
50
+ <img width="1512" alt="Screenshot 2025-04-16 at 9 29 42 AM" src="https://github.com/user-attachments/assets/457ebaf2-640c-4f21-a236-fcb2b92748ab" />
51
+
52
+ MCP is useful to test out the `terminator` lib and see what you can do. You can use any model.
53
+
54
+ <br>
55
+
56
+ ## Development
57
+
58
+ If you want to build and test the agent locally, clone the repo and run:
59
+
60
+ ```sh
61
+ git clone https://github.com/mediar-ai/terminator
62
+ cd terminator/terminator-mcp-agent
63
+ npm install
64
+ npm run build
65
+ npm install --global .
66
+ ```
67
+
68
+ You can then use the CLI as above.
69
+
70
+ ---
71
+
72
+ ## Troubleshooting
73
+
74
+ - Make sure you have Node.js installed (v16+ recommended).
75
+ - For VS Code/Insiders, ensure the CLI (`code` or `code-insiders`) is available in your PATH.
76
+ - If you encounter issues, try running with elevated permissions or check the config file paths above.
77
+
78
+ ---
package/index.js ADDED
@@ -0,0 +1,372 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn, execSync, existsSync } = require("child_process");
4
+ const path = require("path");
5
+ const fs = require("fs");
6
+ const os = require("os");
7
+ const readline = require("readline");
8
+
9
+ function getPlatformInfo() {
10
+ const platform = process.platform;
11
+ const arch = process.arch;
12
+ if (platform === "win32" && arch === "x64")
13
+ return {
14
+ pkg: "terminator-mcp-win32-x64-msvc",
15
+ bin: "terminator-mcp-agent.exe",
16
+ npmDir: "win32-x64-msvc",
17
+ };
18
+ if (platform === "linux" && arch === "x64")
19
+ return {
20
+ pkg: "terminator-mcp-linux-x64-gnu",
21
+ bin: "terminator-mcp-agent",
22
+ npmDir: "linux-x64-gnu",
23
+ };
24
+ if (platform === "darwin" && arch === "x64")
25
+ return {
26
+ pkg: "terminator-mcp-darwin-x64",
27
+ bin: "terminator-mcp-agent",
28
+ npmDir: "darwin-x64",
29
+ };
30
+ if (platform === "darwin" && arch === "arm64")
31
+ return {
32
+ pkg: "terminator-mcp-darwin-arm64",
33
+ bin: "terminator-mcp-agent",
34
+ npmDir: "darwin-arm64",
35
+ };
36
+ throw new Error(`Unsupported platform: ${platform} ${arch}`);
37
+ }
38
+
39
+ function getMcpServerEntry() {
40
+ return {
41
+ command: "npx",
42
+ args: ["-y", "terminator-mcp-agent"],
43
+ };
44
+ }
45
+
46
+ function addToCursorConfig() {
47
+ const home = os.homedir();
48
+ const configDir = path.join(home, ".cursor");
49
+ const configFile = path.join(configDir, "mcp.json");
50
+ if (!fs.existsSync(configDir)) {
51
+ fs.mkdirSync(configDir, { recursive: true });
52
+ }
53
+ let config = {};
54
+ if (fs.existsSync(configFile)) {
55
+ try {
56
+ config = JSON.parse(fs.readFileSync(configFile, "utf8"));
57
+ } catch (e) {
58
+ config = {};
59
+ }
60
+ }
61
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
62
+ config.mcpServers = {};
63
+ }
64
+ config.mcpServers["terminator-mcp-agent"] = getMcpServerEntry();
65
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
66
+ console.log(`Cursor configuration saved to ${configFile}`);
67
+ }
68
+
69
+ function addToClaudeConfig() {
70
+ const platform = process.platform;
71
+ let configDir, configFile;
72
+ if (platform === "win32") {
73
+ const appData =
74
+ process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
75
+ configDir = path.join(appData, "Claude");
76
+ configFile = path.join(configDir, "claude_desktop_config.json");
77
+ } else if (platform === "darwin") {
78
+ configDir = path.join(
79
+ os.homedir(),
80
+ "Library",
81
+ "Application Support",
82
+ "Claude",
83
+ );
84
+ configFile = path.join(configDir, "claude_desktop_config.json");
85
+ } else {
86
+ console.error("Claude desktop is only supported on Windows and macOS.");
87
+ process.exit(1);
88
+ }
89
+ if (!fs.existsSync(configDir)) {
90
+ console.error(
91
+ `Claude desktop config directory does not exist: ${configDir}\nPlease make sure the Claude desktop app is installed for your platform.`,
92
+ );
93
+ process.exit(1);
94
+ }
95
+ let config = {};
96
+ if (fs.existsSync(configFile)) {
97
+ try {
98
+ config = JSON.parse(fs.readFileSync(configFile, "utf8"));
99
+ } catch (e) {
100
+ config = {};
101
+ }
102
+ }
103
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
104
+ config.mcpServers = {};
105
+ }
106
+ config.mcpServers["terminator-mcp-agent"] = getMcpServerEntry();
107
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
108
+ console.log(`Claude configuration saved to ${configFile}`);
109
+ }
110
+
111
+ function buildVSCodeMcpJsonArg(mcpJson) {
112
+ // VS Code expects: --add-mcp "{\"name\":\"...\",...}"
113
+ return `"${JSON.stringify(mcpJson).replace(/"/g, '\\"')}"`;
114
+ }
115
+
116
+ function addToVSCodeConfig() {
117
+ // VS Code CLI-based setup
118
+ console.log("Adding Terminator MCP to VS Code via code CLI...");
119
+ const mcpJson = {
120
+ name: "terminator-mcp-agent",
121
+ command: "npx",
122
+ args: ["-y", "terminator-mcp-agent"],
123
+ };
124
+ const jsonArg = buildVSCodeMcpJsonArg(mcpJson);
125
+ const vscodeCmd = "code";
126
+ try {
127
+ const { spawnSync } = require("child_process");
128
+ const result = spawnSync(`${vscodeCmd} --add-mcp ${jsonArg}`, [], {
129
+ stdio: "inherit",
130
+ shell: true,
131
+ });
132
+ if (result.error) {
133
+ if (result.error.code === "ENOENT") {
134
+ console.error(
135
+ "'code' command not found in PATH. Make sure VS Code CLI is installed and available.",
136
+ );
137
+ } else {
138
+ console.error("Failed to launch VS Code CLI:", result.error.message);
139
+ }
140
+ process.exit(1);
141
+ }
142
+ if (result.status !== 0) {
143
+ console.error(`VS Code CLI exited with code ${result.status}`);
144
+ process.exit(1);
145
+ }
146
+ console.log("Successfully added Terminator MCP to VS Code.");
147
+ } catch (e) {
148
+ console.error("Failed to add MCP to VS Code:", e.message);
149
+ process.exit(1);
150
+ }
151
+ }
152
+
153
+ function addToVSCodeInsidersConfig() {
154
+ // VS Code Insiders CLI-based setup
155
+ console.log(
156
+ "Adding Terminator MCP to VS Code Insiders via code-insiders CLI...",
157
+ );
158
+ const mcpJson = {
159
+ name: "terminator-mcp-agent",
160
+ command: "npx",
161
+ args: ["-y", "terminator-mcp-agent"],
162
+ };
163
+ const jsonArg = buildVSCodeMcpJsonArg(mcpJson);
164
+ const codeInsidersCmd = "code-insiders";
165
+ try {
166
+ const { spawnSync } = require("child_process");
167
+ const result = spawnSync(`${codeInsidersCmd} --add-mcp ${jsonArg}`, [], {
168
+ stdio: "inherit",
169
+ shell: true,
170
+ });
171
+ if (result.error) {
172
+ if (result.error.code === "ENOENT") {
173
+ console.error(
174
+ "'code-insiders' command not found in PATH. Make sure VS Code Insiders CLI is installed and available.",
175
+ );
176
+ } else {
177
+ console.error(
178
+ "Failed to launch VS Code Insiders CLI:",
179
+ result.error.message,
180
+ );
181
+ }
182
+ process.exit(1);
183
+ }
184
+ if (result.status !== 0) {
185
+ console.error(`VS Code Insiders CLI exited with code ${result.status}`);
186
+ process.exit(1);
187
+ }
188
+ console.log("Successfully added Terminator MCP to VS Code Insiders.");
189
+ } catch (e) {
190
+ console.error("Failed to add MCP to VS Code Insiders:", e.message);
191
+ process.exit(1);
192
+ }
193
+ }
194
+
195
+ function addToWindsurfConfig() {
196
+ // Windsurf config: %USERPROFILE%/.codeium/windsurf/mcp_config.json
197
+ const home = os.homedir();
198
+ const configDir = path.join(home, ".codeium", "windsurf");
199
+ const configFile = path.join(configDir, "mcp_config.json");
200
+ if (!fs.existsSync(configDir)) {
201
+ fs.mkdirSync(configDir, { recursive: true });
202
+ }
203
+ let config = {};
204
+ if (fs.existsSync(configFile)) {
205
+ try {
206
+ config = JSON.parse(fs.readFileSync(configFile, "utf8"));
207
+ } catch (e) {
208
+ config = {};
209
+ }
210
+ }
211
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
212
+ config.mcpServers = {};
213
+ }
214
+ config.mcpServers["terminator-mcp-agent"] = getMcpServerEntry();
215
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2));
216
+ console.log(`Windsurf configuration saved to ${configFile}`);
217
+ }
218
+
219
+ function addToApp(app) {
220
+ switch ((app || "").toLowerCase()) {
221
+ case "cursor":
222
+ addToCursorConfig();
223
+ break;
224
+ case "claude":
225
+ addToClaudeConfig();
226
+ break;
227
+ case "vscode":
228
+ addToVSCodeConfig();
229
+ break;
230
+ case "insiders":
231
+ addToVSCodeInsidersConfig();
232
+ break;
233
+ case "windsurf":
234
+ addToWindsurfConfig();
235
+ break;
236
+ default:
237
+ console.error("Unknown app: " + app);
238
+ process.exit(1);
239
+ }
240
+ }
241
+
242
+ const argv = process.argv.slice(2);
243
+
244
+ if (argv.includes("--add-to-app")) {
245
+ const appIndex = argv.indexOf("--add-to-app") + 1;
246
+ const app =
247
+ argv[appIndex] && !argv[appIndex].startsWith("--")
248
+ ? argv[appIndex]
249
+ : undefined;
250
+ if (!app) {
251
+ // Interactive prompt
252
+ const rl = readline.createInterface({
253
+ input: process.stdin,
254
+ output: process.stdout,
255
+ });
256
+ console.log("========== Terminator MCP Setup ==========");
257
+ console.log("Which app do you want to configure Terminator MCP for?");
258
+ console.log("");
259
+ console.log(" 1. Cursor");
260
+ console.log(" 2. Claude");
261
+ console.log(" 3. VS Code");
262
+ console.log(" 4. VS Code Insiders");
263
+ console.log(" 5. Windsurf");
264
+ console.log("");
265
+ rl.question("Enter your choice (1-5): ", (answer) => {
266
+ let selectedApp = null;
267
+ switch (answer.trim()) {
268
+ case "1":
269
+ selectedApp = "cursor";
270
+ break;
271
+ case "2":
272
+ selectedApp = "claude";
273
+ break;
274
+ case "3":
275
+ selectedApp = "vscode";
276
+ break;
277
+ case "4":
278
+ selectedApp = "insiders";
279
+ break;
280
+ case "5":
281
+ selectedApp = "windsurf";
282
+ break;
283
+ default:
284
+ console.error("Invalid choice. Skipping app configuration.");
285
+ rl.close();
286
+ process.exit(1);
287
+ }
288
+ rl.close();
289
+ addToApp(selectedApp);
290
+ process.exit(0);
291
+ });
292
+ return;
293
+ } else {
294
+ addToApp(app);
295
+ process.exit(0);
296
+ }
297
+ }
298
+
299
+ // Default or --start: run the agent
300
+ if (argv.length === 0 || argv.includes("--start")) {
301
+ const { pkg, bin, npmDir } = getPlatformInfo();
302
+ let binary;
303
+
304
+ // 1. Try local build (for dev)
305
+ const localPath = path.join(__dirname, "npm", npmDir, bin);
306
+ if (fs.existsSync(localPath)) {
307
+ binary = localPath;
308
+ } else {
309
+ // 2. Try installed npm package
310
+ try {
311
+ const pkgPath = require.resolve(`${pkg}/package.json`);
312
+ const binDir = path.dirname(pkgPath);
313
+ binary = path.join(binDir, bin);
314
+ } catch (e) {
315
+ console.error(`Failed to find platform binary: ${pkg}`);
316
+ process.exit(1);
317
+ }
318
+ }
319
+
320
+ const child = spawn(binary, [], { stdio: ["pipe", "pipe", "inherit"] });
321
+
322
+ process.stdin.pipe(child.stdin);
323
+ child.stdout.pipe(process.stdout);
324
+
325
+ function killProcess(proc) {
326
+ if (!proc) return;
327
+ const pid = proc.pid;
328
+ if (process.platform === "win32") {
329
+ try {
330
+ execSync(`taskkill /PID ${pid} /T /F`);
331
+ } catch (e) {}
332
+ } else {
333
+ try {
334
+ process.kill(-pid, "SIGKILL");
335
+ } catch (e) {}
336
+ }
337
+ }
338
+
339
+ let shuttingDown = false;
340
+ function shutdown() {
341
+ if (shuttingDown) return;
342
+ shuttingDown = true;
343
+ if (child && !child.killed) {
344
+ if (child.stdin) child.stdin.end();
345
+ const termTimeout = setTimeout(() => {
346
+ if (!child.killed) {
347
+ if (process.platform === "win32") {
348
+ killProcess(child);
349
+ } else {
350
+ try {
351
+ process.kill(child.pid, "SIGTERM");
352
+ } catch (e) {}
353
+ setTimeout(() => {
354
+ if (!child.killed) killProcess(child);
355
+ }, 2000);
356
+ }
357
+ }
358
+ }, 2000);
359
+ child.on("exit", () => clearTimeout(termTimeout));
360
+ }
361
+ process.exit();
362
+ }
363
+
364
+ process.on("SIGINT", shutdown);
365
+ process.on("SIGTERM", shutdown);
366
+ process.on("exit", shutdown);
367
+
368
+ child.on("exit", (code) => {
369
+ console.log(`[MCP exited with code ${code}]`);
370
+ process.exit(code);
371
+ });
372
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "bin": {
3
+ "terminator-mcp-agent": "index.js"
4
+ },
5
+ "description": "Cross-platform Model Context Protocol (MCP) agent for Terminator",
6
+ "engines": {
7
+ "node": ">= 10"
8
+ },
9
+ "files": [
10
+ "index.js"
11
+ ],
12
+ "name": "terminator-mcp-agent",
13
+ "optionalDependencies": {
14
+ "terminator-mcp-darwin-arm64": "0.5.6",
15
+ "terminator-mcp-darwin-x64": "0.5.6",
16
+ "terminator-mcp-linux-x64-gnu": "0.5.6",
17
+ "terminator-mcp-win32-x64-msvc": "0.5.6"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/mediar-ai/terminator"
22
+ },
23
+ "scripts": {
24
+ "build": "node ./utils/build-server.js --release",
25
+ "build:debug": "node ./utils/build-server.js --debug",
26
+ "sync-version": "node ./utils/sync-version.js",
27
+ "update-badges": "node ./utils/update-badges.js"
28
+ },
29
+ "version": "0.5.6"
30
+ }