{
  "markdown": "# JCAppleScript\n\nA Swift package for executing AppleScript from macOS applications, featuring a built-in **MCP server** that lets AI assistants control macOS apps through pre-built command shortcuts.\n\n## Overview\n\nJCAppleScript provides three components:\n\n1. **JCAppleScript** (library) - Core AppleScript execution engine\n2. **AppShortcuts** (library) - Registry of pre-built AppleScript commands for popular macOS apps\n3. **jcas-mcp** (executable) - MCP (Model Context Protocol) server for AI-driven app automation\n\n## Installation\n\n### Swift Package Manager\n\nAdd JCAppleScript to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/johnnyclem/JCAppleScript.git\", from: \"2.0.0\")\n]\n```\n\nThen add the targets you need:\n\n```swift\n.target(\n    name: \"YourTarget\",\n    dependencies: [\n        \"JCAppleScript\",     // Core engine only\n        \"AppShortcuts\",      // App command registry\n    ]\n)\n```\n\n## Quick Start\n\n### Using the Core Engine\n\n```swift\nimport JCAppleScript\n\nlet engine = AppleScriptEngine.shared\n\n// Execute raw AppleScript\nlet result = try engine.execute(\"\"\"\n    tell application \"Finder\"\n        display dialog \"Hello from Swift!\"\n    end tell\n\"\"\")\n\n// Send a command to an application\nlet output = try engine.tell(application: \"Music\", command: \"play\")\n\n// Execute a script file with variable substitution\nlet fileResult = try engine.executeFile(at: \"/path/to/script.scpt\", variables: [\"Alice\", \"Hello!\"])\n\n// Execute JavaScript for Automation (JXA)\nlet jxa = try engine.execute(\"Application('Music').play()\", language: .javaScript)\n\n// Check syntax without executing\ntry engine.checkSyntax(\"tell application \\\"Finder\\\" to activate\")\n\n// All execution APIs also have async variants\nlet asyncResult = try await engine.execute(\"return 40 + 2\")\n```\n\nWhen embedding untrusted values in script source, escape them first:\n\n```swift\nlet userInput = \"…\"\nlet script = \"display dialog \\(AppleScriptString.quoted(userInput))\"\n```\n\n### Using App Shortcuts\n\n```swift\nimport AppShortcuts\n\nlet registry = AppRegistry.shared\n\n// Execute a pre-built command\nlet result = try registry.executeCommand(\"messages.send_message\", arguments: [\n    \"recipient\": \"+15551234567\",\n    \"message\": \"Hello from JCAppleScript!\"\n])\n\n// Discover available commands\nlet commands = registry.commands(forApp: \"Reminders\")\nfor cmd in commands {\n    print(\"\\(cmd.id): \\(cmd.name) - \\(cmd.description)\")\n}\n\n// Search across all apps\nlet results = registry.searchCommands(\"send\")\n```\n\n### Using the MCP Server\n\nThe `jcas-mcp` executable is a [Model Context Protocol](https://modelcontextprotocol.io) server that AI assistants (Claude, GPT, etc.) can use to control macOS applications.\n\nIt is published to the [official MCP registry](https://registry.modelcontextprotocol.io) as **`io.github.johnnyclem/jcas-mcp`**, and each GitHub release ships a prebuilt `jcas-mcp.mcpb` bundle (universal macOS binary) that can be installed directly in Claude Desktop via Settings → Extensions.\n\n#### Setup with Claude Desktop\n\nAdd to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"applescript\": {\n      \"command\": \"/path/to/jcas-mcp\"\n    }\n  }\n}\n```\n\nBuild the server:\n\n```bash\nswift build -c release\n# Binary at: .build/release/jcas-mcp\n```\n\n#### Available MCP Tools\n\n| Tool | Description |\n|------|-------------|\n| `execute_applescript` | Execute arbitrary AppleScript code † |\n| `execute_jxa` | Execute JavaScript for Automation (JXA) code † |\n| `tell_application` | Send a command to a specific app via `tell` block † |\n| `check_script_syntax` | Compile a script (AppleScript or JXA) without executing it |\n| `list_running_applications` | Get currently running applications |\n| `list_registered_apps` | Browse all registered app command sheets |\n| `search_commands` | Search for commands by keyword |\n| `run_app_command` | Execute a pre-built command by ID |\n| `preview_app_command` | Dry-run: show the exact script a command would execute |\n| `get_app_commands` | Get detailed command info for a specific app |\n\n† Hidden and disabled when the server runs in safe mode (see below).\n\n#### Server Configuration\n\n| Environment variable | Effect |\n|----------------------|--------|\n| `JCAS_SAFE_MODE=1` | Disables the arbitrary-code tools (`execute_applescript`, `execute_jxa`, `tell_application`) and registry commands flagged `dangerous` (e.g. `terminal.run_command`, `safari.run_javascript`). Only pre-built, sanitized registry commands remain available. |\n| `JCAS_APP_MANIFESTS=a.json:b.json` | Colon-separated JSON manifest files with additional community app definitions to load at startup. |\n\nCLI flags: `jcas-mcp --manifest` prints the full registry as JSON, `--version` prints the server version, `--help` shows usage.\n\n#### Example AI Interaction\n\n```\nUser: \"Send a message to John saying I'll be late\"\nAI uses tool: run_app_command\n  command_id: \"messages.send_message\"\n  arguments: { \"recipient\": \"John\", \"message\": \"I'll be late\" }\n```\n\n## Supported Applications\n\nJCAppleScript ships with command sheets for 12 built-in macOS apps:\n\n| App | Category | Commands | Examples |\n|-----|----------|----------|----------|\n| **Messages** | Communication | 6 | Send message, list chats, get participants |\n| **Mail** | Communication | 7 | Compose email, search, check mail, list accounts |\n| **Reminders** | Productivity | 7 | Create/complete/delete reminders, search, list |\n| **Calendar** | Productivity | 6 | Create events, list today's events, upcoming |\n| **Notes** | Productivity | 8 | Create/search/append notes, manage folders |\n| **Finder** | System | 12 | File operations, folder contents, labels, trash |\n| **Safari** | Internet | 10 | Open URLs, manage tabs, run JavaScript, get page content |\n| **Music** | Media | 13 | Playback control, playlists, library search, ratings |\n| **Terminal** | Development | 8 | Run commands, manage windows/tabs, profiles |\n| **System Settings** | System | 13 | Dark mode, volume, notifications, dialogs, system info |\n| **Xcode** | Development | 20+ | Open/build/run/test projects, schemes, build logs, debugging |\n| **Speech Recognition** | System | 3 | Listen for spoken phrases via the system speech engine |\n\n## Adding Custom App Support\n\nImplement the `ScriptableApp` protocol to add support for any scriptable macOS app:\n\n```swift\nimport AppShortcuts\n\nstruct MyApp: ScriptableApp {\n    static let bundleIdentifier = \"com.example.myapp\"\n    static let appName = \"MyApp\"\n    static let description = \"My custom application\"\n    static let category = AppCategory.productivity\n\n    static let commands: [AppCommand] = [\n        AppCommand(\n            id: \"myapp.do_thing\",\n            name: \"Do Thing\",\n            description: \"Performs the thing\",\n            parameters: [\n                CommandParameter(name: \"input\", description: \"The input value\"),\n            ]\n        ) { args in\n            let input = args[\"input\", default: \"\"]\n            return \"\"\"\n            tell application \"MyApp\"\n                do thing with \"\\(input)\"\n            end tell\n            \"\"\"\n        },\n    ]\n}\n\n// Register at runtime\nAppRegistry.shared.register(MyApp.self)\n```\n\n## Community App Registry\n\nJCAppleScript is designed to grow through community contributions. The app shortcut system uses a standard protocol (`ScriptableApp`) that makes it easy to:\n\n- **Add new applications** - Implement `ScriptableApp` for any scriptable macOS app\n- **Extend existing apps** - Submit new commands for already-registered apps\n- **Share command sheets** - Export/import app definitions via JSON manifests\n\nWe're building a browsable registry (similar to npmjs.org) where you can:\n- Browse applications and their supported AppleScript commands\n- Submit new commands for existing apps\n- Add entirely new applications to the registry\n- Generate JSON manifests for integration with other tools\n\n### Exporting and Importing the Registry\n\n```swift\n// Export all registered apps as manifest JSON\nlet json = try AppRegistry.shared.exportManifestJSON()\n\n// Import community app definitions from a JSON manifest.\n// Manifest commands are declarative script templates with ${param}\n// placeholders; argument values are sanitized before substitution.\ntry AppRegistry.shared.loadManifest(contentsOf: URL(fileURLWithPath: \"community.json\"))\n```\n\nExample manifest:\n\n```json\n[\n  {\n    \"name\": \"CoolApp\",\n    \"bundleIdentifier\": \"com.example.coolapp\",\n    \"description\": \"A community-contributed app\",\n    \"category\": \"Productivity\",\n    \"commands\": [\n      {\n        \"id\": \"coolapp.greet\",\n        \"name\": \"Greet\",\n        \"description\": \"Show a greeting\",\n        \"script\": \"tell application \\\"CoolApp\\\"\\n    greet \\\"${who}\\\"\\nend tell\",\n        \"parameters\": [\n          {\"name\": \"who\", \"description\": \"Who to greet\", \"required\": true, \"type\": \"string\"}\n        ]\n      }\n    ]\n  }\n]\n```\n\nThe MCP server loads extra manifests from the `JCAS_APP_MANIFESTS` environment variable at startup.\n\n## Security Model\n\nRegistry command arguments are **sanitized before script generation**:\n\n- String, file-path, and date arguments are escaped (`\\`, `\"`, and control characters) so they cannot break out of AppleScript string literals.\n- Integer and boolean arguments are strictly validated/normalized — malformed values are rejected at validation and fall back to declared defaults during generation.\n- Values outside a parameter's `allowedValues` list are dropped.\n- Arguments that don't correspond to a declared parameter are discarded.\n\nCommands that execute caller-supplied code (Terminal shell commands, Safari JavaScript) are flagged `dangerous` and can be disabled wholesale with `JCAS_SAFE_MODE=1`. Use the `preview_app_command` tool to inspect the exact script a command will run before executing it.\n\nNote that `execute_applescript`, `execute_jxa`, and `tell_application` execute arbitrary code by design — only expose them to clients you trust, or run the server in safe mode.\n\n## Architecture\n\n```\nJCAppleScript/\n├── Sources/\n│   ├── JCAppleScript/           # Core engine\n│   │   ├── AppleScriptEngine.swift\n│   │   ├── AppleScriptSanitizer.swift\n│   │   ├── ScriptResult.swift\n│   │   └── ScriptError.swift\n│   ├── AppShortcuts/            # App command registry\n│   │   ├── AppProtocol.swift    # ScriptableApp protocol\n│   │   ├── AppCommand.swift     # Command & parameter types + sanitization\n│   │   ├── AppDefinition.swift  # Instance-based app description\n│   │   ├── AppManifest.swift    # JSON import/export\n│   │   ├── AppRegistry.swift    # Central registry\n│   │   └── Apps/                # Built-in app sheets\n│   │       ├── MessagesApp.swift\n│   │       ├── RemindersApp.swift\n│   │       ├── FinderApp.swift\n│   │       ├── SafariApp.swift\n│   │       ├── MailApp.swift\n│   │       ├── CalendarApp.swift\n│   │       ├── NotesApp.swift\n│   │       ├── MusicApp.swift\n│   │       ├── TerminalApp.swift\n│   │       └── SystemSettingsApp.swift\n│   └── JCAppleScriptMCP/       # MCP server\n│       ├── main.swift\n│       ├── MCPServer.swift\n│       ├── MCPTransport.swift\n│       └── MCPTypes.swift\n├── Tests/\n├── Legacy/                      # Original Obj-C implementation\n├── Package.swift\n└── LICENSE\n```\n\n## Requirements\n\n- macOS 13.0+\n- Swift 5.9+\n\n## Legacy\n\nThe original Objective-C implementation (2013) is preserved in the `Legacy/` directory for reference. It provided basic NSAppleScript wrapping with variable substitution. The new Swift implementation builds on those concepts while adding the MCP server, app registry, and modern Swift patterns.\n\n## License\n\nMIT License - Copyright (c) 2013 John Clem. See [LICENSE](LICENSE) for details.\n",
  "bytes": 11674,
  "sha": "73e4b56f2529cf6361a0c935b15074500239bc672bcb907af13ebd6a781827a9",
  "repo_slug": "johnnyclem/jcapplescript",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_johnnyclem_jcas_mcp_ac4e53aa/readme"
}