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
184 lines (146 loc) • 6 kB
Markdown
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
- `npm run build` - 编译 TypeScript 并复制脚本文件到 dist/ 目录
- `npm run dev` - 开发模式,监听文件变化并实时编译
- `npm start` - 运行编译后的服务器
- `npm run copy-files` - 复制 OmniFocus 脚本文件到 dist 目录
- `npm test` - 运行测试(当前只是占位符,需要手动测试)
## Architecture Overview
这是一个基于 TypeScript 的 Model Context Protocol (MCP) 服务器,专门用于 OmniFocus 集成。
你给用户输出的东西,不要包含表情符号,这是一个严肃的项目
### 核心架构层次:
1. **服务器层** (`src/server.ts`) - MCP 服务器主入口,注册所有工具
2. **工具定义层** (`src/tools/definitions/`) - 定义工具的 schema 和处理器
3. **基础功能层** (`src/tools/primitives/`) - 实际的业务逻辑实现
4. **脚本执行层** (`src/utils/scriptExecution.ts`) - JXA/OmniJS 脚本执行引擎
5. **OmniFocus 脚本层** (`src/utils/omnifocusScripts/`) - 具体的 OmniFocus 操作脚本
### 工具分类:
- **数据库管理**: dump_database
- **任务管理**: add_omnifocus_task, remove_item, edit_item, get_task_by_id
- **项目管理**: add_project
- **批量操作**: batch_add_items, batch_remove_items
- **透视图**: get_inbox_tasks, get_flagged_tasks, get_forecast_tasks, get_tasks_by_tag
- **高级过滤**: filter_tasks
- **自定义透视图**: list_custom_perspectives (基于 OmniJS Perspective.Custom.all API)
- **完成任务**: get_today_completed_tasks
## 重要约束
- 不要使用 AppleScript 来解决问题,使用 JXA (JavaScript for Automation) 或 OmniJS
- 仅支持 macOS 平台(依赖 OmniFocus 应用)
- 需要 Node.js 18+ 环境
- 所有脚本文件必须在构建时复制到 dist/ 目录
- **CLAUDE.md 是私人约定文件,不要提交到 git**
## 代码结构规范
### 添加新工具的步骤:
1. 在 `src/tools/definitions/` 创建工具定义文件
2. 在 `src/tools/primitives/` 实现具体功能
3. 在 `src/server.ts` 中注册新工具
4. 如需要,在 `src/utils/omnifocusScripts/` 添加相应的 JXA 脚本
### 工具定义模式:
```typescript
// 在 definitions/ 文件中
export const schema = z.object({...});
export const handler = async (args: any) => {
// 调用 primitives/ 中的实现
};
```
## 脚本执行机制
使用 `src/utils/scriptExecution.ts` 来执行 JXA/OmniJS 脚本:
- JXA 脚本通过 `osascript -l JavaScript` 执行
- OmniJS 脚本通过 OmniFocus 的脚本引擎执行
- 脚本文件必须保存在 `src/utils/omnifocusScripts/` 目录
## 类型定义
所有类型定义在 `src/types.ts` 中,包括:
- OmniFocus 对象类型(Task, Project, Context等)
- 工具参数和返回值类型
- 脚本执行结果类型
## 踩坑记录和解决方案
### ❌ 重大错误:executeOmniFocusScript 返回值处理
**错误现象**: 工具返回 "脚本执行返回了无效的结果"
**问题根源**: `executeOmniFocusScript` 函数可能返回两种类型:
1. **JSON 对象** - 当脚本执行成功且 JSON.parse 成功时
2. **字符串** - 当 JSON.parse 失败时,返回原始 stdout
**我犯的错误**:
```typescript
// ❌ 只处理了字符串情况,忽略了对象类型
if (typeof result === 'string') {
const data = JSON.parse(result);
// 处理...
}
throw new Error('脚本执行返回了无效的结果'); // 对象类型会跳到这里!
```
**正确处理方式**:
```typescript
// ✅ 同时处理字符串和对象两种情况
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}`);
}
```
**调试技巧**:
- 先用 console.log 打印 result 的类型和值
- 添加详细的错误信息,包含实际的返回值
- 不要假设返回类型,要做完整的类型检查
### ✅ OmniJS 脚本最佳实践
**统一的脚本格式模板**:
```javascript
(() => {
try {
// 获取数据的业务逻辑
const customPerspectives = Perspective.Custom.all;
// 格式化结果
const perspectives = customPerspectives.map(p => ({
name: p.name,
identifier: p.identifier
}));
// 返回统一格式
const result = {
success: true,
count: perspectives.length,
perspectives: perspectives
};
return JSON.stringify(result);
} catch (error) {
// 错误处理
const errorResult = {
success: false,
error: error.message || String(error),
count: 0,
perspectives: []
};
return JSON.stringify(errorResult);
}
})();
```
**关键要点**:
- 用 IIFE `(() => {})()` 包装避免全局污染
- 统一返回 JSON 字符串格式
- 必须包含 `success` 字段指示成功/失败
- 错误处理要完整,包含错误信息
- 数据结构要一致,便于 TypeScript 处理
### 🔧 工具开发流程教训
**正确的开发顺序**:
1. 先写 OmniJS 脚本并单独测试
2. 实现 primitive 函数,处理返回值
3. 创建工具定义,定义 schema
4. 在 server.ts 中注册新工具
5. 编译测试,逐步调试
**重要提醒**:
- 遇到错误先加详细的 console.log 调试日志
- 不要积累太多问题,每一步都要测试
- 错误信息要包含实际的数据类型和值
- primitive 函数要处理所有可能的返回类型
### 🎯 成功案例:list_custom_perspectives
基于 `Perspective.Custom.all` API 成功实现的工具:
- OmniJS 脚本:使用原生 API 获取透视列表
- 支持 simple 和 detailed 两种格式
- 完整的错误处理和类型检查
- 编译和运行都正常