@stephendolan/omnifocus-cli 2.1.0 → 2.3.0

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.
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  // src/cli.ts
4
- import { Command as Command8 } from "commander";
4
+ import { Command as Command9 } from "commander";
5
5
 
6
6
  // src/lib/output.ts
7
7
  var globalOutputOptions = {};
@@ -369,7 +369,7 @@ var OmniFocus = class {
369
369
  updates.push(`task.flagged = ${options.flagged};`);
370
370
  }
371
371
  if (options.completed !== void 0) {
372
- updates.push(`task.completed = ${options.completed};`);
372
+ updates.push(options.completed ? "task.markComplete();" : "task.markIncomplete();");
373
373
  }
374
374
  if (options.estimatedMinutes !== void 0) {
375
375
  updates.push(`task.estimatedMinutes = ${options.estimatedMinutes};`);
@@ -997,21 +997,21 @@ function createTaskCommand() {
997
997
  command.description("Manage OmniFocus tasks");
998
998
  command.command("list").alias("ls").description("List tasks").option("-f, --flagged", "Show only flagged tasks").option("-p, --project <name>", "Filter by project").option("-t, --tag <name>", "Filter by tag").option("-c, --completed", "Include completed tasks").action(
999
999
  withErrorHandling(async (options) => {
1000
- const of = new OmniFocus();
1000
+ const of2 = new OmniFocus();
1001
1001
  const filters = {
1002
1002
  includeCompleted: options.completed,
1003
1003
  ...options.flagged && { flagged: true },
1004
1004
  ...options.project && { project: options.project },
1005
1005
  ...options.tag && { tag: options.tag }
1006
1006
  };
1007
- const tasks = await of.listTasks(filters);
1007
+ const tasks = await of2.listTasks(filters);
1008
1008
  outputJson(tasks);
1009
1009
  })
1010
1010
  );
1011
1011
  command.command("create <name>").description("Create a new task").option("-p, --project <name>", "Assign to project").option("--note <text>", "Add note").option("-t, --tag <tags...>", "Add tags").option("-d, --due <date>", "Set due date").option("-D, --defer <date>", "Set defer date").option("-f, --flagged", "Flag the task").option("-e, --estimate <minutes>", "Estimated time in minutes", parseInt).action(
1012
1012
  withErrorHandling(async (name, options) => {
1013
- const of = new OmniFocus();
1014
- const task = await of.createTask({
1013
+ const of2 = new OmniFocus();
1014
+ const task = await of2.createTask({
1015
1015
  name,
1016
1016
  note: options.note,
1017
1017
  project: options.project,
@@ -1026,7 +1026,7 @@ function createTaskCommand() {
1026
1026
  );
1027
1027
  command.command("update <idOrName>").description("Update an existing task").option("-n, --name <name>", "New name").option("--note <text>", "New note").option("-p, --project <name>", "Move to project").option("-t, --tag <tags...>", "Replace tags").option("-d, --due <date>", "Set due date").option("-D, --defer <date>", "Set defer date").option("-f, --flag", "Flag the task").option("-F, --unflag", "Unflag the task").option("-c, --complete", "Mark as completed").option("-C, --incomplete", "Mark as incomplete").option("-e, --estimate <minutes>", "Estimated time in minutes", parseInt).action(
1028
1028
  withErrorHandling(async (idOrName, options) => {
1029
- const of = new OmniFocus();
1029
+ const of2 = new OmniFocus();
1030
1030
  const updates = {
1031
1031
  ...options.name && { name: options.name },
1032
1032
  ...options.note !== void 0 && { note: options.note },
@@ -1044,28 +1044,28 @@ function createTaskCommand() {
1044
1044
  ...options.incomplete && { completed: false },
1045
1045
  ...options.estimate !== void 0 && { estimatedMinutes: options.estimate }
1046
1046
  };
1047
- const task = await of.updateTask(idOrName, updates);
1047
+ const task = await of2.updateTask(idOrName, updates);
1048
1048
  outputJson(task);
1049
1049
  })
1050
1050
  );
1051
1051
  command.command("delete <idOrName>").alias("rm").description("Delete a task").action(
1052
1052
  withErrorHandling(async (idOrName) => {
1053
- const of = new OmniFocus();
1054
- await of.deleteTask(idOrName);
1053
+ const of2 = new OmniFocus();
1054
+ await of2.deleteTask(idOrName);
1055
1055
  outputJson({ message: "Task deleted successfully" });
1056
1056
  })
1057
1057
  );
1058
1058
  command.command("view <idOrName>").description("View task details").action(
1059
1059
  withErrorHandling(async (idOrName) => {
1060
- const of = new OmniFocus();
1061
- const task = await of.getTask(idOrName);
1060
+ const of2 = new OmniFocus();
1061
+ const task = await of2.getTask(idOrName);
1062
1062
  outputJson(task);
1063
1063
  })
1064
1064
  );
1065
1065
  command.command("stats").description("Show task statistics").action(
1066
1066
  withErrorHandling(async () => {
1067
- const of = new OmniFocus();
1068
- const stats = await of.getTaskStats();
1067
+ const of2 = new OmniFocus();
1068
+ const stats = await of2.getTaskStats();
1069
1069
  outputJson(stats);
1070
1070
  })
1071
1071
  );
@@ -1079,20 +1079,20 @@ function createProjectCommand() {
1079
1079
  command.description("Manage OmniFocus projects");
1080
1080
  command.command("list").alias("ls").description("List projects").option("-f, --folder <name>", "Filter by folder").option("-s, --status <status>", "Filter by status (active, on hold, dropped)").option("-d, --dropped", "Include dropped projects").action(
1081
1081
  withErrorHandling(async (options) => {
1082
- const of = new OmniFocus();
1082
+ const of2 = new OmniFocus();
1083
1083
  const filters = {
1084
1084
  includeDropped: options.dropped,
1085
1085
  ...options.folder && { folder: options.folder },
1086
1086
  ...options.status && { status: options.status }
1087
1087
  };
1088
- const projects = await of.listProjects(filters);
1088
+ const projects = await of2.listProjects(filters);
1089
1089
  outputJson(projects);
1090
1090
  })
1091
1091
  );
1092
1092
  command.command("create <name>").description("Create a new project").option("-f, --folder <name>", "Assign to folder").option("--note <text>", "Add note").option("-t, --tag <tags...>", "Add tags").option("-s, --sequential", "Make it a sequential project").option("--status <status>", "Set status (active, on hold, dropped)").action(
1093
1093
  withErrorHandling(async (name, options) => {
1094
- const of = new OmniFocus();
1095
- const project = await of.createProject({
1094
+ const of2 = new OmniFocus();
1095
+ const project = await of2.createProject({
1096
1096
  name,
1097
1097
  note: options.note,
1098
1098
  folder: options.folder,
@@ -1105,7 +1105,7 @@ function createProjectCommand() {
1105
1105
  );
1106
1106
  command.command("update <idOrName>").description("Update an existing project").option("-n, --name <name>", "Rename project").option("--note <text>", "New note").option("-f, --folder <name>", "Move to folder").option("-t, --tag <tags...>", "Replace tags").option("-s, --sequential", "Make it sequential").option("-p, --parallel", "Make it parallel").option("--status <status>", "Set status (active, on hold, dropped)").action(
1107
1107
  withErrorHandling(async (idOrName, options) => {
1108
- const of = new OmniFocus();
1108
+ const of2 = new OmniFocus();
1109
1109
  const updates = {
1110
1110
  ...options.name && { name: options.name },
1111
1111
  ...options.note !== void 0 && { note: options.note },
@@ -1115,28 +1115,28 @@ function createProjectCommand() {
1115
1115
  ...options.parallel && { sequential: false },
1116
1116
  ...options.status && { status: options.status }
1117
1117
  };
1118
- const project = await of.updateProject(idOrName, updates);
1118
+ const project = await of2.updateProject(idOrName, updates);
1119
1119
  outputJson(project);
1120
1120
  })
1121
1121
  );
1122
1122
  command.command("delete <idOrName>").alias("rm").description("Delete a project").action(
1123
1123
  withErrorHandling(async (idOrName) => {
1124
- const of = new OmniFocus();
1125
- await of.deleteProject(idOrName);
1124
+ const of2 = new OmniFocus();
1125
+ await of2.deleteProject(idOrName);
1126
1126
  outputJson({ message: "Project deleted successfully" });
1127
1127
  })
1128
1128
  );
1129
1129
  command.command("view <idOrName>").description("View project details").action(
1130
1130
  withErrorHandling(async (idOrName) => {
1131
- const of = new OmniFocus();
1132
- const project = await of.getProject(idOrName);
1131
+ const of2 = new OmniFocus();
1132
+ const project = await of2.getProject(idOrName);
1133
1133
  outputJson(project);
1134
1134
  })
1135
1135
  );
1136
1136
  command.command("stats").description("Show project statistics").action(
1137
1137
  withErrorHandling(async () => {
1138
- const of = new OmniFocus();
1139
- const stats = await of.getProjectStats();
1138
+ const of2 = new OmniFocus();
1139
+ const stats = await of2.getProjectStats();
1140
1140
  outputJson(stats);
1141
1141
  })
1142
1142
  );
@@ -1150,18 +1150,33 @@ function createInboxCommand() {
1150
1150
  command.description("Manage OmniFocus inbox");
1151
1151
  command.command("list").alias("ls").description("List inbox tasks").action(
1152
1152
  withErrorHandling(async () => {
1153
- const of = new OmniFocus();
1154
- const tasks = await of.listInboxTasks();
1153
+ const of2 = new OmniFocus();
1154
+ const tasks = await of2.listInboxTasks();
1155
1155
  outputJson(tasks);
1156
1156
  })
1157
1157
  );
1158
1158
  command.command("count").description("Get inbox count").action(
1159
1159
  withErrorHandling(async () => {
1160
- const of = new OmniFocus();
1161
- const count = await of.getInboxCount();
1160
+ const of2 = new OmniFocus();
1161
+ const count = await of2.getInboxCount();
1162
1162
  outputJson({ count });
1163
1163
  })
1164
1164
  );
1165
+ command.command("add <name>").description("Add a task to inbox").option("--note <text>", "Add note").option("-t, --tag <tags...>", "Add tags").option("-d, --due <date>", "Set due date").option("-D, --defer <date>", "Set defer date").option("-f, --flagged", "Flag the task").option("-e, --estimate <minutes>", "Estimated time in minutes", parseInt).action(
1166
+ withErrorHandling(async (name, options) => {
1167
+ const of2 = new OmniFocus();
1168
+ const task = await of2.createTask({
1169
+ name,
1170
+ note: options.note,
1171
+ tags: options.tag,
1172
+ due: options.due ? parseDateTime(options.due) : void 0,
1173
+ defer: options.defer ? parseDateTime(options.defer) : void 0,
1174
+ flagged: options.flagged,
1175
+ estimatedMinutes: options.estimate
1176
+ });
1177
+ outputJson(task);
1178
+ })
1179
+ );
1165
1180
  return command;
1166
1181
  }
1167
1182
 
@@ -1173,8 +1188,8 @@ function createSearchCommand() {
1173
1188
  command.argument("<query>", "Search query");
1174
1189
  command.action(
1175
1190
  withErrorHandling(async (query) => {
1176
- const of = new OmniFocus();
1177
- const tasks = await of.searchTasks(query);
1191
+ const of2 = new OmniFocus();
1192
+ const tasks = await of2.searchTasks(query);
1178
1193
  outputJson(tasks);
1179
1194
  })
1180
1195
  );
@@ -1188,15 +1203,15 @@ function createPerspectiveCommand() {
1188
1203
  command.description("Manage OmniFocus perspectives");
1189
1204
  command.command("list").alias("ls").description("List all perspectives").action(
1190
1205
  withErrorHandling(async () => {
1191
- const of = new OmniFocus();
1192
- const perspectives = await of.listPerspectives();
1206
+ const of2 = new OmniFocus();
1207
+ const perspectives = await of2.listPerspectives();
1193
1208
  outputJson(perspectives);
1194
1209
  })
1195
1210
  );
1196
1211
  command.command("view <name>").description("View tasks in a perspective").action(
1197
1212
  withErrorHandling(async (name) => {
1198
- const of = new OmniFocus();
1199
- const tasks = await of.getPerspectiveTasks(name);
1213
+ const of2 = new OmniFocus();
1214
+ const tasks = await of2.getPerspectiveTasks(name);
1200
1215
  outputJson(tasks);
1201
1216
  })
1202
1217
  );
@@ -1210,8 +1225,8 @@ function createTagCommand() {
1210
1225
  command.description("Manage and analyze OmniFocus tags");
1211
1226
  command.command("list").alias("ls").description("List tags with usage information").option("-u, --unused-days <days>", "Show tags unused for N days", parseInt).option("-s, --sort <field>", "Sort by: name, usage, activity (default: name)", "name").option("-a, --active-only", "Only count active (incomplete) tasks").action(
1212
1227
  withErrorHandling(async (options) => {
1213
- const of = new OmniFocus();
1214
- const tags = await of.listTags({
1228
+ const of2 = new OmniFocus();
1229
+ const tags = await of2.listTags({
1215
1230
  unusedDays: options.unusedDays,
1216
1231
  sortBy: options.sort,
1217
1232
  activeOnly: options.activeOnly
@@ -1221,8 +1236,8 @@ function createTagCommand() {
1221
1236
  );
1222
1237
  command.command("create <name>").description("Create a new tag").option("-p, --parent <name>", "Create as child of parent tag").option("-s, --status <status>", "Set status (active, on hold, dropped)").action(
1223
1238
  withErrorHandling(async (name, options) => {
1224
- const of = new OmniFocus();
1225
- const tag = await of.createTag({
1239
+ const of2 = new OmniFocus();
1240
+ const tag = await of2.createTag({
1226
1241
  name,
1227
1242
  parent: options.parent,
1228
1243
  status: options.status
@@ -1232,33 +1247,33 @@ function createTagCommand() {
1232
1247
  );
1233
1248
  command.command("view <idOrName>").description("View tag details").action(
1234
1249
  withErrorHandling(async (idOrName) => {
1235
- const of = new OmniFocus();
1236
- const tag = await of.getTag(idOrName);
1250
+ const of2 = new OmniFocus();
1251
+ const tag = await of2.getTag(idOrName);
1237
1252
  outputJson(tag);
1238
1253
  })
1239
1254
  );
1240
1255
  command.command("update <idOrName>").description("Update an existing tag").option("-n, --name <name>", "Rename tag").option("-s, --status <status>", "Set status (active, on hold, dropped)").action(
1241
1256
  withErrorHandling(async (idOrName, options) => {
1242
- const of = new OmniFocus();
1257
+ const of2 = new OmniFocus();
1243
1258
  const updates = {
1244
1259
  ...options.name && { name: options.name },
1245
1260
  ...options.status && { status: options.status }
1246
1261
  };
1247
- const tag = await of.updateTag(idOrName, updates);
1262
+ const tag = await of2.updateTag(idOrName, updates);
1248
1263
  outputJson(tag);
1249
1264
  })
1250
1265
  );
1251
1266
  command.command("delete <idOrName>").alias("rm").description("Delete a tag").action(
1252
1267
  withErrorHandling(async (idOrName) => {
1253
- const of = new OmniFocus();
1254
- await of.deleteTag(idOrName);
1268
+ const of2 = new OmniFocus();
1269
+ await of2.deleteTag(idOrName);
1255
1270
  outputJson({ message: "Tag deleted successfully" });
1256
1271
  })
1257
1272
  );
1258
1273
  command.command("stats").description("Show tag usage statistics").action(
1259
1274
  withErrorHandling(async () => {
1260
- const of = new OmniFocus();
1261
- const stats = await of.getTagStats();
1275
+ const of2 = new OmniFocus();
1276
+ const stats = await of2.getTagStats();
1262
1277
  outputJson(stats);
1263
1278
  })
1264
1279
  );
@@ -1272,25 +1287,273 @@ function createFolderCommand() {
1272
1287
  command.description("View OmniFocus folder hierarchy");
1273
1288
  command.command("list").alias("ls").description("List top-level folders with nested children").option("-d, --dropped", "Include dropped folders").action(
1274
1289
  withErrorHandling(async (options) => {
1275
- const of = new OmniFocus();
1276
- const folders = await of.listFolders({ includeDropped: options.dropped });
1290
+ const of2 = new OmniFocus();
1291
+ const folders = await of2.listFolders({ includeDropped: options.dropped });
1277
1292
  outputJson(folders);
1278
1293
  })
1279
1294
  );
1280
1295
  command.command("view <idOrName>").description("View folder details and children").option("-d, --dropped", "Include dropped child folders").action(
1281
1296
  withErrorHandling(async (idOrName, options) => {
1282
- const of = new OmniFocus();
1297
+ const of2 = new OmniFocus();
1283
1298
  const filters = { includeDropped: options.dropped };
1284
- const folder = await of.getFolder(idOrName, filters);
1299
+ const folder = await of2.getFolder(idOrName, filters);
1285
1300
  outputJson(folder);
1286
1301
  })
1287
1302
  );
1288
1303
  return command;
1289
1304
  }
1290
1305
 
1306
+ // src/commands/mcp.ts
1307
+ import { Command as Command8 } from "commander";
1308
+
1309
+ // src/mcp/server.ts
1310
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
1311
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1312
+ import { z } from "zod";
1313
+ var server = new McpServer({
1314
+ name: "omnifocus",
1315
+ version: "1.0.0"
1316
+ });
1317
+ var of = new OmniFocus();
1318
+ function jsonResponse(data) {
1319
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
1320
+ }
1321
+ server.tool(
1322
+ "list_tasks",
1323
+ "List tasks with optional filtering",
1324
+ {
1325
+ includeCompleted: z.boolean().optional().describe("Include completed tasks"),
1326
+ includeDropped: z.boolean().optional().describe("Include dropped tasks"),
1327
+ flagged: z.boolean().optional().describe("Only show flagged tasks"),
1328
+ project: z.string().optional().describe("Filter by project name"),
1329
+ tag: z.string().optional().describe("Filter by tag name")
1330
+ },
1331
+ async (filters) => jsonResponse(await of.listTasks(filters))
1332
+ );
1333
+ server.tool(
1334
+ "get_task",
1335
+ "Get a specific task by ID or name",
1336
+ { idOrName: z.string().describe("Task ID or name") },
1337
+ async ({ idOrName }) => jsonResponse(await of.getTask(idOrName))
1338
+ );
1339
+ server.tool(
1340
+ "create_task",
1341
+ "Create a new task",
1342
+ {
1343
+ name: z.string().describe("Task name"),
1344
+ note: z.string().optional().describe("Task note"),
1345
+ project: z.string().optional().describe("Project to add task to"),
1346
+ tags: z.array(z.string()).optional().describe("Tags to assign"),
1347
+ defer: z.string().optional().describe("Defer date (ISO 8601)"),
1348
+ due: z.string().optional().describe("Due date (ISO 8601)"),
1349
+ flagged: z.boolean().optional().describe("Flag the task"),
1350
+ estimatedMinutes: z.number().optional().describe("Estimated duration in minutes")
1351
+ },
1352
+ async (options) => jsonResponse(await of.createTask(options))
1353
+ );
1354
+ server.tool(
1355
+ "update_task",
1356
+ "Update an existing task",
1357
+ {
1358
+ idOrName: z.string().describe("Task ID or name"),
1359
+ name: z.string().optional().describe("New task name"),
1360
+ note: z.string().optional().describe("New task note"),
1361
+ project: z.string().optional().describe("Move to project"),
1362
+ tags: z.array(z.string()).optional().describe("Replace tags"),
1363
+ defer: z.string().optional().describe("New defer date (ISO 8601)"),
1364
+ due: z.string().optional().describe("New due date (ISO 8601)"),
1365
+ flagged: z.boolean().optional().describe("Flag/unflag the task"),
1366
+ estimatedMinutes: z.number().optional().describe("New estimated duration"),
1367
+ completed: z.boolean().optional().describe("Mark complete/incomplete")
1368
+ },
1369
+ async ({ idOrName, ...options }) => jsonResponse(await of.updateTask(idOrName, options))
1370
+ );
1371
+ server.tool(
1372
+ "delete_task",
1373
+ "Delete a task",
1374
+ { idOrName: z.string().describe("Task ID or name") },
1375
+ async ({ idOrName }) => {
1376
+ await of.deleteTask(idOrName);
1377
+ return jsonResponse({ deleted: true });
1378
+ }
1379
+ );
1380
+ server.tool(
1381
+ "search_tasks",
1382
+ "Search tasks by name or note content",
1383
+ { query: z.string().describe("Search query") },
1384
+ async ({ query }) => jsonResponse(await of.searchTasks(query))
1385
+ );
1386
+ server.tool(
1387
+ "get_task_stats",
1388
+ "Get task statistics",
1389
+ {},
1390
+ async () => jsonResponse(await of.getTaskStats())
1391
+ );
1392
+ server.tool(
1393
+ "list_inbox",
1394
+ "List all inbox tasks",
1395
+ {},
1396
+ async () => jsonResponse(await of.listInboxTasks())
1397
+ );
1398
+ server.tool(
1399
+ "get_inbox_count",
1400
+ "Get the number of inbox tasks",
1401
+ {},
1402
+ async () => jsonResponse({ count: await of.getInboxCount() })
1403
+ );
1404
+ server.tool(
1405
+ "list_projects",
1406
+ "List projects with optional filtering",
1407
+ {
1408
+ includeDropped: z.boolean().optional().describe("Include dropped projects"),
1409
+ status: z.enum(["active", "on hold", "dropped"]).optional().describe("Filter by status"),
1410
+ folder: z.string().optional().describe("Filter by folder name")
1411
+ },
1412
+ async (filters) => jsonResponse(await of.listProjects(filters))
1413
+ );
1414
+ server.tool(
1415
+ "get_project",
1416
+ "Get a specific project by ID or name",
1417
+ { idOrName: z.string().describe("Project ID or name") },
1418
+ async ({ idOrName }) => jsonResponse(await of.getProject(idOrName))
1419
+ );
1420
+ server.tool(
1421
+ "create_project",
1422
+ "Create a new project",
1423
+ {
1424
+ name: z.string().describe("Project name"),
1425
+ note: z.string().optional().describe("Project note"),
1426
+ folder: z.string().optional().describe("Folder to create project in"),
1427
+ sequential: z.boolean().optional().describe("Sequential project (tasks must be done in order)"),
1428
+ tags: z.array(z.string()).optional().describe("Tags to assign"),
1429
+ status: z.enum(["active", "on hold", "dropped"]).optional().describe("Initial status")
1430
+ },
1431
+ async (options) => jsonResponse(await of.createProject(options))
1432
+ );
1433
+ server.tool(
1434
+ "update_project",
1435
+ "Update an existing project",
1436
+ {
1437
+ idOrName: z.string().describe("Project ID or name"),
1438
+ name: z.string().optional().describe("New project name"),
1439
+ note: z.string().optional().describe("New project note"),
1440
+ folder: z.string().optional().describe("Move to folder"),
1441
+ sequential: z.boolean().optional().describe("Set sequential/parallel"),
1442
+ tags: z.array(z.string()).optional().describe("Replace tags"),
1443
+ status: z.enum(["active", "on hold", "dropped"]).optional().describe("New status")
1444
+ },
1445
+ async ({ idOrName, ...options }) => jsonResponse(await of.updateProject(idOrName, options))
1446
+ );
1447
+ server.tool(
1448
+ "delete_project",
1449
+ "Delete a project",
1450
+ { idOrName: z.string().describe("Project ID or name") },
1451
+ async ({ idOrName }) => {
1452
+ await of.deleteProject(idOrName);
1453
+ return jsonResponse({ deleted: true });
1454
+ }
1455
+ );
1456
+ server.tool(
1457
+ "get_project_stats",
1458
+ "Get project statistics",
1459
+ {},
1460
+ async () => jsonResponse(await of.getProjectStats())
1461
+ );
1462
+ server.tool(
1463
+ "list_perspectives",
1464
+ "List all available perspectives",
1465
+ {},
1466
+ async () => jsonResponse(await of.listPerspectives())
1467
+ );
1468
+ server.tool(
1469
+ "get_perspective_tasks",
1470
+ "Get tasks from a specific perspective",
1471
+ { name: z.string().describe("Perspective name (e.g., Inbox, Flagged, or custom perspective)") },
1472
+ async ({ name }) => jsonResponse(await of.getPerspectiveTasks(name))
1473
+ );
1474
+ server.tool(
1475
+ "list_tags",
1476
+ "List all tags with optional filtering and sorting",
1477
+ {
1478
+ unusedDays: z.number().optional().describe("Only show tags unused for this many days"),
1479
+ sortBy: z.enum(["name", "usage", "activity"]).optional().describe("Sort order"),
1480
+ activeOnly: z.boolean().optional().describe("Only count active tasks")
1481
+ },
1482
+ async (options) => jsonResponse(await of.listTags(options))
1483
+ );
1484
+ server.tool(
1485
+ "get_tag",
1486
+ "Get a specific tag by ID or name",
1487
+ { idOrName: z.string().describe('Tag ID, name, or path (e.g., "Parent/Child")') },
1488
+ async ({ idOrName }) => jsonResponse(await of.getTag(idOrName))
1489
+ );
1490
+ server.tool(
1491
+ "create_tag",
1492
+ "Create a new tag",
1493
+ {
1494
+ name: z.string().describe("Tag name"),
1495
+ parent: z.string().optional().describe("Parent tag name or path"),
1496
+ status: z.enum(["active", "on hold", "dropped"]).optional().describe("Initial status")
1497
+ },
1498
+ async (options) => jsonResponse(await of.createTag(options))
1499
+ );
1500
+ server.tool(
1501
+ "update_tag",
1502
+ "Update an existing tag",
1503
+ {
1504
+ idOrName: z.string().describe("Tag ID, name, or path"),
1505
+ name: z.string().optional().describe("New tag name"),
1506
+ status: z.enum(["active", "on hold", "dropped"]).optional().describe("New status")
1507
+ },
1508
+ async ({ idOrName, ...options }) => jsonResponse(await of.updateTag(idOrName, options))
1509
+ );
1510
+ server.tool(
1511
+ "delete_tag",
1512
+ "Delete a tag",
1513
+ { idOrName: z.string().describe("Tag ID, name, or path") },
1514
+ async ({ idOrName }) => {
1515
+ await of.deleteTag(idOrName);
1516
+ return jsonResponse({ deleted: true });
1517
+ }
1518
+ );
1519
+ server.tool(
1520
+ "get_tag_stats",
1521
+ "Get tag statistics",
1522
+ {},
1523
+ async () => jsonResponse(await of.getTagStats())
1524
+ );
1525
+ server.tool(
1526
+ "list_folders",
1527
+ "List all folders",
1528
+ { includeDropped: z.boolean().optional().describe("Include dropped folders") },
1529
+ async (filters) => jsonResponse(await of.listFolders(filters))
1530
+ );
1531
+ server.tool(
1532
+ "get_folder",
1533
+ "Get a specific folder by ID or name",
1534
+ {
1535
+ idOrName: z.string().describe("Folder ID or name"),
1536
+ includeDropped: z.boolean().optional().describe("Include dropped children")
1537
+ },
1538
+ async ({ idOrName, includeDropped }) => jsonResponse(await of.getFolder(idOrName, { includeDropped }))
1539
+ );
1540
+ async function runMcpServer() {
1541
+ const transport = new StdioServerTransport();
1542
+ await server.connect(transport);
1543
+ }
1544
+
1545
+ // src/commands/mcp.ts
1546
+ function createMcpCommand() {
1547
+ const cmd = new Command8("mcp").description("Run OmniFocus MCP server");
1548
+ cmd.action(async () => {
1549
+ await runMcpServer();
1550
+ });
1551
+ return cmd;
1552
+ }
1553
+
1291
1554
  // src/cli.ts
1292
- var program = new Command8();
1293
- program.name("of").description("A command-line interface for OmniFocus on macOS").version("2.1.0").option("-c, --compact", "Minified JSON output (single line)").hook("preAction", (thisCommand) => {
1555
+ var program = new Command9();
1556
+ program.name("of").description("A command-line interface for OmniFocus on macOS").version("2.3.0").option("-c, --compact", "Minified JSON output (single line)").hook("preAction", (thisCommand) => {
1294
1557
  const options = thisCommand.opts();
1295
1558
  setOutputOptions({
1296
1559
  compact: options.compact
@@ -1303,6 +1566,7 @@ program.addCommand(createSearchCommand());
1303
1566
  program.addCommand(createPerspectiveCommand());
1304
1567
  program.addCommand(createTagCommand());
1305
1568
  program.addCommand(createFolderCommand());
1569
+ program.addCommand(createMcpCommand());
1306
1570
  program.parseAsync().catch(() => {
1307
1571
  process.exit(1);
1308
1572
  });