@scardis/omnifocus-mcp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/dist/schemas/shapes.d.ts +3 -33
  2. package/dist/schemas/shapes.d.ts.map +1 -1
  3. package/dist/schemas/shapes.js +3 -5
  4. package/dist/schemas/shapes.js.map +1 -1
  5. package/dist/server.js +3 -5
  6. package/dist/server.js.map +1 -1
  7. package/dist/tools/createTask.d.ts +1 -11
  8. package/dist/tools/createTask.d.ts.map +1 -1
  9. package/dist/tools/editTask.d.ts +1 -11
  10. package/dist/tools/editTask.d.ts.map +1 -1
  11. package/dist/tools/index.d.ts +2 -22
  12. package/dist/tools/index.d.ts.map +1 -1
  13. package/package.json +7 -1
  14. package/src/snippets/edit_task.js +4 -6
  15. package/.claude/commands/opsx/apply.md +0 -152
  16. package/.claude/commands/opsx/archive.md +0 -157
  17. package/.claude/commands/opsx/bulk-archive.md +0 -242
  18. package/.claude/commands/opsx/continue.md +0 -114
  19. package/.claude/commands/opsx/explore.md +0 -173
  20. package/.claude/commands/opsx/ff.md +0 -97
  21. package/.claude/commands/opsx/new.md +0 -69
  22. package/.claude/commands/opsx/onboard.md +0 -550
  23. package/.claude/commands/opsx/propose.md +0 -106
  24. package/.claude/commands/opsx/sync.md +0 -134
  25. package/.claude/commands/opsx/verify.md +0 -164
  26. package/.claude/skills/openspec-apply-change/SKILL.md +0 -156
  27. package/.claude/skills/openspec-archive-change/SKILL.md +0 -114
  28. package/.claude/skills/openspec-bulk-archive-change/SKILL.md +0 -246
  29. package/.claude/skills/openspec-continue-change/SKILL.md +0 -118
  30. package/.claude/skills/openspec-explore/SKILL.md +0 -288
  31. package/.claude/skills/openspec-ff-change/SKILL.md +0 -101
  32. package/.claude/skills/openspec-new-change/SKILL.md +0 -74
  33. package/.claude/skills/openspec-onboard/SKILL.md +0 -554
  34. package/.claude/skills/openspec-propose/SKILL.md +0 -110
  35. package/.claude/skills/openspec-sync-specs/SKILL.md +0 -138
  36. package/.claude/skills/openspec-verify-change/SKILL.md +0 -168
  37. package/CONTRIBUTING.md +0 -83
  38. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/.openspec.yaml +0 -2
  39. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/design.md +0 -162
  40. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/proposal.md +0 -49
  41. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/attachments/spec.md +0 -9
  42. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/batch-operations/spec.md +0 -9
  43. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/database-inspection/spec.md +0 -9
  44. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/execution-runtime/spec.md +0 -69
  45. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/folder-management/spec.md +0 -25
  46. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/forecast/spec.md +0 -9
  47. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/identity-resolution/spec.md +0 -45
  48. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/perspective-management/spec.md +0 -9
  49. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/project-management/spec.md +0 -25
  50. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/recurrence/spec.md +0 -9
  51. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/settings/spec.md +0 -9
  52. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/tag-management/spec.md +0 -25
  53. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/task-management/spec.md +0 -29
  54. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/url-automation/spec.md +0 -9
  55. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/specs/window-state/spec.md +0 -9
  56. package/openspec/changes/archive/2026-04-09-bootstrap-omnifocus-mcp/tasks.md +0 -84
  57. package/openspec/changes/archive/2026-04-09-folder-crud/.openspec.yaml +0 -2
  58. package/openspec/changes/archive/2026-04-09-folder-crud/design.md +0 -58
  59. package/openspec/changes/archive/2026-04-09-folder-crud/proposal.md +0 -28
  60. package/openspec/changes/archive/2026-04-09-folder-crud/specs/folder-write/spec.md +0 -45
  61. package/openspec/changes/archive/2026-04-09-folder-crud/tasks.md +0 -41
  62. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/.openspec.yaml +0 -2
  63. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/design.md +0 -38
  64. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/proposal.md +0 -30
  65. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/folder-management/spec.md +0 -21
  66. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/specs/tag-management/spec.md +0 -21
  67. package/openspec/changes/archive/2026-04-09-folder-tag-list-filtering/tasks.md +0 -35
  68. package/openspec/changes/archive/2026-04-09-move-operations/.openspec.yaml +0 -2
  69. package/openspec/changes/archive/2026-04-09-move-operations/design.md +0 -43
  70. package/openspec/changes/archive/2026-04-09-move-operations/proposal.md +0 -25
  71. package/openspec/changes/archive/2026-04-09-move-operations/specs/move-operations/spec.md +0 -41
  72. package/openspec/changes/archive/2026-04-09-move-operations/tasks.md +0 -40
  73. package/openspec/changes/archive/2026-04-09-project-crud/.openspec.yaml +0 -2
  74. package/openspec/changes/archive/2026-04-09-project-crud/design.md +0 -60
  75. package/openspec/changes/archive/2026-04-09-project-crud/proposal.md +0 -29
  76. package/openspec/changes/archive/2026-04-09-project-crud/specs/project-write/spec.md +0 -74
  77. package/openspec/changes/archive/2026-04-09-project-crud/tasks.md +0 -48
  78. package/openspec/changes/archive/2026-04-09-project-filtering/.openspec.yaml +0 -2
  79. package/openspec/changes/archive/2026-04-09-project-filtering/design.md +0 -52
  80. package/openspec/changes/archive/2026-04-09-project-filtering/proposal.md +0 -26
  81. package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-filtering/spec.md +0 -66
  82. package/openspec/changes/archive/2026-04-09-project-filtering/specs/project-management/spec.md +0 -13
  83. package/openspec/changes/archive/2026-04-09-project-filtering/tasks.md +0 -41
  84. package/openspec/changes/archive/2026-04-09-tag-crud/.openspec.yaml +0 -2
  85. package/openspec/changes/archive/2026-04-09-tag-crud/design.md +0 -45
  86. package/openspec/changes/archive/2026-04-09-tag-crud/proposal.md +0 -28
  87. package/openspec/changes/archive/2026-04-09-tag-crud/specs/tag-write/spec.md +0 -49
  88. package/openspec/changes/archive/2026-04-09-tag-crud/tasks.md +0 -41
  89. package/openspec/changes/archive/2026-04-09-task-crud/.openspec.yaml +0 -2
  90. package/openspec/changes/archive/2026-04-09-task-crud/design.md +0 -62
  91. package/openspec/changes/archive/2026-04-09-task-crud/proposal.md +0 -29
  92. package/openspec/changes/archive/2026-04-09-task-crud/specs/task-management/spec.md +0 -17
  93. package/openspec/changes/archive/2026-04-09-task-crud/specs/task-write/spec.md +0 -89
  94. package/openspec/changes/archive/2026-04-09-task-crud/tasks.md +0 -55
  95. package/openspec/changes/archive/2026-04-09-task-filtering/.openspec.yaml +0 -2
  96. package/openspec/changes/archive/2026-04-09-task-filtering/design.md +0 -61
  97. package/openspec/changes/archive/2026-04-09-task-filtering/proposal.md +0 -26
  98. package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-filtering/spec.md +0 -63
  99. package/openspec/changes/archive/2026-04-09-task-filtering/specs/task-management/spec.md +0 -17
  100. package/openspec/changes/archive/2026-04-09-task-filtering/tasks.md +0 -42
  101. package/openspec/changes/archive/2026-04-10-planned-date/.openspec.yaml +0 -2
  102. package/openspec/changes/archive/2026-04-10-planned-date/design.md +0 -27
  103. package/openspec/changes/archive/2026-04-10-planned-date/proposal.md +0 -29
  104. package/openspec/changes/archive/2026-04-10-planned-date/specs/task-management/spec.md +0 -29
  105. package/openspec/changes/archive/2026-04-10-planned-date/specs/task-write/spec.md +0 -69
  106. package/openspec/changes/archive/2026-04-10-planned-date/tasks.md +0 -26
  107. package/openspec/changes/archive/2026-04-10-task-recurrence/.openspec.yaml +0 -2
  108. package/openspec/changes/archive/2026-04-10-task-recurrence/design.md +0 -81
  109. package/openspec/changes/archive/2026-04-10-task-recurrence/proposal.md +0 -28
  110. package/openspec/changes/archive/2026-04-10-task-recurrence/specs/recurrence/spec.md +0 -47
  111. package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-management/spec.md +0 -25
  112. package/openspec/changes/archive/2026-04-10-task-recurrence/specs/task-write/spec.md +0 -61
  113. package/openspec/changes/archive/2026-04-10-task-recurrence/tasks.md +0 -39
  114. package/openspec/config.yaml +0 -20
  115. package/openspec/specs/attachments/spec.md +0 -15
  116. package/openspec/specs/batch-operations/spec.md +0 -15
  117. package/openspec/specs/database-inspection/spec.md +0 -15
  118. package/openspec/specs/execution-runtime/spec.md +0 -75
  119. package/openspec/specs/folder-management/spec.md +0 -39
  120. package/openspec/specs/folder-write/spec.md +0 -45
  121. package/openspec/specs/forecast/spec.md +0 -15
  122. package/openspec/specs/identity-resolution/spec.md +0 -51
  123. package/openspec/specs/move-operations/spec.md +0 -41
  124. package/openspec/specs/perspective-management/spec.md +0 -15
  125. package/openspec/specs/project-filtering/spec.md +0 -72
  126. package/openspec/specs/project-management/spec.md +0 -31
  127. package/openspec/specs/project-write/spec.md +0 -79
  128. package/openspec/specs/recurrence/spec.md +0 -51
  129. package/openspec/specs/settings/spec.md +0 -15
  130. package/openspec/specs/tag-management/spec.md +0 -39
  131. package/openspec/specs/tag-write/spec.md +0 -49
  132. package/openspec/specs/task-filtering/spec.md +0 -63
  133. package/openspec/specs/task-management/spec.md +0 -51
  134. package/openspec/specs/task-write/spec.md +0 -115
  135. package/openspec/specs/url-automation/spec.md +0 -15
  136. package/openspec/specs/window-state/spec.md +0 -15
  137. package/scripts/cleanup-fixtures.ts +0 -89
  138. package/server.json +0 -21
  139. package/src/runtime/bridge.ts +0 -97
  140. package/src/runtime/index.ts +0 -4
  141. package/src/runtime/jxaShim.ts +0 -55
  142. package/src/runtime/resultProtocol.ts +0 -62
  143. package/src/runtime/snippetLoader.ts +0 -79
  144. package/src/schemas/enums.ts +0 -32
  145. package/src/schemas/index.ts +0 -38
  146. package/src/schemas/shapes.ts +0 -267
  147. package/src/server.ts +0 -58
  148. package/src/tools/completeProject.ts +0 -21
  149. package/src/tools/completeTask.ts +0 -23
  150. package/src/tools/createFolder.ts +0 -20
  151. package/src/tools/createProject.ts +0 -20
  152. package/src/tools/createTag.ts +0 -20
  153. package/src/tools/createTask.ts +0 -20
  154. package/src/tools/deleteFolder.ts +0 -24
  155. package/src/tools/deleteProject.ts +0 -24
  156. package/src/tools/deleteTag.ts +0 -24
  157. package/src/tools/deleteTask.ts +0 -26
  158. package/src/tools/dropProject.ts +0 -21
  159. package/src/tools/dropTask.ts +0 -23
  160. package/src/tools/editFolder.ts +0 -19
  161. package/src/tools/editProject.ts +0 -20
  162. package/src/tools/editTag.ts +0 -20
  163. package/src/tools/editTask.ts +0 -20
  164. package/src/tools/getFolder.ts +0 -24
  165. package/src/tools/getProject.ts +0 -24
  166. package/src/tools/getTag.ts +0 -24
  167. package/src/tools/getTask.ts +0 -24
  168. package/src/tools/index.ts +0 -85
  169. package/src/tools/listFolders.ts +0 -32
  170. package/src/tools/listProjects.ts +0 -32
  171. package/src/tools/listTags.ts +0 -32
  172. package/src/tools/listTasks.ts +0 -56
  173. package/src/tools/moveProject.ts +0 -20
  174. package/src/tools/moveTask.ts +0 -20
  175. package/src/tools/resolveName.ts +0 -37
  176. package/test/integration/.gitkeep +0 -0
  177. package/test/integration/completeProject.int.test.ts +0 -25
  178. package/test/integration/completeTask.int.test.ts +0 -30
  179. package/test/integration/createFolder.int.test.ts +0 -50
  180. package/test/integration/createProject.int.test.ts +0 -49
  181. package/test/integration/createTag.int.test.ts +0 -52
  182. package/test/integration/createTask.int.test.ts +0 -55
  183. package/test/integration/deleteFolder.int.test.ts +0 -64
  184. package/test/integration/deleteProject.int.test.ts +0 -31
  185. package/test/integration/deleteTag.int.test.ts +0 -61
  186. package/test/integration/deleteTask.int.test.ts +0 -36
  187. package/test/integration/dropProject.int.test.ts +0 -24
  188. package/test/integration/dropTask.int.test.ts +0 -29
  189. package/test/integration/editFolder.int.test.ts +0 -43
  190. package/test/integration/editProject.int.test.ts +0 -39
  191. package/test/integration/editTag.int.test.ts +0 -43
  192. package/test/integration/editTask.int.test.ts +0 -56
  193. package/test/integration/fixtures.ts +0 -219
  194. package/test/integration/getTask.int.test.ts +0 -64
  195. package/test/integration/listFoldersFiltered.int.test.ts +0 -98
  196. package/test/integration/listProjects.int.test.ts +0 -73
  197. package/test/integration/listProjectsFiltered.int.test.ts +0 -96
  198. package/test/integration/listTagsFiltered.int.test.ts +0 -54
  199. package/test/integration/listTasksFiltered.int.test.ts +0 -141
  200. package/test/integration/moveProject.int.test.ts +0 -57
  201. package/test/integration/moveTask.int.test.ts +0 -61
  202. package/test/integration/plannedDate.int.test.ts +0 -72
  203. package/test/integration/preflight.ts +0 -60
  204. package/test/integration/resolveName.int.test.ts +0 -86
  205. package/test/integration/taskRecurrence.int.test.ts +0 -106
  206. package/test/unit/.gitkeep +0 -0
  207. package/test/unit/bridge.injection.test.ts +0 -66
  208. package/test/unit/resultProtocol.test.ts +0 -71
  209. package/test/unit/schemas.createFolder.test.ts +0 -38
  210. package/test/unit/schemas.createProject.test.ts +0 -115
  211. package/test/unit/schemas.createTask.test.ts +0 -87
  212. package/test/unit/schemas.editTag.test.ts +0 -64
  213. package/test/unit/schemas.folderTagFiltering.test.ts +0 -42
  214. package/test/unit/schemas.listProjects.test.ts +0 -44
  215. package/test/unit/schemas.moveOperations.test.ts +0 -60
  216. package/test/unit/schemas.recurrence.test.ts +0 -120
  217. package/test/unit/schemas.test.ts +0 -126
  218. package/test/unit/snippetLoader.test.ts +0 -56
  219. package/test/unit/tools.deleteTask.test.ts +0 -19
  220. package/test/unit/tools.listTasks.test.ts +0 -126
  221. package/tsconfig.json +0 -19
  222. package/vitest.config.ts +0 -8
  223. package/vitest.integration.config.ts +0 -18
