A Model Context Protocol (MCP) server that enables LLM clients to interact with macOS applications through AppleScript. Built using the @beyondbetter/bb-mcp-server library, this server provides safe, controlled execution of predefined scripts with optional support for arbitrary script execution.
- π Safe by Default: Arbitrary script execution disabled by default
- π¦ Plugin Architecture: Modular design with task-focused plugins
- π οΈ Standard Tools: Read dictionaries, check permissions, Finder operations
- π BBEdit Integration: Create notebooks and projects
- β±οΈ Timeout Protection: Configurable timeouts with hard limits
- π Structured Errors: Consistent error handling with LLM-friendly hints
- π Performance: Support for both compiled and template-based scripts
- Deno: Version 2.5.0 or later
- macOS: Required for AppleScript execution
- Applications: Any macOS application that supports AppleEvents (scriptable applications)
- Examples: Finder (built-in), BBEdit, Mail, Safari, etc.
- This server includes plugins for Finder and BBEdit by default
- You can create plugins for any scriptable application
No installation required! Just Deno and a config file.
curl -fsSL https://deno.land/install.sh | sh
Global Configuration (applies to all projects):
- Open BB Settings
- Go to "MCP Servers" tab
- Click "Configure Servers"
- Add new server with:
- Server ID:
applescript
- Display Name:
AppleScript Tools
- Transport Type:
STDIO (Local Process)
- Command:
deno
- Arguments:
run --allow-all --unstable-kv jsr:@beyondbetter/bb-applescript-mcp-server
- Server ID:
Project-Level Configuration (specific project only):
- Open your project in BB
- Go to Project Settings β "MCP Servers"
- Enable server for
AppleScript Tools
That's it! The server runs directly from JSR with no download or installation needed.
Alternative: Claude Desktop Configuration
Add to ~/Library/Application Support/Claude/claude_desktop_config.json
:
{
"mcpServers": {
"applescript": {
"command": "deno",
"args": [
"run",
"--allow-all",
"--unstable-kv",
"jsr:@beyondbetter/bb-applescript-mcp-server"
]
}
}
}
Restart Claude Desktop.
By default, the server runs with safe defaults:
- Arbitrary script execution: disabled
- All standard plugins: enabled
- Default timeouts: 30s default, 5min max
To customize, add environment variables in BB's MCP server configuration:
In BB Settings β MCP Servers β Edit Server β Environment Variables:
Key | Value | Description |
---|---|---|
LOG_LEVEL |
info |
Set to debug for detailed logs |
ENABLE_ARBITRARY_SCRIPTS |
false |
Set to true to enable run_script tool |
PLUGINS_ALLOWED_LIST |
(empty) | Comma-separated list to load specific plugins |
PLUGINS_BLOCKED_LIST |
(empty) | Comma-separated list to block plugins |
For development or customization:
# Clone the repository
git clone https://github.com/your-username/bb-mcp-applescript.git
cd bb-mcp-applescript/server
# Copy environment template
cp .env.example .env
Edit .env
to customize:
# Basic Configuration
MCP_TRANSPORT=stdio
LOG_LEVEL=info
# Plugin Control
PLUGINS_ALLOWED_LIST= # Empty = load all plugins
PLUGINS_BLOCKED_LIST= # Block specific plugins
# Safety (IMPORTANT)
ENABLE_ARBITRARY_SCRIPTS=false # Set to true to enable run_script tool
# Timeouts
APPLESCRIPT_TIMEOUT_DEFAULT=30000 # 30 seconds
APPLESCRIPT_TIMEOUT_MAX=300000 # 5 minutes
# Development mode (with auto-reload)
deno task dev
# Production mode
deno task start
Global Configuration:
- Open BB Settings (or Project Settings)
- Go to "MCP Servers" tab
- Add new server:
- Server ID:
applescript
- Display Name:
AppleScript Tools (Local Dev)
- Transport Type:
STDIO (Local Process)
- Command:
deno
- Arguments:
run --allow-all --unstable-kv /absolute/path/to/bb-mcp-applescript/server/main.ts
- Environment Variables (optional):
LOG_LEVEL
:debug
ENABLE_ARBITRARY_SCRIPTS
:false
- Server ID:
Alternative: Claude Desktop Configuration
Add to ~/Library/Application Support/Claude/claude_desktop_config.json
:
{
"mcpServers": {
"applescript": {
"command": "deno",
"args": [
"run",
"--allow-all",
"--unstable-kv",
"/absolute/path/to/bb-mcp-applescript/server/main.ts"
]
}
}
}
Restart Claude Desktop.
server/
βββ main.ts # Server entry point
βββ deno.jsonc # Deno configuration
βββ .env.example # Environment template
βββ mcp_server_instructions.md # LLM context/instructions
βββ src/
β βββ plugins/
β β βββ standard.plugin/ # Standard tools plugin
β β β βββ plugin.ts # Plugin implementation
β β β βββ scripts/ # AppleScript files
β β β βββ read_dictionary.applescript
β β β βββ check_permissions.applescript
β β β βββ finder/
β β β βββ set_file_label.applescript
β β β βββ get_file_label.applescript
β β β βββ get_file_info_extended.applescript
β β β βββ reveal_in_finder.applescript
β β β βββ get_selection.applescript
β β βββ bbedit.plugin/ # BBEdit tools plugin
β β βββ plugin.ts
β β βββ scripts/
β β βββ create_notebook.applescript
β β βββ create_project.applescript
β βββ utils/
β βββ scriptLoader.ts # Script loading/discovery
β βββ scriptRunner.ts # Script execution
β βββ templateRenderer.ts # Template interpolation
β βββ errorHandler.ts # Error standardization
βββ tests/
Status: π Disabled by default
Execute arbitrary AppleScript code.
Enable: Set ENABLE_ARBITRARY_SCRIPTS=true
in .env
Parameters:
script
: AppleScript code to executetimeout
: Optional timeout in milliseconds
Example:
{
script: 'tell application "Finder" to get name of startup disk',
timeout: 5000
}
Read an application's AppleScript dictionary to understand its scriptable interface.
Parameters:
application
: Application name (e.g., "BBEdit", "Finder")filter
: Optional filter criteriainclude.types
: Array of types to includeinclude.names
: Specific names to includesearchTerms
: Terms for fuzzy matchingformat
: "full" or "summary" (default: "summary")
timeout
: Optional timeout
Example:
{
application: "BBEdit",
filter: {
include: { names: ["project", "notebook"] },
searchTerms: ["create"],
format: "summary"
}
}
Check automation permissions for applications.
Parameters:
applications
: Optional array of app names (default: ["Finder", "BBEdit", "Terminal"])timeout
: Optional timeout
Returns: Permission status for each application with instructions if needed.
Set Finder label colors for files/folders.
Parameters:
paths
: Array of file/folder pathslabelIndex
: 0=None, 1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Blue, 6=Purple, 7=Graytimeout
: Optional timeout
Get Finder label colors for files/folders.
Parameters:
paths
: Array of file/folder pathstimeout
: Optional timeout
Get detailed file information including Spotlight comments, tags, labels, etc.
Parameters:
path
: File or folder pathtimeout
: Optional timeout
Reveal and select files/folders in Finder.
Parameters:
paths
: Array of paths to revealtimeout
: Optional timeout
Get currently selected items in Finder.
Parameters:
timeout
: Optional timeout
Create a new BBEdit notebook.
Parameters:
name
: Notebook name (required)location
: Optional save location (default: ~/Documents/BBEdit Notebooks/)content
: Optional array of content itemstype
: "text" or "file"data
: Text content or file path
open
: Whether to open after creation (default: true)timeout
: Optional timeout
Example:
{
name: "Project Notes",
content: [
{ type: "text", data: "Meeting notes..." },
{ type: "file", data: "/path/to/notes.txt" }
],
open: true
}
Create a new BBEdit project.
Parameters:
name
: Project name (required)location
: Optional save location (default: ~/Documents/BBEdit Projects/)items
: Optional array of file/folder paths to addsettings
: Optional project settings (reserved for future)open
: Whether to open after creation (default: true)timeout
: Optional timeout
Example:
{
name: "Website Project",
items: [
"/Users/username/Projects/website/src",
"/Users/username/Projects/website/docs"
],
open: true
}
This server uses a hybrid plugin loading approach that automatically adapts to the runtime environment:
-
JSR Mode: When running from JSR (e.g.,
jsr:@beyondbetter/bb-applescript-mcp-server
):- Built-in plugins (standard-tools, bbedit) are statically loaded
- User plugins can be loaded from local directories via
PLUGINS_DISCOVERY_PATHS
- Allows users to extend the server with custom plugins
-
Local Mode: When running from a local directory:
- Built-in plugins are statically loaded as fallback
- Dynamic discovery enabled by default
- Hot-reload support during development
The server automatically detects which mode it's running in and configures plugin loading appropriately. No configuration is needed - it just works!
π For technical details, see Plugin Loading Documentation
You can add your own plugins even when running from JSR:
- Create a plugin directory:
mkdir ~/my-applescript-plugins
- Add your plugin files (see plugin guide)
- Set environment variable:
PLUGINS_DISCOVERY_PATHS=/path/to/your/plugins
- Restart the server
Your custom plugins will be discovered and loaded alongside the built-in plugins.
Plugins are auto-discovered from src/plugins/
directory (local mode) or statically loaded (JSR mode).
Load all plugins (default):
PLUGINS_ALLOWED_LIST=
PLUGINS_BLOCKED_LIST=
Load specific plugins only:
PLUGINS_ALLOWED_LIST=standard,bbedit
PLUGINS_BLOCKED_LIST=
Load all except specific plugins:
PLUGINS_ALLOWED_LIST=
PLUGINS_BLOCKED_LIST=experimental
Plugins allow you to add tools for any scriptable macOS application. See the complete guide:
Quick example for a Mail.app plugin:
// src/plugins/mail.plugin/plugin.ts
import { AppPlugin, ToolRegistry, z } from 'jsr:@beyondbetter/bb-mcp-server';
import { findAndExecuteScript } from '../../utils/scriptLoader.ts';
export default {
name: 'mail',
version: '1.0.0',
description: 'Tools for Mail.app',
workflows: [],
tools: [],
async initialize(dependencies, toolRegistry, workflowRegistry) {
const pluginDir = getPluginDir();
toolRegistry.registerTool(
'send_email',
{
title: 'Send Email',
description: 'Create an email in Mail.app',
category: 'Mail',
inputSchema: {
to: z.string().email().describe('Recipient'),
subject: z.string().describe('Subject'),
body: z.string().describe('Body'),
},
},
async (args) => {
// Runs scripts/send_email.applescript with template variables
return await findAndExecuteScript(
pluginDir,
'send_email',
{ to: args.to, subject: args.subject, body: args.body }
);
},
);
},
} as AppPlugin;
The guide covers:
- Plugin structure and file naming
- AppleScript template variables
- Multiple tools per plugin
- Error handling and debugging
- Complete working examples
Use template variables in .applescript
files:
-- my_script.applescript
tell application "Finder"
set file label of (POSIX file ${filePath} as alias) to ${labelIndex}
end tell
Variables are automatically escaped and type-converted:
- Strings: Wrapped in quotes, escaped
- Arrays: Converted to AppleScript lists:
{"item1", "item2"}
- Objects: Converted to AppleScript records:
{key:"value"}
- null/undefined: Converted to
missing value
For better performance, use compiled scripts:
# Compile an AppleScript
osacompile -o my_script.scpt my_script.applescript
Compiled scripts run directly without compilation overhead.
All tools return consistent error structures:
{
"success": false,
"error": {
"type": "permission|timeout|script_error|system_error|disabled",
"message": "Human-readable error",
"code": "ERR_CODE",
"hint": "LLM-friendly suggestion",
"details": "Technical details"
},
"metadata": {
"executionTime": 123,
"scriptPath": "/path/to/script",
"timeoutUsed": 30000
}
}
Solution: Grant automation permissions
- Use
check_applescript_permissions
to identify which apps need permissions - Go to System Settings > Privacy & Security > Automation
- Enable automation for the MCP server process
Solution: Increase timeout or optimize script
- Set higher
timeout
parameter - Check
APPLESCRIPT_TIMEOUT_MAX
configuration - Break complex operations into smaller scripts
Solution: Enable in configuration
- Set
ENABLE_ARBITRARY_SCRIPTS=true
in.env
- Restart server
- Be aware of security implications
- π Arbitrary Scripts: Disabled by default. Only enable if you trust the LLM client.
- π File System: Scripts run with server process permissions.
- π― Application Control: Can control any app with granted automation permissions.
- β±οΈ Timeout Limits: Always enforced to prevent runaway scripts.
- π Error Details: May contain system information. Review error output.
- Use compiled scripts (
.scpt
) for frequently-used operations - Set appropriate timeouts based on operation complexity
- Batch operations when possible (e.g., multiple files at once)
- Cache results for read-only operations (e.g., dictionary reads)
# Run tests
deno task test
# Run specific test file
deno test tests/scriptRunner.test.ts
This is a showcase example for the @beyondbetter/bb-mcp-server library.
Contributions welcome:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
MIT License - see LICENSE file for details
- bb-mcp-server: The MCP server library powering this project
- Model Context Protocol: The protocol specification
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Library Docs: bb-mcp-server Documentation
Built with @beyondbetter/bb-mcp-server - A powerful framework for building MCP servers with plugins, workflows, and OAuth support.