Skip to main content
Glama

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);
      }
    })();
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds useful context about hierarchical tree display and clarifies the nature of custom perspectives, but doesn't address important behavioral aspects like whether this is a read-only operation, potential rate limits, authentication requirements, or error conditions. The description provides some behavioral context but leaves significant gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is efficiently structured in two sentences that each serve clear purposes: the first establishes the core functionality and usage context, the second adds important behavioral context about hierarchical display. There's no wasted text, though it could be slightly more front-loaded with the core purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with 4 parameters, no annotations, and no output schema, the description provides good usage context and distinguishes from siblings, but leaves significant gaps in behavioral transparency (no mention of read/write nature, error handling, or response format). The combination of good purpose clarity and usage guidelines with incomplete behavioral disclosure results in a moderately complete description.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the schema already fully documents all 4 parameters. The description adds minimal value beyond the schema - it mentions hierarchical tree display (which maps to showHierarchy) and perspective names (which maps to perspectiveName), but doesn't provide additional semantic context or usage examples beyond what's already in the schema descriptions. This meets the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Get tasks') from a specific resource ('OmniFocus custom perspective by name'), and explicitly distinguishes it from sibling tools by noting these are 'custom views created in OmniFocus, NOT tags' (differentiating from get_tasks_by_tag). The verb+resource combination is precise and unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance on when to use this tool ('when user refers to perspective names like '今日工作安排', '今日复盘', '本周项目' etc.') and clarifies what NOT to use it for ('these are custom views... NOT tags'), effectively distinguishing it from alternatives like get_tasks_by_tag. This gives clear contextual boundaries for tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

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