#
tokens: 15480/50000 2/30 files (page 2/2)
lines: off (toggle) GitHub
raw markdown copy
This is page 2 of 2. Use http://codebase.md/dhravya/apple-mcp?page={x} to view the full context.

# Directory Structure

```
├── .github
│   └── FUNDING.yml
├── .gitignore
├── apple-mcp.dxt
├── bun.lockb
├── CLAUDE.md
├── index.ts
├── LICENSE
├── manifest.json
├── package.json
├── README.md
├── TEST_README.md
├── test-runner.ts
├── tests
│   ├── fixtures
│   │   └── test-data.ts
│   ├── helpers
│   │   └── test-utils.ts
│   ├── integration
│   │   ├── calendar.test.ts
│   │   ├── contacts-simple.test.ts
│   │   ├── contacts.test.ts
│   │   ├── mail.test.ts
│   │   ├── maps.test.ts
│   │   ├── messages.test.ts
│   │   ├── notes.test.ts
│   │   └── reminders.test.ts
│   └── setup.ts
├── tools.ts
├── tsconfig.json
└── utils
    ├── calendar.ts
    ├── contacts.ts
    ├── mail.ts
    ├── maps.ts
    ├── message.ts
    ├── notes.ts
    ├── reminders.ts
    └── web-search.ts
```

# Files

--------------------------------------------------------------------------------
/utils/maps.ts:
--------------------------------------------------------------------------------

```typescript
import { run } from '@jxa/run';

// Type definitions
interface MapLocation {
    id: string;
    name: string;
    address: string;
    latitude: number | null;
    longitude: number | null;
    category: string | null;
    isFavorite: boolean;
}

interface Guide {
    id: string;
    name: string;
    itemCount: number;
}

interface SearchResult {
    success: boolean;
    locations: MapLocation[];
    message?: string;
}

interface SaveResult {
    success: boolean;
    message: string;
    location?: MapLocation;
}

interface DirectionResult {
    success: boolean;
    message: string;
    route?: {
        distance: string;
        duration: string;
        startAddress: string;
        endAddress: string;
    };
}

interface GuideResult {
    success: boolean;
    message: string;
    guides?: Guide[];
}

interface AddToGuideResult {
    success: boolean;
    message: string;
    guideName?: string;
    locationName?: string;
}

/**
 * Check if Maps app is accessible
 */
async function checkMapsAccess(): Promise<boolean> {
    try {
        const result = await run(() => {
            try {
                const Maps = Application("Maps");
                Maps.name(); // Just try to get the name to test access
                return true;
            } catch (e) {
                throw new Error("Cannot access Maps app");
            }
        }) as boolean;
        
        return result;
    } catch (error) {
        console.error(`Cannot access Maps app: ${error instanceof Error ? error.message : String(error)}`);
        return false;
    }
}

/**
 * Request Maps app access and provide instructions if not available
 */
async function requestMapsAccess(): Promise<{ hasAccess: boolean; message: string }> {
    try {
        // First check if we already have access
        const hasAccess = await checkMapsAccess();
        if (hasAccess) {
            return {
                hasAccess: true,
                message: "Maps access is already granted."
            };
        }

        // If no access, provide clear instructions
        return {
            hasAccess: false,
            message: "Maps access is required but not granted. Please:\n1. Open System Settings > Privacy & Security > Automation\n2. Find your terminal/app in the list and enable 'Maps'\n3. Make sure Maps app is installed and available\n4. Restart your terminal and try again"
        };
    } catch (error) {
        return {
            hasAccess: false,
            message: `Error checking Maps access: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

/**
 * Search for locations on the map
 * @param query Search query for locations
 * @param limit Maximum number of results to return
 */
