@mtaap/mcp 0.2.6 → 0.2.8

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.
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Collab MCP Server
3
+ *
4
+ * Model Context Protocol server for AI agent integration.
5
+ * Uses REST API to communicate with the Collab webapp.
6
+ */
7
+ declare function createMCPServer(): Promise<void>;
8
+
9
+ export { createMCPServer };
package/dist/index.js CHANGED
@@ -37,7 +37,7 @@ 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.6";
40
+ var VERSION = "0.2.7";
41
41
 
42
42
  // src/index.ts
43
43
  var import_zod3 = require("zod");
@@ -429,6 +429,7 @@ var ListTasksInputSchema = import_zod2.z.object({
429
429
  includeArchived: import_zod2.z.boolean().optional()
430
430
  });
431
431
  var cuidOrPrefixedId = import_zod2.z.string().regex(/^([a-z0-9]+|[a-z]+_[a-zA-Z0-9]+)$/);
432
+ var gitBranchName = import_zod2.z.string().min(1).max(100).regex(/^[a-zA-Z0-9][-a-zA-Z0-9._/]*[a-zA-Z0-9]$|^[a-zA-Z0-9]$/, "Branch name must start and end with alphanumeric character").refine((val) => !val.includes("..") && !val.includes("@{") && !val.includes("//") && !val.endsWith(".lock") && !val.includes("~") && !val.includes("^") && !val.includes(":") && !val.includes("?") && !val.includes("*") && !val.includes("[") && !val.includes("\\") && !val.includes(" ") && !val.includes(";") && !val.includes("&") && !val.includes("|") && !val.includes("$") && !val.includes("`") && !val.includes("'") && !val.includes('"') && !val.includes("<") && !val.includes(">") && !val.includes("(") && !val.includes(")"), "Invalid branch name: contains forbidden characters or sequences");
432
433
  var GetTaskInputSchema = import_zod2.z.object({
433
434
  taskId: cuidOrPrefixedId
434
435
  });
@@ -640,6 +641,17 @@ var UpdateTaskMCPInputSchema = import_zod2.z.object({
640
641
  description: import_zod2.z.string().min(1).max(500)
641
642
  })).optional()
642
643
  });
644
+ var ReportBranchInputSchema = import_zod2.z.object({
645
+ projectId: cuidOrPrefixedId,
646
+ taskId: cuidOrPrefixedId,
647
+ branchName: gitBranchName
648
+ });
649
+ var ReportPRInputSchema = import_zod2.z.object({
650
+ projectId: cuidOrPrefixedId,
651
+ taskId: cuidOrPrefixedId,
652
+ pullRequestUrl: import_zod2.z.string().url(),
653
+ pullRequestNumber: import_zod2.z.number().int().positive()
654
+ });
643
655
 
644
656
  // ../../packages/core/dist/logging/metrics.js
645
657
  var metrics = /* @__PURE__ */ new Map();
@@ -939,6 +951,12 @@ var errorTrackerInstance = new NoOpErrorTracker();
939
951
 
940
952
  // src/api-client.ts
941
953
  var DEFAULT_TIMEOUT = 3e4;
