Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions docs/plans/2025-10-20-applescript-multiline-fix-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# AppleScript Multi-line Execution Fix Design

**Date:** 2025-10-20
**Author:** Claude Code with Pedro Dusso
**Version:** 1.0.1

## Problem Statement

The MCP Apple Notes server was failing to execute AppleScript commands with the following error:
```
33:37: syntax error: Expected end of line, found "tell". (-2741)
```

**Root Cause:** The `src/utils/applescript.ts` file was replacing newlines with spaces (`.replace(/[\r\n]+/g, ' ')`), breaking the multi-line structure required by AppleScript.

## Solution Approach

After evaluating three architectural approaches:
1. Minimal Fix (30 min) - Fix critical bugs only ✅ **Selected**
2. Robust Refactoring (2 hours) - Add account detection, permissions, better error handling
3. Complete Architecture (4+ hours) - Full refactoring with tests, CI/CD, caching

We chose the **Minimal Fix** approach to maintain compatibility and solve issues quickly.

## Implementation Details

### 1. AppleScript Execution Fix (`src/utils/applescript.ts`)

**Key Changes:**
- **Multi-line Support:** Using multiple `-e` flags (Apple's official approach)
- **Proper Escaping:** Single quotes escaped with `'\\''`
- **Better Error Handling:** Capture stderr for detailed debugging
- **Validation:** Check for empty scripts before execution

**Technical Decision:** Multiple `-e` flags were chosen over:
- Preserving newlines (escaping issues)
- Semi-colon conversion (doesn't work for all commands)
- Temporary files (I/O overhead)

### 2. Notes Manager Improvements (`src/services/appleNotesManager.ts`)

**Key Changes:**
- **Proper String Escaping:** New `escapeAppleScriptString()` function for all special characters
- **Account Detection:** Auto-detect iCloud vs default account on initialization
- **Centralized Script Building:** `buildScript()` method for DRY principle
- **Correct List Parsing:** Using `split(', ')` instead of `split(',')` for AppleScript lists
- **Unique IDs:** Timestamp + random string for collision-free note IDs

**Technical Decisions:**
- Account detection happens once in constructor (not per-operation)
- Fallback to default account if iCloud unavailable
- No breaking API changes

### 3. Better Initialization (`src/index.ts`)

**Key Changes:**
- **Try-catch Initialization:** Graceful failure with helpful error messages
- **Permission Guidance:** Clear instructions for macOS automation permissions
- **Version Bump:** 1.0.0 → 1.0.1
- **Account Logging:** Shows which account is being used

## Testing Results

All tests passed successfully:

1. ✅ **Multi-line scripts** execute without syntax errors
2. ✅ **Special characters** (quotes, apostrophes) handled correctly
3. ✅ **Empty scripts** return appropriate error
4. ✅ **iCloud account** detected and used
5. ✅ **Note creation** works with escaped content
6. ✅ **Note search** returns correct results with proper parsing
7. ✅ **Note content** retrieval successful

## Benefits of This Approach

- **Minimal Changes:** Full backward compatibility
- **Maximum Impact:** All critical bugs resolved
- **Robustness:** Automatic account fallback
- **Debuggability:** Clear logs at each step
- **User Experience:** Actionable error messages
- **Maintainability:** Clean, organized code

## Files Modified

```
src/
├── utils/
│ └── applescript.ts (~50 lines changed)
├── services/
│ └── appleNotesManager.ts (~80 lines changed)
└── index.ts (~20 lines changed)
```

Total: ~150 lines modified across 3 files

## Commit Information

```bash
Branch: fix/applescript-multiline-execution
Version: 1.0.1
Changes: 3 files modified
Tests: All passing
```

## Future Improvements (Not Implemented)

For future iterations, consider:
1. Unit tests with Jest
2. Account management UI
3. Permission checker utility
4. Support for folders/notebooks
5. Note editing and deletion
6. Rich text formatting support

## Decision Rationale

The minimal fix approach was chosen because:
1. **Urgency:** Server was completely broken, needed immediate fix
2. **Risk:** Minimal changes reduce regression risk
3. **Compatibility:** No breaking changes to existing API
4. **Time:** 30-minute implementation vs 2-4 hours for larger refactoring
5. **Value:** Solves all critical issues without over-engineering

## Conclusion

The implementation successfully resolves all critical bugs while maintaining full backward compatibility. The server now correctly handles multi-line AppleScript commands, properly escapes special characters, and provides better error messages for troubleshooting.
31 changes: 27 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,39 @@ import { z } from "zod";
import { AppleNotesManager } from "@/services/appleNotesManager.js";
import type { CreateNoteParams, SearchParams, GetNoteParams } from "@/types.js";

// Initialize notes manager with error handling
let notesManager: AppleNotesManager;

try {
notesManager = new AppleNotesManager();
// Use stderr for logging in MCP context (stdout is reserved for JSON-RPC)
if (process.stderr) {
process.stderr.write('✅ Apple Notes MCP Server initialized successfully\n');
process.stderr.write(`📝 Using account: ${notesManager.getCurrentAccount() || 'default'}\n`);
}
} catch (error) {
// Use stderr for error messages in MCP context
if (process.stderr) {
process.stderr.write(`❌ Failed to initialize Apple Notes: ${error}\n`);
process.stderr.write('\n🔧 Troubleshooting:\n');
process.stderr.write('1. Ensure Apple Notes app is installed\n');
process.stderr.write('2. Configure at least one account in Notes\n');
process.stderr.write('3. Grant permission when prompted\n');
process.stderr.write('4. Check System Settings > Privacy & Security > Automation\n');
process.stderr.write(' - Look for Terminal.app or Claude.app\n');
process.stderr.write(' - Enable access to Notes.app\n');
process.stderr.write('\n📖 For more help: https://github.com/punkpeye/mcp-apple-notes#troubleshooting\n');
}
process.exit(1);
}

// Initialize the MCP server
const server = new McpServer({
name: "apple-notes",
version: "1.0.0",
version: "1.0.1",
description: "MCP server for interacting with Apple Notes"
});

// Initialize the notes manager
const notesManager = new AppleNotesManager();

// Define tool schemas
const createNoteSchema = {
title: z.string().min(1, "Title is required"),
Expand Down
Loading