Skip to main content
Glama
jqlts1

OmniFocus MCP Enhanced

by jqlts1

get_custom_perspective_tasks

Retrieve tasks from a specific OmniFocus custom perspective by name, showing hierarchical task relationships or flat views. Exclude or include completed tasks as needed for focused task management.

Instructions

Get tasks from a specific OmniFocus custom perspective by name. Use this when user refers to perspective names like '今日工作安排', '今日复盘', '本周项目' etc. - these are custom views created in OmniFocus, NOT tags. Supports hierarchical tree display of task relationships.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
hideCompletedNoWhether to hide completed tasks. Set to false to show all tasks including completed ones (default: true)
limitNoMaximum number of tasks to return in flat view mode (default: 1000, ignored in hierarchy mode)
perspectiveNameYesExact name of the OmniFocus custom perspective (e.g., '今日工作安排', '今日复盘', '本周项目'). This is NOT a tag name.
showHierarchyNoDisplay tasks in hierarchical tree structure showing parent-child relationships. Use this when user wants '层级显示' or 'tree view' (default: false)

Implementation Reference

  • MCP tool handler: wrapper that calls the primitive getCustomPerspectiveTasks with processed args and returns MCP-formatted text content or error.
    export async function handler(args: z.infer<typeof schema>, extra: RequestHandlerExtra) { try { const result = await getCustomPerspectiveTasks({ perspectiveName: args.perspectiveName, hideCompleted: args.hideCompleted !== false, // Default to true limit: args.limit || 1000, showHierarchy: args.showHierarchy || false // Default to false }); return { content: [{ type: "text" as const, text: result }] }; } catch (err: unknown) { const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred'; return { content: [{ type: "text" as const, text: `Error getting custom perspective tasks: ${errorMessage}` }], isError: true }; } }
  • Zod schema defining input parameters for the tool: perspectiveName (required), hideCompleted, limit, showHierarchy.
    export const schema = z.object({ perspectiveName: z.string().describe("Exact name of the OmniFocus custom perspective (e.g., '今日工作安排', '今日复盘', '本周项目'). This is NOT a tag name."), hideCompleted: z.boolean().optional().describe("Whether to hide completed tasks. Set to false to show all tasks including completed ones (default: true)"), limit: z.number().optional().describe("Maximum number of tasks to return in flat view mode (default: 1000, ignored in hierarchy mode)"), showHierarchy: z.boolean().optional().describe("Display tasks in hierarchical tree structure showing parent-child relationships. Use this when user wants '层级显示' or 'tree view' (default: false)") });
  • src/server.ts:143-148 (registration)
    Tool registration in MCP server using name, description, schema.shape, and handler from definitions module.
    server.tool( "get_custom_perspective_tasks", "Get tasks from a specific OmniFocus custom perspective by name. Use this when user refers to perspective names like '今日工作安排', '今日复盘', '本周项目' etc. - these are custom views created in OmniFocus, NOT tags. Supports hierarchical tree display of task relationships.", getCustomPerspectiveTasksTool.schema.shape, getCustomPerspectiveTasksTool.handler );
  • Core primitive function: executes OmniFocus script, parses results, filters tasks, and formats output as hierarchical tree or flat list.
    export async function getCustomPerspectiveTasks(options: GetCustomPerspectiveTasksOptions): Promise<string> { const { perspectiveName, hideCompleted = true, limit = 1000, showHierarchy = false } = options; if (!perspectiveName) { return "❌ **错误**: 透视名称不能为空"; } try { // Execute the get custom perspective tasks script const result = await executeOmniFocusScript('@getCustomPerspectiveTasks.js', { perspectiveName: perspectiveName }); // 处理各种可能的返回类型(避免之前的错误) let data: any; if (typeof result === 'string') { try { data = JSON.parse(result); } catch (parseError) { throw new Error(`解析字符串结果失败: ${result}`); } } else if (typeof result === 'object' && result !== null) { data = result; } else { throw new Error(`脚本执行返回了无效的结果类型: ${typeof result}, 值: ${result}`); } // 检查是否有错误 if (!data.success) { throw new Error(data.error || 'Unknown error occurred'); } // 处理taskMap数据(新的层级结构) const taskMap = data.taskMap || {}; const allTasks = Object.values(taskMap); // 过滤已完成任务(如果需要) let filteredTasks = allTasks; if (hideCompleted) { filteredTasks = allTasks.filter((task: any) => !task.completed); } if (filteredTasks.length === 0) { return `**透视任务:${perspectiveName}**\n\n暂无${hideCompleted ? '未完成' : ''}任务。`; } // 根据是否显示层级关系选择不同的输出格式 if (showHierarchy) { return formatHierarchicalTasks(perspectiveName, taskMap, hideCompleted); } else { return formatFlatTasks(perspectiveName, filteredTasks, limit, data.count); } } catch (error) { console.error('Error in getCustomPerspectiveTasks:', error); return `❌ **错误**: ${error instanceof Error ? error.message : String(error)}`; } }
  • OmniFocus AppleScript/JS: switches to custom perspective, traverses content tree, collects tasks with parent-child relationships into taskMap, returns JSON.
    // 通过自定义透视名称获取任务(支持层级关系) // 基于用户提供的优秀代码改进 (() => { try { // 获取注入的参数 const perspectiveName = injectedArgs && injectedArgs.perspectiveName ? injectedArgs.perspectiveName : null; if (!perspectiveName) { throw new Error("透视名称不能为空"); } // 通过名称获取自定义透视 let perspective = Perspective.Custom.byName(perspectiveName); if (!perspective) { throw new Error(`未找到名为 "${perspectiveName}" 的自定义透视`); } // 切换到指定透视 document.windows[0].perspective = perspective; // 用于存储所有任务,key为任务ID(支持层级关系) let taskMap = {}; // 遍历内容树,收集任务信息(含层级关系) let rootNode = document.windows[0].content.rootNode; function collectTasks(node, parentId) { if (node.object && node.object instanceof Task) { let t = node.object; let id = t.id.primaryKey; // 记录任务信息(包含层级关系) taskMap[id] = { id: id, name: t.name, note: t.note || "", project: t.project ? t.project.name : null, tags: t.tags ? t.tags.map(tag => tag.name) : [], dueDate: t.dueDate ? t.dueDate.toISOString() : null, deferDate: t.deferDate ? t.deferDate.toISOString() : null, completed: t.completed, flagged: t.flagged, estimatedMinutes: t.estimatedMinutes || null, repetitionRule: t.repetitionRule ? t.repetitionRule.toString() : null, creationDate: t.added ? t.added.toISOString() : null, completionDate: t.completedDate ? t.completedDate.toISOString() : null, parent: parentId, // 父任务ID children: [], // 子任务ID列表,后面补充 }; // 递归收集子任务 node.children.forEach(childNode => { if (childNode.object && childNode.object instanceof Task) { let childId = childNode.object.id.primaryKey; taskMap[id].children.push(childId); collectTasks(childNode, id); } else { collectTasks(childNode, id); } }); } else { // 不是任务节点,递归子节点 node.children.forEach(childNode => collectTasks(childNode, parentId)); } } // 开始收集任务(根任务的parent为null) if (rootNode && rootNode.children) { rootNode.children.forEach(node => collectTasks(node, null)); } // 计算任务总数 const taskCount = Object.keys(taskMap).length; // 返回结果(包含层级结构) const result = { success: true, perspectiveName: perspectiveName, perspectiveId: perspective.identifier, count: taskCount, taskMap: taskMap }; return JSON.stringify(result); } catch (error) { // 错误处理 const errorResult = { success: false, error: error.message || String(error), perspectiveName: perspectiveName || null, perspectiveId: null, count: 0, taskMap: {} }; return JSON.stringify(errorResult); } })();

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