omnifocus-mcp
Version:
Model Context Protocol (MCP) server that integrates with OmniFocus for AI assistant interaction
108 lines (107 loc) ⢠4.84 kB
JavaScript
import { z } from 'zod';
import { getPerspectiveView } from '../primitives/getPerspectiveView.js';
export const schema = z.object({
perspectiveName: z.string().describe("Perspective name, built-in ('Inbox', 'Flagged', ā¦) or custom"),
limit: z.number().optional().describe("Max items to return (default: 100)"),
includeMetadata: z.boolean().optional().describe("Include project names, tags, dates (default: true)"),
fields: z.array(z.string()).optional().describe("Only return the listed fields: id, name, note, flagged, dueDate, deferDate, completionDate, taskStatus, projectName, tagNames, estimatedMinutes")
});
export async function handler(args, extra) {
try {
const result = await getPerspectiveView({
perspectiveName: args.perspectiveName,
limit: args.limit ?? 100,
includeMetadata: args.includeMetadata ?? true,
fields: args.fields
});
if (result.success) {
const items = result.items || [];
// Format the output
let output = `## ${args.perspectiveName} Perspective (${items.length} items)\n\n`;
if (items.length === 0) {
output += "No items visible in this perspective.";
}
else {
// Format each item
items.forEach(item => {
const parts = [];
// Core display
const flag = item.flagged ? 'š© ' : '';
const checkbox = item.completed ? 'ā' : 'ā';
parts.push(`${checkbox} ${flag}${item.name || 'Unnamed'}`);
// Project context
if (item.projectName) {
parts.push(`(${item.projectName})`);
}
// Due date
if (item.dueDate) {
const date = new Date(item.dueDate);
const dateStr = `${date.getMonth() + 1}/${date.getDate()}`;
parts.push(`[due: ${dateStr}]`);
}
// Defer date
if (item.deferDate) {
const date = new Date(item.deferDate);
const dateStr = `${date.getMonth() + 1}/${date.getDate()}`;
parts.push(`[defer: ${dateStr}]`);
}
// Time estimate
if (item.estimatedMinutes) {
const hours = item.estimatedMinutes >= 60
? `${Math.floor(item.estimatedMinutes / 60)}h${item.estimatedMinutes % 60 > 0 ? (item.estimatedMinutes % 60) + 'm' : ''}`
: `${item.estimatedMinutes}m`;
parts.push(`(${hours})`);
}
// Tags
if (item.tagNames && item.tagNames.length > 0) {
parts.push(`<${item.tagNames.join(',')}>`);
}
// Status
if (item.taskStatus && item.taskStatus !== 'Available') {
parts.push(`#${item.taskStatus.toLowerCase()}`);
}
// ID for reference
if (item.id) {
parts.push(`[${item.id}]`);
}
output += `⢠${parts.join(' ')}\n`;
// Add note preview if present and not too long
if (item.note && item.note.trim()) {
const notePreview = item.note.trim().split('\n')[0].substring(0, 80);
const ellipsis = item.note.length > 80 || item.note.includes('\n') ? '...' : '';
output += ` āā ${notePreview}${ellipsis}\n`;
}
});
}
if (items.length === args.limit) {
output += `\nā ļø Results limited to ${args.limit} items. More may be available in this perspective.`;
}
return {
content: [{
type: "text",
text: output
}]
};
}
else {
return {
content: [{
type: "text",
text: `Failed to get perspective view: ${result.error}`
}],
isError: true
};
}
}
catch (err) {
const error = err;
console.error(`Error getting perspective view: ${error.message}`);
return {
content: [{
type: "text",
text: `Error getting perspective view: ${error.message}`
}],
isError: true
};
}
}