@goforgeit/mcp-macos-automation 0.5.2

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/LICENSE ADDED
@@ -0,0 +1,50 @@
1
+ Forge
2
+ Copyright (c) 2025 Prometheus Group LLC. All Rights Reserved.
3
+
4
+ PROPRIETARY LICENSE
5
+
6
+ This software and associated documentation files (the "Software") are the
7
+ exclusive property of Prometheus Group LLC. The Software is protected by
8
+ copyright laws and international treaty provisions.
9
+
10
+ GRANT OF ACCESS
11
+
12
+ Access to this Software is granted solely for the purpose of evaluation,
13
+ testing, and providing feedback to Prometheus Group LLC. This access does
14
+ not constitute a transfer of ownership, license to use commercially, or
15
+ any other rights not expressly stated herein.
16
+
17
+ RESTRICTIONS
18
+
19
+ You may NOT:
20
+ - Use the Software for commercial purposes without explicit written permission
21
+ - Copy, modify, merge, publish, distribute, sublicense, or sell copies of the Software
22
+ - Reverse engineer, decompile, or disassemble the Software
23
+ - Remove or alter any proprietary notices, labels, or marks on the Software
24
+ - Claim ownership or authorship of the Software or any derivative works
25
+
26
+ FEEDBACK
27
+
28
+ Any feedback, suggestions, ideas, or improvements you provide regarding the
29
+ Software shall become the exclusive property of Prometheus Group LLC. You
30
+ hereby assign all rights, title, and interest in such feedback to Prometheus
31
+ Group LLC without any obligation of compensation or attribution.
32
+
33
+ NO WARRANTY
34
+
35
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38
+ PROMETHEUS GROUP LLC BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
39
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
40
+ OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
41
+
42
+ TERMINATION
43
+
44
+ This access may be terminated at any time by Prometheus Group LLC without
45
+ prior notice. Upon termination, you must destroy all copies of the Software
46
+ in your possession.
47
+
48
+ CONTACT
49
+
50
+ For licensing inquiries, please visit: https://goforgeit.com
@@ -0,0 +1,19 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+
3
+ /**
4
+ * @goforgeit/mcp-macos-automation
5
+ *
6
+ * Forge MCP server for macOS automation.
7
+ * Provides general AppleScript/JXA execution, plus dedicated tools
8
+ * for Microsoft PowerPoint and Microsoft Word via osascript.
9
+ */
10
+
11
+ interface MacOSAutomationMCPServer {
12
+ server: McpServer;
13
+ getRegisteredTools(): string[];
14
+ start(): Promise<void>;
15
+ }
16
+ declare function runAppleScript(script: string, timeout?: number): Promise<string>;
17
+ declare function createMacOSAutomationMCPServer(): MacOSAutomationMCPServer;
18
+
19
+ export { type MacOSAutomationMCPServer, createMacOSAutomationMCPServer, runAppleScript };
package/dist/index.js ADDED
@@ -0,0 +1,406 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { z } from "zod";
7
+ import { execFile } from "child_process";
8
+ import { promisify } from "util";
9
+ var execFileAsync = promisify(execFile);
10
+ var OSASCRIPT_TIMEOUT = 3e4;
11
+ var MAX_BUFFER = 1024 * 1024;
12
+ async function runAppleScript(script, timeout = OSASCRIPT_TIMEOUT) {
13
+ const { stdout } = await execFileAsync("osascript", ["-e", script], {
14
+ timeout,
15
+ maxBuffer: MAX_BUFFER
16
+ });
17
+ return stdout.trim();
18
+ }
19
+ async function runJxa(script, timeout = OSASCRIPT_TIMEOUT) {
20
+ const { stdout } = await execFileAsync("osascript", ["-l", "JavaScript", "-e", script], {
21
+ timeout,
22
+ maxBuffer: MAX_BUFFER
23
+ });
24
+ return stdout.trim();
25
+ }
26
+ function createMacOSAutomationMCPServer() {
27
+ const server = new McpServer({
28
+ name: "@goforgeit/mcp-macos-automation",
29
+ version: "0.1.0"
30
+ });
31
+ const registeredTools = [];
32
+ server.tool(
33
+ "run_applescript",
34
+ "Execute arbitrary AppleScript code via osascript. Returns the script output as text.",
35
+ {
36
+ script: z.string().describe("The AppleScript code to execute"),
37
+ timeout: z.number().optional().default(OSASCRIPT_TIMEOUT).describe("Execution timeout in milliseconds (default: 30000)")
38
+ },
39
+ async ({ script, timeout }) => {
40
+ try {
41
+ const result = await runAppleScript(script, timeout);
42
+ return { content: [{ type: "text", text: result }] };
43
+ } catch (error) {
44
+ return {
45
+ content: [
46
+ {
47
+ type: "text",
48
+ text: `AppleScript error: ${error instanceof Error ? error.message : String(error)}`
49
+ }
50
+ ],
51
+ isError: true
52
+ };
53
+ }
54
+ }
55
+ );
56
+ registeredTools.push("run_applescript");
57
+ server.tool(
58
+ "run_jxa",
59
+ "Execute JavaScript for Automation (JXA) code via osascript -l JavaScript. Returns the script output as text.",
60
+ {
61
+ script: z.string().describe("The JXA (JavaScript for Automation) code to execute"),
62
+ timeout: z.number().optional().default(OSASCRIPT_TIMEOUT).describe("Execution timeout in milliseconds (default: 30000)")
63
+ },
64
+ async ({ script, timeout }) => {
65
+ try {
66
+ const result = await runJxa(script, timeout);
67
+ return { content: [{ type: "text", text: result }] };
68
+ } catch (error) {
69
+ return {
70
+ content: [
71
+ {
72
+ type: "text",
73
+ text: `JXA error: ${error instanceof Error ? error.message : String(error)}`
74
+ }
75
+ ],
76
+ isError: true
77
+ };
78
+ }
79
+ }
80
+ );
81
+ registeredTools.push("run_jxa");
82
+ server.tool(
83
+ "list_presentations",
84
+ "List all currently open Microsoft PowerPoint presentations with their names and slide counts",
85
+ {},
86
+ async () => {
87
+ const script = `
88
+ tell application "Microsoft PowerPoint"
89
+ set output to "["
90
+ set isFirst to true
91
+ repeat with pres in presentations
92
+ if not isFirst then
93
+ set output to output & ","
94
+ end if
95
+ set presName to name of pres
96
+ set slideCount to count of slides of pres
97
+ set output to output & "{\\"name\\":\\"" & presName & "\\",\\"slides\\":" & slideCount & "}"
98
+ set isFirst to false
99
+ end repeat
100
+ return output & "]"
101
+ end tell
102
+ `;
103
+ try {
104
+ const result = await runAppleScript(script);
105
+ return { content: [{ type: "text", text: result }] };
106
+ } catch (error) {
107
+ return {
108
+ content: [
109
+ {
110
+ type: "text",
111
+ text: `Failed to list presentations: ${error instanceof Error ? error.message : String(error)}`
112
+ }
113
+ ],
114
+ isError: true
115
+ };
116
+ }
117
+ }
118
+ );
119
+ registeredTools.push("list_presentations");
120
+ server.tool(
121
+ "get_slide_content",
122
+ "Read text content and notes from a specific slide in the active PowerPoint presentation",
123
+ {
124
+ slide_number: z.number().describe("Slide number (1-indexed)")
125
+ },
126
+ async ({ slide_number }) => {
127
+ const script = `
128
+ tell application "Microsoft PowerPoint"
129
+ if (count of presentations) = 0 then
130
+ return "No presentation is open"
131
+ end if
132
+ set activePres to active presentation
133
+ set targetSlide to slide ${slide_number} of activePres
134
+ set slideText to ""
135
+ repeat with shp in shapes of targetSlide
136
+ if has text frame shp then
137
+ set slideText to slideText & (content of text range of text frame of shp) & return
138
+ end if
139
+ end repeat
140
+ return slideText
141
+ end tell
142
+ `;
143
+ try {
144
+ const result = await runAppleScript(script);
145
+ return { content: [{ type: "text", text: result }] };
146
+ } catch (error) {
147
+ return {
148
+ content: [
149
+ {
150
+ type: "text",
151
+ text: `Failed to get slide content: ${error instanceof Error ? error.message : String(error)}`
152
+ }
153
+ ],
154
+ isError: true
155
+ };
156
+ }
157
+ }
158
+ );
159
+ registeredTools.push("get_slide_content");
160
+ server.tool(
161
+ "create_presentation",
162
+ "Create a new PowerPoint presentation with a title slide",
163
+ {
164
+ title: z.string().optional().describe("Title for the first slide")
165
+ },
166
+ async ({ title }) => {
167
+ const titlePart = title ? `
168
+ set targetSlide to slide 1 of activePres
169
+ set content of text range of text frame of shape 1 of targetSlide to ${JSON.stringify(title)}` : "";
170
+ const script = `
171
+ tell application "Microsoft PowerPoint"
172
+ activate
173
+ set activePres to make new presentation
174
+ ${titlePart}
175
+ return "New presentation created successfully"
176
+ end tell
177
+ `;
178
+ try {
179
+ const result = await runAppleScript(script);
180
+ return { content: [{ type: "text", text: result }] };
181
+ } catch (error) {
182
+ return {
183
+ content: [
184
+ {
185
+ type: "text",
186
+ text: `Failed to create presentation: ${error instanceof Error ? error.message : String(error)}`
187
+ }
188
+ ],
189
+ isError: true
190
+ };
191
+ }
192
+ }
193
+ );
194
+ registeredTools.push("create_presentation");
195
+ server.tool(
196
+ "add_slide",
197
+ "Add a new slide with optional content to an open PowerPoint presentation",
198
+ {
199
+ title: z.string().optional().describe("Slide title text"),
200
+ body: z.string().optional().describe("Slide body text"),
201
+ layout: z.enum(["title", "title_content", "blank"]).optional().default("title_content").describe("Slide layout type")
202
+ },
203
+ async ({ title, body, layout }) => {
204
+ const layoutMap = {
205
+ title: "slide layout title slide",
206
+ title_content: "slide layout title only",
207
+ blank: "slide layout blank"
208
+ };
209
+ const pptLayout = layoutMap[layout];
210
+ const titlePart = title ? `set content of text range of text frame of shape 1 of newSlide to ${JSON.stringify(title)}` : "";
211
+ const bodyPart = body ? `set content of text range of text frame of shape 2 of newSlide to ${JSON.stringify(body)}` : "";
212
+ const script = `
213
+ tell application "Microsoft PowerPoint"
214
+ if (count of presentations) = 0 then
215
+ return "No presentation is open"
216
+ end if
217
+ set activePres to active presentation
218
+ set newSlide to make new slide at end of activePres with properties {slide layout:${pptLayout}}
219
+ ${titlePart}
220
+ ${bodyPart}
221
+ return "Slide added successfully"
222
+ end tell
223
+ `;
224
+ try {
225
+ const result = await runAppleScript(script);
226
+ return { content: [{ type: "text", text: result }] };
227
+ } catch (error) {
228
+ return {
229
+ content: [
230
+ {
231
+ type: "text",
232
+ text: `Failed to add slide: ${error instanceof Error ? error.message : String(error)}`
233
+ }
234
+ ],
235
+ isError: true
236
+ };
237
+ }
238
+ }
239
+ );
240
+ registeredTools.push("add_slide");
241
+ server.tool(
242
+ "list_documents",
243
+ "List all currently open Microsoft Word documents with their names",
244
+ {},
245
+ async () => {
246
+ const script = `
247
+ tell application "Microsoft Word"
248
+ set output to "["
249
+ set isFirst to true
250
+ repeat with doc in documents
251
+ if not isFirst then
252
+ set output to output & ","
253
+ end if
254
+ set docName to name of doc
255
+ set output to output & "{\\"name\\":\\"" & docName & "\\"}"
256
+ set isFirst to false
257
+ end repeat
258
+ return output & "]"
259
+ end tell
260
+ `;
261
+ try {
262
+ const result = await runAppleScript(script);
263
+ return { content: [{ type: "text", text: result }] };
264
+ } catch (error) {
265
+ return {
266
+ content: [
267
+ {
268
+ type: "text",
269
+ text: `Failed to list documents: ${error instanceof Error ? error.message : String(error)}`
270
+ }
271
+ ],
272
+ isError: true
273
+ };
274
+ }
275
+ }
276
+ );
277
+ registeredTools.push("list_documents");
278
+ server.tool(
279
+ "get_document_content",
280
+ "Read all text content from the active Microsoft Word document",
281
+ {},
282
+ async () => {
283
+ const script = `
284
+ tell application "Microsoft Word"
285
+ if (count of documents) = 0 then
286
+ return "No document is open"
287
+ end if
288
+ set activeDoc to active document
289
+ return content of text object of activeDoc as string
290
+ end tell
291
+ `;
292
+ try {
293
+ const result = await runAppleScript(script);
294
+ return { content: [{ type: "text", text: result }] };
295
+ } catch (error) {
296
+ return {
297
+ content: [
298
+ {
299
+ type: "text",
300
+ text: `Failed to get document content: ${error instanceof Error ? error.message : String(error)}`
301
+ }
302
+ ],
303
+ isError: true
304
+ };
305
+ }
306
+ }
307
+ );
308
+ registeredTools.push("get_document_content");
309
+ server.tool(
310
+ "create_document",
311
+ "Create a new Microsoft Word document with optional initial content",
312
+ {
313
+ content: z.string().optional().describe("Initial text content for the document")
314
+ },
315
+ async ({ content: docContent }) => {
316
+ const contentPart = docContent ? `
317
+ tell newDoc
318
+ set content of text object to ${JSON.stringify(docContent)}
319
+ end tell` : "";
320
+ const script = `
321
+ tell application "Microsoft Word"
322
+ activate
323
+ set newDoc to make new document
324
+ ${contentPart}
325
+ return "New document created successfully"
326
+ end tell
327
+ `;
328
+ try {
329
+ const result = await runAppleScript(script);
330
+ return { content: [{ type: "text", text: result }] };
331
+ } catch (error) {
332
+ return {
333
+ content: [
334
+ {
335
+ type: "text",
336
+ text: `Failed to create document: ${error instanceof Error ? error.message : String(error)}`
337
+ }
338
+ ],
339
+ isError: true
340
+ };
341
+ }
342
+ }
343
+ );
344
+ registeredTools.push("create_document");
345
+ server.tool(
346
+ "search_in_document",
347
+ "Find text in the active Microsoft Word document. Returns whether the text was found.",
348
+ {
349
+ query: z.string().describe("Text to search for in the document")
350
+ },
351
+ async ({ query }) => {
352
+ const script = `
353
+ tell application "Microsoft Word"
354
+ if (count of documents) = 0 then
355
+ return "No document is open"
356
+ end if
357
+ set activeDoc to active document
358
+ set docText to content of text object of activeDoc as string
359
+ if docText contains ${JSON.stringify(query)} then
360
+ return "Found: " & ${JSON.stringify(query)}
361
+ else
362
+ return "Not found: " & ${JSON.stringify(query)}
363
+ end if
364
+ end tell
365
+ `;
366
+ try {
367
+ const result = await runAppleScript(script);
368
+ return { content: [{ type: "text", text: result }] };
369
+ } catch (error) {
370
+ return {
371
+ content: [
372
+ {
373
+ type: "text",
374
+ text: `Failed to search document: ${error instanceof Error ? error.message : String(error)}`
375
+ }
376
+ ],
377
+ isError: true
378
+ };
379
+ }
380
+ }
381
+ );
382
+ registeredTools.push("search_in_document");
383
+ return {
384
+ server,
385
+ getRegisteredTools() {
386
+ return [...registeredTools];
387
+ },
388
+ async start() {
389
+ const transport = new StdioServerTransport();
390
+ await server.connect(transport);
391
+ }
392
+ };
393
+ }
394
+ var isMainModule = process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, "/"));
395
+ if (isMainModule || process.env.FORGE_MCP_START === "true") {
396
+ const mcpServer = createMacOSAutomationMCPServer();
397
+ mcpServer.start().catch((err) => {
398
+ console.error("Failed to start macOS Automation MCP server:", err);
399
+ process.exit(1);
400
+ });
401
+ }
402
+ export {
403
+ createMacOSAutomationMCPServer,
404
+ runAppleScript
405
+ };
406
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @goforgeit/mcp-macos-automation\n *\n * Forge MCP server for macOS automation.\n * Provides general AppleScript/JXA execution, plus dedicated tools\n * for Microsoft PowerPoint and Microsoft Word via osascript.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { execFile } from 'child_process';\nimport { promisify } from 'util';\n\nconst execFileAsync = promisify(execFile);\n\nconst OSASCRIPT_TIMEOUT = 30000;\nconst MAX_BUFFER = 1024 * 1024;\n\nexport interface MacOSAutomationMCPServer {\n server: McpServer;\n getRegisteredTools(): string[];\n start(): Promise<void>;\n}\n\nexport async function runAppleScript(script: string, timeout = OSASCRIPT_TIMEOUT): Promise<string> {\n const { stdout } = await execFileAsync('osascript', ['-e', script], {\n timeout,\n maxBuffer: MAX_BUFFER,\n });\n return stdout.trim();\n}\n\nasync function runJxa(script: string, timeout = OSASCRIPT_TIMEOUT): Promise<string> {\n const { stdout } = await execFileAsync('osascript', ['-l', 'JavaScript', '-e', script], {\n timeout,\n maxBuffer: MAX_BUFFER,\n });\n return stdout.trim();\n}\n\nexport function createMacOSAutomationMCPServer(): MacOSAutomationMCPServer {\n const server = new McpServer({\n name: '@goforgeit/mcp-macos-automation',\n version: '0.1.0',\n });\n\n const registeredTools: string[] = [];\n\n // ============================================================\n // General AppleScript Tools\n // ============================================================\n\n // --- run_applescript ---\n server.tool(\n 'run_applescript',\n 'Execute arbitrary AppleScript code via osascript. Returns the script output as text.',\n {\n script: z.string().describe('The AppleScript code to execute'),\n timeout: z\n .number()\n .optional()\n .default(OSASCRIPT_TIMEOUT)\n .describe('Execution timeout in milliseconds (default: 30000)'),\n },\n async ({ script, timeout }) => {\n try {\n const result = await runAppleScript(script, timeout);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `AppleScript error: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('run_applescript');\n\n // --- run_jxa ---\n server.tool(\n 'run_jxa',\n 'Execute JavaScript for Automation (JXA) code via osascript -l JavaScript. Returns the script output as text.',\n {\n script: z.string().describe('The JXA (JavaScript for Automation) code to execute'),\n timeout: z\n .number()\n .optional()\n .default(OSASCRIPT_TIMEOUT)\n .describe('Execution timeout in milliseconds (default: 30000)'),\n },\n async ({ script, timeout }) => {\n try {\n const result = await runJxa(script, timeout);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `JXA error: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('run_jxa');\n\n // ============================================================\n // PowerPoint Tools\n // ============================================================\n\n // --- list_presentations ---\n server.tool(\n 'list_presentations',\n 'List all currently open Microsoft PowerPoint presentations with their names and slide counts',\n {},\n async () => {\n const script = `\n tell application \"Microsoft PowerPoint\"\n set output to \"[\"\n set isFirst to true\n repeat with pres in presentations\n if not isFirst then\n set output to output & \",\"\n end if\n set presName to name of pres\n set slideCount to count of slides of pres\n set output to output & \"{\\\\\"name\\\\\":\\\\\"\" & presName & \"\\\\\",\\\\\"slides\\\\\":\" & slideCount & \"}\"\n set isFirst to false\n end repeat\n return output & \"]\"\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to list presentations: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('list_presentations');\n\n // --- get_slide_content ---\n server.tool(\n 'get_slide_content',\n 'Read text content and notes from a specific slide in the active PowerPoint presentation',\n {\n slide_number: z.number().describe('Slide number (1-indexed)'),\n },\n async ({ slide_number }) => {\n const script = `\n tell application \"Microsoft PowerPoint\"\n if (count of presentations) = 0 then\n return \"No presentation is open\"\n end if\n set activePres to active presentation\n set targetSlide to slide ${slide_number} of activePres\n set slideText to \"\"\n repeat with shp in shapes of targetSlide\n if has text frame shp then\n set slideText to slideText & (content of text range of text frame of shp) & return\n end if\n end repeat\n return slideText\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to get slide content: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('get_slide_content');\n\n // --- create_presentation ---\n server.tool(\n 'create_presentation',\n 'Create a new PowerPoint presentation with a title slide',\n {\n title: z.string().optional().describe('Title for the first slide'),\n },\n async ({ title }) => {\n const titlePart = title\n ? `\n set targetSlide to slide 1 of activePres\n set content of text range of text frame of shape 1 of targetSlide to ${JSON.stringify(title)}`\n : '';\n\n const script = `\n tell application \"Microsoft PowerPoint\"\n activate\n set activePres to make new presentation\n ${titlePart}\n return \"New presentation created successfully\"\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to create presentation: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('create_presentation');\n\n // --- add_slide ---\n server.tool(\n 'add_slide',\n 'Add a new slide with optional content to an open PowerPoint presentation',\n {\n title: z.string().optional().describe('Slide title text'),\n body: z.string().optional().describe('Slide body text'),\n layout: z\n .enum(['title', 'title_content', 'blank'])\n .optional()\n .default('title_content')\n .describe('Slide layout type'),\n },\n async ({ title, body, layout }) => {\n const layoutMap: Record<string, string> = {\n title: 'slide layout title slide',\n title_content: 'slide layout title only',\n blank: 'slide layout blank',\n };\n const pptLayout = layoutMap[layout];\n\n const titlePart = title\n ? `set content of text range of text frame of shape 1 of newSlide to ${JSON.stringify(title)}`\n : '';\n const bodyPart = body\n ? `set content of text range of text frame of shape 2 of newSlide to ${JSON.stringify(body)}`\n : '';\n\n const script = `\n tell application \"Microsoft PowerPoint\"\n if (count of presentations) = 0 then\n return \"No presentation is open\"\n end if\n set activePres to active presentation\n set newSlide to make new slide at end of activePres with properties {slide layout:${pptLayout}}\n ${titlePart}\n ${bodyPart}\n return \"Slide added successfully\"\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to add slide: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('add_slide');\n\n // ============================================================\n // Word Tools\n // ============================================================\n\n // --- list_documents ---\n server.tool(\n 'list_documents',\n 'List all currently open Microsoft Word documents with their names',\n {},\n async () => {\n const script = `\n tell application \"Microsoft Word\"\n set output to \"[\"\n set isFirst to true\n repeat with doc in documents\n if not isFirst then\n set output to output & \",\"\n end if\n set docName to name of doc\n set output to output & \"{\\\\\"name\\\\\":\\\\\"\" & docName & \"\\\\\"}\"\n set isFirst to false\n end repeat\n return output & \"]\"\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to list documents: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('list_documents');\n\n // --- get_document_content ---\n server.tool(\n 'get_document_content',\n 'Read all text content from the active Microsoft Word document',\n {},\n async () => {\n const script = `\n tell application \"Microsoft Word\"\n if (count of documents) = 0 then\n return \"No document is open\"\n end if\n set activeDoc to active document\n return content of text object of activeDoc as string\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to get document content: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('get_document_content');\n\n // --- create_document ---\n server.tool(\n 'create_document',\n 'Create a new Microsoft Word document with optional initial content',\n {\n content: z.string().optional().describe('Initial text content for the document'),\n },\n async ({ content: docContent }) => {\n const contentPart = docContent\n ? `\n tell newDoc\n set content of text object to ${JSON.stringify(docContent)}\n end tell`\n : '';\n\n const script = `\n tell application \"Microsoft Word\"\n activate\n set newDoc to make new document\n ${contentPart}\n return \"New document created successfully\"\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to create document: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('create_document');\n\n // --- search_in_document ---\n server.tool(\n 'search_in_document',\n 'Find text in the active Microsoft Word document. Returns whether the text was found.',\n {\n query: z.string().describe('Text to search for in the document'),\n },\n async ({ query }) => {\n const script = `\n tell application \"Microsoft Word\"\n if (count of documents) = 0 then\n return \"No document is open\"\n end if\n set activeDoc to active document\n set docText to content of text object of activeDoc as string\n if docText contains ${JSON.stringify(query)} then\n return \"Found: \" & ${JSON.stringify(query)}\n else\n return \"Not found: \" & ${JSON.stringify(query)}\n end if\n end tell\n `;\n try {\n const result = await runAppleScript(script);\n return { content: [{ type: 'text' as const, text: result }] };\n } catch (error) {\n return {\n content: [\n {\n type: 'text' as const,\n text: `Failed to search document: ${error instanceof Error ? error.message : String(error)}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n registeredTools.push('search_in_document');\n\n return {\n server,\n getRegisteredTools() {\n return [...registeredTools];\n },\n async start() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n },\n };\n}\n\n// CLI entry point\nconst isMainModule =\n process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\\\/g, '/'));\n\nif (isMainModule || process.env.FORGE_MCP_START === 'true') {\n const mcpServer = createMacOSAutomationMCPServer();\n mcpServer.start().catch((err) => {\n console.error('Failed to start macOS Automation MCP server:', err);\n process.exit(1);\n });\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@goforgeit/mcp-macos-automation",
3
+ "version": "0.5.2",
4
+ "type": "module",
5
+ "description": "Forge MCP server for macOS automation — AppleScript, JXA, Microsoft PowerPoint, and Microsoft Word via osascript",
6
+ "bin": {
7
+ "mcp-macos-automation": "./dist/index.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "dependencies": {
19
+ "@modelcontextprotocol/sdk": "^1.12.1",
20
+ "zod": "^4.2.1"
21
+ },
22
+ "devDependencies": {
23
+ "@types/node": "^22.10.2",
24
+ "tsup": "^8.0.0",
25
+ "vitest": "^3.2.4"
26
+ },
27
+ "os": [
28
+ "darwin"
29
+ ],
30
+ "engines": {
31
+ "node": ">=18.0.0"
32
+ },
33
+ "scripts": {
34
+ "build": "tsup",
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "vitest run"
37
+ }
38
+ }