@mtaap/mcp 0.2.1 → 0.2.3

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/index.js CHANGED
@@ -37,16 +37,19 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
37
37
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
38
38
 
39
39
  // src/version.ts
40
- var VERSION = "0.2.1";
40
+ var VERSION = "0.2.3";
41
+
42
+ // src/index.ts
43
+ var import_zod3 = require("zod");
41
44
 
42
45
  // ../../packages/core/dist/constants/enums.js
43
46
  var TaskState;
44
- (function(TaskState3) {
45
- TaskState3["BACKLOG"] = "BACKLOG";
46
- TaskState3["READY"] = "READY";
47
- TaskState3["IN_PROGRESS"] = "IN_PROGRESS";
48
- TaskState3["REVIEW"] = "REVIEW";
49
- TaskState3["DONE"] = "DONE";
47
+ (function(TaskState2) {
48
+ TaskState2["BACKLOG"] = "BACKLOG";
49
+ TaskState2["READY"] = "READY";
50
+ TaskState2["IN_PROGRESS"] = "IN_PROGRESS";
51
+ TaskState2["REVIEW"] = "REVIEW";
52
+ TaskState2["DONE"] = "DONE";
50
53
  })(TaskState || (TaskState = {}));
51
54
  var UserRole;
52
55
  (function(UserRole2) {
@@ -410,56 +413,78 @@ var ListTasksInputSchema = import_zod2.z.object({
410
413
  state: import_zod2.z.nativeEnum(TaskState).optional(),
411
414
  assigneeId: import_zod2.z.string().optional()
412
415
  });
416
+ var cuidOrPrefixedId = import_zod2.z.string().regex(/^([a-z0-9]+|[a-z]+_[a-zA-Z0-9]+)$/);
413
417
  var GetTaskInputSchema = import_zod2.z.object({
414
- taskId: import_zod2.z.string().regex(/^tsk_[a-zA-Z0-9]+$/)
418
+ taskId: cuidOrPrefixedId
415
419
  });
416
420
  var AssignTaskInputSchema = import_zod2.z.object({
417
- projectId: import_zod2.z.string().regex(/^prj_[a-zA-Z0-9]+$/),
418
- taskId: import_zod2.z.string().regex(/^tsk_[a-zA-Z0-9]+$/),
421
+ projectId: cuidOrPrefixedId,
422
+ taskId: cuidOrPrefixedId,
419
423
  expectedState: import_zod2.z.nativeEnum(TaskState).default(TaskState.READY)
420
424
  });
421
425
  var UpdateProgressInputSchema = import_zod2.z.object({
422
- taskId: import_zod2.z.string().regex(/^tsk_[a-zA-Z0-9]+$/),
426
+ taskId: cuidOrPrefixedId,
423
427
  statusMessage: import_zod2.z.string().max(1e3).optional(),
424
428
  completedCheckpointIds: import_zod2.z.array(import_zod2.z.string()).optional(),
425
429
  currentCheckpointIndex: import_zod2.z.number().int().optional()
426
430
  });
427
431
  var CompleteTaskInputSchema = import_zod2.z.object({
428
- projectId: import_zod2.z.string().regex(/^prj_[a-zA-Z0-9]+$/),
429
- taskId: import_zod2.z.string().regex(/^tsk_[a-zA-Z0-9]+$/),
432
+ projectId: cuidOrPrefixedId,
433
+ taskId: cuidOrPrefixedId,
430
434
  pullRequestTitle: import_zod2.z.string().min(1).max(300).optional(),
431
435
  pullRequestBody: import_zod2.z.string().max(1e4).optional()
432
436
  });
433
437
  var ReportErrorInputSchema = import_zod2.z.object({
434
- taskId: import_zod2.z.string().regex(/^tsk_[a-zA-Z0-9]+$/),
438
+ taskId: cuidOrPrefixedId,
435
439
  errorType: import_zod2.z.nativeEnum(ErrorType),
436
440
  errorMessage: import_zod2.z.string().min(1).max(1e3),
437
441
  context: import_zod2.z.string().max(2e3).optional()
438
442
  });
439
443
  var GetProjectContextInputSchema = import_zod2.z.object({
440
- projectId: import_zod2.z.string().regex(/^prj_[a-zA-Z0-9]+$/)
444
+ projectId: cuidOrPrefixedId
441
445
  });
442
446
  var AddNoteInputSchema = import_zod2.z.object({
443
- taskId: import_zod2.z.string().regex(/^tsk_[a-zA-Z0-9]+$/),
447
+ taskId: cuidOrPrefixedId,
444
448
  content: import_zod2.z.string().min(1).max(500)
445
449
  });
446
450
  var AbandonTaskInputSchema = import_zod2.z.object({
447
- projectId: import_zod2.z.string().regex(/^prj_[a-zA-Z0-9]+$/),
448
- taskId: import_zod2.z.string().regex(/^tsk_[a-zA-Z0-9]+$/),
451
+ projectId: cuidOrPrefixedId,
452
+ taskId: cuidOrPrefixedId,
449
453
  deleteBranch: import_zod2.z.boolean().optional()
450
454
  });
455
+ var RequestChangesInputSchema = import_zod2.z.object({
456
+ projectId: cuidOrPrefixedId,
457
+ taskId: cuidOrPrefixedId,
458
+ reviewComments: import_zod2.z.string().min(1).max(5e3),
459
+ requestedChanges: import_zod2.z.array(import_zod2.z.string().min(1).max(500)).optional()
460
+ });
461
+ var ApproveTaskInputSchema = import_zod2.z.object({
462
+ projectId: cuidOrPrefixedId,
463
+ taskId: cuidOrPrefixedId,
464
+ reviewComments: import_zod2.z.string().max(2e3).optional()
465
+ });
451
466
  var CreatePersonalProjectInputSchema = import_zod2.z.object({
452
467
  name: import_zod2.z.string().min(1).max(100),
453
468
  description: import_zod2.z.string().max(500).optional(),
454
469
  repositoryUrl: import_zod2.z.string().url()
455
470
  });
456
471
  var CheckActiveTaskInputSchema = import_zod2.z.object({});
472
+ var CreateTaskMCPInputSchema = import_zod2.z.object({
473
+ projectId: cuidOrPrefixedId,
474
+ epicId: cuidOrPrefixedId.nullable().optional(),
475
+ title: import_zod2.z.string().min(1).max(200),
476
+ description: import_zod2.z.string().max(5e3),
477
+ priority: import_zod2.z.nativeEnum(TaskPriority).default(TaskPriority.MEDIUM),
478
+ acceptanceCriteria: import_zod2.z.array(import_zod2.z.object({
479
+ description: import_zod2.z.string().min(1).max(500)
480
+ })).min(1)
481
+ });
457
482
  var CreateOrganizationInputSchema = import_zod2.z.object({
458
483
  name: import_zod2.z.string().min(1).max(255),
459
484
  slug: import_zod2.z.string().min(1).max(100).regex(/^[a-z0-9-]+$/).optional()
460
485
  });
461
486
  var UpdateOrganizationInputSchema = import_zod2.z.object({
462
- organizationId: import_zod2.z.string().regex(/^org_[a-zA-Z0-9]+$/),
487
+ organizationId: cuidOrPrefixedId,
463
488
  name: import_zod2.z.string().min(1).max(255).optional(),
464
489
  logoUrl: import_zod2.z.string().url().nullable().optional(),
465
490
  accentColor: import_zod2.z.string().regex(/^#[0-9A-Fa-f]{6}$/, "Invalid hex color format. Expected #RRGGBB").nullable().optional(),
@@ -482,7 +507,7 @@ var UpdateProjectInputSchema = import_zod2.z.object({
482
507
  tags: import_zod2.z.array(import_zod2.z.string()).optional()
483
508
  });
484
509
  var CreateEpicInputSchema = import_zod2.z.object({
485
- projectId: import_zod2.z.string().regex(/^prj_[a-zA-Z0-9]+$/),
510
+ projectId: cuidOrPrefixedId,
486
511
  name: import_zod2.z.string().min(1).max(200),
487
512
  description: import_zod2.z.string().max(2e3).optional()
488
513
  });
@@ -514,14 +539,14 @@ var AssignTaskWebappInputSchema = import_zod2.z.object({
514
539
  userId: import_zod2.z.string().min(1)
515
540
  });
516
541
  var CreateTagInputSchema = import_zod2.z.object({
517
- organizationId: import_zod2.z.string().regex(/^org_[a-zA-Z0-9]+$/),
542
+ organizationId: cuidOrPrefixedId,
518
543
  name: import_zod2.z.string().min(1).max(50).regex(/^[a-zA-Z0-9\s-]+$/)
519
544
  });
520
545
  var UpdateTagInputSchema = import_zod2.z.object({
521
546
  name: import_zod2.z.string().min(1).max(50).regex(/^[a-zA-Z0-9\s-]+$/)
522
547
  });
523
548
  var UpdateOrganizationSettingsInputSchema = import_zod2.z.object({
524
- organizationId: import_zod2.z.string().regex(/^org_[a-zA-Z0-9]+$/),
549
+ organizationId: cuidOrPrefixedId,
525
550
  ldapEnabled: import_zod2.z.boolean().optional(),
526
551
  ldapUrl: import_zod2.z.string().url().nullable().optional(),
527
552
  ldapBindDN: import_zod2.z.string().nullable().optional(),
@@ -534,21 +559,21 @@ var UpdateOrganizationSettingsInputSchema = import_zod2.z.object({
534
559
  tenantName: import_zod2.z.string().max(255).nullable().optional()
535
560
  });
536
561
  var InviteUserInputSchema = import_zod2.z.object({
537
- organizationId: import_zod2.z.string().regex(/^org_[a-zA-Z0-9]+$/),
562
+ organizationId: cuidOrPrefixedId,
538
563
  email: import_zod2.z.string().email(),
539
564
  role: import_zod2.z.nativeEnum(UserRole).default(UserRole.MEMBER),
540
565
  tags: import_zod2.z.array(import_zod2.z.string()).default([])
541
566
  });
542
567
  var AssignUserTagsInputSchema = import_zod2.z.object({
543
- userId: import_zod2.z.string().regex(/^usr_[a-zA-Z0-9]+$/),
568
+ userId: cuidOrPrefixedId,
544
569
  tags: import_zod2.z.array(import_zod2.z.string()).min(0)
545
570
  });
546
571
  var InviteCollaboratorInputSchema = import_zod2.z.object({
547
- projectId: import_zod2.z.string().regex(/^prj_[a-zA-Z0-9]+$/),
572
+ projectId: cuidOrPrefixedId,
548
573
  email: import_zod2.z.string().email()
549
574
  });
550
575
  var PublishProjectInputSchema = import_zod2.z.object({
551
- projectId: import_zod2.z.string().regex(/^prj_[a-zA-Z0-9]+$/),
576
+ projectId: cuidOrPrefixedId,
552
577
  transferOwnership: import_zod2.z.boolean().default(false),
553
578
  tags: import_zod2.z.array(import_zod2.z.string()).min(1)
554
579
  });
@@ -558,7 +583,7 @@ var GenerateApiKeyInputSchema = import_zod2.z.object({
558
583
  permissions: import_zod2.z.nativeEnum(ApiKeyPermission).default(ApiKeyPermission.WRITE)
559
584
  });
560
585
  var RevokeApiKeyInputSchema = import_zod2.z.object({
561
- keyId: import_zod2.z.string().regex(/^key_[a-zA-Z0-9]+$/)
586
+ keyId: cuidOrPrefixedId
562
587
  });
563
588
  var LoginInputSchema = import_zod2.z.object({
564
589
  email: import_zod2.z.string().email(),
@@ -991,6 +1016,12 @@ var MCPApiClient = class {
991
1016
  { name, description, repositoryUrl }
992
1017
  );
993
1018
  }
1019
+ /**
1020
+ * Create a task in a project
1021
+ */
1022
+ async createTask(input) {
1023
+ return this.request("POST", "/api/mcp/tasks", input);
1024
+ }
994
1025
  /**
995
1026
  * List tasks with optional filters
996
1027
  */
@@ -1059,6 +1090,25 @@ var MCPApiClient = class {
1059
1090
  async addNote(taskId, content) {
1060
1091
  return this.request("POST", `/api/mcp/tasks/${taskId}/notes`, { content });
1061
1092
  }
1093
+ /**
1094
+ * Request changes on a task in review
1095
+ */
1096
+ async requestChanges(taskId, projectId, reviewComments, requestedChanges) {
1097
+ return this.request("POST", `/api/mcp/tasks/${taskId}/request-changes`, {
1098
+ projectId,
1099
+ reviewComments,
1100
+ requestedChanges
1101
+ });
1102
+ }
1103
+ /**
1104
+ * Approve a task in review and mark as DONE
1105
+ */
1106
+ async approveTask(taskId, projectId, reviewComments) {
1107
+ return this.request("POST", `/api/mcp/tasks/${taskId}/approve`, {
1108
+ projectId,
1109
+ reviewComments
1110
+ });
1111
+ }
1062
1112
  /**
1063
1113
  * Get GitHub token
1064
1114
  */
@@ -1130,7 +1180,10 @@ async function createMCPServer() {
1130
1180
  server.registerTool(
1131
1181
  "list_projects",
1132
1182
  {
1133
- description: "List accessible projects (personal + team via tags)"
1183
+ description: "List accessible projects (personal + team via tags)",
1184
+ inputSchema: {
1185
+ workspaceType: import_zod3.z.enum(["TEAM", "PERSONAL", "ALL"]).optional().describe("Filter by workspace type")
1186
+ }
1134
1187
  },
1135
1188
  async (args) => {
1136
1189
  assertApiKeyPermission(
@@ -1157,7 +1210,12 @@ async function createMCPServer() {
1157
1210
  server.registerTool(
1158
1211
  "list_tasks",
1159
1212
  {
1160
- description: "Returns available tasks (filterable by project, state)"
1213
+ description: "Returns available tasks (filterable by project, state)",
1214
+ inputSchema: {
1215
+ projectId: import_zod3.z.string().optional().describe("Filter by project ID"),
1216
+ state: import_zod3.z.nativeEnum(TaskState).optional().describe("Filter by task state"),
1217
+ assigneeId: import_zod3.z.string().optional().describe("Filter by assignee ID")
1218
+ }
1161
1219
  },
1162
1220
  async (args) => {
1163
1221
  assertApiKeyPermission(mockApiKey, ApiKeyPermission.READ, "list_tasks");
@@ -1184,7 +1242,10 @@ async function createMCPServer() {
1184
1242
  server.registerTool(
1185
1243
  "get_task",
1186
1244
  {
1187
- description: "Full task details including acceptance criteria"
1245
+ description: "Full task details including acceptance criteria",
1246
+ inputSchema: {
1247
+ taskId: import_zod3.z.string().describe("The task ID to retrieve")
1248
+ }
1188
1249
  },
1189
1250
  async (args) => {
1190
1251
  assertApiKeyPermission(mockApiKey, ApiKeyPermission.READ, "get_task");
@@ -1207,7 +1268,12 @@ async function createMCPServer() {
1207
1268
  server.registerTool(
1208
1269
  "assign_task",
1209
1270
  {
1210
- description: "Atomic claim - creates branch. Fails if already taken."
1271
+ description: "Atomic claim - creates branch. Fails if already taken.",
1272
+ inputSchema: {
1273
+ projectId: import_zod3.z.string().describe("The project ID"),
1274
+ taskId: import_zod3.z.string().describe("The task ID to assign"),
1275
+ expectedState: import_zod3.z.nativeEnum(TaskState).optional().describe("Expected task state (default: READY)")
1276
+ }
1211
1277
  },
1212
1278
  async (args) => {
1213
1279
  assertApiKeyPermission(
@@ -1238,7 +1304,13 @@ async function createMCPServer() {
1238
1304
  server.registerTool(
1239
1305
  "update_progress",
1240
1306
  {
1241
- description: "Reports status, updates checkboxes, writes checkpoint"
1307
+ description: "Reports status, updates checkboxes, writes checkpoint",
1308
+ inputSchema: {
1309
+ taskId: import_zod3.z.string().describe("The task ID to update"),
1310
+ statusMessage: import_zod3.z.string().optional().describe("Status message (max 1000 chars)"),
1311
+ completedCheckpointIds: import_zod3.z.array(import_zod3.z.string()).optional().describe("Array of completed checkpoint IDs"),
1312
+ currentCheckpointIndex: import_zod3.z.number().optional().describe("Current checkpoint index")
1313
+ }
1242
1314
  },
1243
1315
  async (args) => {
1244
1316
  assertApiKeyPermission(
@@ -1269,7 +1341,13 @@ async function createMCPServer() {
1269
1341
  server.registerTool(
1270
1342
  "complete_task",
1271
1343
  {
1272
- description: "Marks complete, triggers PR, deletes local state file"
1344
+ description: "Marks complete, triggers PR, deletes local state file",
1345
+ inputSchema: {
1346
+ projectId: import_zod3.z.string().describe("The project ID"),
1347
+ taskId: import_zod3.z.string().describe("The task ID to complete"),
1348
+ pullRequestTitle: import_zod3.z.string().optional().describe("PR title (max 300 chars)"),
1349
+ pullRequestBody: import_zod3.z.string().optional().describe("PR body/description (max 10000 chars)")
1350
+ }
1273
1351
  },
1274
1352
  async (args) => {
1275
1353
  assertApiKeyPermission(
@@ -1323,7 +1401,13 @@ async function createMCPServer() {
1323
1401
  server.registerTool(
1324
1402
  "report_error",
1325
1403
  {
1326
- description: "Report unrecoverable error, displays on task in webapp"
1404
+ description: "Report unrecoverable error, displays on task in webapp",
1405
+ inputSchema: {
1406
+ taskId: import_zod3.z.string().describe("The task ID"),
1407
+ errorType: import_zod3.z.nativeEnum(ErrorType).describe("Error type: BUILD_FAILURE, TEST_FAILURE, CONFLICT, AUTH_ERROR, OTHER"),
1408
+ errorMessage: import_zod3.z.string().describe("Error message (max 1000 chars)"),
1409
+ context: import_zod3.z.string().optional().describe("Additional context (max 2000 chars)")
1410
+ }
1327
1411
  },
1328
1412
  async (args) => {
1329
1413
  assertApiKeyPermission(
@@ -1355,7 +1439,10 @@ async function createMCPServer() {
1355
1439
  server.registerTool(
1356
1440
  "get_project_context",
1357
1441
  {
1358
- description: "Returns assembled context (README, stack, conventions)"
1442
+ description: "Returns assembled context (README, stack, conventions)",
1443
+ inputSchema: {
1444
+ projectId: import_zod3.z.string().describe("The project ID")
1445
+ }
1359
1446
  },
1360
1447
  async (args) => {
1361
1448
  assertApiKeyPermission(
@@ -1382,7 +1469,11 @@ async function createMCPServer() {
1382
1469
  server.registerTool(
1383
1470
  "add_note",
1384
1471
  {
1385
- description: "Append implementation notes to task"
1472
+ description: "Append implementation notes to task",
1473
+ inputSchema: {
1474
+ taskId: import_zod3.z.string().describe("The task ID"),
1475
+ content: import_zod3.z.string().describe("Note content (max 500 chars)")
1476
+ }
1386
1477
  },
1387
1478
  async (args) => {
1388
1479
  assertApiKeyPermission(mockApiKey, ApiKeyPermission.WRITE, "add_note");
@@ -1408,7 +1499,12 @@ async function createMCPServer() {
1408
1499
  server.registerTool(
1409
1500
  "abandon_task",
1410
1501
  {
1411
- description: "Unassign from a task and optionally delete the branch"
1502
+ description: "Unassign from a task and optionally delete the branch",
1503
+ inputSchema: {
1504
+ projectId: import_zod3.z.string().describe("The project ID"),
1505
+ taskId: import_zod3.z.string().describe("The task ID to abandon"),
1506
+ deleteBranch: import_zod3.z.boolean().optional().describe("Whether to delete the associated branch")
1507
+ }
1412
1508
  },
1413
1509
  async (args) => {
1414
1510
  assertApiKeyPermission(
@@ -1439,7 +1535,12 @@ async function createMCPServer() {
1439
1535
  server.registerTool(
1440
1536
  "create_personal_project",
1441
1537
  {
1442
- description: "Create project in user's personal workspace"
1538
+ description: "Create project in user's personal workspace",
1539
+ inputSchema: {
1540
+ name: import_zod3.z.string().describe("Project name (max 100 chars)"),
1541
+ description: import_zod3.z.string().optional().describe("Project description (max 500 chars)"),
1542
+ repositoryUrl: import_zod3.z.string().describe("GitHub repository URL")
1543
+ }
1443
1544
  },
1444
1545
  async (args) => {
1445
1546
  assertApiKeyPermission(
@@ -1467,6 +1568,126 @@ async function createMCPServer() {
1467
1568
  }
1468
1569
  }
1469
1570
  );
1571
+ server.registerTool(
1572
+ "create_task",
1573
+ {
1574
+ description: "Create a new task in a project. Requires WRITE permission and project access.",
1575
+ inputSchema: {
1576
+ projectId: import_zod3.z.string().describe("The project ID to create the task in"),
1577
+ epicId: import_zod3.z.string().nullable().optional().describe("Optional epic ID to associate the task with"),
1578
+ title: import_zod3.z.string().describe("Task title (max 200 chars)"),
1579
+ description: import_zod3.z.string().describe("Task description (max 5000 chars)"),
1580
+ priority: import_zod3.z.nativeEnum(TaskPriority).optional().describe("Task priority: LOW, MEDIUM, HIGH, CRITICAL (default: MEDIUM)"),
1581
+ acceptanceCriteria: import_zod3.z.array(
1582
+ import_zod3.z.object({
1583
+ description: import_zod3.z.string().describe("Acceptance criterion description (max 500 chars)")
1584
+ })
1585
+ ).describe("Array of acceptance criteria (at least 1 required)")
1586
+ }
1587
+ },
1588
+ async (args) => {
1589
+ assertApiKeyPermission(
1590
+ mockApiKey,
1591
+ ApiKeyPermission.WRITE,
1592
+ "create_task"
1593
+ );
1594
+ const validated = CreateTaskMCPInputSchema.parse(args);
1595
+ try {
1596
+ const result = await apiClient.createTask({
1597
+ projectId: validated.projectId,
1598
+ epicId: validated.epicId,
1599
+ title: validated.title,
1600
+ description: validated.description,
1601
+ priority: validated.priority,
1602
+ acceptanceCriteria: validated.acceptanceCriteria
1603
+ });
1604
+ return {
1605
+ content: [
1606
+ {
1607
+ type: "text",
1608
+ text: JSON.stringify(result, null, 2)
1609
+ }
1610
+ ]
1611
+ };
1612
+ } catch (error) {
1613
+ return handleApiError(error);
1614
+ }
1615
+ }
1616
+ );
1617
+ server.registerTool(
1618
+ "request_changes",
1619
+ {
1620
+ description: "Submit review comments requesting changes on a task in REVIEW state",
1621
+ inputSchema: {
1622
+ projectId: import_zod3.z.string().describe("The project ID"),
1623
+ taskId: import_zod3.z.string().describe("The task ID to review"),
1624
+ reviewComments: import_zod3.z.string().describe("Review comments explaining requested changes (max 5000 chars)"),
1625
+ requestedChanges: import_zod3.z.array(import_zod3.z.string()).optional().describe("List of specific changes requested")
1626
+ }
1627
+ },
1628
+ async (args) => {
1629
+ assertApiKeyPermission(
1630
+ mockApiKey,
1631
+ ApiKeyPermission.WRITE,
1632
+ "request_changes"
1633
+ );
1634
+ const validated = RequestChangesInputSchema.parse(args);
1635
+ try {
1636
+ const result = await apiClient.requestChanges(
1637
+ validated.taskId,
1638
+ validated.projectId,
1639
+ validated.reviewComments,
1640
+ validated.requestedChanges
1641
+ );
1642
+ return {
1643
+ content: [
1644
+ {
1645
+ type: "text",
1646
+ text: JSON.stringify(result, null, 2)
1647
+ }
1648
+ ]
1649
+ };
1650
+ } catch (error) {
1651
+ return handleApiError(error);
1652
+ }
1653
+ }
1654
+ );
1655
+ server.registerTool(
1656
+ "approve_task",
1657
+ {
1658
+ description: "Approve a task in REVIEW state and mark it as DONE",
1659
+ inputSchema: {
1660
+ projectId: import_zod3.z.string().describe("The project ID"),
1661
+ taskId: import_zod3.z.string().describe("The task ID to approve"),
1662
+ reviewComments: import_zod3.z.string().optional().describe("Optional approval comments (max 2000 chars)")
1663
+ }
1664
+ },
1665
+ async (args) => {
1666
+ assertApiKeyPermission(
1667
+ mockApiKey,
1668
+ ApiKeyPermission.WRITE,
1669
+ "approve_task"
1670
+ );
1671
+ const validated = ApproveTaskInputSchema.parse(args);
1672
+ try {
1673
+ const result = await apiClient.approveTask(
1674
+ validated.taskId,
1675
+ validated.projectId,
1676
+ validated.reviewComments
1677
+ );
1678
+ return {
1679
+ content: [
1680
+ {
1681
+ type: "text",
1682
+ text: JSON.stringify(result, null, 2)
1683
+ }
1684
+ ]
1685
+ };
1686
+ } catch (error) {
1687
+ return handleApiError(error);
1688
+ }
1689
+ }
1690
+ );
1470
1691
  server.registerTool(
1471
1692
  "get_version",
1472
1693
  {