954
+ function sanitizeForLogging(str) {
955
+ let sanitized = str.replace(/\bcollab_[a-zA-Z0-9_-]+\b/gi, "[REDACTED_API_KEY]");
956
+ sanitized = sanitized.replace(/\bBearer\s+[a-zA-Z0-9._-]+\b/gi, "Bearer [REDACTED]");
957
+ sanitized = sanitized.replace(/([?&](api_?key|token|auth|key|secret)=)[^&\s]+/gi, "$1[REDACTED]");
958
+ return sanitized;
959
+ }
942
960
  var ApiError = class extends Error {
943
961
  constructor(message, code, status, details) {
944
962
  super(message);
@@ -968,7 +986,7 @@ var MCPApiClient = class {
968
986
  const controller = new AbortController();
969
987
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
970
988
  if (this.debug) {
971
- console.error(`[mcp-api] ${method} ${url}`);
989
+ console.error(`[mcp-api] ${method} ${sanitizeForLogging(path)}`);
972
990
  }
973
991
  try {
974
992
  const response = await fetch(url, {
@@ -1003,8 +1021,9 @@ var MCPApiClient = class {
1003
1021
  408
1004
1022
  );
1005
1023
  }
1024
+ const rawMessage = error instanceof Error ? error.message : "Unknown error";
1006
1025
  throw new ApiError(
1007
- error instanceof Error ? error.message : "Unknown error",
1026
+ sanitizeForLogging(rawMessage),
1008
1027
  "NETWORK_ERROR",
1009
1028
  0
1010
1029
  );
@@ -1102,7 +1121,8 @@ var MCPApiClient = class {
1102
1121
  return this.request("POST", `/api/mcp/tasks/${taskId}/progress`, data);
1103
1122
  }
1104
1123
  /**
1105
- * Complete task and create PR
1124
+ * Complete task and prepare for PR creation.
1125
+ * Returns PR suggestions for the agent to use when creating the PR locally.
1106
1126
  */
1107
1127
  async completeTask(taskId, projectId, pullRequestTitle, pullRequestBody) {
1108
1128
  return this.request("POST", `/api/mcp/tasks/${taskId}/complete`, {
@@ -1172,11 +1192,23 @@ var MCPApiClient = class {
1172
1192
  });
1173
1193
  }
1174
1194
  /**
1175
- * Get GitHub token
1195
+ * Report branch created by agent
1176
1196
  */
1177
- async getGitHubToken(organizationId) {
1178
- const path = organizationId ? `/api/mcp/github-token?organizationId=${encodeURIComponent(organizationId)}` : "/api/mcp/github-token";
1179
- return this.request("GET", path);
1197
+ async reportBranch(taskId, projectId, branchName) {
1198
+ return this.request("POST", `/api/mcp/tasks/${taskId}/branch`, {
1199
+ projectId,
1200
+ branchName
1201
+ });
1202
+ }
1203
+ /**
1204
+ * Report PR created by agent
1205
+ */
1206
+ async reportPR(taskId, projectId, pullRequestUrl, pullRequestNumber) {
1207
+ return this.request("POST", `/api/mcp/tasks/${taskId}/pr`, {
1208
+ projectId,
1209
+ pullRequestUrl,
1210
+ pullRequestNumber
1211
+ });
1180
1212
  }
1181
1213
  /**
1182
1214
  * Verify a DRAFT task to move it to TODO state
@@ -1265,10 +1297,13 @@ TOOL CATEGORIES:
1265
1297
  4. Task Execution (WRITE):
1266
1298
  - assign_task, update_progress, add_note, complete_task, abandon_task, report_error
1267
1299
 
1268
- 5. Code Review (WRITE):
1300
+ 5. Git Operations (WRITE):
1301
+ - report_branch, report_pr
1302
+
1303
+ 6. Code Review (WRITE):
1269
1304
  - request_changes, approve_task
1270
1305
 
1271
- 6. Session Management (READ):
1306
+ 7. Session Management (READ):
1272
1307
  - check_active_task
1273
1308
 
1274
1309
  WORKFLOWS:
@@ -1277,7 +1312,15 @@ Task Creation & Verification:
1277
1312
  create_task -> get_task_prompt (DRAFT) -> verify_task(approved=true) -> task moves to TODO
1278
1313
 
1279
1314
  Standard Task Workflow:
1280
- list_projects -> get_project_context -> list_tasks(state=TODO) -> get_task -> get_task_prompt (TODO) -> assign_task -> [update_progress...] -> complete_task
1315
+ list_projects -> get_project_context -> list_tasks(state=TODO) -> get_task -> get_task_prompt (TODO)
1316
+ -> assign_task (returns suggested branch name)
1317
+ -> git checkout -b <branchName> (local git command)
1318
+ -> git push -u origin <branchName> (local git command)
1319
+ -> report_branch (tell server about the branch)
1320
+ -> [update_progress...]
1321
+ -> complete_task (returns PR suggestions)
1322
+ -> gh pr create (local gh command)
1323
+ -> report_pr (tell server about the PR)
1281
1324
 
1282
1325
  Resume Workflow:
1283
1326
  check_active_task -> (if active) get_task -> get_task_prompt (IN_PROGRESS) -> update_progress -> complete_task
@@ -1289,9 +1332,15 @@ Task Editing:
1289
1332
  update_task (DRAFT/TODO only) -> if was TODO, reverts to DRAFT -> verify_task again
1290
1333
 
1291
1334
  Error Recovery:
1292
- report_error -> abandon_task(deleteBranch=false) -> list_tasks -> assign_task (retry or pick different task)
1335
+ report_error -> abandon_task -> list_tasks -> assign_task (retry or pick different task)
1293
1336
  (abandon_task returns IN_PROGRESS tasks to TODO state)
1294
1337
 
1338
+ GIT OPERATIONS NOTE:
1339
+ The agent handles all git operations locally. After assign_task returns a suggested branch name,
1340
+ the agent creates the branch with git, pushes it, and reports it via report_branch.
1341
+ After complete_task returns PR suggestions, the agent creates the PR with gh, and reports it via report_pr.
1342
+ This eliminates the need for GitHub tokens on the server.
1343
+
1295
1344
  TASK STATE FLOW:
1296
1345
  DRAFT -> TODO -> IN_PROGRESS -> REVIEW -> DONE
1297
1346
  (verify_task: DRAFT -> TODO)
@@ -1308,7 +1357,8 @@ CONSTRAINTS:
1308
1357
  - complete_task requires IN_PROGRESS state
1309
1358
  - request_changes/approve_task require REVIEW state
1310
1359
  - Always check_active_task before starting new work
1311
- - Call update_progress frequently to checkpoint`;
1360
+ - Call update_progress frequently to checkpoint
1361
+ - Agent must have git/gh CLI configured for local git operations`;
1312
1362
  async function createMCPServer() {
1313
1363
  const server = new import_mcp.McpServer(
1314
1364
  {
@@ -1498,7 +1548,7 @@ async function createMCPServer() {
1498
1548
  server.registerTool(
1499
1549
  "complete_task",
1500
1550
  {
1501
- description: "Finish task and create pull request. Moves task to REVIEW. Requires IN_PROGRESS state.",
1551
+ description: "Prepare task for PR creation. Returns PR suggestions. After creating PR locally, call report_pr to transition to REVIEW. Requires IN_PROGRESS state.",
1502
1552
  inputSchema: {
1503
1553
  projectId: import_zod3.z.string().describe("The project ID"),
1504
1554
  taskId: import_zod3.z.string().describe("The task ID to complete"),
@@ -1689,6 +1739,80 @@ async function createMCPServer() {
1689
1739
  }
1690
1740
  }
1691
1741
  );
1742
+ server.registerTool(
1743
+ "report_branch",
1744
+ {
1745
+ description: "Report a branch created by the agent. Call after using git to create and push a branch. Task must be IN_PROGRESS.",
1746
+ inputSchema: {
1747
+ projectId: import_zod3.z.string().describe("The project ID"),
1748
+ taskId: import_zod3.z.string().describe("The task ID"),
1749
+ branchName: import_zod3.z.string().describe("Name of the branch created (e.g., feature/TASK-123-fix-login)")
1750
+ }
1751
+ },
1752
+ async (args) => {
1753
+ assertApiKeyPermission(
1754
+ mockApiKey,
1755
+ ApiKeyPermission.WRITE,
1756
+ "report_branch"
1757
+ );
1758
+ const validated = ReportBranchInputSchema.parse(args);
1759
+ try {
1760
+ const result = await apiClient.reportBranch(
1761
+ validated.taskId,
1762
+ validated.projectId,
1763
+ validated.branchName
1764
+ );
1765
+ return {
1766
+ content: [
1767
+ {
1768
+ type: "text",
1769
+ text: JSON.stringify(result, null, 2)
1770
+ }
1771
+ ]
1772
+ };
1773
+ } catch (error) {
1774
+ return handleApiError(error);
1775
+ }
1776
+ }
1777
+ );
1778
+ server.registerTool(
1779
+ "report_pr",
1780
+ {
1781
+ description: "Report a PR created by the agent. Call after using gh pr create. Transitions task to REVIEW state.",
1782
+ inputSchema: {
1783
+ projectId: import_zod3.z.string().describe("The project ID"),
1784
+ taskId: import_zod3.z.string().describe("The task ID"),
1785
+ pullRequestUrl: import_zod3.z.string().describe("Full URL of the created PR (e.g., https://github.com/owner/repo/pull/123)"),
1786
+ pullRequestNumber: import_zod3.z.number().describe("PR number (e.g., 123)")
1787
+ }
1788
+ },
1789
+ async (args) => {
1790
+ assertApiKeyPermission(
1791
+ mockApiKey,
1792
+ ApiKeyPermission.WRITE,
1793
+ "report_pr"
1794
+ );
1795
+ const validated = ReportPRInputSchema.parse(args);
1796
+ try {
1797
+ const result = await apiClient.reportPR(
1798
+ validated.taskId,
1799
+ validated.projectId,
1800
+ validated.pullRequestUrl,
1801
+ validated.pullRequestNumber
1802
+ );
1803
+ return {
1804
+ content: [
1805
+ {
1806
+ type: "text",
1807
+ text: JSON.stringify(result, null, 2)
1808
+ }
1809
+ ]
1810
+ };
1811
+ } catch (error) {
1812
+ return handleApiError(error);
1813
+ }
1814
+ }
1815
+ );
1692
1816
  server.registerTool(
1693
1817
  "archive_task",
1694
1818
  {
@@ -2085,22 +2209,76 @@ function handleApiError(error) {
2085
2209
  isError: true
2086
2210
  };
2087
2211
  }
2212
+ var ActiveTaskSchema = import_zod3.z.object({
2213
+ taskId: import_zod3.z.string().min(1),
2214
+ projectId: import_zod3.z.string().min(1),
2215
+ branchName: import_zod3.z.string().optional(),
2216
+ startedAt: import_zod3.z.string().optional()
2217
+ });
2088
2218
  async function checkActiveTask() {
2089
2219
  const fs = await import("fs");
2090
2220
  const path = await import("path");
2091
- const activeTaskPath = path.join(process.cwd(), ".collab", "active-task.json");
2221
+ const cwd = process.cwd();
2222
+ const collabDir = path.join(cwd, ".collab");
2223
+ const activeTaskPath = path.join(collabDir, "active-task.json");
2092
2224
  try {
2093
- await fs.promises.access(activeTaskPath);
2225
+ const resolvedPath = path.resolve(activeTaskPath);
2226
+ const resolvedCollabDir = path.resolve(collabDir);
2227
+ if (!resolvedPath.startsWith(resolvedCollabDir + path.sep) && resolvedPath !== resolvedCollabDir) {
2228
+ return {
2229
+ hasActiveTask: false,
2230
+ task: null,
2231
+ error: "Invalid active task path"
2232
+ };
2233
+ }
2234
+ const stats = await fs.promises.lstat(activeTaskPath);
2235
+ if (stats.isSymbolicLink()) {
2236
+ return {
2237
+ hasActiveTask: false,
2238
+ task: null,
2239
+ error: "Symlinks not allowed for active task file"
2240
+ };
2241
+ }
2242
+ if (!stats.isFile()) {
2243
+ return {
2244
+ hasActiveTask: false,
2245
+ task: null
2246
+ };
2247
+ }
2094
2248
  const content = await fs.promises.readFile(activeTaskPath, "utf-8");
2095
- const activeTask = JSON.parse(content);
2249
+ let parsed;
2250
+ try {
2251
+ parsed = JSON.parse(content);
2252
+ } catch {
2253
+ return {
2254
+ hasActiveTask: false,
2255
+ task: null,
2256
+ error: "Invalid JSON in active task file"
2257
+ };
2258
+ }
2259
+ const validationResult = ActiveTaskSchema.safeParse(parsed);
2260
+ if (!validationResult.success) {
2261
+ return {
2262
+ hasActiveTask: false,
2263
+ task: null,
2264
+ error: "Active task file has invalid structure"
2265
+ };
2266
+ }
2096
2267
  return {
2097
2268
  hasActiveTask: true,
2098
- task: activeTask
2269
+ task: validationResult.data
2099
2270
  };
2100
- } catch {
2271
+ } catch (err) {
2272
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
2273
+ return {
2274
+ hasActiveTask: false,
2275
+ task: null
2276
+ };
2277
+ }
2101
2278
  return {
2102
2279
  hasActiveTask: false,
2103
- task: null
2280
+ task: null,
2281
+ error: "Failed to read active task file"
2104
2282
  };
2105
2283
  }
2106
2284
  }