@mtaap/mcp 0.2.13 → 0.5.1

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/server.js CHANGED
@@ -36,15 +36,18 @@ var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
36
36
  // package.json
37
37
  var package_default = {
38
38
  name: "@mtaap/mcp",
39
- version: "0.2.12",
39
+ version: "0.5.1",
40
40
  description: "Model Context Protocol (MCP) server for AI agents to interact with Collab - the multi-tenant collaborative agent development platform",
41
41
  mcpName: "collab",
42
42
  scripts: {
43
- build: "tsup"
43
+ build: "pnpm build:ui && tsup",
44
+ "build:ui": "node scripts/build-ui.mjs",
45
+ "dev:ui": "vite build --watch"
44
46
  },
45
47
  main: "./dist/index.js",
46
48
  types: "./dist/index.d.ts",
47
49
  bin: {
50
+ mcp: "./dist/cli.js",
48
51
  "collab-mcp": "./dist/cli.js",
49
52
  "collab-mcp-server": "./dist/server.js"
50
53
  },
@@ -77,6 +80,7 @@ var package_default = {
77
80
  },
78
81
  dependencies: {
79
82
  "@modelcontextprotocol/sdk": "^1.0.0",
83
+ "@modelcontextprotocol/ext-apps": "^1.0.1",
80
84
  express: "^5.0.1",
81
85
  zod: "^4.3.5"
82
86
  },
@@ -85,8 +89,18 @@ var package_default = {
85
89
  "@mtaap/core": "workspace:*",
86
90
  "@types/express": "^5.0.0",
87
91
  "@types/node": "^22.0.0",
92
+ "@types/react": "^19.0.0",
93
+ "@types/react-dom": "^19.0.0",
94
+ "@vitejs/plugin-react": "^4.4.0",
95
+ autoprefixer: "^10.4.21",
96
+ postcss: "^8.5.3",
97
+ react: "^19.0.0",
98
+ "react-dom": "^19.0.0",
99
+ tailwindcss: "^3.4.17",
88
100
  tsup: "^8.5.1",
89
- typescript: "^5.4.0"
101
+ typescript: "^5.4.0",
102
+ vite: "^6.3.2",
103
+ "vite-plugin-singlefile": "^2.0.3"
90
104
  }
91
105
  };
92
106
 
@@ -94,7 +108,7 @@ var package_default = {
94
108
  var VERSION = package_default.version;
95
109
 
96
110
  // src/index.ts
97
- var import_zod3 = require("zod");
111
+ var import_zod9 = require("zod");
98
112
 
99
113
  // ../../packages/core/dist/constants/enums.js
100
114
  var TaskState;
@@ -180,13 +194,11 @@ var WebSocketEventType;
180
194
  WebSocketEventType2["TASK_UPDATED"] = "task.updated";
181
195
  WebSocketEventType2["TASK_DELETED"] = "task.deleted";
182
196
  WebSocketEventType2["MEMBER_JOINED"] = "member.joined";
197
+ WebSocketEventType2["AGENT_STARTED"] = "agent.started";
198
+ WebSocketEventType2["AGENT_OUTPUT"] = "agent.output";
199
+ WebSocketEventType2["AGENT_STOPPED"] = "agent.stopped";
200
+ WebSocketEventType2["AGENT_ERROR"] = "agent.error";
183
201
  })(WebSocketEventType || (WebSocketEventType = {}));
184
- var AuthProvider;
185
- (function(AuthProvider2) {
186
- AuthProvider2["CREDENTIALS"] = "credentials";
187
- AuthProvider2["LDAP"] = "ldap";
188
- AuthProvider2["SSO"] = "sso";
189
- })(AuthProvider || (AuthProvider = {}));
190
202
  var SubscriptionStatus;
191
203
  (function(SubscriptionStatus2) {
192
204
  SubscriptionStatus2["ACTIVE"] = "ACTIVE";
@@ -202,6 +214,37 @@ var EventType;
202
214
  EventType2["ACCESS"] = "ACCESS";
203
215
  EventType2["MODIFICATION"] = "MODIFICATION";
204
216
  })(EventType || (EventType = {}));
217
+ var CreatedVia;
218
+ (function(CreatedVia2) {
219
+ CreatedVia2["UI"] = "UI";
220
+ CreatedVia2["API_KEY"] = "API_KEY";
221
+ CreatedVia2["OAUTH"] = "OAUTH";
222
+ })(CreatedVia || (CreatedVia = {}));
223
+ var AgentModel;
224
+ (function(AgentModel2) {
225
+ AgentModel2["OPUS"] = "OPUS";
226
+ AgentModel2["SONNET"] = "SONNET";
227
+ AgentModel2["HAIKU"] = "HAIKU";
228
+ })(AgentModel || (AgentModel = {}));
229
+ var AgentModelMode;
230
+ (function(AgentModelMode2) {
231
+ AgentModelMode2["DEFAULT"] = "DEFAULT";
232
+ AgentModelMode2["PRESET"] = "PRESET";
233
+ AgentModelMode2["CUSTOM"] = "CUSTOM";
234
+ })(AgentModelMode || (AgentModelMode = {}));
235
+ var AgentCLIType;
236
+ (function(AgentCLIType2) {
237
+ AgentCLIType2["CLAUDE_CODE"] = "CLAUDE_CODE";
238
+ AgentCLIType2["OPENCODE"] = "OPENCODE";
239
+ })(AgentCLIType || (AgentCLIType = {}));
240
+ var AgentSessionStatus;
241
+ (function(AgentSessionStatus2) {
242
+ AgentSessionStatus2["STARTING"] = "STARTING";
243
+ AgentSessionStatus2["RUNNING"] = "RUNNING";
244
+ AgentSessionStatus2["STOPPING"] = "STOPPING";
245
+ AgentSessionStatus2["STOPPED"] = "STOPPED";
246
+ AgentSessionStatus2["ERROR"] = "ERROR";
247
+ })(AgentSessionStatus || (AgentSessionStatus = {}));
205
248
 
206
249
  // ../../packages/core/dist/constants/state-machine.js
