Skip to content

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.

License

Notifications You must be signed in to change notification settings

Beyond-Better/bb-applescript-mcp-server

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

40 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

AppleScript MCP Server

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.

Features

  • πŸ”’ 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

Prerequisites

  • 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

Quick Start

Option 1: Run Directly from JSR (Recommended)

No installation required! Just Deno and a config file.

1. Install Deno (if needed)

curl -fsSL https://deno.land/install.sh | sh

2. Configure in Beyond Better

Global Configuration (applies to all projects):

  1. Open BB Settings
  2. Go to "MCP Servers" tab
  3. Click "Configure Servers"
  4. 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
      

Project-Level Configuration (specific project only):

  1. Open your project in BB
  2. Go to Project Settings β†’ "MCP Servers"
  3. Enable server for AppleScript Tools

3. Start Using

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.

4. Optional: Configuration

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

Option 2: Run from Local Repository

For development or customization:

1. Clone and Setup

# 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

2. Configure Environment

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

3. Run the Server

# Development mode (with auto-reload)
deno task dev

# Production mode
deno task start

4. Configure in Beyond Better

Global Configuration:

  1. Open BB Settings (or Project Settings)
  2. Go to "MCP Servers" tab
  3. 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
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.

Project Structure

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/

Available Tools

AppleScript Tools (Standard Plugin)

πŸ”§ run_script

Status: πŸ”’ Disabled by default

Execute arbitrary AppleScript code.

Enable: Set ENABLE_ARBITRARY_SCRIPTS=true in .env

Parameters:

  • script: AppleScript code to execute
  • timeout: Optional timeout in milliseconds

Example:

{
  script: 'tell application "Finder" to get name of startup disk',
  timeout: 5000
}

πŸ“– read_dictionary

Read an application's AppleScript dictionary to understand its scriptable interface.

Parameters:

  • application: Application name (e.g., "BBEdit", "Finder")
  • filter: Optional filter criteria
    • include.types: Array of types to include
    • include.names: Specific names to include
    • searchTerms: Terms for fuzzy matching
    • format: "full" or "summary" (default: "summary")
  • timeout: Optional timeout

Example:

{
  application: "BBEdit",
  filter: {
    include: { names: ["project", "notebook"] },
    searchTerms: ["create"],
    format: "summary"
  }
}

βœ… check_applescript_permissions

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.

Finder Tools (Standard Plugin)

🏷️ set_file_label

Set Finder label colors for files/folders.

Parameters:

  • paths: Array of file/folder paths
  • labelIndex: 0=None, 1=Red, 2=Orange, 3=Yellow, 4=Green, 5=Blue, 6=Purple, 7=Gray
  • timeout: Optional timeout

🏷️ get_file_label

Get Finder label colors for files/folders.

Parameters:

  • paths: Array of file/folder paths
  • timeout: Optional timeout

πŸ“Š get_file_info_extended

Get detailed file information including Spotlight comments, tags, labels, etc.

Parameters:

  • path: File or folder path
  • timeout: Optional timeout

πŸ” reveal_in_finder

Reveal and select files/folders in Finder.

Parameters:

  • paths: Array of paths to reveal
  • timeout: Optional timeout

πŸ“‹ get_finder_selection

Get currently selected items in Finder.

Parameters:

  • timeout: Optional timeout

BBEdit Tools (BBEdit Plugin)

πŸ““ create_bbedit_notebook

Create a new BBEdit notebook.

Parameters:

  • name: Notebook name (required)
  • location: Optional save location (default: ~/Documents/BBEdit Notebooks/)
  • content: Optional array of content items
    • type: "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_bbedit_project

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 add
  • settings: 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
}

Plugin Management

Plugin Loading Strategy

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

Adding Custom Plugins

You can add your own plugins even when running from JSR:

  1. Create a plugin directory: mkdir ~/my-applescript-plugins
  2. Add your plugin files (see plugin guide)
  3. Set environment variable: PLUGINS_DISCOVERY_PATHS=/path/to/your/plugins
  4. Restart the server

Your custom plugins will be discovered and loaded alongside the built-in plugins.

Loading 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

Creating Custom Plugins

Plugins allow you to add tools for any scriptable macOS application. See the complete guide:

πŸ“ Creating Plugins 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

Script Development

Template Scripts

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

Compiled Scripts

For better performance, use compiled scripts:

# Compile an AppleScript
osacompile -o my_script.scpt my_script.applescript

Compiled scripts run directly without compilation overhead.

Error Handling

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
	}
}

Common Issues

Permission Denied

Solution: Grant automation permissions

  1. Use check_applescript_permissions to identify which apps need permissions
  2. Go to System Settings > Privacy & Security > Automation
  3. Enable automation for the MCP server process

Script Timeouts

Solution: Increase timeout or optimize script

  • Set higher timeout parameter
  • Check APPLESCRIPT_TIMEOUT_MAX configuration
  • Break complex operations into smaller scripts

run_script Disabled

Solution: Enable in configuration

  • Set ENABLE_ARBITRARY_SCRIPTS=true in .env
  • Restart server
  • Be aware of security implications

Security Considerations

  1. πŸ”’ Arbitrary Scripts: Disabled by default. Only enable if you trust the LLM client.
  2. πŸ“ File System: Scripts run with server process permissions.
  3. 🎯 Application Control: Can control any app with granted automation permissions.
  4. ⏱️ Timeout Limits: Always enforced to prevent runaway scripts.
  5. πŸ“ Error Details: May contain system information. Review error output.

Performance Tips

  • 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)

Testing

# Run tests
deno task test

# Run specific test file
deno test tests/scriptRunner.test.ts

Contributing

This is a showcase example for the @beyondbetter/bb-mcp-server library.

Contributions welcome:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Submit a pull request

License

MIT License - see LICENSE file for details

Related Projects

Support


Built with @beyondbetter/bb-mcp-server - A powerful framework for building MCP servers with plugins, workflows, and OAuth support.

About

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.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published