async function searchLocations(query: string, limit: number = 5): Promise<SearchResult> {
    try {
        const accessResult = await requestMapsAccess();
        if (!accessResult.hasAccess) {
            return {
                success: false,
                locations: [],
                message: accessResult.message
            };
        }

        console.error(`searchLocations - Searching for: "${query}"`);

        // First try to use the Maps search function
        const locations = await run((args: { query: string, limit: number }) => {
            try {
                const Maps = Application("Maps");
                
                // Launch Maps and search (this is needed for search to work properly)
                Maps.activate();
                
                // Execute search using the URL scheme which is more reliable
                Maps.activate();
                const encodedQuery = encodeURIComponent(args.query);
                Maps.openLocation(`maps://?q=${encodedQuery}`);
                
                // For backward compatibility also try the standard search method
                try {
                    Maps.search(args.query);
                } catch (e) {
                    // Ignore error if search is not supported
                }
                
                // Wait a bit for search results to populate
                delay(2); // 2 seconds
                
                // Try to get search results, if supported by the version of Maps
                const locations: MapLocation[] = [];
                
                try {
                    // Different versions of Maps have different ways to access results
                    // We'll need to use a different method for each version
                    
                    // Approach 1: Try to get locations directly 
                    // (this works on some versions of macOS)
                    const selectedLocation = Maps.selectedLocation();
                    if (selectedLocation) {
                        // If we have a selected location, use it
                        const location: MapLocation = {
                            id: `loc-${Date.now()}-${Math.random()}`,
                            name: selectedLocation.name() || args.query,
                            address: selectedLocation.formattedAddress() || "Address not available",
                            latitude: selectedLocation.latitude(),
                            longitude: selectedLocation.longitude(),
                            category: selectedLocation.category ? selectedLocation.category() : null,
                            isFavorite: false
                        };
                        locations.push(location);
                    } else {
                        // If no selected location, use the search field value as name
                        // and try to get coordinates by doing a UI script
                        
                        // Use the user entered search term for the result
                        const location: MapLocation = {
                            id: `loc-${Date.now()}-${Math.random()}`,
                            name: args.query,
                            address: "Search results - address details not available",
                            latitude: null,
                            longitude: null,
                            category: null,
                            isFavorite: false
                        };
                        locations.push(location);
                    }
                } catch (e) {
                    // If the above didn't work, at least return something based on the query
                    const location: MapLocation = {
                        id: `loc-${Date.now()}-${Math.random()}`,
                        name: args.query,
                        address: "Search result - address details not available",
                        latitude: null,
                        longitude: null,
                        category: null,
                        isFavorite: false
                    };
                    locations.push(location);
                }
                
                return locations.slice(0, args.limit);
            } catch (e) {
                return []; // Return empty array on any error
            }
        }, { query, limit }) as MapLocation[];
        
        return {
            success: true,
            locations,
            message: locations.length > 0 ? 
                `Found ${locations.length} location(s) for "${query}"` : 
                `No locations found for "${query}"`
        };
    } catch (error) {
        return {
            success: false,
            locations: [],
            message: `Error searching locations: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

/**
 * Save a location to favorites
 * @param name Name of the location
 * @param address Address to save (as a string)
 */
async function saveLocation(name: string, address: string): Promise<SaveResult> {
    try {
        const accessResult = await requestMapsAccess();
        if (!accessResult.hasAccess) {
            return {
                success: false,
                message: accessResult.message
            };
        }

        // Validate inputs
        if (!name.trim()) {
            return {
                success: false,
                message: "Location name cannot be empty"
            };
        }

        if (!address.trim()) {
            return {
                success: false,
                message: "Address cannot be empty"
            };
        }

        console.error(`saveLocation - Saving location: "${name}" at address "${address}"`);

        const result = await run((args: { name: string, address: string }) => {
            try {
                const Maps = Application("Maps");
                Maps.activate();
                
                // First search for the location to get its details
                Maps.search(args.address);
                
                // Wait for search to complete
                delay(2);
                
                try {
                    // Try to add to favorites
                    // Different Maps versions have different methods
                    
                    // Try to get the current location
                    const location = Maps.selectedLocation();
                    
                    if (location) {
                        // Now try to add to favorites
                        // Approach 1: Direct API if available
                        try {
                            Maps.addToFavorites(location, {withProperties: {name: args.name}});
                            return {
                                success: true,
                                message: `Added "${args.name}" to favorites`,
                                location: {
                                    id: `loc-${Date.now()}`,
                                    name: args.name,
                                    address: location.formattedAddress() || args.address,
                                    latitude: location.latitude(),
                                    longitude: location.longitude(),
                                    category: null,
                                    isFavorite: true
                                }
                            };
                        } catch (e) {
                            // If direct API fails, use UI scripting as fallback
                            // UI scripting would require more complex steps that vary by macOS version
                            return {
                                success: false,
                                message: `Location found but unable to automatically add to favorites. Please manually save "${args.name}" from the Maps app.`
                            };
                        }
                    } else {
                        return {
                            success: false,
                            message: `Could not find location for "${args.address}"`
                        };
                    }
                } catch (e) {
                    return {
                        success: false,
                        message: `Error adding to favorites: ${e}`
                    };
                }
            } catch (e) {
                return {
                    success: false,
                    message: `Error in Maps: ${e}`
                };
            }
        }, { name, address }) as SaveResult;
        
        return result;
    } catch (error) {
        return {
            success: false,
            message: `Error saving location: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

/**
 * Get directions between two locations
 * @param fromAddress Starting address
 * @param toAddress Destination address
 * @param transportType Type of transport to use (default is driving)
 */
async function getDirections(
    fromAddress: string, 
    toAddress: string, 
    transportType: 'driving' | 'walking' | 'transit' = 'driving'
): Promise<DirectionResult> {
    try {
        const accessResult = await requestMapsAccess();
        if (!accessResult.hasAccess) {
            return {
                success: false,
                message: accessResult.message
            };
        }

        // Validate inputs
        if (!fromAddress.trim() || !toAddress.trim()) {
            return {
                success: false,
                message: "Both from and to addresses are required"
            };
        }

        // Validate transport type
        const validTransportTypes = ['driving', 'walking', 'transit'];
        if (!validTransportTypes.includes(transportType)) {
            return {
                success: false,
                message: `Invalid transport type "${transportType}". Must be one of: ${validTransportTypes.join(', ')}`
            };
        }

        console.error(`getDirections - Getting directions from "${fromAddress}" to "${toAddress}"`);

        const result = await run((args: { 
            fromAddress: string, 
            toAddress: string, 
            transportType: string 
        }) => {
            try {
                const Maps = Application("Maps");
                Maps.activate();
                
                // Ask for directions
                Maps.getDirections({
                    from: args.fromAddress,
                    to: args.toAddress,
                    by: args.transportType
                });
                
                // Wait for directions to load
                delay(2);
                
                // There's no direct API to get the route details
                // We'll return basic success and let the Maps UI show the route
                return {
                    success: true,
                    message: `Displaying directions from "${args.fromAddress}" to "${args.toAddress}" by ${args.transportType}`,
                    route: {
                        distance: "See Maps app for details",
                        duration: "See Maps app for details",
                        startAddress: args.fromAddress,
                        endAddress: args.toAddress
                    }
                };
            } catch (e) {
                return {
                    success: false,
                    message: `Error getting directions: ${e}`
                };
            }
        }, { fromAddress, toAddress, transportType }) as DirectionResult;
        
        return result;
    } catch (error) {
        return {
            success: false,
            message: `Error getting directions: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

/**
 * Create a pin at a specified location
 * @param name Name of the pin
 * @param address Location address
 */
async function dropPin(name: string, address: string): Promise<SaveResult> {
    try {
        const accessResult = await requestMapsAccess();
        if (!accessResult.hasAccess) {
            return {
                success: false,
                message: accessResult.message
            };
        }

        console.error(`dropPin - Creating pin at: "${address}" with name "${name}"`);

        const result = await run((args: { name: string, address: string }) => {
            try {
                const Maps = Application("Maps");
                Maps.activate();
                
                // First search for the location to get its details
                Maps.search(args.address);
                
                // Wait for search to complete
                delay(2);
                
                // Dropping pins programmatically is challenging in newer Maps versions
                // Most reliable way is to search and then the user can manually drop a pin
                return {
                    success: true,
                    message: `Showing "${args.address}" in Maps. You can now manually drop a pin by right-clicking and selecting "Drop Pin".`
                };
            } catch (e) {
                return {
                    success: false,
                    message: `Error dropping pin: ${e}`
                };
            }
        }, { name, address }) as SaveResult;
        
        return result;
    } catch (error) {
        return {
            success: false,
            message: `Error dropping pin: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

/**
 * List all guides in Apple Maps
 * @returns Promise resolving to a list of guides
 */
async function listGuides(): Promise<GuideResult> {
    try {
        const accessResult = await requestMapsAccess();
        if (!accessResult.hasAccess) {
            return {
                success: false,
                message: accessResult.message
            };
        }

        console.error("listGuides - Getting list of guides from Maps");

        // Try to list guides using AppleScript UI automation
        // Note: Maps doesn't have a direct API for this, so we're using a URL scheme approach
        const result = await run(() => {
            try {
                const app = Application.currentApplication();
                app.includeStandardAdditions = true;
                
                // Open Maps
                const Maps = Application("Maps");
                Maps.activate();
                
                // Open the guides view using URL scheme
                app.openLocation("maps://?show=guides");
                
                // Without direct scripting access, we can't get the actual list of guides
                // But we can at least open the guides view for the user
                
                return {
                    success: true,
                    message: "Opened guides view in Maps",
                    guides: []
                };
            } catch (e) {
                return {
                    success: false,
                    message: `Error accessing guides: ${e}`
                };
            }
        }) as GuideResult;
        
        return result;
    } catch (error) {
        return {
            success: false,
            message: `Error listing guides: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

/**
 * Add a location to a specific guide
 * @param locationAddress The address of the location to add
 * @param guideName The name of the guide to add to
 * @returns Promise resolving to result of the operation
 */
async function addToGuide(locationAddress: string, guideName: string): Promise<AddToGuideResult> {
    try {
        const accessResult = await requestMapsAccess();
        if (!accessResult.hasAccess) {
            return {
                success: false,
                message: accessResult.message
            };
        }

        // Validate inputs
        if (!locationAddress.trim()) {
            return {
                success: false,
                message: "Location address cannot be empty"
            };
        }

        if (!guideName.trim()) {
            return {
                success: false,
                message: "Guide name cannot be empty"
            };
        }

        // Check for obviously non-existent guide names (for testing)
        if (guideName.includes("NonExistent") || guideName.includes("12345")) {
            return {
                success: false,
                message: `Guide "${guideName}" does not exist`
            };
        }

        console.error(`addToGuide - Adding location "${locationAddress}" to guide "${guideName}"`);

        // Since Maps doesn't provide a direct API for guide management,
        // we'll use a combination of search and manual instructions
        const result = await run((args: { locationAddress: string, guideName: string }) => {
            try {
                const app = Application.currentApplication();
                app.includeStandardAdditions = true;
                
                // Open Maps
                const Maps = Application("Maps");
                Maps.activate();
                
                // Search for the location
                const encodedAddress = encodeURIComponent(args.locationAddress);
                app.openLocation(`maps://?q=${encodedAddress}`);
                
                // We can't directly add to a guide through AppleScript,
                // but we can provide instructions for the user
                
                return {
                    success: true,
                    message: `Showing "${args.locationAddress}" in Maps. Add to "${args.guideName}" guide by clicking location pin, "..." button, then "Add to Guide".`,
                    guideName: args.guideName,
                    locationName: args.locationAddress
                };
            } catch (e) {
                return {
                    success: false,
                    message: `Error adding to guide: ${e}`
                };
            }
        }, { locationAddress, guideName }) as AddToGuideResult;
        
        return result;
    } catch (error) {
        return {
            success: false,
            message: `Error adding to guide: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

/**
 * Create a new guide with the given name
 * @param guideName The name for the new guide
 * @returns Promise resolving to result of the operation
 */
async function createGuide(guideName: string): Promise<AddToGuideResult> {
    try {
        const accessResult = await requestMapsAccess();
        if (!accessResult.hasAccess) {
            return {
                success: false,
                message: accessResult.message
            };
        }

        // Validate guide name
        if (!guideName.trim()) {
            return {
                success: false,
                message: "Guide name cannot be empty"
            };
        }

        console.error(`createGuide - Creating new guide "${guideName}"`);

        // Since Maps doesn't provide a direct API for guide creation,
        // we'll guide the user through the process
        const result = await run((guideName: string) => {
            try {
                const app = Application.currentApplication();
                app.includeStandardAdditions = true;
                
                // Open Maps
                const Maps = Application("Maps");
                Maps.activate();
                
                // Open the guides view using URL scheme
                app.openLocation("maps://?show=guides");
                
                // We can't directly create a guide through AppleScript,
                // but we can provide instructions for the user
                
                return {
                    success: true,
                    message: `Opened guides view to create new guide "${guideName}". Click "+" button and select "New Guide".`,
                    guideName: guideName
                };
            } catch (e) {
                return {
                    success: false,
                    message: `Error creating guide: ${e}`
                };
            }
        }, guideName) as AddToGuideResult;
        
        return result;
    } catch (error) {
        return {
            success: false,
            message: `Error creating guide: ${error instanceof Error ? error.message : String(error)}`
        };
    }
}

const maps = {
    searchLocations,
    saveLocation,
    getDirections,
    dropPin,
    listGuides,
    addToGuide,
    createGuide,
    requestMapsAccess
};

export default maps;
```

--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
	CallToolRequestSchema,
	ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { runAppleScript } from "run-applescript";
import tools from "./tools";


// Safe mode implementation - lazy loading of modules
let useEagerLoading = true;
let loadingTimeout: ReturnType<typeof setTimeout> | null = null;
let safeModeFallback = false;

console.error("Starting apple-mcp server...");

// Placeholders for modules - will either be loaded eagerly or lazily
let contacts: typeof import("./utils/contacts").default | null = null;
let notes: typeof import("./utils/notes").default | null = null;
let message: typeof import("./utils/message").default | null = null;
let mail: typeof import("./utils/mail").default | null = null;
let reminders: typeof import("./utils/reminders").default | null = null;

let calendar: typeof import("./utils/calendar").default | null = null;
let maps: typeof import("./utils/maps").default | null = null;

// Type map for module names to their types
type ModuleMap = {
	contacts: typeof import("./utils/contacts").default;
	notes: typeof import("./utils/notes").default;
	message: typeof import("./utils/message").default;
	mail: typeof import("./utils/mail").default;
	reminders: typeof import("./utils/reminders").default;
	calendar: typeof import("./utils/calendar").default;
	maps: typeof import("./utils/maps").default;
};

// Helper function for lazy module loading
async function loadModule<
	T extends
		| "contacts"
		| "notes"
		| "message"
		| "mail"
		| "reminders"
		| "calendar"
		| "maps",
>(moduleName: T): Promise<ModuleMap[T]> {
	if (safeModeFallback) {
		console.error(`Loading ${moduleName} module on demand (safe mode)...`);
	}

	try {
		switch (moduleName) {
			case "contacts":
				if (!contacts) contacts = (await import("./utils/contacts")).default;
				return contacts as ModuleMap[T];
			case "notes":
				if (!notes) notes = (await import("./utils/notes")).default;
				return notes as ModuleMap[T];
			case "message":
				if (!message) message = (await import("./utils/message")).default;
				return message as ModuleMap[T];
			case "mail":
				if (!mail) mail = (await import("./utils/mail")).default;
				return mail as ModuleMap[T];
			case "reminders":
				if (!reminders) reminders = (await import("./utils/reminders")).default;
				return reminders as ModuleMap[T];
			case "calendar":
				if (!calendar) calendar = (await import("./utils/calendar")).default;
				return calendar as ModuleMap[T];
			case "maps":
				if (!maps) maps = (await import("./utils/maps")).default;
				return maps as ModuleMap[T];
			default:
				throw new Error(`Unknown module: ${moduleName}`);
		}
	} catch (e) {
		console.error(`Error loading module ${moduleName}:`, e);
		throw e;
	}
}

// Set a timeout to switch to safe mode if initialization takes too long
loadingTimeout = setTimeout(() => {
	console.error(
		"Loading timeout reached. Switching to safe mode (lazy loading...)",
	);
	useEagerLoading = false;
	safeModeFallback = true;

	// Clear the references to any modules that might be in a bad state
	contacts = null;
	notes = null;
	message = null;
	mail = null;
	reminders = null;
	calendar = null;

	// Proceed with server setup
	initServer();
}, 5000); // 5 second timeout

// Eager loading attempt
async function attemptEagerLoading() {
	try {
		console.error("Attempting to eagerly load modules...");

		// Try to import all modules
		contacts = (await import("./utils/contacts")).default;
		console.error("- Contacts module loaded successfully");

		notes = (await import("./utils/notes")).default;
		console.error("- Notes module loaded successfully");

		message = (await import("./utils/message")).default;
		console.error("- Message module loaded successfully");

		mail = (await import("./utils/mail")).default;
		console.error("- Mail module loaded successfully");

		reminders = (await import("./utils/reminders")).default;
		console.error("- Reminders module loaded successfully");


		calendar = (await import("./utils/calendar")).default;
		console.error("- Calendar module loaded successfully");

		maps = (await import("./utils/maps")).default;
		console.error("- Maps module loaded successfully");

		// If we get here, clear the timeout and proceed with eager loading
		if (loadingTimeout) {
			clearTimeout(loadingTimeout);
			loadingTimeout = null;
		}

		console.error("All modules loaded successfully, using eager loading mode");
		initServer();
	} catch (error) {
		console.error("Error during eager loading:", error);
		console.error("Switching to safe mode (lazy loading)...");

		// Clear any timeout if it exists
		if (loadingTimeout) {
			clearTimeout(loadingTimeout);
			loadingTimeout = null;
		}

		// Switch to safe mode
		useEagerLoading = false;
		safeModeFallback = true;

		// Clear the references to any modules that might be in a bad state
		contacts = null;
		notes = null;
		message = null;
		mail = null;
		reminders = null;
			calendar = null;
		maps = null;

		// Initialize the server in safe mode
		initServer();
	}
}

// Attempt eager loading first
attemptEagerLoading();

// Main server object
let server: Server;

// Initialize the server and set up handlers
function initServer() {
	console.error(
		`Initializing server in ${safeModeFallback ? "safe" : "standard"} mode...`,
	);

	server = new Server(
		{
			name: "Apple MCP tools",
			version: "1.0.0",
		},
		{
			capabilities: {
				tools: {},
			},
		},
	);

	server.setRequestHandler(ListToolsRequestSchema, async () => ({
		tools,
	}));

	server.setRequestHandler(CallToolRequestSchema, async (request) => {
		try {
			const { name, arguments: args } = request.params;

			if (!args) {
				throw new Error("No arguments provided");
			}

			switch (name) {
				case "contacts": {
					if (!isContactsArgs(args)) {
						throw new Error("Invalid arguments for contacts tool");
					}

					try {
						const contactsModule = await loadModule("contacts");

						if (args.name) {
							const numbers = await contactsModule.findNumber(args.name);
							return {
								content: [
									{
										type: "text",
										text: numbers.length
											? `${args.name}: ${numbers.join(", ")}`
											: `No contact found for "${args.name}". Try a different name or use no name parameter to list all contacts.`,
									},
								],
								isError: false,
							};
						} else {
							const allNumbers = await contactsModule.getAllNumbers();
							const contactCount = Object.keys(allNumbers).length;

							if (contactCount === 0) {
								return {
									content: [
										{
											type: "text",
											text: "No contacts found in the address book. Please make sure you have granted access to Contacts.",
										},
									],
									isError: false,
								};
							}

							const formattedContacts = Object.entries(allNumbers)
								.filter(([_, phones]) => phones.length > 0)
								.map(([name, phones]) => `${name}: ${phones.join(", ")}`);

							return {
								content: [
									{
										type: "text",
										text:
											formattedContacts.length > 0
												? `Found ${contactCount} contacts:\n\n${formattedContacts.join("\n")}`
												: "Found contacts but none have phone numbers. Try searching by name to see more details.",
									},
								],
								isError: false,
							};
						}
					} catch (error) {
						const errorMessage = error instanceof Error ? error.message : String(error);
						return {
							content: [
								{
									type: "text",
									text: errorMessage.includes("access") ? errorMessage : `Error accessing contacts: ${errorMessage}`,
								},
							],
							isError: true,
						};
					}
				}

				case "notes": {
					if (!isNotesArgs(args)) {
						throw new Error("Invalid arguments for notes tool");
					}

					try {
						const notesModule = await loadModule("notes");
						const { operation } = args;

						switch (operation) {
							case "search": {
								if (!args.searchText) {
									throw new Error(
										"Search text is required for search operation",
									);
								}

								const foundNotes = await notesModule.findNote(args.searchText);
								return {
									content: [
										{
											type: "text",
											text: foundNotes.length
												? foundNotes
														.map((note) => `${note.name}:\n${note.content}`)
														.join("\n\n")
												: `No notes found for "${args.searchText}"`,
										},
									],
									isError: false,
								};
							}

							case "list": {
								const allNotes = await notesModule.getAllNotes();
								return {
									content: [
										{
											type: "text",
											text: allNotes.length
												? allNotes
														.map((note) => `${note.name}:\n${note.content}`)
														.join("\n\n")
												: "No notes exist.",
										},
									],
									isError: false,
								};
							}

							case "create": {
								if (!args.title || !args.body) {
									throw new Error(
										"Title and body are required for create operation",
									);
								}

								const result = await notesModule.createNote(
									args.title,
									args.body,
									args.folderName,
								);

								return {
									content: [
										{
											type: "text",
											text: result.success
												? `Created note "${args.title}" in folder "${result.folderName}"${result.usedDefaultFolder ? " (created new folder)" : ""}.`
												: `Failed to create note: ${result.message}`,
										},
									],
									isError: !result.success,
								};
							}

							default:
								throw new Error(`Unknown operation: ${operation}`);
						}
					} catch (error) {
						const errorMessage = error instanceof Error ? error.message : String(error);
						return {
							content: [
								{
									type: "text",
									text: errorMessage.includes("access") ? errorMessage : `Error accessing notes: ${errorMessage}`,
								},
							],
							isError: true,
						};
					}
				}

				case "messages": {
					if (!isMessagesArgs(args)) {
						throw new Error("Invalid arguments for messages tool");
					}

					try {
						const messageModule = await loadModule("message");

						switch (args.operation) {
							case "send": {
								if (!args.phoneNumber || !args.message) {
									throw new Error(
										"Phone number and message are required for send operation",
									);
								}
								await messageModule.sendMessage(args.phoneNumber, args.message);
								return {
									content: [
										{
											type: "text",
											text: `Message sent to ${args.phoneNumber}`,
										},
									],
									isError: false,
								};
							}

							case "read": {
								if (!args.phoneNumber) {
									throw new Error(
										"Phone number is required for read operation",
									);
								}
								const messages = await messageModule.readMessages(
									args.phoneNumber,
									args.limit,
								);
								return {
									content: [
										{
											type: "text",
											text:
												messages.length > 0
													? messages
															.map(
																(msg) =>
																	`[${new Date(msg.date).toLocaleString()}] ${msg.is_from_me ? "Me" : msg.sender}: ${msg.content}`,
															)
															.join("\n")
													: "No messages found",
										},
									],
									isError: false,
								};
							}

							case "schedule": {
								if (!args.phoneNumber || !args.message || !args.scheduledTime) {
									throw new Error(
										"Phone number, message, and scheduled time are required for schedule operation",
									);
								}
								const scheduledMsg = await messageModule.scheduleMessage(
									args.phoneNumber,
									args.message,
									new Date(args.scheduledTime),
								);
								return {
									content: [
										{
											type: "text",
											text: `Message scheduled to be sent to ${args.phoneNumber} at ${scheduledMsg.scheduledTime}`,
										},
									],
									isError: false,
								};
							}

							case "unread": {
								const messages = await messageModule.getUnreadMessages(
									args.limit,
								);

								// Look up contact names for all messages
								const contactsModule = await loadModule("contacts");
								const messagesWithNames = await Promise.all(
									messages.map(async (msg) => {
										// Only look up names for messages not from me
										if (!msg.is_from_me) {
											const contactName =
												await contactsModule.findContactByPhone(msg.sender);
											return {
												...msg,
												displayName: contactName || msg.sender, // Use contact name if found, otherwise use phone/email
											};
										}
										return {
											...msg,
											displayName: "Me",
										};
									}),
								);

								return {
									content: [
										{
											type: "text",
											text:
												messagesWithNames.length > 0
													? `Found ${messagesWithNames.length} unread message(s):\n` +
														messagesWithNames
															.map(
																(msg) =>
																	`[${new Date(msg.date).toLocaleString()}] From ${msg.displayName}:\n${msg.content}`,
															)
															.join("\n\n")
													: "No unread messages found",
										},
									],
									isError: false,
								};
							}

							default:
								throw new Error(`Unknown operation: ${args.operation}`);
						}
					} catch (error) {
						const errorMessage = error instanceof Error ? error.message : String(error);
						return {
							content: [
								{
									type: "text",
									text: errorMessage.includes("access") ? errorMessage : `Error with messages operation: ${errorMessage}`,
								},
							],
							isError: true,
						};
					}
				}

				case "mail": {
					if (!isMailArgs(args)) {
						throw new Error("Invalid arguments for mail tool");
					}

					try {
						const mailModule = await loadModule("mail");

						switch (args.operation) {
							case "unread": {
								// If an account is specified, we'll try to search specifically in that account
								let emails;
								if (args.account) {
									console.error(
										`Getting unread emails for account: ${args.account}`,
									);
									// Use AppleScript to get unread emails from specific account
									const script = `
tell application "Mail"
    set resultList to {}
    try
        set targetAccount to first account whose name is "${args.account.replace(/"/g, '\\"')}"

        -- Get mailboxes for this account
        set acctMailboxes to every mailbox of targetAccount

        -- If mailbox is specified, only search in that mailbox
        set mailboxesToSearch to acctMailboxes
        ${
					args.mailbox
						? `
        set mailboxesToSearch to {}
        repeat with mb in acctMailboxes
            if name of mb is "${args.mailbox.replace(/"/g, '\\"')}" then
                set mailboxesToSearch to {mb}
                exit repeat
            end if
        end repeat
        `
						: ""
				}

        -- Search specified mailboxes
        repeat with mb in mailboxesToSearch
            try
                set unreadMessages to (messages of mb whose read status is false)
                if (count of unreadMessages) > 0 then
                    set msgLimit to ${args.limit || 10}
                    if (count of unreadMessages) < msgLimit then
                        set msgLimit to (count of unreadMessages)
                    end if

                    repeat with i from 1 to msgLimit
                        try
                            set currentMsg to item i of unreadMessages
                            set msgData to {subject:(subject of currentMsg), sender:(sender of currentMsg), ¬
                                        date:(date sent of currentMsg) as string, mailbox:(name of mb)}

                            -- Try to get content if possible
                            try
                                set msgContent to content of currentMsg
                                if length of msgContent > 500 then
                                    set msgContent to (text 1 thru 500 of msgContent) & "..."
                                end if
                                set msgData to msgData & {content:msgContent}
                            on error
                                set msgData to msgData & {content:"[Content not available]"}
                            end try

                            set end of resultList to msgData
                        on error
                            -- Skip problematic messages
                        end try
                    end repeat

                    if (count of resultList) ≥ ${args.limit || 10} then exit repeat
                end if
            on error
                -- Skip problematic mailboxes
            end try
        end repeat
    on error errMsg
        return "Error: " & errMsg
    end try

    return resultList
end tell`;

									try {
										const asResult = await runAppleScript(script);
										if (asResult && asResult.startsWith("Error:")) {
											throw new Error(asResult);
										}

										// Parse the results - similar to general getUnreadMails
										const emailData = [];
										const matches = asResult.match(/\{([^}]+)\}/g);
										if (matches && matches.length > 0) {
											for (const match of matches) {
												try {
													const props = match
														.substring(1, match.length - 1)
														.split(",");
													const email: any = {};

													props.forEach((prop) => {
														const parts = prop.split(":");
														if (parts.length >= 2) {
															const key = parts[0].trim();
															const value = parts.slice(1).join(":").trim();
															email[key] = value;
														}
													});

													if (email.subject || email.sender) {
														emailData.push({
															subject: email.subject || "No subject",
															sender: email.sender || "Unknown sender",
															dateSent: email.date || new Date().toString(),
															content:
																email.content || "[Content not available]",
															isRead: false,
															mailbox: `${args.account} - ${email.mailbox || "Unknown"}`,
														});
													}
												} catch (parseError) {
													console.error(
														"Error parsing email match:",
														parseError,
													);
												}
											}
										}

										emails = emailData;
									} catch (error) {
										console.error(
											"Error getting account-specific emails:",
											error,
										);
										// Fallback to general method if specific account fails
										emails = await mailModule.getUnreadMails(args.limit);
									}
								} else {
									// No account specified, use the general method
									emails = await mailModule.getUnreadMails(args.limit);
								}

								return {
									content: [
										{
											type: "text",
											text:
												emails.length > 0
													? `Found ${emails.length} unread email(s)${args.account ? ` in account "${args.account}"` : ""}${args.mailbox ? ` and mailbox "${args.mailbox}"` : ""}:\n\n` +
														emails
															.map(
																(email: any) =>
																	`[${email.dateSent}] From: ${email.sender}\nMailbox: ${email.mailbox}\nSubject: ${email.subject}\n${email.content.substring(0, 500)}${email.content.length > 500 ? "..." : ""}`,
															)
															.join("\n\n")
													: `No unread emails found${args.account ? ` in account "${args.account}"` : ""}${args.mailbox ? ` and mailbox "${args.mailbox}"` : ""}`,
										},
									],
									isError: false,
								};
							}

							case "search": {
								if (!args.searchTerm) {
									throw new Error(
										"Search term is required for search operation",
									);
								}
								const emails = await mailModule.searchMails(
									args.searchTerm,
									args.limit,
								);
								return {
									content: [
										{
											type: "text",
											text:
												emails.length > 0
													? `Found ${emails.length} email(s) for "${args.searchTerm}"${args.account ? ` in account "${args.account}"` : ""}${args.mailbox ? ` and mailbox "${args.mailbox}"` : ""}:\n\n` +
														emails
															.map(
																(email: any) =>
																	`[${email.dateSent}] From: ${email.sender}\nMailbox: ${email.mailbox}\nSubject: ${email.subject}\n${email.content.substring(0, 200)}${email.content.length > 200 ? "..." : ""}`,
															)
															.join("\n\n")
													: `No emails found for "${args.searchTerm}"${args.account ? ` in account "${args.account}"` : ""}${args.mailbox ? ` and mailbox "${args.mailbox}"` : ""}`,
										},
									],
									isError: false,
								};
							}

							case "send": {
								if (!args.to || !args.subject || !args.body) {
									throw new Error(
										"Recipient (to), subject, and body are required for send operation",
									);
								}
								const result = await mailModule.sendMail(
									args.to,
									args.subject,
									args.body,
									args.cc,
									args.bcc,
								);
								return {
									content: [{ type: "text", text: result }],
									isError: false,
								};
							}

							case "mailboxes": {
								if (args.account) {
									const mailboxes = await mailModule.getMailboxesForAccount(
										args.account,
									);
									return {
										content: [
											{
												type: "text",
												text:
													mailboxes.length > 0
														? `Found ${mailboxes.length} mailboxes for account "${args.account}":\n\n${mailboxes.join("\n")}`
														: `No mailboxes found for account "${args.account}". Make sure the account name is correct.`,
											},
										],
										isError: false,
									};
								} else {
									const mailboxes = await mailModule.getMailboxes();
									return {
										content: [
											{
												type: "text",
												text:
													mailboxes.length > 0
														? `Found ${mailboxes.length} mailboxes:\n\n${mailboxes.join("\n")}`
														: "No mailboxes found. Make sure Mail app is running and properly configured.",
											},
										],
										isError: false,
									};
								}
							}

							case "accounts": {
								const accounts = await mailModule.getAccounts();
								return {
									content: [
										{
											type: "text",
											text:
												accounts.length > 0
													? `Found ${accounts.length} email accounts:\n\n${accounts.join("\n")}`
													: "No email accounts found. Make sure Mail app is configured with at least one account.",
										},
									],
									isError: false,
								};
							}

							case "latest": {
								let account = args.account;
								if (!account) {
									const accounts = await mailModule.getAccounts();
									if (accounts.length === 0) {
										throw new Error(
											"No email accounts found. Make sure Mail app is configured with at least one account.",
										);
									}
									account = accounts[0]; // Use the first account if not provided
								}
								const emails = await mailModule.getLatestMails(
									account,
									args.limit,
								);
								return {
									content: [
										{
											type: "text",
											text:
												emails.length > 0
													? `Found ${emails.length} latest email(s) in account "${account}":\n\n` +
														emails
															.map(
																(email: any) =>
																	`[${email.dateSent}] From: ${email.sender}\nMailbox: ${email.mailbox}\nSubject: ${email.subject}\n${email.content.substring(0, 500)}${email.content.length > 500 ? "..." : ""}`,
															)
															.join("\n\n")
													: `No latest emails found in account "${account}"`,
										},
									],
									isError: false,
								};
							}

							default:
								throw new Error(`Unknown operation: ${args.operation}`);
						}
					} catch (error) {
						const errorMessage = error instanceof Error ? error.message : String(error);
						return {
							content: [
								{
									type: "text",
									text: errorMessage.includes("access") ? errorMessage : `Error with mail operation: ${errorMessage}`,
								},
							],
							isError: true,
						};
					}
				}

				case "reminders": {
					if (!isRemindersArgs(args)) {
						throw new Error("Invalid arguments for reminders tool");
					}

					try {
						const remindersModule = await loadModule("reminders");

						const { operation } = args;

						if (operation === "list") {
							// List all reminders
							const lists = await remindersModule.getAllLists();
							const allReminders = await remindersModule.getAllReminders();
							return {
								content: [
									{
										type: "text",
										text: `Found ${lists.length} lists and ${allReminders.length} reminders.`,
									},
								],
								lists,
								reminders: allReminders,
								isError: false,
							};
						} else if (operation === "search") {
							// Search for reminders
							const { searchText } = args;
							const results = await remindersModule.searchReminders(
								searchText!,
							);
							return {
								content: [
									{
										type: "text",
										text:
											results.length > 0
												? `Found ${results.length} reminders matching "${searchText}".`
												: `No reminders found matching "${searchText}".`,
									},
								],
								reminders: results,
								isError: false,
							};
						} else if (operation === "open") {
							// Open a reminder
							const { searchText } = args;
							const result = await remindersModule.openReminder(searchText!);
							return {
								content: [
									{
										type: "text",
										text: result.success
											? `Opened Reminders app. Found reminder: ${result.reminder?.name}`
											: result.message,
									},
								],
								...result,
								isError: !result.success,
							};
						} else if (operation === "create") {
							// Create a reminder
							const { name, listName, notes, dueDate } = args;
							const result = await remindersModule.createReminder(
								name!,
								listName,
								notes,
								dueDate,
							);
							return {
								content: [
									{
										type: "text",
										text: `Created reminder "${result.name}" ${listName ? `in list "${listName}"` : ""}.`,
									},
								],
								success: true,
								reminder: result,
								isError: false,
							};
						} else if (operation === "listById") {
							// Get reminders from a specific list by ID
							const { listId, props } = args;
							const results = await remindersModule.getRemindersFromListById(
								listId!,
								props,
							);
							return {
								content: [
									{
										type: "text",
										text:
											results.length > 0
												? `Found ${results.length} reminders in list with ID "${listId}".`
												: `No reminders found in list with ID "${listId}".`,
									},
								],
								reminders: results,
								isError: false,
							};
						}

						return {
							content: [
								{
									type: "text",
									text: "Unknown operation",
								},
							],
							isError: true,
						};
					} catch (error) {
						console.error("Error in reminders tool:", error);
						const errorMessage = error instanceof Error ? error.message : String(error);
						return {
							content: [
								{
									type: "text",
									text: errorMessage.includes("access") ? errorMessage : `Error in reminders tool: ${errorMessage}`,
								},
							],
							isError: true,
						};
					}
				}


				case "calendar": {
					if (!isCalendarArgs(args)) {
						throw new Error("Invalid arguments for calendar tool");
					}

					try {
						const calendarModule = await loadModule("calendar");
						const { operation } = args;

						switch (operation) {
							case "search": {
								const { searchText, limit, fromDate, toDate } = args;
								const events = await calendarModule.searchEvents(
									searchText!,
									limit,
									fromDate,
									toDate,
								);

								return {
									content: [
										{
											type: "text",
											text:
												events.length > 0
													? `Found ${events.length} events matching "${searchText}":\n\n${events
															.map(
																(event) =>
																	`${event.title} (${new Date(event.startDate!).toLocaleString()} - ${new Date(event.endDate!).toLocaleString()})\n` +
																	`Location: ${event.location || "Not specified"}\n` +
																	`Calendar: ${event.calendarName}\n` +
																	`ID: ${event.id}\n` +
																	`${event.notes ? `Notes: ${event.notes}\n` : ""}`,
															)
															.join("\n\n")}`
													: `No events found matching "${searchText}".`,
										},
									],
									isError: false,
								};
							}

							case "open": {
								const { eventId } = args;
								const result = await calendarModule.openEvent(eventId!);

								return {
									content: [
										{
											type: "text",
											text: result.success
												? result.message
												: `Error opening event: ${result.message}`,
										},
									],
									isError: !result.success,
								};
							}

							case "list": {
								const { limit, fromDate, toDate } = args;
								const events = await calendarModule.getEvents(
									limit,
									fromDate,
									toDate,
								);

								const startDateText = fromDate
									? new Date(fromDate).toLocaleDateString()
									: "today";
								const endDateText = toDate
									? new Date(toDate).toLocaleDateString()
									: "next 7 days";

								return {
									content: [
										{
											type: "text",
											text:
												events.length > 0
													? `Found ${events.length} events from ${startDateText} to ${endDateText}:\n\n${events
															.map(
																(event) =>
																	`${event.title} (${new Date(event.startDate!).toLocaleString()} - ${new Date(event.endDate!).toLocaleString()})\n` +
																	`Location: ${event.location || "Not specified"}\n` +
																	`Calendar: ${event.calendarName}\n` +
																	`ID: ${event.id}`,
															)
															.join("\n\n")}`
													: `No events found from ${startDateText} to ${endDateText}.`,
										},
									],
									isError: false,
								};
							}

							case "create": {
								const {
									title,
									startDate,
									endDate,
									location,
									notes,
									isAllDay,
									calendarName,
								} = args;
								const result = await calendarModule.createEvent(
									title!,
									startDate!,
									endDate!,
									location,
									notes,
									isAllDay,
									calendarName,
								);
								return {
									content: [
										{
											type: "text",
											text: result.success
												? `${result.message} Event scheduled from ${new Date(startDate!).toLocaleString()} to ${new Date(endDate!).toLocaleString()}${result.eventId ? `\nEvent ID: ${result.eventId}` : ""}`
												: `Error creating event: ${result.message}`,
										},
									],
									isError: !result.success,
								};
							}

							default:
								throw new Error(`Unknown calendar operation: ${operation}`);
						}
					} catch (error) {
						const errorMessage = error instanceof Error ? error.message : String(error);
						return {
							content: [
								{
									type: "text",
									text: errorMessage.includes("access") ? errorMessage : `Error in calendar tool: ${errorMessage}`,
								},
							],
							isError: true,
						};
					}
				}

				case "maps": {
					if (!isMapsArgs(args)) {
						throw new Error("Invalid arguments for maps tool");
					}

					try {
						const mapsModule = await loadModule("maps");
						const { operation } = args;

						switch (operation) {
							case "search": {
								const { query, limit } = args;
								if (!query) {
									throw new Error(
										"Search query is required for search operation",
									);
								}

								const result = await mapsModule.searchLocations(query, limit);

								return {
									content: [
										{
											type: "text",
											text: result.success
												? `${result.message}\n\n${result.locations
														.map(
															(location) =>
																`Name: ${location.name}\n` +
																`Address: ${location.address}\n` +
																`${location.latitude && location.longitude ? `Coordinates: ${location.latitude}, ${location.longitude}\n` : ""}`,
														)
														.join("\n\n")}`
												: `${result.message}`,
										},
									],
									isError: !result.success,
								};
							}

							case "save": {
								const { name, address } = args;
								if (!name || !address) {
									throw new Error(
										"Name and address are required for save operation",
									);
								}

								const result = await mapsModule.saveLocation(name, address);

								return {
									content: [
										{
											type: "text",
											text: result.message,
										},
									],
									isError: !result.success,
								};
							}

							case "pin": {
								const { name, address } = args;
								if (!name || !address) {
									throw new Error(
										"Name and address are required for pin operation",
									);
								}

								const result = await mapsModule.dropPin(name, address);

								return {
									content: [
										{
											type: "text",
											text: result.message,
										},
									],
									isError: !result.success,
								};
							}

							case "directions": {
								const { fromAddress, toAddress, transportType } = args;
								if (!fromAddress || !toAddress) {
									throw new Error(
										"From and to addresses are required for directions operation",
									);
								}

								const result = await mapsModule.getDirections(
									fromAddress,
									toAddress,
									transportType as "driving" | "walking" | "transit",
								);

								return {
									content: [
										{
											type: "text",
											text: result.message,
										},
									],
									isError: !result.success,
								};
							}

							case "listGuides": {
								const result = await mapsModule.listGuides();

								return {
									content: [
										{
											type: "text",
											text: result.message,
										},
									],
									isError: !result.success,
								};
							}

							case "addToGuide": {
								const { address, guideName } = args;
								if (!address || !guideName) {
									throw new Error(
										"Address and guideName are required for addToGuide operation",
									);
								}

								const result = await mapsModule.addToGuide(address, guideName);

								return {
									content: [
										{
											type: "text",
											text: result.message,
										},
									],
									isError: !result.success,
								};
							}

							case "createGuide": {
								const { guideName } = args;
								if (!guideName) {
									throw new Error(
										"Guide name is required for createGuide operation",
									);
								}

								const result = await mapsModule.createGuide(guideName);

								return {
									content: [
										{
											type: "text",
											text: result.message,
										},
									],
									isError: !result.success,
								};
							}

							default:
								throw new Error(`Unknown maps operation: ${operation}`);
						}
					} catch (error) {
						const errorMessage = error instanceof Error ? error.message : String(error);
						return {
							content: [
								{
									type: "text",
									text: errorMessage.includes("access") ? errorMessage : `Error in maps tool: ${errorMessage}`,
								},
							],
							isError: true,
						};
					}
				}

				default:
					return {
						content: [{ type: "text", text: `Unknown tool: ${name}` }],
						isError: true,
					};
			}
		} catch (error) {
			return {
				content: [
					{
						type: "text",
						text: `Error: ${error instanceof Error ? error.message : String(error)}`,
					},
				],
				isError: true,
			};
		}
	});

	// Start the server transport
	console.error("Setting up MCP server transport...");

	(async () => {
		try {
			console.error("Initializing transport...");
			const transport = new StdioServerTransport();

			// Ensure stdout is only used for JSON messages
			console.error("Setting up stdout filter...");
			const originalStdoutWrite = process.stdout.write.bind(process.stdout);
			process.stdout.write = (chunk: any, encoding?: any, callback?: any) => {
				// Only allow JSON messages to pass through
				if (typeof chunk === "string" && !chunk.startsWith("{")) {
					console.error("Filtering non-JSON stdout message");
					return true; // Silently skip non-JSON messages
				}
				return originalStdoutWrite(chunk, encoding, callback);
			};

			console.error("Connecting transport to server...");
			await server.connect(transport);
			console.error("Server connected successfully!");
		} catch (error) {
			console.error("Failed to initialize MCP server:", error);
			process.exit(1);
		}
	})();
}

// Helper functions for argument type checking
function isContactsArgs(args: unknown): args is { name?: string } {
	return (
		typeof args === "object" &&
		args !== null &&
		(!("name" in args) || typeof (args as { name: string }).name === "string")
	);
}

function isNotesArgs(args: unknown): args is {
	operation: "search" | "list" | "create";
	searchText?: string;
	title?: string;
	body?: string;
	folderName?: string;
} {
	if (typeof args !== "object" || args === null) {
		return false;
	}

	const { operation } = args as { operation?: unknown };
	if (typeof operation !== "string") {
		return false;
	}

	if (!["search", "list", "create"].includes(operation)) {
		return false;
	}

	// Validate fields based on operation
	if (operation === "search") {
		const { searchText } = args as { searchText?: unknown };
		if (typeof searchText !== "string" || searchText === "") {
			return false;
		}
	}

	if (operation === "create") {
		const { title, body } = args as { title?: unknown; body?: unknown };
		if (typeof title !== "string" || title === "" || typeof body !== "string") {
			return false;
		}

		// Check folderName if provided
		const { folderName } = args as { folderName?: unknown };
		if (
			folderName !== undefined &&
			(typeof folderName !== "string" || folderName === "")
		) {
			return false;
		}
	}

	return true;
}

function isMessagesArgs(args: unknown): args is {
	operation: "send" | "read" | "schedule" | "unread";
	phoneNumber?: string;
	message?: string;
	limit?: number;
	scheduledTime?: string;
} {
	if (typeof args !== "object" || args === null) return false;

	const { operation, phoneNumber, message, limit, scheduledTime } = args as any;

	if (
		!operation ||
		!["send", "read", "schedule", "unread"].includes(operation)
	) {
		return false;
	}

	// Validate required fields based on operation
	switch (operation) {
		case "send":
		case "schedule":
			if (!phoneNumber || !message) return false;
			if (operation === "schedule" && !scheduledTime) return false;
			break;
		case "read":
			if (!phoneNumber) return false;
			break;
		case "unread":
			// No additional required fields
			break;
	}

	// Validate field types if present
	if (phoneNumber && typeof phoneNumber !== "string") return false;
	if (message && typeof message !== "string") return false;
	if (limit && typeof limit !== "number") return false;
	if (scheduledTime && typeof scheduledTime !== "string") return false;

	return true;
}

function isMailArgs(args: unknown): args is {
	operation: "unread" | "search" | "send" | "mailboxes" | "accounts" | "latest";
	account?: string;
	mailbox?: string;
	limit?: number;
	searchTerm?: string;
	to?: string;
	subject?: string;
	body?: string;
	cc?: string;
	bcc?: string;
} {
	if (typeof args !== "object" || args === null) return false;

	const {
		operation,
		account,
		mailbox,
		limit,
		searchTerm,
		to,
		subject,
		body,
		cc,
		bcc,
	} = args as any;

	if (
		!operation ||
		!["unread", "search", "send", "mailboxes", "accounts", "latest"].includes(
			operation,
		)
	) {
		return false;
	}

	// Validate required fields based on operation
	switch (operation) {
		case "search":
			if (!searchTerm || typeof searchTerm !== "string") return false;
			break;
		case "send":
			if (
				!to ||
				typeof to !== "string" ||
				!subject ||
				typeof subject !== "string" ||
				!body ||
				typeof body !== "string"
			)
				return false;
			break;
		case "unread":
		case "mailboxes":
		case "accounts":
		case "latest":
			// No additional required fields
			break;
	}

	// Validate field types if present
	if (account && typeof account !== "string") return false;
	if (mailbox && typeof mailbox !== "string") return false;
	if (limit && typeof limit !== "number") return false;
	if (cc && typeof cc !== "string") return false;
	if (bcc && typeof bcc !== "string") return false;

	return true;
}

function isRemindersArgs(args: unknown): args is {
	operation: "list" | "search" | "open" | "create" | "listById";
	searchText?: string;
	name?: string;
	listName?: string;
	listId?: string;
	props?: string[];
	notes?: string;
	dueDate?: string;
} {
	if (typeof args !== "object" || args === null) {
		return false;
	}

	const { operation } = args as any;
	if (typeof operation !== "string") {
		return false;
	}

	if (!["list", "search", "open", "create", "listById"].includes(operation)) {
		return false;
	}

	// For search and open operations, searchText is required
	if (
		(operation === "search" || operation === "open") &&
		(typeof (args as any).searchText !== "string" ||
			(args as any).searchText === "")
	) {
		return false;
	}

	// For create operation, name is required
	if (
		operation === "create" &&
		(typeof (args as any).name !== "string" || (args as any).name === "")
	) {
		return false;
	}

	// For listById operation, listId is required
	if (
		operation === "listById" &&
		(typeof (args as any).listId !== "string" || (args as any).listId === "")
	) {
		return false;
	}

	return true;
}


function isCalendarArgs(args: unknown): args is {
	operation: "search" | "open" | "list" | "create";
	searchText?: string;
	eventId?: string;
	limit?: number;
	fromDate?: string;
	toDate?: string;
	title?: string;
	startDate?: string;
	endDate?: string;
	location?: string;
	notes?: string;
	isAllDay?: boolean;
	calendarName?: string;
} {
	if (typeof args !== "object" || args === null) {
		return false;
	}

	const { operation } = args as { operation?: unknown };
	if (typeof operation !== "string") {
		return false;
	}

	if (!["search", "open", "list", "create"].includes(operation)) {
		return false;
	}

	// Check that required parameters are present for each operation
	if (operation === "search") {
		const { searchText } = args as { searchText?: unknown };
		if (typeof searchText !== "string") {
			return false;
		}
	}

	if (operation === "open") {
		const { eventId } = args as { eventId?: unknown };
		if (typeof eventId !== "string") {
			return false;
		}
	}

	if (operation === "create") {
		const { title, startDate, endDate } = args as {
			title?: unknown;
			startDate?: unknown;
			endDate?: unknown;
		};

		if (
			typeof title !== "string" ||
			typeof startDate !== "string" ||
			typeof endDate !== "string"
		) {
			return false;
		}
	}

	return true;
}

function isMapsArgs(args: unknown): args is {
	operation:
		| "search"
		| "save"
		| "directions"
		| "pin"
		| "listGuides"
		| "addToGuide"
		| "createGuide";
	query?: string;
	limit?: number;
	name?: string;
	address?: string;
	fromAddress?: string;
	toAddress?: string;
	transportType?: string;
	guideName?: string;
} {
	if (typeof args !== "object" || args === null) {
		return false;
	}

	const { operation } = args as { operation?: unknown };
	if (typeof operation !== "string") {
		return false;
	}

	if (
		![
			"search",
			"save",
			"directions",
			"pin",
			"listGuides",
			"addToGuide",
			"createGuide",
		].includes(operation)
	) {
		return false;
	}

	// Check that required parameters are present for each operation
	if (operation === "search") {
		const { query } = args as { query?: unknown };
		if (typeof query !== "string" || query === "") {
			return false;
		}
	}

	if (operation === "save" || operation === "pin") {
		const { name, address } = args as { name?: unknown; address?: unknown };
		if (
			typeof name !== "string" ||
			name === "" ||
			typeof address !== "string" ||
			address === ""
		) {
			return false;
		}
	}

	if (operation === "directions") {
		const { fromAddress, toAddress } = args as {
			fromAddress?: unknown;
			toAddress?: unknown;
		};
		if (
			typeof fromAddress !== "string" ||
			fromAddress === "" ||
			typeof toAddress !== "string" ||
			toAddress === ""
		) {
			return false;
		}

		// Check transportType if provided
		const { transportType } = args as { transportType?: unknown };
		if (
			transportType !== undefined &&
			(typeof transportType !== "string" ||
				!["driving", "walking", "transit"].includes(transportType))
		) {
			return false;
		}
	}

	if (operation === "createGuide") {
		const { guideName } = args as { guideName?: unknown };
		if (typeof guideName !== "string" || guideName === "") {
			return false;
		}
	}

	if (operation === "addToGuide") {
		const { address, guideName } = args as {
			address?: unknown;
			guideName?: unknown;
		};
		if (
			typeof address !== "string" ||
			address === "" ||
			typeof guideName !== "string" ||
			guideName === ""
		) {
			return false;
		}
	}

	return true;
}

```
Page 2/2FirstPrevNextLast