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
136 lines (116 loc) • 4.32 kB
JavaScript
// OmniJS script to get tasks by tag from OmniFocus
(() => {
try {
// Use default values since parameters are not easily available in JXA mode
const tagName = "编程"; // Default for testing
const hideCompleted = true; // Default to true
const exactMatch = false;
if (!tagName) {
return JSON.stringify({
success: false,
error: "Tag name is required"
});
}
// Helper function to format dates consistently
function formatDate(date) {
if (!date) return null;
return date.toISOString();
}
// Get task status enum mapping
const taskStatusMap = {
[Task.Status.Available]: "Available",
[Task.Status.Blocked]: "Blocked",
[Task.Status.Completed]: "Completed",
[Task.Status.Dropped]: "Dropped",
[Task.Status.DueSoon]: "DueSoon",
[Task.Status.Next]: "Next",
[Task.Status.Overdue]: "Overdue"
};
function getTaskStatus(status) {
return taskStatusMap[status] || "Unknown";
}
const exportData = {
exportDate: new Date().toISOString(),
searchTag: tagName,
exactMatch: exactMatch,
matchedTags: [],
tasks: [],
availableTags: []
};
// Get all active tags for reference
const allTags = flattenedTags.filter(tag => tag.active);
exportData.availableTags = allTags.map(tag => tag.name).sort();
console.log(`Searching for tags matching "${tagName}" (exact: ${exactMatch})`);
// Find matching tags
let matchingTags = [];
if (exactMatch) {
matchingTags = allTags.filter(tag =>
tag.name.toLowerCase() === tagName.toLowerCase()
);
} else {
matchingTags = allTags.filter(tag =>
tag.name.toLowerCase().includes(tagName.toLowerCase())
);
}
exportData.matchedTags = matchingTags.map(tag => tag.name);
console.log(`Found ${matchingTags.length} matching tags: ${exportData.matchedTags.join(', ')}`);
if (matchingTags.length === 0) {
console.log("No matching tags found");
return JSON.stringify(exportData);
}
// Get all tasks that have any of the matching tags
let matchingTasks = [];
matchingTags.forEach(tag => {
const tasksWithTag = tag.tasks;
console.log(`Tag "${tag.name}" has ${tasksWithTag.length} tasks`);
tasksWithTag.forEach(task => {
// Avoid duplicates (a task might have multiple matching tags)
if (!matchingTasks.find(t => t.id.primaryKey === task.id.primaryKey)) {
matchingTasks.push(task);
}
});
});
console.log(`Found ${matchingTasks.length} unique tasks with matching tags`);
// Filter by completion status if needed
if (hideCompleted) {
matchingTasks = matchingTasks.filter(task =>
task.taskStatus !== Task.Status.Completed &&
task.taskStatus !== Task.Status.Dropped
);
}
console.log(`Processing ${matchingTasks.length} tasks after filtering completed`);
// Process each matching task
matchingTasks.forEach(task => {
try {
const taskData = {
id: task.id.primaryKey,
name: task.name,
note: task.note || "",
taskStatus: getTaskStatus(task.taskStatus),
flagged: task.flagged,
dueDate: formatDate(task.dueDate),
deferDate: formatDate(task.deferDate),
estimatedMinutes: task.estimatedMinutes,
projectId: task.containingProject ? task.containingProject.id.primaryKey : null,
projectName: task.containingProject ? task.containingProject.name : null,
inInbox: task.inInbox,
tags: task.tags.map(tag => ({
id: tag.id.primaryKey,
name: tag.name
}))
};
exportData.tasks.push(taskData);
} catch (taskError) {
console.log(`Error processing task with tag: ${taskError}`);
}
});
console.log(`Successfully processed ${exportData.tasks.length} tasks with matching tags`);
return JSON.stringify(exportData);
} catch (error) {
console.error(`Error in tasksByTag script: ${error}`);
return JSON.stringify({
success: false,
error: `Error getting tasks by tag: ${error}`
});
}
})();