scpl-updated-mcp-server 1.0.4 ā 1.0.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.
- package/SCPL_REFERENCE.md +504 -0
- package/index.js +277 -353
- package/package.json +6 -1
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
# ScPL Language Reference
|
|
2
|
+
|
|
3
|
+
ScPL (Shortcut Programming Language) lets you write Apple Shortcuts as text code instead of dragging blocks.
|
|
4
|
+
|
|
5
|
+
## Basic Syntax
|
|
6
|
+
|
|
7
|
+
```scpl
|
|
8
|
+
ActionName "argument1" "argument2"
|
|
9
|
+
ActionName argument=value anotherArg=value
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Variables
|
|
13
|
+
|
|
14
|
+
Three types with `type:name` syntax:
|
|
15
|
+
|
|
16
|
+
| Type | Syntax | Description |
|
|
17
|
+
|------|--------|-------------|
|
|
18
|
+
| Named | `v:MyVar` | Set with SetVariable |
|
|
19
|
+
| Magic | `mv:MyVar` | Auto-created with `->` arrow |
|
|
20
|
+
| Special | `s:Name` | Built-in system variables |
|
|
21
|
+
|
|
22
|
+
### Special Variables
|
|
23
|
+
- `s:ShortcutInput` - Input to the shortcut
|
|
24
|
+
- `s:Clipboard` - Current clipboard contents
|
|
25
|
+
- `s:CurrentDate` - Current date/time
|
|
26
|
+
- `s:AskWhenRun` - Prompt user at runtime
|
|
27
|
+
- `s:ActionInput` - Input to current action
|
|
28
|
+
|
|
29
|
+
### Setting Variables
|
|
30
|
+
|
|
31
|
+
```scpl
|
|
32
|
+
# Named variable (creates SetVariable action)
|
|
33
|
+
Text "Hello" -> v:MyText
|
|
34
|
+
# or
|
|
35
|
+
SetVariable v:MyText
|
|
36
|
+
|
|
37
|
+
# Magic variable (no action created, just reference)
|
|
38
|
+
Text "Hello" -> mv:MyMagic
|
|
39
|
+
|
|
40
|
+
# Pre-assignment style
|
|
41
|
+
mv:Result = Text "Hello"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Using Variables in Text
|
|
45
|
+
|
|
46
|
+
```scpl
|
|
47
|
+
Text "The value is \(v:MyVariable)"
|
|
48
|
+
Text "Clipboard: \(s:Clipboard)"
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Flow Control
|
|
52
|
+
|
|
53
|
+
### If/Otherwise/End If
|
|
54
|
+
|
|
55
|
+
```scpl
|
|
56
|
+
Text "test"
|
|
57
|
+
If Equals "test"
|
|
58
|
+
ShowResult "Match!"
|
|
59
|
+
Otherwise
|
|
60
|
+
ShowResult "No match"
|
|
61
|
+
End If
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Repeat
|
|
65
|
+
|
|
66
|
+
```scpl
|
|
67
|
+
Repeat 5
|
|
68
|
+
ShowResult "Loop iteration"
|
|
69
|
+
End Repeat
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Repeat with Each
|
|
73
|
+
|
|
74
|
+
```scpl
|
|
75
|
+
RepeatWithEach [item1, item2, item3]
|
|
76
|
+
ShowResult "Processing: \(mv:RepeatItem)"
|
|
77
|
+
End RepeatWithEach
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Choose from Menu
|
|
81
|
+
|
|
82
|
+
```scpl
|
|
83
|
+
ChooseFromMenu "Pick one" ["Option A", "Option B"]
|
|
84
|
+
Case "Option A"
|
|
85
|
+
ShowResult "You picked A"
|
|
86
|
+
Case "Option B"
|
|
87
|
+
ShowResult "You picked B"
|
|
88
|
+
End Menu
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Field Types
|
|
92
|
+
|
|
93
|
+
### Text Fields
|
|
94
|
+
```scpl
|
|
95
|
+
Text "Single line"
|
|
96
|
+
Text
|
|
97
|
+
| Multiline text
|
|
98
|
+
| Second line
|
|
99
|
+
| With variable: \(v:Name)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Numbers
|
|
103
|
+
```scpl
|
|
104
|
+
Number 42
|
|
105
|
+
Number 3.14
|
|
106
|
+
Number -100
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Booleans
|
|
110
|
+
```scpl
|
|
111
|
+
SetWifi true
|
|
112
|
+
SetBluetooth false
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Lists
|
|
116
|
+
```scpl
|
|
117
|
+
List ["item1", "item2", "item3"]
|
|
118
|
+
# or
|
|
119
|
+
List
|
|
120
|
+
| Item 1
|
|
121
|
+
| Item 2
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Dictionaries
|
|
125
|
+
```scpl
|
|
126
|
+
Dictionary {
|
|
127
|
+
key: "value"
|
|
128
|
+
number: 42
|
|
129
|
+
nested: {inner: "data"}
|
|
130
|
+
}
|
|
131
|
+
# Quotes and commas optional
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Variable Aggrandizements
|
|
135
|
+
|
|
136
|
+
Access properties of variables:
|
|
137
|
+
|
|
138
|
+
```scpl
|
|
139
|
+
# Get dictionary key
|
|
140
|
+
v:MyDict{as:Dictionary,key:myKey}
|
|
141
|
+
# Shorthand
|
|
142
|
+
v:MyDict:myKey
|
|
143
|
+
|
|
144
|
+
# Get property
|
|
145
|
+
v:Contact{as:Contact,get:Email}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Actions Inside Actions
|
|
149
|
+
|
|
150
|
+
Inline actions in parentheses:
|
|
151
|
+
|
|
152
|
+
```scpl
|
|
153
|
+
Text "Number is \(Number 42)"
|
|
154
|
+
If Equals (Text "compare value")
|
|
155
|
+
ShowResult "Match"
|
|
156
|
+
End If
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Comments
|
|
160
|
+
|
|
161
|
+
```scpl
|
|
162
|
+
# This is a comment
|
|
163
|
+
// Also a comment
|
|
164
|
+
-- Also works
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
# Common Actions
|
|
170
|
+
|
|
171
|
+
## Text & Display
|
|
172
|
+
|
|
173
|
+
```scpl
|
|
174
|
+
Text "Your text here"
|
|
175
|
+
ShowResult "Display this"
|
|
176
|
+
ShowAlert title="Title" message="Body text"
|
|
177
|
+
ShowNotification title="Hey" body="Message"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Clipboard
|
|
181
|
+
|
|
182
|
+
```scpl
|
|
183
|
+
GetClipboard
|
|
184
|
+
SetClipboard "New content"
|
|
185
|
+
# or with variable
|
|
186
|
+
Text "Copy this" -> mv:Content
|
|
187
|
+
SetClipboard mv:Content
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Variables
|
|
191
|
+
|
|
192
|
+
```scpl
|
|
193
|
+
SetVariable v:Name
|
|
194
|
+
GetVariable v:Name
|
|
195
|
+
AddToVariable v:List
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Lists & Dictionaries
|
|
199
|
+
|
|
200
|
+
```scpl
|
|
201
|
+
List ["a", "b", "c"]
|
|
202
|
+
GetItemFromList "First Item"
|
|
203
|
+
GetItemFromList "Last Item"
|
|
204
|
+
GetItemFromList "Random Item"
|
|
205
|
+
Count "Items"
|
|
206
|
+
|
|
207
|
+
Dictionary {key: "value"}
|
|
208
|
+
GetDictionaryValue key="mykey"
|
|
209
|
+
SetDictionaryValue key="mykey" value="newvalue"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Math
|
|
213
|
+
|
|
214
|
+
```scpl
|
|
215
|
+
Number 10
|
|
216
|
+
Calculate "+" 5
|
|
217
|
+
Calculate "*" 2
|
|
218
|
+
Calculate "/" 4
|
|
219
|
+
RoundNumber "Normal"
|
|
220
|
+
RandomNumber min=1 max=100
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Dates
|
|
224
|
+
|
|
225
|
+
```scpl
|
|
226
|
+
Date "tomorrow at 9am"
|
|
227
|
+
FormatDate "short"
|
|
228
|
+
AdjustDate "1 week"
|
|
229
|
+
GetTimeBetweenDates unit="Days"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Files
|
|
233
|
+
|
|
234
|
+
```scpl
|
|
235
|
+
GetFile service="iCloud Drive" filepath="/path/to/file"
|
|
236
|
+
SaveFile destinationpath="/save/here"
|
|
237
|
+
GetFolderContents
|
|
238
|
+
DeleteFiles immediately=true
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Web
|
|
242
|
+
|
|
243
|
+
```scpl
|
|
244
|
+
URL "https://example.com"
|
|
245
|
+
GetContentsOfURL
|
|
246
|
+
ExpandURL
|
|
247
|
+
GetComponentOfURL component="Host"
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
# macOS Tahoe Actions (New)
|
|
253
|
+
|
|
254
|
+
## Apple Intelligence (Apple Silicon only)
|
|
255
|
+
|
|
256
|
+
```scpl
|
|
257
|
+
# Ask AI model
|
|
258
|
+
AskLLM model="Apple Intelligence" prompt="Summarize this text"
|
|
259
|
+
AskLLM model="ChatGPT" prompt="Explain quantum computing"
|
|
260
|
+
|
|
261
|
+
# Image generation
|
|
262
|
+
GenerateImage prompt="A sunset over mountains" style="Illustration"
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## AI Assistants
|
|
266
|
+
|
|
267
|
+
```scpl
|
|
268
|
+
# ChatGPT (requires app)
|
|
269
|
+
AskChatGPT prompt="Help me write an email"
|
|
270
|
+
OpenChatGPTVoiceMode
|
|
271
|
+
|
|
272
|
+
# Claude (requires app)
|
|
273
|
+
AskClaude message="Analyze this code"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Shell Scripts
|
|
277
|
+
|
|
278
|
+
```scpl
|
|
279
|
+
# Bash script
|
|
280
|
+
RunShellScript shell="/bin/bash" script="echo Hello World"
|
|
281
|
+
|
|
282
|
+
# Zsh with input
|
|
283
|
+
GetClipboard
|
|
284
|
+
RunShellScript shell="/bin/zsh" script="cat | wc -w"
|
|
285
|
+
|
|
286
|
+
# AppleScript
|
|
287
|
+
RunAppleScript script="display dialog \"Hello!\""
|
|
288
|
+
|
|
289
|
+
# JavaScript for Automation
|
|
290
|
+
RunJavaScriptForAutomation script="Application('Finder').selection()"
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## File Operations
|
|
294
|
+
|
|
295
|
+
```scpl
|
|
296
|
+
# Get file by path
|
|
297
|
+
File path="~/Documents/myfile.txt"
|
|
298
|
+
|
|
299
|
+
# Rename
|
|
300
|
+
RenameFile name="newname.txt"
|
|
301
|
+
|
|
302
|
+
# Move
|
|
303
|
+
MoveFile destination="~/Desktop/"
|
|
304
|
+
|
|
305
|
+
# Reveal in Finder
|
|
306
|
+
RevealInFinder
|
|
307
|
+
|
|
308
|
+
# Select file (prompt)
|
|
309
|
+
SelectFile
|
|
310
|
+
|
|
311
|
+
# Get folder contents
|
|
312
|
+
GetFolderContents path="~/Documents"
|
|
313
|
+
|
|
314
|
+
# Get Finder selection
|
|
315
|
+
GetSelectedFiles
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## System
|
|
319
|
+
|
|
320
|
+
```scpl
|
|
321
|
+
# Dark/Light mode
|
|
322
|
+
SetAppearance "Dark"
|
|
323
|
+
SetAppearance "Light"
|
|
324
|
+
|
|
325
|
+
# Screenshots
|
|
326
|
+
TakeScreenshot
|
|
327
|
+
|
|
328
|
+
# Lock screen
|
|
329
|
+
LockScreen
|
|
330
|
+
|
|
331
|
+
# On-screen OCR
|
|
332
|
+
GetOnScreenContent
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Clock & Timers
|
|
336
|
+
|
|
337
|
+
```scpl
|
|
338
|
+
StartStopwatch
|
|
339
|
+
StopStopwatch
|
|
340
|
+
CreateAlarm time="7:00 AM" label="Wake up"
|
|
341
|
+
StartTimer minutes=25
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## Voice Memos
|
|
345
|
+
|
|
346
|
+
```scpl
|
|
347
|
+
CreateRecording
|
|
348
|
+
PlayRecording
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
# Complete Workflow Examples
|
|
354
|
+
|
|
355
|
+
## Morning Routine
|
|
356
|
+
|
|
357
|
+
```scpl
|
|
358
|
+
# Turn off dark mode
|
|
359
|
+
SetAppearance "Light"
|
|
360
|
+
|
|
361
|
+
# Check weather
|
|
362
|
+
GetCurrentWeather -> mv:Weather
|
|
363
|
+
Text "Good morning! Today's weather: \(mv:Weather)"
|
|
364
|
+
ShowNotification title="Morning" body=mv:Text
|
|
365
|
+
|
|
366
|
+
# Open calendar
|
|
367
|
+
OpenApp "Calendar"
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Clipboard AI Enhancement
|
|
371
|
+
|
|
372
|
+
```scpl
|
|
373
|
+
GetClipboard -> mv:Original
|
|
374
|
+
AskLLM model="Apple Intelligence" prompt="Improve this text for clarity: \(mv:Original)"
|
|
375
|
+
SetClipboard
|
|
376
|
+
ShowAlert title="Done" message="Improved text copied!"
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Pomodoro Timer with Logging
|
|
380
|
+
|
|
381
|
+
```scpl
|
|
382
|
+
# Start work session
|
|
383
|
+
ShowNotification title="Pomodoro" body="25 minute focus session started"
|
|
384
|
+
StartTimer minutes=25
|
|
385
|
+
|
|
386
|
+
# Log session
|
|
387
|
+
Date -> mv:StartTime
|
|
388
|
+
Text "Work session started at \(mv:StartTime)" -> mv:Log
|
|
389
|
+
AppendToFile path="~/Documents/pomodoro-log.txt" text=mv:Log
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Screenshot with AI Description
|
|
393
|
+
|
|
394
|
+
```scpl
|
|
395
|
+
TakeScreenshot -> mv:Screenshot
|
|
396
|
+
AskLLM model="Apple Intelligence" prompt="Describe what's in this image"
|
|
397
|
+
SetVariable v:Description
|
|
398
|
+
|
|
399
|
+
# Save with description
|
|
400
|
+
Text "Screenshot: \(v:Description)"
|
|
401
|
+
ShowResult
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
## DevOps Status Check
|
|
405
|
+
|
|
406
|
+
```scpl
|
|
407
|
+
RunShellScript shell="/bin/zsh" script="git status --short" -> mv:GitStatus
|
|
408
|
+
RunShellScript shell="/bin/zsh" script="docker ps --format 'table {{.Names}}\t{{.Status}}'" -> mv:Docker
|
|
409
|
+
|
|
410
|
+
Text
|
|
411
|
+
| Git Status:
|
|
412
|
+
| \(mv:GitStatus)
|
|
413
|
+
|
|
|
414
|
+
| Docker Containers:
|
|
415
|
+
| \(mv:Docker)
|
|
416
|
+
|
|
417
|
+
ShowResult
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
## File Organizer
|
|
421
|
+
|
|
422
|
+
```scpl
|
|
423
|
+
GetSelectedFiles -> mv:Files
|
|
424
|
+
RepeatWithEach mv:Files
|
|
425
|
+
GetDetailsOfFiles detail="File Extension" -> mv:Ext
|
|
426
|
+
|
|
427
|
+
If Equals "pdf"
|
|
428
|
+
MoveFile destination="~/Documents/PDFs/"
|
|
429
|
+
Otherwise
|
|
430
|
+
If Equals "jpg"
|
|
431
|
+
MoveFile destination="~/Pictures/"
|
|
432
|
+
End If
|
|
433
|
+
End If
|
|
434
|
+
End RepeatWithEach
|
|
435
|
+
|
|
436
|
+
ShowNotification title="Done" body="Files organized!"
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
## Quick Note from Voice
|
|
440
|
+
|
|
441
|
+
```scpl
|
|
442
|
+
DictateText -> mv:Spoken
|
|
443
|
+
AskLLM model="Apple Intelligence" prompt="Clean up and format: \(mv:Spoken)"
|
|
444
|
+
SetVariable v:CleanedNote
|
|
445
|
+
|
|
446
|
+
CreateNote title="Voice Note" body=v:CleanedNote
|
|
447
|
+
ShowNotification title="Saved" body="Voice note created"
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
# Tips & Common Patterns
|
|
453
|
+
|
|
454
|
+
## Error Handling Pattern
|
|
455
|
+
```scpl
|
|
456
|
+
GetFile path="~/file.txt" errorIfNotFound=false -> mv:File
|
|
457
|
+
Count
|
|
458
|
+
If Equals 0
|
|
459
|
+
ShowAlert title="Error" message="File not found"
|
|
460
|
+
ExitShortcut
|
|
461
|
+
End If
|
|
462
|
+
# Continue with file...
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## User Input Validation
|
|
466
|
+
```scpl
|
|
467
|
+
AskForInput prompt="Enter a number" -> mv:Input
|
|
468
|
+
If "is not" "Number"
|
|
469
|
+
ShowAlert title="Error" message="Please enter a valid number"
|
|
470
|
+
ExitShortcut
|
|
471
|
+
End If
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
## Chaining API Calls
|
|
475
|
+
```scpl
|
|
476
|
+
URL "https://api.example.com/data"
|
|
477
|
+
GetContentsOfURL -> mv:Response
|
|
478
|
+
GetDictionaryValue key="items"
|
|
479
|
+
RepeatWithEach
|
|
480
|
+
# Process each item
|
|
481
|
+
End RepeatWithEach
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
---
|
|
485
|
+
|
|
486
|
+
# Quick Reference
|
|
487
|
+
|
|
488
|
+
| Task | ScPL Code |
|
|
489
|
+
|------|-----------|
|
|
490
|
+
| Show text | `ShowResult "Hello"` |
|
|
491
|
+
| Get clipboard | `GetClipboard` |
|
|
492
|
+
| Set clipboard | `SetClipboard "text"` |
|
|
493
|
+
| User input | `AskForInput prompt="Question"` |
|
|
494
|
+
| Run shell | `RunShellScript shell="/bin/zsh" script="cmd"` |
|
|
495
|
+
| AI prompt | `AskLLM model="Apple Intelligence" prompt="..."` |
|
|
496
|
+
| Variable | `Text "x" -> v:Name` then `\(v:Name)` |
|
|
497
|
+
| Magic var | `Text "x" -> mv:Name` then `mv:Name` |
|
|
498
|
+
| Condition | `If Equals "value"` ... `End If` |
|
|
499
|
+
| Loop | `Repeat 5` ... `End Repeat` |
|
|
500
|
+
| Menu | `ChooseFromMenu "title" [options]` ... `End Menu` |
|
|
501
|
+
|
|
502
|
+
---
|
|
503
|
+
|
|
504
|
+
**493 total actions available.** Use `list_actions` tool to search by category or keyword.
|
package/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
11
11
|
import pkg from "scpl-macos-updated";
|
|
12
12
|
const { convert } = pkg;
|
|
13
|
-
import { writeFileSync, readFileSync, existsSync, mkdirSync } from "fs";
|
|
13
|
+
import { writeFileSync, readFileSync, existsSync, mkdirSync, appendFileSync } from "fs";
|
|
14
14
|
import { join, dirname } from "path";
|
|
15
15
|
import { homedir } from "os";
|
|
16
16
|
import { fileURLToPath } from "url";
|
|
@@ -29,20 +29,31 @@ USAGE:
|
|
|
29
29
|
npx scpl-updated-mcp-server [OPTIONS]
|
|
30
30
|
|
|
31
31
|
OPTIONS:
|
|
32
|
-
--setup Auto-install for Claude Code
|
|
32
|
+
--setup Auto-install for Claude Code (CLI)
|
|
33
|
+
--setup-desktop Auto-install for Claude Desktop (GUI app)
|
|
33
34
|
--setup-codex Auto-install for OpenAI Codex CLI (~/.codex)
|
|
34
35
|
--setup-codex=<dir> Auto-install for Codex forks with custom directory
|
|
35
36
|
--help, -h Show this help message
|
|
36
37
|
|
|
38
|
+
You can combine multiple flags to set up multiple tools at once:
|
|
39
|
+
npx scpl-updated-mcp-server --setup --setup-desktop --setup-codex
|
|
40
|
+
|
|
37
41
|
EXAMPLES:
|
|
42
|
+
# Claude Code (CLI) only
|
|
38
43
|
npx scpl-updated-mcp-server --setup
|
|
44
|
+
|
|
45
|
+
# Claude Desktop (GUI app) only
|
|
46
|
+
npx scpl-updated-mcp-server --setup-desktop
|
|
47
|
+
|
|
48
|
+
# Codex only
|
|
39
49
|
npx scpl-updated-mcp-server --setup-codex
|
|
40
|
-
npx scpl-updated-mcp-server --setup-codex=~/.code
|
|
41
|
-
npx scpl-updated-mcp-server --setup-codex=/path/to/code_config
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
# Custom Codex directory (just-every/code, etc.)
|
|
44
52
|
npx scpl-updated-mcp-server --setup-codex=$CODE_HOME
|
|
45
53
|
|
|
54
|
+
# All Claude + Codex tools at once
|
|
55
|
+
npx scpl-updated-mcp-server --setup --setup-desktop --setup-codex
|
|
56
|
+
|
|
46
57
|
After setup, restart your AI coding tool and ask:
|
|
47
58
|
"Create a shortcut that starts a timer and plays a sound"
|
|
48
59
|
`);
|
|
@@ -50,303 +61,324 @@ After setup, restart your AI coding tool and ask:
|
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
// ============================================================================
|
|
53
|
-
//
|
|
64
|
+
// SETUP FUNCTIONS
|
|
54
65
|
// ============================================================================
|
|
55
|
-
const codexArg = process.argv.find(arg => arg.startsWith("--setup-codex"));
|
|
56
|
-
if (codexArg) {
|
|
57
|
-
// Check for custom directory: --setup-codex=/path/to/dir or --setup-codex=~/custom
|
|
58
|
-
let codexDir = join(homedir(), ".codex"); // default
|
|
59
66
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
customDir = customDir.replace("~", homedir());
|
|
65
|
-
}
|
|
66
|
-
codexDir = customDir;
|
|
67
|
-
}
|
|
67
|
+
function setupClaudeCode() {
|
|
68
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
69
|
+
console.log("š Setting up ScPL Shortcuts for Claude Code...");
|
|
70
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
const skillDir = join(codexDir, "skills", "scpl-shortcuts");
|
|
72
|
+
const claudeJsonPath = join(homedir(), ".claude.json");
|
|
73
|
+
const pluginsDir = join(homedir(), ".claude", "plugins", "local", "scpl-shortcuts");
|
|
74
|
+
const installedPluginsPath = join(homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
73
75
|
|
|
74
|
-
// Step 1: Add MCP server to
|
|
75
|
-
console.log(
|
|
76
|
+
// Step 1: Add MCP server to ~/.claude.json
|
|
77
|
+
console.log("š Step 1: Adding MCP server to ~/.claude.json...");
|
|
76
78
|
try {
|
|
77
|
-
let
|
|
78
|
-
if (existsSync(
|
|
79
|
-
|
|
79
|
+
let claudeConfig = {};
|
|
80
|
+
if (existsSync(claudeJsonPath)) {
|
|
81
|
+
claudeConfig = JSON.parse(readFileSync(claudeJsonPath, "utf-8"));
|
|
80
82
|
}
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
if (!claudeConfig.mcpServers) {
|
|
85
|
+
claudeConfig.mcpServers = {};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (claudeConfig.mcpServers["scpl-shortcuts"]) {
|
|
84
89
|
console.log(" āļø Already configured, skipping...\n");
|
|
85
90
|
} else {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
command
|
|
89
|
-
args
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
writeFileSync(codexConfigPath, config + tomlBlock);
|
|
91
|
+
claudeConfig.mcpServers["scpl-shortcuts"] = {
|
|
92
|
+
type: "stdio",
|
|
93
|
+
command: "npx",
|
|
94
|
+
args: ["-y", "scpl-updated-mcp-server"]
|
|
95
|
+
};
|
|
96
|
+
writeFileSync(claudeJsonPath, JSON.stringify(claudeConfig, null, 2));
|
|
93
97
|
console.log(" ā
MCP server added!\n");
|
|
94
98
|
}
|
|
95
99
|
} catch (error) {
|
|
96
|
-
console.error(" ā Failed
|
|
97
|
-
console.log(" Add this to ~/.codex/config.toml manually:");
|
|
98
|
-
console.log(`
|
|
99
|
-
[mcp_servers.scpl-shortcuts]
|
|
100
|
-
command = "npx"
|
|
101
|
-
args = ["-y", "scpl-updated-mcp-server"]
|
|
102
|
-
startup_timeout_sec = 60.0
|
|
103
|
-
`);
|
|
100
|
+
console.error(" ā Failed:", error.message, "\n");
|
|
104
101
|
}
|
|
105
102
|
|
|
106
|
-
// Step 2:
|
|
107
|
-
console.log(
|
|
103
|
+
// Step 2: Create plugin directory and files
|
|
104
|
+
console.log("š Step 2: Installing plugin files...");
|
|
108
105
|
try {
|
|
109
|
-
mkdirSync(
|
|
106
|
+
mkdirSync(join(pluginsDir, "skills"), { recursive: true });
|
|
110
107
|
|
|
111
|
-
const
|
|
112
|
-
name: scpl-shortcuts
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
108
|
+
const pluginJson = {
|
|
109
|
+
name: "scpl-shortcuts",
|
|
110
|
+
version: "1.0.0",
|
|
111
|
+
description: "Create macOS Shortcuts using natural language and ScPL",
|
|
112
|
+
skills: [
|
|
113
|
+
{
|
|
114
|
+
name: "create-shortcut",
|
|
115
|
+
description: "Create a macOS Shortcut using natural language.",
|
|
116
|
+
path: "skills/create-shortcut.md"
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
};
|
|
120
|
+
writeFileSync(join(pluginsDir, "plugin.json"), JSON.stringify(pluginJson, null, 2));
|
|
121
|
+
|
|
122
|
+
// Read comprehensive ScPL reference
|
|
123
|
+
const refPath = join(__dirname, "SCPL_REFERENCE.md");
|
|
124
|
+
let skillContent = "";
|
|
125
|
+
if (existsSync(refPath)) {
|
|
126
|
+
const reference = readFileSync(refPath, "utf-8");
|
|
127
|
+
skillContent = `---
|
|
128
|
+
description: Create macOS Shortcuts using natural language with ScPL.
|
|
129
|
+
tags: [shortcuts, automation, macos, scpl, apple-intelligence]
|
|
116
130
|
---
|
|
117
131
|
|
|
118
132
|
# ScPL Shortcuts Skill
|
|
119
133
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
## MCP Tools Available
|
|
123
|
-
|
|
124
|
-
You have access to the \`scpl-shortcuts\` MCP server with these tools:
|
|
125
|
-
|
|
126
|
-
| Tool | Description |
|
|
127
|
-
|------|-------------|
|
|
128
|
-
| \`create_shortcut\` | Generate a .shortcut file from ScPL code |
|
|
129
|
-
| \`validate_scpl\` | Check if ScPL code is valid before creating |
|
|
130
|
-
| \`list_actions\` | Search available actions by category or keyword |
|
|
134
|
+
You have access to the ScPL MCP server with **493 actions**.
|
|
131
135
|
|
|
132
|
-
##
|
|
136
|
+
## Available Tools
|
|
137
|
+
- \`create_shortcut\` - Convert ScPL code to .shortcut file
|
|
138
|
+
- \`validate_scpl\` - Check syntax without creating file
|
|
139
|
+
- \`list_actions\` - Search available actions by category/keyword
|
|
133
140
|
|
|
134
|
-
|
|
141
|
+
${reference}
|
|
142
|
+
`;
|
|
143
|
+
} else {
|
|
144
|
+
// Fallback minimal skill if reference not found
|
|
145
|
+
skillContent = `---
|
|
146
|
+
description: Create macOS Shortcuts using natural language.
|
|
147
|
+
tags: [shortcuts, automation, macos, scpl]
|
|
148
|
+
---
|
|
135
149
|
|
|
136
|
-
|
|
137
|
-
# Comments start with #
|
|
138
|
-
Text "Hello World"
|
|
139
|
-
ShowResult
|
|
150
|
+
# Create Shortcut Skill
|
|
140
151
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
SetVariable v:myVar
|
|
144
|
-
ShowResult v:myVar
|
|
152
|
+
You have access to the ScPL MCP server with 493 actions.
|
|
153
|
+
Tools: create_shortcut, validate_scpl, list_actions
|
|
145
154
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
Basic syntax: \`ActionName "arg"\` or \`ActionName param=value\`
|
|
156
|
+
Variables: \`v:Named\`, \`mv:Magic\`, \`s:Special\`
|
|
157
|
+
Flow: \`If\`/\`End If\`, \`Repeat\`/\`End Repeat\`
|
|
158
|
+
`;
|
|
159
|
+
}
|
|
160
|
+
writeFileSync(join(pluginsDir, "skills", "create-shortcut.md"), skillContent);
|
|
161
|
+
console.log(" ā
Plugin files created!\n");
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error(" ā Failed:", error.message, "\n");
|
|
164
|
+
}
|
|
150
165
|
|
|
151
|
-
|
|
166
|
+
// Step 3: Register plugin
|
|
167
|
+
console.log("š Step 3: Registering plugin...");
|
|
168
|
+
try {
|
|
169
|
+
let installedPlugins = { version: 2, plugins: {} };
|
|
170
|
+
if (existsSync(installedPluginsPath)) {
|
|
171
|
+
installedPlugins = JSON.parse(readFileSync(installedPluginsPath, "utf-8"));
|
|
172
|
+
}
|
|
152
173
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
174
|
+
if (installedPlugins.plugins["scpl-shortcuts@local"]) {
|
|
175
|
+
console.log(" āļø Already registered, skipping...\n");
|
|
176
|
+
} else {
|
|
177
|
+
installedPlugins.plugins["scpl-shortcuts@local"] = [
|
|
178
|
+
{
|
|
179
|
+
scope: "user",
|
|
180
|
+
installPath: pluginsDir,
|
|
181
|
+
version: "1.0.0",
|
|
182
|
+
installedAt: new Date().toISOString(),
|
|
183
|
+
lastUpdated: new Date().toISOString(),
|
|
184
|
+
isLocal: true
|
|
185
|
+
}
|
|
186
|
+
];
|
|
187
|
+
writeFileSync(installedPluginsPath, JSON.stringify(installedPlugins, null, 2));
|
|
188
|
+
console.log(" ā
Plugin registered!\n");
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error(" ā Failed:", error.message, "\n");
|
|
192
|
+
}
|
|
160
193
|
|
|
161
|
-
|
|
194
|
+
console.log("ā
Claude Code setup complete!\n");
|
|
195
|
+
}
|
|
162
196
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
197
|
+
function setupClaudeDesktop() {
|
|
198
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
199
|
+
console.log("š Setting up ScPL Shortcuts for Claude Desktop...");
|
|
200
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
201
|
+
|
|
202
|
+
// Determine config path based on OS
|
|
203
|
+
let configPath;
|
|
204
|
+
const platform = process.platform;
|
|
205
|
+
if (platform === "darwin") {
|
|
206
|
+
configPath = join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
|
|
207
|
+
} else if (platform === "win32") {
|
|
208
|
+
configPath = join(process.env.APPDATA || "", "Claude", "claude_desktop_config.json");
|
|
209
|
+
} else {
|
|
210
|
+
configPath = join(homedir(), ".config", "Claude", "claude_desktop_config.json");
|
|
211
|
+
}
|
|
169
212
|
|
|
170
|
-
|
|
213
|
+
console.log(`š Adding MCP server to ${configPath}...`);
|
|
214
|
+
try {
|
|
215
|
+
// Ensure directory exists
|
|
216
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
171
217
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
\`\`\`
|
|
218
|
+
let config = {};
|
|
219
|
+
if (existsSync(configPath)) {
|
|
220
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
221
|
+
}
|
|
177
222
|
|
|
178
|
-
|
|
223
|
+
if (!config.mcpServers) {
|
|
224
|
+
config.mcpServers = {};
|
|
225
|
+
}
|
|
179
226
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
227
|
+
if (config.mcpServers["scpl-shortcuts"]) {
|
|
228
|
+
console.log(" āļø Already configured, skipping...\n");
|
|
229
|
+
} else {
|
|
230
|
+
config.mcpServers["scpl-shortcuts"] = {
|
|
231
|
+
command: "npx",
|
|
232
|
+
args: ["-y", "scpl-updated-mcp-server"]
|
|
233
|
+
};
|
|
234
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
235
|
+
console.log(" ā
MCP server added!\n");
|
|
236
|
+
}
|
|
190
237
|
} catch (error) {
|
|
191
|
-
console.error(" ā Failed
|
|
238
|
+
console.error(" ā Failed:", error.message, "\n");
|
|
192
239
|
}
|
|
193
240
|
|
|
194
|
-
console.log("
|
|
195
|
-
console.log("
|
|
196
|
-
console.log(
|
|
197
|
-
|
|
241
|
+
console.log("ā
Claude Desktop setup complete!\n");
|
|
242
|
+
console.log("ā¹ļø Note: Claude Desktop doesn't have plugins/skills.\n");
|
|
243
|
+
console.log(" The MCP tools (create_shortcut, validate_scpl, list_actions)");
|
|
244
|
+
console.log(" will be available, but Claude won't have the full ScPL reference");
|
|
245
|
+
console.log(" loaded as context. Consider using Claude Code for best results.\n");
|
|
198
246
|
}
|
|
199
247
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
console.log("š Setting up ScPL Shortcuts for Claude Code...\n");
|
|
248
|
+
function setupCodex(codexDir) {
|
|
249
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
250
|
+
console.log(`š Setting up ScPL Shortcuts for Codex at ${codexDir}...`);
|
|
251
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā\n");
|
|
205
252
|
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
const installedPluginsPath = join(homedir(), ".claude", "plugins", "installed_plugins.json");
|
|
253
|
+
const codexConfigPath = join(codexDir, "config.toml");
|
|
254
|
+
const skillDir = join(codexDir, "skills", "scpl-shortcuts");
|
|
209
255
|
|
|
210
|
-
// Step 1: Add MCP server to
|
|
211
|
-
console.log(
|
|
256
|
+
// Step 1: Add MCP server to config.toml
|
|
257
|
+
console.log(`š Step 1: Adding MCP server to ${codexConfigPath}...`);
|
|
212
258
|
try {
|
|
213
|
-
let
|
|
214
|
-
if (existsSync(
|
|
215
|
-
|
|
259
|
+
let config = "";
|
|
260
|
+
if (existsSync(codexConfigPath)) {
|
|
261
|
+
config = readFileSync(codexConfigPath, "utf-8");
|
|
216
262
|
}
|
|
217
263
|
|
|
218
|
-
if (
|
|
219
|
-
|
|
264
|
+
if (config.includes('[mcp_servers."scpl-shortcuts"]') || config.includes('[mcp_servers.scpl-shortcuts]')) {
|
|
265
|
+
console.log(" āļø Already configured, skipping...\n");
|
|
266
|
+
} else {
|
|
267
|
+
const tomlBlock = `
|
|
268
|
+
[mcp_servers.scpl-shortcuts]
|
|
269
|
+
command = "npx"
|
|
270
|
+
args = ["-y", "scpl-updated-mcp-server"]
|
|
271
|
+
startup_timeout_sec = 60.0
|
|
272
|
+
`;
|
|
273
|
+
writeFileSync(codexConfigPath, config + tomlBlock);
|
|
274
|
+
console.log(" ā
MCP server added!\n");
|
|
220
275
|
}
|
|
221
|
-
|
|
222
|
-
claudeConfig.mcpServers["scpl-shortcuts"] = {
|
|
223
|
-
type: "stdio",
|
|
224
|
-
command: "npx",
|
|
225
|
-
args: ["-y", "scpl-updated-mcp-server"]
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
writeFileSync(claudeJsonPath, JSON.stringify(claudeConfig, null, 2));
|
|
229
|
-
console.log(" ā
MCP server added!\n");
|
|
230
276
|
} catch (error) {
|
|
231
|
-
console.error(" ā Failed
|
|
232
|
-
console.log(" You can manually add this to ~/.claude.json under mcpServers:");
|
|
233
|
-
console.log(` "scpl-shortcuts": { "type": "stdio", "command": "npx", "args": ["-y", "scpl-updated-mcp-server"] }\n`);
|
|
277
|
+
console.error(" ā Failed:", error.message, "\n");
|
|
234
278
|
}
|
|
235
279
|
|
|
236
|
-
// Step 2:
|
|
237
|
-
console.log(
|
|
280
|
+
// Step 2: Install skill
|
|
281
|
+
console.log(`š Step 2: Installing skill to ${skillDir}...`);
|
|
238
282
|
try {
|
|
239
|
-
mkdirSync(
|
|
240
|
-
|
|
241
|
-
// Write plugin.json
|
|
242
|
-
const pluginJson = {
|
|
243
|
-
name: "scpl-shortcuts",
|
|
244
|
-
version: "1.0.0",
|
|
245
|
-
description: "Create macOS Shortcuts using natural language and ScPL",
|
|
246
|
-
skills: [
|
|
247
|
-
{
|
|
248
|
-
name: "create-shortcut",
|
|
249
|
-
description: "Create a macOS Shortcut using natural language. Guides you through the process and generates a .shortcut file.",
|
|
250
|
-
path: "skills/create-shortcut.md"
|
|
251
|
-
}
|
|
252
|
-
]
|
|
253
|
-
};
|
|
254
|
-
writeFileSync(join(pluginsDir, "plugin.json"), JSON.stringify(pluginJson, null, 2));
|
|
283
|
+
mkdirSync(skillDir, { recursive: true });
|
|
255
284
|
|
|
256
|
-
//
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
285
|
+
// Read comprehensive ScPL reference
|
|
286
|
+
const refPath = join(__dirname, "SCPL_REFERENCE.md");
|
|
287
|
+
let skillContent = "";
|
|
288
|
+
if (existsSync(refPath)) {
|
|
289
|
+
const reference = readFileSync(refPath, "utf-8");
|
|
290
|
+
skillContent = `---
|
|
291
|
+
name: scpl-shortcuts
|
|
292
|
+
description: Create macOS Shortcuts using natural language with ScPL.
|
|
293
|
+
metadata:
|
|
294
|
+
short-description: Create macOS Shortcuts with AI
|
|
260
295
|
---
|
|
261
296
|
|
|
262
|
-
#
|
|
297
|
+
# ScPL Shortcuts Skill
|
|
263
298
|
|
|
264
|
-
You have access to the ScPL MCP server with 493 actions
|
|
299
|
+
You have access to the ScPL MCP server with **493 actions**.
|
|
265
300
|
|
|
266
301
|
## Available Tools
|
|
302
|
+
- \`create_shortcut\` - Convert ScPL code to .shortcut file
|
|
303
|
+
- \`validate_scpl\` - Check syntax without creating file
|
|
304
|
+
- \`list_actions\` - Search available actions by category/keyword
|
|
267
305
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
306
|
+
${reference}
|
|
307
|
+
`;
|
|
308
|
+
} else {
|
|
309
|
+
// Fallback minimal skill
|
|
310
|
+
skillContent = `---
|
|
311
|
+
name: scpl-shortcuts
|
|
312
|
+
description: Create macOS Shortcuts using natural language.
|
|
313
|
+
metadata:
|
|
314
|
+
short-description: Create macOS Shortcuts with AI
|
|
315
|
+
---
|
|
271
316
|
|
|
272
|
-
|
|
317
|
+
# ScPL Shortcuts Skill
|
|
273
318
|
|
|
274
|
-
|
|
275
|
-
- **Clock**: StartStopwatch, CreateAlarm, GetCurrentTime
|
|
276
|
-
- **Voice Memos**: CreateRecording, PlayRecording
|
|
277
|
-
- **System**: SetDarkMode, TakeScreenshot, LockScreen
|
|
278
|
-
- **Files**: GetFile, SaveFile, RenameFile, RevealInFinder
|
|
279
|
-
- **Scripting**: RunShellScript, RunAppleScript
|
|
319
|
+
493 actions available. Tools: create_shortcut, validate_scpl, list_actions
|
|
280
320
|
|
|
281
|
-
|
|
321
|
+
Basic syntax: \`ActionName "arg"\` or \`ActionName param=value\`
|
|
322
|
+
Variables: \`v:Named\`, \`mv:Magic\`, \`s:Special\`
|
|
323
|
+
Flow: \`If\`/\`End If\`, \`Repeat\`/\`End Repeat\`
|
|
324
|
+
`;
|
|
325
|
+
}
|
|
326
|
+
writeFileSync(join(skillDir, "SKILL.md"), skillContent);
|
|
327
|
+
console.log(" ā
Skill installed!\n");
|
|
328
|
+
} catch (error) {
|
|
329
|
+
console.error(" ā Failed:", error.message, "\n");
|
|
330
|
+
}
|
|
282
331
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
ShowResult "Hello World!"
|
|
332
|
+
console.log(`ā
Codex setup complete for ${codexDir}!\n`);
|
|
333
|
+
}
|
|
286
334
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
ShowResult
|
|
335
|
+
// ============================================================================
|
|
336
|
+
// PROCESS SETUP FLAGS
|
|
337
|
+
// ============================================================================
|
|
291
338
|
|
|
292
|
-
|
|
293
|
-
RunShellScript shell="/bin/zsh" script="echo Hello"
|
|
294
|
-
ShowResult
|
|
339
|
+
let didSetup = false;
|
|
295
340
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
341
|
+
// Claude Code setup
|
|
342
|
+
if (process.argv.includes("--setup")) {
|
|
343
|
+
setupClaudeCode();
|
|
344
|
+
didSetup = true;
|
|
345
|
+
}
|
|
301
346
|
|
|
302
|
-
|
|
347
|
+
// Claude Desktop setup
|
|
348
|
+
if (process.argv.includes("--setup-desktop")) {
|
|
349
|
+
setupClaudeDesktop();
|
|
350
|
+
didSetup = true;
|
|
351
|
+
}
|
|
303
352
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
5. Use \`create_shortcut\` to generate the .shortcut file
|
|
309
|
-
6. Tell user to drag the file onto Shortcut Source Helper to install
|
|
310
|
-
`;
|
|
311
|
-
writeFileSync(join(pluginsDir, "skills", "create-shortcut.md"), skillContent);
|
|
312
|
-
console.log(" ā
Plugin files created!\n");
|
|
313
|
-
} catch (error) {
|
|
314
|
-
console.error(" ā Failed to create plugin files:", error.message, "\n");
|
|
315
|
-
}
|
|
353
|
+
// Codex setups (can have multiple --setup-codex and --setup-codex=<dir>)
|
|
354
|
+
const codexArgs = process.argv.filter(arg => arg.startsWith("--setup-codex"));
|
|
355
|
+
for (const codexArg of codexArgs) {
|
|
356
|
+
let codexDir = join(homedir(), ".codex"); // default
|
|
316
357
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
if (existsSync(installedPluginsPath)) {
|
|
322
|
-
installedPlugins = JSON.parse(readFileSync(installedPluginsPath, "utf-8"));
|
|
358
|
+
if (codexArg.includes("=")) {
|
|
359
|
+
let customDir = codexArg.split("=")[1];
|
|
360
|
+
if (customDir.startsWith("~")) {
|
|
361
|
+
customDir = customDir.replace("~", homedir());
|
|
323
362
|
}
|
|
324
|
-
|
|
325
|
-
installedPlugins.plugins["scpl-shortcuts@local"] = [
|
|
326
|
-
{
|
|
327
|
-
scope: "user",
|
|
328
|
-
installPath: pluginsDir,
|
|
329
|
-
version: "1.0.0",
|
|
330
|
-
installedAt: new Date().toISOString(),
|
|
331
|
-
lastUpdated: new Date().toISOString(),
|
|
332
|
-
isLocal: true
|
|
333
|
-
}
|
|
334
|
-
];
|
|
335
|
-
|
|
336
|
-
writeFileSync(installedPluginsPath, JSON.stringify(installedPlugins, null, 2));
|
|
337
|
-
console.log(" ā
Plugin registered!\n");
|
|
338
|
-
} catch (error) {
|
|
339
|
-
console.error(" ā Failed to register plugin:", error.message, "\n");
|
|
363
|
+
codexDir = customDir;
|
|
340
364
|
}
|
|
341
365
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
366
|
+
setupCodex(codexDir);
|
|
367
|
+
didSetup = true;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Exit after setup(s)
|
|
371
|
+
if (didSetup) {
|
|
372
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
373
|
+
console.log("š All setups complete! Restart your AI tool(s) to use shortcuts.");
|
|
374
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
375
|
+
console.log('\nUsage: Just ask to create a shortcut!');
|
|
376
|
+
console.log(' Example: "Create a shortcut that starts a timer"\n');
|
|
345
377
|
process.exit(0);
|
|
346
378
|
}
|
|
347
379
|
|
|
348
380
|
// ============================================================================
|
|
349
|
-
// MCP SERVER
|
|
381
|
+
// MCP SERVER (runs if no setup flags)
|
|
350
382
|
// ============================================================================
|
|
351
383
|
|
|
352
384
|
const server = new Server(
|
|
@@ -430,24 +462,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
430
462
|
if (name === "create_shortcut") {
|
|
431
463
|
const { scpl_code, output_name, output_dir } = args;
|
|
432
464
|
|
|
433
|
-
// Convert ScPL to shortcut
|
|
434
465
|
const shortcutBuffer = convert(scpl_code, {
|
|
435
466
|
makePlist: true,
|
|
436
467
|
makeShortcut: true,
|
|
437
468
|
});
|
|
438
469
|
|
|
439
|
-
// Determine output path (default to ~/Documents)
|
|
440
470
|
const dir = output_dir || join(homedir(), "Documents");
|
|
441
471
|
const outputPath = join(dir, `${output_name}.shortcut`);
|
|
442
472
|
|
|
443
|
-
// Write the file
|
|
444
473
|
writeFileSync(outputPath, shortcutBuffer);
|
|
445
474
|
|
|
446
475
|
return {
|
|
447
476
|
content: [
|
|
448
477
|
{
|
|
449
478
|
type: "text",
|
|
450
|
-
text: `ā
Shortcut created
|
|
479
|
+
text: `ā
Shortcut created!\n\nPath: ${outputPath}\n\nTo install: Drag onto Shortcut Source Helper from RoutineHub`,
|
|
451
480
|
},
|
|
452
481
|
],
|
|
453
482
|
};
|
|
@@ -457,69 +486,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
457
486
|
const { scpl_code } = args;
|
|
458
487
|
|
|
459
488
|
try {
|
|
460
|
-
convert(scpl_code, {
|
|
461
|
-
|
|
462
|
-
makeShortcut: false,
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
return {
|
|
466
|
-
content: [
|
|
467
|
-
{
|
|
468
|
-
type: "text",
|
|
469
|
-
text: "ā
ScPL code is valid!",
|
|
470
|
-
},
|
|
471
|
-
],
|
|
472
|
-
};
|
|
489
|
+
convert(scpl_code, { makePlist: false, makeShortcut: false });
|
|
490
|
+
return { content: [{ type: "text", text: "ā
ScPL code is valid!" }] };
|
|
473
491
|
} catch (error) {
|
|
474
|
-
return {
|
|
475
|
-
content: [
|
|
476
|
-
{
|
|
477
|
-
type: "text",
|
|
478
|
-
text: `ā ScPL validation failed:\n\n${error.message}`,
|
|
479
|
-
},
|
|
480
|
-
],
|
|
481
|
-
isError: true,
|
|
482
|
-
};
|
|
492
|
+
return { content: [{ type: "text", text: `ā Invalid: ${error.message}` }], isError: true };
|
|
483
493
|
}
|
|
484
494
|
}
|
|
485
495
|
|
|
486
496
|
if (name === "list_actions") {
|
|
487
497
|
const { category, search } = args || {};
|
|
488
498
|
|
|
489
|
-
// Embedded action list (top actions for quick reference)
|
|
490
499
|
const topActions = {
|
|
491
|
-
// AI
|
|
492
500
|
"is.workflow.actions.askllm": { Name: "Ask LLM", Category: "AI" },
|
|
493
501
|
"is.workflow.actions.askchatgpt": { Name: "Ask ChatGPT", Category: "AI" },
|
|
494
502
|
"com.anthropic.claudeforipad.AskClaudeIntentExtension": { Name: "Ask Claude", Category: "AI" },
|
|
495
|
-
// Clock
|
|
496
503
|
"com.apple.clock.StartStopwatchIntent": { Name: "Start Stopwatch", Category: "Clock" },
|
|
497
504
|
"com.apple.clock.StopStopwatchIntent": { Name: "Stop Stopwatch", Category: "Clock" },
|
|
498
505
|
"com.apple.clock.CreateAlarmIntent": { Name: "Create Alarm", Category: "Clock" },
|
|
499
|
-
// Voice Memos
|
|
500
506
|
"com.apple.VoiceMemos.CreateRecordingIntent": { Name: "Create Recording", Category: "Voice Memos" },
|
|
501
507
|
"com.apple.VoiceMemos.PlayRecordingIntent": { Name: "Play Recording", Category: "Voice Memos" },
|
|
502
|
-
// System
|
|
503
508
|
"is.workflow.actions.appearance": { Name: "Set Dark/Light Mode", Category: "System" },
|
|
504
509
|
"is.workflow.actions.takescreenshot": { Name: "Take Screenshot", Category: "System" },
|
|
505
510
|
"is.workflow.actions.lockscreen": { Name: "Lock Screen", Category: "System" },
|
|
506
|
-
// Scripting
|
|
507
511
|
"is.workflow.actions.runshellscript": { Name: "Run Shell Script", Category: "Scripting" },
|
|
508
512
|
"is.workflow.actions.runapplescript": { Name: "Run AppleScript", Category: "Scripting" },
|
|
509
513
|
"is.workflow.actions.runjavascriptforautomation": { Name: "Run JavaScript", Category: "Scripting" },
|
|
510
|
-
// Files
|
|
511
514
|
"is.workflow.actions.file.getfile": { Name: "Get File", Category: "Files" },
|
|
512
515
|
"is.workflow.actions.file.savefile": { Name: "Save File", Category: "Files" },
|
|
513
|
-
"is.workflow.actions.file.renamefile": { Name: "Rename File", Category: "Files" },
|
|
514
|
-
"is.workflow.actions.file.revealfile": { Name: "Reveal in Finder", Category: "Files" },
|
|
515
|
-
// Text
|
|
516
|
-
"is.workflow.actions.gettext": { Name: "Get Text", Category: "Text" },
|
|
517
516
|
"is.workflow.actions.showresult": { Name: "Show Result", Category: "Text" },
|
|
518
517
|
"is.workflow.actions.alert": { Name: "Show Alert", Category: "Text" },
|
|
519
|
-
// Variables
|
|
520
518
|
"is.workflow.actions.setvariable": { Name: "Set Variable", Category: "Variables" },
|
|
521
|
-
"is.workflow.actions.getvariable": { Name: "Get Variable", Category: "Variables" },
|
|
522
|
-
// Clipboard
|
|
523
519
|
"is.workflow.actions.getclipboard": { Name: "Get Clipboard", Category: "Clipboard" },
|
|
524
520
|
"is.workflow.actions.setclipboard": { Name: "Set Clipboard", Category: "Clipboard" },
|
|
525
521
|
};
|
|
@@ -527,108 +523,36 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
527
523
|
let filtered = Object.entries(topActions);
|
|
528
524
|
|
|
529
525
|
if (category) {
|
|
530
|
-
filtered = filtered.filter(
|
|
531
|
-
([_, action]) => action.Category?.toLowerCase() === category.toLowerCase()
|
|
532
|
-
);
|
|
526
|
+
filtered = filtered.filter(([_, a]) => a.Category?.toLowerCase() === category.toLowerCase());
|
|
533
527
|
}
|
|
534
|
-
|
|
535
528
|
if (search) {
|
|
536
|
-
const
|
|
537
|
-
filtered = filtered.filter(
|
|
538
|
-
(
|
|
539
|
-
id.toLowerCase().includes(searchLower) ||
|
|
540
|
-
action.Name?.toLowerCase().includes(searchLower) ||
|
|
541
|
-
action.Category?.toLowerCase().includes(searchLower)
|
|
529
|
+
const s = search.toLowerCase();
|
|
530
|
+
filtered = filtered.filter(([id, a]) =>
|
|
531
|
+
id.toLowerCase().includes(s) || a.Name?.toLowerCase().includes(s)
|
|
542
532
|
);
|
|
543
533
|
}
|
|
544
534
|
|
|
545
|
-
const results = filtered
|
|
546
|
-
|
|
547
|
-
.join("\n");
|
|
548
|
-
|
|
549
|
-
return {
|
|
550
|
-
content: [
|
|
551
|
-
{
|
|
552
|
-
type: "text",
|
|
553
|
-
text: `Found ${filtered.length} actions:\n\n${results}\n\nš” This is a subset of 493 total actions. For the full list, see: https://github.com/cavingraves/scpl-macos-updated`,
|
|
554
|
-
},
|
|
555
|
-
],
|
|
556
|
-
};
|
|
535
|
+
const results = filtered.map(([id, a]) => `⢠**${a.Name}** - \`${id}\``).join("\n");
|
|
536
|
+
return { content: [{ type: "text", text: `${filtered.length} actions:\n\n${results}\n\n(493 total available)` }] };
|
|
557
537
|
}
|
|
558
538
|
|
|
559
539
|
throw new Error(`Unknown tool: ${name}`);
|
|
560
540
|
} catch (error) {
|
|
561
|
-
return {
|
|
562
|
-
content: [
|
|
563
|
-
{
|
|
564
|
-
type: "text",
|
|
565
|
-
text: `Error: ${error.message}`,
|
|
566
|
-
},
|
|
567
|
-
],
|
|
568
|
-
isError: true,
|
|
569
|
-
};
|
|
541
|
+
return { content: [{ type: "text", text: `Error: ${error.message}` }], isError: true };
|
|
570
542
|
}
|
|
571
543
|
});
|
|
572
544
|
|
|
573
|
-
// Resource handlers
|
|
574
545
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
575
|
-
return {
|
|
576
|
-
resources: [
|
|
577
|
-
{
|
|
578
|
-
uri: "scpl://examples",
|
|
579
|
-
name: "ScPL Examples",
|
|
580
|
-
description: "Example shortcuts written in ScPL",
|
|
581
|
-
mimeType: "text/markdown",
|
|
582
|
-
},
|
|
583
|
-
],
|
|
584
|
-
};
|
|
546
|
+
return { resources: [{ uri: "scpl://examples", name: "ScPL Examples", mimeType: "text/markdown" }] };
|
|
585
547
|
});
|
|
586
548
|
|
|
587
549
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
if (uri === "scpl://examples") {
|
|
591
|
-
const examples = `# ScPL Examples
|
|
592
|
-
|
|
593
|
-
## Apple Intelligence
|
|
594
|
-
\`\`\`scpl
|
|
595
|
-
Text "Summarize this document"
|
|
596
|
-
AskLLM model="Apple Intelligence" prompt="Make it concise"
|
|
597
|
-
ShowResult
|
|
598
|
-
\`\`\`
|
|
599
|
-
|
|
600
|
-
## Timer with Sound
|
|
601
|
-
\`\`\`scpl
|
|
602
|
-
# Start a 5-minute timer
|
|
603
|
-
StartTimer minutes=5
|
|
604
|
-
ShowAlert title="Timer Started" message="5 minutes"
|
|
605
|
-
\`\`\`
|
|
606
|
-
|
|
607
|
-
## Shell Script
|
|
608
|
-
\`\`\`scpl
|
|
609
|
-
RunShellScript shell="/bin/zsh" script="echo Hello World"
|
|
610
|
-
ShowResult
|
|
611
|
-
\`\`\`
|
|
612
|
-
|
|
613
|
-
## Clipboard Workflow
|
|
614
|
-
\`\`\`scpl
|
|
615
|
-
GetClipboard
|
|
616
|
-
SetVariable v:text
|
|
617
|
-
AskChatGPT prompt="Improve this: \\(v:text)"
|
|
618
|
-
SetClipboard
|
|
619
|
-
ShowAlert title="Done" message="Improved text copied!"
|
|
620
|
-
\`\`\`
|
|
621
|
-
`;
|
|
622
|
-
|
|
623
|
-
return {
|
|
624
|
-
contents: [{ uri, mimeType: "text/markdown", text: examples }],
|
|
625
|
-
};
|
|
550
|
+
if (request.params.uri === "scpl://examples") {
|
|
551
|
+
return { contents: [{ uri: "scpl://examples", mimeType: "text/markdown", text: "# Examples\n\n```scpl\nShowResult \"Hello!\"\n```" }] };
|
|
626
552
|
}
|
|
627
|
-
|
|
628
|
-
throw new Error(`Unknown resource: ${uri}`);
|
|
553
|
+
throw new Error("Unknown resource");
|
|
629
554
|
});
|
|
630
555
|
|
|
631
|
-
// Start server
|
|
632
556
|
async function main() {
|
|
633
557
|
const transport = new StdioServerTransport();
|
|
634
558
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scpl-updated-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "AI-powered Apple Shortcuts creation with Claude Code! Generate macOS shortcuts using natural language. 493 actions available. MCP server for text-based shortcut programming. Vibe code your automation workflows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"scpl-updated-mcp-server": "index.js"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"SCPL_REFERENCE.md",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
10
15
|
"scripts": {
|
|
11
16
|
"start": "node index.js"
|
|
12
17
|
},
|