@@ -1,55 +0,0 @@
1
- /**
2
- * JXA wrapper template. The `__SNIPPET__` placeholder is replaced with the
3
- * OmniJS snippet to execute. The resulting script is passed to
4
- * `osascript -l JavaScript` via stdin.
5
- *
6
- * This shim:
7
- * - Calls Application('OmniFocus').evaluateJavascript() with the snippet
8
- * - Wraps in try/catch
9
- * - Prints exactly one line of JSON: {ok, data} | {ok: false, error: {...}}
10
- *
11
- * The JXA scripting dictionary is used ONLY for evaluateJavascript.
12
- * No other scripting-dictionary domain methods are invoked here.
13
- */
14
- export const JXA_SHIM_TEMPLATE = `
15
- (function() {
16
- var snippet = __SNIPPET__;
17
- var app = Application('OmniFocus');
18
- app.includeStandardAdditions = true;
19
- try {
20
- var result = app.evaluateJavascript(snippet);
21
- // result is already a JSON string produced by the snippet
22
- $.NSFileHandle.fileHandleWithStandardOutput.writeData(
23
- $.NSString.alloc.initWithString(result + '\\n')
24
- .dataUsingEncoding($.NSUTF8StringEncoding)
25
- );
26
- } catch(e) {
27
- var errName = e.name || 'Error';
28
- var errMsg = e.message || String(e);
29
- // OmniJS wraps thrown errors; the original name is embedded in the message
30
- // as "SomeName: original message". Recover it when the name is generic.
31
- var nameMatch = errMsg.match(/^([A-Z]\\w*Error): ([\\s\\S]*)$/);
32
- if (nameMatch) { errName = nameMatch[1]; errMsg = nameMatch[2]; }
33
- var err = JSON.stringify({
34
- ok: false,
35
- error: {
36
- name: errName,
37
- message: errMsg
38
- }
39
- });
40
- $.NSFileHandle.fileHandleWithStandardOutput.writeData(
41
- $.NSString.alloc.initWithString(err + '\\n')
42
- .dataUsingEncoding($.NSUTF8StringEncoding)
43
- );
44
- }
45
- })();
46
- `;
47
-
48
- /**
49
- * Build the complete JXA script to pass to osascript.
50
- * The snippet is embedded as a JS string literal via JSON.stringify,
51
- * so it is safe to contain any characters including quotes, backslashes, etc.
52
- */
53
- export function buildJxaScript(snippet: string): string {
54
- return JXA_SHIM_TEMPLATE.replace("__SNIPPET__", JSON.stringify(snippet));
55
- }
@@ -1,62 +0,0 @@
1
- import { z } from "zod";
2
-
3
- const ErrorDetail = z.object({
4
- name: z.string(),
5
- message: z.string(),
6
- stack: z.string().optional(),
7
- });
8
-
9
- const SuccessEnvelope = z.object({
10
- ok: z.literal(true),
11
- data: z.unknown(),
12
- });
13
-
14
- const ErrorEnvelope = z.object({
15
- ok: z.literal(false),
16
- error: ErrorDetail,
17
- });
18
-
19
- const Envelope = z.discriminatedUnion("ok", [SuccessEnvelope, ErrorEnvelope]);
20
-
21
- export type ResultEnvelope = z.infer<typeof Envelope>;
22
-
23
- export class ExecutionError extends Error {
24
- readonly errorName: string;
25
- readonly remoteStack?: string;
26
-
27
- constructor(detail: z.infer<typeof ErrorDetail>) {
28
- super(detail.message);
29
- this.name = "ExecutionError";
30
- this.errorName = detail.name;
31
- this.remoteStack = detail.stack;
32
- }
33
- }
34
-
35
- /**
36
- * Scans stdout for the first JSON-parseable line and validates it as a result
37
- * envelope. Ignores any preceding non-JSON chatter (e.g. osascript warnings).
38
- */
39
- export function parseResultLine(stdout: string): ResultEnvelope {
40
- const lines = stdout.split("\n");
41
- for (const line of lines) {
42
- const trimmed = line.trim();
43
- if (!trimmed) continue;
44
- let parsed: unknown;
45
- try {
46
- parsed = JSON.parse(trimmed);
47
- } catch {
48
- continue; // not JSON, skip
49
- }
50
- const result = Envelope.safeParse(parsed);
51
- if (result.success) {
52
- return result.data;
53
- }
54
- // Parsed as JSON but not a valid envelope — this is a protocol error
55
- throw new Error(
56
- `Bridge returned JSON that is not a valid result envelope: ${trimmed}`
57
- );
58
- }
59
- throw new Error(
60
- `No valid JSON result envelope found in osascript output:\n${stdout}`
61
- );
62
- }
@@ -1,79 +0,0 @@
1
- import { readFileSync } from "fs";
2
- import { join, dirname } from "path";
3
- import { fileURLToPath } from "url";
4
-
5
- const __dirname = dirname(fileURLToPath(import.meta.url));
6
- const SNIPPETS_DIR = join(__dirname, "..", "snippets");
7
-
8
- // Allowlist of valid snippet names. Adding a new snippet requires an explicit
9
- // entry here, which prevents path-traversal attacks if a snippet name ever
10
- // reaches this loader from a dynamic source.
11
- const ALLOWED_SNIPPETS = new Set([
12
- "get_folder",
13
- "get_project",
14
- "get_tag",
15
- "get_task",
16
- "list_folders",
17
- "list_projects",
18
- "list_tags",
19
- "list_tasks",
20
- "resolve_name",
21
- "create_task",
22
- "edit_task",
23
- "complete_task",
24
- "drop_task",
25
- "delete_task",
26
- "create_project",
27
- "edit_project",
28
- "complete_project",
29
- "drop_project",
30
- "delete_project",
31
- "create_folder",
32
- "edit_folder",
33
- "delete_folder",
34
- "create_tag",
35
- "edit_tag",
36
- "delete_tag",
37
- "move_task",
38
- "move_project",
39
- ]);
40
-
41
- const cache = new Map<string, string>();
42
-
43
- export function loadSnippet(name: string): string {
44
- if (!ALLOWED_SNIPPETS.has(name)) {
45
- throw new Error(`Unknown snippet: "${name}"`);
46
- }
47
-
48
- if (cache.has(name)) {
49
- return cache.get(name)!;
50
- }
51
-
52
- const filePath = join(SNIPPETS_DIR, `${name}.js`);
53
- let content: string;
54
- try {
55
- content = readFileSync(filePath, "utf-8");
56
- } catch {
57
- throw new Error(`Snippet "${name}" could not be loaded`);
58
- }
59
-
60
- const matches = content.split("__ARGS__").length - 1;
61
- if (matches === 0) {
62
- throw new Error(
63
- `Snippet "${name}" contains no __ARGS__ placeholder. Every snippet must have exactly one.`
64
- );
65
- }
66
- if (matches > 1) {
67
- throw new Error(
68
- `Snippet "${name}" contains ${matches} __ARGS__ placeholders. Exactly one is required.`
69
- );
70
- }
71
-
72
- cache.set(name, content);
73
- return content;
74
- }
75
-
76
- /** For testing: clear the in-memory cache. */
77
- export function clearSnippetCache(): void {
78
- cache.clear();
79
- }
@@ -1,32 +0,0 @@
1
- import { z } from "zod";
2
-
3
- export const IdSchema = z.string().min(1, "ID must be a non-empty string");
4
-
5
- export const EntityType = z.enum([
6
- "task",
7
- "project",
8
- "folder",
9
- "tag",
10
- "perspective",
11
- ]);
12
-
13
- export const ProjectType = z.enum(["parallel", "sequential", "singleActions"]);
14
-
15
- export const ProjectStatus = z.enum(["active", "onHold", "done", "dropped"]);
16
-
17
- export const TaskStatus = z.enum([
18
- "available",
19
- "incomplete",
20
- "completedByChildren",
21
- "complete",
22
- "dropped",
23
- "dueSoon",
24
- "overdue",
25
- "flagged",
26
- "blocked",
27
- "next",
28
- ]);
29
-
30
- export const TagStatus = z.enum(["active", "onHold", "dropped"]);
31
-
32
- export const FolderStatus = z.enum(["active", "dropped"]);
@@ -1,38 +0,0 @@
1
- export {
2
- IdSchema,
3
- EntityType,
4
- ProjectType,
5
- ProjectStatus,
6
- TaskStatus,
7
- TagStatus,
8
- FolderStatus,
9
- } from "./enums.js";
10
-
11
- export {
12
- RepetitionRuleInput,
13
- RepetitionRuleDetail,
14
- TaskSummary,
15
- ListTasksFilter,
16
- TaskDetail,
17
- CreateTaskInput,
18
- EditTaskInput,
19
- MoveTaskInput,
20
- ReviewIntervalInput,
21
- CreateProjectInput,
22
- EditProjectInput,
23
- MoveProjectInput,
24
- ListProjectsFilter,
25
- ProjectSummary,
26
- ProjectDetail,
27
- ListFoldersFilter,
28
- CreateFolderInput,
29
- EditFolderInput,
30
- FolderSummary,
31
- FolderDetail,
32
- ListTagsFilter,
33
- CreateTagInput,
34
- EditTagInput,
35
- TagSummary,
36
- TagDetail,
37
- ResolveCandidate,
38
- } from "./shapes.js";
@@ -1,267 +0,0 @@
1
- import { z } from "zod";
2
- import {
3
- IdSchema,
4
- ProjectType,
5
- ProjectStatus,
6
- TaskStatus,
7
- TagStatus,
8
- FolderStatus,
9
- EntityType,
10
- } from "./enums.js";
11
-
12
- // ─── Recurrence ──────────────────────────────────────────────────────────────
13
-
14
- const RepetitionFrequency = z.enum(["daily", "weekly", "monthly", "yearly"]);
15
- const RepetitionMethod = z.enum(["fixed", "dueDate", "start"]);
16
- const DayOfWeek = z.enum(["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"]);
17
-
18
- export const RepetitionRuleInput = z
19
- .object({
20
- frequency: RepetitionFrequency,
21
- interval: z.number().int().positive().default(1),
22
- daysOfWeek: z.array(DayOfWeek).optional(),
23
- method: RepetitionMethod,
24
- })
25
- .refine(
26
- (d) => d.daysOfWeek === undefined || d.frequency === "weekly",
27
- { message: "daysOfWeek is only valid when frequency is 'weekly'" }
28
- );
29
-
30
- export const RepetitionRuleDetail = z.object({
31
- frequency: RepetitionFrequency,
32
- interval: z.number(),
33
- daysOfWeek: z.array(DayOfWeek).optional(),
34
- method: RepetitionMethod,
35
- });
36
-
37
- // ─── Task ────────────────────────────────────────────────────────────────────
38
-
39
- export const TaskSummary = z.object({
40
- id: IdSchema,
41
- name: z.string(),
42
- status: TaskStatus,
43
- flagged: z.boolean(),
44
- containerId: IdSchema.nullable(),
45
- containerType: z.enum(["project", "inbox", "task"]).nullable(),
46
- dueDate: z.string().datetime().nullable(),
47
- tagIds: z.array(IdSchema),
48
- });
49
-
50
- export const ListTasksFilter = z.object({
51
- flagged: z.literal(true).optional(),
52
- status: z.array(TaskStatus).optional(),
53
- tagId: IdSchema.optional(),
54
- dueBeforeDate: z.string().datetime().optional(),
55
- });
56
-
57
- export const TaskDetail = z.object({
58
- id: IdSchema,
59
- name: z.string(),
60
- note: z.string(),
61
- status: TaskStatus,
62
- flagged: z.boolean(),
63
- deferDate: z.string().datetime().nullable(),
64
- plannedDate: z.string().datetime().nullable(),
65
- dueDate: z.string().datetime().nullable(),
66
- completionDate: z.string().datetime().nullable(),
67
- estimatedMinutes: z.number().nullable(),
68
- containerId: IdSchema.nullable(),
69
- containerType: z.enum(["project", "inbox", "task"]).nullable(),
70
- tagIds: z.array(IdSchema),
71
- parentTaskId: IdSchema.nullable(),
72
- repetitionRule: RepetitionRuleDetail.nullable(),
73
- });
74
-
75
- export const CreateTaskInput = z
76
- .object({
77
- name: z.string().min(1),
78
- note: z.string().optional(),
79
- flagged: z.boolean().optional(),
80
- deferDate: z.string().datetime().optional(),
81
- plannedDate: z.string().datetime().optional(),
82
- dueDate: z.string().datetime().optional(),
83
- estimatedMinutes: z.number().int().positive().optional(),
84
- projectId: IdSchema.optional(),
85
- parentTaskId: IdSchema.optional(),
86
- tagIds: z.array(IdSchema).optional(),
87
- repetitionRule: RepetitionRuleInput.optional(),
88
- })
89
- .refine((d) => !(d.projectId && d.parentTaskId), {
90
- message: "Provide projectId or parentTaskId, not both",
91
- });
92
-
93
- export const EditTaskInput = z.object({
94
- id: IdSchema,
95
- name: z.string().min(1).optional(),
96
- note: z.string().optional(),
97
- flagged: z.boolean().optional(),
98
- deferDate: z.string().datetime().nullable().optional(),
99
- plannedDate: z.string().datetime().nullable().optional(),
100
- dueDate: z.string().datetime().nullable().optional(),
101
- estimatedMinutes: z.number().int().positive().nullable().optional(),
102
- tagIds: z.array(IdSchema).optional(),
103
- repetitionRule: RepetitionRuleInput.nullable().optional(),
104
- });
105
-
106
- // ─── Project ─────────────────────────────────────────────────────────────────
107
-
108
- export const ReviewIntervalInput = z.object({
109
- steps: z.number().int().positive(),
110
- unit: z.enum(["days", "weeks", "months", "years"]),
111
- });
112
-
113
- export const CreateProjectInput = z.object({
114
- name: z.string().min(1),
115
- folderId: IdSchema.optional(),
116
- note: z.string().optional(),
117
- type: ProjectType.optional(),
118
- status: z.enum(["active", "onHold"]).optional(),
119
- flagged: z.boolean().optional(),
120
- deferDate: z.string().datetime().optional(),
121
- dueDate: z.string().datetime().optional(),
122
- reviewInterval: ReviewIntervalInput.optional(),
123
- tagIds: z.array(IdSchema).optional(),
124
- });
125
-
126
- export const EditProjectInput = z.object({
127
- id: IdSchema,
128
- name: z.string().min(1).optional(),
129
- note: z.string().optional(),
130
- type: ProjectType.optional(),
131
- status: z.enum(["active", "onHold"]).optional(),
132
- flagged: z.boolean().optional(),
133
- deferDate: z.string().datetime().nullable().optional(),
134
- dueDate: z.string().datetime().nullable().optional(),
135
- reviewInterval: ReviewIntervalInput.optional(),
136
- tagIds: z.array(IdSchema).optional(),
137
- });
138
-
139
- export const MoveTaskInput = z
140
- .object({
141
- id: IdSchema,
142
- projectId: IdSchema.optional(),
143
- parentTaskId: IdSchema.optional(),
144
- })
145
- .refine(
146
- (d) => (d.projectId !== undefined) !== (d.parentTaskId !== undefined),
147
- { message: "Exactly one of projectId or parentTaskId must be provided" }
148
- );
149
-
150
- export const MoveProjectInput = z.object({
151
- id: IdSchema,
152
- folderId: IdSchema.nullable(),
153
- });
154
-
155
- export const ListFoldersFilter = z.object({
156
- status: FolderStatus.optional(),
157
- });
158
-
159
- export const ListTagsFilter = z.object({
160
- status: TagStatus.optional(),
161
- });
162
-
163
- export const ListProjectsFilter = z.object({
164
- status: z.array(ProjectStatus).optional(),
165
- folderId: IdSchema.optional(),
166
- flagged: z.literal(true).optional(),
167
- });
168
-
169
- export const ProjectSummary = z.object({
170
- id: IdSchema,
171
- name: z.string(),
172
- folderPath: z.string(),
173
- folderId: IdSchema.nullable(),
174
- status: ProjectStatus,
175
- type: ProjectType,
176
- flagged: z.boolean(),
177
- });
178
-
179
- export const ProjectDetail = z.object({
180
- id: IdSchema,
181
- name: z.string(),
182
- note: z.string(),
183
- folderPath: z.string(),
184
- status: ProjectStatus,
185
- type: ProjectType,
186
- flagged: z.boolean(),
187
- deferDate: z.string().datetime().nullable(),
188
- dueDate: z.string().datetime().nullable(),
189
- completionDate: z.string().datetime().nullable(),
190
- reviewInterval: z.string().nullable(),
191
- nextReviewDate: z.string().datetime().nullable(),
192
- lastReviewDate: z.string().datetime().nullable(),
193
- tagIds: z.array(IdSchema),
194
- });
195
-
196
- export const CreateFolderInput = z.object({
197
- name: z.string().min(1),
198
- parentFolderId: IdSchema.optional(),
199
- });
200
-
201
- export const EditFolderInput = z.object({
202
- id: IdSchema,
203
- name: z.string().min(1),
204
- });
205
-
206
- // ─── Folder ──────────────────────────────────────────────────────────────────
207
-
208
- export const FolderSummary = z.object({
209
- id: IdSchema,
210
- name: z.string(),
211
- path: z.string(),
212
- parentId: IdSchema.nullable(),
213
- status: FolderStatus,
214
- });
215
-
216
- export const FolderDetail = z.object({
217
- id: IdSchema,
218
- name: z.string(),
219
- path: z.string(),
220
- parentId: IdSchema.nullable(),
221
- status: FolderStatus,
222
- childFolderIds: z.array(IdSchema),
223
- projectIds: z.array(IdSchema),
224
- });
225
-
226
- export const CreateTagInput = z.object({
227
- name: z.string().min(1),
228
- parentTagId: IdSchema.optional(),
229
- });
230
-
231
- export const EditTagInput = z
232
- .object({
233
- id: IdSchema,
234
- name: z.string().min(1).optional(),
235
- status: TagStatus.optional(),
236
- })
237
- .refine((d) => d.name !== undefined || d.status !== undefined, {
238
- message: "Provide at least one of name or status",
239
- });
240
-
241
- // ─── Tag ─────────────────────────────────────────────────────────────────────
242
-
243
- export const TagSummary = z.object({
244
- id: IdSchema,
245
- name: z.string(),
246
- path: z.string(),
247
- parentId: IdSchema.nullable(),
248
- status: TagStatus,
249
- });
250
-
251
- export const TagDetail = z.object({
252
- id: IdSchema,
253
- name: z.string(),
254
- path: z.string(),
255
- parentId: IdSchema.nullable(),
256
- status: TagStatus,
257
- childTagIds: z.array(IdSchema),
258
- });
259
-
260
- // ─── Resolution ──────────────────────────────────────────────────────────────
261
-
262
- export const ResolveCandidate = z.object({
263
- id: IdSchema,
264
- name: z.string(),
265
- path: z.string(),
266
- type: EntityType,
267
- });
package/src/server.ts DELETED
@@ -1,58 +0,0 @@
1
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { z } from "zod";
4
- import { allTools } from "./tools/index.js";
5
-
6
- const server = new McpServer({
7
- name: "omnifocus-mcp",
8
- version: "0.1.0",
9
- });
10
-
11
- for (const tool of allTools) {
12
- // Extract the shape from the zod object schema for registerTool.
13
- // .refine() wraps in ZodEffects which has no .shape; unwrap via ._def.schema first.
14
- const baseSchema =
15
- tool.inputSchema instanceof z.ZodEffects
16
- ? (tool.inputSchema._def.schema as z.AnyZodObject)
17
- : (tool.inputSchema as z.AnyZodObject);
18
- const inputShape = baseSchema.shape;
19
-
20
- server.registerTool(
21
- tool.name,
22
- {
23
- description: tool.description,
24
- inputSchema: inputShape,
25
- },
26
- async (args: Record<string, unknown>) => {
27
- try {
28
- // Validate input (already parsed by MCP SDK, re-parse for our shape)
29
- const input = tool.inputSchema.parse(args);
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- const result = await (tool.handler as (input: any) => Promise<unknown>)(input);
32
- return {
33
- content: [
34
- {
35
- type: "text" as const,
36
- text: JSON.stringify(result, null, 2),
37
- },
38
- ],
39
- };
40
- } catch (err) {
41
- const message =
42
- err instanceof Error ? err.message : String(err);
43
- return {
44
- content: [
45
- {
46
- type: "text" as const,
47
- text: `Error: ${message}`,
48
- },
49
- ],
50
- isError: true,
51
- };
52
- }
53
- }
54
- );
55
- }
56
-
57
- const transport = new StdioServerTransport();
58
- await server.connect(transport);
@@ -1,21 +0,0 @@
1
- import { z } from "zod";
2
- import { runSnippet } from "../runtime/index.js";
3
- import { IdSchema, ProjectDetail } from "../schemas/index.js";
4
-
5
- const CompleteProjectInput = z.object({
6
- id: IdSchema.describe("The project's id.primaryKey"),
7
- });
8
-
9
- export async function completeProjectHandler(
10
- input: z.infer<typeof CompleteProjectInput>
11
- ): Promise<z.infer<typeof ProjectDetail>> {
12
- const raw = await runSnippet("complete_project", { id: input.id });
13
- return ProjectDetail.parse(raw);
14
- }
15
-
16
- export const completeProjectTool = {
17
- name: "complete_project",
18
- description: "Mark a project as done (complete). Returns the updated project detail.",
19
- inputSchema: CompleteProjectInput,
20
- handler: completeProjectHandler,
21
- } as const;
@@ -1,23 +0,0 @@
1
- import { z } from "zod";
2
- import { runSnippet } from "../runtime/index.js";
3
- import { IdSchema, TaskDetail } from "../schemas/index.js";
4
-
5
- const completeTaskSchema = z.object({
6
- id: IdSchema.describe("The task's id.primaryKey"),
7
- });
8
-
9
- export type CompleteTaskInput = z.infer<typeof completeTaskSchema>;
10
-
11
- export async function completeTaskHandler(
12
- input: CompleteTaskInput
13
- ): Promise<z.infer<typeof TaskDetail>> {
14
- const raw = await runSnippet("complete_task", { id: input.id });
15
- return TaskDetail.parse(raw);
16
- }
17
-
18
- export const completeTaskTool = {
19
- name: "complete_task",
20
- description: "Mark a task complete by its stable ID. Returns the updated task detail.",
21
- inputSchema: completeTaskSchema,
22
- handler: completeTaskHandler,
23
- } as const;
@@ -1,20 +0,0 @@
1
- import { z } from "zod";
2
- import { runSnippet } from "../runtime/index.js";
3
- import { CreateFolderInput, FolderDetail } from "../schemas/index.js";
4
-
5
- export type CreateFolderInputType = z.infer<typeof CreateFolderInput>;
6
-
7
- export async function createFolderHandler(
8
- input: CreateFolderInputType
9
- ): Promise<z.infer<typeof FolderDetail>> {
10
- const raw = await runSnippet("create_folder", input);
11
- return FolderDetail.parse(raw);
12
- }
13
-
14
- export const createFolderTool = {
15
- name: "create_folder",
16
- description:
17
- "Create a new OmniFocus folder. Omit parentFolderId to create at the top level; provide parentFolderId to nest it inside an existing folder.",
18
- inputSchema: CreateFolderInput,
19
- handler: createFolderHandler,
20
- } as const;
@@ -1,20 +0,0 @@
1
- import { z } from "zod";
2
- import { runSnippet } from "../runtime/index.js";
3
- import { CreateProjectInput, ProjectDetail } from "../schemas/index.js";
4
-
5
- export type CreateProjectInputType = z.infer<typeof CreateProjectInput>;
6
-
7
- export async function createProjectHandler(
8
- input: CreateProjectInputType
9
- ): Promise<z.infer<typeof ProjectDetail>> {
10
- const raw = await runSnippet("create_project", input);
11
- return ProjectDetail.parse(raw);
12
- }
13
-
14
- export const createProjectTool = {
15
- name: "create_project",
16
- description:
17
- "Create a new OmniFocus project. Omit folderId to create at the top level; provide folderId to place it inside a folder. Optionally set type (parallel/sequential/singleActions), status (active/onHold), review interval, and tags.",
18
- inputSchema: CreateProjectInput,
19
- handler: createProjectHandler,
20
- } as const;