UNPKG

omnifocus-mcp-enhanced

Version:

🚀 NEW: Native Custom Perspective Access! Enhanced MCP server with OmniFocus custom perspective support, hierarchical task display, AI-optimized tool selection, and comprehensive task management

114 lines (109 loc) • 4.25 kB
import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); /** * Generate pure AppleScript for project creation */ function generateAppleScript(params) { // Sanitize and prepare parameters for AppleScript const name = params.name.replace(/['"\\]/g, '\\$&'); // Escape quotes and backslashes const note = params.note?.replace(/['"\\]/g, '\\$&') || ''; const dueDate = params.dueDate || ''; const deferDate = params.deferDate || ''; const flagged = params.flagged === true; const estimatedMinutes = params.estimatedMinutes?.toString() || ''; const tags = params.tags || []; const folderName = params.folderName?.replace(/['"\\]/g, '\\$&') || ''; const sequential = params.sequential === true; // Construct AppleScript with error handling let script = ` try tell application "OmniFocus" tell front document -- Determine the container (root or folder) if "${folderName}" is "" then -- Create project at the root level set newProject to make new project with properties {name:"${name}"} else -- Use specified folder try set theFolder to first flattened folder where name = "${folderName}" set newProject to make new project with properties {name:"${name}"} at end of projects of theFolder on error return "{\\\"success\\\":false,\\\"error\\\":\\\"Folder not found: ${folderName}\\\"}" end try end if -- Set project properties ${note ? `set note of newProject to "${note}"` : ''} ${dueDate ? ` set due date of newProject to (current date) + (time to GMT) set due date of newProject to date "${dueDate}"` : ''} ${deferDate ? ` set defer date of newProject to (current date) + (time to GMT) set defer date of newProject to date "${deferDate}"` : ''} ${flagged ? `set flagged of newProject to true` : ''} ${estimatedMinutes ? `set estimated minutes of newProject to ${estimatedMinutes}` : ''} ${`set sequential of newProject to ${sequential}`} -- Get the project ID set projectId to id of newProject as string -- Add tags if provided ${tags.length > 0 ? tags.map(tag => { const sanitizedTag = tag.replace(/['"\\]/g, '\\$&'); return ` try set theTag to first flattened tag where name = "${sanitizedTag}" tell newProject to add theTag on error -- Ignore errors finding/adding tags end try`; }).join('\n') : ''} -- Return success with project ID return "{\\\"success\\\":true,\\\"projectId\\\":\\"" & projectId & "\\",\\\"name\\\":\\"${name}\\"}" end tell end tell on error errorMessage return "{\\\"success\\\":false,\\\"error\\\":\\"" & errorMessage & "\\"}" end try `; return script; } /** * Add a project to OmniFocus */ export async function addProject(params) { try { // Generate AppleScript const script = generateAppleScript(params); console.error("Executing AppleScript directly..."); // Execute AppleScript directly const { stdout, stderr } = await execAsync(`osascript -e '${script}'`); if (stderr) { console.error("AppleScript stderr:", stderr); } console.error("AppleScript stdout:", stdout); // Parse the result try { const result = JSON.parse(stdout); // Return the result return { success: result.success, projectId: result.projectId, error: result.error }; } catch (parseError) { console.error("Error parsing AppleScript result:", parseError); return { success: false, error: `Failed to parse result: ${stdout}` }; } } catch (error) { console.error("Error in addProject:", error); return { success: false, error: error?.message || "Unknown error in addProject" }; } }