@zereight/mcp-gitlab 2.1.24 → 2.1.26
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 +5 -0
- package/build/config.js +1 -0
- package/build/index.js +644 -324
- package/build/oauth.js +65 -3
- package/build/schemas.js +475 -197
- package/build/test/dynamic-api-url-test.js +3 -3
- package/build/test/oauth-tests.js +39 -0
- package/build/test/remote-auth-simple-test.js +13 -2
- package/build/test/schema-tests.js +51 -0
- package/build/test/streamable-http-concurrent-session.test.js +92 -0
- package/build/test/streamable-http-unauthenticated-discovery.test.js +113 -0
- package/build/test/test-ci-catalog.js +177 -0
- package/build/test/test-create-repository.js +120 -0
- package/build/test/test-list-issues.js +15 -3
- package/build/test/test-toolset-filtering.js +6 -5
- package/build/test/test-update-project.js +112 -0
- package/build/test/utils/forwarded-public-base-url.test.js +38 -0
- package/build/tools/registry.js +26 -3
- package/build/utils/forwarded-public-base-url.js +62 -0
- package/build/utils/schema.js +15 -1
- package/package.json +4 -2
package/build/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { getConfig, ENABLE_DYNAMIC_API_URL, GITLAB_AUTH_COOKIE_PATH, GITLAB_CA_CERT_PATH, GITLAB_JOB_TOKEN, GITLAB_MCP_OAUTH, GITLAB_OAUTH_APP_ID, GITLAB_OAUTH_SCOPES, GITLAB_OAUTH_CALLBACK_PROXY, GITLAB_PERSONAL_ACCESS_TOKEN, GITLAB_POOL_MAX_SIZE, GITLAB_READ_ONLY_MODE, GITLAB_TOOLSETS_RAW, GITLAB_TOOLS_RAW, HOST, HTTP_PROXY, HTTPS_PROXY, IS_OLD, MCP_SERVER_URL, NODE_TLS_REJECT_UNAUTHORIZED, NO_PROXY, OAUTH_STATELESS_CLIENT_TTL_SECONDS, OAUTH_STATELESS_MODE, OAUTH_STATELESS_PENDING_TTL_SECONDS, OAUTH_STATELESS_SESSION_TTL_SECONDS, OAUTH_STATELESS_STORED_TTL_SECONDS, PORT, REMOTE_AUTHORIZATION, SESSION_TIMEOUT_SECONDS, SSE, STREAMABLE_HTTP, MCP_TRUST_PROXY, USE_GITLAB_WIKI, USE_MILESTONE, USE_OAUTH, USE_PIPELINE, GITLAB_TOOL_POLICY_APPROVE_RAW, GITLAB_TOOL_POLICY_HIDDEN_RAW, GITLAB_OAUTH_ALLOWED_GROUPS_RAW, GITLAB_ALLOWED_GROUPS_RAW, GITLAB_OAUTH_ALLOWED_GROUPS, } from "./config.js";
|
|
2
|
+
import { getConfig, ENABLE_DYNAMIC_API_URL, GITLAB_AUTH_COOKIE_PATH, GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY, GITLAB_CA_CERT_PATH, GITLAB_JOB_TOKEN, GITLAB_MCP_OAUTH, GITLAB_OAUTH_APP_ID, GITLAB_OAUTH_SCOPES, GITLAB_OAUTH_CALLBACK_PROXY, GITLAB_PERSONAL_ACCESS_TOKEN, GITLAB_POOL_MAX_SIZE, GITLAB_READ_ONLY_MODE, GITLAB_TOOLSETS_RAW, GITLAB_TOOLS_RAW, HOST, HTTP_PROXY, HTTPS_PROXY, IS_OLD, MCP_SERVER_URL, NODE_TLS_REJECT_UNAUTHORIZED, NO_PROXY, OAUTH_STATELESS_CLIENT_TTL_SECONDS, OAUTH_STATELESS_MODE, OAUTH_STATELESS_PENDING_TTL_SECONDS, OAUTH_STATELESS_SESSION_TTL_SECONDS, OAUTH_STATELESS_STORED_TTL_SECONDS, PORT, REMOTE_AUTHORIZATION, SESSION_TIMEOUT_SECONDS, SSE, STREAMABLE_HTTP, MCP_TRUST_PROXY, USE_GITLAB_WIKI, USE_MILESTONE, USE_OAUTH, USE_PIPELINE, GITLAB_TOOL_POLICY_APPROVE_RAW, GITLAB_TOOL_POLICY_HIDDEN_RAW, GITLAB_OAUTH_ALLOWED_GROUPS_RAW, GITLAB_ALLOWED_GROUPS_RAW, GITLAB_OAUTH_ALLOWED_GROUPS, } from "./config.js";
|
|
3
3
|
/** True when the server is running in remote/network mode (SSE or StreamableHTTP transport). */
|
|
4
4
|
const IS_REMOTE = SSE || STREAMABLE_HTTP;
|
|
5
5
|
/**
|
|
@@ -59,62 +59,6 @@ function decryptDownloadToken(tokenStr) {
|
|
|
59
59
|
return null;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
function getLastHeaderValue(value) {
|
|
63
|
-
const raw = Array.isArray(value) ? value[value.length - 1] : value;
|
|
64
|
-
if (!raw)
|
|
65
|
-
return undefined;
|
|
66
|
-
const parts = raw.split(",").map(part => part.trim()).filter(Boolean);
|
|
67
|
-
return parts.length > 0 ? parts[parts.length - 1] : undefined;
|
|
68
|
-
}
|
|
69
|
-
function unquoteHeaderValue(value) {
|
|
70
|
-
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
|
|
71
|
-
return value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
72
|
-
}
|
|
73
|
-
return value;
|
|
74
|
-
}
|
|
75
|
-
function getForwardedPublicBaseUrl(req) {
|
|
76
|
-
if (!MCP_TRUST_PROXY)
|
|
77
|
-
return undefined;
|
|
78
|
-
const forwarded = getLastHeaderValue(req.headers.forwarded);
|
|
79
|
-
const forwardedValues = {};
|
|
80
|
-
if (forwarded) {
|
|
81
|
-
for (const part of forwarded.split(";")) {
|
|
82
|
-
const separator = part.indexOf("=");
|
|
83
|
-
if (separator <= 0)
|
|
84
|
-
continue;
|
|
85
|
-
const key = part.slice(0, separator).trim().toLowerCase();
|
|
86
|
-
const value = unquoteHeaderValue(part.slice(separator + 1).trim());
|
|
87
|
-
if (key && value)
|
|
88
|
-
forwardedValues[key] = value;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const proto = (forwardedValues.proto || getLastHeaderValue(req.headers["x-forwarded-proto"]))?.toLowerCase();
|
|
92
|
-
const host = forwardedValues.host || getLastHeaderValue(req.headers["x-forwarded-host"]);
|
|
93
|
-
if (!proto || !host || !/^https?$/.test(proto))
|
|
94
|
-
return undefined;
|
|
95
|
-
if (/[\s/\\]/.test(host))
|
|
96
|
-
return undefined;
|
|
97
|
-
const prefix = getLastHeaderValue(req.headers["x-forwarded-prefix"]);
|
|
98
|
-
const safePrefix = prefix &&
|
|
99
|
-
prefix.startsWith("/") &&
|
|
100
|
-
!prefix.startsWith("//") &&
|
|
101
|
-
!prefix.includes("://") &&
|
|
102
|
-
!/[\s\\]/.test(prefix)
|
|
103
|
-
? prefix.replace(/\/+$/, "")
|
|
104
|
-
: undefined;
|
|
105
|
-
try {
|
|
106
|
-
const baseUrl = new URL(`${proto}://${host}`);
|
|
107
|
-
if (baseUrl.username || baseUrl.password || baseUrl.pathname !== "/" || baseUrl.search || baseUrl.hash) {
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
if (safePrefix)
|
|
111
|
-
baseUrl.pathname = safePrefix;
|
|
112
|
-
return baseUrl.toString().replace(/\/$/, "");
|
|
113
|
-
}
|
|
114
|
-
catch {
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
62
|
/**
|
|
119
63
|
* Build a URL pointing to the download proxy endpoint.
|
|
120
64
|
* Embeds an encrypted auth token (and API URL for dynamic routing)
|
|
@@ -185,8 +129,9 @@ import { createGitLabOAuthProvider } from "./oauth-proxy.js";
|
|
|
185
129
|
import { mcpAuthRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
|
|
186
130
|
import { ipKeyGenerator } from "express-rate-limit";
|
|
187
131
|
import { normalizeProxyClientIpForRateLimit } from "./utils/proxy-client-ip.js";
|
|
132
|
+
import { getForwardedPublicBaseUrl } from "./utils/forwarded-public-base-url.js";
|
|
188
133
|
import { normalizeGitLabApiUrl } from "./utils/url.js";
|
|
189
|
-
import { estimateMergeCommitCount, filterDiffsByPatterns, summarizeWebhookEvents } from "./utils/helpers.js";
|
|
134
|
+
import { estimateMergeCommitCount, filterDiffsByPatterns, summarizeWebhookEvents, } from "./utils/helpers.js";
|
|
190
135
|
import { graphqlQueryContainsWriteOperation } from "./utils/graphql-query.js";
|
|
191
136
|
import { resolveNestedWikiUpdateTitle } from "./utils/wiki-title.js";
|
|
192
137
|
import { cleanMutuallyExclusiveIdUsernameOptions, LIST_MERGE_REQUESTS_ID_USERNAME_PAIRS, sanitizeToolArguments, } from "./utils/tool-args.js";
|
|
@@ -201,7 +146,7 @@ GitLabDiscussionNoteSchema, // Added
|
|
|
201
146
|
GitLabDiscussionSchema,
|
|
202
147
|
// Draft Notes Schemas
|
|
203
148
|
GitLabDraftNoteSchema, GitLabForkSchema, GitLabBranchSchema, GitLabProtectedBranchSchema, GitLabGroupSchema, GitLabIssueLinkSchema, GitLabIssueSchema, GitLabIssueWithLinkDetailsSchema, GitLabMarkdownUploadSchema, GitLabMergeRequestPipelineSchema, GitLabMergeRequestSchema, GitLabMilestonesSchema, GitLabNamespaceExistsResponseSchema, GitLabNamespaceSchema, GitLabPipelineJobSchema, GitLabDeploymentSchema, GitLabEnvironmentSchema, GitLabPipelineSchema, GitLabPipelineTriggerJobSchema, GitLabProjectMemberSchema, GitLabProjectSchema, GitLabTodoSchema, GitLabReferenceSchema, GitLabRepositorySchema, GitLabSearchBlobResultSchema, GitLabSearchResponseSchema, GitLabTreeItemSchema, GitLabUserSchema, GitLabUsersResponseSchema, GitLabWikiPageSchema, GroupIteration, ListCommitStatusesSchema, ListBranchesSchema, ListCommitsSchema, ListDraftNotesSchema, ListGroupIterationsSchema, ListGroupProjectsSchema, GitLabCiVariableSchema, ListProjectVariablesSchema, GetProjectVariableSchema, CreateProjectVariableSchema, UpdateProjectVariableSchema, DeleteProjectVariableSchema, ListGroupVariablesSchema, GetGroupVariableSchema, CreateGroupVariableSchema, UpdateGroupVariableSchema, DeleteGroupVariableSchema, GitLabDependencyProxySchema, GitLabDependencyProxyBlobSchema, GetDependencyProxySettingsSchema, UpdateDependencyProxySettingsSchema, ListDependencyProxyBlobsSchema, PurgeDependencyProxyCacheSchema, ListIssueDiscussionsSchema, ListIssueLinksSchema, ListIssuesSchema, ListTodosSchema, ListLabelsSchema, ListMergeRequestDiffsSchema, // Added
|
|
204
|
-
GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, MarkAllTodosDoneSchema, MarkTodoDoneSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema, ExecuteGraphQLSchema, GitLabReleaseSchema, ListReleasesSchema, GetReleaseSchema, CreateReleaseSchema, UpdateReleaseSchema, DeleteReleaseSchema, CreateReleaseEvidenceSchema, DownloadReleaseAssetSchema, ListTagsSchema, GetTagSchema, CreateTagSchema, DeleteTagSchema, GetTagSignatureSchema, GitLabTagSchema, GitLabTagSignatureSchema, GetMergeRequestNotesSchema, GetMergeRequestNoteSchema, DeleteMergeRequestDiscussionNoteSchema, ResolveMergeRequestThreadSchema, GetWorkItemSchema, ListWorkItemsSchema, CreateWorkItemSchema, UpdateWorkItemSchema, ConvertWorkItemTypeSchema, ListWorkItemStatusesSchema, ListWorkItemNotesSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, MoveWorkItemSchema, ListCustomFieldDefinitionsSchema, GetTimelineEventsSchema, CreateTimelineEventSchema, ListWebhooksSchema, ListWebhookEventsSchema, GetWebhookEventSchema, HealthCheckSchema, } from "./schemas.js";
|
|
149
|
+
GetMergeRequestFileDiffSchema, ListMergeRequestChangedFilesSchema, ListMergeRequestDiscussionsSchema, ListMergeRequestPipelinesSchema, ListMergeRequestsSchema, ListMergeRequestVersionsSchema, GetMergeRequestVersionSchema, GitLabMergeRequestVersionSchema, GitLabMergeRequestVersionDetailSchema, ListNamespacesSchema, ListPipelineJobsSchema, ListPipelinesSchema, ListDeploymentsSchema, ListEnvironmentsSchema, ListPipelineTriggerJobsSchema, ValidateCiLintSchema, ValidateProjectCiLintSchema, ListCiCatalogResourcesSchema, GetCiCatalogResourceSchema, ListProjectMembersSchema, ListProjectMilestonesSchema, ListProjectsSchema, ListWikiPagesSchema, GetGroupWikiPageSchema, ListGroupWikiPagesSchema, UpdateGroupWikiPageSchema, MarkdownUploadSchema, MarkdownUploadRemoteSchema, DownloadAttachmentSchema, DownloadJobArtifactsSchema, GetJobArtifactFileSchema, GitLabArtifactEntrySchema, ListJobArtifactsSchema, MergeMergeRequestSchema, ApproveMergeRequestSchema, UnapproveMergeRequestSchema, GetMergeRequestApprovalStateSchema, GetMergeRequestConflictsSchema, GitLabMergeRequestApprovalsResponseSchema, GitLabMergeRequestApprovalStateSchema, MyIssuesSchema, MarkAllTodosDoneSchema, MarkTodoDoneSchema, PaginatedDiscussionsResponseSchema, PromoteProjectMilestoneSchema, PublishDraftNoteSchema, PlayPipelineJobSchema, PushFilesSchema, RetryPipelineJobSchema, RetryPipelineSchema, SearchCodeSchema, SearchGroupCodeSchema, SearchProjectCodeSchema, SearchRepositoriesSchema, UpdateDraftNoteSchema, UpdateIssueNoteSchema, UpdateIssueSchema, UpdateIssueDescriptionPatchSchema, UpdateLabelSchema, UpdateProjectSchema, UpdateMergeRequestNoteSchema, UpdateMergeRequestDiscussionNoteSchema, UpdateMergeRequestSchema, UpdateWikiPageSchema, VerifyNamespaceSchema, GitLabEventSchema, ListEventsSchema, GetProjectEventsSchema, ExecuteGraphQLSchema, GitLabReleaseSchema, ListReleasesSchema, GetReleaseSchema, CreateReleaseSchema, UpdateReleaseSchema, DeleteReleaseSchema, CreateReleaseEvidenceSchema, DownloadReleaseAssetSchema, ListTagsSchema, GetTagSchema, CreateTagSchema, DeleteTagSchema, GetTagSignatureSchema, GitLabTagSchema, GitLabTagSignatureSchema, GetMergeRequestNotesSchema, GetMergeRequestNoteSchema, DeleteMergeRequestDiscussionNoteSchema, ResolveMergeRequestThreadSchema, GetWorkItemSchema, ListWorkItemsSchema, CreateWorkItemSchema, UpdateWorkItemSchema, ConvertWorkItemTypeSchema, ListWorkItemStatusesSchema, ListWorkItemNotesSchema, CreateWorkItemNoteSchema, CreateWorkItemEmojiReactionSchema, CreateWorkItemNoteEmojiReactionSchema, ListWorkItemEmojiReactionsSchema, ListWorkItemNoteEmojiReactionsSchema, DeleteWorkItemEmojiReactionSchema, DeleteWorkItemNoteEmojiReactionSchema, MoveWorkItemSchema, ListCustomFieldDefinitionsSchema, GetTimelineEventsSchema, CreateTimelineEventSchema, ListWebhooksSchema, ListWebhookEventsSchema, GetWebhookEventSchema, HealthCheckSchema, } from "./schemas.js";
|
|
205
150
|
import { randomUUID, createCipheriv, createDecipheriv, randomBytes, createHash } from "node:crypto";
|
|
206
151
|
import { pino } from "pino";
|
|
207
152
|
const logger = pino({
|
|
@@ -325,7 +270,9 @@ function createServer() {
|
|
|
325
270
|
const modified = { ...tool };
|
|
326
271
|
// Safety net: remove $schema if present (toJSONSchema strips it for zod schemas,
|
|
327
272
|
// but manually-defined schemas like discover_tools may still have it)
|
|
328
|
-
if (modified.inputSchema &&
|
|
273
|
+
if (modified.inputSchema &&
|
|
274
|
+
typeof modified.inputSchema === "object" &&
|
|
275
|
+
modified.inputSchema !== null) {
|
|
329
276
|
if ("$schema" in modified.inputSchema) {
|
|
330
277
|
modified.inputSchema = { ...modified.inputSchema };
|
|
331
278
|
delete modified.inputSchema.$schema;
|
|
@@ -371,7 +318,12 @@ function createServer() {
|
|
|
371
318
|
};
|
|
372
319
|
const logError = (error) => {
|
|
373
320
|
const durationMs = Date.now() - start;
|
|
374
|
-
logger.error({
|
|
321
|
+
logger.error({
|
|
322
|
+
tool: toolName,
|
|
323
|
+
event: "tool_call_error",
|
|
324
|
+
durationMs,
|
|
325
|
+
error: error instanceof Error ? error.message : String(error),
|
|
326
|
+
}, `tool_call_error: ${toolName} (${durationMs}ms)`);
|
|
375
327
|
throw error;
|
|
376
328
|
};
|
|
377
329
|
try {
|
|
@@ -390,16 +342,18 @@ function createServer() {
|
|
|
390
342
|
return logCompletion({
|
|
391
343
|
content: [{
|
|
392
344
|
type: "text",
|
|
393
|
-
text: JSON.stringify({ categories, hint: "Call discover_tools with a category name to activate it" }
|
|
345
|
+
text: JSON.stringify({ categories, hint: "Call discover_tools with a category name to activate it" }),
|
|
394
346
|
}],
|
|
395
347
|
});
|
|
396
348
|
}
|
|
397
349
|
if (!ALL_TOOLSET_IDS.has(category)) {
|
|
398
350
|
return logCompletion({
|
|
399
|
-
content: [
|
|
351
|
+
content: [
|
|
352
|
+
{
|
|
400
353
|
type: "text",
|
|
401
354
|
text: `Unknown category "${category}". Available: ${[...ALL_TOOLSET_IDS].join(", ")}`,
|
|
402
|
-
}
|
|
355
|
+
},
|
|
356
|
+
],
|
|
403
357
|
isError: true,
|
|
404
358
|
});
|
|
405
359
|
}
|
|
@@ -414,10 +368,12 @@ function createServer() {
|
|
|
414
368
|
const alreadyActive = [...toolsetDef.tools].every(t => currentToolNames.has(t));
|
|
415
369
|
if (alreadyActive) {
|
|
416
370
|
return logCompletion({
|
|
417
|
-
content: [
|
|
371
|
+
content: [
|
|
372
|
+
{
|
|
418
373
|
type: "text",
|
|
419
374
|
text: `Category "${category}" is already active (${toolsetDef.tools.size} tools).`,
|
|
420
|
-
}
|
|
375
|
+
},
|
|
376
|
+
],
|
|
421
377
|
});
|
|
422
378
|
}
|
|
423
379
|
// Add tools from this toolset, respecting all filtering policies
|
|
@@ -437,10 +393,12 @@ function createServer() {
|
|
|
437
393
|
}
|
|
438
394
|
if (newTools.length === 0) {
|
|
439
395
|
return logCompletion({
|
|
440
|
-
content: [
|
|
396
|
+
content: [
|
|
397
|
+
{
|
|
441
398
|
type: "text",
|
|
442
399
|
text: `Category "${category}" has no additional tools to activate (all already active or filtered).`,
|
|
443
|
-
}
|
|
400
|
+
},
|
|
401
|
+
],
|
|
444
402
|
});
|
|
445
403
|
}
|
|
446
404
|
filteredTools.push(...newTools);
|
|
@@ -460,7 +418,7 @@ function createServer() {
|
|
|
460
418
|
activated: category,
|
|
461
419
|
addedTools: addedNames,
|
|
462
420
|
totalTools: filteredTools.length,
|
|
463
|
-
}
|
|
421
|
+
}),
|
|
464
422
|
}],
|
|
465
423
|
});
|
|
466
424
|
}
|
|
@@ -470,10 +428,12 @@ function createServer() {
|
|
|
470
428
|
if (!confirmed) {
|
|
471
429
|
logger.info({ tool: toolName, event: "tool_call_approval_required" }, `Approval required: ${toolName}`);
|
|
472
430
|
return logCompletion({
|
|
473
|
-
content: [
|
|
431
|
+
content: [
|
|
432
|
+
{
|
|
474
433
|
type: "text",
|
|
475
434
|
text: `Tool "${toolName}" requires confirmation. This tool is marked as requiring approval before execution. Re-call with _confirmed: true to proceed.`,
|
|
476
|
-
}
|
|
435
|
+
},
|
|
436
|
+
],
|
|
477
437
|
});
|
|
478
438
|
}
|
|
479
439
|
// Strip _confirmed from args before forwarding to handler
|
|
@@ -636,13 +596,24 @@ function redactSessionIdForLog(sid) {
|
|
|
636
596
|
function isInitializationRequestBody(body) {
|
|
637
597
|
if (!body)
|
|
638
598
|
return false;
|
|
639
|
-
const isInitObj = (m) => typeof m === "object" &&
|
|
640
|
-
m !== null &&
|
|
641
|
-
m.method === "initialize";
|
|
599
|
+
const isInitObj = (m) => typeof m === "object" && m !== null && m.method === "initialize";
|
|
642
600
|
if (Array.isArray(body))
|
|
643
601
|
return body.some(isInitObj);
|
|
644
602
|
return isInitObj(body);
|
|
645
603
|
}
|
|
604
|
+
function isUnauthenticatedDiscoveryRequestBody(body) {
|
|
605
|
+
if (!body)
|
|
606
|
+
return false;
|
|
607
|
+
const isDiscoveryMethod = (m) => {
|
|
608
|
+
if (typeof m !== "object" || m === null)
|
|
609
|
+
return false;
|
|
610
|
+
const method = m.method;
|
|
611
|
+
return (method === "initialize" || method === "notifications/initialized" || method === "tools/list");
|
|
612
|
+
};
|
|
613
|
+
if (Array.isArray(body))
|
|
614
|
+
return body.every(isDiscoveryMethod);
|
|
615
|
+
return isDiscoveryMethod(body);
|
|
616
|
+
}
|
|
646
617
|
/**
|
|
647
618
|
* Normalize an `Mcp-Session-Id` header value.
|
|
648
619
|
*
|
|
@@ -699,9 +670,7 @@ catch (err) {
|
|
|
699
670
|
* a 401 from the OAuth bearer middleware.
|
|
700
671
|
*/
|
|
701
672
|
export function hasStatelessSessionId(req) {
|
|
702
|
-
return Boolean(OAUTH_STATELESS_MODE &&
|
|
703
|
-
STATELESS_MATERIAL &&
|
|
704
|
-
readMcpSessionIdHeader(req));
|
|
673
|
+
return Boolean(OAUTH_STATELESS_MODE && STATELESS_MATERIAL && readMcpSessionIdHeader(req));
|
|
705
674
|
}
|
|
706
675
|
/**
|
|
707
676
|
* Ensure the OAuth token is valid before making an API call.
|
|
@@ -845,7 +814,9 @@ function defaultAuthRetryConfig() {
|
|
|
845
814
|
return {
|
|
846
815
|
isOAuthEnabled: () => USE_OAUTH && oauthClient != null,
|
|
847
816
|
refreshToken: (force) => oauthClient.getAccessToken(force),
|
|
848
|
-
onTokenRefreshed: (token) => {
|
|
817
|
+
onTokenRefreshed: (token) => {
|
|
818
|
+
OAUTH_ACCESS_TOKEN = token;
|
|
819
|
+
},
|
|
849
820
|
buildAuthHeaders,
|
|
850
821
|
logger,
|
|
851
822
|
};
|
|
@@ -1058,7 +1029,12 @@ if (GITLAB_MCP_OAUTH) {
|
|
|
1058
1029
|
}
|
|
1059
1030
|
logger.info("MCP OAuth enabled: GitLab OAuth proxy active (Private-Token/JOB-TOKEN headers bypass OAuth)");
|
|
1060
1031
|
}
|
|
1061
|
-
if (!REMOTE_AUTHORIZATION &&
|
|
1032
|
+
if (!REMOTE_AUTHORIZATION &&
|
|
1033
|
+
!GITLAB_MCP_OAUTH &&
|
|
1034
|
+
!USE_OAUTH &&
|
|
1035
|
+
!GITLAB_PERSONAL_ACCESS_TOKEN &&
|
|
1036
|
+
!GITLAB_JOB_TOKEN &&
|
|
1037
|
+
!GITLAB_AUTH_COOKIE_PATH) {
|
|
1062
1038
|
// Standard mode: token must be in environment (unless using OAuth)
|
|
1063
1039
|
logger.error("GITLAB_PERSONAL_ACCESS_TOKEN environment variable is not set");
|
|
1064
1040
|
logger.info("Either set GITLAB_PERSONAL_ACCESS_TOKEN or enable OAuth with GITLAB_USE_OAUTH=true");
|
|
@@ -1483,18 +1459,28 @@ async function resolveNamesToIds(projectPath, labelNames, usernames) {
|
|
|
1483
1459
|
if (!labelNames?.length && !usernames?.length) {
|
|
1484
1460
|
return { labelIds: [], userIds: [] };
|
|
1485
1461
|
}
|
|
1486
|
-
|
|
1487
|
-
|
|
1462
|
+
labelNames ??= [];
|
|
1463
|
+
usernames ??= [];
|
|
1464
|
+
const labelVars = Object.fromEntries(labelNames.map((name, i) => [`l${i}`, name]));
|
|
1465
|
+
// One alias per label — exact title match via the `title` argument, includes ancestor
|
|
1466
|
+
// group labels, single round trip with no pagination needed.
|
|
1467
|
+
const varDefs = labelNames.map((_, i) => `$l${i}: String!`).join(", ");
|
|
1468
|
+
const aliases = labelNames.map((_, i) => `l${i}: labels(title: $l${i}, includeAncestorGroups: true, first: 1) { nodes { id } }`).join(" ");
|
|
1469
|
+
const { project, users } = await executeGraphQL(`query($path: ID!, $usernames: [String!]!${varDefs ? `, ${varDefs}` : ""}) {
|
|
1470
|
+
project(fullPath: $path) { ${aliases || "__typename"} }
|
|
1488
1471
|
users(usernames: $usernames) { nodes { id username } }
|
|
1489
|
-
}`, { path: projectPath, usernames
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1472
|
+
}`, { path: projectPath, usernames, ...labelVars });
|
|
1473
|
+
if (!project) {
|
|
1474
|
+
throw new Error(`Project '${projectPath}' not found or inaccessible`);
|
|
1475
|
+
}
|
|
1476
|
+
const labelIds = labelNames.map((name, i) => {
|
|
1477
|
+
const nodes = project[`l${i}`]?.nodes;
|
|
1478
|
+
if (!nodes?.length)
|
|
1493
1479
|
throw new Error(`Label '${name}' not found in project`);
|
|
1494
|
-
return
|
|
1480
|
+
return nodes[0].id;
|
|
1495
1481
|
});
|
|
1496
|
-
const userIds =
|
|
1497
|
-
const user =
|
|
1482
|
+
const userIds = usernames.map(name => {
|
|
1483
|
+
const user = users.nodes.find(u => u.username === name);
|
|
1498
1484
|
if (!user)
|
|
1499
1485
|
throw new Error(`User '${name}' not found`);
|
|
1500
1486
|
return user.id;
|
|
@@ -1534,7 +1520,7 @@ async function resolveWorkItemTypeGID(projectPath, typeName) {
|
|
|
1534
1520
|
}
|
|
1535
1521
|
}
|
|
1536
1522
|
}`, { path: projectPath });
|
|
1537
|
-
const typeNode = data.namespace?.workItemTypes?.nodes?.find(
|
|
1523
|
+
const typeNode = data.namespace?.workItemTypes?.nodes?.find(n => n.name === targetName);
|
|
1538
1524
|
if (!typeNode) {
|
|
1539
1525
|
throw new Error(`Work item type '${targetName}' not found in project ${projectPath}`);
|
|
1540
1526
|
}
|
|
@@ -2140,7 +2126,11 @@ async function getWorkItem(projectId, iid) {
|
|
|
2140
2126
|
if (wi.closedAt)
|
|
2141
2127
|
result.closedAt = wi.closedAt;
|
|
2142
2128
|
if (statusWidget?.status)
|
|
2143
|
-
result.status = {
|
|
2129
|
+
result.status = {
|
|
2130
|
+
name: statusWidget.status.name,
|
|
2131
|
+
id: statusWidget.status.id,
|
|
2132
|
+
category: statusWidget.status.category,
|
|
2133
|
+
};
|
|
2144
2134
|
const labels = (labelsWidget?.labels?.nodes || []).map((l) => l.title);
|
|
2145
2135
|
if (labels.length > 0)
|
|
2146
2136
|
result.labels = labels;
|
|
@@ -2178,10 +2168,23 @@ async function getWorkItem(projectId, iid) {
|
|
|
2178
2168
|
if (colorWidget?.color)
|
|
2179
2169
|
result.color = colorWidget.color;
|
|
2180
2170
|
if (hierarchyWidget?.parent)
|
|
2181
|
-
result.parent = {
|
|
2171
|
+
result.parent = {
|
|
2172
|
+
iid: hierarchyWidget.parent.iid,
|
|
2173
|
+
title: hierarchyWidget.parent.title,
|
|
2174
|
+
type: hierarchyWidget.parent.workItemType?.name,
|
|
2175
|
+
project: hierarchyWidget.parent.namespace?.fullPath,
|
|
2176
|
+
webUrl: hierarchyWidget.parent.webUrl,
|
|
2177
|
+
};
|
|
2182
2178
|
const children = hierarchyWidget?.children?.nodes || [];
|
|
2183
2179
|
if (children.length > 0)
|
|
2184
|
-
result.children = children.map((c) => ({
|
|
2180
|
+
result.children = children.map((c) => ({
|
|
2181
|
+
iid: c.iid,
|
|
2182
|
+
title: c.title,
|
|
2183
|
+
state: c.state,
|
|
2184
|
+
type: c.workItemType?.name,
|
|
2185
|
+
project: c.namespace?.fullPath,
|
|
2186
|
+
webUrl: c.webUrl,
|
|
2187
|
+
}));
|
|
2185
2188
|
if (linkedItemsWidget?.blocked)
|
|
2186
2189
|
result.blocked = true;
|
|
2187
2190
|
if (linkedItemsWidget?.blockedByCount > 0)
|
|
@@ -2253,7 +2256,7 @@ async function listWorkItems(projectId, options) {
|
|
|
2253
2256
|
first: options.first || 20,
|
|
2254
2257
|
};
|
|
2255
2258
|
if (options.types && options.types.length > 0) {
|
|
2256
|
-
variables.types = options.types.map(
|
|
2259
|
+
variables.types = options.types.map(t => typeMap[t] || t.replace(/ /g, "_").toUpperCase());
|
|
2257
2260
|
}
|
|
2258
2261
|
if (options.state) {
|
|
2259
2262
|
variables.state = options.state === "opened" ? "opened" : "closed";
|
|
@@ -2721,7 +2724,9 @@ async function updateWorkItem(projectId, iid, options) {
|
|
|
2721
2724
|
linked_items_added: options.linked_items_to_add?.length || 0,
|
|
2722
2725
|
linked_items_removed: options.linked_items_to_remove?.length || 0,
|
|
2723
2726
|
...(options.severity !== undefined && { severity: options.severity }),
|
|
2724
|
-
...(options.escalation_status !== undefined && {
|
|
2727
|
+
...(options.escalation_status !== undefined && {
|
|
2728
|
+
escalation_status: options.escalation_status,
|
|
2729
|
+
}),
|
|
2725
2730
|
};
|
|
2726
2731
|
}
|
|
2727
2732
|
/**
|
|
@@ -3023,9 +3028,7 @@ async function updateIssueNote(projectId, issueIid, discussionId, noteId, body,
|
|
|
3023
3028
|
async function createIssueNote(projectId, issueIid, discussionId, body, createdAt) {
|
|
3024
3029
|
projectId = decodeURIComponent(projectId); // Decode project ID
|
|
3025
3030
|
const basePath = `${getEffectiveApiUrl()}/projects/${encodeURIComponent(getEffectiveProjectId(projectId))}/issues/${issueIid}`;
|
|
3026
|
-
const url = new URL(discussionId
|
|
3027
|
-
? `${basePath}/discussions/${discussionId}/notes`
|
|
3028
|
-
: `${basePath}/notes`);
|
|
3031
|
+
const url = new URL(discussionId ? `${basePath}/discussions/${discussionId}/notes` : `${basePath}/notes`);
|
|
3029
3032
|
const payload = { body };
|
|
3030
3033
|
if (createdAt) {
|
|
3031
3034
|
payload.created_at = createdAt;
|
|
@@ -3392,6 +3395,7 @@ async function createRepository(options) {
|
|
|
3392
3395
|
method: "POST",
|
|
3393
3396
|
body: JSON.stringify({
|
|
3394
3397
|
name: options.name,
|
|
3398
|
+
...(options.namespace_id !== undefined ? { namespace_id: options.namespace_id } : {}),
|
|
3395
3399
|
description: options.description,
|
|
3396
3400
|
visibility: options.visibility,
|
|
3397
3401
|
initialize_with_readme: options.initialize_with_readme,
|
|
@@ -5718,7 +5722,7 @@ async function getCurrentUser() {
|
|
|
5718
5722
|
if ((response.status === 401 || response.status === 403) && usesJobTokenHeader()) {
|
|
5719
5723
|
const jobResponse = await fetch(`${getEffectiveApiUrl()}/job`, getFetchConfig());
|
|
5720
5724
|
if (jobResponse.ok) {
|
|
5721
|
-
const jobData = await jobResponse.json();
|
|
5725
|
+
const jobData = (await jobResponse.json());
|
|
5722
5726
|
if (jobData.user) {
|
|
5723
5727
|
return GitLabUserSchema.parse(jobData.user);
|
|
5724
5728
|
}
|
|
@@ -5737,8 +5741,19 @@ async function getCurrentUser() {
|
|
|
5737
5741
|
async function myIssues(options = {}) {
|
|
5738
5742
|
// Get current user to find their username
|
|
5739
5743
|
const currentUser = await getCurrentUser();
|
|
5740
|
-
|
|
5741
|
-
|
|
5744
|
+
let effectiveProjectId;
|
|
5745
|
+
try {
|
|
5746
|
+
effectiveProjectId = getEffectiveProjectId(options.project_id || "");
|
|
5747
|
+
}
|
|
5748
|
+
catch (err) {
|
|
5749
|
+
if (err instanceof Error &&
|
|
5750
|
+
err.message.includes("No project ID provided and GITLAB_PROJECT_ID is not set")) {
|
|
5751
|
+
effectiveProjectId = "";
|
|
5752
|
+
}
|
|
5753
|
+
else {
|
|
5754
|
+
throw err;
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5742
5757
|
// Use listIssues with assignee_username filter
|
|
5743
5758
|
let listIssuesOptions = {
|
|
5744
5759
|
state: options.state || "opened", // Default to "opened" if not specified
|
|
@@ -5946,7 +5961,11 @@ async function updateGroupVariable(groupId, key, options) {
|
|
|
5946
5961
|
if (filter?.environment_scope) {
|
|
5947
5962
|
url.searchParams.append("filter[environment_scope]", filter.environment_scope);
|
|
5948
5963
|
}
|
|
5949
|
-
const response = await fetch(url.toString(), {
|
|
5964
|
+
const response = await fetch(url.toString(), {
|
|
5965
|
+
...getFetchConfig(),
|
|
5966
|
+
method: "PUT",
|
|
5967
|
+
body: JSON.stringify(body),
|
|
5968
|
+
});
|
|
5950
5969
|
await handleGitLabError(response);
|
|
5951
5970
|
const data = await response.json();
|
|
5952
5971
|
return GitLabCiVariableSchema.parse(data);
|
|
@@ -5994,7 +6013,9 @@ async function getDependencyProxySettings(groupPath) {
|
|
|
5994
6013
|
});
|
|
5995
6014
|
}
|
|
5996
6015
|
async function updateDependencyProxySettings(groupPath, options) {
|
|
5997
|
-
if (options.enabled === undefined &&
|
|
6016
|
+
if (options.enabled === undefined &&
|
|
6017
|
+
options.identity === undefined &&
|
|
6018
|
+
options.secret === undefined) {
|
|
5998
6019
|
throw new Error("At least one of enabled, identity, or secret must be provided");
|
|
5999
6020
|
}
|
|
6000
6021
|
const fullPath = await resolveGroupFullPath(groupPath);
|
|
@@ -6028,7 +6049,11 @@ async function listDependencyProxyBlobs(groupPath, options = {}) {
|
|
|
6028
6049
|
if (!conn)
|
|
6029
6050
|
throw new Error(`Group not found or dependency proxy not enabled: ${fullPath}`);
|
|
6030
6051
|
return {
|
|
6031
|
-
blobs: conn.nodes.map(n => GitLabDependencyProxyBlobSchema.parse({
|
|
6052
|
+
blobs: conn.nodes.map(n => GitLabDependencyProxyBlobSchema.parse({
|
|
6053
|
+
file_name: n.fileName,
|
|
6054
|
+
size: n.size,
|
|
6055
|
+
created_at: n.createdAt,
|
|
6056
|
+
})),
|
|
6032
6057
|
pageInfo: conn.pageInfo,
|
|
6033
6058
|
};
|
|
6034
6059
|
}
|
|
@@ -6088,7 +6113,7 @@ async function markdownUpload(projectId, filePath, content, filename) {
|
|
|
6088
6113
|
const response = await fetch(url.toString(), {
|
|
6089
6114
|
...defaultFetchConfig,
|
|
6090
6115
|
method: "POST",
|
|
6091
|
-
body: form
|
|
6116
|
+
body: form,
|
|
6092
6117
|
});
|
|
6093
6118
|
if (!response.ok) {
|
|
6094
6119
|
await handleGitLabError(response);
|
|
@@ -6409,6 +6434,34 @@ async function getTagSignature(projectId, tagName) {
|
|
|
6409
6434
|
const data = await response.json();
|
|
6410
6435
|
return GitLabTagSignatureSchema.parse(data);
|
|
6411
6436
|
}
|
|
6437
|
+
async function executeGitLabGraphQL(query, variables = {}) {
|
|
6438
|
+
const apiUrl = new URL(getEffectiveApiUrl());
|
|
6439
|
+
const restPath = apiUrl.pathname || "";
|
|
6440
|
+
const idx = restPath.lastIndexOf("/api/v4");
|
|
6441
|
+
const prefix = idx >= 0 ? restPath.slice(0, idx) : "";
|
|
6442
|
+
const graphqlUrl = process.env.GITLAB_GRAPHQL_URL || `${apiUrl.origin}${prefix}/api/graphql`;
|
|
6443
|
+
const controller = new AbortController();
|
|
6444
|
+
const timeout = setTimeout(() => controller.abort(), 45000);
|
|
6445
|
+
try {
|
|
6446
|
+
const response = await fetch(graphqlUrl, {
|
|
6447
|
+
...getFetchConfig(),
|
|
6448
|
+
method: "POST",
|
|
6449
|
+
headers: {
|
|
6450
|
+
...BASE_HEADERS,
|
|
6451
|
+
...buildAuthHeaders(),
|
|
6452
|
+
},
|
|
6453
|
+
body: JSON.stringify({ query, variables }),
|
|
6454
|
+
signal: controller.signal,
|
|
6455
|
+
});
|
|
6456
|
+
if (!response.ok) {
|
|
6457
|
+
await handleGitLabError(response);
|
|
6458
|
+
}
|
|
6459
|
+
return await response.json();
|
|
6460
|
+
}
|
|
6461
|
+
finally {
|
|
6462
|
+
clearTimeout(timeout);
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6412
6465
|
// Request handlers are now registered inside createServer() factory function
|
|
6413
6466
|
// to ensure each transport connection gets its own Server instance (GHSA-345p-7cg4-v4c7).
|
|
6414
6467
|
async function handleToolCall(params) {
|
|
@@ -6472,7 +6525,7 @@ async function handleToolCall(params) {
|
|
|
6472
6525
|
}
|
|
6473
6526
|
const json = await response.json();
|
|
6474
6527
|
return {
|
|
6475
|
-
content: [{ type: "text", text: JSON.stringify(json
|
|
6528
|
+
content: [{ type: "text", text: JSON.stringify(json) }],
|
|
6476
6529
|
};
|
|
6477
6530
|
}
|
|
6478
6531
|
catch (err) {
|
|
@@ -6481,7 +6534,7 @@ async function handleToolCall(params) {
|
|
|
6481
6534
|
content: [
|
|
6482
6535
|
{
|
|
6483
6536
|
type: "text",
|
|
6484
|
-
text: JSON.stringify({ error: `GraphQL request failed: ${message}` }
|
|
6537
|
+
text: JSON.stringify({ error: `GraphQL request failed: ${message}` }),
|
|
6485
6538
|
},
|
|
6486
6539
|
],
|
|
6487
6540
|
};
|
|
@@ -6496,7 +6549,7 @@ async function handleToolCall(params) {
|
|
|
6496
6549
|
try {
|
|
6497
6550
|
const forkedProject = await forkProject(forkArgs.project_id, forkArgs.namespace);
|
|
6498
6551
|
return {
|
|
6499
|
-
content: [{ type: "text", text: JSON.stringify(forkedProject
|
|
6552
|
+
content: [{ type: "text", text: JSON.stringify(forkedProject) }],
|
|
6500
6553
|
};
|
|
6501
6554
|
}
|
|
6502
6555
|
catch (forkError) {
|
|
@@ -6509,7 +6562,7 @@ async function handleToolCall(params) {
|
|
|
6509
6562
|
content: [
|
|
6510
6563
|
{
|
|
6511
6564
|
type: "text",
|
|
6512
|
-
text: JSON.stringify({ error: forkErrorMessage }
|
|
6565
|
+
text: JSON.stringify({ error: forkErrorMessage }),
|
|
6513
6566
|
},
|
|
6514
6567
|
],
|
|
6515
6568
|
};
|
|
@@ -6526,7 +6579,7 @@ async function handleToolCall(params) {
|
|
|
6526
6579
|
ref,
|
|
6527
6580
|
});
|
|
6528
6581
|
return {
|
|
6529
|
-
content: [{ type: "text", text: JSON.stringify(branch
|
|
6582
|
+
content: [{ type: "text", text: JSON.stringify(branch) }],
|
|
6530
6583
|
};
|
|
6531
6584
|
}
|
|
6532
6585
|
case "get_branch_diffs": {
|
|
@@ -6534,14 +6587,14 @@ async function handleToolCall(params) {
|
|
|
6534
6587
|
const diffResp = await getBranchDiffs(args.project_id, args.from, args.to, args.straight);
|
|
6535
6588
|
diffResp.diffs = filterDiffsByPatterns(diffResp.diffs, args.excluded_file_patterns);
|
|
6536
6589
|
return {
|
|
6537
|
-
content: [{ type: "text", text: JSON.stringify(diffResp
|
|
6590
|
+
content: [{ type: "text", text: JSON.stringify(diffResp) }],
|
|
6538
6591
|
};
|
|
6539
6592
|
}
|
|
6540
6593
|
case "search_repositories": {
|
|
6541
6594
|
const args = SearchRepositoriesSchema.parse(params.arguments);
|
|
6542
6595
|
const results = await searchProjects(args.search, args.page, args.per_page);
|
|
6543
6596
|
return {
|
|
6544
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6597
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6545
6598
|
};
|
|
6546
6599
|
}
|
|
6547
6600
|
case "search_code": {
|
|
@@ -6555,7 +6608,7 @@ async function handleToolCall(params) {
|
|
|
6555
6608
|
per_page: args.per_page,
|
|
6556
6609
|
});
|
|
6557
6610
|
return {
|
|
6558
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6611
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6559
6612
|
};
|
|
6560
6613
|
}
|
|
6561
6614
|
case "search_project_code": {
|
|
@@ -6571,7 +6624,7 @@ async function handleToolCall(params) {
|
|
|
6571
6624
|
per_page: args.per_page,
|
|
6572
6625
|
});
|
|
6573
6626
|
return {
|
|
6574
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6627
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6575
6628
|
};
|
|
6576
6629
|
}
|
|
6577
6630
|
case "search_group_code": {
|
|
@@ -6586,7 +6639,7 @@ async function handleToolCall(params) {
|
|
|
6586
6639
|
per_page: args.per_page,
|
|
6587
6640
|
});
|
|
6588
6641
|
return {
|
|
6589
|
-
content: [{ type: "text", text: JSON.stringify(results
|
|
6642
|
+
content: [{ type: "text", text: JSON.stringify(results) }],
|
|
6590
6643
|
};
|
|
6591
6644
|
}
|
|
6592
6645
|
case "create_repository": {
|
|
@@ -6594,7 +6647,7 @@ async function handleToolCall(params) {
|
|
|
6594
6647
|
const args = CreateRepositorySchema.parse(params.arguments);
|
|
6595
6648
|
const repository = await createRepository(args);
|
|
6596
6649
|
return {
|
|
6597
|
-
content: [{ type: "text", text: JSON.stringify(repository
|
|
6650
|
+
content: [{ type: "text", text: JSON.stringify(repository) }],
|
|
6598
6651
|
};
|
|
6599
6652
|
}
|
|
6600
6653
|
case "create_group": {
|
|
@@ -6621,28 +6674,28 @@ async function handleToolCall(params) {
|
|
|
6621
6674
|
const data = await response.json();
|
|
6622
6675
|
const group = GitLabGroupSchema.parse(data);
|
|
6623
6676
|
return {
|
|
6624
|
-
content: [{ type: "text", text: JSON.stringify(group
|
|
6677
|
+
content: [{ type: "text", text: JSON.stringify(group) }],
|
|
6625
6678
|
};
|
|
6626
6679
|
}
|
|
6627
6680
|
case "get_file_contents": {
|
|
6628
6681
|
const args = GetFileContentsSchema.parse(params.arguments);
|
|
6629
6682
|
const contents = await getFileContents(args.project_id, args.file_path, args.ref);
|
|
6630
6683
|
return {
|
|
6631
|
-
content: [{ type: "text", text: JSON.stringify(contents
|
|
6684
|
+
content: [{ type: "text", text: JSON.stringify(contents) }],
|
|
6632
6685
|
};
|
|
6633
6686
|
}
|
|
6634
6687
|
case "create_or_update_file": {
|
|
6635
6688
|
const args = CreateOrUpdateFileSchema.parse(params.arguments);
|
|
6636
6689
|
const result = await createOrUpdateFile(args.project_id, args.file_path, args.content, args.commit_message, args.branch, args.previous_path, args.last_commit_id, args.commit_id);
|
|
6637
6690
|
return {
|
|
6638
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
6691
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
6639
6692
|
};
|
|
6640
6693
|
}
|
|
6641
6694
|
case "push_files": {
|
|
6642
6695
|
const args = PushFilesSchema.parse(params.arguments);
|
|
6643
6696
|
const result = await createCommit(args.project_id, args.commit_message, args.branch, args.files.map(f => ({ path: f.file_path, content: f.content })));
|
|
6644
6697
|
return {
|
|
6645
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
6698
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
6646
6699
|
};
|
|
6647
6700
|
}
|
|
6648
6701
|
case "create_issue": {
|
|
@@ -6650,7 +6703,7 @@ async function handleToolCall(params) {
|
|
|
6650
6703
|
const { project_id, ...options } = args;
|
|
6651
6704
|
const issue = await createIssue(project_id, options);
|
|
6652
6705
|
return {
|
|
6653
|
-
content: [{ type: "text", text: JSON.stringify(issue
|
|
6706
|
+
content: [{ type: "text", text: JSON.stringify(issue) }],
|
|
6654
6707
|
};
|
|
6655
6708
|
}
|
|
6656
6709
|
case "create_merge_request": {
|
|
@@ -6658,7 +6711,7 @@ async function handleToolCall(params) {
|
|
|
6658
6711
|
const { project_id, ...options } = args;
|
|
6659
6712
|
const mergeRequest = await createMergeRequest(project_id, options);
|
|
6660
6713
|
return {
|
|
6661
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequest
|
|
6714
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest) }],
|
|
6662
6715
|
};
|
|
6663
6716
|
}
|
|
6664
6717
|
case "delete_merge_request_discussion_note": {
|
|
@@ -6675,21 +6728,21 @@ async function handleToolCall(params) {
|
|
|
6675
6728
|
args.resolved // Now one of body or resolved must be provided, not both
|
|
6676
6729
|
);
|
|
6677
6730
|
return {
|
|
6678
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6731
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6679
6732
|
};
|
|
6680
6733
|
}
|
|
6681
6734
|
case "create_merge_request_discussion_note": {
|
|
6682
6735
|
const args = CreateMergeRequestDiscussionNoteSchema.parse(params.arguments);
|
|
6683
6736
|
const note = await createMergeRequestDiscussionNote(args.project_id, args.merge_request_iid, args.discussion_id, args.body, args.created_at);
|
|
6684
6737
|
return {
|
|
6685
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6738
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6686
6739
|
};
|
|
6687
6740
|
}
|
|
6688
6741
|
case "create_merge_request_note": {
|
|
6689
6742
|
const args = CreateMergeRequestNoteSchema.parse(params.arguments);
|
|
6690
6743
|
const note = await createMergeRequestNote(args.project_id, args.merge_request_iid, args.body);
|
|
6691
6744
|
return {
|
|
6692
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6745
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6693
6746
|
};
|
|
6694
6747
|
}
|
|
6695
6748
|
case "delete_merge_request_note": {
|
|
@@ -6703,121 +6756,141 @@ async function handleToolCall(params) {
|
|
|
6703
6756
|
const args = GetMergeRequestNoteSchema.parse(params.arguments);
|
|
6704
6757
|
const note = await getMergeRequestNote(args.project_id, args.merge_request_iid, args.note_id);
|
|
6705
6758
|
return {
|
|
6706
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6759
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6707
6760
|
};
|
|
6708
6761
|
}
|
|
6709
6762
|
case "get_merge_request_notes": {
|
|
6710
6763
|
const args = GetMergeRequestNotesSchema.parse(params.arguments);
|
|
6711
6764
|
const notes = await getMergeRequestNotes(args.project_id, args.merge_request_iid, args.sort, args.order_by, args.per_page, args.page);
|
|
6712
6765
|
return {
|
|
6713
|
-
content: [{ type: "text", text: JSON.stringify(notes
|
|
6766
|
+
content: [{ type: "text", text: JSON.stringify(notes) }],
|
|
6714
6767
|
};
|
|
6715
6768
|
}
|
|
6716
6769
|
case "update_merge_request_note": {
|
|
6717
6770
|
const args = UpdateMergeRequestNoteSchema.parse(params.arguments);
|
|
6718
6771
|
const note = await updateMergeRequestNote(args.project_id, args.merge_request_iid, args.note_id, args.body);
|
|
6719
6772
|
return {
|
|
6720
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6773
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6721
6774
|
};
|
|
6722
6775
|
}
|
|
6723
6776
|
case "list_merge_request_emoji_reactions": {
|
|
6724
6777
|
const args = ListMergeRequestEmojiReactionsSchema.parse(params.arguments);
|
|
6725
6778
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid);
|
|
6726
6779
|
const result = await listRestAwardEmoji(path);
|
|
6727
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6780
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6728
6781
|
}
|
|
6729
6782
|
case "list_merge_request_note_emoji_reactions": {
|
|
6730
6783
|
const args = ListMergeRequestNoteEmojiReactionsSchema.parse(params.arguments);
|
|
6731
6784
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid, { noteId: args.note_id, discussionId: args.discussion_id });
|
|
6732
6785
|
const result = await listRestAwardEmoji(path);
|
|
6733
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6786
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6734
6787
|
}
|
|
6735
6788
|
case "create_merge_request_emoji_reaction": {
|
|
6736
6789
|
const args = CreateMergeRequestEmojiReactionSchema.parse(params.arguments);
|
|
6737
6790
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid);
|
|
6738
6791
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6739
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6792
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6740
6793
|
}
|
|
6741
6794
|
case "delete_merge_request_emoji_reaction": {
|
|
6742
6795
|
const args = DeleteMergeRequestEmojiReactionSchema.parse(params.arguments);
|
|
6743
6796
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid, { awardId: args.award_id });
|
|
6744
6797
|
await deleteRestAwardEmoji(path);
|
|
6745
|
-
return {
|
|
6798
|
+
return {
|
|
6799
|
+
content: [{ type: "text", text: "Merge request emoji reaction deleted successfully" }],
|
|
6800
|
+
};
|
|
6746
6801
|
}
|
|
6747
6802
|
case "create_merge_request_note_emoji_reaction": {
|
|
6748
6803
|
const args = CreateMergeRequestNoteEmojiReactionSchema.parse(params.arguments);
|
|
6749
6804
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid, { noteId: args.note_id, discussionId: args.discussion_id });
|
|
6750
6805
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6751
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6806
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6752
6807
|
}
|
|
6753
6808
|
case "delete_merge_request_note_emoji_reaction": {
|
|
6754
6809
|
const args = DeleteMergeRequestNoteEmojiReactionSchema.parse(params.arguments);
|
|
6755
6810
|
const path = buildAwardEmojiPath("merge_requests", args.project_id, args.merge_request_iid, { noteId: args.note_id, discussionId: args.discussion_id, awardId: args.award_id });
|
|
6756
6811
|
await deleteRestAwardEmoji(path);
|
|
6757
|
-
return {
|
|
6812
|
+
return {
|
|
6813
|
+
content: [
|
|
6814
|
+
{ type: "text", text: "Merge request note emoji reaction deleted successfully" },
|
|
6815
|
+
],
|
|
6816
|
+
};
|
|
6758
6817
|
}
|
|
6759
6818
|
case "update_issue_note": {
|
|
6760
6819
|
const args = UpdateIssueNoteSchema.parse(params.arguments);
|
|
6761
6820
|
const note = await updateIssueNote(args.project_id, args.issue_iid, args.discussion_id, args.note_id, args.body, args.resolved);
|
|
6762
6821
|
return {
|
|
6763
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6822
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6764
6823
|
};
|
|
6765
6824
|
}
|
|
6766
6825
|
case "create_issue_note": {
|
|
6767
6826
|
const args = CreateIssueNoteSchema.parse(params.arguments);
|
|
6768
6827
|
const note = await createIssueNote(args.project_id, args.issue_iid, args.discussion_id, args.body, args.created_at);
|
|
6769
6828
|
return {
|
|
6770
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
6829
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
6771
6830
|
};
|
|
6772
6831
|
}
|
|
6773
6832
|
case "list_issue_emoji_reactions": {
|
|
6774
6833
|
const args = ListIssueEmojiReactionsSchema.parse(params.arguments);
|
|
6775
6834
|
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid);
|
|
6776
6835
|
const result = await listRestAwardEmoji(path);
|
|
6777
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6836
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6778
6837
|
}
|
|
6779
6838
|
case "list_issue_note_emoji_reactions": {
|
|
6780
6839
|
const args = ListIssueNoteEmojiReactionsSchema.parse(params.arguments);
|
|
6781
|
-
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6840
|
+
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6841
|
+
noteId: args.note_id,
|
|
6842
|
+
discussionId: args.discussion_id,
|
|
6843
|
+
});
|
|
6782
6844
|
const result = await listRestAwardEmoji(path);
|
|
6783
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6845
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6784
6846
|
}
|
|
6785
6847
|
case "create_issue_emoji_reaction": {
|
|
6786
6848
|
const args = CreateIssueEmojiReactionSchema.parse(params.arguments);
|
|
6787
6849
|
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid);
|
|
6788
6850
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6789
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6851
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6790
6852
|
}
|
|
6791
6853
|
case "delete_issue_emoji_reaction": {
|
|
6792
6854
|
const args = DeleteIssueEmojiReactionSchema.parse(params.arguments);
|
|
6793
|
-
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6855
|
+
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6856
|
+
awardId: args.award_id,
|
|
6857
|
+
});
|
|
6794
6858
|
await deleteRestAwardEmoji(path);
|
|
6795
6859
|
return { content: [{ type: "text", text: "Issue emoji reaction deleted successfully" }] };
|
|
6796
6860
|
}
|
|
6797
6861
|
case "create_issue_note_emoji_reaction": {
|
|
6798
6862
|
const args = CreateIssueNoteEmojiReactionSchema.parse(params.arguments);
|
|
6799
|
-
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6863
|
+
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6864
|
+
noteId: args.note_id,
|
|
6865
|
+
discussionId: args.discussion_id,
|
|
6866
|
+
});
|
|
6800
6867
|
const result = await createRestAwardEmoji(path, args.name);
|
|
6801
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
6868
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
6802
6869
|
}
|
|
6803
6870
|
case "delete_issue_note_emoji_reaction": {
|
|
6804
6871
|
const args = DeleteIssueNoteEmojiReactionSchema.parse(params.arguments);
|
|
6805
|
-
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6872
|
+
const path = buildAwardEmojiPath("issues", args.project_id, args.issue_iid, {
|
|
6873
|
+
noteId: args.note_id,
|
|
6874
|
+
discussionId: args.discussion_id,
|
|
6875
|
+
awardId: args.award_id,
|
|
6876
|
+
});
|
|
6806
6877
|
await deleteRestAwardEmoji(path);
|
|
6807
|
-
return {
|
|
6878
|
+
return {
|
|
6879
|
+
content: [{ type: "text", text: "Issue note emoji reaction deleted successfully" }],
|
|
6880
|
+
};
|
|
6808
6881
|
}
|
|
6809
6882
|
case "list_todos": {
|
|
6810
6883
|
const args = ListTodosSchema.parse(params.arguments);
|
|
6811
6884
|
const todos = await listTodos(args);
|
|
6812
6885
|
return {
|
|
6813
|
-
content: [{ type: "text", text: JSON.stringify(todos
|
|
6886
|
+
content: [{ type: "text", text: JSON.stringify(todos) }],
|
|
6814
6887
|
};
|
|
6815
6888
|
}
|
|
6816
6889
|
case "mark_todo_done": {
|
|
6817
6890
|
const args = MarkTodoDoneSchema.parse(params.arguments);
|
|
6818
6891
|
const todo = await markTodoDone(args.id);
|
|
6819
6892
|
return {
|
|
6820
|
-
content: [{ type: "text", text: JSON.stringify(todo
|
|
6893
|
+
content: [{ type: "text", text: JSON.stringify(todo) }],
|
|
6821
6894
|
};
|
|
6822
6895
|
}
|
|
6823
6896
|
case "mark_all_todos_done": {
|
|
@@ -6851,7 +6924,7 @@ async function handleToolCall(params) {
|
|
|
6851
6924
|
content: [
|
|
6852
6925
|
{
|
|
6853
6926
|
type: "text",
|
|
6854
|
-
text: JSON.stringify(mergeRequestWithDeploymentSummary
|
|
6927
|
+
text: JSON.stringify(mergeRequestWithDeploymentSummary),
|
|
6855
6928
|
},
|
|
6856
6929
|
],
|
|
6857
6930
|
};
|
|
@@ -6861,14 +6934,14 @@ async function handleToolCall(params) {
|
|
|
6861
6934
|
const diffs = await getMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.view);
|
|
6862
6935
|
const filteredDiffs = filterDiffsByPatterns(diffs, args.excluded_file_patterns);
|
|
6863
6936
|
return {
|
|
6864
|
-
content: [{ type: "text", text: JSON.stringify(filteredDiffs
|
|
6937
|
+
content: [{ type: "text", text: JSON.stringify(filteredDiffs) }],
|
|
6865
6938
|
};
|
|
6866
6939
|
}
|
|
6867
6940
|
case "list_merge_request_changed_files": {
|
|
6868
6941
|
const args = ListMergeRequestChangedFilesSchema.parse(params.arguments);
|
|
6869
6942
|
const files = await listMergeRequestChangedFiles(args.project_id, args.merge_request_iid, args.source_branch, args.excluded_file_patterns);
|
|
6870
6943
|
return {
|
|
6871
|
-
content: [{ type: "text", text: JSON.stringify(files
|
|
6944
|
+
content: [{ type: "text", text: JSON.stringify(files) }],
|
|
6872
6945
|
};
|
|
6873
6946
|
}
|
|
6874
6947
|
case "list_merge_request_pipelines": {
|
|
@@ -6876,35 +6949,35 @@ async function handleToolCall(params) {
|
|
|
6876
6949
|
const { project_id, merge_request_iid, ...options } = args;
|
|
6877
6950
|
const pipelines = await listMergeRequestPipelines(project_id, merge_request_iid, options);
|
|
6878
6951
|
return {
|
|
6879
|
-
content: [{ type: "text", text: JSON.stringify(pipelines
|
|
6952
|
+
content: [{ type: "text", text: JSON.stringify(pipelines) }],
|
|
6880
6953
|
};
|
|
6881
6954
|
}
|
|
6882
6955
|
case "list_merge_request_diffs": {
|
|
6883
6956
|
const args = ListMergeRequestDiffsSchema.parse(params.arguments);
|
|
6884
6957
|
const changes = await listMergeRequestDiffs(args.project_id, args.merge_request_iid, args.source_branch, args.page, args.per_page, args.unidiff);
|
|
6885
6958
|
return {
|
|
6886
|
-
content: [{ type: "text", text: JSON.stringify(changes
|
|
6959
|
+
content: [{ type: "text", text: JSON.stringify(changes) }],
|
|
6887
6960
|
};
|
|
6888
6961
|
}
|
|
6889
6962
|
case "get_merge_request_file_diff": {
|
|
6890
6963
|
const args = GetMergeRequestFileDiffSchema.parse(params.arguments);
|
|
6891
6964
|
const fileDiff = await getMergeRequestFileDiff(args.project_id, args.file_paths, args.merge_request_iid, args.source_branch, args.unidiff);
|
|
6892
6965
|
return {
|
|
6893
|
-
content: [{ type: "text", text: JSON.stringify(fileDiff
|
|
6966
|
+
content: [{ type: "text", text: JSON.stringify(fileDiff) }],
|
|
6894
6967
|
};
|
|
6895
6968
|
}
|
|
6896
6969
|
case "list_merge_request_versions": {
|
|
6897
6970
|
const args = ListMergeRequestVersionsSchema.parse(params.arguments);
|
|
6898
6971
|
const versions = await listMergeRequestVersions(args.project_id, args.merge_request_iid);
|
|
6899
6972
|
return {
|
|
6900
|
-
content: [{ type: "text", text: JSON.stringify(versions
|
|
6973
|
+
content: [{ type: "text", text: JSON.stringify(versions) }],
|
|
6901
6974
|
};
|
|
6902
6975
|
}
|
|
6903
6976
|
case "get_merge_request_version": {
|
|
6904
6977
|
const args = GetMergeRequestVersionSchema.parse(params.arguments);
|
|
6905
6978
|
const version = await getMergeRequestVersion(args.project_id, args.merge_request_iid, args.version_id, args.unidiff);
|
|
6906
6979
|
return {
|
|
6907
|
-
content: [{ type: "text", text: JSON.stringify(version
|
|
6980
|
+
content: [{ type: "text", text: JSON.stringify(version) }],
|
|
6908
6981
|
};
|
|
6909
6982
|
}
|
|
6910
6983
|
case "update_merge_request": {
|
|
@@ -6912,7 +6985,7 @@ async function handleToolCall(params) {
|
|
|
6912
6985
|
const { project_id, merge_request_iid, source_branch, ...options } = args;
|
|
6913
6986
|
const mergeRequest = await updateMergeRequest(project_id, options, merge_request_iid, source_branch);
|
|
6914
6987
|
return {
|
|
6915
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequest
|
|
6988
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest) }],
|
|
6916
6989
|
};
|
|
6917
6990
|
}
|
|
6918
6991
|
case "merge_merge_request": {
|
|
@@ -6920,35 +6993,35 @@ async function handleToolCall(params) {
|
|
|
6920
6993
|
const { project_id, merge_request_iid, ...options } = args;
|
|
6921
6994
|
const mergeRequest = await mergeMergeRequest(project_id, options, merge_request_iid);
|
|
6922
6995
|
return {
|
|
6923
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequest
|
|
6996
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequest) }],
|
|
6924
6997
|
};
|
|
6925
6998
|
}
|
|
6926
6999
|
case "approve_merge_request": {
|
|
6927
7000
|
const args = ApproveMergeRequestSchema.parse(params.arguments);
|
|
6928
7001
|
const approvalState = await approveMergeRequest(args.project_id, args.merge_request_iid, args.sha, args.approval_password);
|
|
6929
7002
|
return {
|
|
6930
|
-
content: [{ type: "text", text: JSON.stringify(approvalState
|
|
7003
|
+
content: [{ type: "text", text: JSON.stringify(approvalState) }],
|
|
6931
7004
|
};
|
|
6932
7005
|
}
|
|
6933
7006
|
case "unapprove_merge_request": {
|
|
6934
7007
|
const args = UnapproveMergeRequestSchema.parse(params.arguments);
|
|
6935
7008
|
const approvalState = await unapproveMergeRequest(args.project_id, args.merge_request_iid);
|
|
6936
7009
|
return {
|
|
6937
|
-
content: [{ type: "text", text: JSON.stringify(approvalState
|
|
7010
|
+
content: [{ type: "text", text: JSON.stringify(approvalState) }],
|
|
6938
7011
|
};
|
|
6939
7012
|
}
|
|
6940
7013
|
case "get_merge_request_approval_state": {
|
|
6941
7014
|
const args = GetMergeRequestApprovalStateSchema.parse(params.arguments);
|
|
6942
7015
|
const approvalState = await getMergeRequestApprovalState(args.project_id, args.merge_request_iid);
|
|
6943
7016
|
return {
|
|
6944
|
-
content: [{ type: "text", text: JSON.stringify(approvalState
|
|
7017
|
+
content: [{ type: "text", text: JSON.stringify(approvalState) }],
|
|
6945
7018
|
};
|
|
6946
7019
|
}
|
|
6947
7020
|
case "get_merge_request_conflicts": {
|
|
6948
7021
|
const args = GetMergeRequestConflictsSchema.parse(params.arguments);
|
|
6949
7022
|
const conflicts = await getMergeRequestConflicts(args.project_id, args.merge_request_iid);
|
|
6950
7023
|
return {
|
|
6951
|
-
content: [{ type: "text", text: JSON.stringify(conflicts
|
|
7024
|
+
content: [{ type: "text", text: JSON.stringify(conflicts) }],
|
|
6952
7025
|
};
|
|
6953
7026
|
}
|
|
6954
7027
|
case "mr_discussions": {
|
|
@@ -6956,7 +7029,7 @@ async function handleToolCall(params) {
|
|
|
6956
7029
|
const { project_id, merge_request_iid, ...options } = args;
|
|
6957
7030
|
const discussions = await listMergeRequestDiscussions(project_id, merge_request_iid, options);
|
|
6958
7031
|
return {
|
|
6959
|
-
content: [{ type: "text", text: JSON.stringify(discussions
|
|
7032
|
+
content: [{ type: "text", text: JSON.stringify(discussions) }],
|
|
6960
7033
|
};
|
|
6961
7034
|
}
|
|
6962
7035
|
case "list_namespaces": {
|
|
@@ -6981,7 +7054,7 @@ async function handleToolCall(params) {
|
|
|
6981
7054
|
const data = await response.json();
|
|
6982
7055
|
const namespaces = z.array(GitLabNamespaceSchema).parse(data);
|
|
6983
7056
|
return {
|
|
6984
|
-
content: [{ type: "text", text: JSON.stringify(namespaces
|
|
7057
|
+
content: [{ type: "text", text: JSON.stringify(namespaces) }],
|
|
6985
7058
|
};
|
|
6986
7059
|
}
|
|
6987
7060
|
case "get_namespace": {
|
|
@@ -6994,12 +7067,14 @@ async function handleToolCall(params) {
|
|
|
6994
7067
|
const data = await response.json();
|
|
6995
7068
|
const namespace = GitLabNamespaceSchema.parse(data);
|
|
6996
7069
|
return {
|
|
6997
|
-
content: [{ type: "text", text: JSON.stringify(namespace
|
|
7070
|
+
content: [{ type: "text", text: JSON.stringify(namespace) }],
|
|
6998
7071
|
};
|
|
6999
7072
|
}
|
|
7000
7073
|
case "verify_namespace": {
|
|
7001
7074
|
const args = VerifyNamespaceSchema.parse(params.arguments);
|
|
7002
7075
|
const url = new URL(`${GITLAB_API_URL}/namespaces/${encodeURIComponent(args.path)}/exists`);
|
|
7076
|
+
if (args.parent_id !== undefined)
|
|
7077
|
+
url.searchParams.set("parent_id", String(args.parent_id));
|
|
7003
7078
|
const response = await fetch(url.toString(), {
|
|
7004
7079
|
...getFetchConfig(),
|
|
7005
7080
|
});
|
|
@@ -7007,7 +7082,7 @@ async function handleToolCall(params) {
|
|
|
7007
7082
|
const data = await response.json();
|
|
7008
7083
|
const namespaceExists = GitLabNamespaceExistsResponseSchema.parse(data);
|
|
7009
7084
|
return {
|
|
7010
|
-
content: [{ type: "text", text: JSON.stringify(namespaceExists
|
|
7085
|
+
content: [{ type: "text", text: JSON.stringify(namespaceExists) }],
|
|
7011
7086
|
};
|
|
7012
7087
|
}
|
|
7013
7088
|
case "get_project": {
|
|
@@ -7028,14 +7103,29 @@ async function handleToolCall(params) {
|
|
|
7028
7103
|
const data = await response.json();
|
|
7029
7104
|
// Return raw data without parsing through our schema to avoid type mismatches in tests
|
|
7030
7105
|
return {
|
|
7031
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
7106
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
7032
7107
|
};
|
|
7033
7108
|
}
|
|
7034
7109
|
case "list_projects": {
|
|
7035
7110
|
const args = ListProjectsSchema.parse(params.arguments);
|
|
7036
7111
|
const projects = await listProjects(args);
|
|
7037
7112
|
return {
|
|
7038
|
-
content: [{ type: "text", text: JSON.stringify(projects
|
|
7113
|
+
content: [{ type: "text", text: JSON.stringify(projects) }],
|
|
7114
|
+
};
|
|
7115
|
+
}
|
|
7116
|
+
case "update_project": {
|
|
7117
|
+
const { project_id, ...updates } = UpdateProjectSchema.parse(params.arguments);
|
|
7118
|
+
const effectiveProjectId = getEffectiveProjectId(project_id);
|
|
7119
|
+
const body = Object.fromEntries(Object.entries(updates).filter(([, value]) => value !== undefined));
|
|
7120
|
+
const response = await fetch(`${getEffectiveApiUrl()}/projects/${encodeURIComponent(effectiveProjectId)}`, {
|
|
7121
|
+
...getFetchConfig(),
|
|
7122
|
+
method: "PUT",
|
|
7123
|
+
body: JSON.stringify(body),
|
|
7124
|
+
});
|
|
7125
|
+
await handleGitLabError(response);
|
|
7126
|
+
const data = await response.json();
|
|
7127
|
+
return {
|
|
7128
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
7039
7129
|
};
|
|
7040
7130
|
}
|
|
7041
7131
|
case "list_project_members": {
|
|
@@ -7043,14 +7133,14 @@ async function handleToolCall(params) {
|
|
|
7043
7133
|
const { project_id, ...options } = args;
|
|
7044
7134
|
const members = await listProjectMembers(project_id, options);
|
|
7045
7135
|
return {
|
|
7046
|
-
content: [{ type: "text", text: JSON.stringify(members
|
|
7136
|
+
content: [{ type: "text", text: JSON.stringify(members) }],
|
|
7047
7137
|
};
|
|
7048
7138
|
}
|
|
7049
7139
|
case "get_users": {
|
|
7050
7140
|
const args = GetUsersSchema.parse(params.arguments);
|
|
7051
7141
|
const usersMap = await getUsers(args.usernames);
|
|
7052
7142
|
return {
|
|
7053
|
-
content: [{ type: "text", text: JSON.stringify(usersMap
|
|
7143
|
+
content: [{ type: "text", text: JSON.stringify(usersMap) }],
|
|
7054
7144
|
};
|
|
7055
7145
|
}
|
|
7056
7146
|
case "get_user": {
|
|
@@ -7063,7 +7153,7 @@ async function handleToolCall(params) {
|
|
|
7063
7153
|
const data = await response.json();
|
|
7064
7154
|
const user = GitLabUserFullSchema.parse(data);
|
|
7065
7155
|
return {
|
|
7066
|
-
content: [{ type: "text", text: JSON.stringify(user
|
|
7156
|
+
content: [{ type: "text", text: JSON.stringify(user) }],
|
|
7067
7157
|
};
|
|
7068
7158
|
}
|
|
7069
7159
|
case "whoami": {
|
|
@@ -7076,7 +7166,7 @@ async function handleToolCall(params) {
|
|
|
7076
7166
|
const data = await response.json();
|
|
7077
7167
|
const user = GitLabCurrentUserSchema.parse(data);
|
|
7078
7168
|
return {
|
|
7079
|
-
content: [{ type: "text", text: JSON.stringify(user
|
|
7169
|
+
content: [{ type: "text", text: JSON.stringify(user) }],
|
|
7080
7170
|
};
|
|
7081
7171
|
}
|
|
7082
7172
|
case "create_note": {
|
|
@@ -7084,7 +7174,7 @@ async function handleToolCall(params) {
|
|
|
7084
7174
|
const { project_id, noteable_type, noteable_iid, body } = args;
|
|
7085
7175
|
const note = await createNote(project_id, noteable_type, noteable_iid, body);
|
|
7086
7176
|
return {
|
|
7087
|
-
content: [{ type: "text", text: JSON.stringify(note
|
|
7177
|
+
content: [{ type: "text", text: JSON.stringify(note) }],
|
|
7088
7178
|
};
|
|
7089
7179
|
}
|
|
7090
7180
|
case "get_draft_note": {
|
|
@@ -7092,7 +7182,7 @@ async function handleToolCall(params) {
|
|
|
7092
7182
|
const { project_id, merge_request_iid, draft_note_id } = args;
|
|
7093
7183
|
const draftNote = await getDraftNote(project_id, merge_request_iid, draft_note_id);
|
|
7094
7184
|
return {
|
|
7095
|
-
content: [{ type: "text", text: JSON.stringify(draftNote
|
|
7185
|
+
content: [{ type: "text", text: JSON.stringify(draftNote) }],
|
|
7096
7186
|
};
|
|
7097
7187
|
}
|
|
7098
7188
|
case "list_draft_notes": {
|
|
@@ -7100,15 +7190,15 @@ async function handleToolCall(params) {
|
|
|
7100
7190
|
const { project_id, merge_request_iid } = args;
|
|
7101
7191
|
const draftNotes = await listDraftNotes(project_id, merge_request_iid);
|
|
7102
7192
|
return {
|
|
7103
|
-
content: [{ type: "text", text: JSON.stringify(draftNotes
|
|
7193
|
+
content: [{ type: "text", text: JSON.stringify(draftNotes) }],
|
|
7104
7194
|
};
|
|
7105
7195
|
}
|
|
7106
7196
|
case "create_draft_note": {
|
|
7107
7197
|
const args = CreateDraftNoteSchema.parse(params.arguments);
|
|
7108
|
-
const { project_id, merge_request_iid, body, in_reply_to_discussion_id, position, resolve_discussion } = args;
|
|
7198
|
+
const { project_id, merge_request_iid, body, in_reply_to_discussion_id, position, resolve_discussion, } = args;
|
|
7109
7199
|
const draftNote = await createDraftNote(project_id, merge_request_iid, body, in_reply_to_discussion_id, position, resolve_discussion);
|
|
7110
7200
|
return {
|
|
7111
|
-
content: [{ type: "text", text: JSON.stringify(draftNote
|
|
7201
|
+
content: [{ type: "text", text: JSON.stringify(draftNote) }],
|
|
7112
7202
|
};
|
|
7113
7203
|
}
|
|
7114
7204
|
case "update_draft_note": {
|
|
@@ -7116,7 +7206,7 @@ async function handleToolCall(params) {
|
|
|
7116
7206
|
const { project_id, merge_request_iid, draft_note_id, body, position, resolve_discussion } = args;
|
|
7117
7207
|
const draftNote = await updateDraftNote(project_id, merge_request_iid, draft_note_id, body, position, resolve_discussion);
|
|
7118
7208
|
return {
|
|
7119
|
-
content: [{ type: "text", text: JSON.stringify(draftNote
|
|
7209
|
+
content: [{ type: "text", text: JSON.stringify(draftNote) }],
|
|
7120
7210
|
};
|
|
7121
7211
|
}
|
|
7122
7212
|
case "delete_draft_note": {
|
|
@@ -7132,7 +7222,7 @@ async function handleToolCall(params) {
|
|
|
7132
7222
|
const { project_id, merge_request_iid, draft_note_id } = args;
|
|
7133
7223
|
const publishedNote = await publishDraftNote(project_id, merge_request_iid, draft_note_id);
|
|
7134
7224
|
return {
|
|
7135
|
-
content: [{ type: "text", text: JSON.stringify(publishedNote
|
|
7225
|
+
content: [{ type: "text", text: JSON.stringify(publishedNote) }],
|
|
7136
7226
|
};
|
|
7137
7227
|
}
|
|
7138
7228
|
case "bulk_publish_draft_notes": {
|
|
@@ -7140,7 +7230,7 @@ async function handleToolCall(params) {
|
|
|
7140
7230
|
const { project_id, merge_request_iid } = args;
|
|
7141
7231
|
const publishedNotes = await bulkPublishDraftNotes(project_id, merge_request_iid);
|
|
7142
7232
|
return {
|
|
7143
|
-
content: [{ type: "text", text: JSON.stringify(publishedNotes
|
|
7233
|
+
content: [{ type: "text", text: JSON.stringify(publishedNotes) }],
|
|
7144
7234
|
};
|
|
7145
7235
|
}
|
|
7146
7236
|
case "create_merge_request_thread": {
|
|
@@ -7148,7 +7238,7 @@ async function handleToolCall(params) {
|
|
|
7148
7238
|
const { project_id, merge_request_iid, body, position, created_at } = args;
|
|
7149
7239
|
const thread = await createMergeRequestThread(project_id, merge_request_iid, body, position, created_at);
|
|
7150
7240
|
return {
|
|
7151
|
-
content: [{ type: "text", text: JSON.stringify(thread
|
|
7241
|
+
content: [{ type: "text", text: JSON.stringify(thread) }],
|
|
7152
7242
|
};
|
|
7153
7243
|
}
|
|
7154
7244
|
case "resolve_merge_request_thread": {
|
|
@@ -7165,21 +7255,21 @@ async function handleToolCall(params) {
|
|
|
7165
7255
|
const cleanedOptions = cleanMutuallyExclusiveIdUsernameOptions(options);
|
|
7166
7256
|
const issues = await listIssues(project_id, cleanedOptions);
|
|
7167
7257
|
return {
|
|
7168
|
-
content: [{ type: "text", text: JSON.stringify(issues
|
|
7258
|
+
content: [{ type: "text", text: JSON.stringify(issues) }],
|
|
7169
7259
|
};
|
|
7170
7260
|
}
|
|
7171
7261
|
case "my_issues": {
|
|
7172
7262
|
const args = MyIssuesSchema.parse(params.arguments);
|
|
7173
7263
|
const issues = await myIssues(args);
|
|
7174
7264
|
return {
|
|
7175
|
-
content: [{ type: "text", text: JSON.stringify(issues
|
|
7265
|
+
content: [{ type: "text", text: JSON.stringify(issues) }],
|
|
7176
7266
|
};
|
|
7177
7267
|
}
|
|
7178
7268
|
case "get_issue": {
|
|
7179
7269
|
const args = GetIssueSchema.parse(params.arguments);
|
|
7180
7270
|
const issue = await getIssue(args.project_id, args.issue_iid);
|
|
7181
7271
|
return {
|
|
7182
|
-
content: [{ type: "text", text: JSON.stringify(issue
|
|
7272
|
+
content: [{ type: "text", text: JSON.stringify(issue) }],
|
|
7183
7273
|
};
|
|
7184
7274
|
}
|
|
7185
7275
|
case "update_issue": {
|
|
@@ -7187,7 +7277,7 @@ async function handleToolCall(params) {
|
|
|
7187
7277
|
const { project_id, issue_iid, ...options } = args;
|
|
7188
7278
|
const issue = await updateIssue(project_id, issue_iid, options);
|
|
7189
7279
|
return {
|
|
7190
|
-
content: [{ type: "text", text: JSON.stringify(issue
|
|
7280
|
+
content: [{ type: "text", text: JSON.stringify(issue) }],
|
|
7191
7281
|
};
|
|
7192
7282
|
}
|
|
7193
7283
|
case "update_issue_description_patch": {
|
|
@@ -7281,7 +7371,7 @@ async function handleToolCall(params) {
|
|
|
7281
7371
|
const args = ListIssueLinksSchema.parse(params.arguments);
|
|
7282
7372
|
const links = await listIssueLinks(args.project_id, args.issue_iid);
|
|
7283
7373
|
return {
|
|
7284
|
-
content: [{ type: "text", text: JSON.stringify(links
|
|
7374
|
+
content: [{ type: "text", text: JSON.stringify(links) }],
|
|
7285
7375
|
};
|
|
7286
7376
|
}
|
|
7287
7377
|
case "list_issue_discussions": {
|
|
@@ -7289,21 +7379,21 @@ async function handleToolCall(params) {
|
|
|
7289
7379
|
const { project_id, issue_iid, ...options } = args;
|
|
7290
7380
|
const discussions = await listIssueDiscussions(project_id, issue_iid, options);
|
|
7291
7381
|
return {
|
|
7292
|
-
content: [{ type: "text", text: JSON.stringify(discussions
|
|
7382
|
+
content: [{ type: "text", text: JSON.stringify(discussions) }],
|
|
7293
7383
|
};
|
|
7294
7384
|
}
|
|
7295
7385
|
case "get_issue_link": {
|
|
7296
7386
|
const args = GetIssueLinkSchema.parse(params.arguments);
|
|
7297
7387
|
const link = await getIssueLink(args.project_id, args.issue_iid, args.issue_link_id);
|
|
7298
7388
|
return {
|
|
7299
|
-
content: [{ type: "text", text: JSON.stringify(link
|
|
7389
|
+
content: [{ type: "text", text: JSON.stringify(link) }],
|
|
7300
7390
|
};
|
|
7301
7391
|
}
|
|
7302
7392
|
case "create_issue_link": {
|
|
7303
7393
|
const args = CreateIssueLinkSchema.parse(params.arguments);
|
|
7304
7394
|
const link = await createIssueLink(args.project_id, args.issue_iid, args.target_project_id, args.target_issue_iid, args.link_type);
|
|
7305
7395
|
return {
|
|
7306
|
-
content: [{ type: "text", text: JSON.stringify(link
|
|
7396
|
+
content: [{ type: "text", text: JSON.stringify(link) }],
|
|
7307
7397
|
};
|
|
7308
7398
|
}
|
|
7309
7399
|
case "delete_issue_link": {
|
|
@@ -7325,7 +7415,7 @@ async function handleToolCall(params) {
|
|
|
7325
7415
|
const args = GetWorkItemSchema.parse(params.arguments);
|
|
7326
7416
|
const result = await getWorkItem(args.project_id, args.iid);
|
|
7327
7417
|
return {
|
|
7328
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7418
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7329
7419
|
};
|
|
7330
7420
|
}
|
|
7331
7421
|
case "list_work_items": {
|
|
@@ -7333,7 +7423,7 @@ async function handleToolCall(params) {
|
|
|
7333
7423
|
const { project_id, ...options } = args;
|
|
7334
7424
|
const result = await listWorkItems(project_id, options);
|
|
7335
7425
|
return {
|
|
7336
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7426
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7337
7427
|
};
|
|
7338
7428
|
}
|
|
7339
7429
|
case "create_work_item": {
|
|
@@ -7341,7 +7431,7 @@ async function handleToolCall(params) {
|
|
|
7341
7431
|
const { project_id, ...options } = args;
|
|
7342
7432
|
const result = await createWorkItem(project_id, options);
|
|
7343
7433
|
return {
|
|
7344
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7434
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7345
7435
|
};
|
|
7346
7436
|
}
|
|
7347
7437
|
case "update_work_item": {
|
|
@@ -7349,117 +7439,117 @@ async function handleToolCall(params) {
|
|
|
7349
7439
|
const { project_id, iid, ...options } = args;
|
|
7350
7440
|
const result = await updateWorkItem(project_id, iid, options);
|
|
7351
7441
|
return {
|
|
7352
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7442
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7353
7443
|
};
|
|
7354
7444
|
}
|
|
7355
7445
|
case "convert_work_item_type": {
|
|
7356
7446
|
const args = ConvertWorkItemTypeSchema.parse(params.arguments);
|
|
7357
7447
|
const result = await convertIssueType(args.project_id, args.iid, args.new_type);
|
|
7358
7448
|
return {
|
|
7359
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7449
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7360
7450
|
};
|
|
7361
7451
|
}
|
|
7362
7452
|
case "list_work_item_statuses": {
|
|
7363
7453
|
const args = ListWorkItemStatusesSchema.parse(params.arguments);
|
|
7364
7454
|
const result = await listIssueStatuses(args.project_id, args.work_item_type);
|
|
7365
7455
|
return {
|
|
7366
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7456
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7367
7457
|
};
|
|
7368
7458
|
}
|
|
7369
7459
|
case "list_custom_field_definitions": {
|
|
7370
7460
|
const args = ListCustomFieldDefinitionsSchema.parse(params.arguments);
|
|
7371
7461
|
const result = await listCustomFieldDefinitions(args.project_id, args.work_item_type);
|
|
7372
7462
|
return {
|
|
7373
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7463
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7374
7464
|
};
|
|
7375
7465
|
}
|
|
7376
7466
|
case "move_work_item": {
|
|
7377
7467
|
const args = MoveWorkItemSchema.parse(params.arguments);
|
|
7378
7468
|
const result = await moveWorkItem(args.project_id, args.iid, args.target_project_id);
|
|
7379
7469
|
return {
|
|
7380
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7470
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7381
7471
|
};
|
|
7382
7472
|
}
|
|
7383
7473
|
case "list_work_item_notes": {
|
|
7384
7474
|
const args = ListWorkItemNotesSchema.parse(params.arguments);
|
|
7385
7475
|
const result = await listWorkItemNotes(args.project_id, args.iid, args);
|
|
7386
7476
|
return {
|
|
7387
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7477
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7388
7478
|
};
|
|
7389
7479
|
}
|
|
7390
7480
|
case "create_work_item_note": {
|
|
7391
7481
|
const args = CreateWorkItemNoteSchema.parse(params.arguments);
|
|
7392
7482
|
const result = await createWorkItemNote(args.project_id, args.iid, args.body, args);
|
|
7393
7483
|
return {
|
|
7394
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7484
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7395
7485
|
};
|
|
7396
7486
|
}
|
|
7397
7487
|
case "list_work_item_emoji_reactions": {
|
|
7398
7488
|
const args = ListWorkItemEmojiReactionsSchema.parse(params.arguments);
|
|
7399
7489
|
const { workItemGID } = await resolveWorkItemGID(args.project_id, args.iid);
|
|
7400
7490
|
const result = await listGraphQLAwardEmoji(workItemGID);
|
|
7401
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7491
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7402
7492
|
}
|
|
7403
7493
|
case "list_work_item_note_emoji_reactions": {
|
|
7404
7494
|
const args = ListWorkItemNoteEmojiReactionsSchema.parse(params.arguments);
|
|
7405
7495
|
const result = await listGraphQLAwardEmoji(args.note_id);
|
|
7406
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7496
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7407
7497
|
}
|
|
7408
7498
|
case "create_work_item_emoji_reaction": {
|
|
7409
7499
|
const args = CreateWorkItemEmojiReactionSchema.parse(params.arguments);
|
|
7410
7500
|
const { workItemGID } = await resolveWorkItemGID(args.project_id, args.iid);
|
|
7411
7501
|
const result = await addGraphQLAwardEmoji(workItemGID, args.name);
|
|
7412
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7502
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7413
7503
|
}
|
|
7414
7504
|
case "delete_work_item_emoji_reaction": {
|
|
7415
7505
|
const args = DeleteWorkItemEmojiReactionSchema.parse(params.arguments);
|
|
7416
7506
|
const { workItemGID } = await resolveWorkItemGID(args.project_id, args.iid);
|
|
7417
7507
|
const result = await removeGraphQLAwardEmoji(workItemGID, args.name);
|
|
7418
|
-
return { content: [{ type: "text", text: JSON.stringify(result ?? { status: "success", message: "Work item emoji reaction removed" }
|
|
7508
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? { status: "success", message: "Work item emoji reaction removed" }) }] };
|
|
7419
7509
|
}
|
|
7420
7510
|
case "create_work_item_note_emoji_reaction": {
|
|
7421
7511
|
const args = CreateWorkItemNoteEmojiReactionSchema.parse(params.arguments);
|
|
7422
7512
|
const result = await addGraphQLAwardEmoji(args.note_id, args.name);
|
|
7423
|
-
return { content: [{ type: "text", text: JSON.stringify(result
|
|
7513
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
7424
7514
|
}
|
|
7425
7515
|
case "delete_work_item_note_emoji_reaction": {
|
|
7426
7516
|
const args = DeleteWorkItemNoteEmojiReactionSchema.parse(params.arguments);
|
|
7427
7517
|
const result = await removeGraphQLAwardEmoji(args.note_id, args.name);
|
|
7428
|
-
return { content: [{ type: "text", text: JSON.stringify(result ?? { status: "success", message: "Work item note emoji reaction removed" }
|
|
7518
|
+
return { content: [{ type: "text", text: JSON.stringify(result ?? { status: "success", message: "Work item note emoji reaction removed" }) }] };
|
|
7429
7519
|
}
|
|
7430
7520
|
case "get_timeline_events": {
|
|
7431
7521
|
const args = GetTimelineEventsSchema.parse(params.arguments);
|
|
7432
7522
|
const result = await getTimelineEvents(args.project_id, args.incident_iid);
|
|
7433
7523
|
return {
|
|
7434
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7524
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7435
7525
|
};
|
|
7436
7526
|
}
|
|
7437
7527
|
case "create_timeline_event": {
|
|
7438
7528
|
const args = CreateTimelineEventSchema.parse(params.arguments);
|
|
7439
7529
|
const result = await createTimelineEvent(args.project_id, args.incident_iid, args.note, args.occurred_at, args.tag_names);
|
|
7440
7530
|
return {
|
|
7441
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7531
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7442
7532
|
};
|
|
7443
7533
|
}
|
|
7444
7534
|
case "list_labels": {
|
|
7445
7535
|
const args = ListLabelsSchema.parse(params.arguments);
|
|
7446
7536
|
const labels = await listLabels(args.project_id, args);
|
|
7447
7537
|
return {
|
|
7448
|
-
content: [{ type: "text", text: JSON.stringify(labels
|
|
7538
|
+
content: [{ type: "text", text: JSON.stringify(labels) }],
|
|
7449
7539
|
};
|
|
7450
7540
|
}
|
|
7451
7541
|
case "get_label": {
|
|
7452
7542
|
const args = GetLabelSchema.parse(params.arguments);
|
|
7453
7543
|
const label = await getLabel(args.project_id, args.label_id, args.include_ancestor_groups);
|
|
7454
7544
|
return {
|
|
7455
|
-
content: [{ type: "text", text: JSON.stringify(label
|
|
7545
|
+
content: [{ type: "text", text: JSON.stringify(label) }],
|
|
7456
7546
|
};
|
|
7457
7547
|
}
|
|
7458
7548
|
case "create_label": {
|
|
7459
7549
|
const args = CreateLabelSchema.parse(params.arguments);
|
|
7460
7550
|
const label = await createLabel(args.project_id, args);
|
|
7461
7551
|
return {
|
|
7462
|
-
content: [{ type: "text", text: JSON.stringify(label
|
|
7552
|
+
content: [{ type: "text", text: JSON.stringify(label) }],
|
|
7463
7553
|
};
|
|
7464
7554
|
}
|
|
7465
7555
|
case "update_label": {
|
|
@@ -7467,7 +7557,7 @@ async function handleToolCall(params) {
|
|
|
7467
7557
|
const { project_id, label_id, ...options } = args;
|
|
7468
7558
|
const label = await updateLabel(project_id, label_id, options);
|
|
7469
7559
|
return {
|
|
7470
|
-
content: [{ type: "text", text: JSON.stringify(label
|
|
7560
|
+
content: [{ type: "text", text: JSON.stringify(label) }],
|
|
7471
7561
|
};
|
|
7472
7562
|
}
|
|
7473
7563
|
case "delete_label": {
|
|
@@ -7486,7 +7576,7 @@ async function handleToolCall(params) {
|
|
|
7486
7576
|
const args = ListGroupProjectsSchema.parse(params.arguments);
|
|
7487
7577
|
const projects = await listGroupProjects(args);
|
|
7488
7578
|
return {
|
|
7489
|
-
content: [{ type: "text", text: JSON.stringify(projects
|
|
7579
|
+
content: [{ type: "text", text: JSON.stringify(projects) }],
|
|
7490
7580
|
};
|
|
7491
7581
|
}
|
|
7492
7582
|
case "list_wiki_pages": {
|
|
@@ -7497,28 +7587,28 @@ async function handleToolCall(params) {
|
|
|
7497
7587
|
with_content,
|
|
7498
7588
|
});
|
|
7499
7589
|
return {
|
|
7500
|
-
content: [{ type: "text", text: JSON.stringify(wikiPages
|
|
7590
|
+
content: [{ type: "text", text: JSON.stringify(wikiPages) }],
|
|
7501
7591
|
};
|
|
7502
7592
|
}
|
|
7503
7593
|
case "get_wiki_page": {
|
|
7504
7594
|
const { project_id, slug } = GetWikiPageSchema.parse(params.arguments);
|
|
7505
7595
|
const wikiPage = await getWikiPage(project_id, slug);
|
|
7506
7596
|
return {
|
|
7507
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7597
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7508
7598
|
};
|
|
7509
7599
|
}
|
|
7510
7600
|
case "create_wiki_page": {
|
|
7511
7601
|
const { project_id, title, content, format } = CreateWikiPageSchema.parse(params.arguments);
|
|
7512
7602
|
const wikiPage = await createWikiPage(project_id, title, content, format);
|
|
7513
7603
|
return {
|
|
7514
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7604
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7515
7605
|
};
|
|
7516
7606
|
}
|
|
7517
7607
|
case "update_wiki_page": {
|
|
7518
7608
|
const { project_id, slug, title, content, format } = UpdateWikiPageSchema.parse(params.arguments);
|
|
7519
7609
|
const wikiPage = await updateWikiPage(project_id, slug, title, content, format);
|
|
7520
7610
|
return {
|
|
7521
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7611
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7522
7612
|
};
|
|
7523
7613
|
}
|
|
7524
7614
|
case "delete_wiki_page": {
|
|
@@ -7544,28 +7634,28 @@ async function handleToolCall(params) {
|
|
|
7544
7634
|
with_content,
|
|
7545
7635
|
});
|
|
7546
7636
|
return {
|
|
7547
|
-
content: [{ type: "text", text: JSON.stringify(wikiPages
|
|
7637
|
+
content: [{ type: "text", text: JSON.stringify(wikiPages) }],
|
|
7548
7638
|
};
|
|
7549
7639
|
}
|
|
7550
7640
|
case "get_group_wiki_page": {
|
|
7551
7641
|
const { group_id, slug } = GetGroupWikiPageSchema.parse(params.arguments);
|
|
7552
7642
|
const wikiPage = await getGroupWikiPage(group_id, slug);
|
|
7553
7643
|
return {
|
|
7554
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7644
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7555
7645
|
};
|
|
7556
7646
|
}
|
|
7557
7647
|
case "create_group_wiki_page": {
|
|
7558
7648
|
const { group_id, title, content, format } = CreateGroupWikiPageSchema.parse(params.arguments);
|
|
7559
7649
|
const wikiPage = await createGroupWikiPage(group_id, title, content, format);
|
|
7560
7650
|
return {
|
|
7561
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7651
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7562
7652
|
};
|
|
7563
7653
|
}
|
|
7564
7654
|
case "update_group_wiki_page": {
|
|
7565
7655
|
const { group_id, slug, title, content, format } = UpdateGroupWikiPageSchema.parse(params.arguments);
|
|
7566
7656
|
const wikiPage = await updateGroupWikiPage(group_id, slug, title, content, format);
|
|
7567
7657
|
return {
|
|
7568
|
-
content: [{ type: "text", text: JSON.stringify(wikiPage
|
|
7658
|
+
content: [{ type: "text", text: JSON.stringify(wikiPage) }],
|
|
7569
7659
|
};
|
|
7570
7660
|
}
|
|
7571
7661
|
case "delete_group_wiki_page": {
|
|
@@ -7596,7 +7686,7 @@ async function handleToolCall(params) {
|
|
|
7596
7686
|
}
|
|
7597
7687
|
: items;
|
|
7598
7688
|
return {
|
|
7599
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7689
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7600
7690
|
};
|
|
7601
7691
|
}
|
|
7602
7692
|
case "list_pipelines": {
|
|
@@ -7604,7 +7694,7 @@ async function handleToolCall(params) {
|
|
|
7604
7694
|
const { project_id, ...options } = args;
|
|
7605
7695
|
const pipelines = await listPipelines(project_id, options);
|
|
7606
7696
|
return {
|
|
7607
|
-
content: [{ type: "text", text: JSON.stringify(pipelines
|
|
7697
|
+
content: [{ type: "text", text: JSON.stringify(pipelines) }],
|
|
7608
7698
|
};
|
|
7609
7699
|
}
|
|
7610
7700
|
case "get_pipeline": {
|
|
@@ -7614,7 +7704,7 @@ async function handleToolCall(params) {
|
|
|
7614
7704
|
content: [
|
|
7615
7705
|
{
|
|
7616
7706
|
type: "text",
|
|
7617
|
-
text: JSON.stringify(pipeline
|
|
7707
|
+
text: JSON.stringify(pipeline),
|
|
7618
7708
|
},
|
|
7619
7709
|
],
|
|
7620
7710
|
};
|
|
@@ -7624,14 +7714,14 @@ async function handleToolCall(params) {
|
|
|
7624
7714
|
const { project_id, ...options } = args;
|
|
7625
7715
|
const deployments = await listDeployments(project_id, options);
|
|
7626
7716
|
return {
|
|
7627
|
-
content: [{ type: "text", text: JSON.stringify(deployments
|
|
7717
|
+
content: [{ type: "text", text: JSON.stringify(deployments) }],
|
|
7628
7718
|
};
|
|
7629
7719
|
}
|
|
7630
7720
|
case "get_deployment": {
|
|
7631
7721
|
const { project_id, deployment_id } = GetDeploymentSchema.parse(params.arguments);
|
|
7632
7722
|
const deployment = await getDeployment(project_id, deployment_id);
|
|
7633
7723
|
return {
|
|
7634
|
-
content: [{ type: "text", text: JSON.stringify(deployment
|
|
7724
|
+
content: [{ type: "text", text: JSON.stringify(deployment) }],
|
|
7635
7725
|
};
|
|
7636
7726
|
}
|
|
7637
7727
|
case "list_environments": {
|
|
@@ -7639,14 +7729,14 @@ async function handleToolCall(params) {
|
|
|
7639
7729
|
const { project_id, ...options } = args;
|
|
7640
7730
|
const environments = await listEnvironments(project_id, options);
|
|
7641
7731
|
return {
|
|
7642
|
-
content: [{ type: "text", text: JSON.stringify(environments
|
|
7732
|
+
content: [{ type: "text", text: JSON.stringify(environments) }],
|
|
7643
7733
|
};
|
|
7644
7734
|
}
|
|
7645
7735
|
case "get_environment": {
|
|
7646
7736
|
const { project_id, environment_id } = GetEnvironmentSchema.parse(params.arguments);
|
|
7647
7737
|
const environment = await getEnvironment(project_id, environment_id);
|
|
7648
7738
|
return {
|
|
7649
|
-
content: [{ type: "text", text: JSON.stringify(environment
|
|
7739
|
+
content: [{ type: "text", text: JSON.stringify(environment) }],
|
|
7650
7740
|
};
|
|
7651
7741
|
}
|
|
7652
7742
|
case "list_pipeline_jobs": {
|
|
@@ -7656,7 +7746,7 @@ async function handleToolCall(params) {
|
|
|
7656
7746
|
content: [
|
|
7657
7747
|
{
|
|
7658
7748
|
type: "text",
|
|
7659
|
-
text: JSON.stringify(jobs
|
|
7749
|
+
text: JSON.stringify(jobs),
|
|
7660
7750
|
},
|
|
7661
7751
|
],
|
|
7662
7752
|
};
|
|
@@ -7668,7 +7758,7 @@ async function handleToolCall(params) {
|
|
|
7668
7758
|
content: [
|
|
7669
7759
|
{
|
|
7670
7760
|
type: "text",
|
|
7671
|
-
text: JSON.stringify(triggerJobs
|
|
7761
|
+
text: JSON.stringify(triggerJobs),
|
|
7672
7762
|
},
|
|
7673
7763
|
],
|
|
7674
7764
|
};
|
|
@@ -7680,7 +7770,7 @@ async function handleToolCall(params) {
|
|
|
7680
7770
|
content: [
|
|
7681
7771
|
{
|
|
7682
7772
|
type: "text",
|
|
7683
|
-
text: JSON.stringify(jobDetails
|
|
7773
|
+
text: JSON.stringify(jobDetails),
|
|
7684
7774
|
},
|
|
7685
7775
|
],
|
|
7686
7776
|
};
|
|
@@ -7702,7 +7792,7 @@ async function handleToolCall(params) {
|
|
|
7702
7792
|
const { project_id, ...options } = args;
|
|
7703
7793
|
const result = await validateCiLint(project_id, options);
|
|
7704
7794
|
return {
|
|
7705
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7795
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7706
7796
|
};
|
|
7707
7797
|
}
|
|
7708
7798
|
case "validate_project_ci_lint": {
|
|
@@ -7710,8 +7800,130 @@ async function handleToolCall(params) {
|
|
|
7710
7800
|
const { project_id, ...options } = args;
|
|
7711
7801
|
const result = await validateProjectCiLint(project_id, options);
|
|
7712
7802
|
return {
|
|
7713
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
7714
|
-
};
|
|
7803
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
7804
|
+
};
|
|
7805
|
+
}
|
|
7806
|
+
case "list_ci_catalog_resources": {
|
|
7807
|
+
const args = ListCiCatalogResourcesSchema.parse(params.arguments);
|
|
7808
|
+
const result = await executeGitLabGraphQL(`query ListCiCatalogResources(
|
|
7809
|
+
$search: String,
|
|
7810
|
+
$first: Int,
|
|
7811
|
+
$after: String,
|
|
7812
|
+
$groupIds: [GroupID!],
|
|
7813
|
+
$scope: CiCatalogResourceScope,
|
|
7814
|
+
$sort: CiCatalogResourceSort,
|
|
7815
|
+
$topics: [String!],
|
|
7816
|
+
$verificationLevel: CiCatalogResourceVerificationLevel
|
|
7817
|
+
) {
|
|
7818
|
+
ciCatalogResources(
|
|
7819
|
+
search: $search,
|
|
7820
|
+
first: $first,
|
|
7821
|
+
after: $after,
|
|
7822
|
+
groupIds: $groupIds,
|
|
7823
|
+
scope: $scope,
|
|
7824
|
+
sort: $sort,
|
|
7825
|
+
topics: $topics,
|
|
7826
|
+
verificationLevel: $verificationLevel
|
|
7827
|
+
) {
|
|
7828
|
+
nodes {
|
|
7829
|
+
id
|
|
7830
|
+
name
|
|
7831
|
+
description
|
|
7832
|
+
fullPath
|
|
7833
|
+
icon
|
|
7834
|
+
starCount
|
|
7835
|
+
topics
|
|
7836
|
+
verificationLevel
|
|
7837
|
+
visibilityLevel
|
|
7838
|
+
webPath
|
|
7839
|
+
latestReleasedAt
|
|
7840
|
+
last30DayUsageCount
|
|
7841
|
+
}
|
|
7842
|
+
pageInfo { hasNextPage endCursor }
|
|
7843
|
+
}
|
|
7844
|
+
}`, {
|
|
7845
|
+
search: args.search,
|
|
7846
|
+
first: args.first ?? 20,
|
|
7847
|
+
after: args.after,
|
|
7848
|
+
groupIds: args.group_ids,
|
|
7849
|
+
scope: args.scope,
|
|
7850
|
+
sort: args.sort,
|
|
7851
|
+
topics: args.topics,
|
|
7852
|
+
verificationLevel: args.verification_level,
|
|
7853
|
+
});
|
|
7854
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
7855
|
+
}
|
|
7856
|
+
case "get_ci_catalog_resource": {
|
|
7857
|
+
const args = GetCiCatalogResourceSchema.parse(params.arguments);
|
|
7858
|
+
const result = await executeGitLabGraphQL(`query GetCiCatalogResource(
|
|
7859
|
+
$id: CiCatalogResourceID,
|
|
7860
|
+
$fullPath: ID,
|
|
7861
|
+
$versionLimit: Int!,
|
|
7862
|
+
$componentLimit: Int!,
|
|
7863
|
+
$includeReadme: Boolean!
|
|
7864
|
+
) {
|
|
7865
|
+
ciCatalogResource(id: $id, fullPath: $fullPath) {
|
|
7866
|
+
id
|
|
7867
|
+
name
|
|
7868
|
+
description
|
|
7869
|
+
fullPath
|
|
7870
|
+
icon
|
|
7871
|
+
starCount
|
|
7872
|
+
topics
|
|
7873
|
+
verificationLevel
|
|
7874
|
+
visibilityLevel
|
|
7875
|
+
webPath
|
|
7876
|
+
latestReleasedAt
|
|
7877
|
+
last30DayUsageCount
|
|
7878
|
+
versions(first: $versionLimit) {
|
|
7879
|
+
nodes {
|
|
7880
|
+
id
|
|
7881
|
+
name
|
|
7882
|
+
path
|
|
7883
|
+
createdAt
|
|
7884
|
+
releasedAt
|
|
7885
|
+
readme @include(if: $includeReadme)
|
|
7886
|
+
semver { major minor patch }
|
|
7887
|
+
components(first: $componentLimit) {
|
|
7888
|
+
nodes {
|
|
7889
|
+
id
|
|
7890
|
+
name
|
|
7891
|
+
description
|
|
7892
|
+
includePath
|
|
7893
|
+
last30DayUsageCount
|
|
7894
|
+
inputs {
|
|
7895
|
+
name
|
|
7896
|
+
description
|
|
7897
|
+
type
|
|
7898
|
+
required
|
|
7899
|
+
default
|
|
7900
|
+
options
|
|
7901
|
+
regex
|
|
7902
|
+
}
|
|
7903
|
+
}
|
|
7904
|
+
pageInfo { hasNextPage endCursor }
|
|
7905
|
+
}
|
|
7906
|
+
}
|
|
7907
|
+
pageInfo { hasNextPage endCursor }
|
|
7908
|
+
}
|
|
7909
|
+
}
|
|
7910
|
+
}`, {
|
|
7911
|
+
id: args.id,
|
|
7912
|
+
fullPath: args.full_path,
|
|
7913
|
+
versionLimit: args.version_limit ?? 5,
|
|
7914
|
+
componentLimit: args.component_limit ?? 20,
|
|
7915
|
+
includeReadme: args.include_readme ?? false,
|
|
7916
|
+
});
|
|
7917
|
+
if (args.component_name) {
|
|
7918
|
+
const resource = result?.data?.ciCatalogResource;
|
|
7919
|
+
for (const version of resource?.versions?.nodes ?? []) {
|
|
7920
|
+
const components = version?.components?.nodes;
|
|
7921
|
+
if (Array.isArray(components)) {
|
|
7922
|
+
version.components.nodes = components.filter(component => component?.name === args.component_name);
|
|
7923
|
+
}
|
|
7924
|
+
}
|
|
7925
|
+
}
|
|
7926
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
7715
7927
|
}
|
|
7716
7928
|
case "create_pipeline": {
|
|
7717
7929
|
const { project_id, ref, variables, inputs } = CreatePipelineSchema.parse(params.arguments);
|
|
@@ -7792,7 +8004,7 @@ async function handleToolCall(params) {
|
|
|
7792
8004
|
content: [
|
|
7793
8005
|
{
|
|
7794
8006
|
type: "text",
|
|
7795
|
-
text: JSON.stringify(artifacts
|
|
8007
|
+
text: JSON.stringify(artifacts),
|
|
7796
8008
|
},
|
|
7797
8009
|
],
|
|
7798
8010
|
};
|
|
@@ -7805,7 +8017,7 @@ async function handleToolCall(params) {
|
|
|
7805
8017
|
}
|
|
7806
8018
|
const downloadUrl = buildDownloadUrl("job-artifacts", { project_id, job_id });
|
|
7807
8019
|
return {
|
|
7808
|
-
content: [{ type: "text", text: JSON.stringify({ download_url: downloadUrl, filename: `artifacts_job_${job_id}.zip` }
|
|
8020
|
+
content: [{ type: "text", text: JSON.stringify({ download_url: downloadUrl, filename: `artifacts_job_${job_id}.zip` }) }],
|
|
7809
8021
|
};
|
|
7810
8022
|
}
|
|
7811
8023
|
const filePath = await downloadJobArtifacts(project_id, job_id, local_path);
|
|
@@ -7813,7 +8025,7 @@ async function handleToolCall(params) {
|
|
|
7813
8025
|
content: [
|
|
7814
8026
|
{
|
|
7815
8027
|
type: "text",
|
|
7816
|
-
text: JSON.stringify({ success: true, file_path: filePath }
|
|
8028
|
+
text: JSON.stringify({ success: true, file_path: filePath }),
|
|
7817
8029
|
},
|
|
7818
8030
|
],
|
|
7819
8031
|
};
|
|
@@ -7835,7 +8047,7 @@ async function handleToolCall(params) {
|
|
|
7835
8047
|
const cleanedOptions = cleanMutuallyExclusiveIdUsernameOptions(options, LIST_MERGE_REQUESTS_ID_USERNAME_PAIRS);
|
|
7836
8048
|
const mergeRequests = await listMergeRequests(project_id, cleanedOptions);
|
|
7837
8049
|
return {
|
|
7838
|
-
content: [{ type: "text", text: JSON.stringify(mergeRequests
|
|
8050
|
+
content: [{ type: "text", text: JSON.stringify(mergeRequests) }],
|
|
7839
8051
|
};
|
|
7840
8052
|
}
|
|
7841
8053
|
case "list_milestones": {
|
|
@@ -7845,7 +8057,7 @@ async function handleToolCall(params) {
|
|
|
7845
8057
|
content: [
|
|
7846
8058
|
{
|
|
7847
8059
|
type: "text",
|
|
7848
|
-
text: JSON.stringify(milestones
|
|
8060
|
+
text: JSON.stringify(milestones),
|
|
7849
8061
|
},
|
|
7850
8062
|
],
|
|
7851
8063
|
};
|
|
@@ -7857,7 +8069,7 @@ async function handleToolCall(params) {
|
|
|
7857
8069
|
content: [
|
|
7858
8070
|
{
|
|
7859
8071
|
type: "text",
|
|
7860
|
-
text: JSON.stringify(milestone
|
|
8072
|
+
text: JSON.stringify(milestone),
|
|
7861
8073
|
},
|
|
7862
8074
|
],
|
|
7863
8075
|
};
|
|
@@ -7869,7 +8081,7 @@ async function handleToolCall(params) {
|
|
|
7869
8081
|
content: [
|
|
7870
8082
|
{
|
|
7871
8083
|
type: "text",
|
|
7872
|
-
text: JSON.stringify(milestone
|
|
8084
|
+
text: JSON.stringify(milestone),
|
|
7873
8085
|
},
|
|
7874
8086
|
],
|
|
7875
8087
|
};
|
|
@@ -7881,7 +8093,7 @@ async function handleToolCall(params) {
|
|
|
7881
8093
|
content: [
|
|
7882
8094
|
{
|
|
7883
8095
|
type: "text",
|
|
7884
|
-
text: JSON.stringify(milestone
|
|
8096
|
+
text: JSON.stringify(milestone),
|
|
7885
8097
|
},
|
|
7886
8098
|
],
|
|
7887
8099
|
};
|
|
@@ -7908,7 +8120,7 @@ async function handleToolCall(params) {
|
|
|
7908
8120
|
content: [
|
|
7909
8121
|
{
|
|
7910
8122
|
type: "text",
|
|
7911
|
-
text: JSON.stringify(issues
|
|
8123
|
+
text: JSON.stringify(issues),
|
|
7912
8124
|
},
|
|
7913
8125
|
],
|
|
7914
8126
|
};
|
|
@@ -7920,7 +8132,7 @@ async function handleToolCall(params) {
|
|
|
7920
8132
|
content: [
|
|
7921
8133
|
{
|
|
7922
8134
|
type: "text",
|
|
7923
|
-
text: JSON.stringify(mergeRequests
|
|
8135
|
+
text: JSON.stringify(mergeRequests),
|
|
7924
8136
|
},
|
|
7925
8137
|
],
|
|
7926
8138
|
};
|
|
@@ -7932,7 +8144,7 @@ async function handleToolCall(params) {
|
|
|
7932
8144
|
content: [
|
|
7933
8145
|
{
|
|
7934
8146
|
type: "text",
|
|
7935
|
-
text: JSON.stringify(milestone
|
|
8147
|
+
text: JSON.stringify(milestone),
|
|
7936
8148
|
},
|
|
7937
8149
|
],
|
|
7938
8150
|
};
|
|
@@ -7944,7 +8156,7 @@ async function handleToolCall(params) {
|
|
|
7944
8156
|
content: [
|
|
7945
8157
|
{
|
|
7946
8158
|
type: "text",
|
|
7947
|
-
text: JSON.stringify(events
|
|
8159
|
+
text: JSON.stringify(events),
|
|
7948
8160
|
},
|
|
7949
8161
|
],
|
|
7950
8162
|
};
|
|
@@ -7953,21 +8165,21 @@ async function handleToolCall(params) {
|
|
|
7953
8165
|
const args = ListCommitsSchema.parse(params.arguments);
|
|
7954
8166
|
const commits = await listCommits(args.project_id, args);
|
|
7955
8167
|
return {
|
|
7956
|
-
content: [{ type: "text", text: JSON.stringify(commits
|
|
8168
|
+
content: [{ type: "text", text: JSON.stringify(commits) }],
|
|
7957
8169
|
};
|
|
7958
8170
|
}
|
|
7959
8171
|
case "get_commit": {
|
|
7960
8172
|
const args = GetCommitSchema.parse(params.arguments);
|
|
7961
8173
|
const commit = await getCommit(args.project_id, args.sha, args.stats);
|
|
7962
8174
|
return {
|
|
7963
|
-
content: [{ type: "text", text: JSON.stringify(commit
|
|
8175
|
+
content: [{ type: "text", text: JSON.stringify(commit) }],
|
|
7964
8176
|
};
|
|
7965
8177
|
}
|
|
7966
8178
|
case "get_commit_diff": {
|
|
7967
8179
|
const args = GetCommitDiffSchema.parse(params.arguments);
|
|
7968
8180
|
const diff = await getCommitDiff(args.project_id, args.sha, args.full_diff);
|
|
7969
8181
|
return {
|
|
7970
|
-
content: [{ type: "text", text: JSON.stringify(diff
|
|
8182
|
+
content: [{ type: "text", text: JSON.stringify(diff) }],
|
|
7971
8183
|
};
|
|
7972
8184
|
}
|
|
7973
8185
|
case "get_file_blame": {
|
|
@@ -7975,7 +8187,7 @@ async function handleToolCall(params) {
|
|
|
7975
8187
|
const { project_id, ...options } = args;
|
|
7976
8188
|
const blame = await getFileBlame(project_id, options);
|
|
7977
8189
|
return {
|
|
7978
|
-
content: [{ type: "text", text: JSON.stringify(blame
|
|
8190
|
+
content: [{ type: "text", text: JSON.stringify(blame) }],
|
|
7979
8191
|
};
|
|
7980
8192
|
}
|
|
7981
8193
|
case "list_commit_statuses": {
|
|
@@ -7983,7 +8195,7 @@ async function handleToolCall(params) {
|
|
|
7983
8195
|
const { project_id, sha, ...options } = args;
|
|
7984
8196
|
const statuses = await listCommitStatuses(project_id, sha, options);
|
|
7985
8197
|
return {
|
|
7986
|
-
content: [{ type: "text", text: JSON.stringify(statuses
|
|
8198
|
+
content: [{ type: "text", text: JSON.stringify(statuses) }],
|
|
7987
8199
|
};
|
|
7988
8200
|
}
|
|
7989
8201
|
case "create_commit_status": {
|
|
@@ -7991,14 +8203,14 @@ async function handleToolCall(params) {
|
|
|
7991
8203
|
const { project_id, sha, ...options } = args;
|
|
7992
8204
|
const status = await createCommitStatus(project_id, sha, options);
|
|
7993
8205
|
return {
|
|
7994
|
-
content: [{ type: "text", text: JSON.stringify(status
|
|
8206
|
+
content: [{ type: "text", text: JSON.stringify(status) }],
|
|
7995
8207
|
};
|
|
7996
8208
|
}
|
|
7997
8209
|
case "list_group_iterations": {
|
|
7998
8210
|
const args = ListGroupIterationsSchema.parse(params.arguments);
|
|
7999
8211
|
const iterations = await listGroupIterations(args.group_id, args);
|
|
8000
8212
|
return {
|
|
8001
|
-
content: [{ type: "text", text: JSON.stringify(iterations
|
|
8213
|
+
content: [{ type: "text", text: JSON.stringify(iterations) }],
|
|
8002
8214
|
};
|
|
8003
8215
|
}
|
|
8004
8216
|
// --- CI/CD Variables ---
|
|
@@ -8006,24 +8218,24 @@ async function handleToolCall(params) {
|
|
|
8006
8218
|
const args = ListProjectVariablesSchema.parse(params.arguments);
|
|
8007
8219
|
const { project_id, ...options } = args;
|
|
8008
8220
|
const variables = await listProjectVariables(project_id, options);
|
|
8009
|
-
return { content: [{ type: "text", text: JSON.stringify(variables
|
|
8221
|
+
return { content: [{ type: "text", text: JSON.stringify(variables) }] };
|
|
8010
8222
|
}
|
|
8011
8223
|
case "get_project_variable": {
|
|
8012
8224
|
const args = GetProjectVariableSchema.parse(params.arguments);
|
|
8013
8225
|
const variable = await getProjectVariable(args.project_id, args.key, args.filter);
|
|
8014
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8226
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8015
8227
|
}
|
|
8016
8228
|
case "create_project_variable": {
|
|
8017
8229
|
const args = CreateProjectVariableSchema.parse(params.arguments);
|
|
8018
8230
|
const { project_id, ...options } = args;
|
|
8019
8231
|
const variable = await createProjectVariable(project_id, options);
|
|
8020
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8232
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8021
8233
|
}
|
|
8022
8234
|
case "update_project_variable": {
|
|
8023
8235
|
const args = UpdateProjectVariableSchema.parse(params.arguments);
|
|
8024
8236
|
const { project_id, key, ...options } = args;
|
|
8025
8237
|
const variable = await updateProjectVariable(project_id, key, options);
|
|
8026
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8238
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8027
8239
|
}
|
|
8028
8240
|
case "delete_project_variable": {
|
|
8029
8241
|
const args = DeleteProjectVariableSchema.parse(params.arguments);
|
|
@@ -8042,27 +8254,27 @@ async function handleToolCall(params) {
|
|
|
8042
8254
|
const args = ListGroupVariablesSchema.parse(params.arguments);
|
|
8043
8255
|
const { group_id, ...options } = args;
|
|
8044
8256
|
const variables = await listGroupVariables(group_id, options);
|
|
8045
|
-
return { content: [{ type: "text", text: JSON.stringify(variables
|
|
8257
|
+
return { content: [{ type: "text", text: JSON.stringify(variables) }] };
|
|
8046
8258
|
}
|
|
8047
8259
|
case "get_group_variable": {
|
|
8048
8260
|
rejectIfProjectScopedDeployment("get_group_variable");
|
|
8049
8261
|
const args = GetGroupVariableSchema.parse(params.arguments);
|
|
8050
8262
|
const variable = await getGroupVariable(args.group_id, args.key, args.filter);
|
|
8051
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8263
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8052
8264
|
}
|
|
8053
8265
|
case "create_group_variable": {
|
|
8054
8266
|
rejectIfProjectScopedDeployment("create_group_variable");
|
|
8055
8267
|
const args = CreateGroupVariableSchema.parse(params.arguments);
|
|
8056
8268
|
const { group_id, ...options } = args;
|
|
8057
8269
|
const variable = await createGroupVariable(group_id, options);
|
|
8058
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8270
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8059
8271
|
}
|
|
8060
8272
|
case "update_group_variable": {
|
|
8061
8273
|
rejectIfProjectScopedDeployment("update_group_variable");
|
|
8062
8274
|
const args = UpdateGroupVariableSchema.parse(params.arguments);
|
|
8063
8275
|
const { group_id, key, ...options } = args;
|
|
8064
8276
|
const variable = await updateGroupVariable(group_id, key, options);
|
|
8065
|
-
return { content: [{ type: "text", text: JSON.stringify(variable
|
|
8277
|
+
return { content: [{ type: "text", text: JSON.stringify(variable) }] };
|
|
8066
8278
|
}
|
|
8067
8279
|
case "delete_group_variable": {
|
|
8068
8280
|
rejectIfProjectScopedDeployment("delete_group_variable");
|
|
@@ -8082,7 +8294,7 @@ async function handleToolCall(params) {
|
|
|
8082
8294
|
const args = GetDependencyProxySettingsSchema.parse(params.arguments);
|
|
8083
8295
|
const settings = await getDependencyProxySettings(args.group_id);
|
|
8084
8296
|
return {
|
|
8085
|
-
content: [{ type: "text", text: JSON.stringify(settings
|
|
8297
|
+
content: [{ type: "text", text: JSON.stringify(settings) }],
|
|
8086
8298
|
};
|
|
8087
8299
|
}
|
|
8088
8300
|
case "update_dependency_proxy_settings": {
|
|
@@ -8091,7 +8303,7 @@ async function handleToolCall(params) {
|
|
|
8091
8303
|
const { group_id, ...options } = args;
|
|
8092
8304
|
const settings = await updateDependencyProxySettings(group_id, options);
|
|
8093
8305
|
return {
|
|
8094
|
-
content: [{ type: "text", text: JSON.stringify(settings
|
|
8306
|
+
content: [{ type: "text", text: JSON.stringify(settings) }],
|
|
8095
8307
|
};
|
|
8096
8308
|
}
|
|
8097
8309
|
case "list_dependency_proxy_blobs": {
|
|
@@ -8100,7 +8312,7 @@ async function handleToolCall(params) {
|
|
|
8100
8312
|
const { group_id, ...options } = args;
|
|
8101
8313
|
const result = await listDependencyProxyBlobs(group_id, options);
|
|
8102
8314
|
return {
|
|
8103
|
-
content: [{ type: "text", text: JSON.stringify(result
|
|
8315
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
8104
8316
|
};
|
|
8105
8317
|
}
|
|
8106
8318
|
case "purge_dependency_proxy_cache": {
|
|
@@ -8121,13 +8333,13 @@ async function handleToolCall(params) {
|
|
|
8121
8333
|
const args = MarkdownUploadRemoteSchema.parse(params.arguments);
|
|
8122
8334
|
const upload = await markdownUpload(args.project_id, undefined, args.content, args.filename);
|
|
8123
8335
|
return {
|
|
8124
|
-
content: [{ type: "text", text: JSON.stringify(upload
|
|
8336
|
+
content: [{ type: "text", text: JSON.stringify(upload) }],
|
|
8125
8337
|
};
|
|
8126
8338
|
}
|
|
8127
8339
|
const args = MarkdownUploadSchema.parse(params.arguments);
|
|
8128
8340
|
const upload = await markdownUpload(args.project_id, args.file_path);
|
|
8129
8341
|
return {
|
|
8130
|
-
content: [{ type: "text", text: JSON.stringify(upload
|
|
8342
|
+
content: [{ type: "text", text: JSON.stringify(upload) }],
|
|
8131
8343
|
};
|
|
8132
8344
|
}
|
|
8133
8345
|
case "download_attachment": {
|
|
@@ -8139,10 +8351,12 @@ async function handleToolCall(params) {
|
|
|
8139
8351
|
const mimeType = getImageMimeType(args.filename);
|
|
8140
8352
|
if (IS_REMOTE && !mimeType) {
|
|
8141
8353
|
const downloadUrl = buildDownloadUrl("attachment", {
|
|
8142
|
-
project_id: args.project_id,
|
|
8354
|
+
project_id: args.project_id,
|
|
8355
|
+
secret: args.secret,
|
|
8356
|
+
filename: args.filename,
|
|
8143
8357
|
});
|
|
8144
8358
|
return {
|
|
8145
|
-
content: [{ type: "text", text: JSON.stringify({ download_url: downloadUrl, filename: args.filename }
|
|
8359
|
+
content: [{ type: "text", text: JSON.stringify({ download_url: downloadUrl, filename: args.filename }) }],
|
|
8146
8360
|
};
|
|
8147
8361
|
}
|
|
8148
8362
|
const result = await downloadAttachment(args.project_id, args.secret, args.filename, args.local_path);
|
|
@@ -8154,7 +8368,7 @@ async function handleToolCall(params) {
|
|
|
8154
8368
|
{ type: "image", data: base64, mimeType: result.mimeType },
|
|
8155
8369
|
{
|
|
8156
8370
|
type: "text",
|
|
8157
|
-
text: JSON.stringify({ filename: result.filename, mimeType: result.mimeType }
|
|
8371
|
+
text: JSON.stringify({ filename: result.filename, mimeType: result.mimeType }),
|
|
8158
8372
|
},
|
|
8159
8373
|
],
|
|
8160
8374
|
};
|
|
@@ -8163,7 +8377,7 @@ async function handleToolCall(params) {
|
|
|
8163
8377
|
content: [
|
|
8164
8378
|
{
|
|
8165
8379
|
type: "text",
|
|
8166
|
-
text: JSON.stringify({ success: true, file_path: result.savedPath }
|
|
8380
|
+
text: JSON.stringify({ success: true, file_path: result.savedPath }),
|
|
8167
8381
|
},
|
|
8168
8382
|
],
|
|
8169
8383
|
};
|
|
@@ -8172,7 +8386,7 @@ async function handleToolCall(params) {
|
|
|
8172
8386
|
const args = ListEventsSchema.parse(params.arguments);
|
|
8173
8387
|
const events = await listEvents(args);
|
|
8174
8388
|
return {
|
|
8175
|
-
content: [{ type: "text", text: JSON.stringify(events
|
|
8389
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
8176
8390
|
};
|
|
8177
8391
|
}
|
|
8178
8392
|
case "get_project_events": {
|
|
@@ -8180,7 +8394,7 @@ async function handleToolCall(params) {
|
|
|
8180
8394
|
const { project_id, ...options } = args;
|
|
8181
8395
|
const events = await getProjectEvents(project_id, options);
|
|
8182
8396
|
return {
|
|
8183
|
-
content: [{ type: "text", text: JSON.stringify(events
|
|
8397
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
8184
8398
|
};
|
|
8185
8399
|
}
|
|
8186
8400
|
case "list_releases": {
|
|
@@ -8188,14 +8402,14 @@ async function handleToolCall(params) {
|
|
|
8188
8402
|
const { project_id, ...options } = args;
|
|
8189
8403
|
const releases = await listReleases(project_id, options);
|
|
8190
8404
|
return {
|
|
8191
|
-
content: [{ type: "text", text: JSON.stringify(releases
|
|
8405
|
+
content: [{ type: "text", text: JSON.stringify(releases) }],
|
|
8192
8406
|
};
|
|
8193
8407
|
}
|
|
8194
8408
|
case "get_release": {
|
|
8195
8409
|
const args = GetReleaseSchema.parse(params.arguments);
|
|
8196
8410
|
const release = await getRelease(args.project_id, args.tag_name, args.include_html_description);
|
|
8197
8411
|
return {
|
|
8198
|
-
content: [{ type: "text", text: JSON.stringify(release
|
|
8412
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
8199
8413
|
};
|
|
8200
8414
|
}
|
|
8201
8415
|
case "create_release": {
|
|
@@ -8203,7 +8417,7 @@ async function handleToolCall(params) {
|
|
|
8203
8417
|
const { project_id, ...options } = args;
|
|
8204
8418
|
const release = await createRelease(project_id, options);
|
|
8205
8419
|
return {
|
|
8206
|
-
content: [{ type: "text", text: JSON.stringify(release
|
|
8420
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
8207
8421
|
};
|
|
8208
8422
|
}
|
|
8209
8423
|
case "update_release": {
|
|
@@ -8211,7 +8425,7 @@ async function handleToolCall(params) {
|
|
|
8211
8425
|
const { project_id, tag_name, ...options } = args;
|
|
8212
8426
|
const release = await updateRelease(project_id, tag_name, options);
|
|
8213
8427
|
return {
|
|
8214
|
-
content: [{ type: "text", text: JSON.stringify(release
|
|
8428
|
+
content: [{ type: "text", text: JSON.stringify(release) }],
|
|
8215
8429
|
};
|
|
8216
8430
|
}
|
|
8217
8431
|
case "delete_release": {
|
|
@@ -8242,10 +8456,12 @@ async function handleToolCall(params) {
|
|
|
8242
8456
|
const args = DownloadReleaseAssetSchema.parse(params.arguments);
|
|
8243
8457
|
if (IS_REMOTE) {
|
|
8244
8458
|
const downloadUrl = buildDownloadUrl("release-asset", {
|
|
8245
|
-
project_id: args.project_id,
|
|
8459
|
+
project_id: args.project_id,
|
|
8460
|
+
tag_name: args.tag_name,
|
|
8461
|
+
direct_asset_path: args.direct_asset_path,
|
|
8246
8462
|
});
|
|
8247
8463
|
return {
|
|
8248
|
-
content: [{ type: "text", text: JSON.stringify({ download_url: downloadUrl, filename: args.direct_asset_path.split("/").pop() || args.direct_asset_path }
|
|
8464
|
+
content: [{ type: "text", text: JSON.stringify({ download_url: downloadUrl, filename: args.direct_asset_path.split("/").pop() || args.direct_asset_path }) }],
|
|
8249
8465
|
};
|
|
8250
8466
|
}
|
|
8251
8467
|
const assetContent = await downloadReleaseAsset(args.project_id, args.tag_name, args.direct_asset_path);
|
|
@@ -8258,14 +8474,14 @@ async function handleToolCall(params) {
|
|
|
8258
8474
|
const { project_id, ...options } = args;
|
|
8259
8475
|
const tags = await listTags(project_id, options);
|
|
8260
8476
|
return {
|
|
8261
|
-
content: [{ type: "text", text: JSON.stringify(tags
|
|
8477
|
+
content: [{ type: "text", text: JSON.stringify(tags) }],
|
|
8262
8478
|
};
|
|
8263
8479
|
}
|
|
8264
8480
|
case "get_tag": {
|
|
8265
8481
|
const args = GetTagSchema.parse(params.arguments);
|
|
8266
8482
|
const tag = await getTag(args.project_id, args.tag_name);
|
|
8267
8483
|
return {
|
|
8268
|
-
content: [{ type: "text", text: JSON.stringify(tag
|
|
8484
|
+
content: [{ type: "text", text: JSON.stringify(tag) }],
|
|
8269
8485
|
};
|
|
8270
8486
|
}
|
|
8271
8487
|
case "create_tag": {
|
|
@@ -8273,7 +8489,7 @@ async function handleToolCall(params) {
|
|
|
8273
8489
|
const { project_id, ...options } = args;
|
|
8274
8490
|
const tag = await createTag(project_id, options);
|
|
8275
8491
|
return {
|
|
8276
|
-
content: [{ type: "text", text: JSON.stringify(tag
|
|
8492
|
+
content: [{ type: "text", text: JSON.stringify(tag) }],
|
|
8277
8493
|
};
|
|
8278
8494
|
}
|
|
8279
8495
|
case "delete_tag": {
|
|
@@ -8292,30 +8508,28 @@ async function handleToolCall(params) {
|
|
|
8292
8508
|
const args = GetTagSignatureSchema.parse(params.arguments);
|
|
8293
8509
|
const signature = await getTagSignature(args.project_id, args.tag_name);
|
|
8294
8510
|
return {
|
|
8295
|
-
content: [{ type: "text", text: JSON.stringify(signature
|
|
8511
|
+
content: [{ type: "text", text: JSON.stringify(signature) }],
|
|
8296
8512
|
};
|
|
8297
8513
|
}
|
|
8298
8514
|
case "list_webhooks": {
|
|
8299
8515
|
const args = ListWebhooksSchema.parse(params.arguments);
|
|
8300
8516
|
const webhooks = await listWebhooks(args);
|
|
8301
8517
|
return {
|
|
8302
|
-
content: [{ type: "text", text: JSON.stringify(webhooks
|
|
8518
|
+
content: [{ type: "text", text: JSON.stringify(webhooks) }],
|
|
8303
8519
|
};
|
|
8304
8520
|
}
|
|
8305
8521
|
case "list_webhook_events": {
|
|
8306
8522
|
const args = ListWebhookEventsSchema.parse(params.arguments);
|
|
8307
8523
|
const events = await listWebhookEvents(args);
|
|
8308
8524
|
return {
|
|
8309
|
-
content: [{ type: "text", text: JSON.stringify(events
|
|
8525
|
+
content: [{ type: "text", text: JSON.stringify(events) }],
|
|
8310
8526
|
};
|
|
8311
8527
|
}
|
|
8312
8528
|
case "get_webhook_event": {
|
|
8313
8529
|
const args = GetWebhookEventSchema.parse(params.arguments);
|
|
8314
8530
|
const event = await getWebhookEvent(args);
|
|
8315
8531
|
if (!event) {
|
|
8316
|
-
const searchScope = args.page
|
|
8317
|
-
? `on page ${args.page}`
|
|
8318
|
-
: "in the 500 most recent events";
|
|
8532
|
+
const searchScope = args.page ? `on page ${args.page}` : "in the 500 most recent events";
|
|
8319
8533
|
return {
|
|
8320
8534
|
content: [
|
|
8321
8535
|
{
|
|
@@ -8326,7 +8540,7 @@ async function handleToolCall(params) {
|
|
|
8326
8540
|
};
|
|
8327
8541
|
}
|
|
8328
8542
|
return {
|
|
8329
|
-
content: [{ type: "text", text: JSON.stringify(event
|
|
8543
|
+
content: [{ type: "text", text: JSON.stringify(event) }],
|
|
8330
8544
|
};
|
|
8331
8545
|
}
|
|
8332
8546
|
case "health_check": {
|
|
@@ -8334,13 +8548,24 @@ async function handleToolCall(params) {
|
|
|
8334
8548
|
const url = new URL(`${getEffectiveApiUrl()}/user`);
|
|
8335
8549
|
const response = await fetch(url.toString(), getFetchConfig());
|
|
8336
8550
|
let authenticated = response.ok;
|
|
8337
|
-
if (!authenticated &&
|
|
8551
|
+
if (!authenticated &&
|
|
8552
|
+
(response.status === 401 || response.status === 403) &&
|
|
8553
|
+
(GITLAB_JOB_TOKEN || usesJobTokenHeader())) {
|
|
8338
8554
|
const jobUrl = new URL(`${getEffectiveApiUrl()}/job`);
|
|
8339
8555
|
const jobResponse = await fetch(jobUrl.toString(), getFetchConfig());
|
|
8340
8556
|
authenticated = jobResponse.ok;
|
|
8341
8557
|
}
|
|
8342
8558
|
return {
|
|
8343
|
-
content: [
|
|
8559
|
+
content: [
|
|
8560
|
+
{
|
|
8561
|
+
type: "text",
|
|
8562
|
+
text: JSON.stringify({
|
|
8563
|
+
status: authenticated ? "ok" : "error",
|
|
8564
|
+
authenticated,
|
|
8565
|
+
gitlab_url: getEffectiveApiUrl(),
|
|
8566
|
+
}),
|
|
8567
|
+
},
|
|
8568
|
+
],
|
|
8344
8569
|
};
|
|
8345
8570
|
}
|
|
8346
8571
|
case "get_branch": {
|
|
@@ -8354,7 +8579,7 @@ async function handleToolCall(params) {
|
|
|
8354
8579
|
const data = await response.json();
|
|
8355
8580
|
const branch = GitLabBranchSchema.parse(data);
|
|
8356
8581
|
return {
|
|
8357
|
-
content: [{ type: "text", text: JSON.stringify(branch
|
|
8582
|
+
content: [{ type: "text", text: JSON.stringify(branch) }],
|
|
8358
8583
|
};
|
|
8359
8584
|
}
|
|
8360
8585
|
case "list_branches": {
|
|
@@ -8377,7 +8602,7 @@ async function handleToolCall(params) {
|
|
|
8377
8602
|
const data = await response.json();
|
|
8378
8603
|
const branches = z.array(GitLabBranchSchema).parse(data);
|
|
8379
8604
|
return {
|
|
8380
|
-
content: [{ type: "text", text: JSON.stringify(branches
|
|
8605
|
+
content: [{ type: "text", text: JSON.stringify(branches) }],
|
|
8381
8606
|
};
|
|
8382
8607
|
}
|
|
8383
8608
|
case "delete_branch": {
|
|
@@ -8390,7 +8615,7 @@ async function handleToolCall(params) {
|
|
|
8390
8615
|
});
|
|
8391
8616
|
await handleGitLabError(response);
|
|
8392
8617
|
return {
|
|
8393
|
-
content: [{ type: "text", text: JSON.stringify({ status: "deleted", branch: args.branch_name }
|
|
8618
|
+
content: [{ type: "text", text: JSON.stringify({ status: "deleted", branch: args.branch_name }) }],
|
|
8394
8619
|
};
|
|
8395
8620
|
}
|
|
8396
8621
|
case "list_protected_branches": {
|
|
@@ -8410,7 +8635,7 @@ async function handleToolCall(params) {
|
|
|
8410
8635
|
await handleGitLabError(response);
|
|
8411
8636
|
const data = z.array(GitLabProtectedBranchSchema).parse(await response.json());
|
|
8412
8637
|
return {
|
|
8413
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
8638
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
8414
8639
|
};
|
|
8415
8640
|
}
|
|
8416
8641
|
case "get_protected_branch": {
|
|
@@ -8424,7 +8649,7 @@ async function handleToolCall(params) {
|
|
|
8424
8649
|
await handleGitLabError(response);
|
|
8425
8650
|
const data = GitLabProtectedBranchSchema.parse(await response.json());
|
|
8426
8651
|
return {
|
|
8427
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
8652
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
8428
8653
|
};
|
|
8429
8654
|
}
|
|
8430
8655
|
case "protect_branch": {
|
|
@@ -8451,7 +8676,7 @@ async function handleToolCall(params) {
|
|
|
8451
8676
|
await handleGitLabError(response);
|
|
8452
8677
|
const data = GitLabProtectedBranchSchema.parse(await response.json());
|
|
8453
8678
|
return {
|
|
8454
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
8679
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
8455
8680
|
};
|
|
8456
8681
|
}
|
|
8457
8682
|
case "unprotect_branch": {
|
|
@@ -8465,7 +8690,7 @@ async function handleToolCall(params) {
|
|
|
8465
8690
|
});
|
|
8466
8691
|
await handleGitLabError(response);
|
|
8467
8692
|
return {
|
|
8468
|
-
content: [{ type: "text", text: JSON.stringify({ status: "unprotected", branch: args.branch_name }
|
|
8693
|
+
content: [{ type: "text", text: JSON.stringify({ status: "unprotected", branch: args.branch_name }) }],
|
|
8469
8694
|
};
|
|
8470
8695
|
}
|
|
8471
8696
|
case "update_default_branch": {
|
|
@@ -8481,7 +8706,7 @@ async function handleToolCall(params) {
|
|
|
8481
8706
|
await handleGitLabError(response);
|
|
8482
8707
|
const data = await response.json();
|
|
8483
8708
|
return {
|
|
8484
|
-
content: [{ type: "text", text: JSON.stringify({ status: "updated", default_branch: args.default_branch, project: data }
|
|
8709
|
+
content: [{ type: "text", text: JSON.stringify({ status: "updated", default_branch: args.default_branch, project: data }) }],
|
|
8485
8710
|
};
|
|
8486
8711
|
}
|
|
8487
8712
|
default:
|
|
@@ -8660,7 +8885,9 @@ function registerDownloadProxy(app, maxRequestsPerMinute = Number.parseInt(proce
|
|
|
8660
8885
|
case "release-asset": {
|
|
8661
8886
|
const { project_id, tag_name, direct_asset_path } = req.query;
|
|
8662
8887
|
if (!project_id || !tag_name || !direct_asset_path) {
|
|
8663
|
-
res
|
|
8888
|
+
res
|
|
8889
|
+
.status(400)
|
|
8890
|
+
.json({ error: "project_id, tag_name, and direct_asset_path are required" });
|
|
8664
8891
|
return;
|
|
8665
8892
|
}
|
|
8666
8893
|
const effectiveProjectId = getEffectiveProjectId(decodeURIComponent(project_id));
|
|
@@ -9067,7 +9294,7 @@ async function startStreamableHTTPServer() {
|
|
|
9067
9294
|
token: effective.token,
|
|
9068
9295
|
lastUsed: Date.now(),
|
|
9069
9296
|
apiUrl: effective.apiUrl,
|
|
9070
|
-
publicBaseUrl: getForwardedPublicBaseUrl(req),
|
|
9297
|
+
publicBaseUrl: getForwardedPublicBaseUrl(req, MCP_TRUST_PROXY),
|
|
9071
9298
|
};
|
|
9072
9299
|
// Step 4: create a fresh transport per request.
|
|
9073
9300
|
const transport = isInit
|
|
@@ -9261,16 +9488,14 @@ async function startStreamableHTTPServer() {
|
|
|
9261
9488
|
// Streamable HTTP endpoint - handles both session creation and message handling
|
|
9262
9489
|
app.post("/mcp", mcpBearerAuth, async (req, res) => {
|
|
9263
9490
|
const sessionId = readMcpSessionIdHeader(req);
|
|
9264
|
-
const publicBaseUrl = getForwardedPublicBaseUrl(req);
|
|
9491
|
+
const publicBaseUrl = getForwardedPublicBaseUrl(req, MCP_TRUST_PROXY);
|
|
9265
9492
|
// Track request
|
|
9266
9493
|
metrics.requestsProcessed++;
|
|
9267
9494
|
// Stateless-mode branch: bypass authBySession / streamableTransports
|
|
9268
9495
|
// entirely and derive the session auth from either the current request
|
|
9269
9496
|
// headers (init) or a sealed Mcp-Session-Id (subsequent requests).
|
|
9270
9497
|
// Rate limiting is disabled here because there is no shared counter.
|
|
9271
|
-
if (OAUTH_STATELESS_MODE &&
|
|
9272
|
-
STATELESS_MATERIAL &&
|
|
9273
|
-
(REMOTE_AUTHORIZATION || GITLAB_MCP_OAUTH)) {
|
|
9498
|
+
if (OAUTH_STATELESS_MODE && STATELESS_MATERIAL && (REMOTE_AUTHORIZATION || GITLAB_MCP_OAUTH)) {
|
|
9274
9499
|
await handleStatelessMcpRequest(req, res, STATELESS_MATERIAL, OAUTH_STATELESS_SESSION_TTL_SECONDS);
|
|
9275
9500
|
return;
|
|
9276
9501
|
}
|
|
@@ -9295,9 +9520,11 @@ async function startStreamableHTTPServer() {
|
|
|
9295
9520
|
// Handle remote authorization: extract and store auth headers per session
|
|
9296
9521
|
if (REMOTE_AUTHORIZATION) {
|
|
9297
9522
|
const authData = parseAuthHeaders(req);
|
|
9523
|
+
const allowUnauthenticatedDiscovery = GITLAB_ALLOW_UNAUTHENTICATED_TOOL_DISCOVERY &&
|
|
9524
|
+
isUnauthenticatedDiscoveryRequestBody(req.body);
|
|
9298
9525
|
if (sessionId && !authBySession[sessionId]) {
|
|
9299
|
-
// New session: require auth headers
|
|
9300
|
-
if (!authData) {
|
|
9526
|
+
// New session: require auth headers unless public discovery was explicitly enabled.
|
|
9527
|
+
if (!authData && !allowUnauthenticatedDiscovery) {
|
|
9301
9528
|
metrics.authFailures++;
|
|
9302
9529
|
res.status(401).json({
|
|
9303
9530
|
error: "Missing Private-Token, JOB-TOKEN, or Authorization header",
|
|
@@ -9305,10 +9532,16 @@ async function startStreamableHTTPServer() {
|
|
|
9305
9532
|
});
|
|
9306
9533
|
return;
|
|
9307
9534
|
}
|
|
9308
|
-
// Store auth
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9535
|
+
// Store auth only when provided. Public discovery intentionally leaves the session unauthenticated.
|
|
9536
|
+
if (authData) {
|
|
9537
|
+
authBySession[sessionId] = withPublicBaseUrl(authData, publicBaseUrl);
|
|
9538
|
+
logger.info(`Session ${sessionId}: stored ${authData.header} header`);
|
|
9539
|
+
setAuthTimeout(sessionId);
|
|
9540
|
+
}
|
|
9541
|
+
else if (allowUnauthenticatedDiscovery) {
|
|
9542
|
+
// Schedule cleanup for unauthenticated discovery sessions to prevent slot exhaustion
|
|
9543
|
+
setAuthTimeout(sessionId);
|
|
9544
|
+
}
|
|
9312
9545
|
}
|
|
9313
9546
|
else if (sessionId && authData) {
|
|
9314
9547
|
// Existing session: allow auth rotation/update
|
|
@@ -9481,25 +9714,112 @@ async function startStreamableHTTPServer() {
|
|
|
9481
9714
|
message: "GET /mcp is not supported when STREAMABLE_HTTP is enabled. Use POST to communicate with the MCP server.",
|
|
9482
9715
|
});
|
|
9483
9716
|
});
|
|
9717
|
+
const getMetricsSnapshot = () => ({
|
|
9718
|
+
...metrics,
|
|
9719
|
+
activeSessions: Object.keys(streamableTransports).length,
|
|
9720
|
+
authenticatedSessions: Object.keys(authBySession).length,
|
|
9721
|
+
gitlabClientPool: clientPool.getStats(),
|
|
9722
|
+
uptime: process.uptime(),
|
|
9723
|
+
memoryUsage: process.memoryUsage(),
|
|
9724
|
+
config: {
|
|
9725
|
+
maxSessions: MAX_SESSIONS,
|
|
9726
|
+
maxRequestsPerMinute: MAX_REQUESTS_PER_MINUTE,
|
|
9727
|
+
sessionTimeoutSeconds: SESSION_TIMEOUT_SECONDS,
|
|
9728
|
+
remoteAuthEnabled: REMOTE_AUTHORIZATION,
|
|
9729
|
+
mcpOAuthEnabled: GITLAB_MCP_OAUTH,
|
|
9730
|
+
statelessModeEnabled: OAUTH_STATELESS_MODE && STATELESS_MATERIAL !== null,
|
|
9731
|
+
statelessRotationKey: OAUTH_STATELESS_MODE && STATELESS_MATERIAL?.previous != null,
|
|
9732
|
+
},
|
|
9733
|
+
});
|
|
9734
|
+
const escapePrometheusLabel = (value) => String(value).replace(/\\/g, "\\\\").replace(/\n/g, "\\n").replace(/"/g, '\\"');
|
|
9735
|
+
const formatPrometheusMetrics = () => {
|
|
9736
|
+
const snapshot = getMetricsSnapshot();
|
|
9737
|
+
const configLabels = Object.entries({
|
|
9738
|
+
max_sessions: snapshot.config.maxSessions,
|
|
9739
|
+
max_requests_per_minute: snapshot.config.maxRequestsPerMinute,
|
|
9740
|
+
session_timeout_seconds: snapshot.config.sessionTimeoutSeconds,
|
|
9741
|
+
remote_auth_enabled: snapshot.config.remoteAuthEnabled,
|
|
9742
|
+
mcp_oauth_enabled: snapshot.config.mcpOAuthEnabled,
|
|
9743
|
+
stateless_mode_enabled: snapshot.config.statelessModeEnabled,
|
|
9744
|
+
stateless_rotation_key: snapshot.config.statelessRotationKey,
|
|
9745
|
+
})
|
|
9746
|
+
.map(([key, value]) => `${key}="${escapePrometheusLabel(value)}"`)
|
|
9747
|
+
.join(",");
|
|
9748
|
+
return [
|
|
9749
|
+
"# HELP gitlab_mcp_requests_processed_total Total MCP requests processed",
|
|
9750
|
+
"# TYPE gitlab_mcp_requests_processed_total counter",
|
|
9751
|
+
`gitlab_mcp_requests_processed_total ${snapshot.requestsProcessed}`,
|
|
9752
|
+
"",
|
|
9753
|
+
"# HELP gitlab_mcp_requests_rejected_total Requests rejected, by reason",
|
|
9754
|
+
"# TYPE gitlab_mcp_requests_rejected_total counter",
|
|
9755
|
+
`gitlab_mcp_requests_rejected_total{reason="rate_limit"} ${snapshot.rejectedByRateLimit}`,
|
|
9756
|
+
`gitlab_mcp_requests_rejected_total{reason="capacity"} ${snapshot.rejectedByCapacity}`,
|
|
9757
|
+
"",
|
|
9758
|
+
"# HELP gitlab_mcp_auth_failures_total Authentication failures",
|
|
9759
|
+
"# TYPE gitlab_mcp_auth_failures_total counter",
|
|
9760
|
+
`gitlab_mcp_auth_failures_total ${snapshot.authFailures}`,
|
|
9761
|
+
"",
|
|
9762
|
+
"# HELP gitlab_mcp_sessions_total Total sessions created",
|
|
9763
|
+
"# TYPE gitlab_mcp_sessions_total counter",
|
|
9764
|
+
`gitlab_mcp_sessions_total ${snapshot.totalSessions}`,
|
|
9765
|
+
"",
|
|
9766
|
+
"# HELP gitlab_mcp_sessions_expired_total Sessions expired due to inactivity",
|
|
9767
|
+
"# TYPE gitlab_mcp_sessions_expired_total counter",
|
|
9768
|
+
`gitlab_mcp_sessions_expired_total ${snapshot.expiredSessions}`,
|
|
9769
|
+
"",
|
|
9770
|
+
"# HELP gitlab_mcp_active_sessions Currently active sessions",
|
|
9771
|
+
"# TYPE gitlab_mcp_active_sessions gauge",
|
|
9772
|
+
`gitlab_mcp_active_sessions ${snapshot.activeSessions}`,
|
|
9773
|
+
"",
|
|
9774
|
+
"# HELP gitlab_mcp_authenticated_sessions Currently authenticated sessions",
|
|
9775
|
+
"# TYPE gitlab_mcp_authenticated_sessions gauge",
|
|
9776
|
+
`gitlab_mcp_authenticated_sessions ${snapshot.authenticatedSessions}`,
|
|
9777
|
+
"",
|
|
9778
|
+
"# HELP gitlab_mcp_client_pool_size Current GitLab client pool size",
|
|
9779
|
+
"# TYPE gitlab_mcp_client_pool_size gauge",
|
|
9780
|
+
`gitlab_mcp_client_pool_size ${snapshot.gitlabClientPool.size}`,
|
|
9781
|
+
"",
|
|
9782
|
+
"# HELP gitlab_mcp_client_pool_max_size Maximum GitLab client pool size",
|
|
9783
|
+
"# TYPE gitlab_mcp_client_pool_max_size gauge",
|
|
9784
|
+
`gitlab_mcp_client_pool_max_size ${snapshot.gitlabClientPool.maxSize}`,
|
|
9785
|
+
"",
|
|
9786
|
+
"# HELP gitlab_mcp_uptime_seconds Process uptime in seconds",
|
|
9787
|
+
"# TYPE gitlab_mcp_uptime_seconds gauge",
|
|
9788
|
+
`gitlab_mcp_uptime_seconds ${snapshot.uptime}`,
|
|
9789
|
+
"",
|
|
9790
|
+
"# HELP gitlab_mcp_memory_usage_bytes Node.js memory usage by type",
|
|
9791
|
+
"# TYPE gitlab_mcp_memory_usage_bytes gauge",
|
|
9792
|
+
...Object.entries(snapshot.memoryUsage).map(([key, value]) => `gitlab_mcp_memory_usage_bytes{type="${escapePrometheusLabel(key)}"} ${value}`),
|
|
9793
|
+
"",
|
|
9794
|
+
"# HELP gitlab_mcp_stateless_requests_total Stateless MCP requests processed",
|
|
9795
|
+
"# TYPE gitlab_mcp_stateless_requests_total counter",
|
|
9796
|
+
`gitlab_mcp_stateless_requests_total ${snapshot.statelessRequests}`,
|
|
9797
|
+
"",
|
|
9798
|
+
"# HELP gitlab_mcp_stateless_auth_total Stateless auth successes, by source",
|
|
9799
|
+
"# TYPE gitlab_mcp_stateless_auth_total counter",
|
|
9800
|
+
`gitlab_mcp_stateless_auth_total{source="header"} ${snapshot.statelessAuthFromHeader}`,
|
|
9801
|
+
`gitlab_mcp_stateless_auth_total{source="sealed_session_id"} ${snapshot.statelessAuthFromSealedSid}`,
|
|
9802
|
+
"",
|
|
9803
|
+
"# HELP gitlab_mcp_stateless_auth_failures_total Stateless auth failures",
|
|
9804
|
+
"# TYPE gitlab_mcp_stateless_auth_failures_total counter",
|
|
9805
|
+
`gitlab_mcp_stateless_auth_failures_total ${snapshot.statelessAuthFailures}`,
|
|
9806
|
+
"",
|
|
9807
|
+
"# HELP gitlab_mcp_stateless_session_id_rotations_total Stateless session id rotations",
|
|
9808
|
+
"# TYPE gitlab_mcp_stateless_session_id_rotations_total counter",
|
|
9809
|
+
`gitlab_mcp_stateless_session_id_rotations_total ${snapshot.statelessSidRotated}`,
|
|
9810
|
+
"",
|
|
9811
|
+
"# HELP gitlab_mcp_config_info Static configuration (value is always 1)",
|
|
9812
|
+
"# TYPE gitlab_mcp_config_info gauge",
|
|
9813
|
+
`gitlab_mcp_config_info{${configLabels}} 1`,
|
|
9814
|
+
"",
|
|
9815
|
+
].join("\n");
|
|
9816
|
+
};
|
|
9484
9817
|
// Metrics endpoint
|
|
9485
9818
|
app.get("/metrics", (_req, res) => {
|
|
9486
|
-
res.
|
|
9487
|
-
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
gitlabClientPool: clientPool.getStats(),
|
|
9491
|
-
uptime: process.uptime(),
|
|
9492
|
-
memoryUsage: process.memoryUsage(),
|
|
9493
|
-
config: {
|
|
9494
|
-
maxSessions: MAX_SESSIONS,
|
|
9495
|
-
maxRequestsPerMinute: MAX_REQUESTS_PER_MINUTE,
|
|
9496
|
-
sessionTimeoutSeconds: SESSION_TIMEOUT_SECONDS,
|
|
9497
|
-
remoteAuthEnabled: REMOTE_AUTHORIZATION,
|
|
9498
|
-
mcpOAuthEnabled: GITLAB_MCP_OAUTH,
|
|
9499
|
-
statelessModeEnabled: OAUTH_STATELESS_MODE && STATELESS_MATERIAL !== null,
|
|
9500
|
-
statelessRotationKey: OAUTH_STATELESS_MODE && STATELESS_MATERIAL?.previous != null,
|
|
9501
|
-
},
|
|
9502
|
-
});
|
|
9819
|
+
res.type("text/plain; version=0.0.4").send(formatPrometheusMetrics());
|
|
9820
|
+
});
|
|
9821
|
+
app.get("/metrics.json", (_req, res) => {
|
|
9822
|
+
res.json(getMetricsSnapshot());
|
|
9503
9823
|
});
|
|
9504
9824
|
// Health check endpoint
|
|
9505
9825
|
app.get("/health", (_req, res) => {
|