@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/README.md +4 -4
- package/dist/apps/activity.html +159 -0
- package/dist/apps/agent-sessions.html +159 -0
- package/dist/apps/kanban.html +159 -0
- package/dist/apps/project-overview.html +159 -0
- package/dist/apps/task-details.html +159 -0
- package/dist/cli.js +1067 -285
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +43 -29
- package/dist/index.js +1065 -283
- package/dist/index.js.map +1 -1
- package/dist/server.js +1234 -295
- package/dist/server.js.map +1 -1
- package/package.json +22 -8
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.
|
|
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
|
|
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/
|
|
236
|
-
var
|
|
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.
|
|
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
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
var
|
|
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 =
|
|
743
|
+
var AssignTaskInputSchema = import_zod3.z.object({
|
|
498
744
|
projectId: cuidOrPrefixedId,
|
|
499
745
|
taskId: cuidOrPrefixedId,
|
|
500
|
-
expectedState:
|
|
746
|
+
expectedState: import_zod3.z.nativeEnum(TaskState).default(TaskState.TODO)
|
|
501
747
|
});
|
|
502
|
-
var UpdateProgressInputSchema =
|
|
748
|
+
var UpdateProgressInputSchema = import_zod3.z.object({
|
|
503
749
|
taskId: cuidOrPrefixedId,
|
|
504
|
-
statusMessage:
|
|
505
|
-
completedCheckpointIds:
|
|
506
|
-
currentCheckpointIndex:
|
|
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 =
|
|
754
|
+
var CompleteTaskInputSchema = import_zod3.z.object({
|
|
509
755
|
projectId: cuidOrPrefixedId,
|
|
510
756
|
taskId: cuidOrPrefixedId,
|
|
511
|
-
pullRequestTitle:
|
|
512
|
-
pullRequestBody:
|
|
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 =
|
|
760
|
+
var ReportErrorInputSchema = import_zod3.z.object({
|
|
515
761
|
taskId: cuidOrPrefixedId,
|
|
516
|
-
errorType:
|
|
517
|
-
errorMessage:
|
|
518
|
-
context:
|
|
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 =
|
|
766
|
+
var GetProjectContextInputSchema = import_zod3.z.object({
|
|
521
767
|
projectId: cuidOrPrefixedId
|
|
522
768
|
});
|
|
523
|
-
var AddNoteInputSchema =
|
|
769
|
+
var AddNoteInputSchema = import_zod3.z.object({
|
|
524
770
|
taskId: cuidOrPrefixedId,
|
|
525
|
-
content:
|
|
771
|
+
content: import_zod3.z.string().min(1).max(500)
|
|
526
772
|
});
|
|
527
|
-
var AbandonTaskInputSchema =
|
|
773
|
+
var AbandonTaskInputSchema = import_zod3.z.object({
|
|
528
774
|
projectId: cuidOrPrefixedId,
|
|
529
775
|
taskId: cuidOrPrefixedId,
|
|
530
|
-
deleteBranch:
|
|
776
|
+
deleteBranch: import_zod3.z.boolean().optional()
|
|
531
777
|
});
|
|
532
|
-
var RequestChangesInputSchema =
|
|
778
|
+
var RequestChangesInputSchema = import_zod3.z.object({
|
|
533
779
|
projectId: cuidOrPrefixedId,
|
|
534
780
|
taskId: cuidOrPrefixedId,
|
|
535
|
-
reviewComments:
|
|
536
|
-
requestedChanges:
|
|
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 =
|
|
784
|
+
var ApproveTaskInputSchema = import_zod3.z.object({
|
|
539
785
|
projectId: cuidOrPrefixedId,
|
|
540
786
|
taskId: cuidOrPrefixedId,
|
|
541
|
-
reviewComments:
|
|
787
|
+
reviewComments: import_zod3.z.string().max(2e3).optional()
|
|
542
788
|
});
|
|
543
|
-
var ArchiveTaskInputSchema =
|
|
789
|
+
var ArchiveTaskInputSchema = import_zod3.z.object({
|
|
544
790
|
projectId: cuidOrPrefixedId,
|
|
545
791
|
taskId: cuidOrPrefixedId
|
|
546
792
|
});
|
|
547
|
-
var UnarchiveTaskInputSchema =
|
|
793
|
+
var UnarchiveTaskInputSchema = import_zod3.z.object({
|
|
548
794
|
projectId: cuidOrPrefixedId,
|
|
549
795
|
taskId: cuidOrPrefixedId
|
|
550
796
|
});
|
|
551
|
-
var CreatePersonalProjectInputSchema =
|
|
552
|
-
name:
|
|
553
|
-
description:
|
|
554
|
-
repositoryUrl:
|
|
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 =
|
|
557
|
-
var CreateTaskMCPInputSchema =
|
|
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:
|
|
561
|
-
description:
|
|
562
|
-
priority:
|
|
563
|
-
acceptanceCriteria:
|
|
564
|
-
description:
|
|
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 =
|
|
568
|
-
name:
|
|
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 =
|
|
816
|
+
var UpdateOrganizationInputSchema = import_zod3.z.object({
|
|
572
817
|
organizationId: cuidOrPrefixedId,
|
|
573
|
-
name:
|
|
574
|
-
logoUrl:
|
|
575
|
-
accentColor:
|
|
576
|
-
tenantName:
|
|
577
|
-
});
|
|
578
|
-
var CreateProjectInputSchema =
|
|
579
|
-
name:
|
|
580
|
-
description:
|
|
581
|
-
type:
|
|
582
|
-
repositoryUrl:
|
|
583
|
-
baseBranch:
|
|
584
|
-
tags:
|
|
585
|
-
});
|
|
586
|
-
var UpdateProjectInputSchema =
|
|
587
|
-
projectId:
|
|
588
|
-
name:
|
|
589
|
-
description:
|
|
590
|
-
repositoryUrl:
|
|
591
|
-
baseBranch:
|
|
592
|
-
tags:
|
|
593
|
-
allowMemberArchive:
|
|
594
|
-
|
|
595
|
-
|
|
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:
|
|
598
|
-
description:
|
|
599
|
-
});
|
|
600
|
-
var CreateTaskInputSchema =
|
|
601
|
-
projectId:
|
|
602
|
-
epicId:
|
|
603
|
-
title:
|
|
604
|
-
description:
|
|
605
|
-
priority:
|
|
606
|
-
acceptanceCriteria:
|
|
607
|
-
description:
|
|
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 =
|
|
611
|
-
taskId:
|
|
612
|
-
title:
|
|
613
|
-
description:
|
|
614
|
-
priority:
|
|
615
|
-
state:
|
|
616
|
-
assigneeId:
|
|
617
|
-
acceptanceCriteria:
|
|
618
|
-
id:
|
|
619
|
-
description:
|
|
620
|
-
completed:
|
|
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 =
|
|
624
|
-
taskId:
|
|
625
|
-
userId:
|
|
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 =
|
|
873
|
+
var CreateTagInputSchema = import_zod3.z.object({
|
|
628
874
|
organizationId: cuidOrPrefixedId,
|
|
629
|
-
name:
|
|
875
|
+
name: import_zod3.z.string().min(1).max(50).regex(/^[a-zA-Z0-9\s-]+$/)
|
|
630
876
|
});
|
|
631
|
-
var UpdateTagInputSchema =
|
|
632
|
-
name:
|
|
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 =
|
|
880
|
+
var UpdateOrganizationSettingsInputSchema = import_zod3.z.object({
|
|
635
881
|
organizationId: cuidOrPrefixedId,
|
|
636
|
-
ldapEnabled:
|
|
637
|
-
ldapUrl:
|
|
638
|
-
ldapBindDN:
|
|
639
|
-
ldapSearchBase:
|
|
640
|
-
deleteMergedBranches:
|
|
641
|
-
enforceConventionalCommits:
|
|
642
|
-
maxPersonalProjectsPerUser:
|
|
643
|
-
logoUrl:
|
|
644
|
-
accentColor:
|
|
645
|
-
tenantName:
|
|
646
|
-
});
|
|
647
|
-
var InviteUserInputSchema =
|
|
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:
|
|
650
|
-
role:
|
|
651
|
-
tags:
|
|
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 =
|
|
899
|
+
var AssignUserTagsInputSchema = import_zod3.z.object({
|
|
654
900
|
userId: cuidOrPrefixedId,
|
|
655
|
-
tags:
|
|
901
|
+
tags: import_zod3.z.array(import_zod3.z.string()).min(0)
|
|
656
902
|
});
|
|
657
|
-
var InviteCollaboratorInputSchema =
|
|
903
|
+
var InviteCollaboratorInputSchema = import_zod3.z.object({
|
|
658
904
|
projectId: cuidOrPrefixedId,
|
|
659
|
-
email:
|
|
905
|
+
email: import_zod3.z.string().email()
|
|
660
906
|
});
|
|
661
|
-
var PublishProjectInputSchema =
|
|
907
|
+
var PublishProjectInputSchema = import_zod3.z.object({
|
|
662
908
|
projectId: cuidOrPrefixedId,
|
|
663
|
-
transferOwnership:
|
|
664
|
-
tags:
|
|
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 =
|
|
667
|
-
name:
|
|
668
|
-
|
|
669
|
-
|
|
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
|
|
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 =
|
|
675
|
-
email:
|
|
676
|
-
password:
|
|
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 =
|
|
679
|
-
email:
|
|
680
|
-
password:
|
|
681
|
-
name:
|
|
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 =
|
|
935
|
+
var VerifyTaskInputSchema = import_zod3.z.object({
|
|
685
936
|
projectId: cuidOrPrefixedId,
|
|
686
937
|
taskId: cuidOrPrefixedId,
|
|
687
|
-
approved:
|
|
688
|
-
feedback:
|
|
938
|
+
approved: import_zod3.z.boolean(),
|
|
939
|
+
feedback: import_zod3.z.string().max(5e3).optional()
|
|
689
940
|
});
|
|
690
|
-
var GetTaskPromptInputSchema =
|
|
941
|
+
var GetTaskPromptInputSchema = import_zod3.z.object({
|
|
691
942
|
projectId: cuidOrPrefixedId,
|
|
692
943
|
taskId: cuidOrPrefixedId
|
|
693
944
|
});
|
|
694
|
-
var UpdateTaskMCPInputSchema =
|
|
945
|
+
var UpdateTaskMCPInputSchema = import_zod3.z.object({
|
|
695
946
|
projectId: cuidOrPrefixedId,
|
|
696
947
|
taskId: cuidOrPrefixedId,
|
|
697
|
-
title:
|
|
698
|
-
description:
|
|
699
|
-
priority:
|
|
700
|
-
acceptanceCriteria:
|
|
701
|
-
id:
|
|
702
|
-
description:
|
|
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 =
|
|
956
|
+
var ReportBranchInputSchema = import_zod3.z.object({
|
|
706
957
|
projectId: cuidOrPrefixedId,
|
|
707
958
|
taskId: cuidOrPrefixedId,
|
|
708
959
|
branchName: gitBranchName
|
|
709
960
|
});
|
|
710
|
-
var ReportPRInputSchema =
|
|
961
|
+
var ReportPRInputSchema = import_zod3.z.object({
|
|
711
962
|
projectId: cuidOrPrefixedId,
|
|
712
963
|
taskId: cuidOrPrefixedId,
|
|
713
|
-
pullRequestUrl:
|
|
714
|
-
pullRequestNumber:
|
|
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
|
|
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
|
-
*
|
|
1310
|
+
* Get a cached value if it exists and hasn't expired.
|
|
1044
1311
|
*/
|
|
1045
|
-
|
|
1046
|
-
const
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1162
|
-
return this.request("GET",
|
|
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) ->
|
|
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
|
|
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:
|
|
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:
|
|
1464
|
-
state:
|
|
1465
|
-
assigneeId:
|
|
1466
|
-
includeArchived:
|
|
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:
|
|
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:
|
|
1524
|
-
taskId:
|
|
1525
|
-
expectedState:
|
|
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:
|
|
1560
|
-
statusMessage:
|
|
1561
|
-
completedCheckpointIds:
|
|
1562
|
-
currentCheckpointIndex:
|
|
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:
|
|
1597
|
-
taskId:
|
|
1598
|
-
pullRequestTitle:
|
|
1599
|
-
pullRequestBody:
|
|
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:
|
|
1657
|
-
errorType:
|
|
1658
|
-
errorMessage:
|
|
1659
|
-
context:
|
|
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:
|
|
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:
|
|
1725
|
-
content:
|
|
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:
|
|
1755
|
-
taskId:
|
|
1756
|
-
deleteBranch:
|
|
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:
|
|
1791
|
-
taskId:
|
|
1792
|
-
branchName:
|
|
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:
|
|
1827
|
-
taskId:
|
|
1828
|
-
pullRequestUrl:
|
|
1829
|
-
pullRequestNumber:
|
|
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:
|
|
1865
|
-
taskId:
|
|
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:
|
|
1899
|
-
taskId:
|
|
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:
|
|
1933
|
-
description:
|
|
1934
|
-
repositoryUrl:
|
|
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:
|
|
1969
|
-
epicId:
|
|
1970
|
-
title:
|
|
1971
|
-
description:
|
|
1972
|
-
priority:
|
|
1973
|
-
acceptanceCriteria:
|
|
1974
|
-
|
|
1975
|
-
description:
|
|
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:
|
|
2015
|
-
taskId:
|
|
2016
|
-
reviewComments:
|
|
2017
|
-
requestedChanges:
|
|
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:
|
|
2053
|
-
taskId:
|
|
2054
|
-
reviewComments:
|
|
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:
|
|
2089
|
-
taskId:
|
|
2090
|
-
approved:
|
|
2091
|
-
feedback:
|
|
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:
|
|
2123
|
-
taskId:
|
|
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.
|
|
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:
|
|
2157
|
-
taskId:
|
|
2158
|
-
title:
|
|
2159
|
-
description:
|
|
2160
|
-
priority:
|
|
2161
|
-
acceptanceCriteria:
|
|
2162
|
-
|
|
2163
|
-
id:
|
|
2164
|
-
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 =
|
|
2255
|
-
taskId:
|
|
2256
|
-
projectId:
|
|
2257
|
-
branchName:
|
|
2258
|
-
worktreePath:
|
|
2259
|
-
startedAt:
|
|
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
|
|
2263
|
-
const
|
|
3044
|
+
const fs2 = await import("fs");
|
|
3045
|
+
const path2 = await import("path");
|
|
2264
3046
|
const cwd = process.cwd();
|
|
2265
|
-
const collabDir =
|
|
2266
|
-
const activeTaskPath =
|
|
3047
|
+
const collabDir = path2.join(cwd, ".collab");
|
|
3048
|
+
const activeTaskPath = path2.join(collabDir, "active-task.json");
|
|
2267
3049
|
try {
|
|
2268
|
-
const resolvedPath =
|
|
2269
|
-
const resolvedCollabDir =
|
|
2270
|
-
if (!resolvedPath.startsWith(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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
3417
|
+
console.error(
|
|
3418
|
+
`[collab-mcp-server] Transport closed for session: ${transport.sessionId}`
|
|
3419
|
+
);
|
|
2491
3420
|
}
|
|
2492
3421
|
};
|
|
2493
|
-
const server = initializeMCPServer(
|
|
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(
|
|
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
|
|
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:
|