@ebowwa/osascript 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -0
- package/dist/index.js +574 -0
- package/dist/mcp/index.js +866 -0
- package/dist/mcp/stdio.js +1704 -0
- package/package.json +79 -0
- package/src/index.ts +844 -0
- package/src/mcp/events.ts +468 -0
- package/src/mcp/index.ts +565 -0
- package/src/mcp/stdio.ts +1284 -0
- package/src/types.ts +189 -0
package/src/mcp/index.ts
ADDED
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* osascript MCP Server (HTTP)
|
|
4
|
+
*
|
|
5
|
+
* Model Context Protocol server for macOS automation via osascript
|
|
6
|
+
* Uses HTTP transport for remote access
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
executeAppleScript,
|
|
11
|
+
executeJXA,
|
|
12
|
+
displayNotification,
|
|
13
|
+
displayDialog,
|
|
14
|
+
getVolume,
|
|
15
|
+
setVolume,
|
|
16
|
+
getClipboard,
|
|
17
|
+
setClipboard,
|
|
18
|
+
clearClipboard,
|
|
19
|
+
listRunningApplications,
|
|
20
|
+
getApplicationInfo,
|
|
21
|
+
activateApplication,
|
|
22
|
+
quitApplication,
|
|
23
|
+
launchApplication,
|
|
24
|
+
getFrontmostApplication,
|
|
25
|
+
getApplicationWindows,
|
|
26
|
+
setWindowBounds,
|
|
27
|
+
sendKeystroke,
|
|
28
|
+
sendKeyCode,
|
|
29
|
+
typeText,
|
|
30
|
+
getSafariURL,
|
|
31
|
+
setSafariURL,
|
|
32
|
+
getSafariTabTitles,
|
|
33
|
+
executeSafariJS,
|
|
34
|
+
getFinderSelection,
|
|
35
|
+
openFinderWindow,
|
|
36
|
+
createFolder,
|
|
37
|
+
moveToTrash,
|
|
38
|
+
getDisplayInfo,
|
|
39
|
+
getScreenResolution,
|
|
40
|
+
sayText,
|
|
41
|
+
beep,
|
|
42
|
+
getSystemDateTime,
|
|
43
|
+
isMacOS,
|
|
44
|
+
} from "../index.js";
|
|
45
|
+
|
|
46
|
+
import type { NotificationOptions, DialogOptions, KeystrokeOptions, KeyCodeOptions } from "../types.js";
|
|
47
|
+
|
|
48
|
+
// ==============
|
|
49
|
+
// Configuration
|
|
50
|
+
// ==============
|
|
51
|
+
|
|
52
|
+
const MCP_PORT = parseInt(process.env.MCP_PORT || "8914");
|
|
53
|
+
|
|
54
|
+
// ==============
|
|
55
|
+
// Tool Implementations
|
|
56
|
+
// ==============
|
|
57
|
+
|
|
58
|
+
async function osascriptExecute(
|
|
59
|
+
script: string,
|
|
60
|
+
language: "applescript" | "javascript" = "applescript"
|
|
61
|
+
): Promise<string> {
|
|
62
|
+
const result = language === "javascript" ? await executeJXA(script) : await executeAppleScript(script);
|
|
63
|
+
return JSON.stringify(result, null, 2);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function osascriptNotification(
|
|
67
|
+
message: string,
|
|
68
|
+
title: string = "Notification",
|
|
69
|
+
subtitle?: string,
|
|
70
|
+
soundName?: string
|
|
71
|
+
): Promise<string> {
|
|
72
|
+
const options: NotificationOptions = { message, title, subtitle, soundName };
|
|
73
|
+
const result = await displayNotification(options);
|
|
74
|
+
return result.success ? `Notification sent: ${title}` : `Error: ${result.stderr}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function osascriptDialog(
|
|
78
|
+
message: string,
|
|
79
|
+
title?: string,
|
|
80
|
+
buttons?: string[],
|
|
81
|
+
defaultButton?: number,
|
|
82
|
+
icon?: "note" | "caution" | "stop"
|
|
83
|
+
): Promise<string> {
|
|
84
|
+
const options: DialogOptions = { message, title, buttons, defaultButton, icon };
|
|
85
|
+
try {
|
|
86
|
+
const result = await displayDialog(options);
|
|
87
|
+
return `Button returned: ${result.buttonReturned}${result.gaveUp ? " (timed out)" : ""}`;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
return `Error: ${error}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function osascriptGetVolume(): Promise<string> {
|
|
94
|
+
const info = await getVolume();
|
|
95
|
+
return `Volume: ${info.volume}%${info.muted ? " (muted)" : ""}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function osascriptSetVolume(volume: number, muted?: boolean): Promise<string> {
|
|
99
|
+
const result = await setVolume(volume, muted);
|
|
100
|
+
return result.success ? `Volume set to ${volume}%` : `Error: ${result.stderr}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function osascriptGetClipboard(): Promise<string> {
|
|
104
|
+
try {
|
|
105
|
+
const content = await getClipboard();
|
|
106
|
+
return content || "(clipboard is empty)";
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return `Error: ${error}`;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function osascriptSetClipboard(text: string): Promise<string> {
|
|
113
|
+
const result = await setClipboard(text);
|
|
114
|
+
return result.success ? "Clipboard updated" : `Error: ${result.stderr}`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function osascriptClearClipboard(): Promise<string> {
|
|
118
|
+
const result = await clearClipboard();
|
|
119
|
+
return result.success ? "Clipboard cleared" : `Error: ${result.stderr}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function osascriptListApps(): Promise<string> {
|
|
123
|
+
const apps = await listRunningApplications();
|
|
124
|
+
const lines = ["Running Applications:", "=".repeat(40), ""];
|
|
125
|
+
for (const app of apps) {
|
|
126
|
+
lines.push(`- ${app.name}`);
|
|
127
|
+
}
|
|
128
|
+
return lines.join("\n");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function osascriptGetAppInfo(appName: string): Promise<string> {
|
|
132
|
+
const info = await getApplicationInfo(appName);
|
|
133
|
+
if (!info.running) {
|
|
134
|
+
return `Application "${appName}" is not running`;
|
|
135
|
+
}
|
|
136
|
+
const lines = [
|
|
137
|
+
`Application: ${info.name}`,
|
|
138
|
+
"=".repeat(40),
|
|
139
|
+
`Running: ${info.running}`,
|
|
140
|
+
info.path ? `Path: ${info.path}` : "",
|
|
141
|
+
info.processId ? `PID: ${info.processId}` : "",
|
|
142
|
+
info.windowCount !== undefined ? `Windows: ${info.windowCount}` : "",
|
|
143
|
+
].filter(Boolean);
|
|
144
|
+
return lines.join("\n");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function osascriptActivateApp(appName: string): Promise<string> {
|
|
148
|
+
const result = await activateApplication(appName);
|
|
149
|
+
return result.success ? `Activated ${appName}` : `Error: ${result.stderr}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function osascriptQuitApp(appName: string, force: boolean = false): Promise<string> {
|
|
153
|
+
const result = await quitApplication(appName, force);
|
|
154
|
+
return result.success ? `Quit ${appName}` : `Error: ${result.stderr}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function osascriptLaunchApp(appName: string): Promise<string> {
|
|
158
|
+
const result = await launchApplication(appName);
|
|
159
|
+
return result.success ? `Launched ${appName}` : `Error: ${result.stderr}`;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function osascriptGetFrontmostApp(): Promise<string> {
|
|
163
|
+
const appName = await getFrontmostApplication();
|
|
164
|
+
return `Frontmost application: ${appName}`;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function osascriptGetWindows(appName: string): Promise<string> {
|
|
168
|
+
const windows = await getApplicationWindows(appName);
|
|
169
|
+
if (windows.length === 0) {
|
|
170
|
+
return `No windows for ${appName}`;
|
|
171
|
+
}
|
|
172
|
+
const lines = [`Windows for ${appName}:`, "=".repeat(40), ""];
|
|
173
|
+
for (const win of windows) {
|
|
174
|
+
lines.push(`${win.index}. ${win.name || "(unnamed)"} (ID: ${win.id})`);
|
|
175
|
+
}
|
|
176
|
+
return lines.join("\n");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function osascriptSetWindowBounds(
|
|
180
|
+
appName: string,
|
|
181
|
+
windowIndex: number,
|
|
182
|
+
x: number,
|
|
183
|
+
y: number,
|
|
184
|
+
width: number,
|
|
185
|
+
height: number
|
|
186
|
+
): Promise<string> {
|
|
187
|
+
const result = await setWindowBounds(appName, windowIndex, { x, y, width, height });
|
|
188
|
+
return result.success
|
|
189
|
+
? `Window ${windowIndex} bounds set to ${width}x${height} at (${x}, ${y})`
|
|
190
|
+
: `Error: ${result.stderr}`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function parseModifiers(modifiersStr?: string): ("command" | "shift" | "option" | "control")[] {
|
|
194
|
+
if (!modifiersStr) return [];
|
|
195
|
+
return modifiersStr
|
|
196
|
+
.split(",")
|
|
197
|
+
.map((m) => m.trim().toLowerCase() as "command" | "shift" | "option" | "control")
|
|
198
|
+
.filter((m) => ["command", "shift", "option", "control"].includes(m));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function osascriptKeystroke(
|
|
202
|
+
key: string,
|
|
203
|
+
modifiers: ("command" | "shift" | "option" | "control")[] = []
|
|
204
|
+
): Promise<string> {
|
|
205
|
+
const options: KeystrokeOptions = { key, modifiers };
|
|
206
|
+
const result = await sendKeystroke(options);
|
|
207
|
+
const modStr = modifiers.length > 0 ? ` with ${modifiers.join("+")}` : "";
|
|
208
|
+
return result.success ? `Sent keystroke: ${key}${modStr}` : `Error: ${result.stderr}`;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function osascriptKeyCode(
|
|
212
|
+
keyCode: number,
|
|
213
|
+
modifiers: ("command" | "shift" | "option" | "control")[] = []
|
|
214
|
+
): Promise<string> {
|
|
215
|
+
const options: KeyCodeOptions = { keyCode, modifiers };
|
|
216
|
+
const result = await sendKeyCode(options);
|
|
217
|
+
const modStr = modifiers.length > 0 ? ` with ${modifiers.join("+")}` : "";
|
|
218
|
+
return result.success ? `Sent key code: ${keyCode}${modStr}` : `Error: ${result.stderr}`;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function osascriptTypeText(text: string): Promise<string> {
|
|
222
|
+
const result = await typeText(text);
|
|
223
|
+
return result.success ? `Typed: ${text}` : `Error: ${result.stderr}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function osascriptSafariGetURL(): Promise<string> {
|
|
227
|
+
try {
|
|
228
|
+
const url = await getSafariURL();
|
|
229
|
+
return `Current Safari URL: ${url}`;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
return `Error: ${error}`;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function osascriptSafariSetURL(url: string): Promise<string> {
|
|
236
|
+
const result = await setSafariURL(url);
|
|
237
|
+
return result.success ? `Navigated to: ${url}` : `Error: ${result.stderr}`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function osascriptSafariGetTabs(): Promise<string> {
|
|
241
|
+
const titles = await getSafariTabTitles();
|
|
242
|
+
if (titles.length === 0) {
|
|
243
|
+
return "No Safari tabs";
|
|
244
|
+
}
|
|
245
|
+
const lines = ["Safari Tabs:", "=".repeat(40), ""];
|
|
246
|
+
titles.forEach((title, i) => lines.push(`${i + 1}. ${title}`));
|
|
247
|
+
return lines.join("\n");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function osascriptSafariExecJS(jsCode: string): Promise<string> {
|
|
251
|
+
try {
|
|
252
|
+
const result = await executeSafariJS(jsCode);
|
|
253
|
+
return result || "(no output)";
|
|
254
|
+
} catch (error) {
|
|
255
|
+
return `Error: ${error}`;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async function osascriptFinderGetSelection(): Promise<string> {
|
|
260
|
+
const items = await getFinderSelection();
|
|
261
|
+
if (items.length === 0) {
|
|
262
|
+
return "No Finder selection";
|
|
263
|
+
}
|
|
264
|
+
const lines = ["Finder Selection:", "=".repeat(40), ""];
|
|
265
|
+
for (const item of items) {
|
|
266
|
+
lines.push(`- ${item.name} (${item.type})`);
|
|
267
|
+
lines.push(` ${item.path}`);
|
|
268
|
+
}
|
|
269
|
+
return lines.join("\n");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function osascriptFinderOpen(path: string): Promise<string> {
|
|
273
|
+
const result = await openFinderWindow(path);
|
|
274
|
+
return result.success ? `Opened Finder at: ${path}` : `Error: ${result.stderr}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function osascriptFinderCreateFolder(parentPath: string, folderName: string): Promise<string> {
|
|
278
|
+
const result = await createFolder(parentPath, folderName);
|
|
279
|
+
return result.success ? `Created folder: ${folderName}` : `Error: ${result.stderr}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async function osascriptFinderTrash(path: string): Promise<string> {
|
|
283
|
+
const result = await moveToTrash(path);
|
|
284
|
+
return result.success ? `Moved to trash: ${path}` : `Error: ${result.stderr}`;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
async function osascriptGetDisplays(): Promise<string> {
|
|
288
|
+
const displays = await getDisplayInfo();
|
|
289
|
+
const lines = ["Display Information:", "=".repeat(40), ""];
|
|
290
|
+
for (const display of displays) {
|
|
291
|
+
lines.push(`Display ${display.id}: ${display.width}x${display.height}${display.main ? " (main)" : ""}`);
|
|
292
|
+
}
|
|
293
|
+
return lines.join("\n");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function osascriptGetResolution(): Promise<string> {
|
|
297
|
+
const { width, height } = await getScreenResolution();
|
|
298
|
+
return `Screen resolution: ${width}x${height}`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function osascriptSay(text: string, voice?: string, rate?: number): Promise<string> {
|
|
302
|
+
const result = await sayText(text, voice, rate);
|
|
303
|
+
return result.success ? `Said: "${text}"` : `Error: ${result.stderr}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function osascriptBeep(count: number = 1): Promise<string> {
|
|
307
|
+
const result = await beep(count);
|
|
308
|
+
return result.success ? `Beeped ${count} time(s)` : `Error: ${result.stderr}`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function osascriptGetDateTime(): Promise<string> {
|
|
312
|
+
const dateTime = await getSystemDateTime();
|
|
313
|
+
return `System date/time: ${dateTime}`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// ==============
|
|
317
|
+
// Tool Definitions
|
|
318
|
+
// ==============
|
|
319
|
+
|
|
320
|
+
const TOOLS = [
|
|
321
|
+
// Core
|
|
322
|
+
{ name: "osascript_execute", description: "Execute raw AppleScript or JXA code", args: ["script", "language?"] },
|
|
323
|
+
// System
|
|
324
|
+
{ name: "osascript_display_notification", description: "Display a macOS notification", args: ["message", "title?", "subtitle?", "sound_name?"] },
|
|
325
|
+
{ name: "osascript_display_dialog", description: "Display a dialog with buttons", args: ["message", "title?", "buttons?", "default_button?", "icon?"] },
|
|
326
|
+
// Volume
|
|
327
|
+
{ name: "osascript_get_volume", description: "Get system volume level", args: [] },
|
|
328
|
+
{ name: "osascript_set_volume", description: "Set system volume level (0-100)", args: ["volume", "muted?"] },
|
|
329
|
+
// Clipboard
|
|
330
|
+
{ name: "osascript_get_clipboard", description: "Get clipboard contents", args: [] },
|
|
331
|
+
{ name: "osascript_set_clipboard", description: "Set clipboard contents", args: ["text"] },
|
|
332
|
+
{ name: "osascript_clear_clipboard", description: "Clear clipboard contents", args: [] },
|
|
333
|
+
// Applications
|
|
334
|
+
{ name: "osascript_list_apps", description: "List all running applications", args: [] },
|
|
335
|
+
{ name: "osascript_get_app_info", description: "Get detailed information about an application", args: ["app_name"] },
|
|
336
|
+
{ name: "osascript_activate_app", description: "Activate (bring to front) an application", args: ["app_name"] },
|
|
337
|
+
{ name: "osascript_quit_app", description: "Quit an application", args: ["app_name", "force?"] },
|
|
338
|
+
{ name: "osascript_launch_app", description: "Launch an application", args: ["app_name"] },
|
|
339
|
+
{ name: "osascript_get_frontmost_app", description: "Get the name of the frontmost application", args: [] },
|
|
340
|
+
// Windows
|
|
341
|
+
{ name: "osascript_get_windows", description: "Get window list for an application", args: ["app_name"] },
|
|
342
|
+
{ name: "osascript_set_window_bounds", description: "Set window position and size", args: ["app_name", "window_index", "x", "y", "width", "height"] },
|
|
343
|
+
// Keyboard
|
|
344
|
+
{ name: "osascript_keystroke", description: "Send keystroke with optional modifiers", args: ["key", "modifiers?"] },
|
|
345
|
+
{ name: "osascript_key_code", description: "Send key code (for special keys)", args: ["key_code", "modifiers?"] },
|
|
346
|
+
{ name: "osascript_type_text", description: "Type text (simulate keyboard input)", args: ["text"] },
|
|
347
|
+
// Safari
|
|
348
|
+
{ name: "osascript_safari_get_url", description: "Get current Safari URL", args: [] },
|
|
349
|
+
{ name: "osascript_safari_set_url", description: "Navigate Safari to URL", args: ["url"] },
|
|
350
|
+
{ name: "osascript_safari_get_tabs", description: "Get Safari tab titles", args: [] },
|
|
351
|
+
{ name: "osascript_safari_exec_js", description: "Execute JavaScript in Safari", args: ["js_code"] },
|
|
352
|
+
// Finder
|
|
353
|
+
{ name: "osascript_finder_get_selection", description: "Get currently selected files in Finder", args: [] },
|
|
354
|
+
{ name: "osascript_finder_open", description: "Open Finder window at path", args: ["path"] },
|
|
355
|
+
{ name: "osascript_finder_create_folder", description: "Create new folder in Finder", args: ["parent_path", "folder_name"] },
|
|
356
|
+
{ name: "osascript_finder_trash", description: "Move file/folder to trash", args: ["path"] },
|
|
357
|
+
// Display
|
|
358
|
+
{ name: "osascript_get_displays", description: "Get display information", args: [] },
|
|
359
|
+
{ name: "osascript_get_resolution", description: "Get main screen resolution", args: [] },
|
|
360
|
+
// Audio
|
|
361
|
+
{ name: "osascript_say", description: "Say text using system voice", args: ["text", "voice?", "rate?"] },
|
|
362
|
+
{ name: "osascript_beep", description: "Play system beep", args: ["count?"] },
|
|
363
|
+
// Date/Time
|
|
364
|
+
{ name: "osascript_get_datetime", description: "Get current system date/time", args: [] },
|
|
365
|
+
];
|
|
366
|
+
|
|
367
|
+
// ==============
|
|
368
|
+
// HTTP Server
|
|
369
|
+
// ==============
|
|
370
|
+
|
|
371
|
+
Bun.serve({
|
|
372
|
+
port: MCP_PORT,
|
|
373
|
+
fetch: async (req: Request) => {
|
|
374
|
+
const url = new URL(req.url);
|
|
375
|
+
|
|
376
|
+
// Health check
|
|
377
|
+
if (url.pathname === "/health") {
|
|
378
|
+
return Response.json({
|
|
379
|
+
status: "ok",
|
|
380
|
+
port: MCP_PORT,
|
|
381
|
+
service: "osascript-mcp",
|
|
382
|
+
platform: process.platform,
|
|
383
|
+
macos: isMacOS(),
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// MCP endpoint
|
|
388
|
+
if (url.pathname === "/mcp") {
|
|
389
|
+
if (req.method === "GET") {
|
|
390
|
+
// Return available tools
|
|
391
|
+
return Response.json({
|
|
392
|
+
name: "osascript-mcp",
|
|
393
|
+
version: "1.0.0",
|
|
394
|
+
description: "macOS automation via osascript (AppleScript/JXA)",
|
|
395
|
+
platform: process.platform,
|
|
396
|
+
macos: isMacOS(),
|
|
397
|
+
tools: TOOLS,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (req.method === "POST") {
|
|
402
|
+
// Verify macOS
|
|
403
|
+
if (!isMacOS()) {
|
|
404
|
+
return Response.json({ error: "osascript MCP only works on macOS" }, { status: 500 });
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
408
|
+
const body = (await req.json()) as any;
|
|
409
|
+
const { tool, args = {} } = body;
|
|
410
|
+
|
|
411
|
+
try {
|
|
412
|
+
let result: string;
|
|
413
|
+
|
|
414
|
+
switch (tool) {
|
|
415
|
+
// Core
|
|
416
|
+
case "osascript_execute":
|
|
417
|
+
result = await osascriptExecute(args?.script, args?.language || "applescript");
|
|
418
|
+
break;
|
|
419
|
+
|
|
420
|
+
// System
|
|
421
|
+
case "osascript_display_notification":
|
|
422
|
+
result = await osascriptNotification(args?.message, args?.title, args?.subtitle, args?.sound_name);
|
|
423
|
+
break;
|
|
424
|
+
case "osascript_display_dialog":
|
|
425
|
+
result = await osascriptDialog(
|
|
426
|
+
args?.message,
|
|
427
|
+
args?.title,
|
|
428
|
+
args?.buttons?.split(","),
|
|
429
|
+
args?.default_button,
|
|
430
|
+
args?.icon
|
|
431
|
+
);
|
|
432
|
+
break;
|
|
433
|
+
|
|
434
|
+
// Volume
|
|
435
|
+
case "osascript_get_volume":
|
|
436
|
+
result = await osascriptGetVolume();
|
|
437
|
+
break;
|
|
438
|
+
case "osascript_set_volume":
|
|
439
|
+
result = await osascriptSetVolume(args?.volume, args?.muted);
|
|
440
|
+
break;
|
|
441
|
+
|
|
442
|
+
// Clipboard
|
|
443
|
+
case "osascript_get_clipboard":
|
|
444
|
+
result = await osascriptGetClipboard();
|
|
445
|
+
break;
|
|
446
|
+
case "osascript_set_clipboard":
|
|
447
|
+
result = await osascriptSetClipboard(args?.text);
|
|
448
|
+
break;
|
|
449
|
+
case "osascript_clear_clipboard":
|
|
450
|
+
result = await osascriptClearClipboard();
|
|
451
|
+
break;
|
|
452
|
+
|
|
453
|
+
// Applications
|
|
454
|
+
case "osascript_list_apps":
|
|
455
|
+
result = await osascriptListApps();
|
|
456
|
+
break;
|
|
457
|
+
case "osascript_get_app_info":
|
|
458
|
+
result = await osascriptGetAppInfo(args?.app_name);
|
|
459
|
+
break;
|
|
460
|
+
case "osascript_activate_app":
|
|
461
|
+
result = await osascriptActivateApp(args?.app_name);
|
|
462
|
+
break;
|
|
463
|
+
case "osascript_quit_app":
|
|
464
|
+
result = await osascriptQuitApp(args?.app_name, args?.force);
|
|
465
|
+
break;
|
|
466
|
+
case "osascript_launch_app":
|
|
467
|
+
result = await osascriptLaunchApp(args?.app_name);
|
|
468
|
+
break;
|
|
469
|
+
case "osascript_get_frontmost_app":
|
|
470
|
+
result = await osascriptGetFrontmostApp();
|
|
471
|
+
break;
|
|
472
|
+
|
|
473
|
+
// Windows
|
|
474
|
+
case "osascript_get_windows":
|
|
475
|
+
result = await osascriptGetWindows(args?.app_name);
|
|
476
|
+
break;
|
|
477
|
+
case "osascript_set_window_bounds":
|
|
478
|
+
result = await osascriptSetWindowBounds(
|
|
479
|
+
args?.app_name,
|
|
480
|
+
args?.window_index,
|
|
481
|
+
args?.x,
|
|
482
|
+
args?.y,
|
|
483
|
+
args?.width,
|
|
484
|
+
args?.height
|
|
485
|
+
);
|
|
486
|
+
break;
|
|
487
|
+
|
|
488
|
+
// Keyboard
|
|
489
|
+
case "osascript_keystroke":
|
|
490
|
+
result = await osascriptKeystroke(args?.key, parseModifiers(args?.modifiers));
|
|
491
|
+
break;
|
|
492
|
+
case "osascript_key_code":
|
|
493
|
+
result = await osascriptKeyCode(args?.key_code, parseModifiers(args?.modifiers));
|
|
494
|
+
break;
|
|
495
|
+
case "osascript_type_text":
|
|
496
|
+
result = await osascriptTypeText(args?.text);
|
|
497
|
+
break;
|
|
498
|
+
|
|
499
|
+
// Safari
|
|
500
|
+
case "osascript_safari_get_url":
|
|
501
|
+
result = await osascriptSafariGetURL();
|
|
502
|
+
break;
|
|
503
|
+
case "osascript_safari_set_url":
|
|
504
|
+
result = await osascriptSafariSetURL(args?.url);
|
|
505
|
+
break;
|
|
506
|
+
case "osascript_safari_get_tabs":
|
|
507
|
+
result = await osascriptSafariGetTabs();
|
|
508
|
+
break;
|
|
509
|
+
case "osascript_safari_exec_js":
|
|
510
|
+
result = await osascriptSafariExecJS(args?.js_code);
|
|
511
|
+
break;
|
|
512
|
+
|
|
513
|
+
// Finder
|
|
514
|
+
case "osascript_finder_get_selection":
|
|
515
|
+
result = await osascriptFinderGetSelection();
|
|
516
|
+
break;
|
|
517
|
+
case "osascript_finder_open":
|
|
518
|
+
result = await osascriptFinderOpen(args?.path);
|
|
519
|
+
break;
|
|
520
|
+
case "osascript_finder_create_folder":
|
|
521
|
+
result = await osascriptFinderCreateFolder(args?.parent_path, args?.folder_name);
|
|
522
|
+
break;
|
|
523
|
+
case "osascript_finder_trash":
|
|
524
|
+
result = await osascriptFinderTrash(args?.path);
|
|
525
|
+
break;
|
|
526
|
+
|
|
527
|
+
// Display
|
|
528
|
+
case "osascript_get_displays":
|
|
529
|
+
result = await osascriptGetDisplays();
|
|
530
|
+
break;
|
|
531
|
+
case "osascript_get_resolution":
|
|
532
|
+
result = await osascriptGetResolution();
|
|
533
|
+
break;
|
|
534
|
+
|
|
535
|
+
// Audio
|
|
536
|
+
case "osascript_say":
|
|
537
|
+
result = await osascriptSay(args?.text, args?.voice, args?.rate);
|
|
538
|
+
break;
|
|
539
|
+
case "osascript_beep":
|
|
540
|
+
result = await osascriptBeep(args?.count || 1);
|
|
541
|
+
break;
|
|
542
|
+
|
|
543
|
+
// Date/Time
|
|
544
|
+
case "osascript_get_datetime":
|
|
545
|
+
result = await osascriptGetDateTime();
|
|
546
|
+
break;
|
|
547
|
+
|
|
548
|
+
default:
|
|
549
|
+
return Response.json({ error: `Unknown tool: ${tool}` }, { status: 400 });
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return Response.json({ result });
|
|
553
|
+
} catch (error) {
|
|
554
|
+
return Response.json({ error: String(error) }, { status: 500 });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return Response.json({ error: "Not found" }, { status: 404 });
|
|
560
|
+
},
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
console.log(`osascript MCP Server running on port ${MCP_PORT}`);
|
|
564
|
+
console.log(` Health: http://localhost:${MCP_PORT}/health`);
|
|
565
|
+
console.log(` MCP: http://localhost:${MCP_PORT}/mcp`);
|