207
250
  var VALID_TRANSITIONS = {
@@ -225,6 +268,62 @@ var VALID_TRANSITIONS = {
225
268
  [TaskState.DONE]: []
226
269
  };
227
270
 
271
+ // ../../packages/core/dist/constants/oauth.js
272
+ var OAuthScopes = {
273
+ /** Read-only access to MCP resources */
274
+ READ: "mcp:read",
275
+ /** Read and write access to MCP resources */
276
+ WRITE: "mcp:write",
277
+ /** Full administrative access */
278
+ ADMIN: "mcp:admin"
279
+ };
280
+ var VALID_OAUTH_SCOPES = [
281
+ OAuthScopes.READ,
282
+ OAuthScopes.WRITE,
283
+ OAuthScopes.ADMIN
284
+ ];
285
+ var DEFAULT_OAUTH_SCOPES = `${OAuthScopes.READ} ${OAuthScopes.WRITE}`;
286
+ var OAUTH_SCOPE_TO_PERMISSION = {
287
+ [OAuthScopes.READ]: ApiKeyPermission.READ,
288
+ [OAuthScopes.WRITE]: ApiKeyPermission.WRITE,
289
+ [OAuthScopes.ADMIN]: ApiKeyPermission.ADMIN
290
+ };
291
+ var PERMISSION_TO_OAUTH_SCOPE = {
292
+ [ApiKeyPermission.READ]: OAuthScopes.READ,
293
+ [ApiKeyPermission.WRITE]: OAuthScopes.WRITE,
294
+ [ApiKeyPermission.ADMIN]: OAuthScopes.ADMIN
295
+ };
296
+ var OAuthTokenLifetimes = {
297
+ /** Access token lifetime: 1 hour */
298
+ ACCESS_TOKEN_MS: 60 * 60 * 1e3,
299
+ /** Refresh token lifetime: 30 days */
300
+ REFRESH_TOKEN_MS: 30 * 24 * 60 * 60 * 1e3,
301
+ /** Authorization code lifetime: 10 minutes */
302
+ AUTHORIZATION_CODE_MS: 10 * 60 * 1e3
303
+ };
304
+ var OAuthGrantTypes = {
305
+ AUTHORIZATION_CODE: "authorization_code",
306
+ REFRESH_TOKEN: "refresh_token"
307
+ };
308
+ var SUPPORTED_GRANT_TYPES = [
309
+ OAuthGrantTypes.AUTHORIZATION_CODE,
310
+ OAuthGrantTypes.REFRESH_TOKEN
311
+ ];
312
+ var OAuthResponseTypes = {
313
+ CODE: "code"
314
+ };
315
+ var OAuthCodeChallengeMethods = {
316
+ S256: "S256"
317
+ };
318
+ var OAuthRateLimits = {
319
+ /** /oauth/token: 30 requests per minute per client */
320
+ TOKEN_ENDPOINT: { limit: 30, windowMs: 60 * 1e3 },
321
+ /** /oauth/authorize: 10 requests per minute per user */
322
+ AUTHORIZE_ENDPOINT: { limit: 10, windowMs: 60 * 1e3 },
323
+ /** /oauth/register: 5 requests per minute per IP */
324
+ REGISTER_ENDPOINT: { limit: 5, windowMs: 60 * 1e3 }
325
+ };
326
+
228
327
  // ../../packages/core/dist/config/deployment.js
229
328
  var config = {
230
329
  deploymentMode: process.env.DEPLOYMENT_MODE || "saas"
@@ -232,20 +331,24 @@ var config = {
232
331
  var isSaas = config.deploymentMode === "saas";
233
332
  var isOnPrem = config.deploymentMode === "onprem";
234
333
 
235
- // ../../packages/core/dist/version.js
236
- var VERSION2 = "0.1.0";
334
+ // ../../packages/core/dist/versions.js
335
+ var VERSIONS = {
336
+ core: "0.4.0",
337
+ web: "0.4.0",
338
+ mcp: "0.5.0"
339
+ };
340
+ var VERSION2 = VERSIONS.core;
237
341
 
238
342
  // ../../packages/core/dist/config/index.js
239
343
  var DEPLOYMENT_MODE = process.env.DEPLOYMENT_MODE || "saas";
240
344
  var config2 = {
241
345
  version: VERSION2,
346
+ packages: VERSIONS,
242
347
  deploymentMode: DEPLOYMENT_MODE,
243
348
  billing: {
244
349
  enabled: DEPLOYMENT_MODE === "saas",
245
350
  revenuecat: {
246
- publicKey: process.env.REVENUECAT_PUBLIC_API_KEY,
247
- stripeKey: process.env.STRIPE_SECRET_KEY
248
- // Required by RevenueCat Web Billing
351
+ publicKey: process.env.NEXT_PUBLIC_REVENUECAT_PUBLIC_KEY
249
352
  }
250
353
  },
251
354
  licensing: {
@@ -313,6 +416,18 @@ var config2 = {
313
416
  requestsPerHour: 1e3
314
417
  }
315
418
  },
419
+ agent: {
420
+ defaultModel: "SONNET",
421
+ sessionTimeoutMs: 30 * 60 * 1e3,
422
+ // 30 minutes
423
+ maxSessionDurationMs: 4 * 60 * 60 * 1e3,
424
+ // 4 hours
425
+ maxConcurrentSessions: {
426
+ FREE: 1,
427
+ PRO: 5,
428
+ ENTERPRISE: -1
429
+ }
430
+ },
316
431
  limits: {
317
432
  projectNameMaxLength: 100,
318
433
  taskDescriptionMaxLength: 5e3,
@@ -351,7 +466,6 @@ var OrganizationIdSchema = import_zod.z.string().regex(/^org_[a-zA-Z0-9]+$/);
351
466
  var OrganizationSchema = import_zod.z.object({
352
467
  id: OrganizationIdSchema,
353
468
  name: import_zod.z.string().min(1).max(255),
354
- slug: import_zod.z.string().min(1).max(100).regex(/^[a-z0-9-]+$/),
355
469
  logoUrl: import_zod.z.string().url().nullable(),
356
470
  accentColor: import_zod.z.string().regex(/^#[0-9A-Fa-f]{6}$/).nullable(),
357
471
  tenantName: import_zod.z.string().nullable(),
@@ -479,239 +593,376 @@ var ProjectCollaboratorSchema = import_zod.z.object({
479
593
  });
480
594
 
481
595
  // ../../packages/core/dist/validation/index.js
596
+ var import_zod3 = require("zod");
597
+
598
+ // ../../packages/core/dist/validation/oauth.js
482
599
  var import_zod2 = require("zod");
483
- var ListProjectsInputSchema = import_zod2.z.object({
484
- workspaceType: import_zod2.z.preprocess((val) => typeof val === "string" ? val.toUpperCase() : val, import_zod2.z.enum(["TEAM", "PERSONAL", "ALL"]).optional())
485
- });
486
- var ListTasksInputSchema = import_zod2.z.object({
487
- projectId: import_zod2.z.string().optional(),
488
- state: import_zod2.z.nativeEnum(TaskState).optional(),
489
- assigneeId: import_zod2.z.string().optional(),
490
- includeArchived: import_zod2.z.boolean().optional()
491
- });
492
- var cuidOrPrefixedId = import_zod2.z.string().regex(/^([a-z0-9]+|[a-z]+_[a-zA-Z0-9]+)$/);
493
- 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");
494
- var GetTaskInputSchema = import_zod2.z.object({
600
+ var scopeString = import_zod2.z.string().optional().transform((val) => {
601
+ if (!val)
602
+ return void 0;
603
+ const scopes = val.split(" ").filter(Boolean);
604
+ const validScopes = scopes.filter((s) => VALID_OAUTH_SCOPES.includes(s));
605
+ return validScopes.length > 0 ? validScopes.join(" ") : void 0;
606
+ });
607
+ var redirectUri = import_zod2.z.string().url().refine((uri) => {
608
+ const url = new URL(uri);
609
+ return url.protocol === "https:" || url.hostname === "localhost" || url.hostname === "127.0.0.1";
610
+ }, { message: "redirect_uri must use HTTPS or be localhost" });
611
+ var codeVerifier = import_zod2.z.string().min(43).max(128).regex(/^[A-Za-z0-9._~-]+$/, "code_verifier must only contain unreserved characters");
612
+ var codeChallenge = import_zod2.z.string().min(43).max(128).regex(/^[A-Za-z0-9_-]+$/, "code_challenge must be base64url encoded (no padding)");
613
+ var DynamicClientRegistrationSchema = import_zod2.z.object({
614
+ redirect_uris: import_zod2.z.array(redirectUri).min(1),
615
+ client_name: import_zod2.z.string().min(1).max(255),
616
+ client_uri: import_zod2.z.string().url().optional(),
617
+ logo_uri: import_zod2.z.string().url().optional(),
618
+ // Accept lowercase OAuth spec values
619
+ grant_types: import_zod2.z.array(import_zod2.z.enum(["authorization_code", "refresh_token"])).default(["authorization_code", "refresh_token"]),
620
+ response_types: import_zod2.z.array(import_zod2.z.enum([OAuthResponseTypes.CODE])).default([OAuthResponseTypes.CODE]),
621
+ scope: scopeString,
622
+ // Accept lowercase OAuth spec values
623
+ token_endpoint_auth_method: import_zod2.z.enum(["none", "client_secret_post", "client_secret_basic"]).default("none")
624
+ });
625
+ var DynamicClientRegistrationResponseSchema = import_zod2.z.object({
626
+ client_id: import_zod2.z.string(),
627
+ client_secret: import_zod2.z.string().optional(),
628
+ client_id_issued_at: import_zod2.z.number(),
629
+ client_secret_expires_at: import_zod2.z.number().optional(),
630
+ redirect_uris: import_zod2.z.array(import_zod2.z.string()),
631
+ client_name: import_zod2.z.string(),
632
+ client_uri: import_zod2.z.string().optional(),
633
+ logo_uri: import_zod2.z.string().optional(),
634
+ grant_types: import_zod2.z.array(import_zod2.z.string()),
635
+ response_types: import_zod2.z.array(import_zod2.z.string()),
636
+ scope: import_zod2.z.string(),
637
+ token_endpoint_auth_method: import_zod2.z.string(),
638
+ registration_access_token: import_zod2.z.string().optional(),
639
+ registration_client_uri: import_zod2.z.string().optional()
640
+ });
641
+ var AuthorizationRequestSchema = import_zod2.z.object({
642
+ response_type: import_zod2.z.literal(OAuthResponseTypes.CODE),
643
+ client_id: import_zod2.z.string().min(1),
644
+ redirect_uri: redirectUri,
645
+ scope: scopeString,
646
+ state: import_zod2.z.string().max(255).optional(),
647
+ // PKCE is mandatory in OAuth 2.1
648
+ code_challenge: codeChallenge,
649
+ code_challenge_method: import_zod2.z.literal(OAuthCodeChallengeMethods.S256)
650
+ });
651
+ var TokenRequestAuthorizationCodeSchema = import_zod2.z.object({
652
+ grant_type: import_zod2.z.literal(OAuthGrantTypes.AUTHORIZATION_CODE),
653
+ code: import_zod2.z.string().min(1),
654
+ redirect_uri: redirectUri,
655
+ client_id: import_zod2.z.string().min(1),
656
+ // PKCE code verifier is mandatory
657
+ code_verifier: codeVerifier,
658
+ // Client secret is optional (for confidential clients)
659
+ client_secret: import_zod2.z.string().optional()
660
+ });
661
+ var TokenRequestRefreshTokenSchema = import_zod2.z.object({
662
+ grant_type: import_zod2.z.literal(OAuthGrantTypes.REFRESH_TOKEN),
663
+ refresh_token: import_zod2.z.string().min(1),
664
+ client_id: import_zod2.z.string().min(1),
665
+ // Optional: request reduced scope
666
+ scope: scopeString,
667
+ // Client secret is optional (for confidential clients)
668
+ client_secret: import_zod2.z.string().optional()
669
+ });
670
+ var TokenRequestSchema = import_zod2.z.discriminatedUnion("grant_type", [
671
+ TokenRequestAuthorizationCodeSchema,
672
+ TokenRequestRefreshTokenSchema
673
+ ]);
674
+ var TokenResponseSchema = import_zod2.z.object({
675
+ access_token: import_zod2.z.string(),
676
+ token_type: import_zod2.z.literal("Bearer"),
677
+ expires_in: import_zod2.z.number(),
678
+ refresh_token: import_zod2.z.string().optional(),
679
+ scope: import_zod2.z.string()
680
+ });
681
+ var TokenRevocationRequestSchema = import_zod2.z.object({
682
+ token: import_zod2.z.string().min(1),
683
+ token_type_hint: import_zod2.z.enum(["access_token", "refresh_token"]).optional(),
684
+ client_id: import_zod2.z.string().min(1),
685
+ client_secret: import_zod2.z.string().optional()
686
+ });
687
+ var OAuthErrorResponseSchema = import_zod2.z.object({
688
+ error: import_zod2.z.string(),
689
+ error_description: import_zod2.z.string().optional(),
690
+ error_uri: import_zod2.z.string().url().optional(),
691
+ state: import_zod2.z.string().optional()
692
+ });
693
+ var AuthorizationServerMetadataSchema = import_zod2.z.object({
694
+ issuer: import_zod2.z.string().url(),
695
+ authorization_endpoint: import_zod2.z.string().url(),
696
+ token_endpoint: import_zod2.z.string().url(),
697
+ registration_endpoint: import_zod2.z.string().url().optional(),
698
+ revocation_endpoint: import_zod2.z.string().url().optional(),
699
+ scopes_supported: import_zod2.z.array(import_zod2.z.string()),
700
+ response_types_supported: import_zod2.z.array(import_zod2.z.string()),
701
+ grant_types_supported: import_zod2.z.array(import_zod2.z.string()),
702
+ token_endpoint_auth_methods_supported: import_zod2.z.array(import_zod2.z.string()),
703
+ code_challenge_methods_supported: import_zod2.z.array(import_zod2.z.string()),
704
+ service_documentation: import_zod2.z.string().url().optional()
705
+ });
706
+ var ProtectedResourceMetadataSchema = import_zod2.z.object({
707
+ resource: import_zod2.z.string().url(),
708
+ authorization_servers: import_zod2.z.array(import_zod2.z.string().url()),
709
+ scopes_supported: import_zod2.z.array(import_zod2.z.string()).optional(),
710
+ bearer_methods_supported: import_zod2.z.array(import_zod2.z.string()).optional(),
711
+ resource_signing_alg_values_supported: import_zod2.z.array(import_zod2.z.string()).optional(),
712
+ resource_documentation: import_zod2.z.string().url().optional()
713
+ });
714
+ var InternalTokenValidationRequestSchema = import_zod2.z.object({
715
+ access_token: import_zod2.z.string().min(1)
716
+ });
717
+ var InternalTokenValidationResponseSchema = import_zod2.z.object({
718
+ valid: import_zod2.z.boolean(),
719
+ userId: import_zod2.z.string().optional(),
720
+ userEmail: import_zod2.z.string().optional(),
721
+ userName: import_zod2.z.string().optional(),
722
+ scope: import_zod2.z.string().optional(),
723
+ permissions: import_zod2.z.string().optional(),
724
+ clientId: import_zod2.z.string().optional(),
725
+ expiresAt: import_zod2.z.string().optional()
726
+ });
727
+
728
+ // ../../packages/core/dist/validation/index.js
729
+ var ListProjectsInputSchema = import_zod3.z.object({
730
+ workspaceType: import_zod3.z.preprocess((val) => typeof val === "string" ? val.toUpperCase() : val, import_zod3.z.enum(["TEAM", "PERSONAL", "ALL"]).optional())
731
+ });
732
+ var ListTasksInputSchema = import_zod3.z.object({
733
+ projectId: import_zod3.z.string().optional(),
734
+ state: import_zod3.z.nativeEnum(TaskState).optional(),
735
+ assigneeId: import_zod3.z.string().optional(),
736
+ includeArchived: import_zod3.z.boolean().optional()
737
+ });
738
+ var cuidOrPrefixedId = import_zod3.z.string().regex(/^([a-z0-9]+|[a-z]+_[a-zA-Z0-9]+)$/);
739
+ var gitBranchName = import_zod3.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");
740
+ var GetTaskInputSchema = import_zod3.z.object({
495
741
  taskId: cuidOrPrefixedId
496
742
  });
497
- var AssignTaskInputSchema = import_zod2.z.object({
743
+ var AssignTaskInputSchema = import_zod3.z.object({
498
744
  projectId: cuidOrPrefixedId,
499
745
  taskId: cuidOrPrefixedId,
500
- expectedState: import_zod2.z.nativeEnum(TaskState).default(TaskState.TODO)
746
+ expectedState: import_zod3.z.nativeEnum(TaskState).default(TaskState.TODO)
501
747
  });
502
- var UpdateProgressInputSchema = import_zod2.z.object({
748
+ var UpdateProgressInputSchema = import_zod3.z.object({
503
749
  taskId: cuidOrPrefixedId,
504
- statusMessage: import_zod2.z.string().max(1e3).optional(),
505
- completedCheckpointIds: import_zod2.z.array(import_zod2.z.string()).optional(),
506
- currentCheckpointIndex: import_zod2.z.number().int().optional()
750
+ statusMessage: import_zod3.z.string().max(1e3).optional(),
751
+ completedCheckpointIds: import_zod3.z.array(import_zod3.z.string()).optional(),
752
+ currentCheckpointIndex: import_zod3.z.number().int().optional()
507
753
  });
508
- var CompleteTaskInputSchema = import_zod2.z.object({
754
+ var CompleteTaskInputSchema = import_zod3.z.object({
509
755
  projectId: cuidOrPrefixedId,
510
756
  taskId: cuidOrPrefixedId,
511
- pullRequestTitle: import_zod2.z.string().min(1).max(300).optional(),
512
- pullRequestBody: import_zod2.z.string().max(1e4).optional()
757
+ pullRequestTitle: import_zod3.z.string().min(1).max(300).optional(),
758
+ pullRequestBody: import_zod3.z.string().max(1e4).optional()
513
759
  });
514
- var ReportErrorInputSchema = import_zod2.z.object({
760
+ var ReportErrorInputSchema = import_zod3.z.object({
515
761
  taskId: cuidOrPrefixedId,
516
- errorType: import_zod2.z.nativeEnum(ErrorType),
517
- errorMessage: import_zod2.z.string().min(1).max(1e3),
518
- context: import_zod2.z.string().max(2e3).optional()
762
+ errorType: import_zod3.z.nativeEnum(ErrorType),
763
+ errorMessage: import_zod3.z.string().min(1).max(1e3),
764
+ context: import_zod3.z.string().max(2e3).optional()
519
765
  });
520
- var GetProjectContextInputSchema = import_zod2.z.object({
766
+ var GetProjectContextInputSchema = import_zod3.z.object({
521
767
  projectId: cuidOrPrefixedId
522
768
  });
523
- var AddNoteInputSchema = import_zod2.z.object({
769
+ var AddNoteInputSchema = import_zod3.z.object({
524
770
  taskId: cuidOrPrefixedId,
525
- content: import_zod2.z.string().min(1).max(500)
771
+ content: import_zod3.z.string().min(1).max(500)
526
772
  });
527
- var AbandonTaskInputSchema = import_zod2.z.object({
773
+ var AbandonTaskInputSchema = import_zod3.z.object({
528
774
  projectId: cuidOrPrefixedId,
529
775
  taskId: cuidOrPrefixedId,
530
- deleteBranch: import_zod2.z.boolean().optional()
776
+ deleteBranch: import_zod3.z.boolean().optional()
531
777
  });
532
- var RequestChangesInputSchema = import_zod2.z.object({
778
+ var RequestChangesInputSchema = import_zod3.z.object({
533
779
  projectId: cuidOrPrefixedId,
534
780
  taskId: cuidOrPrefixedId,
535
- reviewComments: import_zod2.z.string().min(1).max(5e3),
536
- requestedChanges: import_zod2.z.array(import_zod2.z.string().min(1).max(500)).optional()
781
+ reviewComments: import_zod3.z.string().min(1).max(5e3),
782
+ requestedChanges: import_zod3.z.array(import_zod3.z.string().min(1).max(500)).optional()
537
783
  });
538
- var ApproveTaskInputSchema = import_zod2.z.object({
784
+ var ApproveTaskInputSchema = import_zod3.z.object({
539
785
  projectId: cuidOrPrefixedId,
540
786
  taskId: cuidOrPrefixedId,
541
- reviewComments: import_zod2.z.string().max(2e3).optional()
787
+ reviewComments: import_zod3.z.string().max(2e3).optional()
542
788
  });
543
- var ArchiveTaskInputSchema = import_zod2.z.object({
789
+ var ArchiveTaskInputSchema = import_zod3.z.object({
544
790
  projectId: cuidOrPrefixedId,
545
791
  taskId: cuidOrPrefixedId
546
792
  });
547
- var UnarchiveTaskInputSchema = import_zod2.z.object({
793
+ var UnarchiveTaskInputSchema = import_zod3.z.object({
548
794
  projectId: cuidOrPrefixedId,
549
795
  taskId: cuidOrPrefixedId
550
796
  });
551
- var CreatePersonalProjectInputSchema = import_zod2.z.object({
552
- name: import_zod2.z.string().min(1).max(100),
553
- description: import_zod2.z.string().max(500).optional(),
554
- repositoryUrl: import_zod2.z.string().url()
797
+ var CreatePersonalProjectInputSchema = import_zod3.z.object({
798
+ name: import_zod3.z.string().min(1).max(100),
799
+ description: import_zod3.z.string().max(500).optional(),
800
+ repositoryUrl: import_zod3.z.string().url()
555
801
  });
556
- var CheckActiveTaskInputSchema = import_zod2.z.object({});
557
- var CreateTaskMCPInputSchema = import_zod2.z.object({
802
+ var CheckActiveTaskInputSchema = import_zod3.z.object({});
803
+ var CreateTaskMCPInputSchema = import_zod3.z.object({
558
804
  projectId: cuidOrPrefixedId,
559
805
  epicId: cuidOrPrefixedId.nullable().optional(),
560
- title: import_zod2.z.string().min(1).max(200),
561
- description: import_zod2.z.string().max(5e3),
562
- priority: import_zod2.z.nativeEnum(TaskPriority).default(TaskPriority.MEDIUM),
563
- acceptanceCriteria: import_zod2.z.array(import_zod2.z.object({
564
- description: import_zod2.z.string().min(1).max(500)
806
+ title: import_zod3.z.string().min(1).max(200),
807
+ description: import_zod3.z.string().max(5e3),
808
+ priority: import_zod3.z.nativeEnum(TaskPriority).default(TaskPriority.MEDIUM),
809
+ acceptanceCriteria: import_zod3.z.array(import_zod3.z.object({
810
+ description: import_zod3.z.string().min(1).max(500)
565
811
  })).min(1)
566
812
  });
567
- var CreateOrganizationInputSchema = import_zod2.z.object({
568
- name: import_zod2.z.string().min(1).max(255),
569
- slug: import_zod2.z.string().min(1).max(100).regex(/^[a-z0-9-]+$/).optional()
813
+ var CreateOrganizationInputSchema = import_zod3.z.object({
814
+ name: import_zod3.z.string().min(1).max(255)
570
815
  });
571
- var UpdateOrganizationInputSchema = import_zod2.z.object({
816
+ var UpdateOrganizationInputSchema = import_zod3.z.object({
572
817
  organizationId: cuidOrPrefixedId,
573
- name: import_zod2.z.string().min(1).max(255).optional(),
574
- logoUrl: import_zod2.z.string().url().nullable().optional(),
575
- accentColor: import_zod2.z.string().regex(/^#[0-9A-Fa-f]{6}$/, "Invalid hex color format. Expected #RRGGBB").nullable().optional(),
576
- tenantName: import_zod2.z.string().max(255).nullable().optional()
577
- });
578
- var CreateProjectInputSchema = import_zod2.z.object({
579
- name: import_zod2.z.string().min(1).max(100),
580
- description: import_zod2.z.string().max(500).optional(),
581
- type: import_zod2.z.nativeEnum(ProjectType),
582
- repositoryUrl: import_zod2.z.string().url(),
583
- baseBranch: import_zod2.z.string().default("develop").optional(),
584
- tags: import_zod2.z.array(import_zod2.z.string()).default([])
585
- });
586
- var UpdateProjectInputSchema = import_zod2.z.object({
587
- projectId: import_zod2.z.string().min(1).optional(),
588
- name: import_zod2.z.string().min(1).max(100).optional(),
589
- description: import_zod2.z.string().max(500).optional(),
590
- repositoryUrl: import_zod2.z.string().url().optional(),
591
- baseBranch: import_zod2.z.string().optional(),
592
- tags: import_zod2.z.array(import_zod2.z.string()).optional(),
593
- allowMemberArchive: import_zod2.z.boolean().optional()
594
- });
595
- var CreateEpicInputSchema = import_zod2.z.object({
818
+ name: import_zod3.z.string().min(1).max(255).optional(),
819
+ logoUrl: import_zod3.z.string().url().nullable().optional(),
820
+ accentColor: import_zod3.z.string().regex(/^#[0-9A-Fa-f]{6}$/, "Invalid hex color format. Expected #RRGGBB").nullable().optional(),
821
+ tenantName: import_zod3.z.string().max(255).nullable().optional()
822
+ });
823
+ var CreateProjectInputSchema = import_zod3.z.object({
824
+ name: import_zod3.z.string().min(1).max(100),
825
+ description: import_zod3.z.string().max(500).optional(),
826
+ type: import_zod3.z.nativeEnum(ProjectType),
827
+ repositoryUrl: import_zod3.z.string().url(),
828
+ baseBranch: import_zod3.z.string().default("develop").optional(),
829
+ tags: import_zod3.z.array(import_zod3.z.string()).default([])
830
+ });
831
+ var UpdateProjectInputSchema = import_zod3.z.object({
832
+ projectId: import_zod3.z.string().min(1).optional(),
833
+ name: import_zod3.z.string().min(1).max(100).optional(),
834
+ description: import_zod3.z.string().max(500).optional(),
835
+ repositoryUrl: import_zod3.z.string().url().optional(),
836
+ baseBranch: import_zod3.z.string().optional(),
837
+ tags: import_zod3.z.array(import_zod3.z.string()).optional(),
838
+ allowMemberArchive: import_zod3.z.boolean().optional(),
839
+ localRepoPath: import_zod3.z.string().max(500).optional()
840
+ });
841
+ var CreateEpicInputSchema = import_zod3.z.object({
596
842
  projectId: cuidOrPrefixedId,
597
- name: import_zod2.z.string().min(1).max(200),
598
- description: import_zod2.z.string().max(2e3).optional()
599
- });
600
- var CreateTaskInputSchema = import_zod2.z.object({
601
- projectId: import_zod2.z.string().min(1),
602
- epicId: import_zod2.z.string().min(1).nullable().optional(),
603
- title: import_zod2.z.string().min(1).max(200),
604
- description: import_zod2.z.string().max(5e3),
605
- priority: import_zod2.z.nativeEnum(TaskPriority).default(TaskPriority.MEDIUM),
606
- acceptanceCriteria: import_zod2.z.array(import_zod2.z.object({
607
- description: import_zod2.z.string().min(1).max(500)
843
+ name: import_zod3.z.string().min(1).max(200),
844
+ description: import_zod3.z.string().max(2e3).optional()
845
+ });
846
+ var CreateTaskInputSchema = import_zod3.z.object({
847
+ projectId: import_zod3.z.string().min(1),
848
+ epicId: import_zod3.z.string().min(1).nullable().optional(),
849
+ title: import_zod3.z.string().min(1).max(200),
850
+ description: import_zod3.z.string().max(5e3),
851
+ priority: import_zod3.z.nativeEnum(TaskPriority).default(TaskPriority.MEDIUM),
852
+ acceptanceCriteria: import_zod3.z.array(import_zod3.z.object({
853
+ description: import_zod3.z.string().min(1).max(500)
608
854
  })).min(1)
609
855
  });
610
- var UpdateTaskInputSchema = import_zod2.z.object({
611
- taskId: import_zod2.z.string().min(1),
612
- title: import_zod2.z.string().min(1).max(200).optional(),
613
- description: import_zod2.z.string().max(5e3).optional(),
614
- priority: import_zod2.z.nativeEnum(TaskPriority).optional(),
615
- state: import_zod2.z.nativeEnum(TaskState).optional(),
616
- assigneeId: import_zod2.z.string().nullable().optional(),
617
- acceptanceCriteria: import_zod2.z.array(import_zod2.z.object({
618
- id: import_zod2.z.string().optional(),
619
- description: import_zod2.z.string().min(1).max(500),
620
- completed: import_zod2.z.boolean().optional()
856
+ var UpdateTaskInputSchema = import_zod3.z.object({
857
+ taskId: import_zod3.z.string().min(1),
858
+ title: import_zod3.z.string().min(1).max(200).optional(),
859
+ description: import_zod3.z.string().max(5e3).optional(),
860
+ priority: import_zod3.z.nativeEnum(TaskPriority).optional(),
861
+ state: import_zod3.z.nativeEnum(TaskState).optional(),
862
+ assigneeId: import_zod3.z.string().nullable().optional(),
863
+ acceptanceCriteria: import_zod3.z.array(import_zod3.z.object({
864
+ id: import_zod3.z.string().optional(),
865
+ description: import_zod3.z.string().min(1).max(500),
866
+ completed: import_zod3.z.boolean().optional()
621
867
  })).optional()
622
868
  });
623
- var AssignTaskWebappInputSchema = import_zod2.z.object({
624
- taskId: import_zod2.z.string().min(1),
625
- userId: import_zod2.z.string().min(1)
869
+ var AssignTaskWebappInputSchema = import_zod3.z.object({
870
+ taskId: import_zod3.z.string().min(1),
871
+ userId: import_zod3.z.string().min(1)
626
872
  });
627
- var CreateTagInputSchema = import_zod2.z.object({
873
+ var CreateTagInputSchema = import_zod3.z.object({
628
874
  organizationId: cuidOrPrefixedId,
629
- name: import_zod2.z.string().min(1).max(50).regex(/^[a-zA-Z0-9\s-]+$/)
875
+ name: import_zod3.z.string().min(1).max(50).regex(/^[a-zA-Z0-9\s-]+$/)
630
876
  });
631
- var UpdateTagInputSchema = import_zod2.z.object({
632
- name: import_zod2.z.string().min(1).max(50).regex(/^[a-zA-Z0-9\s-]+$/)
877
+ var UpdateTagInputSchema = import_zod3.z.object({
878
+ name: import_zod3.z.string().min(1).max(50).regex(/^[a-zA-Z0-9\s-]+$/)
633
879
  });
634
- var UpdateOrganizationSettingsInputSchema = import_zod2.z.object({
880
+ var UpdateOrganizationSettingsInputSchema = import_zod3.z.object({
635
881
  organizationId: cuidOrPrefixedId,
636
- ldapEnabled: import_zod2.z.boolean().optional(),
637
- ldapUrl: import_zod2.z.string().url().nullable().optional(),
638
- ldapBindDN: import_zod2.z.string().nullable().optional(),
639
- ldapSearchBase: import_zod2.z.string().nullable().optional(),
640
- deleteMergedBranches: import_zod2.z.boolean().optional(),
641
- enforceConventionalCommits: import_zod2.z.boolean().optional(),
642
- maxPersonalProjectsPerUser: import_zod2.z.number().int().min(0).optional(),
643
- logoUrl: import_zod2.z.string().url().nullable().optional(),
644
- accentColor: import_zod2.z.string().regex(/^#[0-9A-Fa-f]{6}$/, "Invalid hex color format. Expected #RRGGBB").nullable().optional(),
645
- tenantName: import_zod2.z.string().max(255).nullable().optional()
646
- });
647
- var InviteUserInputSchema = import_zod2.z.object({
882
+ ldapEnabled: import_zod3.z.boolean().optional(),
883
+ ldapUrl: import_zod3.z.string().url().nullable().optional(),
884
+ ldapBindDN: import_zod3.z.string().nullable().optional(),
885
+ ldapSearchBase: import_zod3.z.string().nullable().optional(),
886
+ deleteMergedBranches: import_zod3.z.boolean().optional(),
887
+ enforceConventionalCommits: import_zod3.z.boolean().optional(),
888
+ maxPersonalProjectsPerUser: import_zod3.z.number().int().min(0).optional(),
889
+ logoUrl: import_zod3.z.string().url().nullable().optional(),
890
+ accentColor: import_zod3.z.string().regex(/^#[0-9A-Fa-f]{6}$/, "Invalid hex color format. Expected #RRGGBB").nullable().optional(),
891
+ tenantName: import_zod3.z.string().max(255).nullable().optional()
892
+ });
893
+ var InviteUserInputSchema = import_zod3.z.object({
648
894
  organizationId: cuidOrPrefixedId,
649
- email: import_zod2.z.string().email(),
650
- role: import_zod2.z.nativeEnum(UserRole).default(UserRole.MEMBER),
651
- tags: import_zod2.z.array(import_zod2.z.string()).default([])
895
+ email: import_zod3.z.string().email(),
896
+ role: import_zod3.z.nativeEnum(UserRole).default(UserRole.MEMBER),
897
+ tags: import_zod3.z.array(import_zod3.z.string()).default([])
652
898
  });
653
- var AssignUserTagsInputSchema = import_zod2.z.object({
899
+ var AssignUserTagsInputSchema = import_zod3.z.object({
654
900
  userId: cuidOrPrefixedId,
655
- tags: import_zod2.z.array(import_zod2.z.string()).min(0)
901
+ tags: import_zod3.z.array(import_zod3.z.string()).min(0)
656
902
  });
657
- var InviteCollaboratorInputSchema = import_zod2.z.object({
903
+ var InviteCollaboratorInputSchema = import_zod3.z.object({
658
904
  projectId: cuidOrPrefixedId,
659
- email: import_zod2.z.string().email()
905
+ email: import_zod3.z.string().email()
660
906
  });
661
- var PublishProjectInputSchema = import_zod2.z.object({
907
+ var PublishProjectInputSchema = import_zod3.z.object({
662
908
  projectId: cuidOrPrefixedId,
663
- transferOwnership: import_zod2.z.boolean().default(false),
664
- tags: import_zod2.z.array(import_zod2.z.string()).min(1)
909
+ transferOwnership: import_zod3.z.boolean().default(false),
910
+ tags: import_zod3.z.array(import_zod3.z.string()).min(1)
665
911
  });
666
- var GenerateApiKeyInputSchema = import_zod2.z.object({
667
- name: import_zod2.z.string().min(1).max(100),
668
- expiresInDays: import_zod2.z.number().int().min(1).max(365).default(90),
669
- permissions: import_zod2.z.nativeEnum(ApiKeyPermission).default(ApiKeyPermission.WRITE)
912
+ var GenerateApiKeyInputSchema = import_zod3.z.object({
913
+ name: import_zod3.z.string().min(1).max(100),
914
+ publicNickname: import_zod3.z.string().max(50).optional(),
915
+ expiresInDays: import_zod3.z.number().int().min(1).max(365).default(90),
916
+ permissions: import_zod3.z.nativeEnum(ApiKeyPermission).default(ApiKeyPermission.WRITE)
670
917
  });
671
- var RevokeApiKeyInputSchema = import_zod2.z.object({
918
+ var UpdateApiKeyInputSchema = import_zod3.z.object({
919
+ publicNickname: import_zod3.z.string().max(50).nullable().optional(),
920
+ scopedOrganizationId: import_zod3.z.string().optional().nullable(),
921
+ scopedProjectIds: import_zod3.z.array(import_zod3.z.string()).optional()
922
+ });
923
+ var RevokeApiKeyInputSchema = import_zod3.z.object({
672
924
  keyId: cuidOrPrefixedId
673
925
  });
674
- var LoginInputSchema = import_zod2.z.object({
675
- email: import_zod2.z.string().email(),
676
- password: import_zod2.z.string().min(8).max(255)
926
+ var LoginInputSchema = import_zod3.z.object({
927
+ email: import_zod3.z.string().email(),
928
+ password: import_zod3.z.string().min(8).max(255)
677
929
  });
678
- var RegisterInputSchema = import_zod2.z.object({
679
- email: import_zod2.z.string().email(),
680
- password: import_zod2.z.string().min(8).max(255),
681
- name: import_zod2.z.string().min(1).max(255),
682
- organizationSlug: import_zod2.z.string().min(1).max(100).regex(/^[a-z0-9-]+$/).optional()
930
+ var RegisterInputSchema = import_zod3.z.object({
931
+ email: import_zod3.z.string().email(),
932
+ password: import_zod3.z.string().min(8).max(255),
933
+ name: import_zod3.z.string().min(1).max(255)
683
934
  });
684
- var VerifyTaskInputSchema = import_zod2.z.object({
935
+ var VerifyTaskInputSchema = import_zod3.z.object({
685
936
  projectId: cuidOrPrefixedId,
686
937
  taskId: cuidOrPrefixedId,
687
- approved: import_zod2.z.boolean(),
688
- feedback: import_zod2.z.string().max(5e3).optional()
938
+ approved: import_zod3.z.boolean(),
939
+ feedback: import_zod3.z.string().max(5e3).optional()
689
940
  });
690
- var GetTaskPromptInputSchema = import_zod2.z.object({
941
+ var GetTaskPromptInputSchema = import_zod3.z.object({
691
942
  projectId: cuidOrPrefixedId,
692
943
  taskId: cuidOrPrefixedId
693
944
  });
694
- var UpdateTaskMCPInputSchema = import_zod2.z.object({
945
+ var UpdateTaskMCPInputSchema = import_zod3.z.object({
695
946
  projectId: cuidOrPrefixedId,
696
947
  taskId: cuidOrPrefixedId,
697
- title: import_zod2.z.string().min(1).max(200).optional(),
698
- description: import_zod2.z.string().max(5e3).optional(),
699
- priority: import_zod2.z.nativeEnum(TaskPriority).optional(),
700
- acceptanceCriteria: import_zod2.z.array(import_zod2.z.object({
701
- id: import_zod2.z.string().optional(),
702
- description: import_zod2.z.string().min(1).max(500)
948
+ title: import_zod3.z.string().min(1).max(200).optional(),
949
+ description: import_zod3.z.string().max(5e3).optional(),
950
+ priority: import_zod3.z.nativeEnum(TaskPriority).optional(),
951
+ acceptanceCriteria: import_zod3.z.array(import_zod3.z.object({
952
+ id: import_zod3.z.string().optional(),
953
+ description: import_zod3.z.string().min(1).max(500)
703
954
  })).optional()
704
955
  });
705
- var ReportBranchInputSchema = import_zod2.z.object({
956
+ var ReportBranchInputSchema = import_zod3.z.object({
706
957
  projectId: cuidOrPrefixedId,
707
958
  taskId: cuidOrPrefixedId,
708
959
  branchName: gitBranchName
709
960
  });
710
- var ReportPRInputSchema = import_zod2.z.object({
961
+ var ReportPRInputSchema = import_zod3.z.object({
711
962
  projectId: cuidOrPrefixedId,
712
963
  taskId: cuidOrPrefixedId,
713
- pullRequestUrl: import_zod2.z.string().url(),
714
- pullRequestNumber: import_zod2.z.number().int().positive()
964
+ pullRequestUrl: import_zod3.z.string().url(),
965
+ pullRequestNumber: import_zod3.z.number().int().positive()
715
966
  });
716
967
 
717
968
  // ../../packages/core/dist/logging/metrics.js
@@ -792,23 +1043,20 @@ function createHistogram(name, help, buckets = [5e-3, 0.01, 0.025, 0.05, 0.1, 0.
792
1043
  var httpRequestsTotal = createCounter("mtaap_http_requests_total", "Total number of HTTP requests");
793
1044
  var httpRequestDuration = createHistogram("mtaap_http_request_duration_seconds", "HTTP request duration in seconds");
794
1045
  var activeUsers = createGauge("mtaap_active_users", "Number of active users");
795
- var tasksTotal = createCounter("mtaap_tasks_total", "Total number of tasks by state");
796
- var taskStateChanges = createCounter("mtaap_task_state_changes_total", "Total number of task state changes");
797
1046
  var httpErrorsTotal = createCounter("mtaap_http_errors_total", "Total number of HTTP errors");
798
1047
  var httpActiveConnections = createGauge("mtaap_http_active_connections", "Number of active HTTP connections");
799
1048
  var newSignupsTotal = createCounter("mtaap_new_signups_total", "Total number of new user signups");
800
1049
  var loginSuccessTotal = createCounter("mtaap_login_success_total", "Total number of successful logins");
801
1050
  var loginFailureTotal = createCounter("mtaap_login_failure_total", "Total number of failed logins");
802
- var dbConnectionPoolActive = createGauge("mtaap_db_connection_pool_active", "Number of active database connections");
803
- var dbConnectionPoolIdle = createGauge("mtaap_db_connection_pool_idle", "Number of idle database connections");
804
- var dbConnectionPoolMax = createGauge("mtaap_db_connection_pool_max", "Maximum number of database connections");
805
1051
  var dbQueryDuration = createHistogram("mtaap_db_query_duration_seconds", "Database query duration in seconds");
806
1052
  var dbSlowQueriesTotal = createCounter("mtaap_db_slow_queries_total", "Total number of slow database queries (>1s)");
807
- var dbErrorsTotal = createCounter("mtaap_db_errors_total", "Total number of database errors");
808
1053
  var tasksCreatedTotal = createCounter("mtaap_tasks_created_total", "Total number of tasks created");
809
1054
  var tasksAssignedTotal = createCounter("mtaap_tasks_assigned_total", "Total number of tasks assigned");
810
1055
  var tasksCompletedTotal = createCounter("mtaap_tasks_completed_total", "Total number of tasks completed");
811
- var tasksByState = createGauge("mtaap_tasks_by_state", "Number of tasks by state");
1056
+ var agentSessionsTotal = createCounter("mtaap_agent_sessions_total", "Total number of agent sessions started");
1057
+ var agentErrorsTotal = createCounter("mtaap_agent_errors_total", "Total number of agent errors");
1058
+ var agentSessionDuration = createHistogram("mtaap_agent_session_duration_seconds", "Agent session duration in seconds", [1, 5, 15, 30, 60, 120, 300, 600, 1800, 3600]);
1059
+ var agentSessionsActive = createGauge("mtaap_agent_sessions_active", "Number of currently active agent sessions");
812
1060
 
813
1061
  // ../../packages/core/dist/logging/performance-monitor.js
814
1062
  var MAX_SAMPLES = 1e3;
@@ -1012,6 +1260,9 @@ var errorTrackerInstance = new NoOpErrorTracker();
1012
1260
 
1013
1261
  // src/api-client.ts
1014
1262
  var DEFAULT_TIMEOUT = 3e4;
1263
+ var DEFAULT_CACHE_TTL = 3e4;
1264
+ var MAX_RETRIES = 2;
1265
+ var INITIAL_RETRY_DELAY = 500;
1015
1266
  function sanitizeForLogging(str) {
1016
1267
  let sanitized = str.replace(/\bcollab_[a-zA-Z0-9_-]+\b/gi, "[REDACTED_API_KEY]");
1017
1268
  sanitized = sanitized.replace(/\bBearer\s+[a-zA-Z0-9._-]+\b/gi, "Bearer [REDACTED]");
@@ -1026,42 +1277,113 @@ var ApiError = class extends Error {
1026
1277
  this.details = details;
1027
1278
  this.name = "ApiError";
1028
1279
  }
1280
+ /**
1281
+ * Whether this error is transient and the request can be retried.
1282
+ * Retries on network errors (status 0), timeouts (408), and server errors (5xx).
1283
+ */
1284
+ get isRetryable() {
1285
+ return this.status === 0 || this.status === 408 || this.status >= 500;
1286
+ }
1029
1287
  };
1288
+ function sleep(ms) {
1289
+ return new Promise((resolve) => setTimeout(resolve, ms));
1290
+ }
1030
1291
  var MCPApiClient = class {
1031
1292
  baseUrl;
1032
1293
  apiKey;
1294
+ oauthToken;
1033
1295
  timeout;
1034
1296
  debug;
1035
1297
  authContext = null;
1298
+ cache = /* @__PURE__ */ new Map();
1036
1299
  constructor(config3) {
1300
+ if (!config3.apiKey && !config3.oauthToken) {
1301
+ throw new Error("Either apiKey or oauthToken must be provided");
1302
+ }
1037
1303
  this.baseUrl = config3.baseUrl.replace(/\/$/, "");
1038
1304
  this.apiKey = config3.apiKey;
1305
+ this.oauthToken = config3.oauthToken;
1039
1306
  this.timeout = config3.timeout ?? DEFAULT_TIMEOUT;
1040
1307
  this.debug = config3.debug ?? false;
1041
1308
  }
1042
1309
  /**
1043
- * Make an HTTP request to the API
1310
+ * Get a cached value if it exists and hasn't expired.
1044
1311
  */
1045
- async request(method, path, body) {
1046
- const url = `${this.baseUrl}${path}`;
1312
+ getCached(key) {
1313
+ const entry = this.cache.get(key);
1314
+ if (!entry) return void 0;
1315
+ if (Date.now() > entry.expiresAt) {
1316
+ this.cache.delete(key);
1317
+ return void 0;
1318
+ }
1319
+ return entry.data;
1320
+ }
1321
+ /**
1322
+ * Store a value in cache with a TTL.
1323
+ */
1324
+ setCache(key, data, ttl = DEFAULT_CACHE_TTL) {
1325
+ this.cache.set(key, { data, expiresAt: Date.now() + ttl });
1326
+ }
1327
+ /**
1328
+ * Make an HTTP request to the API with automatic retry on transient failures.
1329
+ */
1330
+ async request(method, path2, body) {
1331
+ let lastError;
1332
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
1333
+ try {
1334
+ return await this.requestOnce(method, path2, body);
1335
+ } catch (error) {
1336
+ if (!(error instanceof ApiError) || !error.isRetryable || attempt === MAX_RETRIES) {
1337
+ throw error;
1338
+ }
1339
+ lastError = error;
1340
+ const delay = INITIAL_RETRY_DELAY * Math.pow(2, attempt);
1341
+ if (this.debug) {
1342
+ console.error(`[mcp-api] Retry ${attempt + 1}/${MAX_RETRIES} after ${delay}ms (${lastError.code})`);
1343
+ }
1344
+ await sleep(delay);
1345
+ }
1346
+ }
1347
+ throw lastError;
1348
+ }
1349
+ /**
1350
+ * Execute a single HTTP request to the API.
1351
+ */
1352
+ async requestOnce(method, path2, body) {
1353
+ const url = `${this.baseUrl}${path2}`;
1047
1354
  const controller = new AbortController();
1048
1355
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
1049
1356
  if (this.debug) {
1050
- console.error(`[mcp-api] ${method} ${sanitizeForLogging(path)}`);
1357
+ console.error(`[mcp-api] ${method} ${sanitizeForLogging(path2)}`);
1051
1358
  }
1052
1359
  try {
1360
+ const headers = {
1361
+ "Content-Type": "application/json"
1362
+ };
1363
+ if (this.oauthToken) {
1364
+ headers["Authorization"] = `Bearer ${this.oauthToken}`;
1365
+ } else if (this.apiKey) {
1366
+ headers["X-API-Key"] = this.apiKey;
1367
+ }
1053
1368
  const response = await fetch(url, {
1054
1369
  method,
1055
- headers: {
1056
- "Content-Type": "application/json",
1057
- "X-API-Key": this.apiKey
1058
- },
1370
+ headers,
1059
1371
  body: body ? JSON.stringify(body) : void 0,
1060
1372
  signal: controller.signal
1061
1373
  });
1062
1374
  clearTimeout(timeoutId);
1063
1375
  const data = await response.json();
1064
1376
  if (!response.ok) {
1377
+ if (response.status === 403 && data.code === "EMAIL_NOT_VERIFIED" && data.verificationUrl) {
1378
+ throw new ApiError(
1379
+ `${data.error}
1380
+
1381
+ To verify your email, visit: ${data.verificationUrl}
1382
+ ${data.hint ? `Hint: ${data.hint}` : ""}`,
1383
+ "EMAIL_NOT_VERIFIED",
1384
+ 403
1385
+ );
1386
+ }
1065
1387
  throw new ApiError(
1066
1388
  data.error || "API request failed",
1067
1389
  data.code || "UNKNOWN_ERROR",
@@ -1108,14 +1430,19 @@ var MCPApiClient = class {
1108
1430
  return this.authenticate();
1109
1431
  }
1110
1432
  /**
1111
- * List accessible projects
1433
+ * List accessible projects (cached for 30s)
1112
1434
  */
1113
1435
  async listProjects(workspaceType) {
1114
1436
  const type = workspaceType || "ALL";
1115
- return this.request(
1437
+ const cacheKey = `listProjects:${type}`;
1438
+ const cached = this.getCached(cacheKey);
1439
+ if (cached) return cached;
1440
+ const result = await this.request(
1116
1441
  "GET",
1117
1442
  `/api/mcp/projects?workspaceType=${encodeURIComponent(type)}`
1118
1443
  );
1444
+ this.setCache(cacheKey, result);
1445
+ return result;
1119
1446
  }
1120
1447
  /**
1121
1448
  * Get single project details
@@ -1124,13 +1451,18 @@ var MCPApiClient = class {
1124
1451
  return this.request("GET", `/api/mcp/projects/${projectId}`);
1125
1452
  }
1126
1453
  /**
1127
- * Get project context (README, stack, conventions)
1454
+ * Get project context (README, stack, conventions) (cached for 60s)
1128
1455
  */
1129
1456
  async getProjectContext(projectId) {
1130
- return this.request(
1457
+ const cacheKey = `projectContext:${projectId}`;
1458
+ const cached = this.getCached(cacheKey);
1459
+ if (cached) return cached;
1460
+ const result = await this.request(
1131
1461
  "GET",
1132
1462
  `/api/mcp/projects/${projectId}/context`
1133
1463
  );
1464
+ this.setCache(cacheKey, result, 6e4);
1465
+ return result;
1134
1466
  }
1135
1467
  /**
1136
1468
  * Create a personal project
@@ -1158,8 +1490,8 @@ var MCPApiClient = class {
1158
1490
  if (filters.assigneeId) params.set("assigneeId", filters.assigneeId);
1159
1491
  if (filters.includeArchived) params.set("includeArchived", "true");
1160
1492
  const queryString = params.toString();
1161
- const path = queryString ? `/api/mcp/tasks?${queryString}` : "/api/mcp/tasks";
1162
- return this.request("GET", path);
1493
+ const path2 = queryString ? `/api/mcp/tasks?${queryString}` : "/api/mcp/tasks";
1494
+ return this.request("GET", path2);
1163
1495
  }
1164
1496
  /**
1165
1497
  * Get full task details
@@ -1292,7 +1624,8 @@ var MCPApiClient = class {
1292
1624
  );
1293
1625
  }
1294
1626
  /**
1295
- * Update task details (DRAFT/TODO states only)
1627
+ * Update task details (DRAFT/TODO states only).
1628
+ * Task stays in its current state.
1296
1629
  */
1297
1630
  async updateTask(taskId, projectId, data) {
1298
1631
  return this.request("PATCH", `/api/mcp/tasks/${taskId}`, {
@@ -1309,7 +1642,7 @@ var PERMISSION_RANK = {
1309
1642
  ADMIN: 3
1310
1643
  };
1311
1644
  function assertApiKeyPermission(apiKey, required, toolName) {
1312
- const actualRank = PERMISSION_RANK[apiKey.permissions] ?? 0;
1645
+ const actualRank = apiKey.permissions ? PERMISSION_RANK[apiKey.permissions] ?? 0 : 0;
1313
1646
  const requiredRank = PERMISSION_RANK[required] ?? 0;
1314
1647
  if (actualRank >= requiredRank) {
1315
1648
  return;
@@ -1327,6 +1660,454 @@ function assertApiKeyPermission(apiKey, required, toolName) {
1327
1660
  throw error;
1328
1661
  }
1329
1662
 
1663
+ // src/apps/task-details.ts
1664
+ var import_server2 = require("@modelcontextprotocol/ext-apps/server");
1665
+ var import_zod4 = require("zod");
1666
+
1667
+ // src/apps/helpers.ts
1668
+ var import_server = require("@modelcontextprotocol/ext-apps/server");
1669
+ var import_promises = __toESM(require("fs/promises"));
1670
+ var import_node_path = __toESM(require("path"));
1671
+ var import_node_url = require("url");
1672
+ var import_meta = {};
1673
+ var getDirname = () => {
1674
+ try {
1675
+ return import_node_path.default.dirname((0, import_node_url.fileURLToPath)(import_meta.url));
1676
+ } catch {
1677
+ return __dirname;
1678
+ }
1679
+ };
1680
+ function getAppHtmlPath(htmlFileName) {
1681
+ return import_node_path.default.join(getDirname(), "apps", htmlFileName);
1682
+ }
1683
+ function registerAppHtmlResource(server, config3) {
1684
+ (0, import_server.registerAppResource)(
1685
+ server,
1686
+ config3.name,
1687
+ config3.uri,
1688
+ {
1689
+ mimeType: import_server.RESOURCE_MIME_TYPE,
1690
+ description: config3.description
1691
+ },
1692
+ async () => {
1693
+ const htmlPath = getAppHtmlPath(config3.htmlFileName);
1694
+ try {
1695
+ const html = await import_promises.default.readFile(htmlPath, "utf-8");
1696
+ return {
1697
+ contents: [
1698
+ {
1699
+ uri: config3.uri,
1700
+ mimeType: import_server.RESOURCE_MIME_TYPE,
1701
+ text: html
1702
+ }
1703
+ ]
1704
+ };
1705
+ } catch {
1706
+ return {
1707
+ contents: [
1708
+ {
1709
+ uri: config3.uri,
1710
+ mimeType: import_server.RESOURCE_MIME_TYPE,
1711
+ text: `<!DOCTYPE html><html><body><p>${config3.fallbackLabel} UI not available. Build the UI apps first.</p></body></html>`
1712
+ }
1713
+ ]
1714
+ };
1715
+ }
1716
+ }
1717
+ );
1718
+ }
1719
+
1720
+ // src/apps/task-details.ts
1721
+ var RESOURCE_URI = "ui://collab/task-details.html";
1722
+ function registerTaskDetailsApp(server, apiClient, _authContext) {
1723
+ (0, import_server2.registerAppTool)(
1724
+ server,
1725
+ "view_task_details",
1726
+ {
1727
+ title: "Task Details",
1728
+ description: "View interactive task details with acceptance criteria, notes, and progress. Opens a rich UI panel.",
1729
+ inputSchema: {
1730
+ taskId: import_zod4.z.string().describe("The task ID to view"),
1731
+ projectId: import_zod4.z.string().describe("The project ID")
1732
+ },
1733
+ _meta: { ui: { resourceUri: RESOURCE_URI } }
1734
+ },
1735
+ async ({ taskId, projectId }) => {
1736
+ try {
1737
+ const task = await apiClient.getTask(taskId);
1738
+ let project = null;
1739
+ try {
1740
+ project = await apiClient.getProject(projectId);
1741
+ } catch {
1742
+ }
1743
+ return {
1744
+ content: [
1745
+ {
1746
+ type: "text",
1747
+ text: `Viewing task: ${task.title}`
1748
+ }
1749
+ ],
1750
+ structuredContent: {
1751
+ task,
1752
+ project
1753
+ }
1754
+ };
1755
+ } catch (error) {
1756
+ const message = error instanceof Error ? error.message : String(error);
1757
+ return {
1758
+ content: [{ type: "text", text: `Error loading task: ${message}` }],
1759
+ isError: true
1760
+ };
1761
+ }
1762
+ }
1763
+ );
1764
+ registerAppHtmlResource(server, {
1765
+ name: "Task Details UI",
1766
+ uri: RESOURCE_URI,
1767
+ description: "Interactive task details view with acceptance criteria and notes",
1768
+ htmlFileName: "task-details.html",
1769
+ fallbackLabel: "Task Details"
1770
+ });
1771
+ }
1772
+
1773
+ // src/apps/kanban.ts
1774
+ var import_server3 = require("@modelcontextprotocol/ext-apps/server");
1775
+ var import_zod5 = require("zod");
1776
+ var RESOURCE_URI2 = "ui://collab/kanban.html";
1777
+ function registerKanbanApp(server, apiClient, _authContext) {
1778
+ (0, import_server3.registerAppTool)(
1779
+ server,
1780
+ "view_kanban_board",
1781
+ {
1782
+ title: "Kanban Board",
1783
+ description: "View interactive drag-and-drop Kanban board for a project. Drag tasks between columns to change their state.",
1784
+ inputSchema: {
1785
+ projectId: import_zod5.z.string().describe("The project ID to display")
1786
+ },
1787
+ _meta: { ui: { resourceUri: RESOURCE_URI2 } }
1788
+ },
1789
+ async ({ projectId }) => {
1790
+ try {
1791
+ const [project, tasks] = await Promise.all([
1792
+ apiClient.getProject(projectId),
1793
+ apiClient.listTasks({ projectId })
1794
+ ]);
1795
+ return {
1796
+ content: [
1797
+ {
1798
+ type: "text",
1799
+ text: `Kanban board for ${project.name} (${tasks.length} tasks)`
1800
+ }
1801
+ ],
1802
+ structuredContent: {
1803
+ project,
1804
+ tasks
1805
+ }
1806
+ };
1807
+ } catch (error) {
1808
+ const message = error instanceof Error ? error.message : String(error);
1809
+ return {
1810
+ content: [{ type: "text", text: `Error loading kanban: ${message}` }],
1811
+ isError: true
1812
+ };
1813
+ }
1814
+ }
1815
+ );
1816
+ registerAppHtmlResource(server, {
1817
+ name: "Kanban Board UI",
1818
+ uri: RESOURCE_URI2,
1819
+ description: "Interactive drag-and-drop Kanban board for task management",
1820
+ htmlFileName: "kanban.html",
1821
+ fallbackLabel: "Kanban"
1822
+ });
1823
+ }
1824
+
1825
+ // src/apps/activity.ts
1826
+ var import_server4 = require("@modelcontextprotocol/ext-apps/server");
1827
+ var import_zod6 = require("zod");
1828
+ var RESOURCE_URI3 = "ui://collab/activity.html";
1829
+ function registerActivityApp(server, apiClient, _authContext) {
1830
+ (0, import_server4.registerAppTool)(
1831
+ server,
1832
+ "view_activity",
1833
+ {
1834
+ title: "User Activity",
1835
+ description: "View who is working on what across the team. Shows IN_PROGRESS and REVIEW tasks per user.",
1836
+ inputSchema: {
1837
+ projectId: import_zod6.z.string().optional().describe("Optional project ID to filter by")
1838
+ },
1839
+ _meta: { ui: { resourceUri: RESOURCE_URI3 } }
1840
+ },
1841
+ async ({ projectId }) => {
1842
+ try {
1843
+ const [inProgressTasks, reviewTasks] = await Promise.all([
1844
+ apiClient.listTasks({
1845
+ projectId,
1846
+ state: TaskState.IN_PROGRESS
1847
+ }),
1848
+ apiClient.listTasks({
1849
+ projectId,
1850
+ state: TaskState.REVIEW
1851
+ })
1852
+ ]);
1853
+ const userMap = /* @__PURE__ */ new Map();
1854
+ for (const task of inProgressTasks) {
1855
+ if (task.assigneeId) {
1856
+ if (!userMap.has(task.assigneeId)) {
1857
+ userMap.set(task.assigneeId, {
1858
+ id: task.assigneeId,
1859
+ name: task.assigneeName || "Unknown",
1860
+ email: task.assigneeEmail || "",
1861
+ activeTasks: [],
1862
+ reviewTasks: []
1863
+ });
1864
+ }
1865
+ userMap.get(task.assigneeId).activeTasks.push(task);
1866
+ }
1867
+ }
1868
+ for (const task of reviewTasks) {
1869
+ if (task.assigneeId) {
1870
+ if (!userMap.has(task.assigneeId)) {
1871
+ userMap.set(task.assigneeId, {
1872
+ id: task.assigneeId,
1873
+ name: task.assigneeName || "Unknown",
1874
+ email: task.assigneeEmail || "",
1875
+ activeTasks: [],
1876
+ reviewTasks: []
1877
+ });
1878
+ }
1879
+ userMap.get(task.assigneeId).reviewTasks.push(task);
1880
+ }
1881
+ }
1882
+ const users = Array.from(userMap.values());
1883
+ return {
1884
+ content: [
1885
+ {
1886
+ type: "text",
1887
+ text: `Activity: ${users.length} users with active work`
1888
+ }
1889
+ ],
1890
+ structuredContent: {
1891
+ users,
1892
+ projectId
1893
+ }
1894
+ };
1895
+ } catch (error) {
1896
+ const message = error instanceof Error ? error.message : String(error);
1897
+ return {
1898
+ content: [
1899
+ { type: "text", text: `Error loading activity: ${message}` }
1900
+ ],
1901
+ isError: true
1902
+ };
1903
+ }
1904
+ }
1905
+ );
1906
+ registerAppHtmlResource(server, {
1907
+ name: "User Activity UI",
1908
+ uri: RESOURCE_URI3,
1909
+ description: "Dashboard showing team member activity and workload",
1910
+ htmlFileName: "activity.html",
1911
+ fallbackLabel: "Activity"
1912
+ });
1913
+ }
1914
+
1915
+ // src/apps/project-overview.ts
1916
+ var import_server5 = require("@modelcontextprotocol/ext-apps/server");
1917
+ var import_zod7 = require("zod");
1918
+ var RESOURCE_URI4 = "ui://collab/project-overview.html";
1919
+ function registerProjectOverviewApp(server, apiClient, _authContext) {
1920
+ (0, import_server5.registerAppTool)(
1921
+ server,
1922
+ "view_project_overview",
1923
+ {
1924
+ title: "Project Overview",
1925
+ description: "View project health dashboard with task metrics, state distribution, and conventions.",
1926
+ inputSchema: {
1927
+ projectId: import_zod7.z.string().describe("The project ID to display")
1928
+ },
1929
+ _meta: { ui: { resourceUri: RESOURCE_URI4 } }
1930
+ },
1931
+ async ({ projectId }) => {
1932
+ try {
1933
+ const [project, context, tasks] = await Promise.all([
1934
+ apiClient.getProject(projectId),
1935
+ apiClient.getProjectContext(projectId).catch(() => null),
1936
+ apiClient.listTasks({ projectId })
1937
+ ]);
1938
+ const activeTasks = tasks.filter((t) => !t.isArchived);
1939
+ const inProgress = activeTasks.filter(
1940
+ (t) => t.state === TaskState.IN_PROGRESS
1941
+ ).length;
1942
+ const done = activeTasks.filter(
1943
+ (t) => t.state === TaskState.DONE
1944
+ ).length;
1945
+ const blocked = activeTasks.filter((t) => t.errorType).length;
1946
+ const tasksByState = {
1947
+ DRAFT: 0,
1948
+ TODO: 0,
1949
+ IN_PROGRESS: 0,
1950
+ REVIEW: 0,
1951
+ DONE: 0
1952
+ };
1953
+ for (const task of activeTasks) {
1954
+ tasksByState[task.state]++;
1955
+ }
1956
+ const tasksByPriority = {
1957
+ LOW: 0,
1958
+ MEDIUM: 0,
1959
+ HIGH: 0,
1960
+ CRITICAL: 0
1961
+ };
1962
+ for (const task of activeTasks) {
1963
+ tasksByPriority[task.priority]++;
1964
+ }
1965
+ const recentCompleted = activeTasks.filter((t) => t.state === TaskState.DONE).sort(
1966
+ (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
1967
+ ).slice(0, 5);
1968
+ return {
1969
+ content: [
1970
+ {
1971
+ type: "text",
1972
+ text: `Project overview for ${project.name}: ${activeTasks.length} tasks`
1973
+ }
1974
+ ],
1975
+ structuredContent: {
1976
+ project,
1977
+ context,
1978
+ metrics: {
1979
+ totalTasks: activeTasks.length,
1980
+ inProgress,
1981
+ blocked,
1982
+ done
1983
+ },
1984
+ tasksByState,
1985
+ tasksByPriority,
1986
+ recentCompleted
1987
+ }
1988
+ };
1989
+ } catch (error) {
1990
+ const message = error instanceof Error ? error.message : String(error);
1991
+ return {
1992
+ content: [
1993
+ { type: "text", text: `Error loading project overview: ${message}` }
1994
+ ],
1995
+ isError: true
1996
+ };
1997
+ }
1998
+ }
1999
+ );
2000
+ registerAppHtmlResource(server, {
2001
+ name: "Project Overview UI",
2002
+ uri: RESOURCE_URI4,
2003
+ description: "Project health dashboard with metrics and visualizations",
2004
+ htmlFileName: "project-overview.html",
2005
+ fallbackLabel: "Project Overview"
2006
+ });
2007
+ }
2008
+
2009
+ // src/apps/agent-sessions.ts
2010
+ var import_server6 = require("@modelcontextprotocol/ext-apps/server");
2011
+ var import_zod8 = require("zod");
2012
+ var RESOURCE_URI5 = "ui://collab/agent-sessions.html";
2013
+ function registerAgentSessionsApp(server, apiClient, authContext) {
2014
+ (0, import_server6.registerAppTool)(
2015
+ server,
2016
+ "view_agent_sessions",
2017
+ {
2018
+ title: "Agent Sessions",
2019
+ description: "View active AI agent sessions working on tasks. Monitor real-time progress and session status.",
2020
+ inputSchema: {
2021
+ projectId: import_zod8.z.string().optional().describe("Optional project ID to filter by")
2022
+ },
2023
+ _meta: { ui: { resourceUri: RESOURCE_URI5 } }
2024
+ },
2025
+ async ({ projectId }) => {
2026
+ try {
2027
+ const [inProgressTasks, reviewTasks] = await Promise.all([
2028
+ apiClient.listTasks({
2029
+ projectId,
2030
+ state: TaskState.IN_PROGRESS
2031
+ }),
2032
+ apiClient.listTasks({
2033
+ projectId,
2034
+ state: TaskState.REVIEW
2035
+ })
2036
+ ]);
2037
+ const sessions2 = [];
2038
+ for (const task of inProgressTasks) {
2039
+ if (task.assigneeId) {
2040
+ const totalCriteria = task.acceptanceCriteria?.length || 0;
2041
+ const completedCriteria = task.acceptanceCriteria?.filter((c) => c.completed).length || 0;
2042
+ const progress = totalCriteria > 0 ? Math.round(completedCriteria / totalCriteria * 100) : 0;
2043
+ const latestUpdate = task.progressUpdates?.[task.progressUpdates.length - 1];
2044
+ sessions2.push({
2045
+ id: `session-${task.id}`,
2046
+ taskId: task.id,
2047
+ taskTitle: task.title,
2048
+ agentName: task.assigneeName || "Agent",
2049
+ status: "RUNNING",
2050
+ startedAt: task.updatedAt,
2051
+ lastCheckpoint: latestUpdate?.timestamp,
2052
+ currentStep: latestUpdate?.statusMessage,
2053
+ progress
2054
+ });
2055
+ }
2056
+ }
2057
+ for (const task of reviewTasks) {
2058
+ if (task.assigneeId) {
2059
+ sessions2.push({
2060
+ id: `session-${task.id}`,
2061
+ taskId: task.id,
2062
+ taskTitle: task.title,
2063
+ agentName: task.assigneeName || "Agent",
2064
+ status: "STOPPED",
2065
+ startedAt: task.updatedAt,
2066
+ progress: 100
2067
+ });
2068
+ }
2069
+ }
2070
+ return {
2071
+ content: [
2072
+ {
2073
+ type: "text",
2074
+ text: `Agent sessions: ${sessions2.length} active`
2075
+ }
2076
+ ],
2077
+ structuredContent: {
2078
+ sessions: sessions2,
2079
+ projectId
2080
+ }
2081
+ };
2082
+ } catch (error) {
2083
+ const message = error instanceof Error ? error.message : String(error);
2084
+ return {
2085
+ content: [
2086
+ { type: "text", text: `Error loading agent sessions: ${message}` }
2087
+ ],
2088
+ isError: true
2089
+ };
2090
+ }
2091
+ }
2092
+ );
2093
+ registerAppHtmlResource(server, {
2094
+ name: "Agent Sessions UI",
2095
+ uri: RESOURCE_URI5,
2096
+ description: "Real-time view of AI agent sessions and their progress",
2097
+ htmlFileName: "agent-sessions.html",
2098
+ fallbackLabel: "Agent Sessions"
2099
+ });
2100
+ }
2101
+
2102
+ // src/apps/index.ts
2103
+ function registerMCPApps(server, apiClient, authContext) {
2104
+ registerTaskDetailsApp(server, apiClient, authContext);
2105
+ registerKanbanApp(server, apiClient, authContext);
2106
+ registerActivityApp(server, apiClient, authContext);
2107
+ registerProjectOverviewApp(server, apiClient, authContext);
2108
+ registerAgentSessionsApp(server, apiClient, authContext);
2109
+ }
2110
+
1330
2111
  // src/index.ts
1331
2112
  var COLLAB_SERVER_INSTRUCTIONS = `Collab - AI-assisted software development task management platform.
1332
2113
 
@@ -1378,7 +2159,7 @@ Review Workflow:
1378
2159
  list_tasks(state=REVIEW) -> get_task -> approve_task OR request_changes
1379
2160
 
1380
2161
  Task Editing:
1381
- update_task (DRAFT/TODO only) -> if was TODO, reverts to DRAFT -> verify_task again
2162
+ update_task (DRAFT/TODO only) -> task stays in current state
1382
2163
 
1383
2164
  Error Recovery:
1384
2165
  report_error -> abandon_task -> list_tasks -> assign_task (retry or pick different task)
@@ -1397,7 +2178,7 @@ GIT OPERATIONS NOTE:
1397
2178
  TASK STATE FLOW:
1398
2179
  DRAFT -> TODO -> IN_PROGRESS -> REVIEW -> DONE
1399
2180
  (verify_task: DRAFT -> TODO)
1400
- (update_task on TODO: reverts to DRAFT)
2181
+ (update_task: DRAFT/TODO only, state unchanged)
1401
2182
  (request_changes: REVIEW -> IN_PROGRESS)
1402
2183
  (abandon_task: IN_PROGRESS -> TODO)
1403
2184
 
@@ -1430,7 +2211,7 @@ function initializeMCPServer(apiClient, authContext) {
1430
2211
  {
1431
2212
  description: "Discover all accessible projects. Use first to find project IDs. Filter by TEAM, PERSONAL, or ALL workspaces.",
1432
2213
  inputSchema: {
1433
- workspaceType: import_zod3.z.enum(["TEAM", "PERSONAL", "ALL"]).optional().describe("Filter by workspace type")
2214
+ workspaceType: import_zod9.z.enum(["TEAM", "PERSONAL", "ALL"]).optional().describe("Filter by workspace type")
1434
2215
  }
1435
2216
  },
1436
2217
  async (args) => {
@@ -1460,10 +2241,10 @@ function initializeMCPServer(apiClient, authContext) {
1460
2241
  {
1461
2242
  description: "Query tasks with filters. Use state=TODO for assignable tasks, state=REVIEW for pending reviews.",
1462
2243
  inputSchema: {
1463
- projectId: import_zod3.z.string().optional().describe("Filter by project ID"),
1464
- state: import_zod3.z.nativeEnum(TaskState).optional().describe("Filter by task state"),
1465
- assigneeId: import_zod3.z.string().optional().describe("Filter by assignee ID"),
1466
- includeArchived: import_zod3.z.boolean().optional().describe("Include archived tasks (default: false)")
2244
+ projectId: import_zod9.z.string().optional().describe("Filter by project ID"),
2245
+ state: import_zod9.z.nativeEnum(TaskState).optional().describe("Filter by task state"),
2246
+ assigneeId: import_zod9.z.string().optional().describe("Filter by assignee ID"),
2247
+ includeArchived: import_zod9.z.boolean().optional().describe("Include archived tasks (default: false)")
1467
2248
  }
1468
2249
  },
1469
2250
  async (args) => {
@@ -1494,7 +2275,7 @@ function initializeMCPServer(apiClient, authContext) {
1494
2275
  {
1495
2276
  description: "Get complete task details with acceptance criteria and notes. Call before assign_task to understand requirements.",
1496
2277
  inputSchema: {
1497
- taskId: import_zod3.z.string().describe("The task ID to retrieve")
2278
+ taskId: import_zod9.z.string().describe("The task ID to retrieve")
1498
2279
  }
1499
2280
  },
1500
2281
  async (args) => {
@@ -1520,9 +2301,9 @@ function initializeMCPServer(apiClient, authContext) {
1520
2301
  {
1521
2302
  description: "Atomically claim a task. Race-safe - fails if already assigned. Task must be TODO. Returns suggested branch name and worktree path for isolated parallel development.",
1522
2303
  inputSchema: {
1523
- projectId: import_zod3.z.string().describe("The project ID"),
1524
- taskId: import_zod3.z.string().describe("The task ID to assign"),
1525
- expectedState: import_zod3.z.nativeEnum(TaskState).optional().describe("Expected task state (default: TODO)")
2304
+ projectId: import_zod9.z.string().describe("The project ID"),
2305
+ taskId: import_zod9.z.string().describe("The task ID to assign"),
2306
+ expectedState: import_zod9.z.nativeEnum(TaskState).optional().describe("Expected task state (default: TODO)")
1526
2307
  }
1527
2308
  },
1528
2309
  async (args) => {
@@ -1556,10 +2337,10 @@ function initializeMCPServer(apiClient, authContext) {
1556
2337
  {
1557
2338
  description: "Report progress and checkpoint work. Call frequently during execution. Marks acceptance criteria complete.",
1558
2339
  inputSchema: {
1559
- taskId: import_zod3.z.string().describe("The task ID to update"),
1560
- statusMessage: import_zod3.z.string().optional().describe("Status message (max 1000 chars)"),
1561
- completedCheckpointIds: import_zod3.z.array(import_zod3.z.string()).optional().describe("Array of completed checkpoint IDs"),
1562
- currentCheckpointIndex: import_zod3.z.number().optional().describe("Current checkpoint index")
2340
+ taskId: import_zod9.z.string().describe("The task ID to update"),
2341
+ statusMessage: import_zod9.z.string().optional().describe("Status message (max 1000 chars)"),
2342
+ completedCheckpointIds: import_zod9.z.array(import_zod9.z.string()).optional().describe("Array of completed checkpoint IDs"),
2343
+ currentCheckpointIndex: import_zod9.z.number().optional().describe("Current checkpoint index")
1563
2344
  }
1564
2345
  },
1565
2346
  async (args) => {
@@ -1593,10 +2374,10 @@ function initializeMCPServer(apiClient, authContext) {
1593
2374
  {
1594
2375
  description: "Prepare task for PR creation. Returns PR suggestions. After creating PR locally, call report_pr to transition to REVIEW. Requires IN_PROGRESS state.",
1595
2376
  inputSchema: {
1596
- projectId: import_zod3.z.string().describe("The project ID"),
1597
- taskId: import_zod3.z.string().describe("The task ID to complete"),
1598
- pullRequestTitle: import_zod3.z.string().optional().describe("PR title (max 300 chars)"),
1599
- pullRequestBody: import_zod3.z.string().optional().describe("PR body/description (max 10000 chars)")
2377
+ projectId: import_zod9.z.string().describe("The project ID"),
2378
+ taskId: import_zod9.z.string().describe("The task ID to complete"),
2379
+ pullRequestTitle: import_zod9.z.string().optional().describe("PR title (max 300 chars)"),
2380
+ pullRequestBody: import_zod9.z.string().optional().describe("PR body/description (max 10000 chars)")
1600
2381
  }
1601
2382
  },
1602
2383
  async (args) => {
@@ -1653,10 +2434,10 @@ function initializeMCPServer(apiClient, authContext) {
1653
2434
  {
1654
2435
  description: "Report unrecoverable errors (BUILD_FAILURE, TEST_FAILURE, CONFLICT, AUTH_ERROR). Consider abandon_task after.",
1655
2436
  inputSchema: {
1656
- taskId: import_zod3.z.string().describe("The task ID"),
1657
- errorType: import_zod3.z.nativeEnum(ErrorType).describe("Error type: BUILD_FAILURE, TEST_FAILURE, CONFLICT, AUTH_ERROR, OTHER"),
1658
- errorMessage: import_zod3.z.string().describe("Error message (max 1000 chars)"),
1659
- context: import_zod3.z.string().optional().describe("Additional context (max 2000 chars)")
2437
+ taskId: import_zod9.z.string().describe("The task ID"),
2438
+ errorType: import_zod9.z.nativeEnum(ErrorType).describe("Error type: BUILD_FAILURE, TEST_FAILURE, CONFLICT, AUTH_ERROR, OTHER"),
2439
+ errorMessage: import_zod9.z.string().describe("Error message (max 1000 chars)"),
2440
+ context: import_zod9.z.string().optional().describe("Additional context (max 2000 chars)")
1660
2441
  }
1661
2442
  },
1662
2443
  async (args) => {
@@ -1691,7 +2472,7 @@ function initializeMCPServer(apiClient, authContext) {
1691
2472
  {
1692
2473
  description: "Load project README, tech stack, and coding conventions. Call after selecting project.",
1693
2474
  inputSchema: {
1694
- projectId: import_zod3.z.string().describe("The project ID")
2475
+ projectId: import_zod9.z.string().describe("The project ID")
1695
2476
  }
1696
2477
  },
1697
2478
  async (args) => {
@@ -1721,8 +2502,8 @@ function initializeMCPServer(apiClient, authContext) {
1721
2502
  {
1722
2503
  description: "Document implementation decisions. Notes persist for future reference and handoff.",
1723
2504
  inputSchema: {
1724
- taskId: import_zod3.z.string().describe("The task ID"),
1725
- content: import_zod3.z.string().describe("Note content (max 500 chars)")
2505
+ taskId: import_zod9.z.string().describe("The task ID"),
2506
+ content: import_zod9.z.string().describe("Note content (max 500 chars)")
1726
2507
  }
1727
2508
  },
1728
2509
  async (args) => {
@@ -1751,9 +2532,9 @@ function initializeMCPServer(apiClient, authContext) {
1751
2532
  {
1752
2533
  description: "Release task assignment and optionally clean up branch. Task returns to TODO. Use after errors.",
1753
2534
  inputSchema: {
1754
- projectId: import_zod3.z.string().describe("The project ID"),
1755
- taskId: import_zod3.z.string().describe("The task ID to abandon"),
1756
- deleteBranch: import_zod3.z.boolean().optional().describe("Whether to delete the associated branch")
2535
+ projectId: import_zod9.z.string().describe("The project ID"),
2536
+ taskId: import_zod9.z.string().describe("The task ID to abandon"),
2537
+ deleteBranch: import_zod9.z.boolean().optional().describe("Whether to delete the associated branch")
1757
2538
  }
1758
2539
  },
1759
2540
  async (args) => {
@@ -1787,9 +2568,9 @@ function initializeMCPServer(apiClient, authContext) {
1787
2568
  {
1788
2569
  description: "Report a branch created by the agent. Call after using git to create and push a branch. Task must be IN_PROGRESS.",
1789
2570
  inputSchema: {
1790
- projectId: import_zod3.z.string().describe("The project ID"),
1791
- taskId: import_zod3.z.string().describe("The task ID"),
1792
- branchName: import_zod3.z.string().describe("Name of the branch created (e.g., feature/TASK-123-fix-login)")
2571
+ projectId: import_zod9.z.string().describe("The project ID"),
2572
+ taskId: import_zod9.z.string().describe("The task ID"),
2573
+ branchName: import_zod9.z.string().describe("Name of the branch created (e.g., feature/TASK-123-fix-login)")
1793
2574
  }
1794
2575
  },
1795
2576
  async (args) => {
@@ -1823,10 +2604,10 @@ function initializeMCPServer(apiClient, authContext) {
1823
2604
  {
1824
2605
  description: "Report a PR created by the agent. Call after using gh pr create. Transitions task to REVIEW state.",
1825
2606
  inputSchema: {
1826
- projectId: import_zod3.z.string().describe("The project ID"),
1827
- taskId: import_zod3.z.string().describe("The task ID"),
1828
- pullRequestUrl: import_zod3.z.string().describe("Full URL of the created PR (e.g., https://github.com/owner/repo/pull/123)"),
1829
- pullRequestNumber: import_zod3.z.number().describe("PR number (e.g., 123)")
2607
+ projectId: import_zod9.z.string().describe("The project ID"),
2608
+ taskId: import_zod9.z.string().describe("The task ID"),
2609
+ pullRequestUrl: import_zod9.z.string().describe("Full URL of the created PR (e.g., https://github.com/owner/repo/pull/123)"),
2610
+ pullRequestNumber: import_zod9.z.number().describe("PR number (e.g., 123)")
1830
2611
  }
1831
2612
  },
1832
2613
  async (args) => {
@@ -1861,8 +2642,8 @@ function initializeMCPServer(apiClient, authContext) {
1861
2642
  {
1862
2643
  description: "Soft-delete a task. Hidden but restorable via unarchive_task.",
1863
2644
  inputSchema: {
1864
- projectId: import_zod3.z.string().describe("The project ID"),
1865
- taskId: import_zod3.z.string().describe("The task ID to archive")
2645
+ projectId: import_zod9.z.string().describe("The project ID"),
2646
+ taskId: import_zod9.z.string().describe("The task ID to archive")
1866
2647
  }
1867
2648
  },
1868
2649
  async (args) => {
@@ -1895,8 +2676,8 @@ function initializeMCPServer(apiClient, authContext) {
1895
2676
  {
1896
2677
  description: "Restore previously archived task to original state.",
1897
2678
  inputSchema: {
1898
- projectId: import_zod3.z.string().describe("The project ID"),
1899
- taskId: import_zod3.z.string().describe("The task ID to restore")
2679
+ projectId: import_zod9.z.string().describe("The project ID"),
2680
+ taskId: import_zod9.z.string().describe("The task ID to restore")
1900
2681
  }
1901
2682
  },
1902
2683
  async (args) => {
@@ -1929,9 +2710,9 @@ function initializeMCPServer(apiClient, authContext) {
1929
2710
  {
1930
2711
  description: "Create new project in personal workspace linked to GitHub repository.",
1931
2712
  inputSchema: {
1932
- name: import_zod3.z.string().describe("Project name (max 100 chars)"),
1933
- description: import_zod3.z.string().optional().describe("Project description (max 500 chars)"),
1934
- repositoryUrl: import_zod3.z.string().describe("GitHub repository URL")
2713
+ name: import_zod9.z.string().describe("Project name (max 100 chars)"),
2714
+ description: import_zod9.z.string().optional().describe("Project description (max 500 chars)"),
2715
+ repositoryUrl: import_zod9.z.string().describe("GitHub repository URL")
1935
2716
  }
1936
2717
  },
1937
2718
  async (args) => {
@@ -1965,14 +2746,14 @@ function initializeMCPServer(apiClient, authContext) {
1965
2746
  {
1966
2747
  description: "Create task with title, description, acceptance criteria. Starts in DRAFT. Priority: LOW/MEDIUM/HIGH/CRITICAL.",
1967
2748
  inputSchema: {
1968
- projectId: import_zod3.z.string().describe("The project ID to create the task in"),
1969
- epicId: import_zod3.z.string().nullable().optional().describe("Optional epic ID to associate the task with"),
1970
- title: import_zod3.z.string().describe("Task title (max 200 chars)"),
1971
- description: import_zod3.z.string().describe("Task description (max 5000 chars)"),
1972
- priority: import_zod3.z.nativeEnum(TaskPriority).optional().describe("Task priority: LOW, MEDIUM, HIGH, CRITICAL (default: MEDIUM)"),
1973
- acceptanceCriteria: import_zod3.z.array(
1974
- import_zod3.z.object({
1975
- description: import_zod3.z.string().describe("Acceptance criterion description (max 500 chars)")
2749
+ projectId: import_zod9.z.string().describe("The project ID to create the task in"),
2750
+ epicId: import_zod9.z.string().nullable().optional().describe("Optional epic ID to associate the task with"),
2751
+ title: import_zod9.z.string().describe("Task title (max 200 chars)"),
2752
+ description: import_zod9.z.string().describe("Task description (max 5000 chars)"),
2753
+ priority: import_zod9.z.nativeEnum(TaskPriority).optional().describe("Task priority: LOW, MEDIUM, HIGH, CRITICAL (default: MEDIUM)"),
2754
+ acceptanceCriteria: import_zod9.z.array(
2755
+ import_zod9.z.object({
2756
+ description: import_zod9.z.string().describe("Acceptance criterion description (max 500 chars)")
1976
2757
  })
1977
2758
  ).describe("Array of acceptance criteria (at least 1 required)")
1978
2759
  }
@@ -2011,10 +2792,10 @@ function initializeMCPServer(apiClient, authContext) {
2011
2792
  {
2012
2793
  description: "Return task from REVIEW to IN_PROGRESS with feedback. Original assignee addresses changes.",
2013
2794
  inputSchema: {
2014
- projectId: import_zod3.z.string().describe("The project ID"),
2015
- taskId: import_zod3.z.string().describe("The task ID to review"),
2016
- reviewComments: import_zod3.z.string().describe("Review comments explaining requested changes (max 5000 chars)"),
2017
- requestedChanges: import_zod3.z.array(import_zod3.z.string()).optional().describe("List of specific changes requested")
2795
+ projectId: import_zod9.z.string().describe("The project ID"),
2796
+ taskId: import_zod9.z.string().describe("The task ID to review"),
2797
+ reviewComments: import_zod9.z.string().describe("Review comments explaining requested changes (max 5000 chars)"),
2798
+ requestedChanges: import_zod9.z.array(import_zod9.z.string()).optional().describe("List of specific changes requested")
2018
2799
  }
2019
2800
  },
2020
2801
  async (args) => {
@@ -2049,9 +2830,9 @@ function initializeMCPServer(apiClient, authContext) {
2049
2830
  {
2050
2831
  description: "Approve completed work and mark DONE. Only for REVIEW state tasks.",
2051
2832
  inputSchema: {
2052
- projectId: import_zod3.z.string().describe("The project ID"),
2053
- taskId: import_zod3.z.string().describe("The task ID to approve"),
2054
- reviewComments: import_zod3.z.string().optional().describe("Optional approval comments (max 2000 chars)")
2833
+ projectId: import_zod9.z.string().describe("The project ID"),
2834
+ taskId: import_zod9.z.string().describe("The task ID to approve"),
2835
+ reviewComments: import_zod9.z.string().optional().describe("Optional approval comments (max 2000 chars)")
2055
2836
  }
2056
2837
  },
2057
2838
  async (args) => {
@@ -2085,10 +2866,10 @@ function initializeMCPServer(apiClient, authContext) {
2085
2866
  {
2086
2867
  description: "Verify a DRAFT task and move it to TODO state. Requires task to pass programmatic validation (title 10+ chars, description 50+ chars, each criterion 20+ chars). If approved=false, stores feedback with NEEDS_REVISION status.",
2087
2868
  inputSchema: {
2088
- projectId: import_zod3.z.string().describe("The project ID"),
2089
- taskId: import_zod3.z.string().describe("The task ID to verify"),
2090
- approved: import_zod3.z.boolean().describe("Whether to approve the task"),
2091
- feedback: import_zod3.z.string().optional().describe("Feedback for the task (required if not approved)")
2869
+ projectId: import_zod9.z.string().describe("The project ID"),
2870
+ taskId: import_zod9.z.string().describe("The task ID to verify"),
2871
+ approved: import_zod9.z.boolean().describe("Whether to approve the task"),
2872
+ feedback: import_zod9.z.string().optional().describe("Feedback for the task (required if not approved)")
2092
2873
  }
2093
2874
  },
2094
2875
  async (args) => {
@@ -2119,8 +2900,8 @@ function initializeMCPServer(apiClient, authContext) {
2119
2900
  {
2120
2901
  description: "Get state-appropriate prompt for a task. Returns verify prompt for DRAFT, assignment prompt for TODO, or continue prompt for IN_PROGRESS tasks.",
2121
2902
  inputSchema: {
2122
- projectId: import_zod3.z.string().describe("The project ID"),
2123
- taskId: import_zod3.z.string().describe("The task ID")
2903
+ projectId: import_zod9.z.string().describe("The project ID"),
2904
+ taskId: import_zod9.z.string().describe("The task ID")
2124
2905
  }
2125
2906
  },
2126
2907
  async (args) => {
@@ -2151,17 +2932,17 @@ function initializeMCPServer(apiClient, authContext) {
2151
2932
  server.registerTool(
2152
2933
  "update_task",
2153
2934
  {
2154
- description: "Update task details (title, description, priority, acceptanceCriteria). Only works for DRAFT and TODO states. If task is in TODO state, it reverts to DRAFT and requires re-verification.",
2935
+ description: "Update task details (title, description, priority, acceptanceCriteria). Only works for DRAFT and TODO states. Task stays in its current state.",
2155
2936
  inputSchema: {
2156
- projectId: import_zod3.z.string().describe("The project ID"),
2157
- taskId: import_zod3.z.string().describe("The task ID to update"),
2158
- title: import_zod3.z.string().optional().describe("New task title"),
2159
- description: import_zod3.z.string().optional().describe("New task description"),
2160
- priority: import_zod3.z.nativeEnum(TaskPriority).optional().describe("New task priority"),
2161
- acceptanceCriteria: import_zod3.z.array(
2162
- import_zod3.z.object({
2163
- id: import_zod3.z.string().optional().describe("Existing criterion ID (for updates)"),
2164
- description: import_zod3.z.string().describe("Criterion description")
2937
+ projectId: import_zod9.z.string().describe("The project ID"),
2938
+ taskId: import_zod9.z.string().describe("The task ID to update"),
2939
+ title: import_zod9.z.string().optional().describe("New task title"),
2940
+ description: import_zod9.z.string().optional().describe("New task description"),
2941
+ priority: import_zod9.z.nativeEnum(TaskPriority).optional().describe("New task priority"),
2942
+ acceptanceCriteria: import_zod9.z.array(
2943
+ import_zod9.z.object({
2944
+ id: import_zod9.z.string().optional().describe("Existing criterion ID (for updates)"),
2945
+ description: import_zod9.z.string().describe("Criterion description")
2165
2946
  })
2166
2947
  ).optional().describe("New acceptance criteria (replaces existing)")
2167
2948
  }
@@ -2217,6 +2998,7 @@ function initializeMCPServer(apiClient, authContext) {
2217
2998
  };
2218
2999
  }
2219
3000
  );
3001
+ registerMCPApps(server, apiClient, authContext);
2220
3002
  return server;
2221
3003
  }
2222
3004
  function handleApiError(error) {
@@ -2251,30 +3033,30 @@ function handleApiError(error) {
2251
3033
  isError: true
2252
3034
  };
2253
3035
  }
2254
- var ActiveTaskSchema = import_zod3.z.object({
2255
- taskId: import_zod3.z.string().min(1),
2256
- projectId: import_zod3.z.string().min(1),
2257
- branchName: import_zod3.z.string().optional(),
2258
- worktreePath: import_zod3.z.string().optional(),
2259
- startedAt: import_zod3.z.string().optional()
3036
+ var ActiveTaskSchema = import_zod9.z.object({
3037
+ taskId: import_zod9.z.string().min(1),
3038
+ projectId: import_zod9.z.string().min(1),
3039
+ branchName: import_zod9.z.string().optional(),
3040
+ worktreePath: import_zod9.z.string().optional(),
3041
+ startedAt: import_zod9.z.string().optional()
2260
3042
  });
2261
3043
  async function checkActiveTask() {
2262
- const fs = await import("fs");
2263
- const path = await import("path");
3044
+ const fs2 = await import("fs");
3045
+ const path2 = await import("path");
2264
3046
  const cwd = process.cwd();
2265
- const collabDir = path.join(cwd, ".collab");
2266
- const activeTaskPath = path.join(collabDir, "active-task.json");
3047
+ const collabDir = path2.join(cwd, ".collab");
3048
+ const activeTaskPath = path2.join(collabDir, "active-task.json");
2267
3049
  try {
2268
- const resolvedPath = path.resolve(activeTaskPath);
2269
- const resolvedCollabDir = path.resolve(collabDir);
2270
- if (!resolvedPath.startsWith(resolvedCollabDir + path.sep) && resolvedPath !== resolvedCollabDir) {
3050
+ const resolvedPath = path2.resolve(activeTaskPath);
3051
+ const resolvedCollabDir = path2.resolve(collabDir);
3052
+ if (!resolvedPath.startsWith(resolvedCollabDir + path2.sep) && resolvedPath !== resolvedCollabDir) {
2271
3053
  return {
2272
3054
  hasActiveTask: false,
2273
3055
  task: null,
2274
3056
  error: "Invalid active task path"
2275
3057
  };
2276
3058
  }
2277
- const stats = await fs.promises.lstat(activeTaskPath);
3059
+ const stats = await fs2.promises.lstat(activeTaskPath);
2278
3060
  if (stats.isSymbolicLink()) {
2279
3061
  return {
2280
3062
  hasActiveTask: false,
@@ -2288,7 +3070,7 @@ async function checkActiveTask() {
2288
3070
  task: null
2289
3071
  };
2290
3072
  }
2291
- const content = await fs.promises.readFile(activeTaskPath, "utf-8");
3073
+ const content = await fs2.promises.readFile(activeTaskPath, "utf-8");
2292
3074
  let parsed;
2293
3075
  try {
2294
3076
  parsed = JSON.parse(content);
@@ -2327,15 +3109,97 @@ async function checkActiveTask() {
2327
3109
  }
2328
3110
 
2329
3111
  // src/auth.ts
3112
+ function extractBearerToken(authorizationHeader) {
3113
+ if (!authorizationHeader) return null;
3114
+ const parts = authorizationHeader.split(" ");
3115
+ if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
3116
+ return null;
3117
+ }
3118
+ return parts[1];
3119
+ }
3120
+ async function validateOAuthToken(baseUrl, accessToken) {
3121
+ const internalUrl = process.env.COLLAB_INTERNAL_URL || baseUrl;
3122
+ const response = await fetch(`${internalUrl}/api/oauth/validate`, {
3123
+ method: "POST",
3124
+ headers: {
3125
+ "Content-Type": "application/json"
3126
+ },
3127
+ body: JSON.stringify({ access_token: accessToken })
3128
+ });
3129
+ if (!response.ok) {
3130
+ throw new Error(`Token validation failed: ${response.status}`);
3131
+ }
3132
+ return response.json();
3133
+ }
3134
+ function buildWwwAuthenticateHeader(req) {
3135
+ const protocol = req.headers["x-forwarded-proto"] || req.protocol || "https";
3136
+ const host = req.headers["x-forwarded-host"] || req.headers.host;
3137
+ const resourceMetadataUrl = `${protocol}://${host}/.well-known/oauth-protected-resource`;
3138
+ return `Bearer resource_metadata="${resourceMetadataUrl}"`;
3139
+ }
2330
3140
  function createAuthMiddleware(baseUrl) {
2331
3141
  return async (req, res, next) => {
3142
+ const authHeader = req.headers.authorization;
3143
+ const bearerToken = extractBearerToken(authHeader);
3144
+ if (bearerToken) {
3145
+ try {
3146
+ const validation = await validateOAuthToken(baseUrl, bearerToken);
3147
+ if (!validation.valid) {
3148
+ res.status(401).set("WWW-Authenticate", buildWwwAuthenticateHeader(req)).json({
3149
+ jsonrpc: "2.0",
3150
+ error: {
3151
+ code: -32001,
3152
+ message: validation.userId ? "Token expired" : "Invalid access token"
3153
+ },
3154
+ id: null
3155
+ });
3156
+ return;
3157
+ }
3158
+ const rawPermissions = validation.permissions || "WRITE";
3159
+ const validPermissions = ["READ", "WRITE", "ADMIN"];
3160
+ let permissions;
3161
+ if (validPermissions.includes(rawPermissions)) {
3162
+ permissions = rawPermissions;
3163
+ } else {
3164
+ console.warn(`[collab-mcp-server] Unexpected permissions value "${rawPermissions}", defaulting to WRITE`);
3165
+ permissions = "WRITE";
3166
+ }
3167
+ const authContext = {
3168
+ userId: validation.userId,
3169
+ userEmail: validation.userEmail,
3170
+ userName: validation.userName || validation.userEmail,
3171
+ permissions
3172
+ };
3173
+ const apiClient = new MCPApiClient({
3174
+ baseUrl,
3175
+ oauthToken: bearerToken,
3176
+ timeout: 3e4
3177
+ });
3178
+ req.apiClient = apiClient;
3179
+ req.authContext = authContext;
3180
+ req.authMethod = "oauth";
3181
+ next();
3182
+ return;
3183
+ } catch (error) {
3184
+ console.error("[collab-mcp-server] OAuth validation error:", error);
3185
+ res.status(401).set("WWW-Authenticate", buildWwwAuthenticateHeader(req)).json({
3186
+ jsonrpc: "2.0",
3187
+ error: {
3188
+ code: -32001,
3189
+ message: "OAuth token validation failed"
3190
+ },
3191
+ id: null
3192
+ });
3193
+ return;
3194
+ }
3195
+ }
2332
3196
  const apiKey = req.headers["x-api-key"];
2333
3197
  if (!apiKey || typeof apiKey !== "string") {
2334
- res.status(401).json({
3198
+ res.status(401).set("WWW-Authenticate", buildWwwAuthenticateHeader(req)).json({
2335
3199
  jsonrpc: "2.0",
2336
3200
  error: {
2337
3201
  code: -32001,
2338
- message: "Missing or invalid X-API-Key header"
3202
+ message: "Missing authentication. Provide Authorization: Bearer <token> or X-API-Key header"
2339
3203
  },
2340
3204
  id: null
2341
3205
  });
@@ -2361,6 +3225,7 @@ function createAuthMiddleware(baseUrl) {
2361
3225
  const authContext = await apiClient.authenticate();
2362
3226
  req.apiClient = apiClient;
2363
3227
  req.authContext = authContext;
3228
+ req.authMethod = "api_key";
2364
3229
  next();
2365
3230
  } catch (error) {
2366
3231
  if (error instanceof ApiError) {
@@ -2408,7 +3273,9 @@ function startSessionCleanup() {
2408
3273
  }
2409
3274
  }
2410
3275
  if (cleanedCount > 0) {
2411
- console.error(`[collab-mcp-server] Cleaned up ${cleanedCount} expired session(s)`);
3276
+ console.error(
3277
+ `[collab-mcp-server] Cleaned up ${cleanedCount} expired session(s)`
3278
+ );
2412
3279
  }
2413
3280
  }, SESSION_CLEANUP_INTERVAL_MS);
2414
3281
  }
@@ -2423,21 +3290,73 @@ async function createHTTPServer() {
2423
3290
  const port = parseInt(process.env.PORT || String(DEFAULT_PORT), 10);
2424
3291
  const baseUrl = process.env.COLLAB_BASE_URL;
2425
3292
  if (!baseUrl) {
2426
- console.error("[collab-mcp-server] Error: COLLAB_BASE_URL environment variable is required");
3293
+ console.error(
3294
+ "[collab-mcp-server] Error: COLLAB_BASE_URL environment variable is required"
3295
+ );
2427
3296
  console.error("\nRequired environment variables:");
2428
- console.error(" COLLAB_BASE_URL Collab webapp URL (e.g., https://collab.mtaap.de)");
3297
+ console.error(
3298
+ " COLLAB_BASE_URL Collab webapp URL (e.g., https://collab.mtaap.de)"
3299
+ );
2429
3300
  console.error(" PORT Server port (default: 3001)");
2430
- console.error("\nClients must provide X-API-Key header with their Collab API key.");
3301
+ console.error("\nClients must authenticate using one of:");
3302
+ console.error(
3303
+ " - Authorization: Bearer <token> header with OAuth access token"
3304
+ );
3305
+ console.error(" - X-API-Key header with their Collab API key");
2431
3306
  process.exit(1);
2432
3307
  }
2433
3308
  try {
2434
3309
  new URL(baseUrl);
2435
3310
  } catch {
2436
- console.error(`[collab-mcp-server] Error: COLLAB_BASE_URL is not a valid URL: ${baseUrl}`);
3311
+ console.error(
3312
+ `[collab-mcp-server] Error: COLLAB_BASE_URL is not a valid URL: ${baseUrl}`
3313
+ );
2437
3314
  process.exit(1);
2438
3315
  }
2439
3316
  const app = (0, import_express.default)();
2440
3317
  app.use(import_express.default.json());
3318
+ function buildProtectedResourceMetadata(req) {
3319
+ const protocol = req.headers["x-forwarded-proto"] || req.protocol || "https";
3320
+ const host = req.headers["x-forwarded-host"] || req.headers.host;
3321
+ const resourceUrl = `${protocol}://${host}/mcp`;
3322
+ return {
3323
+ resource: resourceUrl,
3324
+ authorization_servers: [baseUrl],
3325
+ scopes_supported: [
3326
+ OAuthScopes.READ,
3327
+ OAuthScopes.WRITE,
3328
+ OAuthScopes.ADMIN
3329
+ ],
3330
+ bearer_methods_supported: ["header"],
3331
+ resource_documentation: `${baseUrl}/docs/mcp`
3332
+ };
3333
+ }
3334
+ app.use((_req, res, next) => {
3335
+ const origin = _req.headers.origin;
3336
+ const allowedOrigins = [
3337
+ "https://claude.ai",
3338
+ "https://claude.com",
3339
+ "http://localhost:3000",
3340
+ "http://localhost:5173"
3341
+ ];
3342
+ if (origin && allowedOrigins.includes(origin)) {
3343
+ res.header("Access-Control-Allow-Origin", origin);
3344
+ }
3345
+ res.header(
3346
+ "Access-Control-Allow-Methods",
3347
+ "GET, POST, DELETE, OPTIONS"
3348
+ );
3349
+ res.header(
3350
+ "Access-Control-Allow-Headers",
3351
+ "Content-Type, Authorization, X-API-Key, mcp-session-id"
3352
+ );
3353
+ res.header("Access-Control-Expose-Headers", "mcp-session-id");
3354
+ if (_req.method === "OPTIONS") {
3355
+ res.status(204).end();
3356
+ return;
3357
+ }
3358
+ next();
3359
+ });
2441
3360
  app.get("/mcp/health", (_req, res) => {
2442
3361
  res.json({
2443
3362
  status: "ok",
@@ -2446,6 +3365,12 @@ async function createHTTPServer() {
2446
3365
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
2447
3366
  });
2448
3367
  });
3368
+ app.get("/.well-known/oauth-protected-resource", (req, res) => {
3369
+ res.json(buildProtectedResourceMetadata(req));
3370
+ });
3371
+ app.get("/mcp/.well-known/oauth-protected-resource", (req, res) => {
3372
+ res.json(buildProtectedResourceMetadata(req));
3373
+ });
2449
3374
  const authMiddleware = createAuthMiddleware(baseUrl);
2450
3375
  app.post("/mcp", authMiddleware, async (req, res) => {
2451
3376
  const authReq = req;
@@ -2477,7 +3402,9 @@ async function createHTTPServer() {
2477
3402
  createdAt: now,
2478
3403
  lastAccessedAt: now
2479
3404
  });
2480
- console.error(`[collab-mcp-server] Session initialized: ${id} (user: ${authReq.authContext.userEmail})`);
3405
+ console.error(
3406
+ `[collab-mcp-server] Session initialized: ${id} (user: ${authReq.authContext.userEmail})`
3407
+ );
2481
3408
  },
2482
3409
  onsessionclosed: (id) => {
2483
3410
  sessions.delete(id);
@@ -2487,10 +3414,15 @@ async function createHTTPServer() {
2487
3414
  transport.onclose = () => {
2488
3415
  if (transport.sessionId) {
2489
3416
  sessions.delete(transport.sessionId);
2490
- console.error(`[collab-mcp-server] Transport closed for session: ${transport.sessionId}`);
3417
+ console.error(
3418
+ `[collab-mcp-server] Transport closed for session: ${transport.sessionId}`
3419
+ );
2491
3420
  }
2492
3421
  };
2493
- const server = initializeMCPServer(authReq.apiClient, authReq.authContext);
3422
+ const server = initializeMCPServer(
3423
+ authReq.apiClient,
3424
+ authReq.authContext
3425
+ );
2494
3426
  await server.connect(transport);
2495
3427
  sessionData = {
2496
3428
  transport,
@@ -2573,8 +3505,13 @@ async function createHTTPServer() {
2573
3505
  console.error(`[collab-mcp-server] Listening on http://0.0.0.0:${port}`);
2574
3506
  console.error(`[collab-mcp-server] MCP endpoint: POST/GET/DELETE /mcp`);
2575
3507
  console.error(`[collab-mcp-server] Health check: GET /mcp/health`);
3508
+ console.error(
3509
+ `[collab-mcp-server] OAuth PRM: GET /.well-known/oauth-protected-resource`
3510
+ );
2576
3511
  console.error(`[collab-mcp-server] API URL: ${baseUrl}`);
2577
- console.error(`[collab-mcp-server] Session timeout: ${SESSION_TIMEOUT_MS / 1e3 / 60} minutes`);
3512
+ console.error(
3513
+ `[collab-mcp-server] Session timeout: ${SESSION_TIMEOUT_MS / 1e3 / 60} minutes`
3514
+ );
2578
3515
  });
2579
3516
  }
2580
3517
  function handleCliFlags() {
@@ -2600,7 +3537,9 @@ Environment Variables:
2600
3537
  PORT Server port (default: 3001)
2601
3538
 
2602
3539
  Client Authentication:
2603
- Clients must provide an X-API-Key header with their Collab API key.
3540
+ Clients must authenticate using one of these methods:
3541
+ - X-API-Key header with their Collab API key
3542
+ - Authorization: Bearer <token> header with OAuth access token
2604
3543
  Each session is authenticated independently.
2605
3544
 
2606
3545
  Example deployment with Docker: