Skip to main content
Glama
jqlts1

OmniFocus MCP Enhanced

by jqlts1

dump_database

Retrieve the current state of your OmniFocus database, with options to hide completed tasks or recurring duplicates, enabling efficient task management and organization.

Instructions

Gets the current state of your OmniFocus database

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
hideCompletedNoSet to false to show completed and dropped tasks (default: true)
hideRecurringDuplicatesNoSet to true to hide duplicate instances of recurring tasks (default: true)

Implementation Reference

  • src/server.ts:34-39 (registration)
    Registers the 'dump_database' tool in the MCP server using the schema and handler exported from './tools/definitions/dumpDatabase.js'.
    server.tool( "dump_database", "Gets the current state of your OmniFocus database", dumpDatabaseTool.schema.shape, dumpDatabaseTool.handler );
  • Zod schema for the tool input parameters: optional booleans to control hiding of completed tasks and recurring duplicates.
    export const schema = z.object({ hideCompleted: z.boolean().optional().describe("Set to false to show completed and dropped tasks (default: true)"), hideRecurringDuplicates: z.boolean().optional().describe("Set to true to hide duplicate instances of recurring tasks (default: true)") });
  • Tool handler that dumps the OmniFocus database using dumpDatabase(), applies formatting with options, and returns a compact text report. Handles errors gracefully.
    export async function handler(args: z.infer<typeof schema>, extra: RequestHandlerExtra) { try { // Get raw database const database = await dumpDatabase(); // Format as compact report const formattedReport = formatCompactReport(database, { hideCompleted: args.hideCompleted !== false, // Default to true hideRecurringDuplicates: args.hideRecurringDuplicates !== false // Default to true }); return { content: [{ type: "text" as const, text: formattedReport }] }; } catch (err: unknown) { return { content: [{ type: "text" as const, text: `Error generating report. Please ensure OmniFocus is running and try again.` }], isError: true }; } }
  • Helper function that executes AppleScript to dump the raw OmniFocus database and transforms it into a structured OmnifocusDatabase object used by the tool handler.
    export async function dumpDatabase(): Promise<OmnifocusDatabase> { try { // Execute the OmniFocus script const data = await executeOmniFocusScript('@omnifocusDump.js') as OmnifocusDumpData; // wait 1 second await new Promise(resolve => setTimeout(resolve, 1000)); // Create an empty database if no data returned if (!data) { return { exportDate: new Date().toISOString(), tasks: [], projects: {}, folders: {}, tags: {} }; } // Initialize the database object const database: OmnifocusDatabase = { exportDate: data.exportDate, tasks: [], projects: {}, folders: {}, tags: {} }; // Process tasks if (data.tasks && Array.isArray(data.tasks)) { // Convert the tasks to our OmnifocusTask format database.tasks = data.tasks.map((task: OmnifocusDumpTask) => { // Get tag names from the tag IDs const tagNames = (task.tags || []).map(tagId => { return data.tags[tagId]?.name || 'Unknown Tag'; }); return { id: String(task.id), name: String(task.name), note: String(task.note || ""), flagged: Boolean(task.flagged), completed: task.taskStatus === "Completed", completionDate: null, // Not available in the new format dropDate: null, // Not available in the new format taskStatus: String(task.taskStatus), active: task.taskStatus !== "Completed" && task.taskStatus !== "Dropped", dueDate: task.dueDate, deferDate: task.deferDate, estimatedMinutes: task.estimatedMinutes ? Number(task.estimatedMinutes) : null, tags: task.tags || [], tagNames: tagNames, parentId: task.parentTaskID || null, containingProjectId: task.projectID || null, projectId: task.projectID || null, childIds: task.children || [], hasChildren: (task.children && task.children.length > 0) || false, sequential: Boolean(task.sequential), completedByChildren: Boolean(task.completedByChildren), isRepeating: false, // Not available in the new format repetitionMethod: null, // Not available in the new format repetitionRule: null, // Not available in the new format attachments: [], // Default empty array linkedFileURLs: [], // Default empty array notifications: [], // Default empty array shouldUseFloatingTimeZone: false // Default value }; }); } // Process projects if (data.projects) { for (const [id, project] of Object.entries(data.projects)) { database.projects[id] = { id: String(project.id), name: String(project.name), status: String(project.status), folderID: project.folderID || null, sequential: Boolean(project.sequential), effectiveDueDate: project.effectiveDueDate, effectiveDeferDate: project.effectiveDeferDate, dueDate: project.dueDate, deferDate: project.deferDate, completedByChildren: Boolean(project.completedByChildren), containsSingletonActions: Boolean(project.containsSingletonActions), note: String(project.note || ""), tasks: project.tasks || [], flagged: false, // Default value estimatedMinutes: null // Default value }; } } // Process folders if (data.folders) { for (const [id, folder] of Object.entries(data.folders)) { database.folders[id] = { id: String(folder.id), name: String(folder.name), parentFolderID: folder.parentFolderID || null, status: String(folder.status), projects: folder.projects || [], subfolders: folder.subfolders || [] }; } } // Process tags if (data.tags) { for (const [id, tag] of Object.entries(data.tags)) { database.tags[id] = { id: String(tag.id), name: String(tag.name), parentTagID: tag.parentTagID || null, active: Boolean(tag.active), allowsNextAction: Boolean(tag.allowsNextAction), tasks: tag.tasks || [] }; } } return database; } catch (error) { console.error("Error in dumpDatabase:", error); throw error; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jqlts1/omnifocus-mcp-enhanced'

If you have feedback or need assistance with the MCP directory API, please join our Discord server