@mrfentmen/applescript-mcp 0.0.0-stage → 1.0.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 +56 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -0
- package/dist/run.d.ts +31 -0
- package/dist/run.js +71 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +249 -0
- package/package.json +43 -4
- package/server.json +20 -0
package/README.md
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AppleScript MCP
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
AppleScript is a missing language in the MCP ecosystem. This server gives agents real macOS automation tools instead of a stub.
|
|
4
|
+
|
|
5
|
+
Keyless. No API key. No signup. Requires macOS and the built-in `osascript`, `osacompile`, `sdef`, `open`, and `pbcopy` tools.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx -y @mrfentmen/applescript-mcp
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or in an MCP client:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"mcpServers": {
|
|
18
|
+
"applescript-mcp": {
|
|
19
|
+
"command": "npx",
|
|
20
|
+
"args": ["-y", "@mrfentmen/applescript-mcp"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Tools
|
|
27
|
+
|
|
28
|
+
| Tool | What it does |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `run_script` | Run an AppleScript string. |
|
|
31
|
+
| `run_script_file` | Run a `.scpt` or `.applescript` file with argv values. |
|
|
32
|
+
| `check_syntax` | Compile without running. |
|
|
33
|
+
| `compile_script` | Compile a string to a `.scpt` file. |
|
|
34
|
+
| `run_handler` | Call one named handler from a script. |
|
|
35
|
+
| `list_running_apps` | List visible foreground apps. |
|
|
36
|
+
| `get_app_info` | Check running, frontmost and visible state. |
|
|
37
|
+
| `get_app_dictionary` | Read an app's AppleScript dictionary with `sdef`. |
|
|
38
|
+
| `tell_app` | Send a raw command inside a `tell application` block. |
|
|
39
|
+
| `display_dialog` | Show a dialog and return the chosen button. |
|
|
40
|
+
| `display_notification` | Show a macOS notification. |
|
|
41
|
+
| `get_clipboard` | Read plain text from the clipboard. |
|
|
42
|
+
| `set_clipboard` | Replace the clipboard with plain text. |
|
|
43
|
+
| `open_app` | Open an app by name. |
|
|
44
|
+
| `quit_app` | Ask an app to quit. |
|
|
45
|
+
| `system_info` | Return the macOS system info record. |
|
|
46
|
+
|
|
47
|
+
## Safety
|
|
48
|
+
|
|
49
|
+
These tools can run arbitrary AppleScript and control applications. Use them with trusted input only. `run_script`, `run_script_file`, `tell_app`, and `run_handler` are intentionally powerful.
|
|
50
|
+
|
|
51
|
+
## Run locally
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install
|
|
55
|
+
npm run build
|
|
56
|
+
node dist/index.js
|
|
57
|
+
```
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createServer } from "./server.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
const server = createServer();
|
|
6
|
+
const transport = new StdioServerTransport();
|
|
7
|
+
await server.connect(transport);
|
|
8
|
+
console.error(`${process.env.npm_package_name ?? "language-mcp"} running on stdio`);
|
|
9
|
+
}
|
|
10
|
+
main().catch((error) => {
|
|
11
|
+
console.error("Fatal error:", error);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
});
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare class ToolError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export interface RunResult {
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
code: number;
|
|
7
|
+
}
|
|
8
|
+
export interface RunOptions {
|
|
9
|
+
input?: string;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
cwd?: string;
|
|
12
|
+
}
|
|
13
|
+
/** Run a command without a shell and capture both streams. */
|
|
14
|
+
export declare function run(command: string, args: string[], options?: RunOptions): Promise<RunResult>;
|
|
15
|
+
export declare function runChecked(command: string, args: string[], options?: RunOptions): Promise<RunResult>;
|
|
16
|
+
export declare function requireCommand(command: string): Promise<void>;
|
|
17
|
+
export declare function text(value: string): {
|
|
18
|
+
content: {
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}[];
|
|
22
|
+
};
|
|
23
|
+
export declare function fail(value: string): {
|
|
24
|
+
content: {
|
|
25
|
+
type: "text";
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
isError: boolean;
|
|
29
|
+
};
|
|
30
|
+
export declare function errorMessage(error: unknown): string;
|
|
31
|
+
export declare function clean(value: string): string;
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
export class ToolError extends Error {
|
|
3
|
+
}
|
|
4
|
+
/** Run a command without a shell and capture both streams. */
|
|
5
|
+
export function run(command, args, options = {}) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const child = spawn(command, args, {
|
|
8
|
+
cwd: options.cwd,
|
|
9
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
10
|
+
});
|
|
11
|
+
let stdout = "";
|
|
12
|
+
let stderr = "";
|
|
13
|
+
let settled = false;
|
|
14
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
15
|
+
const timer = setTimeout(() => {
|
|
16
|
+
if (settled)
|
|
17
|
+
return;
|
|
18
|
+
settled = true;
|
|
19
|
+
child.kill("SIGKILL");
|
|
20
|
+
reject(new ToolError(`${command} timed out after ${timeoutMs}ms`));
|
|
21
|
+
}, timeoutMs);
|
|
22
|
+
child.on("error", (error) => {
|
|
23
|
+
if (settled)
|
|
24
|
+
return;
|
|
25
|
+
settled = true;
|
|
26
|
+
clearTimeout(timer);
|
|
27
|
+
reject(new ToolError(`${command} could not start: ${error.message}`));
|
|
28
|
+
});
|
|
29
|
+
child.stdout.on("data", (chunk) => {
|
|
30
|
+
stdout += chunk.toString();
|
|
31
|
+
});
|
|
32
|
+
child.stderr.on("data", (chunk) => {
|
|
33
|
+
stderr += chunk.toString();
|
|
34
|
+
});
|
|
35
|
+
child.stdin.on("error", () => { });
|
|
36
|
+
child.on("close", (code) => {
|
|
37
|
+
if (settled)
|
|
38
|
+
return;
|
|
39
|
+
settled = true;
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
resolve({ stdout, stderr, code: code ?? 0 });
|
|
42
|
+
});
|
|
43
|
+
if (options.input !== undefined)
|
|
44
|
+
child.stdin.write(options.input);
|
|
45
|
+
child.stdin.end();
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export async function runChecked(command, args, options = {}) {
|
|
49
|
+
const result = await run(command, args, options);
|
|
50
|
+
if (result.code !== 0) {
|
|
51
|
+
throw new ToolError((result.stderr || result.stdout || `${command} exited with code ${result.code}`).trim());
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
export async function requireCommand(command) {
|
|
56
|
+
const result = await run("which", [command], { timeoutMs: 5_000 });
|
|
57
|
+
if (result.code !== 0)
|
|
58
|
+
throw new ToolError(`${command} is not installed or not on PATH`);
|
|
59
|
+
}
|
|
60
|
+
export function text(value) {
|
|
61
|
+
return { content: [{ type: "text", text: value }] };
|
|
62
|
+
}
|
|
63
|
+
export function fail(value) {
|
|
64
|
+
return { content: [{ type: "text", text: value }], isError: true };
|
|
65
|
+
}
|
|
66
|
+
export function errorMessage(error) {
|
|
67
|
+
return error instanceof Error ? error.message : String(error);
|
|
68
|
+
}
|
|
69
|
+
export function clean(value) {
|
|
70
|
+
return value.replace(/\s+/g, " ").trim();
|
|
71
|
+
}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { access, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
4
|
+
import { homedir, tmpdir } from "node:os";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { errorMessage, fail, requireCommand, run, runChecked, text, ToolError, } from "./run.js";
|
|
7
|
+
const quote = (value) => `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
8
|
+
const appName = z
|
|
9
|
+
.string()
|
|
10
|
+
.min(1)
|
|
11
|
+
.max(120)
|
|
12
|
+
.regex(/^[A-Za-z0-9 ._-]+$/, "Use an app name, not a path");
|
|
13
|
+
const timeout = z.number().int().min(1_000).max(120_000).default(30_000);
|
|
14
|
+
async function osa(script, timeoutMs = 30_000) {
|
|
15
|
+
const result = await run("osascript", ["-e", script], { timeoutMs });
|
|
16
|
+
if (result.code !== 0) {
|
|
17
|
+
throw new ToolError(result.stderr.trim() || `osascript exited with code ${result.code}`);
|
|
18
|
+
}
|
|
19
|
+
return result.stdout.trim();
|
|
20
|
+
}
|
|
21
|
+
async function osaFile(path, args, timeoutMs = 30_000) {
|
|
22
|
+
const result = await run("osascript", [path, ...args], { timeoutMs });
|
|
23
|
+
if (result.code !== 0) {
|
|
24
|
+
throw new ToolError(result.stderr.trim() || `osascript exited with code ${result.code}`);
|
|
25
|
+
}
|
|
26
|
+
return result.stdout.trim();
|
|
27
|
+
}
|
|
28
|
+
async function appPath(name) {
|
|
29
|
+
const candidates = [
|
|
30
|
+
join("/Applications", `${name}.app`),
|
|
31
|
+
join("/System/Applications", `${name}.app`),
|
|
32
|
+
join(homedir(), "Applications", `${name}.app`),
|
|
33
|
+
];
|
|
34
|
+
for (const candidate of candidates) {
|
|
35
|
+
try {
|
|
36
|
+
await access(candidate);
|
|
37
|
+
return candidate;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Try the next standard app location.
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
throw new ToolError(`Could not find ${name}.app in /Applications, /System/Applications or ~/Applications`);
|
|
44
|
+
}
|
|
45
|
+
export function createServer() {
|
|
46
|
+
const server = new McpServer({ name: "applescript-mcp", version: "1.0.0" });
|
|
47
|
+
server.tool("run_script", "Run an AppleScript string with osascript. The script can control apps and the system; run only trusted code.", { script: z.string().min(1).max(200_000), timeoutMs: timeout }, async (a) => {
|
|
48
|
+
try {
|
|
49
|
+
return text(await osa(a.script, a.timeoutMs));
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
return fail(errorMessage(error));
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
server.tool("run_script_file", "Run an AppleScript .scpt or .applescript file with optional argv values.", {
|
|
56
|
+
path: z.string().min(1).max(2_000),
|
|
57
|
+
args: z.array(z.string().max(2_000)).max(50).default([]),
|
|
58
|
+
timeoutMs: timeout,
|
|
59
|
+
}, async (a) => {
|
|
60
|
+
try {
|
|
61
|
+
return text(await osaFile(resolve(a.path), a.args, a.timeoutMs));
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
return fail(errorMessage(error));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
server.tool("check_syntax", "Compile an AppleScript without running it. Returns OK or the compiler error.", { script: z.string().min(1).max(200_000) }, async (a) => {
|
|
68
|
+
const dir = await mkdtemp(join(tmpdir(), "applescript-mcp-"));
|
|
69
|
+
try {
|
|
70
|
+
await requireCommand("osacompile");
|
|
71
|
+
await runChecked("osacompile", ["-e", a.script, "-o", join(dir, "check.scpt")], {
|
|
72
|
+
timeoutMs: 30_000,
|
|
73
|
+
});
|
|
74
|
+
return text("AppleScript syntax OK");
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return fail(errorMessage(error));
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
await rm(dir, { recursive: true, force: true });
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
server.tool("compile_script", "Compile an AppleScript string to a .scpt file without running it.", {
|
|
84
|
+
script: z.string().min(1).max(200_000),
|
|
85
|
+
outputPath: z.string().min(1).max(2_000),
|
|
86
|
+
}, async (a) => {
|
|
87
|
+
try {
|
|
88
|
+
await requireCommand("osacompile");
|
|
89
|
+
const output = resolve(a.outputPath);
|
|
90
|
+
await runChecked("osacompile", ["-e", a.script, "-o", output], { timeoutMs: 30_000 });
|
|
91
|
+
return text(`Compiled AppleScript to ${output}`);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return fail(errorMessage(error));
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
server.tool("run_handler", "Run one named AppleScript handler from a script, passing string or number arguments.", {
|
|
98
|
+
script: z.string().min(1).max(200_000),
|
|
99
|
+
handler: z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "Use a valid AppleScript handler name"),
|
|
100
|
+
args: z.array(z.union([z.string(), z.number()])).max(20).default([]),
|
|
101
|
+
timeoutMs: timeout,
|
|
102
|
+
}, async (a) => {
|
|
103
|
+
const dir = await mkdtemp(join(tmpdir(), "applescript-mcp-"));
|
|
104
|
+
const file = join(dir, "handler.applescript");
|
|
105
|
+
const callArgs = a.args
|
|
106
|
+
.map((value) => (typeof value === "number" ? String(value) : quote(value)))
|
|
107
|
+
.join(", ");
|
|
108
|
+
const source = `${a.script}\n\non run argv\n return ${a.handler}(${callArgs})\nend run\n`;
|
|
109
|
+
try {
|
|
110
|
+
await writeFile(file, source, "utf8");
|
|
111
|
+
return text(await osaFile(file, [], a.timeoutMs));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
return fail(errorMessage(error));
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
await rm(dir, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
server.tool("list_running_apps", "List visible foreground applications currently running.", {}, async () => {
|
|
121
|
+
try {
|
|
122
|
+
const output = await osa('tell application "System Events" to get name of every process whose background only is false');
|
|
123
|
+
const apps = output
|
|
124
|
+
.split(",")
|
|
125
|
+
.map((name) => name.trim())
|
|
126
|
+
.filter(Boolean);
|
|
127
|
+
return text(apps.length ? apps.join("\n") : "No foreground apps reported");
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return fail(errorMessage(error));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
server.tool("get_app_info", "Check whether an app is running and whether it is frontmost and visible.", { app: appName }, async (a) => {
|
|
134
|
+
try {
|
|
135
|
+
return text(await osa(`tell application "System Events"\n` +
|
|
136
|
+
`if exists application process ${quote(a.app)} then\n` +
|
|
137
|
+
`tell application process ${quote(a.app)} to return "running, frontmost=" & (frontmost as text) & ", visible=" & (visible as text)\n` +
|
|
138
|
+
`else\nreturn "not running"\nend if\nend tell`));
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
return fail(errorMessage(error));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
server.tool("get_app_dictionary", "Read an installed app's AppleScript dictionary with sdef.", { app: appName }, async (a) => {
|
|
145
|
+
try {
|
|
146
|
+
await requireCommand("sdef");
|
|
147
|
+
const path = await appPath(a.app);
|
|
148
|
+
const result = await runChecked("sdef", [path], { timeoutMs: 30_000 });
|
|
149
|
+
const body = result.stdout.trim();
|
|
150
|
+
return text(body.slice(0, 30_000) || "The app returned an empty dictionary");
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
return fail(errorMessage(error));
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
server.tool("tell_app", "Send one raw AppleScript command to an app. The command is executed inside a tell block.", {
|
|
157
|
+
app: appName,
|
|
158
|
+
command: z.string().min(1).max(20_000),
|
|
159
|
+
timeoutMs: timeout,
|
|
160
|
+
}, async (a) => {
|
|
161
|
+
try {
|
|
162
|
+
return text(await osa(`tell application ${quote(a.app)} to ${a.command}`, a.timeoutMs));
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
return fail(errorMessage(error));
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
server.tool("display_dialog", "Show a macOS dialog and return the button the user chose.", {
|
|
169
|
+
message: z.string().min(1).max(10_000),
|
|
170
|
+
title: z.string().max(500).optional(),
|
|
171
|
+
buttons: z.array(z.string().min(1).max(80)).min(1).max(4).default(["OK"]),
|
|
172
|
+
defaultButton: z.string().max(80).optional(),
|
|
173
|
+
timeoutMs: timeout,
|
|
174
|
+
}, async (a) => {
|
|
175
|
+
try {
|
|
176
|
+
let script = `display dialog ${quote(a.message)}`;
|
|
177
|
+
if (a.title)
|
|
178
|
+
script += ` with title ${quote(a.title)}`;
|
|
179
|
+
script += ` buttons {${a.buttons.map(quote).join(", ")}}`;
|
|
180
|
+
if (a.defaultButton)
|
|
181
|
+
script += ` default button ${quote(a.defaultButton)}`;
|
|
182
|
+
return text(await osa(script, a.timeoutMs));
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
return fail(errorMessage(error));
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
server.tool("display_notification", "Show a macOS notification. This does not wait for the user.", {
|
|
189
|
+
message: z.string().min(1).max(2_000),
|
|
190
|
+
title: z.string().min(1).max(200).default("AppleScript MCP"),
|
|
191
|
+
subtitle: z.string().max(200).optional(),
|
|
192
|
+
timeoutMs: timeout,
|
|
193
|
+
}, async (a) => {
|
|
194
|
+
try {
|
|
195
|
+
let script = `display notification ${quote(a.message)} with title ${quote(a.title)}`;
|
|
196
|
+
if (a.subtitle)
|
|
197
|
+
script += ` subtitle ${quote(a.subtitle)}`;
|
|
198
|
+
await osa(script, a.timeoutMs);
|
|
199
|
+
return text("Notification sent");
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
return fail(errorMessage(error));
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
server.tool("get_clipboard", "Read plain text from the macOS clipboard.", {}, async () => {
|
|
206
|
+
try {
|
|
207
|
+
return text(await osa("the clipboard as text"));
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
return fail(errorMessage(error));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
server.tool("set_clipboard", "Replace the macOS clipboard with plain text.", { value: z.string().max(1_000_000) }, async (a) => {
|
|
214
|
+
try {
|
|
215
|
+
await runChecked("pbcopy", [], { input: a.value, timeoutMs: 10_000 });
|
|
216
|
+
return text(`Copied ${a.value.length} characters to the clipboard`);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
return fail(errorMessage(error));
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
server.tool("open_app", "Open an installed macOS app by name.", { app: appName }, async (a) => {
|
|
223
|
+
try {
|
|
224
|
+
await runChecked("open", ["-a", a.app], { timeoutMs: 15_000 });
|
|
225
|
+
return text(`Opened ${a.app}`);
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
return fail(errorMessage(error));
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
server.tool("quit_app", "Ask an app to quit. Unsaved work may prompt the app.", { app: appName }, async (a) => {
|
|
232
|
+
try {
|
|
233
|
+
await osa(`tell application ${quote(a.app)} to quit`);
|
|
234
|
+
return text(`Asked ${a.app} to quit`);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
return fail(errorMessage(error));
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
server.tool("system_info", "Return the macOS system info record (OS version, model, CPU, memory).", {}, async () => {
|
|
241
|
+
try {
|
|
242
|
+
return text(await osa("system info"));
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
return fail(errorMessage(error));
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
return server;
|
|
249
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrfentmen/applescript-mcp",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AppleScript MCP: run, compile, inspect and control macOS apps with 14 real tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"mcpName": "io.github.mrfentmen/applescript-mcp",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/mrfentmen/awesome-servers.git"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"applescript-mcp": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"server.json",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.json",
|
|
22
|
+
"start": "node dist/index.js",
|
|
23
|
+
"test": "npm run build && node --test tests/tools.test.mjs"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"mcp",
|
|
27
|
+
"model-context-protocol",
|
|
28
|
+
"applescript",
|
|
29
|
+
"macos",
|
|
30
|
+
"automation",
|
|
31
|
+
"osascript"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
36
|
+
"zod": "^3.23.8"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"typescript": "^5.6.0"
|
|
41
|
+
},
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=20"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.mrfentmen/applescript-mcp",
|
|
4
|
+
"description": "AppleScript MCP: run, compile, inspect and control macOS apps with 14 real tools.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/mrfentmen/awesome-servers/tree/main/mcp/servers/applescript-mcp",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "npm",
|
|
13
|
+
"identifier": "@mrfentmen/applescript-mcp",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|